summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorng0 <ng0@n0.is>2019-09-12 15:09:29 +0000
committerng0 <ng0@n0.is>2019-09-12 15:09:29 +0000
commit5d5a61dc56228532927a7786375a13d7ae749180 (patch)
tree1b4b73a0016f005655aaa18982df8383f790527f
parentbc555b4f37422efffcc9969f645f9dbf3cb444bd (diff)
parent9cd755e1d768bbf228e7c9faf223b7459f7e0105 (diff)
downloadgnurl-5d5a61dc56228532927a7786375a13d7ae749180.tar.gz
gnurl-5d5a61dc56228532927a7786375a13d7ae749180.tar.bz2
gnurl-5d5a61dc56228532927a7786375a13d7ae749180.zip
Merge tag 'curl-7_66_0'
7.66.0
-rw-r--r--.lgtm.yml2
-rw-r--r--.mailmap7
-rw-r--r--.travis.yml81
-rw-r--r--CMake/FindGSS.cmake4
-rw-r--r--CMakeLists.txt3
-rw-r--r--Makefile.am13
-rw-r--r--RELEASE-NOTES185
-rw-r--r--appveyor.yml4
-rwxr-xr-xconfigure.ac338
-rw-r--r--docs/ALTSVC.md64
-rw-r--r--docs/DEPRECATE.md15
-rw-r--r--docs/EXPERIMENTAL.md22
-rw-r--r--docs/HTTP3.md121
-rw-r--r--docs/INTERNALS.md2
-rw-r--r--docs/KNOWN_BUGS68
-rw-r--r--docs/MANUAL1058
-rw-r--r--docs/MANUAL.md1011
-rw-r--r--docs/Makefile.am3
-rw-r--r--docs/PARALLEL-TRANSFERS.md58
-rw-r--r--docs/ROADMAP.md65
-rw-r--r--docs/THANKS25
-rw-r--r--docs/THANKS-filter1
-rw-r--r--docs/TODO238
-rw-r--r--docs/cmdline-opts/Makefile.inc7
-rw-r--r--docs/cmdline-opts/config.d2
-rw-r--r--docs/cmdline-opts/http0.9.d3
-rw-r--r--docs/cmdline-opts/http2.d1
-rw-r--r--docs/cmdline-opts/http3.d19
-rw-r--r--docs/cmdline-opts/parallel-max.d9
-rw-r--r--docs/cmdline-opts/parallel.d7
-rw-r--r--docs/cmdline-opts/retry.d3
-rw-r--r--docs/cmdline-opts/sasl-authzid.d11
-rw-r--r--docs/examples/Makefile.inc5
-rw-r--r--docs/examples/altsvc.c56
-rw-r--r--docs/examples/curlx.c6
-rw-r--r--docs/examples/ephiperfifo.c42
-rw-r--r--docs/examples/hiperfifo.c52
-rw-r--r--docs/examples/http3-present.c47
-rw-r--r--docs/examples/http3.c54
-rw-r--r--docs/examples/imap-authzid.c71
-rw-r--r--docs/examples/pop3-authzid.c70
-rw-r--r--docs/examples/smtp-authzid.c161
-rw-r--r--docs/libcurl/Makefile.inc1
-rw-r--r--docs/libcurl/curl_multi_poll.3110
-rw-r--r--docs/libcurl/gnurl_easy_getinfo.33
-rw-r--r--docs/libcurl/gnurl_easy_setopt.32
-rw-r--r--docs/libcurl/gnurl_global_init_mem.34
-rw-r--r--docs/libcurl/gnurl_version_info.3103
-rw-r--r--docs/libcurl/libgnurl-errors.32
-rw-r--r--docs/libcurl/opts/CURLINFO_RETRY_AFTER.363
-rw-r--r--docs/libcurl/opts/CURLOPT_SASL_AUTHZID.364
-rw-r--r--docs/libcurl/opts/GNURLINFO_APPCONNECT_TIME.34
-rw-r--r--docs/libcurl/opts/GNURLINFO_APPCONNECT_TIME_T.34
-rw-r--r--docs/libcurl/opts/GNURLINFO_CONNECT_TIME.34
-rw-r--r--docs/libcurl/opts/GNURLINFO_CONNECT_TIME_T.35
-rw-r--r--docs/libcurl/opts/GNURLINFO_HTTP_VERSION.39
-rw-r--r--docs/libcurl/opts/GNURLINFO_NAMELOOKUP_TIME.34
-rw-r--r--docs/libcurl/opts/GNURLINFO_NAMELOOKUP_TIME_T.34
-rw-r--r--docs/libcurl/opts/GNURLINFO_PRETRANSFER_TIME.34
-rw-r--r--docs/libcurl/opts/GNURLINFO_PRETRANSFER_TIME_T.34
-rw-r--r--docs/libcurl/opts/GNURLINFO_STARTTRANSFER_TIME.34
-rw-r--r--docs/libcurl/opts/GNURLINFO_STARTTRANSFER_TIME_T.34
-rw-r--r--docs/libcurl/opts/GNURLINFO_TOTAL_TIME.34
-rw-r--r--docs/libcurl/opts/GNURLINFO_TOTAL_TIME_T.34
-rw-r--r--docs/libcurl/opts/GNURLOPT_ALTSVC.32
-rw-r--r--docs/libcurl/opts/GNURLOPT_ALTSVC_CTRL.37
-rw-r--r--docs/libcurl/opts/GNURLOPT_HEADERFUNCTION.35
-rw-r--r--docs/libcurl/opts/GNURLOPT_HTTP09_ALLOWED.310
-rw-r--r--docs/libcurl/opts/GNURLOPT_HTTP_VERSION.312
-rw-r--r--docs/libcurl/opts/GNURLOPT_POST.35
-rw-r--r--docs/libcurl/opts/GNURLOPT_PROXY_SSL_VERIFYHOST.313
-rw-r--r--docs/libcurl/opts/GNURLOPT_READFUNCTION.333
-rw-r--r--docs/libcurl/opts/GNURLOPT_SSL_VERIFYHOST.316
-rw-r--r--docs/libcurl/opts/Makefile.inc2
-rw-r--r--docs/libcurl/symbols-in-versions7
-rw-r--r--include/gnurl/curl.h35
-rw-r--r--include/gnurl/curlver.h14
-rw-r--r--include/gnurl/easy.h6
-rw-r--r--include/gnurl/mprintf.h8
-rw-r--r--include/gnurl/multi.h20
-rw-r--r--include/gnurl/stdcheaders.h8
-rw-r--r--include/gnurl/system.h30
-rw-r--r--include/gnurl/typecheck-gcc.h547
-rw-r--r--include/gnurl/urlapi.h6
-rw-r--r--lib/Makefile.am8
-rw-r--r--lib/Makefile.inc16
-rw-r--r--lib/altsvc.c65
-rw-r--r--lib/altsvc.h27
-rw-r--r--lib/asyn-ares.c6
-rwxr-xr-x[-rw-r--r--]lib/asyn-thread.c102
-rw-r--r--lib/asyn.h3
-rw-r--r--lib/base64.c4
-rw-r--r--lib/config-os400.h3
-rw-r--r--lib/config-plan9.h217
-rw-r--r--lib/connect.c160
-rw-r--r--lib/connect.h3
-rw-r--r--lib/cookie.c14
-rw-r--r--lib/curl_md4.h10
-rw-r--r--lib/curl_ntlm_core.c70
-rw-r--r--lib/curl_path.c2
-rw-r--r--lib/curl_rtmp.c14
-rw-r--r--lib/curl_sasl.c10
-rw-r--r--lib/curl_setup.h17
-rw-r--r--lib/doh.h3
-rw-r--r--lib/easy.c87
-rw-r--r--lib/ftp.c23
-rw-r--r--lib/getenv.c4
-rw-r--r--lib/getinfo.c8
-rw-r--r--lib/hostip.c8
-rw-r--r--lib/hostip.h6
-rw-r--r--lib/hostip6.c3
-rw-r--r--lib/http.c208
-rw-r--r--lib/http.h31
-rw-r--r--lib/http2.c23
-rw-r--r--lib/http_negotiate.c4
-rw-r--r--lib/imap.c8
-rw-r--r--lib/md4.c248
-rwxr-xr-x[-rw-r--r--]lib/multi.c252
-rw-r--r--lib/multiif.h9
-rw-r--r--lib/netrc.c151
-rw-r--r--lib/openldap.c28
-rw-r--r--lib/pingpong.c13
-rw-r--r--lib/pingpong.h5
-rw-r--r--lib/pop3.c8
-rw-r--r--lib/progress.c13
-rw-r--r--lib/quic.h53
-rw-r--r--lib/rtsp.c9
-rw-r--r--lib/security.c6
-rw-r--r--lib/select.h6
-rw-r--r--lib/setopt.c37
-rw-r--r--lib/smb.c12
-rw-r--r--lib/smtp.c14
-rw-r--r--lib/ssh.h13
-rw-r--r--lib/strerror.c3
-rw-r--r--lib/tftp.c77
-rw-r--r--lib/timeval.c24
-rw-r--r--lib/timeval.h10
-rw-r--r--lib/transfer.c27
-rw-r--r--lib/transfer.h3
-rw-r--r--lib/url.c281
-rw-r--r--lib/url.h13
-rw-r--r--lib/urlapi.c17
-rw-r--r--lib/urldata.h58
-rw-r--r--lib/vauth/digest_sspi.c15
-rw-r--r--lib/vauth/krb5_gssapi.c10
-rw-r--r--lib/vauth/krb5_sspi.c26
-rw-r--r--lib/vauth/ntlm_sspi.c14
-rw-r--r--lib/vauth/spnego_gssapi.c6
-rw-r--r--lib/vauth/spnego_sspi.c23
-rw-r--r--lib/version.c60
-rw-r--r--lib/vquic/ngtcp2.c1600
-rw-r--r--lib/vquic/ngtcp2.h63
-rw-r--r--lib/vquic/quiche.c780
-rw-r--r--lib/vquic/quiche.h49
-rw-r--r--lib/vssh/libssh.c (renamed from lib/ssh-libssh.c)40
-rw-r--r--lib/vssh/libssh2.c (renamed from lib/ssh.c)49
-rw-r--r--lib/vtls/mesalink.c62
-rw-r--r--lib/vtls/nss.c16
-rw-r--r--lib/vtls/openssl.c233
-rw-r--r--lib/vtls/vtls.c10
-rw-r--r--lib/vtls/vtls.h3
-rw-r--r--packages/OS400/README.OS4001
-rw-r--r--packages/OS400/ccsidcurl.c8
-rw-r--r--packages/OS400/ccsidcurl.h8
-rw-r--r--packages/OS400/curl.inc.in4
-rw-r--r--plan9/BUILD.PLAN9.txt55
-rw-r--r--plan9/include/mkfile34
-rw-r--r--plan9/lib/mkfile39
-rwxr-xr-xplan9/lib/mkfile.inc25
-rw-r--r--plan9/mkfile36
-rw-r--r--plan9/mkfile.proto30
-rw-r--r--plan9/src/mkfile45
-rwxr-xr-xplan9/src/mkfile.inc25
-rw-r--r--projects/build-openssl.bat19
-rw-r--r--src/Makefile.am2
-rw-r--r--src/Makefile.inc2
-rw-r--r--src/tool_cb_hdr.c12
-rw-r--r--src/tool_cb_wrt.c8
-rw-r--r--src/tool_cfgable.c3
-rw-r--r--src/tool_cfgable.h11
-rw-r--r--src/tool_getparam.c74
-rw-r--r--src/tool_help.c15
-rw-r--r--src/tool_help.h4
-rw-r--r--src/tool_main.c26
-rw-r--r--src/tool_main.h5
-rw-r--r--src/tool_metalink.c15
-rw-r--r--src/tool_metalink.h3
-rw-r--r--src/tool_operate.c1638
-rw-r--r--src/tool_operate.h48
-rw-r--r--src/tool_operhlp.c8
-rw-r--r--src/tool_operhlp.h4
-rw-r--r--src/tool_paramhlp.c22
-rw-r--r--src/tool_paramhlp.h3
-rw-r--r--src/tool_parsecfg.c120
-rw-r--r--src/tool_progress.c314
-rw-r--r--src/tool_progress.h39
-rw-r--r--src/tool_setopt.c11
-rw-r--r--src/tool_writeout.c5
-rw-r--r--tests/README27
-rw-r--r--tests/data/Makefile.inc18
-rw-r--r--tests/data/test10028
-rw-r--r--tests/data/test11351
-rw-r--r--tests/data/test117450
-rw-r--r--tests/data/test126934
-rw-r--r--tests/data/test129110
-rw-r--r--tests/data/test14011
-rw-r--r--tests/data/test14021
-rw-r--r--tests/data/test14031
-rw-r--r--tests/data/test14041
-rw-r--r--tests/data/test14062
-rw-r--r--tests/data/test141218
-rw-r--r--tests/data/test141813
-rw-r--r--tests/data/test14201
-rw-r--r--tests/data/test151417
-rw-r--r--tests/data/test15324
-rw-r--r--tests/data/test15383
-rw-r--r--tests/data/test159452
-rw-r--r--tests/data/test159551
-rw-r--r--tests/data/test159652
-rw-r--r--tests/data/test16546
-rw-r--r--tests/data/test20064
-rw-r--r--tests/data/test20074
-rw-r--r--tests/data/test20084
-rw-r--r--tests/data/test20094
-rw-r--r--tests/data/test20104
-rw-r--r--tests/data/test20472
-rw-r--r--tests/data/test207742
-rw-r--r--tests/data/test207854
-rw-r--r--tests/data/test335102
-rw-r--r--tests/data/test3562
-rw-r--r--tests/data/test84856
-rw-r--r--tests/data/test84951
-rw-r--r--tests/data/test89257
-rw-r--r--tests/data/test89353
-rw-r--r--tests/data/test95356
-rw-r--r--tests/data/test95455
-rw-r--r--tests/libtest/Makefile.inc9
-rw-r--r--tests/libtest/first.c6
-rw-r--r--tests/libtest/lib1560.c4
-rw-r--r--tests/libtest/lib1594.c66
-rwxr-xr-xtests/symbol-scan.pl4
-rw-r--r--tests/unit/unit1607.c8
-rw-r--r--tests/unit/unit1609.c10
-rw-r--r--tests/unit/unit1654.c2
-rw-r--r--winbuild/MakefileBuild.vc4
245 files changed, 10635 insertions, 3966 deletions
diff --git a/.lgtm.yml b/.lgtm.yml
index bb6945f0f..030c563bd 100644
--- a/.lgtm.yml
+++ b/.lgtm.yml
@@ -7,4 +7,4 @@ extraction:
- rm -f CMakeLists.txt
- ./buildconf
configure: # enable as many optional features as possible
- command: ./configure --enable-ares --with-libssh2 --with-gssapi --with-librtmp --with-libmetalink --with-libmetalink
+ command: ./configure --enable-ares --with-libssh2 --with-gssapi --with-librtmp --with-libmetalink
diff --git a/.mailmap b/.mailmap
index cc86d0032..7e67800c2 100644
--- a/.mailmap
+++ b/.mailmap
@@ -46,3 +46,10 @@ Nick Zitzmann <nickzman@gmail.com>
Kees Dekker <kees.dekker@infor.com>
Max Savenkov <max.savenkov@gmail.com>
Daniel Jelinski <daniel.jelinski@thomsonreuters.com> <30433125+djelinski@users.noreply.github.com>
+Amit Katyal <amkatyal@cisco.com>
+Giorgos Oikonomou <giorgos.n.oikonomou@gmail.com>
+Evgeny Grin <k2k@narod.ru>
+Peter Pih <railsnewbie257@gmail.com>
+Anton Malov <malov.anton@gmail.com>
+Marquis de Muesli <marquis.de.muesli@gmail.com>
+Kyohei Kadota <lufia@lufia.org>
diff --git a/.travis.yml b/.travis.yml
index 76bd6114f..b468c6f61 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,7 +4,7 @@ cache:
directories:
- $HOME/wolfssl-4.0.0-stable
- $HOME/mesalink-1.0.0
- - $HOME/nghttp2-1.34.0
+ - $HOME/nghttp2-1.39.2
env:
global:
@@ -101,6 +101,34 @@ matrix:
compiler: gcc
dist: xenial
env:
+ - T=novalgrind BORINGSSL=yes QUICHE="yes" C="--with-ssl=$HOME/boringssl --with-quiche=$HOME/quiche/target/release --enable-alt-svc" LD_LIBRARY_PATH=/home/travis/boringssl/lib:$HOME/quiche/target/release:/usr/local/lib
+ - OVERRIDE_CC="CC=gcc-8" OVERRIDE_CXX="CXX=g++-8"
+ addons:
+ apt:
+ sources:
+ - *common_sources
+ packages:
+ - *common_packages
+ - libpsl-dev
+ - libbrotli-dev
+ #- os: linux
+ # compiler: gcc
+ # dist: xenial
+ # env:
+ # - T=novalgrind NGTCP2=yes C="--with-ssl=$HOME/ngbuild --with-ngtcp2=$HOME/ngbuild --with-nghttp3=$HOME/ngbuild --enable-alt-svc" NOTESTS=
+ # - OVERRIDE_CC="CC=gcc-8" OVERRIDE_CXX="CXX=g++-8"
+ # addons:
+ # apt:
+ # sources:
+ # - *common_sources
+ # packages:
+ # - *common_packages
+ # - libpsl-dev
+ # - libbrotli-dev
+ - os: linux
+ compiler: gcc
+ dist: xenial
+ env:
- T=debug-wolfssl C="--with-wolfssl --without-ssl"
- OVERRIDE_CC="CC=gcc-8" OVERRIDE_CXX="CXX=g++-8"
addons:
@@ -394,6 +422,28 @@ install:
before_script:
- ./buildconf
- |
+ if [ "$NGTCP2" = yes ]; then
+ (cd $HOME &&
+ git clone --depth 1 -b openssl-quic-draft-22 https://github.com/tatsuhiro-t/openssl possl &&
+ cd possl &&
+ ./config enable-tls1_3 --prefix=$HOME/ngbuild &&
+ make && make install_sw &&
+
+ cd .. &&
+ git clone --depth 1 https://github.com/ngtcp2/nghttp3
+ cd nghttp3 &&
+ autoreconf -i &&
+ ./configure --prefix=$HOME/ngbuild --enable-lib-only &&
+ make && make install &&
+
+ cd .. &&
+ git clone --depth 1 -b draft-22 https://github.com/ngtcp2/ngtcp2 &&
+ cd ngtcp2 &&
+ autoreconf -i &&
+ ./configure PKG_CONFIG_PATH=$HOME/ngbuild/lib/pkgconfig LDFLAGS="-Wl,-rpath,$HOME/ngbuild/lib" --prefix=$HOME/ngbuild &&
+ make && make install)
+ fi
+ - |
if [ "$TRAVIS_OS_NAME" = linux -a "$BORINGSSL" ]; then
(cd $HOME &&
git clone --depth=1 https://boringssl.googlesource.com/boringssl &&
@@ -405,12 +455,25 @@ before_script:
cd .. &&
mkdir lib &&
cd lib &&
- ln -s ../build/crypto/libcrypto.so . &&
- ln -s ../build/ssl/libssl.so . &&
+ cp ../build/crypto/libcrypto.so . &&
+ cp ../build/ssl/libssl.so . &&
echo "BoringSSL lib dir: "`pwd` &&
+ cd ../build &&
+ make clean && rm -f CMakeCache.txt &&
+ CXX="g++" CC="gcc" cmake -DCMAKE_POSITION_INDEPENDENT_CODE=on .. &&
+ make &&
export LIBS=-lpthread )
fi
- |
+ if [ "$TRAVIS_OS_NAME" = linux -a "$QUICHE" ]; then
+ (cd $HOME &&
+ git clone --depth=1 https://github.com/cloudflare/quiche.git &&
+ curl https://sh.rustup.rs -sSf | sh -s -- -y &&
+ source $HOME/.cargo/env &&
+ cd quiche &&
+ QUICHE_BSSL_PATH=$HOME/boringssl cargo build -v --release --features pkg-config-meta)
+ fi
+ - |
if [ $TRAVIS_OS_NAME = linux ]; then
if [ ! -e $HOME/wolfssl-4.0.0-stable/Makefile ]; then
(cd $HOME && \
@@ -439,11 +502,11 @@ before_script:
fi
- |
if [ $TRAVIS_OS_NAME = linux ]; then
- if [ ! -e $HOME/nghttp2-1.34.0/Makefile ]; then
+ if [ ! -e $HOME/nghttp2-1.39.2/Makefile ]; then
(cd $HOME && \
- curl -L https://github.com/nghttp2/nghttp2/releases/download/v1.34.0/nghttp2-1.34.0.tar.gz |
+ curl -L https://github.com/nghttp2/nghttp2/releases/download/v1.39.2/nghttp2-1.39.2.tar.gz |
tar xzf - && \
- cd nghttp2-1.34.0 && \
+ cd nghttp2-1.39.2 && \
CXX="g++-8" CC="gcc-8" CFLAGS="" LDFLAGS="" LIBS="" ./configure --disable-threads --enable-app && \
make)
fi
@@ -452,7 +515,7 @@ before_script:
if [ $TRAVIS_OS_NAME = linux ]; then
(cd $HOME/wolfssl-4.0.0-stable && sudo make install)
(cd $HOME/mesalink-1.0.0 && sudo make install)
- (cd $HOME/nghttp2-1.34.0 && sudo make install)
+ (cd $HOME/nghttp2-1.39.2 && sudo make install)
fi
script:
@@ -463,7 +526,7 @@ script:
make
make TFLAGS=-n test-nonflaky
make "TFLAGS=-n -e" test-nonflaky
- tests="1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 200 201 202 300 301 302 500 501 502 503 504 506 507 508 509 510 511 512 513 514 515 516 517 518 519 700 701 702 800 801 802 803 900 901 902 903 1000 1001 1002 1004 1100 1101 1200 1201 1302 1303 1304 1305 1306 1308 1400 1401 1402 1404 1450 1451 1452 1502 1507 1508 1600 1602 1603 1605 1650 1651 1652 1653 1654 2001 2100 3000"
+ tests="1 200 300 500 700 800 900 1000 1100 1200 1302 1400 1502 3000"
make "TFLAGS=-n -t $tests" test-nonflaky
coveralls --gcov /usr/bin/gcov-8 --gcov-options '\-lp' -i src -e lib -e tests -e docs -b $PWD/src
coveralls --gcov /usr/bin/gcov-8 --gcov-options '\-lp' -e src -i lib -e tests -e docs -b $PWD/lib
@@ -494,7 +557,7 @@ script:
- |
set -eo pipefail
if [ "$T" = "novalgrind" ]; then
- ./configure $C
+ ./configure --enable-werror $C
make && make examples
make TFLAGS=-n test-nonflaky
fi
diff --git a/CMake/FindGSS.cmake b/CMake/FindGSS.cmake
index 44bcfd063..a2f150cda 100644
--- a/CMake/FindGSS.cmake
+++ b/CMake/FindGSS.cmake
@@ -62,6 +62,7 @@ if(NOT _GSS_FOUND) #not found by pkg-config. Let's take more traditional approac
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
@@ -84,6 +85,7 @@ if(NOT _GSS_FOUND) #not found by pkg-config. Let's take more traditional approac
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}")
@@ -110,6 +112,7 @@ if(NOT _GSS_FOUND) #not found by pkg-config. Let's take more traditional approac
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.
@@ -121,6 +124,7 @@ if(NOT _GSS_FOUND) #not found by pkg-config. Let's take more traditional approac
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.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7525c33d1..27f99295d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -260,7 +260,7 @@ if(ENABLE_THREADED_RESOLVER)
endif()
# Check for all needed libraries
-check_library_exists_concat("dl" dlopen HAVE_LIBDL)
+check_library_exists_concat("${CMAKE_DL_LIBS}" dlopen HAVE_LIBDL)
check_library_exists_concat("socket" connect HAVE_LIBSOCKET)
check_library_exists("c" gethostbyname "" NOT_NEED_LIBNSL)
@@ -860,6 +860,7 @@ check_symbol_exists(strlcat "${CURL_INCLUDES}" HAVE_STRLCAT)
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(usleep "${CURL_INCLUDES}" HAVE_USLEEP)
check_symbol_exists(utime "${CURL_INCLUDES}" HAVE_UTIME)
check_symbol_exists(gmtime_r "${CURL_INCLUDES}" HAVE_GMTIME_R)
check_symbol_exists(localtime_r "${CURL_INCLUDES}" HAVE_LOCALTIME_R)
diff --git a/Makefile.am b/Makefile.am
index 2cdd8a898..a61f9e6ce 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -156,9 +156,20 @@ VC_DIST = projects/README \
WINBUILD_DIST = winbuild/BUILD.WINDOWS.txt winbuild/gen_resp_file.bat \
winbuild/MakefileBuild.vc winbuild/Makefile.vc
+PLAN9_DIST = plan9/include/mkfile \
+ plan9/include/mkfile \
+ plan9/mkfile.proto \
+ plan9/mkfile \
+ plan9/BUILD.PLAN9.txt \
+ plan9/lib/mkfile.inc \
+ plan9/lib/mkfile \
+ plan9/src/mkfile.inc \
+ plan9/src/mkfile
+
EXTRA_DIST = CHANGES COPYING maketgz Makefile.dist gnurl-config.in \
RELEASE-NOTES buildconf libgnurl.pc.in MacOSX-Framework \
- scripts/updatemanpages.pl $(CMAKE_DIST) $(VC_DIST) $(WINBUILD_DIST) \
+ scripts/updatemanpages.pl $(CMAKE_DIST) \
+ $(VC_DIST) $(WINBUILD_DIST) $(PLAN9_DIST) \
lib/libcurl.vers.in buildconf.bat scripts/coverage.sh scripts/completion.pl
CLEANFILES = $(VC6_LIBDSP) $(VC6_SRCDSP) $(VC7_LIBVCPROJ) $(VC7_SRCVCPROJ) \
diff --git a/RELEASE-NOTES b/RELEASE-NOTES
index bedfa2c56..cd13827f5 100644
--- a/RELEASE-NOTES
+++ b/RELEASE-NOTES
@@ -1,14 +1,103 @@
-curl and libcurl 7.65.3
+curl and libcurl 7.66.0
- Public curl releases: 184
- Command line options: 221
- curl_easy_setopt() options: 268
- Public functions in libcurl: 80
+ Public curl releases: 185
+ Command line options: 225
+ curl_easy_setopt() options: 269
+ Public functions in libcurl: 81
Contributors: 1991
+This release includes the following changes:
+
+ o CURLINFO_RETRY_AFTER: parse the Retry-After header value [35]
+ o HTTP3: initial (experimental still not working) support [5]
+ o curl: --sasl-authzid added to support CURLOPT_SASL_AUTHZID from the tool [27]
+ o curl: support parallel transfers with -Z [4]
+ o curl_multi_poll: a sister to curl_multi_wait() that waits more [28]
+ o sasl: Implement SASL authorisation identity via CURLOPT_SASL_AUTHZID [27]
+
This release includes the following bugfixes:
- o progress: make the progress meter appear again [1]
+ o CVE-2019-5481: FTP-KRB double-free [64]
+ o CVE-2019-5482: TFTP small blocksize heap buffer overflow [65]
+ o CI: remove duplicate configure flag for LGTM.com
+ o CMake: remove needless newlines at end of gss variables
+ o CMake: use platform dependent name for dlopen() library [62]
+ o CURLINFO docs: mention that in redirects times are added [55]
+ o CURLOPT_ALTSVC.3: use a "" file name to not load from a file
+ o CURLOPT_ALTSVC_CTRL.3: remove CURLALTSVC_ALTUSED
+ o CURLOPT_HEADERFUNCTION.3: clarify [54]
+ o CURLOPT_HTTP_VERSION: seting this to 3 forces HTTP/3 use directly [33]
+ o CURLOPT_READFUNCTION.3: provide inline example
+ o CURLOPT_SSL_VERIFYHOST: treat the value 1 as 2 [51]
+ o Curl_addr2string: take an addrlen argument too [61]
+ o Curl_fillreadbuffer: avoid double-free trailer buf on error [66]
+ o HTTP: use chunked Transfer-Encoding for HTTP_POST if size unknown [10]
+ o alt-svc: add protocol version selection masking [31]
+ o alt-svc: fix removal of expired cache entry [30]
+ o alt-svc: make it use h3-22 with ngtcp2 as well
+ o alt-svc: more liberal ALPN name parsing [17]
+ o alt-svc: send Alt-Used: in redirected requests [32]
+ o alt-svc: with quiche, use the quiche h3 alpn string [16]
+ o appveyor: pass on -k to make
+ o asyn-thread: create a socketpair to wait on [14]
+ o build-openssl: fix build with Visual Studio 2019 [45]
+ o cleanup: move functions out of url.c and make them static [58]
+ o cleanup: remove the 'numsocks' argument used in many places [25]
+ o configure: avoid undefined check_for_ca_bundle [37]
+ o curl.h: add CURL_HTTP_VERSION_3 to the version enum
+ o curl.h: fix outdated comment [23]
+ o curl: cap the maximum allowed values for retry time arguments [13]
+ o curl: handle a libcurl build without netrc support [63]
+ o curl: make use of CURLINFO_RETRY_AFTER when retrying [35]
+ o curl: remove outdated comment [24]
+ o curl: use .curlrc (with a dot) on Windows [52]
+ o curl: use CURLINFO_PROTOCOL to check for HTTP(s)
+ o curl_global_init_mem.3: mention it was added in 7.12.0
+ o curl_version: bump string buffer size to 250
+ o curl_version_info.3: mentioned ALTSVC and HTTP3
+ o curl_version_info: offer quic (and h3) library info [38]
+ o curl_version_info: provide nghttp2 details [2]
+ o defines: avoid underscore-prefixed defines [47]
+ o docs/ALTSVC: remove what works and the experimental explanation [34]
+ o docs/EXPERIMENTAL: explain what it means and what's experimental now
+ o docs/MANUAL.md: converted to markdown from plain text [3]
+ o docs/examples/curlx: fix errors [48]
+ o docs: s/curl_debug/curl_dbg_debug in comments and docs [36]
+ o easy: resize receive buffer on easy handle reset [9]
+ o examples: Avoid reserved names in hiperfifo examples [8]
+ o examples: add http3.c, altsvc.c and http3-present.c [40]
+ o getenv: support up to 4K environment variable contents on windows [21]
+ o http09: disable HTTP/0.9 by default in both tool and library [29]
+ o http2: when marked for closure and wanted to close == OK [56]
+ o http2_recv: trigger another read when the last data is returned [11]
+ o http: fix use of credentials from URL when using HTTP proxy [44]
+ o http_negotiate: improve handling of gss_init_sec_context() failures [18]
+ o md4: Use our own MD4 when no crypto libraries are available [15]
+ o multi: call detach_connection before Curl_disconnect [6]
+ o netrc: make the code try ".netrc" on Windows [52]
+ o nss: use TLSv1.3 as default if supported [39]
+ o openssl: build warning free with boringssl [50]
+ o openssl: use SSL_CTX_set_<min|max>_proto_version() when available [68]
+ o plan9: add support for running on Plan 9 [22]
+ o progress: reset download/uploaded counter between transfers [12]
+ o readwrite_data: repair setting the TIMER_STARTTRANSFER stamp [26]
+ o scp: fix directory name length used in memcpy [46]
+ o smb: init *msg to NULL in smb_send_and_recv() [60]
+ o smtp: check for and bail out on too short EHLO response [59]
+ o source: remove names from source comments [1]
+ o spnego_sspi: add typecast to fix build warning [49]
+ o src/makefile: fix uncompressed hugehelp.c generation [19]
+ o ssh-libssh: do not specify O_APPEND when not in append mode [7]
+ o ssh: move code into vssh for SSH backends [53]
+ o sspi: fix memory leaks [67]
+ o tests: Replace outdated test case numbering documentation [43]
+ o tftp: return error when packet is too small for options
+ o timediff: make it 64 bit (if possible) even with 32 bit time_t [20]
+ o travis: reduce number of torture tests in 'coverage' [42]
+ o url: make use of new HTTP version if alt-svc has one [16]
+ o urlapi: verify the IPv6 numerical address [69]
+ o urldata: avoid 'generic', use dedicated pointers [57]
+ o vauth: Use CURLE_AUTH_ERROR for auth function errors [41]
This release includes the following known bugs:
@@ -17,11 +106,89 @@ This release includes the following known bugs:
This release would not have looked like this without help, code, reports and
advice from friends like these:
- Chih-Hsuan Yen, Daniel Stenberg,
- (2 contributors)
+ Alessandro Ghedini, Alex Mayorga, Amit Katyal, Balazs Kovacsics,
+ Brad Spencer, Brandon Dong, Carlo Marcelo Arenas Belón, Christopher Head,
+ Clément Notin, codesniffer13 on github, Daniel Gustafsson, Daniel Stenberg,
+ Dominik Hölzl, Eric Wong, Felix Hädicke, Gergely Nagy, Gisle Vanem,
+ Igor Makarov, Ironbars13 on github, Jason Lee, Jeremy Lainé,
+ Jonathan Cardoso Machado, Junho Choi, Kamil Dudka, Kyle Abramowitz,
+ Kyohei Kadota, Lance Ware, Marcel Raad, Max Dymond, Michael Lee,
+ Michal Čaplygin, migueljcrum on github, Mike Crowe, niallor on github,
+ osabc on github, patnyb on github, Patrick Monnerat, Peter Wu, Ray Satiro,
+ Rolf Eike Beer, Steve Holme, Tatsuhiro Tsujikawa, The Infinnovation team,
+ Thomas Vegas, Tom van der Woerdt, Yiming Jing,
+ (46 contributors)
Thanks! (and sorry if I forgot to mention someone)
References to bug reports and discussions on issues:
- [1] = https://curl.haxx.se/bug/?i=4122
+ [1] = https://curl.haxx.se/bug/?i=4129
+ [2] = https://curl.haxx.se/bug/?i=4121
+ [3] = https://curl.haxx.se/bug/?i=4131
+ [4] = https://curl.haxx.se/bug/?i=3804
+ [5] = https://curl.haxx.se/bug/?i=3500
+ [6] = https://curl.haxx.se/bug/?i=4144
+ [7] = https://curl.haxx.se/bug/?i=4147
+ [8] = https://curl.haxx.se/bug/?i=4153
+ [9] = https://curl.haxx.se/bug/?i=4143
+ [10] = https://curl.haxx.se/bug/?i=4138
+ [11] = https://curl.haxx.se/bug/?i=4043
+ [12] = https://curl.haxx.se/bug/?i=4084
+ [13] = https://curl.haxx.se/bug/?i=4166
+ [14] = https://curl.haxx.se/bug/?i=4157
+ [15] = https://curl.haxx.se/bug/?i=3780
+ [16] = https://curl.haxx.se/bug/?i=4183
+ [17] = https://curl.haxx.se/bug/?i=4182
+ [18] = https://curl.haxx.se/bug/?i=3992
+ [19] = https://curl.haxx.se/bug/?i=4176
+ [20] = https://curl.haxx.se/bug/?i=4165
+ [21] = https://curl.haxx.se/bug/?i=4174
+ [22] = https://curl.haxx.se/bug/?i=3701
+ [23] = https://curl.haxx.se/bug/?i=4167
+ [24] = https://curl.haxx.se/bug/?i=4172
+ [25] = https://curl.haxx.se/bug/?i=4169
+ [26] = https://curl.haxx.se/bug/?i=4136
+ [27] = https://curl.haxx.se/bug/?i=3653
+ [28] = https://curl.haxx.se/bug/?i=4163
+ [29] = https://curl.haxx.se/bug/?i=4191
+ [30] = https://curl.haxx.se/bug/?i=4192
+ [31] = https://curl.haxx.se/bug/?i=4201
+ [32] = https://curl.haxx.se/bug/?i=4199
+ [33] = https://curl.haxx.se/bug/?i=4197
+ [34] = https://curl.haxx.se/bug/?i=4198
+ [35] = https://curl.haxx.se/bug/?i=3794
+ [36] = https://curl.haxx.se/bug/?i=3794
+ [37] = https://curl.haxx.se/bug/?i=4213
+ [38] = https://curl.haxx.se/bug/?i=4216
+ [39] = https://curl.haxx.se/bug/?i=4187
+ [40] = https://curl.haxx.se/bug/?i=4221
+ [41] = https://curl.haxx.se/bug/?i=3848
+ [42] = https://curl.haxx.se/bug/?i=4223
+ [43] = https://curl.haxx.se/bug/?i=4227
+ [44] = https://curl.haxx.se/bug/?i=4228
+ [45] = https://curl.haxx.se/bug/?i=4188
+ [46] = https://curl.haxx.se/bug/?i=4258
+ [47] = https://curl.haxx.se/bug/?i=4254
+ [48] = https://curl.haxx.se/bug/?i=4248
+ [49] = https://curl.haxx.se/bug/?i=4245
+ [50] = https://curl.haxx.se/bug/?i=4244
+ [51] = https://curl.haxx.se/bug/?i=4241
+ [52] = https://curl.haxx.se/bug/?i=4230
+ [53] = https://curl.haxx.se/bug/?i=4235
+ [54] = https://curl.haxx.se/bug/?i=4273
+ [55] = https://curl.haxx.se/bug/?i=4250
+ [56] = https://curl.haxx.se/bug/?i=4267
+ [57] = https://curl.haxx.se/bug/?i=4290
+ [58] = https://curl.haxx.se/bug/?i=4289
+ [59] = https://curl.haxx.se/bug/?i=4287
+ [60] = https://curl.haxx.se/bug/?i=4286
+ [61] = https://curl.haxx.se/bug/?i=4283
+ [62] = https://curl.haxx.se/bug/?i=4279
+ [63] = https://curl.haxx.se/bug/?i=4302
+ [64] = https://curl.haxx.se/docs/CVE-2019-5481.html
+ [65] = https://curl.haxx.se/docs/CVE-2019-5482.html
+ [66] = https://curl.haxx.se/bug/?i=4307
+ [67] = https://curl.haxx.se/bug/?i=4299
+ [68] = https://curl.haxx.se/bug/?i=4304
+ [69] = https://curl.haxx.se/bug/?i=4315
diff --git a/appveyor.yml b/appveyor.yml
index a809fb917..d54b500fe 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -80,6 +80,7 @@ environment:
DISABLED_TESTS: "!198"
COMPILER_PATH: "C:\\mingw-w64\\x86_64-8.1.0-posix-seh-rt_v6-rev0\\mingw64\\bin"
MSYS2_ARG_CONV_EXCL: "/*"
+ BUILD_OPT: -k
- APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2015"
BUILD_SYSTEM: CMake
PRJ_GEN: "MSYS Makefiles"
@@ -92,6 +93,7 @@ environment:
DISABLED_TESTS: ""
COMPILER_PATH: "C:\\MinGW\\bin"
MSYS2_ARG_CONV_EXCL: "/*"
+ BUILD_OPT: -k
- APPVEYOR_BUILD_WORKER_IMAGE: "Visual Studio 2017"
BUILD_SYSTEM: VisualStudioSolution
PRJ_CFG: "DLL Debug - DLL Windows SSPI - DLL WinIDN"
@@ -118,7 +120,7 @@ build_script:
-DCMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG=""
-DCMAKE_INSTALL_PREFIX="C:/CURL"
-DCMAKE_BUILD_TYPE=%PRJ_CFG% &&
- cmake --build . --config %PRJ_CFG% --clean-first ) else (
+ cmake --build . --config %PRJ_CFG% --clean-first -- %BUILD_OPT%) else (
if %BUILD_SYSTEM%==VisualStudioSolution (
cd projects &&
.\\generate.bat %VC_VERSION% &&
diff --git a/configure.ac b/configure.ac
index 3db063459..a7585da9f 100755
--- a/configure.ac
+++ b/configure.ac
@@ -2720,7 +2720,7 @@ dnl **********************************************************************
dnl Check for the CA bundle
dnl **********************************************************************
-if test "$check_for_ca_bundle" -gt 0; then
+if test -n "$check_for_ca_bundle"; then
CURL_CHECK_CA_BUNDLE
fi
@@ -3435,6 +3435,331 @@ if test X"$want_h2" != Xno; then
fi
dnl **********************************************************************
+dnl Check for ngtcp2 (QUIC)
+dnl **********************************************************************
+
+OPT_TCP2="yes"
+curl_h3_msg="disabled (--with-ngtcp2, --with-quiche)"
+
+if test "x$disable_http" = "xyes"; then
+ # without HTTP, ngtcp2 is no use
+ OPT_TCP2="no"
+fi
+
+AC_ARG_WITH(ngtcp2,
+AC_HELP_STRING([--with-ngtcp2=PATH],[Enable ngtcp2 usage])
+AC_HELP_STRING([--without-ngtcp2],[Disable ngtcp2 usage]),
+ [OPT_TCP2=$withval])
+case "$OPT_TCP2" in
+ no)
+ dnl --without-ngtcp2 option used
+ want_tcp2="no"
+ ;;
+ yes)
+ dnl --with-ngtcp2 option used without path
+ want_tcp2="default"
+ want_tcp2_path=""
+ ;;
+ *)
+ dnl --with-ngtcp2 option used with path
+ want_tcp2="yes"
+ want_tcp2_path="$withval/lib/pkgconfig"
+ ;;
+esac
+
+curl_tcp2_msg="disabled (--with-ngtcp2)"
+if test X"$want_tcp2" != Xno; then
+ dnl backup the pre-ngtcp2 variables
+ CLEANLDFLAGS="$LDFLAGS"
+ CLEANCPPFLAGS="$CPPFLAGS"
+ CLEANLIBS="$LIBS"
+
+ CURL_CHECK_PKGCONFIG(libngtcp2, $want_tcp2_path)
+
+ if test "$PKGCONFIG" != "no" ; then
+ LIB_TCP2=`CURL_EXPORT_PCDIR([$want_tcp2_path])
+ $PKGCONFIG --libs-only-l libngtcp2`
+ AC_MSG_NOTICE([-l is $LIB_TCP2])
+
+ CPP_TCP2=`CURL_EXPORT_PCDIR([$want_tcp2_path]) dnl
+ $PKGCONFIG --cflags-only-I libngtcp2`
+ AC_MSG_NOTICE([-I is $CPP_TCP2])
+
+ LD_TCP2=`CURL_EXPORT_PCDIR([$want_tcp2_path])
+ $PKGCONFIG --libs-only-L libngtcp2`
+ AC_MSG_NOTICE([-L is $LD_TCP2])
+
+ LDFLAGS="$LDFLAGS $LD_TCP2"
+ CPPFLAGS="$CPPFLAGS $CPP_TCP2"
+ LIBS="$LIB_TCP2 $LIBS"
+
+ if test "x$cross_compiling" != "xyes"; then
+ DIR_TCP2=`echo $LD_TCP2 | $SED -e 's/-L//'`
+ fi
+ AC_CHECK_LIB(ngtcp2, ngtcp2_conn_client_new,
+ [
+ AC_CHECK_HEADERS(ngtcp2/ngtcp2.h,
+ NGTCP2_ENABLED=1
+ AC_DEFINE(USE_NGTCP2, 1, [if ngtcp2 is in use])
+ AC_SUBST(USE_NGTCP2, [1])
+ CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$DIR_TCP2"
+ export CURL_LIBRARY_PATH
+ AC_MSG_NOTICE([Added $DIR_TCP2 to CURL_LIBRARY_PATH])
+ )
+ ],
+ dnl not found, revert back to clean variables
+ LDFLAGS=$CLEANLDFLAGS
+ CPPFLAGS=$CLEANCPPFLAGS
+ LIBS=$CLEANLIBS
+ )
+
+ else
+ dnl no ngtcp2 pkg-config found, deal with it
+ if test X"$want_tcp2" != Xdefault; then
+ dnl To avoid link errors, we do not allow --with-ngtcp2 without
+ dnl a pkgconfig file
+ AC_MSG_ERROR([--with-ngtcp2 was specified but could not find ngtcp2 pkg-config file.])
+ fi
+ fi
+
+fi
+
+if test "x$NGTCP2_ENABLED" = "x1"; then
+ dnl backup the pre-ngtcp2_crypto_openssl variables
+ CLEANLDFLAGS="$LDFLAGS"
+ CLEANCPPFLAGS="$CPPFLAGS"
+ CLEANLIBS="$LIBS"
+
+ CURL_CHECK_PKGCONFIG(libngtcp2_crypto_openssl, $want_tcp2_path)
+
+ if test "$PKGCONFIG" != "no" ; then
+ LIB_NGTCP2_CRYPTO_OPENSSL=`CURL_EXPORT_PCDIR([$want_tcp2_path])
+ $PKGCONFIG --libs-only-l libngtcp2_crypto_openssl`
+ AC_MSG_NOTICE([-l is $LIB_NGTCP2_CRYPTO_OPENSSL])
+
+ CPP_NGTCP2_CRYPTO_OPENSSL=`CURL_EXPORT_PCDIR([$want_tcp2_path]) dnl
+ $PKGCONFIG --cflags-only-I libngtcp2_crypto_openssl`
+ AC_MSG_NOTICE([-I is $CPP_NGTCP2_CRYPTO_OPENSSL])
+
+ LD_NGTCP2_CRYPTO_OPENSSL=`CURL_EXPORT_PCDIR([$want_tcp2_path])
+ $PKGCONFIG --libs-only-L libngtcp2_crypto_openssl`
+ AC_MSG_NOTICE([-L is $LD_NGTCP2_CRYPTO_OPENSSL])
+
+ LDFLAGS="$LDFLAGS $LD_NGTCP2_CRYPTO_OPENSSL"
+ CPPFLAGS="$CPPFLAGS $CPP_NGTCP2_CRYPTO_OPENSSL"
+ LIBS="$LIB_NGTCP2_CRYPTO_OPENSSL $LIBS"
+
+ if test "x$cross_compiling" != "xyes"; then
+ DIR_NGTCP2_CRYPTO_OPENSSL=`echo $LD_NGTCP2_CRYPTO_OPENSSL | $SED -e 's/-L//'`
+ fi
+ AC_CHECK_LIB(ngtcp2_crypto_openssl, ngtcp2_crypto_ctx_initial,
+ [
+ AC_CHECK_HEADERS(ngtcp2/ngtcp2_crypto.h,
+ NGTCP2_ENABLED=1
+ AC_DEFINE(USE_NGTCP2_CRYPTO_OPENSSL, 1, [if ngtcp2_crypto_openssl is in use])
+ AC_SUBST(USE_NGTCP2_CRYPTO_OPENSSL, [1])
+ CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$DIR_NGTCP2_CRYPTO_OPENSSL"
+ export CURL_LIBRARY_PATH
+ AC_MSG_NOTICE([Added $DIR_NGTCP2_CRYPTO_OPENSSL to CURL_LIBRARY_PATH])
+ )
+ ],
+ dnl not found, revert back to clean variables
+ LDFLAGS=$CLEANLDFLAGS
+ CPPFLAGS=$CLEANCPPFLAGS
+ LIBS=$CLEANLIBS
+ )
+
+ else
+ dnl no ngtcp2_crypto_openssl pkg-config found, deal with it
+ if test X"$want_tcp2" != Xdefault; then
+ dnl To avoid link errors, we do not allow --with-ngtcp2 without
+ dnl a pkgconfig file
+ AC_MSG_ERROR([--with-ngtcp2 was specified but could not find ngtcp2_crypto_openssl pkg-config file.])
+ fi
+ fi
+fi
+
+dnl **********************************************************************
+dnl Check for nghttp3 (HTTP/3 with ngtcp2)
+dnl **********************************************************************
+
+OPT_NGHTTP3="yes"
+
+if test "x$NGTCP2_ENABLED" = "x"; then
+ # without ngtcp2, nghttp3 is of no use for us
+ OPT_NGHTTP3="no"
+fi
+
+AC_ARG_WITH(nghttp3,
+AC_HELP_STRING([--with-nghttp3=PATH],[Enable nghttp3 usage])
+AC_HELP_STRING([--without-nghttp3],[Disable nghttp3 usage]),
+ [OPT_NGHTTP3=$withval])
+case "$OPT_NGHTTP3" in
+ no)
+ dnl --without-nghttp3 option used
+ want_nghttp3="no"
+ ;;
+ yes)
+ dnl --with-nghttp3 option used without path
+ want_nghttp3="default"
+ want_nghttp3_path=""
+ ;;
+ *)
+ dnl --with-nghttp3 option used with path
+ want_nghttp3="yes"
+ want_nghttp3_path="$withval/lib/pkgconfig"
+ ;;
+esac
+
+curl_http3_msg="disabled (--with-nghttp3)"
+if test X"$want_nghttp3" != Xno; then
+ dnl backup the pre-nghttp3 variables
+ CLEANLDFLAGS="$LDFLAGS"
+ CLEANCPPFLAGS="$CPPFLAGS"
+ CLEANLIBS="$LIBS"
+
+ CURL_CHECK_PKGCONFIG(libnghttp3, $want_nghttp3_path)
+
+ if test "$PKGCONFIG" != "no" ; then
+ LIB_NGHTTP3=`CURL_EXPORT_PCDIR([$want_nghttp3_path])
+ $PKGCONFIG --libs-only-l libnghttp3`
+ AC_MSG_NOTICE([-l is $LIB_NGHTTP3])
+
+ CPP_NGHTTP3=`CURL_EXPORT_PCDIR([$want_nghttp3_path]) dnl
+ $PKGCONFIG --cflags-only-I libnghttp3`
+ AC_MSG_NOTICE([-I is $CPP_NGHTTP3])
+
+ LD_NGHTTP3=`CURL_EXPORT_PCDIR([$want_nghttp3_path])
+ $PKGCONFIG --libs-only-L libnghttp3`
+ AC_MSG_NOTICE([-L is $LD_NGHTTP3])
+
+ LDFLAGS="$LDFLAGS $LD_NGHTTP3"
+ CPPFLAGS="$CPPFLAGS $CPP_NGHTTP3"
+ LIBS="$LIB_NGHTTP3 $LIBS"
+
+ if test "x$cross_compiling" != "xyes"; then
+ DIR_NGHTTP3=`echo $LD_NGHTTP3 | $SED -e 's/-L//'`
+ fi
+ AC_CHECK_LIB(nghttp3, nghttp3_conn_client_new,
+ [
+ AC_CHECK_HEADERS(nghttp3/nghttp3.h,
+ curl_h3_msg="enabled (ngtcp2 + nghttp3)"
+ NGHTTP3_ENABLED=1
+ AC_DEFINE(USE_NGHTTP3, 1, [if nghttp3 is in use])
+ AC_SUBST(USE_NGHTTP3, [1])
+ CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$DIR_NGHTTP3"
+ export CURL_LIBRARY_PATH
+ AC_MSG_NOTICE([Added $DIR_NGHTTP3 to CURL_LIBRARY_PATH])
+ experimental="$experimental HTTP3"
+ )
+ ],
+ dnl not found, revert back to clean variables
+ LDFLAGS=$CLEANLDFLAGS
+ CPPFLAGS=$CLEANCPPFLAGS
+ LIBS=$CLEANLIBS
+ )
+
+ else
+ dnl no nghttp3 pkg-config found, deal with it
+ if test X"$want_nghttp3" != Xdefault; then
+ dnl To avoid link errors, we do not allow --with-nghttp3 without
+ dnl a pkgconfig file
+ AC_MSG_ERROR([--with-nghttp3 was specified but could not find nghttp3 pkg-config file.])
+ fi
+ fi
+
+fi
+
+dnl **********************************************************************
+dnl Check for quiche (QUIC)
+dnl **********************************************************************
+
+OPT_QUICHE="yes"
+
+if test "x$disable_http" = "xyes" -o "x$USE_NGTCP" = "x1"; then
+ # without HTTP or with ngtcp2, quiche is no use
+ OPT_QUICHE="no"
+fi
+
+AC_ARG_WITH(quiche,
+AC_HELP_STRING([--with-quiche=PATH],[Enable quiche usage])
+AC_HELP_STRING([--without-quiche],[Disable quiche usage]),
+ [OPT_QUICHE=$withval])
+case "$OPT_QUICHE" in
+ no)
+ dnl --without-quiche option used
+ want_quiche="no"
+ ;;
+ yes)
+ dnl --with-quiche option used without path
+ want_quiche="default"
+ want_quiche_path=""
+ ;;
+ *)
+ dnl --with-quiche option used with path
+ want_quiche="yes"
+ want_quiche_path="$withval"
+ ;;
+esac
+
+if test X"$want_quiche" != Xno; then
+ dnl backup the pre-quiche variables
+ CLEANLDFLAGS="$LDFLAGS"
+ CLEANCPPFLAGS="$CPPFLAGS"
+ CLEANLIBS="$LIBS"
+
+ CURL_CHECK_PKGCONFIG(quiche, $want_quiche_path)
+
+ if test "$PKGCONFIG" != "no" ; then
+ LIB_QUICHE=`CURL_EXPORT_PCDIR([$want_quiche_path])
+ $PKGCONFIG --libs-only-l quiche`
+ AC_MSG_NOTICE([-l is $LIB_QUICHE])
+
+ CPP_QUICHE=`CURL_EXPORT_PCDIR([$want_quiche_path]) dnl
+ $PKGCONFIG --cflags-only-I quiche`
+ AC_MSG_NOTICE([-I is $CPP_QUICHE])
+
+ LD_QUICHE=`CURL_EXPORT_PCDIR([$want_quiche_path])
+ $PKGCONFIG --libs-only-L quiche`
+ AC_MSG_NOTICE([-L is $LD_QUICHE])
+
+ LDFLAGS="$LDFLAGS $LD_QUICHE"
+ CPPFLAGS="$CPPFLAGS $CPP_QUICHE"
+ LIBS="$LIB_QUICHE $LIBS"
+
+ if test "x$cross_compiling" != "xyes"; then
+ DIR_QUICHE=`echo $LD_QUICHE | $SED -e 's/-L//'`
+ fi
+ AC_CHECK_LIB(quiche, quiche_connect,
+ [
+ AC_CHECK_HEADERS(quiche.h,
+ experimental="$experimental HTTP3"
+ AC_MSG_NOTICE([HTTP3 support is experimental])
+ curl_h3_msg="enabled (quiche)"
+ QUICHE_ENABLED=1
+ AC_DEFINE(USE_QUICHE, 1, [if quiche is in use])
+ AC_SUBST(USE_QUICHE, [1])
+ CURL_LIBRARY_PATH="$CURL_LIBRARY_PATH:$DIR_QUICHE"
+ export CURL_LIBRARY_PATH
+ AC_MSG_NOTICE([Added $DIR_QUICHE to CURL_LIBRARY_PATH]),
+ )
+ ],
+ dnl not found, revert back to clean variables
+ LDFLAGS=$CLEANLDFLAGS
+ CPPFLAGS=$CLEANCPPFLAGS
+ LIBS=$CLEANLIBS
+ )
+ else
+ dnl no nghttp3 pkg-config found, deal with it
+ if test X"$want_quiche" != Xdefault; then
+ dnl To avoid link errors, we do not allow --with-nghttp3 without
+ dnl a pkgconfig file
+ AC_MSG_ERROR([--with-quiche was specified but could not find quiche pkg-config file.])
+ fi
+ fi
+fi
+
+dnl **********************************************************************
dnl Check for zsh completion path
dnl **********************************************************************
@@ -3768,6 +4093,7 @@ AC_CHECK_FUNCS([fnmatch \
setlocale \
setmode \
setrlimit \
+ usleep \
utime \
utimes
],[
@@ -4265,7 +4591,6 @@ AC_HELP_STRING([--disable-alt-svc],[Disable alt-svc support]),
*) AC_MSG_RESULT(yes)
curl_altsvc_msg="enabled";
enable_altsvc="yes"
- experimental="alt-svc"
;;
esac ],
AC_MSG_RESULT(no)
@@ -4273,7 +4598,7 @@ AC_HELP_STRING([--disable-alt-svc],[Disable alt-svc support]),
if test "$enable_altsvc" = "yes"; then
AC_DEFINE(USE_ALTSVC, 1, [to enable alt-svc])
- experimental="alt-svc"
+ experimental="$experimental alt-svc"
fi
dnl ************************************************************
@@ -4384,6 +4709,10 @@ if test "x$USE_NGHTTP2" = "x1"; then
SUPPORT_FEATURES="$SUPPORT_FEATURES HTTP2"
fi
+if test "x$USE_NGTCP2" = "x1" -o "x$USE_QUICHE" = "x1"; then
+ SUPPORT_FEATURES="$SUPPORT_FEATURES HTTP3"
+fi
+
if test "x$CURL_WITH_MULTI_SSL" = "x1"; then
SUPPORT_FEATURES="$SUPPORT_FEATURES MultiSSL"
fi
@@ -4575,12 +4904,13 @@ AC_MSG_NOTICE([Configured to build gnurl/libgnurl:
PSL: ${curl_psl_msg}
Alt-svc: ${curl_altsvc_msg}
HTTP2: ${curl_h2_msg}
+ HTTP3: ${curl_h3_msg}
Protocols: ${SUPPORT_PROTOCOLS}
Features: ${SUPPORT_FEATURES}
valgrind tests: ${valgrind_msg}
])
if test -n "$experimental"; then
cat >&2 << _EOF
- WARNING: $experimental is enabled but marked EXPERIMENTAL. Use with caution!
+ WARNING: $experimental enabled but marked EXPERIMENTAL. Use with caution!
_EOF
fi
diff --git a/docs/ALTSVC.md b/docs/ALTSVC.md
index 5aca1c950..48401415b 100644
--- a/docs/ALTSVC.md
+++ b/docs/ALTSVC.md
@@ -2,21 +2,6 @@
curl features **EXPERIMENTAL** support for the Alt-Svc: HTTP header.
-## Experimental
-
-Experimental support in curl means:
-
-1. Experimental features are provided to allow users to try them out and
- provide feedback on functionality and API etc before they ship and get
- "carved in stone".
-2. You must enable the feature when invoking configure as otherwise curl will
- not be built with the feature present.
-3. We strongly advice against using this feature in production.
-4. **We reserve the right to change behavior** of the feature without sticking
- to our API/ABI rules as we do for regular features, as long as it is marked
- experimental.
-5. Experimental features are clearly marked so in documentation. Beware.
-
## Enable Alt-Svc in build
`./configure --enable-alt-svc`
@@ -25,35 +10,30 @@ Experimental support in curl means:
[RFC 7838](https://tools.ietf.org/html/rfc7838)
-## What works
-
-- read alt-svc file from disk
-- write alt-svc file from disk
-- parse `Alt-Svc:` response headers, including `ma`, `clear` and `persist`.
-- replaces old entries when new alternatives are received
-- unit tests to verify most of this functionality (test 1654)
-- act on `Alt-Svc:` response headers
-- build conditionally on `configure --enable-alt-svc` only, feature marked as
- **EXPERIMENTAL**
-- implement `CURLOPT_ALTSVC_CTRL`
-- implement `CURLOPT_ALTSVC`
-- document `CURLOPT_ALTSVC_CTRL`
-- document `CURLOPT_ALTSVC`
-- document `--alt-svc`
-- add `CURL_VERSION_ALTSVC`
-- make `curl -V` show 'alt-svc' as a feature if built-in
-- support `curl --alt-svc [file]` to enable caching, using that file
-- make `tests/runtests.pl` able to filter tests on the feature `alt-svc`
-- actually use the existing in-memory alt-svc cache for outgoing connections
-- alt-svc cache expiry
-- test 355 and 356 verify curl acting on Alt-Svc, received from header and
- loaded from cache. The latter needs a debug build since it enables Alt-Svc
- for plain HTTP.
-
-## What is left
+# Alt-Svc cache file format
+
+This a text based file with one line per entry and each line consists of nine
+space separated fields.
+
+## Example
+
+ h2 quic.tech 8443 h3-22 quic.tech 8443 "20190808 06:18:37" 0 0
+
+## Fields
+
+1. The ALPN id for the source origin
+2. The host name for the source origin
+3. The port number for the source origin
+4. The ALPN id for the destination host
+5. The host name for the destination host
+6. The host number for the destination host
+7. The expiration date and time of this entry withing double quotes. The date format is "YYYYMMDD HH:MM:SS" and the time zone is GMT.
+8. Boolean (1 or 0) if "persist" was set for this entry
+9. Integer priority value (not currently used)
+
+# TODO
- handle multiple response headers, when one of them says `clear` (should
override them all)
- using `Age:` value for caching age as per spec
- `CURLALTSVC_IMMEDIATELY` support
-- `CURLALTSVC_ALTUSED` support
diff --git a/docs/DEPRECATE.md b/docs/DEPRECATE.md
index f04f0eeaa..4f4ef8ab6 100644
--- a/docs/DEPRECATE.md
+++ b/docs/DEPRECATE.md
@@ -5,21 +5,6 @@ email the curl-library mailing list as soon as possible and explain to us why
this is a problem for you and how your use case can't be satisfied properly
using a work around.
-## HTTP/0.9
-
-Supporting this is non-obvious and might even come as a surprise to some
-users. Potentially even being a security risk in some cases.
-
-### State
-
-curl 7.64.0 introduces options to disable/enable support for this protocol
-version. The default remains supported for now.
-
-### Removal
-
-The support for HTTP/0.9 will be switched to disabled by default in 6 months,
-in the September 2019 release (possibly called curl 7.68.0).
-
## PolarSSL
The polarssl TLS library has not had an update in over three years. The last
diff --git a/docs/EXPERIMENTAL.md b/docs/EXPERIMENTAL.md
new file mode 100644
index 000000000..6c33bcf53
--- /dev/null
+++ b/docs/EXPERIMENTAL.md
@@ -0,0 +1,22 @@
+# Experimental
+
+Some features and functionality in curl and libcurl are considered
+**EXPERIMENTAL**.
+
+Experimental support in curl means:
+
+1. Experimental features are provided to allow users to try them out and
+ provide feedback on functionality and API etc before they ship and get
+ "carved in stone".
+2. You must enable the feature when invoking configure as otherwise curl will
+ not be built with the feature present.
+3. We strongly advice against using this feature in production.
+4. **We reserve the right to change behavior** of the feature without sticking
+ to our API/ABI rules as we do for regular features, as long as it is marked
+ experimental.
+5. Experimental features are clearly marked so in documentation. Beware.
+
+## Experimental features right now
+
+ - HTTP/3 support and options
+ - alt-svc support and options
diff --git a/docs/HTTP3.md b/docs/HTTP3.md
new file mode 100644
index 000000000..1e9b183c4
--- /dev/null
+++ b/docs/HTTP3.md
@@ -0,0 +1,121 @@
+# HTTP3 (and QUIC)
+
+## Resources
+
+[HTTP/3 Explained](https://daniel.haxx.se/http3-explained/) - the online free
+book describing the protocols involved.
+
+[QUIC implementation](https://github.com/curl/curl/wiki/QUIC-implementation) -
+the wiki page describing the plan for how to support QUIC and HTTP/3 in curl
+and libcurl.
+
+[quicwg.org](https://quicwg.org/) - home of the official protocol drafts
+
+## QUIC libraries
+
+QUIC libraries we're experiementing with:
+
+[ngtcp2](https://github.com/ngtcp2/ngtcp2)
+
+[quiche](https://github.com/cloudflare/quiche)
+
+## Experimental!
+
+HTTP/3 and QUIC support in curl is considered **EXPERIMENTAL** until further
+notice. It needs to be enabled at build-time.
+
+Further development and tweaking of the HTTP/3 support in curl will happen in
+in the master branch using pull-requests, just like ordinary changes.
+
+# ngtcp2 version
+
+## Build
+
+Build (patched) OpenSSL
+
+ % git clone --depth 1 -b openssl-quic-draft-22 https://github.com/tatsuhiro-t/openssl
+ % cd openssl
+ % ./config enable-tls1_3 --prefix=<somewhere1>
+ % make
+ % make install_sw
+
+Build nghttp3
+
+ % cd ..
+ % git clone https://github.com/ngtcp2/nghttp3
+ % cd nghttp3
+ % autoreconf -i
+ % ./configure --prefix=<somewhere2> --enable-lib-only
+ % make
+ % make install
+
+Build ngtcp2
+
+ % cd ..
+ % git clone -b draft-22 https://github.com/ngtcp2/ngtcp2
+ % cd ngtcp2
+ % autoreconf -i
+ % ./configure PKG_CONFIG_PATH=<somewhere1>/lib/pkgconfig:<somewhere2>/lib/pkgconfig LDFLAGS="-Wl,-rpath,<somehere1>/lib" --prefix==<somewhere3>
+ % make
+ % make install
+
+Build curl
+
+ % cd ..
+ % git clone https://github.com/curl/curl
+ % cd curl
+ % ./buildconf
+ % LDFLAGS="-Wl,-rpath,<somewhere1>/lib" ./configure -with-ssl=<somewhere1> --with-nghttp3=<somewhere2> --with-ngtcp2=<somewhere3>
+ % make
+
+## Running
+
+Make sure the custom OpenSSL library is the one used at run-time, as otherwise
+you'll just get ld.so linker errors.
+
+## Invoke from command line
+
+ curl --http3 https://nghttp2.org:8443/
+
+# quiche version
+
+## build
+
+Clone quiche and BoringSSL:
+
+ % git clone --recursive https://github.com/cloudflare/quiche
+
+Build BoringSSL (it needs to be built manually so it can be reused with curl):
+
+ % cd quiche/deps/boringssl
+ % mkdir build
+ % cd build
+ % cmake -DCMAKE_POSITION_INDEPENDENT_CODE=on ..
+ % make -j`nproc`
+ % cd ..
+ % mkdir .openssl/lib -p
+ % cp build/crypto/libcrypto.a build/ssl/libssl.a .openssl/lib
+ % ln -s $PWD/include .openssl
+
+Build quiche:
+
+ % cd ../..
+ % QUICHE_BSSL_PATH=$PWD/deps/boringssl cargo build --release --features pkg-config-meta
+
+Clone and build curl:
+
+ % cd ..
+ % git clone https://github.com/curl/curl
+ % cd curl
+ % ./buildconf
+ % ./configure LDFLAGS="-Wl,-rpath,$PWD/../quiche/target/release" --with-ssl=$PWD/../quiche/deps/boringssl/.openssl --with-quiche=$PWD/../quiche/target/release
+ % make -j`nproc`
+
+## Running
+
+Make an HTTP/3 request.
+
+ % src/curl --http3 https://cloudflare-quic.com/
+ % src/curl --http3 https://facebook.com/
+ % src/curl --http3 https://quic.aiortc.org:4433/
+ % src/curl --http3 https://quic.rocks:4433/
diff --git a/docs/INTERNALS.md b/docs/INTERNALS.md
index cd004e8f4..9ae722898 100644
--- a/docs/INTERNALS.md
+++ b/docs/INTERNALS.md
@@ -773,7 +773,7 @@ Track Down Memory Leaks
Add a line in your application code:
- `curl_memdebug("dump");`
+ `curl_dbg_memdebug("dump");`
This will make the malloc debug system output a full trace of all resource
using functions to the given file name. Make sure you rebuild your program
diff --git a/docs/KNOWN_BUGS b/docs/KNOWN_BUGS
index e385ef597..5850f7fbd 100644
--- a/docs/KNOWN_BUGS
+++ b/docs/KNOWN_BUGS
@@ -13,7 +13,6 @@ problems may have been fixed or changed somewhat since this was written!
1. HTTP
1.1 CURLFORM_CONTENTLEN in an array
- 1.2 Disabling HTTP Pipelining
1.3 STARTTRANSFER time is wrong for HTTP POSTs
1.4 multipart formposts file name encoding
1.5 Expect-100 meets 417
@@ -21,7 +20,6 @@ problems may have been fixed or changed somewhat since this was written!
1.7 Deflate error after all content was received
1.8 DoH isn't used for all name resolves when enabled
1.9 HTTP/2 frames while in the connection pool kill reuse
- 1.10 Strips trailing dot from host name
1.11 CURLOPT_SEEKFUNCTION not called with CURLFORM_STREAM
2. TLS
@@ -48,6 +46,7 @@ problems may have been fixed or changed somewhat since this was written!
4.5 Improve --data-urlencode space encoding
5. Build and portability issues
+ 5.1 USE_UNIX_SOCKETS on Windows
5.2 curl-config --libs contains private details
5.3 curl compiled on OSX 10.13 failed to run on OSX 10.10
5.4 Cannot compile against a static build of OpenLDAP
@@ -98,6 +97,7 @@ problems may have been fixed or changed somewhat since this was written!
11.4 HTTP test server 'connection-monitor' problems
11.5 Connection information when using TCP Fast Open
11.6 slow connect to localhost on Windows
+ 11.7 signal-based resolver timeouts
12. LDAP and OpenLDAP
12.1 OpenLDAP hangs after returning results
@@ -121,14 +121,6 @@ problems may have been fixed or changed somewhat since this was written!
see the now closed related issue:
https://github.com/curl/curl/issues/608
-1.2 Disabling HTTP Pipelining
-
- Disabling HTTP Pipelining when there are ongoing transfers can lead to
- heap corruption and crash. https://curl.haxx.se/bug/view.cgi?id=1411
-
- Similarly, removing a handle when pipelining corrupts data:
- https://github.com/curl/curl/issues/2101
-
1.3 STARTTRANSFER time is wrong for HTTP POSTs
Wrong STARTTRANSFER timer accounting for POST requests Timer works fine with
@@ -189,42 +181,6 @@ problems may have been fixed or changed somewhat since this was written!
This is *best* fixed by adding monitoring to connections while they are kept
in the pool so that pings can be responded to appropriately.
-1.10 Strips trailing dot from host name
-
- When given a URL with a trailing dot for the host name part:
- "https://example.com./", libcurl will strip off the dot and use the name
- without a dot internally and send it dot-less in HTTP Host: headers and in
- the TLS SNI field. For the purpose of resolving the name to an address
- the hostname is used as is without any change.
-
- The HTTP part violates RFC 7230 section 5.4 but the SNI part is accordance
- with RFC 6066 section 3.
-
- URLs using these trailing dots are very rare in the wild and we have not seen
- or gotten any real-world problems with such URLs reported. The popular
- browsers seem to have stayed with not stripping the dot for both uses (thus
- they violate RFC 6066 instead of RFC 7230).
-
- Daniel took the discussion to the HTTPbis mailing list in March 2016:
- https://lists.w3.org/Archives/Public/ietf-http-wg/2016JanMar/0430.html but
- there was not major rush or interest to fix this. The impression I get is
- that most HTTP people rather not rock the boat now and instead prioritize web
- compatibility rather than to strictly adhere to these RFCs.
-
- Our current approach allows a knowing client to send a custom HTTP header
- with the dot added.
-
- In a few cases there is a difference in name resolving to IP addresses with
- a trailing dot, but it can be noted that many HTTP servers will not happily
- accept the trailing dot there unless that has been specifically configured
- to be a fine virtual host.
-
- If URLs with trailing dots for host names become more popular or even just
- used more than for just plain fun experiments, I'm sure we will have reason
- to go back and reconsider.
-
- See https://github.com/curl/curl/issues/716 for the discussion.
-
1.11 CURLOPT_SEEKFUNCTION not called with CURLFORM_STREAM
I'm using libcurl to POST form data using a FILE* with the CURLFORM_STREAM
@@ -389,6 +345,13 @@ problems may have been fixed or changed somewhat since this was written!
5. Build and portability issues
+5.1 USE_UNIX_SOCKETS on Windows
+
+ Due to incorrect CMake checks for the presense of the feature, it will never
+ be enabled for windows in a cmake build.
+
+ See https://github.com/curl/curl/issues/4040
+
5.2 curl-config --libs contains private details
"curl-config --libs" will include details set in LDFLAGS when configure is
@@ -728,6 +691,19 @@ problems may have been fixed or changed somewhat since this was written!
https://github.com/curl/curl/issues/2281
+11.7 signal-based resolver timeouts
+
+ libcurl built without an asynchronous resolver library uses alarm() to time
+ out DNS lookups. When a timeout occurs, this causes libcurl to jump from the
+ signal handler back into the library with a sigsetjmp, which effectively
+ causes libcurl to continue running within the signal handler. This is
+ non-portable and could cause problems on some platforms. A discussion on the
+ problem is available at https://curl.haxx.se/mail/lib-2008-09/0197.html
+
+ Also, alarm() provides timeout resolution only to the nearest second. alarm
+ ought to be replaced by setitimer on systems that support it.
+
+
12. LDAP and OpenLDAP
12.1 OpenLDAP hangs after returning results
diff --git a/docs/MANUAL b/docs/MANUAL
deleted file mode 100644
index 59b97427c..000000000
--- a/docs/MANUAL
+++ /dev/null
@@ -1,1058 +0,0 @@
-LATEST VERSION
-
- You always find news about what's going on as well as the latest versions
- from the curl web pages, located at:
-
- https://curl.haxx.se
-
-SIMPLE USAGE
-
- Get the main page from Netscape's web-server:
-
- curl http://www.netscape.com/
-
- Get the README file the user's home directory at funet's ftp-server:
-
- curl ftp://ftp.funet.fi/README
-
- Get a web page from a server using port 8000:
-
- curl http://www.weirdserver.com:8000/
-
- Get a directory listing of an FTP site:
-
- curl ftp://cool.haxx.se/
-
- Get the definition of curl from a dictionary:
-
- curl dict://dict.org/m:curl
-
- Fetch two documents at once:
-
- curl ftp://cool.haxx.se/ http://www.weirdserver.com:8000/
-
- Get a file off an FTPS server:
-
- curl ftps://files.are.secure.com/secrets.txt
-
- or use the more appropriate FTPS way to get the same file:
-
- curl --ftp-ssl ftp://files.are.secure.com/secrets.txt
-
- Get a file from an SSH server using SFTP:
-
- curl -u username sftp://example.com/etc/issue
-
- Get a file from an SSH server using SCP using a private key
- (not password-protected) to authenticate:
-
- curl -u username: --key ~/.ssh/id_rsa \
- scp://example.com/~/file.txt
-
- Get a file from an SSH server using SCP using a private key
- (password-protected) to authenticate:
-
- curl -u username: --key ~/.ssh/id_rsa --pass private_key_password \
- scp://example.com/~/file.txt
-
- Get the main page from an IPv6 web server:
-
- curl "http://[2001:1890:1112:1::20]/"
-
- Get a file from an SMB server:
-
- curl -u "domain\username:passwd" smb://server.example.com/share/file.txt
-
-DOWNLOAD TO A FILE
-
- Get a web page and store in a local file with a specific name:
-
- curl -o thatpage.html http://www.netscape.com/
-
- Get a web page and store in a local file, make the local file get the name
- of the remote document (if no file name part is specified in the URL, this
- will fail):
-
- curl -O http://www.netscape.com/index.html
-
- Fetch two files and store them with their remote names:
-
- curl -O www.haxx.se/index.html -O curl.haxx.se/download.html
-
-USING PASSWORDS
-
- FTP
-
- To ftp files using name+passwd, include them in the URL like:
-
- curl ftp://name:passwd@machine.domain:port/full/path/to/file
-
- or specify them with the -u flag like
-
- curl -u name:passwd ftp://machine.domain:port/full/path/to/file
-
- FTPS
-
- It is just like for FTP, but you may also want to specify and use
- SSL-specific options for certificates etc.
-
- Note that using FTPS:// as prefix is the "implicit" way as described in the
- standards while the recommended "explicit" way is done by using FTP:// and
- the --ftp-ssl option.
-
- SFTP / SCP
-
- This is similar to FTP, but you can use the --key option to specify a
- private key to use instead of a password. Note that the private key may
- itself be protected by a password that is unrelated to the login password
- of the remote system; this password is specified using the --pass option.
- Typically, curl will automatically extract the public key from the private
- key file, but in cases where curl does not have the proper library support,
- a matching public key file must be specified using the --pubkey option.
-
- HTTP
-
- Curl also supports user and password in HTTP URLs, thus you can pick a file
- like:
-
- curl http://name:passwd@machine.domain/full/path/to/file
-
- or specify user and password separately like in
-
- curl -u name:passwd http://machine.domain/full/path/to/file
-
- HTTP offers many different methods of authentication and curl supports
- several: Basic, Digest, NTLM and Negotiate (SPNEGO). Without telling which
- method to use, curl defaults to Basic. You can also ask curl to pick the
- most secure ones out of the ones that the server accepts for the given URL,
- by using --anyauth.
-
- NOTE! According to the URL specification, HTTP URLs can not contain a user
- and password, so that style will not work when using curl via a proxy, even
- though curl allows it at other times. When using a proxy, you _must_ use
- the -u style for user and password.
-
- HTTPS
-
- Probably most commonly used with private certificates, as explained below.
-
-PROXY
-
- curl supports both HTTP and SOCKS proxy servers, with optional authentication.
- It does not have special support for FTP proxy servers since there are no
- standards for those, but it can still be made to work with many of them. You
- can also use both HTTP and SOCKS proxies to transfer files to and from FTP
- servers.
-
- Get an ftp file using an HTTP proxy named my-proxy that uses port 888:
-
- curl -x my-proxy:888 ftp://ftp.leachsite.com/README
-
- Get a file from an HTTP server that requires user and password, using the
- same proxy as above:
-
- curl -u user:passwd -x my-proxy:888 http://www.get.this/
-
- Some proxies require special authentication. Specify by using -U as above:
-
- curl -U user:passwd -x my-proxy:888 http://www.get.this/
-
- A comma-separated list of hosts and domains which do not use the proxy can
- be specified as:
-
- curl --noproxy localhost,get.this -x my-proxy:888 http://www.get.this/
-
- If the proxy is specified with --proxy1.0 instead of --proxy or -x, then
- curl will use HTTP/1.0 instead of HTTP/1.1 for any CONNECT attempts.
-
- curl also supports SOCKS4 and SOCKS5 proxies with --socks4 and --socks5.
-
- See also the environment variables Curl supports that offer further proxy
- control.
-
- Most FTP proxy servers are set up to appear as a normal FTP server from the
- client's perspective, with special commands to select the remote FTP server.
- curl supports the -u, -Q and --ftp-account options that can be used to
- set up transfers through many FTP proxies. For example, a file can be
- uploaded to a remote FTP server using a Blue Coat FTP proxy with the
- options:
-
- curl -u "Remote-FTP-Username@remote.ftp.server Proxy-Username:Remote-Pass" \
- --ftp-account Proxy-Password --upload-file local-file \
- ftp://my-ftp.proxy.server:21/remote/upload/path/
-
- See the manual for your FTP proxy to determine the form it expects to set up
- transfers, and curl's -v option to see exactly what curl is sending.
-
-RANGES
-
- HTTP 1.1 introduced byte-ranges. Using this, a client can request
- to get only one or more subparts of a specified document. Curl supports
- this with the -r flag.
-
- Get the first 100 bytes of a document:
-
- curl -r 0-99 http://www.get.this/
-
- Get the last 500 bytes of a document:
-
- curl -r -500 http://www.get.this/
-
- Curl also supports simple ranges for FTP files as well. Then you can only
- specify start and stop position.
-
- Get the first 100 bytes of a document using FTP:
-
- curl -r 0-99 ftp://www.get.this/README
-
-UPLOADING
-
- FTP / FTPS / SFTP / SCP
-
- Upload all data on stdin to a specified server:
-
- curl -T - ftp://ftp.upload.com/myfile
-
- Upload data from a specified file, login with user and password:
-
- curl -T uploadfile -u user:passwd ftp://ftp.upload.com/myfile
-
- Upload a local file to the remote site, and use the local file name at the remote
- site too:
-
- curl -T uploadfile -u user:passwd ftp://ftp.upload.com/
-
- Upload a local file to get appended to the remote file:
-
- curl -T localfile -a ftp://ftp.upload.com/remotefile
-
- Curl also supports ftp upload through a proxy, but only if the proxy is
- configured to allow that kind of tunneling. If it does, you can run curl in
- a fashion similar to:
-
- curl --proxytunnel -x proxy:port -T localfile ftp.upload.com
-
-SMB / SMBS
-
- curl -T file.txt -u "domain\username:passwd" \
- smb://server.example.com/share/
-
- HTTP
-
- Upload all data on stdin to a specified HTTP site:
-
- curl -T - http://www.upload.com/myfile
-
- Note that the HTTP server must have been configured to accept PUT before
- this can be done successfully.
-
- For other ways to do HTTP data upload, see the POST section below.
-
-VERBOSE / DEBUG
-
- If curl fails where it isn't supposed to, if the servers don't let you in,
- if you can't understand the responses: use the -v flag to get verbose
- fetching. Curl will output lots of info and what it sends and receives in
- order to let the user see all client-server interaction (but it won't show
- you the actual data).
-
- curl -v ftp://ftp.upload.com/
-
- To get even more details and information on what curl does, try using the
- --trace or --trace-ascii options with a given file name to log to, like
- this:
-
- curl --trace trace.txt www.haxx.se
-
-
-DETAILED INFORMATION
-
- Different protocols provide different ways of getting detailed information
- about specific files/documents. To get curl to show detailed information
- about a single file, you should use -I/--head option. It displays all
- available info on a single file for HTTP and FTP. The HTTP information is a
- lot more extensive.
-
- For HTTP, you can get the header information (the same as -I would show)
- shown before the data by using -i/--include. Curl understands the
- -D/--dump-header option when getting files from both FTP and HTTP, and it
- will then store the headers in the specified file.
-
- Store the HTTP headers in a separate file (headers.txt in the example):
-
- curl --dump-header headers.txt curl.haxx.se
-
- Note that headers stored in a separate file can be very useful at a later
- time if you want curl to use cookies sent by the server. More about that in
- the cookies section.
-
-POST (HTTP)
-
- It's easy to post data using curl. This is done using the -d <data>
- option. The post data must be urlencoded.
-
- Post a simple "name" and "phone" guestbook.
-
- curl -d "name=Rafael%20Sagula&phone=3320780" \
- http://www.where.com/guest.cgi
-
- How to post a form with curl, lesson #1:
-
- Dig out all the <input> tags in the form that you want to fill in.
-
- If there's a "normal" post, you use -d to post. -d takes a full "post
- string", which is in the format
-
- <variable1>=<data1>&<variable2>=<data2>&...
-
- The 'variable' names are the names set with "name=" in the <input> tags, and
- the data is the contents you want to fill in for the inputs. The data *must*
- be properly URL encoded. That means you replace space with + and that you
- replace weird letters with %XX where XX is the hexadecimal representation of
- the letter's ASCII code.
-
- Example:
-
- (page located at http://www.formpost.com/getthis/
-
- <form action="post.cgi" method="post">
- <input name=user size=10>
- <input name=pass type=password size=10>
- <input name=id type=hidden value="blablabla">
- <input name=ding value="submit">
- </form>
-
- We want to enter user 'foobar' with password '12345'.
-
- To post to this, you enter a curl command line like:
-
- curl -d "user=foobar&pass=12345&id=blablabla&ding=submit" \
- http://www.formpost.com/getthis/post.cgi
-
-
- While -d uses the application/x-www-form-urlencoded mime-type, generally
- understood by CGI's and similar, curl also supports the more capable
- multipart/form-data type. This latter type supports things like file upload.
-
- -F accepts parameters like -F "name=contents". If you want the contents to
- be read from a file, use <@filename> as contents. When specifying a file,
- you can also specify the file content type by appending ';type=<mime type>'
- to the file name. You can also post the contents of several files in one
- field. For example, the field name 'coolfiles' is used to send three files,
- with different content types using the following syntax:
-
- curl -F "coolfiles=@fil1.gif;type=image/gif,fil2.txt,fil3.html" \
- http://www.post.com/postit.cgi
-
- If the content-type is not specified, curl will try to guess from the file
- extension (it only knows a few), or use the previously specified type (from
- an earlier file if several files are specified in a list) or else it will
- use the default type 'application/octet-stream'.
-
- Emulate a fill-in form with -F. Let's say you fill in three fields in a
- form. One field is a file name which to post, one field is your name and one
- field is a file description. We want to post the file we have written named
- "cooltext.txt". To let curl do the posting of this data instead of your
- favourite browser, you have to read the HTML source of the form page and
- find the names of the input fields. In our example, the input field names
- are 'file', 'yourname' and 'filedescription'.
-
- curl -F "file=@cooltext.txt" -F "yourname=Daniel" \
- -F "filedescription=Cool text file with cool text inside" \
- http://www.post.com/postit.cgi
-
- To send two files in one post you can do it in two ways:
-
- 1. Send multiple files in a single "field" with a single field name:
-
- curl -F "pictures=@dog.gif,cat.gif"
-
- 2. Send two fields with two field names:
-
- curl -F "docpicture=@dog.gif" -F "catpicture=@cat.gif"
-
- To send a field value literally without interpreting a leading '@'
- or '<', or an embedded ';type=', use --form-string instead of
- -F. This is recommended when the value is obtained from a user or
- some other unpredictable source. Under these circumstances, using
- -F instead of --form-string would allow a user to trick curl into
- uploading a file.
-
-REFERRER
-
- An HTTP request has the option to include information about which address
- referred it to the actual page. Curl allows you to specify the
- referrer to be used on the command line. It is especially useful to
- fool or trick stupid servers or CGI scripts that rely on that information
- being available or contain certain data.
-
- curl -e www.coolsite.com http://www.showme.com/
-
- NOTE: The Referer: [sic] field is defined in the HTTP spec to be a full URL.
-
-USER AGENT
-
- An HTTP request has the option to include information about the browser
- that generated the request. Curl allows it to be specified on the command
- line. It is especially useful to fool or trick stupid servers or CGI
- scripts that only accept certain browsers.
-
- Example:
-
- curl -A 'Mozilla/3.0 (Win95; I)' http://www.nationsbank.com/
-
- Other common strings:
- 'Mozilla/3.0 (Win95; I)' Netscape Version 3 for Windows 95
- 'Mozilla/3.04 (Win95; U)' Netscape Version 3 for Windows 95
- 'Mozilla/2.02 (OS/2; U)' Netscape Version 2 for OS/2
- 'Mozilla/4.04 [en] (X11; U; AIX 4.2; Nav)' NS for AIX
- 'Mozilla/4.05 [en] (X11; U; Linux 2.0.32 i586)' NS for Linux
-
- Note that Internet Explorer tries hard to be compatible in every way:
- 'Mozilla/4.0 (compatible; MSIE 4.01; Windows 95)' MSIE for W95
-
- Mozilla is not the only possible User-Agent name:
- 'Konqueror/1.0' KDE File Manager desktop client
- 'Lynx/2.7.1 libwww-FM/2.14' Lynx command line browser
-
-COOKIES
-
- Cookies are generally used by web servers to keep state information at the
- client's side. The server sets cookies by sending a response line in the
- headers that looks like 'Set-Cookie: <data>' where the data part then
- typically contains a set of NAME=VALUE pairs (separated by semicolons ';'
- like "NAME1=VALUE1; NAME2=VALUE2;"). The server can also specify for what
- path the "cookie" should be used for (by specifying "path=value"), when the
- cookie should expire ("expire=DATE"), for what domain to use it
- ("domain=NAME") and if it should be used on secure connections only
- ("secure").
-
- If you've received a page from a server that contains a header like:
- Set-Cookie: sessionid=boo123; path="/foo";
-
- it means the server wants that first pair passed on when we get anything in
- a path beginning with "/foo".
-
- Example, get a page that wants my name passed in a cookie:
-
- curl -b "name=Daniel" www.sillypage.com
-
- Curl also has the ability to use previously received cookies in following
- sessions. If you get cookies from a server and store them in a file in a
- manner similar to:
-
- curl --dump-header headers www.example.com
-
- ... you can then in a second connect to that (or another) site, use the
- cookies from the 'headers' file like:
-
- curl -b headers www.example.com
-
- While saving headers to a file is a working way to store cookies, it is
- however error-prone and not the preferred way to do this. Instead, make curl
- save the incoming cookies using the well-known netscape cookie format like
- this:
-
- curl -c cookies.txt www.example.com
-
- Note that by specifying -b you enable the "cookie awareness" and with -L
- you can make curl follow a location: (which often is used in combination
- with cookies). So that if a site sends cookies and a location, you can
- use a non-existing file to trigger the cookie awareness like:
-
- curl -L -b empty.txt www.example.com
-
- The file to read cookies from must be formatted using plain HTTP headers OR
- as netscape's cookie file. Curl will determine what kind it is based on the
- file contents. In the above command, curl will parse the header and store
- the cookies received from www.example.com. curl will send to the server the
- stored cookies which match the request as it follows the location. The
- file "empty.txt" may be a nonexistent file.
-
- To read and write cookies from a netscape cookie file, you can set both -b
- and -c to use the same file:
-
- curl -b cookies.txt -c cookies.txt www.example.com
-
-PROGRESS METER
-
- The progress meter exists to show a user that something actually is
- happening. The different fields in the output have the following meaning:
-
- % Total % Received % Xferd Average Speed Time Curr.
- Dload Upload Total Current Left Speed
- 0 151M 0 38608 0 0 9406 0 4:41:43 0:00:04 4:41:39 9287
-
- From left-to-right:
- % - percentage completed of the whole transfer
- Total - total size of the whole expected transfer
- % - percentage completed of the download
- Received - currently downloaded amount of bytes
- % - percentage completed of the upload
- Xferd - currently uploaded amount of bytes
- Average Speed
- Dload - the average transfer speed of the download
- Average Speed
- Upload - the average transfer speed of the upload
- Time Total - expected time to complete the operation
- Time Current - time passed since the invoke
- Time Left - expected time left to completion
- Curr.Speed - the average transfer speed the last 5 seconds (the first
- 5 seconds of a transfer is based on less time of course.)
-
- The -# option will display a totally different progress bar that doesn't
- need much explanation!
-
-SPEED LIMIT
-
- Curl allows the user to set the transfer speed conditions that must be met
- to let the transfer keep going. By using the switch -y and -Y you
- can make curl abort transfers if the transfer speed is below the specified
- lowest limit for a specified time.
-
- To have curl abort the download if the speed is slower than 3000 bytes per
- second for 1 minute, run:
-
- curl -Y 3000 -y 60 www.far-away-site.com
-
- This can very well be used in combination with the overall time limit, so
- that the above operation must be completed in whole within 30 minutes:
-
- curl -m 1800 -Y 3000 -y 60 www.far-away-site.com
-
- Forcing curl not to transfer data faster than a given rate is also possible,
- which might be useful if you're using a limited bandwidth connection and you
- don't want your transfer to use all of it (sometimes referred to as
- "bandwidth throttle").
-
- Make curl transfer data no faster than 10 kilobytes per second:
-
- curl --limit-rate 10K www.far-away-site.com
-
- or
-
- curl --limit-rate 10240 www.far-away-site.com
-
- Or prevent curl from uploading data faster than 1 megabyte per second:
-
- curl -T upload --limit-rate 1M ftp://uploadshereplease.com
-
- When using the --limit-rate option, the transfer rate is regulated on a
- per-second basis, which will cause the total transfer speed to become lower
- than the given number. Sometimes of course substantially lower, if your
- transfer stalls during periods.
-
-CONFIG FILE
-
- Curl automatically tries to read the .curlrc file (or _curlrc file on win32
- systems) from the user's home dir on startup.
-
- The config file could be made up with normal command line switches, but you
- can also specify the long options without the dashes to make it more
- readable. You can separate the options and the parameter with spaces, or
- with = or :. Comments can be used within the file. If the first letter on a
- line is a '#'-symbol the rest of the line is treated as a comment.
-
- If you want the parameter to contain spaces, you must enclose the entire
- parameter within double quotes ("). Within those quotes, you specify a
- quote as \".
-
- NOTE: You must specify options and their arguments on the same line.
-
- Example, set default time out and proxy in a config file:
-
- # We want a 30 minute timeout:
- -m 1800
- # ... and we use a proxy for all accesses:
- proxy = proxy.our.domain.com:8080
-
- White spaces ARE significant at the end of lines, but all white spaces
- leading up to the first characters of each line are ignored.
-
- Prevent curl from reading the default file by using -q as the first command
- line parameter, like:
-
- curl -q www.thatsite.com
-
- Force curl to get and display a local help page in case it is invoked
- without URL by making a config file similar to:
-
- # default url to get
- url = "http://help.with.curl.com/curlhelp.html"
-
- You can specify another config file to be read by using the -K/--config
- flag. If you set config file name to "-" it'll read the config from stdin,
- which can be handy if you want to hide options from being visible in process
- tables etc:
-
- echo "user = user:passwd" | curl -K - http://that.secret.site.com
-
-EXTRA HEADERS
-
- When using curl in your own very special programs, you may end up needing
- to pass on your own custom headers when getting a web page. You can do
- this by using the -H flag.
-
- Example, send the header "X-you-and-me: yes" to the server when getting a
- page:
-
- curl -H "X-you-and-me: yes" www.love.com
-
- This can also be useful in case you want curl to send a different text in a
- header than it normally does. The -H header you specify then replaces the
- header curl would normally send. If you replace an internal header with an
- empty one, you prevent that header from being sent. To prevent the Host:
- header from being used:
-
- curl -H "Host:" www.server.com
-
-FTP and PATH NAMES
-
- Do note that when getting files with the ftp:// URL, the given path is
- relative the directory you enter. To get the file 'README' from your home
- directory at your ftp site, do:
-
- curl ftp://user:passwd@my.site.com/README
-
- But if you want the README file from the root directory of that very same
- site, you need to specify the absolute file name:
-
- curl ftp://user:passwd@my.site.com//README
-
- (I.e with an extra slash in front of the file name.)
-
-SFTP and SCP and PATH NAMES
-
- With sftp: and scp: URLs, the path name given is the absolute name on the
- server. To access a file relative to the remote user's home directory,
- prefix the file with /~/ , such as:
-
- curl -u $USER sftp://home.example.com/~/.bashrc
-
-FTP and firewalls
-
- The FTP protocol requires one of the involved parties to open a second
- connection as soon as data is about to get transferred. There are two ways to
- do this.
-
- The default way for curl is to issue the PASV command which causes the
- server to open another port and await another connection performed by the
- client. This is good if the client is behind a firewall that doesn't allow
- incoming connections.
-
- curl ftp.download.com
-
- If the server, for example, is behind a firewall that doesn't allow connections
- on ports other than 21 (or if it just doesn't support the PASV command), the
- other way to do it is to use the PORT command and instruct the server to
- connect to the client on the given IP number and port (as parameters to the
- PORT command).
-
- The -P flag to curl supports a few different options. Your machine may have
- several IP-addresses and/or network interfaces and curl allows you to select
- which of them to use. Default address can also be used:
-
- curl -P - ftp.download.com
-
- Download with PORT but use the IP address of our 'le0' interface (this does
- not work on windows):
-
- curl -P le0 ftp.download.com
-
- Download with PORT but use 192.168.0.10 as our IP address to use:
-
- curl -P 192.168.0.10 ftp.download.com
-
-NETWORK INTERFACE
-
- Get a web page from a server using a specified port for the interface:
-
- curl --interface eth0:1 http://www.netscape.com/
-
- or
-
- curl --interface 192.168.1.10 http://www.netscape.com/
-
-HTTPS
-
- Secure HTTP requires SSL libraries to be installed and used when curl is
- built. If that is done, curl is capable of retrieving and posting documents
- using the HTTPS protocol.
-
- Example:
-
- curl https://www.secure-site.com
-
- Curl is also capable of using your personal certificates to get/post files
- from sites that require valid certificates. The only drawback is that the
- certificate needs to be in PEM-format. PEM is a standard and open format to
- store certificates with, but it is not used by the most commonly used
- browsers (Netscape and MSIE both use the so called PKCS#12 format). If you
- want curl to use the certificates you use with your (favourite) browser, you
- may need to download/compile a converter that can convert your browser's
- formatted certificates to PEM formatted ones. This kind of converter is
- included in recent versions of OpenSSL, and for older versions Dr Stephen
- N. Henson has written a patch for SSLeay that adds this functionality. You
- can get his patch (that requires an SSLeay installation) from his site at:
- https://web.archive.org/web/20170715155512/www.drh-consultancy.demon.co.uk/
-
- Example on how to automatically retrieve a document using a certificate with
- a personal password:
-
- curl -E /path/to/cert.pem:password https://secure.site.com/
-
- If you neglect to specify the password on the command line, you will be
- prompted for the correct password before any data can be received.
-
- Many older SSL-servers have problems with SSLv3 or TLS, which newer versions
- of OpenSSL etc use, therefore it is sometimes useful to specify what
- SSL-version curl should use. Use -3, -2 or -1 to specify that exact SSL
- version to use (for SSLv3, SSLv2 or TLSv1 respectively):
-
- curl -2 https://secure.site.com/
-
- Otherwise, curl will first attempt to use v3 and then v2.
-
- To use OpenSSL to convert your favourite browser's certificate into a PEM
- formatted one that curl can use, do something like this:
-
- In Netscape, you start with hitting the 'Security' menu button.
-
- Select 'certificates->yours' and then pick a certificate in the list
-
- Press the 'Export' button
-
- enter your PIN code for the certs
-
- select a proper place to save it
-
- Run the 'openssl' application to convert the certificate. If you cd to the
- openssl installation, you can do it like:
-
- # ./apps/openssl pkcs12 -in [file you saved] -clcerts -out [PEMfile]
-
- In Firefox, select Options, then Advanced, then the Encryption tab,
- View Certificates. This opens the Certificate Manager, where you can
- Export. Be sure to select PEM for the Save as type.
-
- In Internet Explorer, select Internet Options, then the Content tab, then
- Certificates. Then you can Export, and depending on the format you may
- need to convert to PEM.
-
- In Chrome, select Settings, then Show Advanced Settings. Under HTTPS/SSL
- select Manage Certificates.
-
-RESUMING FILE TRANSFERS
-
- To continue a file transfer where it was previously aborted, curl supports
- resume on HTTP(S) downloads as well as FTP uploads and downloads.
-
- Continue downloading a document:
-
- curl -C - -o file ftp://ftp.server.com/path/file
-
- Continue uploading a document(*1):
-
- curl -C - -T file ftp://ftp.server.com/path/file
-
- Continue downloading a document from a web server(*2):
-
- curl -C - -o file http://www.server.com/
-
- (*1) = This requires that the FTP server supports the non-standard command
- SIZE. If it doesn't, curl will say so.
-
- (*2) = This requires that the web server supports at least HTTP/1.1. If it
- doesn't, curl will say so.
-
-TIME CONDITIONS
-
- HTTP allows a client to specify a time condition for the document it
- requests. It is If-Modified-Since or If-Unmodified-Since. Curl allows you to
- specify them with the -z/--time-cond flag.
-
- For example, you can easily make a download that only gets performed if the
- remote file is newer than a local copy. It would be made like:
-
- curl -z local.html http://remote.server.com/remote.html
-
- Or you can download a file only if the local file is newer than the remote
- one. Do this by prepending the date string with a '-', as in:
-
- curl -z -local.html http://remote.server.com/remote.html
-
- You can specify a "free text" date as condition. Tell curl to only download
- the file if it was updated since January 12, 2012:
-
- curl -z "Jan 12 2012" http://remote.server.com/remote.html
-
- Curl will then accept a wide range of date formats. You always make the date
- check the other way around by prepending it with a dash '-'.
-
-DICT
-
- For fun try
-
- curl dict://dict.org/m:curl
- curl dict://dict.org/d:heisenbug:jargon
- curl dict://dict.org/d:daniel:web1913
-
- Aliases for 'm' are 'match' and 'find', and aliases for 'd' are 'define'
- and 'lookup'. For example,
-
- curl dict://dict.org/find:curl
-
- Commands that break the URL description of the RFC (but not the DICT
- protocol) are
-
- curl dict://dict.org/show:db
- curl dict://dict.org/show:strat
-
- Authentication is still missing (but this is not required by the RFC)
-
-LDAP
-
- If you have installed the OpenLDAP library, curl can take advantage of it
- and offer ldap:// support.
- On Windows, curl will use WinLDAP from Platform SDK by default.
-
- Default protocol version used by curl is LDAPv3. LDAPv2 will be used as
- fallback mechanism in case if LDAPv3 will fail to connect.
-
- LDAP is a complex thing and writing an LDAP query is not an easy task. I do
- advise you to dig up the syntax description for that elsewhere. One such
- place might be:
-
- RFC 2255, "The LDAP URL Format" https://curl.haxx.se/rfc/rfc2255.txt
-
- To show you an example, this is how I can get all people from my local LDAP
- server that has a certain sub-domain in their email address:
-
- curl -B "ldap://ldap.frontec.se/o=frontec??sub?mail=*sth.frontec.se"
-
- If I want the same info in HTML format, I can get it by not using the -B
- (enforce ASCII) flag.
-
- You also can use authentication when accessing LDAP catalog:
-
- curl -u user:passwd "ldap://ldap.frontec.se/o=frontec??sub?mail=*"
- curl "ldap://user:passwd@ldap.frontec.se/o=frontec??sub?mail=*"
-
- By default, if user and password provided, OpenLDAP/WinLDAP will use basic
- authentication. On Windows you can control this behavior by providing
- one of --basic, --ntlm or --digest option in curl command line
-
- curl --ntlm "ldap://user:passwd@ldap.frontec.se/o=frontec??sub?mail=*"
-
- On Windows, if no user/password specified, auto-negotiation mechanism will
- be used with current logon credentials (SSPI/SPNEGO).
-
-ENVIRONMENT VARIABLES
-
- Curl reads and understands the following environment variables:
-
- http_proxy, HTTPS_PROXY, FTP_PROXY
-
- They should be set for protocol-specific proxies. General proxy should be
- set with
-
- ALL_PROXY
-
- A comma-separated list of host names that shouldn't go through any proxy is
- set in (only an asterisk, '*' matches all hosts)
-
- NO_PROXY
-
- If the host name matches one of these strings, or the host is within the
- domain of one of these strings, transactions with that node will not be
- proxied. When a domain is used, it needs to start with a period. A user can
- specify that both www.example.com and foo.example.com should not use a
- proxy by setting NO_PROXY to ".example.com". By including the full name you
- can exclude specific host names, so to make www.example.com not use a proxy
- but still have foo.example.com do it, set NO_PROXY to "www.example.com"
-
- The usage of the -x/--proxy flag overrides the environment variables.
-
-NETRC
-
- Unix introduced the .netrc concept a long time ago. It is a way for a user
- to specify name and password for commonly visited FTP sites in a file so
- that you don't have to type them in each time you visit those sites. You
- realize this is a big security risk if someone else gets hold of your
- passwords, so therefore most unix programs won't read this file unless it is
- only readable by yourself (curl doesn't care though).
-
- Curl supports .netrc files if told to (using the -n/--netrc and
- --netrc-optional options). This is not restricted to just FTP,
- so curl can use it for all protocols where authentication is used.
-
- A very simple .netrc file could look something like:
-
- machine curl.haxx.se login iamdaniel password mysecret
-
-CUSTOM OUTPUT
-
- To better allow script programmers to get to know about the progress of
- curl, the -w/--write-out option was introduced. Using this, you can specify
- what information from the previous transfer you want to extract.
-
- To display the amount of bytes downloaded together with some text and an
- ending newline:
-
- curl -w 'We downloaded %{size_download} bytes\n' www.download.com
-
-KERBEROS FTP TRANSFER
-
- Curl supports kerberos4 and kerberos5/GSSAPI for FTP transfers. You need
- the kerberos package installed and used at curl build time for it to be
- available.
-
- First, get the krb-ticket the normal way, like with the kinit/kauth tool.
- Then use curl in way similar to:
-
- curl --krb private ftp://krb4site.com -u username:fakepwd
-
- There's no use for a password on the -u switch, but a blank one will make
- curl ask for one and you already entered the real password to kinit/kauth.
-
-TELNET
-
- The curl telnet support is basic and very easy to use. Curl passes all data
- passed to it on stdin to the remote server. Connect to a remote telnet
- server using a command line similar to:
-
- curl telnet://remote.server.com
-
- And enter the data to pass to the server on stdin. The result will be sent
- to stdout or to the file you specify with -o.
-
- You might want the -N/--no-buffer option to switch off the buffered output
- for slow connections or similar.
-
- Pass options to the telnet protocol negotiation, by using the -t option. To
- tell the server we use a vt100 terminal, try something like:
-
- curl -tTTYPE=vt100 telnet://remote.server.com
-
- Other interesting options for it -t include:
-
- - XDISPLOC=<X display> Sets the X display location.
-
- - NEW_ENV=<var,val> Sets an environment variable.
-
- NOTE: The telnet protocol does not specify any way to login with a specified
- user and password so curl can't do that automatically. To do that, you need
- to track when the login prompt is received and send the username and
- password accordingly.
-
-PERSISTENT CONNECTIONS
-
- Specifying multiple files on a single command line will make curl transfer
- all of them, one after the other in the specified order.
-
- libcurl will attempt to use persistent connections for the transfers so that
- the second transfer to the same host can use the same connection that was
- already initiated and was left open in the previous transfer. This greatly
- decreases connection time for all but the first transfer and it makes a far
- better use of the network.
-
- Note that curl cannot use persistent connections for transfers that are used
- in subsequence curl invokes. Try to stuff as many URLs as possible on the
- same command line if they are using the same host, as that'll make the
- transfers faster. If you use an HTTP proxy for file transfers, practically
- all transfers will be persistent.
-
-MULTIPLE TRANSFERS WITH A SINGLE COMMAND LINE
-
- As is mentioned above, you can download multiple files with one command line
- by simply adding more URLs. If you want those to get saved to a local file
- instead of just printed to stdout, you need to add one save option for each
- URL you specify. Note that this also goes for the -O option (but not
- --remote-name-all).
-
- For example: get two files and use -O for the first and a custom file
- name for the second:
-
- curl -O http://url.com/file.txt ftp://ftp.com/moo.exe -o moo.jpg
-
- You can also upload multiple files in a similar fashion:
-
- curl -T local1 ftp://ftp.com/moo.exe -T local2 ftp://ftp.com/moo2.txt
-
-IPv6
-
- curl will connect to a server with IPv6 when a host lookup returns an IPv6
- address and fall back to IPv4 if the connection fails. The --ipv4 and --ipv6
- options can specify which address to use when both are available. IPv6
- addresses can also be specified directly in URLs using the syntax:
-
- http://[2001:1890:1112:1::20]/overview.html
-
- When this style is used, the -g option must be given to stop curl from
- interpreting the square brackets as special globbing characters. Link local
- and site local addresses including a scope identifier, such as fe80::1234%1,
- may also be used, but the scope portion must be numeric or match an existing
- network interface on Linux and the percent character must be URL escaped. The
- previous example in an SFTP URL might look like:
-
- sftp://[fe80::1234%251]/
-
- IPv6 addresses provided other than in URLs (e.g. to the --proxy, --interface
- or --ftp-port options) should not be URL encoded.
-
-METALINK
-
- Curl supports Metalink (both version 3 and 4 (RFC 5854) are supported), a way
- to list multiple URIs and hashes for a file. Curl will make use of the mirrors
- listed within for failover if there are errors (such as the file or server not
- being available). It will also verify the hash of the file after the download
- completes. The Metalink file itself is downloaded and processed in memory and
- not stored in the local file system.
-
- Example to use a remote Metalink file:
-
- curl --metalink http://www.example.com/example.metalink
-
- To use a Metalink file in the local file system, use FILE protocol (file://):
-
- curl --metalink file://example.metalink
-
- Please note that if FILE protocol is disabled, there is no way to use a local
- Metalink file at the time of this writing. Also note that if --metalink and
- --include are used together, --include will be ignored. This is because including
- headers in the response will break Metalink parser and if the headers are included
- in the file described in Metalink file, hash check will fail.
-
-MAILING LISTS
-
- For your convenience, we have several open mailing lists to discuss curl,
- its development and things relevant to this. Get all info at
- https://curl.haxx.se/mail/. Some of the lists available are:
-
- curl-users
-
- Users of the command line tool. How to use it, what doesn't work, new
- features, related tools, questions, news, installations, compilations,
- running, porting etc.
-
- curl-library
-
- Developers using or developing libcurl. Bugs, extensions, improvements.
-
- curl-announce
-
- Low-traffic. Only receives announcements of new public versions. At worst,
- that makes something like one or two mails per month, but usually only one
- mail every second month.
-
- curl-and-php
-
- Using the curl functions in PHP. Everything curl with a PHP angle. Or PHP
- with a curl angle.
-
- curl-and-python
-
- Python hackers using curl with or without the python binding pycurl.
-
- Please direct curl questions, feature requests and trouble reports to one of
- these mailing lists instead of mailing any individual.
diff --git a/docs/MANUAL.md b/docs/MANUAL.md
new file mode 100644
index 000000000..80ab92a63
--- /dev/null
+++ b/docs/MANUAL.md
@@ -0,0 +1,1011 @@
+# curl tutorial
+
+## Simple Usage
+
+Get the main page from Netscape's web-server:
+
+ curl http://www.netscape.com/
+
+Get the README file the user's home directory at funet's ftp-server:
+
+ curl ftp://ftp.funet.fi/README
+
+Get a web page from a server using port 8000:
+
+ curl http://www.weirdserver.com:8000/
+
+Get a directory listing of an FTP site:
+
+ curl ftp://cool.haxx.se/
+
+Get the definition of curl from a dictionary:
+
+ curl dict://dict.org/m:curl
+
+Fetch two documents at once:
+
+ curl ftp://cool.haxx.se/ http://www.weirdserver.com:8000/
+
+Get a file off an FTPS server:
+
+ curl ftps://files.are.secure.com/secrets.txt
+
+or use the more appropriate FTPS way to get the same file:
+
+ curl --ftp-ssl ftp://files.are.secure.com/secrets.txt
+
+Get a file from an SSH server using SFTP:
+
+ curl -u username sftp://example.com/etc/issue
+
+Get a file from an SSH server using SCP using a private key (not
+password-protected) to authenticate:
+
+ curl -u username: --key ~/.ssh/id_rsa scp://example.com/~/file.txt
+
+Get a file from an SSH server using SCP using a private key
+(password-protected) to authenticate:
+
+ curl -u username: --key ~/.ssh/id_rsa --pass private_key_password
+ scp://example.com/~/file.txt
+
+Get the main page from an IPv6 web server:
+
+ curl "http://[2001:1890:1112:1::20]/"
+
+Get a file from an SMB server:
+
+ curl -u "domain\username:passwd" smb://server.example.com/share/file.txt
+
+## Download to a File
+
+Get a web page and store in a local file with a specific name:
+
+ curl -o thatpage.html http://www.netscape.com/
+
+Get a web page and store in a local file, make the local file get the name of
+the remote document (if no file name part is specified in the URL, this will
+fail):
+
+ curl -O http://www.netscape.com/index.html
+
+Fetch two files and store them with their remote names:
+
+ curl -O www.haxx.se/index.html -O curl.haxx.se/download.html
+
+## Using Passwords
+
+### FTP
+
+To ftp files using name+passwd, include them in the URL like:
+
+ curl ftp://name:passwd@machine.domain:port/full/path/to/file
+
+or specify them with the -u flag like
+
+ curl -u name:passwd ftp://machine.domain:port/full/path/to/file
+
+### FTPS
+
+It is just like for FTP, but you may also want to specify and use SSL-specific
+options for certificates etc.
+
+Note that using `FTPS://` as prefix is the "implicit" way as described in the
+standards while the recommended "explicit" way is done by using FTP:// and the
+`--ftp-ssl` option.
+
+### SFTP / SCP
+
+This is similar to FTP, but you can use the `--key` option to specify a
+private key to use instead of a password. Note that the private key may itself
+be protected by a password that is unrelated to the login password of the
+remote system; this password is specified using the `--pass` option.
+Typically, curl will automatically extract the public key from the private key
+file, but in cases where curl does not have the proper library support, a
+matching public key file must be specified using the `--pubkey` option.
+
+### HTTP
+
+Curl also supports user and password in HTTP URLs, thus you can pick a file
+like:
+
+ curl http://name:passwd@machine.domain/full/path/to/file
+
+or specify user and password separately like in
+
+ curl -u name:passwd http://machine.domain/full/path/to/file
+
+HTTP offers many different methods of authentication and curl supports
+several: Basic, Digest, NTLM and Negotiate (SPNEGO). Without telling which
+method to use, curl defaults to Basic. You can also ask curl to pick the most
+secure ones out of the ones that the server accepts for the given URL, by
+using `--anyauth`.
+
+**Note**! According to the URL specification, HTTP URLs can not contain a user
+and password, so that style will not work when using curl via a proxy, even
+though curl allows it at other times. When using a proxy, you _must_ use the
+`-u` style for user and password.
+
+### HTTPS
+
+Probably most commonly used with private certificates, as explained below.
+
+## Proxy
+
+curl supports both HTTP and SOCKS proxy servers, with optional authentication.
+It does not have special support for FTP proxy servers since there are no
+standards for those, but it can still be made to work with many of them. You
+can also use both HTTP and SOCKS proxies to transfer files to and from FTP
+servers.
+
+Get an ftp file using an HTTP proxy named my-proxy that uses port 888:
+
+ curl -x my-proxy:888 ftp://ftp.leachsite.com/README
+
+Get a file from an HTTP server that requires user and password, using the
+same proxy as above:
+
+ curl -u user:passwd -x my-proxy:888 http://www.get.this/
+
+Some proxies require special authentication. Specify by using -U as above:
+
+ curl -U user:passwd -x my-proxy:888 http://www.get.this/
+
+A comma-separated list of hosts and domains which do not use the proxy can be
+specified as:
+
+ curl --noproxy localhost,get.this -x my-proxy:888 http://www.get.this/
+
+If the proxy is specified with `--proxy1.0` instead of `--proxy` or `-x`, then
+curl will use HTTP/1.0 instead of HTTP/1.1 for any `CONNECT` attempts.
+
+curl also supports SOCKS4 and SOCKS5 proxies with `--socks4` and `--socks5`.
+
+See also the environment variables Curl supports that offer further proxy
+control.
+
+Most FTP proxy servers are set up to appear as a normal FTP server from the
+client's perspective, with special commands to select the remote FTP server.
+curl supports the `-u`, `-Q` and `--ftp-account` options that can be used to
+set up transfers through many FTP proxies. For example, a file can be uploaded
+to a remote FTP server using a Blue Coat FTP proxy with the options:
+
+ curl -u "username@ftp.server Proxy-Username:Remote-Pass"
+ --ftp-account Proxy-Password --upload-file local-file
+ ftp://my-ftp.proxy.server:21/remote/upload/path/
+
+See the manual for your FTP proxy to determine the form it expects to set up
+transfers, and curl's `-v` option to see exactly what curl is sending.
+
+## Ranges
+
+HTTP 1.1 introduced byte-ranges. Using this, a client can request to get only
+one or more subparts of a specified document. Curl supports this with the `-r`
+flag.
+
+Get the first 100 bytes of a document:
+
+ curl -r 0-99 http://www.get.this/
+
+Get the last 500 bytes of a document:
+
+ curl -r -500 http://www.get.this/
+
+Curl also supports simple ranges for FTP files as well. Then you can only
+specify start and stop position.
+
+Get the first 100 bytes of a document using FTP:
+
+ curl -r 0-99 ftp://www.get.this/README
+
+## Uploading
+
+### FTP / FTPS / SFTP / SCP
+
+Upload all data on stdin to a specified server:
+
+ curl -T - ftp://ftp.upload.com/myfile
+
+Upload data from a specified file, login with user and password:
+
+ curl -T uploadfile -u user:passwd ftp://ftp.upload.com/myfile
+
+Upload a local file to the remote site, and use the local file name at the
+remote site too:
+
+ curl -T uploadfile -u user:passwd ftp://ftp.upload.com/
+
+Upload a local file to get appended to the remote file:
+
+ curl -T localfile -a ftp://ftp.upload.com/remotefile
+
+Curl also supports ftp upload through a proxy, but only if the proxy is
+configured to allow that kind of tunneling. If it does, you can run curl in a
+fashion similar to:
+
+ curl --proxytunnel -x proxy:port -T localfile ftp.upload.com
+
+### SMB / SMBS
+
+ curl -T file.txt -u "domain\username:passwd"
+ smb://server.example.com/share/
+
+### HTTP
+
+Upload all data on stdin to a specified HTTP site:
+
+ curl -T - http://www.upload.com/myfile
+
+Note that the HTTP server must have been configured to accept PUT before this
+can be done successfully.
+
+For other ways to do HTTP data upload, see the POST section below.
+
+## Verbose / Debug
+
+If curl fails where it isn't supposed to, if the servers don't let you in, if
+you can't understand the responses: use the `-v` flag to get verbose
+fetching. Curl will output lots of info and what it sends and receives in
+order to let the user see all client-server interaction (but it won't show you
+the actual data).
+
+ curl -v ftp://ftp.upload.com/
+
+To get even more details and information on what curl does, try using the
+`--trace` or `--trace-ascii` options with a given file name to log to, like
+this:
+
+ curl --trace trace.txt www.haxx.se
+
+
+## Detailed Information
+
+Different protocols provide different ways of getting detailed information
+about specific files/documents. To get curl to show detailed information about
+a single file, you should use `-I`/`--head` option. It displays all available
+info on a single file for HTTP and FTP. The HTTP information is a lot more
+extensive.
+
+For HTTP, you can get the header information (the same as `-I` would show)
+shown before the data by using `-i`/`--include`. Curl understands the
+`-D`/`--dump-header` option when getting files from both FTP and HTTP, and it
+will then store the headers in the specified file.
+
+Store the HTTP headers in a separate file (headers.txt in the example):
+
+ curl --dump-header headers.txt curl.haxx.se
+
+Note that headers stored in a separate file can be very useful at a later time
+if you want curl to use cookies sent by the server. More about that in the
+cookies section.
+
+## POST (HTTP)
+
+It's easy to post data using curl. This is done using the `-d <data>` option.
+The post data must be urlencoded.
+
+Post a simple "name" and "phone" guestbook.
+
+ curl -d "name=Rafael%20Sagula&phone=3320780" http://www.where.com/guest.cgi
+
+How to post a form with curl, lesson #1:
+
+Dig out all the `<input>` tags in the form that you want to fill in.
+
+If there's a "normal" post, you use `-d` to post. `-d` takes a full "post
+string", which is in the format
+
+ <variable1>=<data1>&<variable2>=<data2>&...
+
+The 'variable' names are the names set with `"name="` in the `<input>` tags,
+and the data is the contents you want to fill in for the inputs. The data
+*must* be properly URL encoded. That means you replace space with + and that
+you replace weird letters with %XX where XX is the hexadecimal representation
+of the letter's ASCII code.
+
+Example:
+
+(page located at `http://www.formpost.com/getthis/`)
+
+ <form action="post.cgi" method="post">
+ <input name=user size=10>
+ <input name=pass type=password size=10>
+ <input name=id type=hidden value="blablabla">
+ <input name=ding value="submit">
+ </form>
+
+We want to enter user 'foobar' with password '12345'.
+
+To post to this, you enter a curl command line like:
+
+ curl -d "user=foobar&pass=12345&id=blablabla&ding=submit"
+ http://www.formpost.com/getthis/post.cgi
+
+While `-d` uses the application/x-www-form-urlencoded mime-type, generally
+understood by CGI's and similar, curl also supports the more capable
+multipart/form-data type. This latter type supports things like file upload.
+
+`-F` accepts parameters like `-F "name=contents"`. If you want the contents to
+be read from a file, use `@filename` as contents. When specifying a file, you
+can also specify the file content type by appending `;type=<mime type>` to the
+file name. You can also post the contents of several files in one field. For
+example, the field name 'coolfiles' is used to send three files, with
+different content types using the following syntax:
+
+ curl -F "coolfiles=@fil1.gif;type=image/gif,fil2.txt,fil3.html"
+ http://www.post.com/postit.cgi
+
+If the content-type is not specified, curl will try to guess from the file
+extension (it only knows a few), or use the previously specified type (from an
+earlier file if several files are specified in a list) or else it will use the
+default type 'application/octet-stream'.
+
+Emulate a fill-in form with `-F`. Let's say you fill in three fields in a
+form. One field is a file name which to post, one field is your name and one
+field is a file description. We want to post the file we have written named
+"cooltext.txt". To let curl do the posting of this data instead of your
+favourite browser, you have to read the HTML source of the form page and find
+the names of the input fields. In our example, the input field names are
+'file', 'yourname' and 'filedescription'.
+
+ curl -F "file=@cooltext.txt" -F "yourname=Daniel"
+ -F "filedescription=Cool text file with cool text inside"
+ http://www.post.com/postit.cgi
+
+To send two files in one post you can do it in two ways:
+
+Send multiple files in a single "field" with a single field name:
+
+ curl -F "pictures=@dog.gif,cat.gif" $URL
+
+Send two fields with two field names
+
+ curl -F "docpicture=@dog.gif" -F "catpicture=@cat.gif" $URL
+
+To send a field value literally without interpreting a leading `@` or `<`, or
+an embedded `;type=`, use `--form-string` instead of `-F`. This is recommended
+when the value is obtained from a user or some other unpredictable
+source. Under these circumstances, using `-F` instead of `--form-string` could
+allow a user to trick curl into uploading a file.
+
+## Referrer
+
+An HTTP request has the option to include information about which address
+referred it to the actual page. Curl allows you to specify the referrer to be
+used on the command line. It is especially useful to fool or trick stupid
+servers or CGI scripts that rely on that information being available or
+contain certain data.
+
+ curl -e www.coolsite.com http://www.showme.com/
+
+## User Agent
+
+An HTTP request has the option to include information about the browser that
+generated the request. Curl allows it to be specified on the command line. It
+is especially useful to fool or trick stupid servers or CGI scripts that only
+accept certain browsers.
+
+Example:
+
+ curl -A 'Mozilla/3.0 (Win95; I)' http://www.nationsbank.com/
+
+Other common strings:
+
+- `Mozilla/3.0 (Win95; I)` - Netscape Version 3 for Windows 95
+- `Mozilla/3.04 (Win95; U)` - Netscape Version 3 for Windows 95
+- `Mozilla/2.02 (OS/2; U)` - Netscape Version 2 for OS/2
+- `Mozilla/4.04 [en] (X11; U; AIX 4.2; Nav)` - Netscape for AIX
+- `Mozilla/4.05 [en] (X11; U; Linux 2.0.32 i586)` - Netscape for Linux
+
+Note that Internet Explorer tries hard to be compatible in every way:
+
+- `Mozilla/4.0 (compatible; MSIE 4.01; Windows 95)` - MSIE for W95
+
+Mozilla is not the only possible User-Agent name:
+
+- `Konqueror/1.0` - KDE File Manager desktop client
+- `Lynx/2.7.1 libwww-FM/2.14` - Lynx command line browser
+
+## Cookies
+
+Cookies are generally used by web servers to keep state information at the
+client's side. The server sets cookies by sending a response line in the
+headers that looks like `Set-Cookie: <data>` where the data part then
+typically contains a set of `NAME=VALUE` pairs (separated by semicolons `;`
+like `NAME1=VALUE1; NAME2=VALUE2;`). The server can also specify for what path
+the "cookie" should be used for (by specifying `path=value`), when the cookie
+should expire (`expire=DATE`), for what domain to use it (`domain=NAME`) and
+if it should be used on secure connections only (`secure`).
+
+If you've received a page from a server that contains a header like:
+
+ Set-Cookie: sessionid=boo123; path="/foo";
+
+it means the server wants that first pair passed on when we get anything in a
+path beginning with "/foo".
+
+Example, get a page that wants my name passed in a cookie:
+
+ curl -b "name=Daniel" www.sillypage.com
+
+Curl also has the ability to use previously received cookies in following
+sessions. If you get cookies from a server and store them in a file in a
+manner similar to:
+
+ curl --dump-header headers www.example.com
+
+... you can then in a second connect to that (or another) site, use the
+cookies from the 'headers' file like:
+
+ curl -b headers www.example.com
+
+While saving headers to a file is a working way to store cookies, it is
+however error-prone and not the preferred way to do this. Instead, make curl
+save the incoming cookies using the well-known netscape cookie format like
+this:
+
+ curl -c cookies.txt www.example.com
+
+Note that by specifying `-b` you enable the "cookie awareness" and with `-L`
+you can make curl follow a location: (which often is used in combination with
+cookies). So that if a site sends cookies and a location, you can use a
+non-existing file to trigger the cookie awareness like:
+
+ curl -L -b empty.txt www.example.com
+
+The file to read cookies from must be formatted using plain HTTP headers OR as
+netscape's cookie file. Curl will determine what kind it is based on the file
+contents. In the above command, curl will parse the header and store the
+cookies received from www.example.com. curl will send to the server the
+stored cookies which match the request as it follows the location. The file
+"empty.txt" may be a nonexistent file.
+
+To read and write cookies from a netscape cookie file, you can set both `-b`
+and `-c` to use the same file:
+
+ curl -b cookies.txt -c cookies.txt www.example.com
+
+## Progress Meter
+
+The progress meter exists to show a user that something actually is
+happening. The different fields in the output have the following meaning:
+
+ % Total % Received % Xferd Average Speed Time Curr.
+ Dload Upload Total Current Left Speed
+ 0 151M 0 38608 0 0 9406 0 4:41:43 0:00:04 4:41:39 9287
+
+From left-to-right:
+
+ - % - percentage completed of the whole transfer
+ - Total - total size of the whole expected transfer
+ - % - percentage completed of the download
+ - Received - currently downloaded amount of bytes
+ - % - percentage completed of the upload
+ - Xferd - currently uploaded amount of bytes
+ - Average Speed Dload - the average transfer speed of the download
+ - Average Speed Upload - the average transfer speed of the upload
+ - Time Total - expected time to complete the operation
+ - Time Current - time passed since the invoke
+ - Time Left - expected time left to completion
+ - Curr.Speed - the average transfer speed the last 5 seconds (the first
+ 5 seconds of a transfer is based on less time of course.)
+
+The `-#` option will display a totally different progress bar that doesn't
+need much explanation!
+
+## Speed Limit
+
+Curl allows the user to set the transfer speed conditions that must be met to
+let the transfer keep going. By using the switch `-y` and `-Y` you can make
+curl abort transfers if the transfer speed is below the specified lowest limit
+for a specified time.
+
+To have curl abort the download if the speed is slower than 3000 bytes per
+second for 1 minute, run:
+
+ curl -Y 3000 -y 60 www.far-away-site.com
+
+This can very well be used in combination with the overall time limit, so
+that the above operation must be completed in whole within 30 minutes:
+
+ curl -m 1800 -Y 3000 -y 60 www.far-away-site.com
+
+Forcing curl not to transfer data faster than a given rate is also possible,
+which might be useful if you're using a limited bandwidth connection and you
+don't want your transfer to use all of it (sometimes referred to as
+"bandwidth throttle").
+
+Make curl transfer data no faster than 10 kilobytes per second:
+
+ curl --limit-rate 10K www.far-away-site.com
+
+or
+
+ curl --limit-rate 10240 www.far-away-site.com
+
+Or prevent curl from uploading data faster than 1 megabyte per second:
+
+ curl -T upload --limit-rate 1M ftp://uploadshereplease.com
+
+When using the `--limit-rate` option, the transfer rate is regulated on a
+per-second basis, which will cause the total transfer speed to become lower
+than the given number. Sometimes of course substantially lower, if your
+transfer stalls during periods.
+
+## Config File
+
+Curl automatically tries to read the `.curlrc` file (or `_curlrc` file on
+Microsoft Windows systems) from the user's home dir on startup.
+
+The config file could be made up with normal command line switches, but you
+can also specify the long options without the dashes to make it more
+readable. You can separate the options and the parameter with spaces, or with
+`=` or `:`. Comments can be used within the file. If the first letter on a
+line is a `#`-symbol the rest of the line is treated as a comment.
+
+If you want the parameter to contain spaces, you must enclose the entire
+parameter within double quotes (`"`). Within those quotes, you specify a quote
+as `\"`.
+
+NOTE: You must specify options and their arguments on the same line.
+
+Example, set default time out and proxy in a config file:
+
+ # We want a 30 minute timeout:
+ -m 1800
+ # ... and we use a proxy for all accesses:
+ proxy = proxy.our.domain.com:8080
+
+White spaces ARE significant at the end of lines, but all white spaces leading
+up to the first characters of each line are ignored.
+
+Prevent curl from reading the default file by using -q as the first command
+line parameter, like:
+
+ curl -q www.thatsite.com
+
+Force curl to get and display a local help page in case it is invoked without
+URL by making a config file similar to:
+
+ # default url to get
+ url = "http://help.with.curl.com/curlhelp.html"
+
+You can specify another config file to be read by using the `-K`/`--config`
+flag. If you set config file name to `-` it'll read the config from stdin,
+which can be handy if you want to hide options from being visible in process
+tables etc:
+
+ echo "user = user:passwd" | curl -K - http://that.secret.site.com
+
+## Extra Headers
+
+When using curl in your own very special programs, you may end up needing
+to pass on your own custom headers when getting a web page. You can do
+this by using the `-H` flag.
+
+Example, send the header `X-you-and-me: yes` to the server when getting a
+page:
+
+ curl -H "X-you-and-me: yes" www.love.com
+
+This can also be useful in case you want curl to send a different text in a
+header than it normally does. The `-H` header you specify then replaces the
+header curl would normally send. If you replace an internal header with an
+empty one, you prevent that header from being sent. To prevent the `Host:`
+header from being used:
+
+ curl -H "Host:" www.server.com
+
+## FTP and Path Names
+
+Do note that when getting files with a `ftp://` URL, the given path is
+relative the directory you enter. To get the file `README` from your home
+directory at your ftp site, do:
+
+ curl ftp://user:passwd@my.site.com/README
+
+But if you want the README file from the root directory of that very same
+site, you need to specify the absolute file name:
+
+ curl ftp://user:passwd@my.site.com//README
+
+(I.e with an extra slash in front of the file name.)
+
+## SFTP and SCP and Path Names
+
+With sftp: and scp: URLs, the path name given is the absolute name on the
+server. To access a file relative to the remote user's home directory, prefix
+the file with `/~/` , such as:
+
+ curl -u $USER sftp://home.example.com/~/.bashrc
+
+## FTP and Firewalls
+
+The FTP protocol requires one of the involved parties to open a second
+connection as soon as data is about to get transferred. There are two ways to
+do this.
+
+The default way for curl is to issue the PASV command which causes the server
+to open another port and await another connection performed by the
+client. This is good if the client is behind a firewall that doesn't allow
+incoming connections.
+
+ curl ftp.download.com
+
+If the server, for example, is behind a firewall that doesn't allow
+connections on ports other than 21 (or if it just doesn't support the `PASV`
+command), the other way to do it is to use the `PORT` command and instruct the
+server to connect to the client on the given IP number and port (as parameters
+to the PORT command).
+
+The `-P` flag to curl supports a few different options. Your machine may have
+several IP-addresses and/or network interfaces and curl allows you to select
+which of them to use. Default address can also be used:
+
+ curl -P - ftp.download.com
+
+Download with `PORT` but use the IP address of our `le0` interface (this does
+not work on windows):
+
+ curl -P le0 ftp.download.com
+
+Download with `PORT` but use 192.168.0.10 as our IP address to use:
+
+ curl -P 192.168.0.10 ftp.download.com
+
+## Network Interface
+
+Get a web page from a server using a specified port for the interface:
+
+ curl --interface eth0:1 http://www.netscape.com/
+
+or
+
+ curl --interface 192.168.1.10 http://www.netscape.com/
+
+## HTTPS
+
+Secure HTTP requires a TLS library to be installed and used when curl is
+built. If that is done, curl is capable of retrieving and posting documents
+using the HTTPS protocol.
+
+Example:
+
+ curl https://www.secure-site.com
+
+curl is also capable of using client certificates to get/post files from sites
+that require valid certificates. The only drawback is that the certificate
+needs to be in PEM-format. PEM is a standard and open format to store
+certificates with, but it is not used by the most commonly used browsers. If
+you want curl to use the certificates you use with your (favourite) browser,
+you may need to download/compile a converter that can convert your browser's
+formatted certificates to PEM formatted ones.
+
+Example on how to automatically retrieve a document using a certificate with a
+personal password:
+
+ curl -E /path/to/cert.pem:password https://secure.site.com/
+
+If you neglect to specify the password on the command line, you will be
+prompted for the correct password before any data can be received.
+
+Many older HTTPS servers have problems with specific SSL or TLS versions,
+which newer versions of OpenSSL etc use, therefore it is sometimes useful to
+specify what SSL-version curl should use. Use -3, -2 or -1 to specify that
+exact SSL version to use (for SSLv3, SSLv2 or TLSv1 respectively):
+
+ curl -2 https://secure.site.com/
+
+Otherwise, curl will attempt to use a sensible TLS default version.
+
+## Resuming File Transfers
+
+To continue a file transfer where it was previously aborted, curl supports
+esume on HTTP(S) downloads as well as FTP uploads and downloads.
+
+Continue downloading a document:
+
+ curl -C - -o file ftp://ftp.server.com/path/file
+
+Continue uploading a document:
+
+ curl -C - -T file ftp://ftp.server.com/path/file
+
+Continue downloading a document from a web server
+
+ curl -C - -o file http://www.server.com/
+
+## Time Conditions
+
+HTTP allows a client to specify a time condition for the document it requests.
+It is `If-Modified-Since` or `If-Unmodified-Since`. curl allows you to specify
+them with the `-z`/`--time-cond` flag.
+
+For example, you can easily make a download that only gets performed if the
+remote file is newer than a local copy. It would be made like:
+
+ curl -z local.html http://remote.server.com/remote.html
+
+Or you can download a file only if the local file is newer than the remote
+one. Do this by prepending the date string with a `-`, as in:
+
+ curl -z -local.html http://remote.server.com/remote.html
+
+You can specify a "free text" date as condition. Tell curl to only download
+the file if it was updated since January 12, 2012:
+
+ curl -z "Jan 12 2012" http://remote.server.com/remote.html
+
+Curl will then accept a wide range of date formats. You always make the date
+check the other way around by prepending it with a dash (`-`).
+
+## DICT
+
+For fun try
+
+ curl dict://dict.org/m:curl
+ curl dict://dict.org/d:heisenbug:jargon
+ curl dict://dict.org/d:daniel:web1913
+
+Aliases for 'm' are 'match' and 'find', and aliases for 'd' are 'define' and
+'lookup'. For example,
+
+ curl dict://dict.org/find:curl
+
+Commands that break the URL description of the RFC (but not the DICT
+protocol) are
+
+ curl dict://dict.org/show:db
+ curl dict://dict.org/show:strat
+
+Authentication support is still missing
+
+## LDAP
+
+If you have installed the OpenLDAP library, curl can take advantage of it and
+offer `ldap://` support. On Windows, curl will use WinLDAP from Platform SDK
+by default.
+
+Default protocol version used by curl is LDAPv3. LDAPv2 will be used as
+fallback mechanism in case if LDAPv3 will fail to connect.
+
+LDAP is a complex thing and writing an LDAP query is not an easy task. I do
+advise you to dig up the syntax description for that elsewhere. One such place
+might be: [RFC 2255, The LDAP URL
+Format](https://curl.haxx.se/rfc/rfc2255.txt)
+
+To show you an example, this is how I can get all people from my local LDAP
+server that has a certain sub-domain in their email address:
+
+ curl -B "ldap://ldap.frontec.se/o=frontec??sub?mail=*sth.frontec.se"
+
+If I want the same info in HTML format, I can get it by not using the `-B`
+(enforce ASCII) flag.
+
+You also can use authentication when accessing LDAP catalog:
+
+ curl -u user:passwd "ldap://ldap.frontec.se/o=frontec??sub?mail=*"
+ curl "ldap://user:passwd@ldap.frontec.se/o=frontec??sub?mail=*"
+
+By default, if user and password provided, OpenLDAP/WinLDAP will use basic
+authentication. On Windows you can control this behavior by providing one of
+`--basic`, `--ntlm` or `--digest` option in curl command line
+
+ curl --ntlm "ldap://user:passwd@ldap.frontec.se/o=frontec??sub?mail=*"
+
+On Windows, if no user/password specified, auto-negotiation mechanism will be
+used with current logon credentials (SSPI/SPNEGO).
+
+## Environment Variables
+
+Curl reads and understands the following environment variables:
+
+ http_proxy, HTTPS_PROXY, FTP_PROXY
+
+They should be set for protocol-specific proxies. General proxy should be set
+with
+
+ ALL_PROXY
+
+A comma-separated list of host names that shouldn't go through any proxy is
+set in (only an asterisk, `*` matches all hosts)
+
+ NO_PROXY
+
+If the host name matches one of these strings, or the host is within the
+domain of one of these strings, transactions with that node will not be
+proxied. When a domain is used, it needs to start with a period. A user can
+specify that both www.example.com and foo.example.com should not use a proxy
+by setting `NO_PROXY` to `.example.com`. By including the full name you can
+exclude specific host names, so to make `www.example.com` not use a proxy but
+still have `foo.example.com` do it, set `NO_PROXY` to `www.example.com`.
+
+The usage of the `-x`/`--proxy` flag overrides the environment variables.
+
+## Netrc
+
+Unix introduced the `.netrc` concept a long time ago. It is a way for a user
+to specify name and password for commonly visited FTP sites in a file so that
+you don't have to type them in each time you visit those sites. You realize
+this is a big security risk if someone else gets hold of your passwords, so
+therefore most unix programs won't read this file unless it is only readable
+by yourself (curl doesn't care though).
+
+Curl supports `.netrc` files if told to (using the `-n`/`--netrc` and
+`--netrc-optional` options). This is not restricted to just FTP, so curl can
+use it for all protocols where authentication is used.
+
+A very simple `.netrc` file could look something like:
+
+ machine curl.haxx.se login iamdaniel password mysecret
+
+## Custom Output
+
+To better allow script programmers to get to know about the progress of curl,
+the `-w`/`--write-out` option was introduced. Using this, you can specify what
+information from the previous transfer you want to extract.
+
+To display the amount of bytes downloaded together with some text and an
+ending newline:
+
+ curl -w 'We downloaded %{size_download} bytes\n' www.download.com
+
+## Kerberos FTP Transfer
+
+Curl supports kerberos4 and kerberos5/GSSAPI for FTP transfers. You need the
+kerberos package installed and used at curl build time for it to be available.
+
+First, get the krb-ticket the normal way, like with the kinit/kauth tool.
+Then use curl in way similar to:
+
+ curl --krb private ftp://krb4site.com -u username:fakepwd
+
+There's no use for a password on the `-u` switch, but a blank one will make
+curl ask for one and you already entered the real password to kinit/kauth.
+
+## TELNET
+
+The curl telnet support is basic and very easy to use. Curl passes all data
+passed to it on stdin to the remote server. Connect to a remote telnet server
+using a command line similar to:
+
+ curl telnet://remote.server.com
+
+And enter the data to pass to the server on stdin. The result will be sent to
+stdout or to the file you specify with `-o`.
+
+You might want the `-N`/`--no-buffer` option to switch off the buffered output
+for slow connections or similar.
+
+Pass options to the telnet protocol negotiation, by using the `-t` option. To
+tell the server we use a vt100 terminal, try something like:
+
+ curl -tTTYPE=vt100 telnet://remote.server.com
+
+Other interesting options for it `-t` include:
+
+ - `XDISPLOC=<X display>` Sets the X display location.
+ - `NEW_ENV=<var,val>` Sets an environment variable.
+
+NOTE: The telnet protocol does not specify any way to login with a specified
+user and password so curl can't do that automatically. To do that, you need to
+track when the login prompt is received and send the username and password
+accordingly.
+
+## Persistent Connections
+
+Specifying multiple files on a single command line will make curl transfer all
+of them, one after the other in the specified order.
+
+libcurl will attempt to use persistent connections for the transfers so that
+the second transfer to the same host can use the same connection that was
+already initiated and was left open in the previous transfer. This greatly
+decreases connection time for all but the first transfer and it makes a far
+better use of the network.
+
+Note that curl cannot use persistent connections for transfers that are used
+in subsequence curl invokes. Try to stuff as many URLs as possible on the same
+command line if they are using the same host, as that'll make the transfers
+faster. If you use an HTTP proxy for file transfers, practically all transfers
+will be persistent.
+
+## Multiple Transfers With A Single Command Line
+
+As is mentioned above, you can download multiple files with one command line
+by simply adding more URLs. If you want those to get saved to a local file
+instead of just printed to stdout, you need to add one save option for each
+URL you specify. Note that this also goes for the `-O` option (but not
+`--remote-name-all`).
+
+For example: get two files and use `-O` for the first and a custom file
+name for the second:
+
+ curl -O http://url.com/file.txt ftp://ftp.com/moo.exe -o moo.jpg
+
+You can also upload multiple files in a similar fashion:
+
+ curl -T local1 ftp://ftp.com/moo.exe -T local2 ftp://ftp.com/moo2.txt
+
+## IPv6
+
+curl will connect to a server with IPv6 when a host lookup returns an IPv6
+address and fall back to IPv4 if the connection fails. The `--ipv4` and
+`--ipv6` options can specify which address to use when both are
+available. IPv6 addresses can also be specified directly in URLs using the
+syntax:
+
+ http://[2001:1890:1112:1::20]/overview.html
+
+When this style is used, the `-g` option must be given to stop curl from
+interpreting the square brackets as special globbing characters. Link local
+and site local addresses including a scope identifier, such as `fe80::1234%1`,
+may also be used, but the scope portion must be numeric or match an existing
+network interface on Linux and the percent character must be URL escaped. The
+previous example in an SFTP URL might look like:
+
+ sftp://[fe80::1234%251]/
+
+IPv6 addresses provided other than in URLs (e.g. to the `--proxy`,
+`--interface` or `--ftp-port` options) should not be URL encoded.
+
+## Metalink
+
+Curl supports Metalink (both version 3 and 4 (RFC 5854) are supported), a way
+to list multiple URIs and hashes for a file. Curl will make use of the mirrors
+listed within for failover if there are errors (such as the file or server not
+being available). It will also verify the hash of the file after the download
+completes. The Metalink file itself is downloaded and processed in memory and
+not stored in the local file system.
+
+Example to use a remote Metalink file:
+
+ curl --metalink http://www.example.com/example.metalink
+
+To use a Metalink file in the local file system, use FILE protocol
+(`file://`):
+
+ curl --metalink file://example.metalink
+
+Please note that if FILE protocol is disabled, there is no way to use a local
+Metalink file at the time of this writing. Also note that if `--metalink` and
+`--include` are used together, `--include` will be ignored. This is because
+including headers in the response will break Metalink parser and if the
+headers are included in the file described in Metalink file, hash check will
+fail.
+
+## Mailing Lists
+
+For your convenience, we have several open mailing lists to discuss curl, its
+development and things relevant to this. Get all info at
+https://curl.haxx.se/mail/.
+
+Please direct curl questions, feature requests and trouble reports to one of
+these mailing lists instead of mailing any individual.
+
+Available lists include:
+
+### curl-users
+
+Users of the command line tool. How to use it, what doesn't work, new
+features, related tools, questions, news, installations, compilations,
+running, porting etc.
+
+### curl-library
+
+Developers using or developing libcurl. Bugs, extensions, improvements.
+
+### curl-announce
+
+Low-traffic. Only receives announcements of new public versions. At worst,
+that makes something like one or two mails per month, but usually only one
+mail every second month.
+
+### curl-and-php
+
+Using the curl functions in PHP. Everything curl with a PHP angle. Or PHP with
+a curl angle.
+
+### curl-and-python
+
+Python hackers using curl with or without the python binding pycurl.
+
diff --git a/docs/Makefile.am b/docs/Makefile.am
index 28e947742..de3487010 100644
--- a/docs/Makefile.am
+++ b/docs/Makefile.am
@@ -49,6 +49,7 @@ EXTRA_DIST = \
CODE_STYLE.md \
CONTRIBUTE.md \
DEPRECATE.md \
+ EXPERIMENTAL.md \
FAQ \
FEATURES \
GOVERNANCE.md \
@@ -56,6 +57,7 @@ EXTRA_DIST = \
HISTORY.md \
HTTP-COOKIES.md \
HTTP2.md \
+ HTTP3.md \
INSTALL \
INSTALL.cmake \
INSTALL.md \
@@ -63,6 +65,7 @@ EXTRA_DIST = \
KNOWN_BUGS \
LICENSE-MIXING.md \
MAIL-ETIQUETTE \
+ PARALLEL-TRANSFERS.md \
README.cmake \
README.md \
README.netware \
diff --git a/docs/PARALLEL-TRANSFERS.md b/docs/PARALLEL-TRANSFERS.md
new file mode 100644
index 000000000..d3b38aee1
--- /dev/null
+++ b/docs/PARALLEL-TRANSFERS.md
@@ -0,0 +1,58 @@
+# Parallel transfers
+
+curl 7.66.0 introduces support for doing multiple transfers simultaneously; in
+parallel.
+
+## -Z, --parallel
+
+When this command line option is used, curl will perform the transfers given
+to it at the same time. It will do up to `--parallel-max` concurrent
+transfers, with a default value of 50.
+
+## Progress meter
+
+The progress meter that is displayed when doing parallel transfers is
+completely different than the regular one used for each single transfer.
+
+ It shows:
+
+ o percent download (if known, which means *all* transfers need to have a
+ known size)
+ o precent upload (if known, with the same caveat as for download)
+ o total amount of downloaded data
+ o total amount of uploaded data
+ o number of transfers to perform
+ o number of concurrent transfers being transferred right now
+ o number of transfers queued up waiting to start
+ o total time all transfers are expected to take (if sizes are known)
+ o current time the transfers have spent so far
+ o estimated time left (if sizes are known)
+ o current transfer speed (the faster of UL/DL speeds measured over the last
+ few seconds)
+
+Example:
+
+ DL% UL% Dled Uled Xfers Live Qd Total Current Left Speed
+ 72 -- 37.9G 0 101 30 23 0:00:55 0:00:34 0:00:22 2752M
+
+## Behavior differences
+
+Connections are shared fine between different easy handles, but the
+"authentication contexts" are not. So for example doing HTTP Digest auth with
+one handle for a particular transfer and then continue on with another handle
+that reuses the same connection, the second handle can't send the necessary
+Authorization header at once since the context is only kept in the original
+easy handle.
+
+To fix this, the authorization state could be made possible to share with the
+share API as well, as a context per origin + path (realm?) basically.
+
+Visible in test 153, 1412 and more.
+
+## Feedback!
+
+This is early days for parallel transfer support. Keep your eyes open for
+unintended side effects or downright bugs.
+
+Tell us what you think and how you think we could improve this feature!
+
diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md
index 10e7effee..1d47682bf 100644
--- a/docs/ROADMAP.md
+++ b/docs/ROADMAP.md
@@ -5,10 +5,19 @@ Roadmap of things Daniel Stenberg wants to work on next. It is intended to
serve as a guideline for others for information, feedback and possible
participation.
-HTTP/3
-------
+HSTS
+----
+
+ Complete and merge [the existing PR](https://github.com/curl/curl/pull/2682).
+
+ Loading a huge preload file is probably not too interesting to most people,
+ but using a custom file and reacting to HSTS response header probably are
+ good features.
- See the [QUIC and HTTP/3 wiki page](https://github.com/curl/curl/wiki/QUIC).
+DNS-over-TLS
+------------
+
+ Similar to DNS-over-HTTPS. Could share quite a lot of generic code.
ESNI (Encrypted SNI)
--------------------
@@ -16,44 +25,32 @@ ESNI (Encrypted SNI)
See Daniel's post on [Support of Encrypted
SNI](https://curl.haxx.se/mail/lib-2019-03/0000.html) on the mailing list.
-HSTS
-----
+ Initial work exists in https://github.com/curl/curl/pull/4011
-Complete and merge [the existing PR](https://github.com/curl/curl/pull/2682).
+tiny-curl
+---------
-Parallel transfers for the curl tool
-------------------------------------
+ There's no immediate action for this but users seem keen on being able to
+ building custom minimized versions of libcurl for their products. Make sure
+ new features that are "niche" can still be disabled at build-time.
-This will require several new command line options to enable and control.
-
- 1. switch to creating a list of all the transfers first before any transfer
- is done
- 2. make the transfers using the multi interface
- 3. optionally fire up more transfers before the previous has completed
-
-Option to refuse HTTPS => HTTP redirects
-----------------------------------------
-
-Possibly as a new bit to `CURLOPT_FOLLOWLOCATION` ?
-
-Option to let CURLOPT_CUSTOMREQUEST be overridden on redirect
--------------------------------------------------------------
-
-(This is a common problem for people using `-X` and `-L` together.)
+MQTT
+----
-Possibly as a new bit to `CURLOPT_FOLLOWLOCATION` ?
+ Support receiving and sending MQTT messages. Initial work exists in
+ https://github.com/curl/curl/pull/3514
Hardcode “localhost”
--------------------
-No need to resolve it. Avoid a risk where this is resolved over the network
-and actually responds with something else than a local address. Some operating
-systems already do this. Also:
-https://tools.ietf.org/html/draft-ietf-dnsop-let-localhost-be-localhost-02
+ No need to resolve it. Avoid a risk where this is resolved over the network
+ and actually responds with something else than a local address. Some
+ operating systems already do this. Also:
+ https://tools.ietf.org/html/draft-ietf-dnsop-let-localhost-be-localhost-02
-Consider "menu config"-style build feature selection
-----------------------------------------------------
+"menu config"-style build feature selection
+-------------------------------------------
-Allow easier building of custom libcurl versions with only a selected feature
-where the available features are easily browsable and toggle-able ON/OFF or
-similar.
+ Allow easier building of custom libcurl versions with only a selected feature
+ where the available features are easily browsable and toggle-able ON/OFF or
+ similar.
diff --git a/docs/THANKS b/docs/THANKS
index 385ecd851..73b84cfdb 100644
--- a/docs/THANKS
+++ b/docs/THANKS
@@ -52,6 +52,7 @@ Alex Fishman
Alex Grebenschikov
Alex Gruz
Alex Malinovich
+Alex Mayorga
Alex McLellan
Alex Neblett
Alex Nichols
@@ -84,6 +85,7 @@ Alfonso Martone
Alfred Gebert
Allen Pulsifer
Alona Rossen
+Amit Katyal
Amol Pattekar
Amr Shahin
Anatol Belski
@@ -172,6 +174,7 @@ Ayoub Boudhar
Balaji Parasuram
Balaji S Rao
Balaji Salunke
+Balazs Kovacsics
Balint Szilakszi
Barry Abrahamson
Bart Whiteley
@@ -230,6 +233,7 @@ Brad King
Brad Spencer
Bradford Bruce
Brandon Casey
+Brandon Dong
Brandon Wang
Brendan Jurd
Brent Beardsley
@@ -261,6 +265,7 @@ Camille Moncelier
Caolan McNamara
Carie Pointer
Carlo Cannas
+Carlo Marcelo Arenas Belón
Carlo Teubner
Carlo Wood
Carlos ORyan
@@ -315,6 +320,7 @@ Clemens Gruber
Cliff Crosland
Clifford Wolf
Clint Clayton
+Clément Notin
Cody Jones
Cody Mack
Colby Ranger
@@ -714,6 +720,7 @@ Ian Wilkes
Ignacio Vazquez-Abrams
Igor Franchuk
Igor Khristophorov
+Igor Makarov
Igor Novoseltsev
Igor Polyakov
Ihor Karpenko
@@ -726,6 +733,7 @@ Ingmar Runge
Ingo Ralf Blum
Ingo Wilken
Irfan Adilovic
+Ironbars13 on github
Irving Wolfe
Isaac Boukris
Isaiah Norton
@@ -775,6 +783,7 @@ Jari Sundell
Jason Baietto
Jason Glasgow
Jason Juang
+Jason Lee
Jason Liu
Jason McDonald
Jason S. Priebe
@@ -809,6 +818,7 @@ Jens Schleusener
Jeremie Rapin
Jeremy Friesner
Jeremy Huddleston
+Jeremy Lainé
Jeremy Lin
Jeremy Pearson
Jeremy Tan
@@ -929,6 +939,7 @@ Julien Chaffraix
Julien Nabet
Julien Royer
Jun-ichiro itojun Hagino
+Junho Choi
Jurij Smakov
Juro Bystricky
Justin Clift
@@ -996,13 +1007,16 @@ Kristiyan Tsaklev
Kristoffer Gleditsch
Kunal Ekawde
Kurt Fankhauser
+Kyle Abramowitz
Kyle Edwards
Kyle J. McKay
Kyle L. Huff
Kyle Sallee
+Kyohei Kadota
Kyselgov E.N
Lachlan O'Dea
Ladar Levison
+Lance Ware
Larry Campbell
Larry Fahnoe
Larry Lin
@@ -1207,6 +1221,7 @@ Michael Kaufmann
Michael Kilburn
Michael Kujawa
Michael König
+Michael Lee
Michael Maltese
Michael Mealling
Michael Mueller
@@ -1220,6 +1235,7 @@ Michael Wallner
Michal Bonino
Michal Marek
Michal Trybus
+Michal Čaplygin
Michał Antoniak
Michał Fita
Michał Górny
@@ -1549,6 +1565,7 @@ Roger Leigh
Roland Blom
Roland Krikava
Roland Zimmermann
+Rolf Eike Beer
Rolland Dudemaine
Romain Coltel
Romain Fliedel
@@ -1682,7 +1699,6 @@ Stephen Kick
Stephen More
Stephen Toub
Sterling Hughes
-Steve Brokenshire
Steve Green
Steve H Truong
Steve Havelka
@@ -1723,6 +1739,7 @@ Teemu Yli-Elsila
Temprimus
Terri Oda
Terry Wu
+The Infinnovation team
TheAssassin on github
Theodore Dubois
Thomas Braun
@@ -1736,6 +1753,7 @@ Thomas Petazzoni
Thomas Ruecker
Thomas Schwinge
Thomas Tonino
+Thomas Vegas
Thomas van Hesteren
Thorsten Schöning
Tiit Pikma
@@ -1921,6 +1939,7 @@ cbartl on github
cclauss on github
clbr on github
cmfrolick on github
+codesniffer13 on github
d912e3 on github
daboul on github
dasimx on github
@@ -1956,20 +1975,24 @@ madblobfish on github
marc-groundctl on github
masbug on github
mccormickt12 on github
+migueljcrum on github
mkzero on github
moohoorama on github
nedres on github
neex on github
neheb on github
nevv on HackerOne/curl
+niallor on github
nianxuejie on github
niner on github
nk
nopjmp on github
olesteban on github
omau on github
+osabc on github
ovidiu-benea on github
patelvivekv1993 on github
+patnyb on github
pendrek at hackerone
pszemus on github
silveja1 on github
diff --git a/docs/THANKS-filter b/docs/THANKS-filter
index 29dc24c8a..d2adda578 100644
--- a/docs/THANKS-filter
+++ b/docs/THANKS-filter
@@ -98,3 +98,4 @@ s/Jason Priebe/Jason S. Priebe/
s/Ale Vesely/Alessandro Vesely/
s/Yamada Yasuharu/Yasuharu Yamada/
s/Jim Gallagher/James Gallagher/
+s/Steve Brokenshire/Stephen Brokenshire/
diff --git a/docs/TODO b/docs/TODO
index 5e1fcefae..6d30d26a4 100644
--- a/docs/TODO
+++ b/docs/TODO
@@ -18,11 +18,8 @@
1. libcurl
1.1 TFO support on Windows
- 1.2 More data sharing
1.3 struct lifreq
- 1.4 signal-based resolver timeouts
1.5 get rid of PATH_MAX
- 1.6 Modified buffer size approach
1.7 Support HTTP/2 for HTTP(S) proxies
1.8 CURLOPT_RESOLVE for any port number
1.9 Cache negative name resolves
@@ -36,12 +33,10 @@
1.17 Add support for IRIs
1.18 try next proxy if one doesn't work
1.20 SRV and URI DNS records
- 1.21 Have the URL API offer IDN decoding
1.22 CURLINFO_PAUSE_STATE
1.23 Offer API to flush the connection pool
1.24 TCP Fast Open for windows
1.25 Expose tried IP addresses that failed
- 1.26 CURL_REFUSE_CLEARTEXT
1.27 hardcode the "localhost" addresses
1.28 FD_CLOEXEC
1.29 Upgrade to websockets
@@ -62,7 +57,6 @@
4.1 HOST
4.2 Alter passive/active on failure and retry
4.3 Earlier bad letter detection
- 4.4 REST for large files
4.5 ASCII support
4.6 GSSAPI via Windows SSPI
4.7 STAT for LIST without data connection
@@ -70,12 +64,9 @@
5. HTTP
5.1 Better persistency for HTTP 1.0
- 5.2 support FF3 sqlite cookie files
5.3 Rearrange request header order
5.4 Allow SAN names in HTTP/2 server push
5.5 auth= in URLs
- 5.6 Refuse "downgrade" redirects
- 5.7 QUIC
6. TELNET
6.1 ditch stdin
@@ -83,12 +74,10 @@
6.3 feature negotiation debug data
7. SMTP
- 7.1 Pipelining
7.2 Enhanced capability support
7.3 Add CURLOPT_MAIL_CLIENT option
8. POP3
- 8.1 Pipelining
8.2 Enhanced capability support
9. IMAP
@@ -104,10 +93,8 @@
11.4 Create remote directories
12. New protocols
- 12.1 RSYNC
13. SSL
- 13.1 Disable specific versions
13.2 Provide mutex locking API
13.3 Support in-memory certs/ca certs/keys
13.4 Cache/share OpenSSL contexts
@@ -115,15 +102,12 @@
13.6 Provide callback for cert verification
13.7 improve configure --with-ssl
13.8 Support DANE
- 13.9 Configurable loading of OpenSSL configuration file
13.10 Support Authority Information Access certificate extension (AIA)
13.11 Support intermediate & root pinning for PINNEDPUBLICKEY
13.12 Support HSTS
- 13.13 Support HPKP
13.14 Support the clienthello extension
14. GnuTLS
- 14.1 SSL engine stuff
14.2 check connection
15. WinSSL/SChannel
@@ -138,7 +122,6 @@
17. SSH protocols
17.1 Multiplexing
- 17.2 SFTP performance
17.3 Support better than MD5 hostkey hash
17.4 Support CURLOPT_PREQUOTE
@@ -146,16 +129,12 @@
18.1 sync
18.2 glob posts
18.3 prevent file overwriting
- 18.4 simultaneous parallel transfers
18.5 UTF-8 filenames in Content-Disposition
- 18.6 warning when setting an option
18.7 at least N milliseconds between requests
18.9 Choose the name of file in braces for complex URLs
18.10 improve how curl works in a windows console window
18.11 Windows: set attribute 'archive' for completed downloads
18.12 keep running, read instructions from pipe/socket
- 18.13 support metalink in http headers
- 18.14 --fail without --location should treat 3xx as a failure
18.15 --retry should resume
18.16 send only part of --data
18.17 consider file name from the redirected URL with -O ?
@@ -202,58 +181,20 @@
See https://github.com/curl/curl/pull/3378
-1.2 More data sharing
-
- curl_share_* functions already exist and work, and they can be extended to
- share more. For example, enable sharing of the ares channel.
-
1.3 struct lifreq
Use 'struct lifreq' and SIOCGLIFADDR instead of 'struct ifreq' and
SIOCGIFADDR on newer Solaris versions as they claim the latter is obsolete.
To support IPv6 interface addresses for network interfaces properly.
-1.4 signal-based resolver timeouts
-
- libcurl built without an asynchronous resolver library uses alarm() to time
- out DNS lookups. When a timeout occurs, this causes libcurl to jump from the
- signal handler back into the library with a sigsetjmp, which effectively
- causes libcurl to continue running within the signal handler. This is
- non-portable and could cause problems on some platforms. A discussion on the
- problem is available at https://curl.haxx.se/mail/lib-2008-09/0197.html
-
- Also, alarm() provides timeout resolution only to the nearest second. alarm
- ought to be replaced by setitimer on systems that support it.
-
1.5 get rid of PATH_MAX
Having code use and rely on PATH_MAX is not nice:
https://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
- Currently the SSH based code uses it a bit, but to remove PATH_MAX from there
- we need libssh2 to properly tell us when we pass in a too small buffer and
- its current API (as of libssh2 1.2.7) doesn't.
-
-1.6 Modified buffer size approach
-
- Current libcurl allocates a fixed 16K size buffer for download and an
- additional 16K for upload. They are always unconditionally part of the easy
- handle. If CRLF translations are requested, an additional 32K "scratch
- buffer" is allocated. A total of 64K transfer buffers in the worst case.
-
- First, while the handles are not actually in use these buffers could be freed
- so that lingering handles just kept in queues or whatever waste less memory.
-
- Secondly, SFTP is a protocol that needs to handle many ~30K blocks at once
- since each need to be individually acked and therefore libssh2 must be
- allowed to send (or receive) many separate ones in parallel to achieve high
- transfer speeds. A current libcurl build with a 16K buffer makes that
- impossible, but one with a 512K buffer will reach MUCH faster transfers. But
- allocating 512K unconditionally for all buffers just in case they would like
- to do fast SFTP transfers at some point is not a good solution either.
-
- Dynamically allocate buffer size depending on protocol in use in combination
- with freeing it after each individual transfer? Other suggestions?
+ Currently the libssh2 SSH based code uses it, but to remove PATH_MAX from
+ there we need libssh2 to properly tell us when we pass in a too small buffer
+ and its current API (as of libssh2 1.2.7) doesn't.
1.7 Support HTTP/2 for HTTP(S) proxies
@@ -377,12 +318,6 @@
Offer support for resolving SRV and URI DNS records for libcurl to know which
server to connect to for various protocols (including HTTP!).
-1.21 Have the URL API offer IDN decoding
-
- Similar to how URL decoding/encoding is done, we could have URL functions to
- convert IDN host names to punycode (probably not the reverse).
- https://github.com/curl/curl/issues/3232
-
1.22 CURLINFO_PAUSE_STATE
Return information about the transfer's current pause state, in both
@@ -407,21 +342,6 @@
https://github.com/curl/curl/issues/2126
-1.26 CURL_REFUSE_CLEARTEXT
-
- An environment variable that when set will make libcurl refuse to use any
- cleartext network protocol. That's all non-encrypted ones (FTP, HTTP, Gopher,
- etc). By adding the check to libcurl and not just curl, this environment
- variable can then help users to block all libcurl-using programs from
- accessing the network using unsafe protocols.
-
- The variable could be given some sort of syntax or different levels and be
- used to also allow for example users to refuse libcurl to do transfers with
- HTTPS certificate checks disabled.
-
- It could also automatically refuse usernames in URLs when set
- (see CURLOPT_DISALLOW_USERNAME_IN_URL)
-
1.27 hardcode the "localhost" addresses
There's this new spec getting adopted that says "localhost" should always and
@@ -539,12 +459,6 @@
Make the detection of (bad) %0d and %0a codes in FTP URL parts earlier in the
process to avoid doing a resolve and connect in vain.
-4.4 REST for large files
-
- REST fix for servers not behaving well on >2GB requests. This should fail if
- the server doesn't set the pointer to the requested index. The tricky
- (impossible?) part is to figure out if the server did the right thing or not.
-
4.5 ASCII support
FTP ASCII transfers do not follow RFC959. They don't convert the data
@@ -577,12 +491,6 @@
"Better" support for persistent connections over HTTP 1.0
https://curl.haxx.se/bug/feature.cgi?id=1089001
-5.2 support FF3 sqlite cookie files
-
- Firefox 3 is changing from its former format to a a sqlite database instead.
- We should consider how (lib)curl can/should support this.
- https://curl.haxx.se/bug/feature.cgi?id=1871388
-
5.3 Rearrange request header order
Server implementors often make an effort to detect browser and to reject
@@ -611,36 +519,19 @@
For example:
- http://test:pass;auth=NTLM@example.com would be equivalent to specifying --user
- test:pass;auth=NTLM or --user test:pass --ntlm from the command line.
+ http://test:pass;auth=NTLM@example.com would be equivalent to specifying
+ --user test:pass;auth=NTLM or --user test:pass --ntlm from the command line.
Additionally this should be implemented for proxy base URLs as well.
-5.6 Refuse "downgrade" redirects
-
- See https://github.com/curl/curl/issues/226
-
- Consider a way to tell curl to refuse to "downgrade" protocol with a redirect
- and/or possibly a bit that refuses redirect to change protocol completely.
-
-5.7 QUIC
-
- The standardization process of QUIC has been taken to the IETF and can be
- followed on the [IETF QUIC Mailing
- list](https://www.ietf.org/mailman/listinfo/quic). I'd like us to get on the
- bandwagon. Ideally, this would be done with a separate library/project to
- handle the binary/framing layer in a similar fashion to how HTTP/2 is
- implemented. This, to allow other projects to benefit from the work and to
- thus broaden the interest and chance of others to participate.
-
6. TELNET
6.1 ditch stdin
-Reading input (to send to the remote server) on stdin is a crappy solution for
-library purposes. We need to invent a good way for the application to be able
-to provide the data to send.
+ Reading input (to send to the remote server) on stdin is a crappy solution
+ for library purposes. We need to invent a good way for the application to be
+ able to provide the data to send.
6.2 ditch telnet-specific select
@@ -650,15 +541,11 @@ to provide the data to send.
6.3 feature negotiation debug data
- Add telnet feature negotiation data to the debug callback as header data.
+ Add telnet feature negotiation data to the debug callback as header data.
7. SMTP
-7.1 Pipelining
-
- Add support for pipelining emails.
-
7.2 Enhanced capability support
Add the ability, for an application that uses libcurl, to obtain the list of
@@ -677,10 +564,6 @@ to provide the data to send.
8. POP3
-8.1 Pipelining
-
- Add support for pipelining commands.
-
8.2 Enhanced capability support
Add the ability, for an application that uses libcurl, to obtain the list of
@@ -725,18 +608,8 @@ that doesn't exist on the server, just like --ftp-create-dirs.
12. New protocols
-12.1 RSYNC
-
- There's no RFC for the protocol or an URI/URL format. An implementation
- should most probably use an existing rsync library, such as librsync.
-
13. SSL
-13.1 Disable specific versions
-
- Provide an option that allows for disabling specific SSL versions, such as
- SSLv2 https://curl.haxx.se/bug/feature.cgi?id=1767276
-
13.2 Provide mutex locking API
Provide a libcurl API for setting mutex callbacks in the underlying SSL
@@ -801,17 +674,6 @@ that doesn't exist on the server, just like --ftp-create-dirs.
Björn Stenberg wrote a separate initial take on DANE that was never
completed.
-13.9 Configurable loading of OpenSSL configuration file
-
- libcurl calls the OpenSSL function CONF_modules_load_file() in openssl.c,
- Curl_ossl_init(). "We regard any changes in the OpenSSL configuration as a
- security risk or at least as unnecessary."
-
- Please add a configuration switch or something similar to disable the
- CONF_modules_load_file() call.
-
- See https://github.com/curl/curl/issues/2724
-
13.10 Support Authority Information Access certificate extension (AIA)
AIA can provide various things like CRLs but more importantly information
@@ -844,21 +706,6 @@ that doesn't exist on the server, just like --ftp-create-dirs.
Doc: https://developer.mozilla.org/en-US/docs/Web/Security/HTTP_strict_transport_security
RFC 6797: https://tools.ietf.org/html/rfc6797
-13.13 Support HPKP
-
- "HTTP Public Key Pinning" is TOFU (trust on first use), time-based
- features indicated by a HTTP header send by the webserver. It's purpose is
- to prevent Man-in-the-middle attacks by trusted CAs by allowing webadmins
- to specify which CAs/certificates/public keys to trust when connection to
- their websites.
-
- It can be build based on PINNEDPUBLICKEY.
-
- Wikipedia: https://en.wikipedia.org/wiki/HTTP_Public_Key_Pinning
- OWASP: https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning
- Doc: https://developer.mozilla.org/de/docs/Web/Security/Public_Key_Pinning
- RFC: https://tools.ietf.org/html/draft-ietf-websec-key-pinning-21
-
13.14 Support the clienthello extension
Certain stupid networks and middle boxes have a problem with SSL handshake
@@ -871,10 +718,6 @@ that doesn't exist on the server, just like --ftp-create-dirs.
14. GnuTLS
-14.1 SSL engine stuff
-
- Is this even possible?
-
14.2 check connection
Add a way to check if the connection seems to be alive, to correspond to the
@@ -949,11 +792,6 @@ that doesn't exist on the server, just like --ftp-create-dirs.
To fix this, libcurl would have to detect an existing connection and "attach"
the new transfer to the existing one.
-17.2 SFTP performance
-
- libcurl's SFTP transfer performance is sub par and can be improved, mostly by
- the approach mentioned in "1.6 Modified buffer size approach".
-
17.3 Support better than MD5 hostkey hash
libcurl offers the CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 option for verifying the
@@ -992,16 +830,6 @@ that doesn't exist on the server, just like --ftp-create-dirs.
existing). So that index.html becomes first index.html.1 and then
index.html.2 etc.
-18.4 simultaneous parallel transfers
-
- The client could be told to use maximum N simultaneous parallel transfers and
- then just make sure that happens. It should of course not make more than one
- connection to the same remote host. This would require the client to use the
- multi interface. https://curl.haxx.se/bug/feature.cgi?id=1558595
-
- Using the multi interface would also allow properly using parallel transfers
- with HTTP/2 and supporting HTTP/2 server push from the command line.
-
18.5 UTF-8 filenames in Content-Disposition
RFC 6266 documents how UTF-8 names can be passed to a client in the
@@ -1009,12 +837,6 @@ that doesn't exist on the server, just like --ftp-create-dirs.
https://github.com/curl/curl/issues/1888
-18.6 warning when setting an option
-
- Display a warning when libcurl returns an error when setting an option.
- This can be useful to tell when support for a particular feature hasn't been
- compiled into the library.
-
18.7 at least N milliseconds between requests
Allow curl command lines issue a lot of request against services that limit
@@ -1063,30 +885,6 @@ that doesn't exist on the server, just like --ftp-create-dirs.
invoke can talk to the still running instance and ask for transfers to get
done, and thus maintain its connection pool, DNS cache and more.
-18.13 support metalink in http headers
-
- Curl has support for downloading a metalink xml file, processing it, and then
- downloading the target of the metalink. This is done via the --metalink option.
- It would be nice if metalink also supported downloading via metalink
- information that is stored in HTTP headers (RFC 6249). Theoretically this could
- also be supported with the --metalink option.
-
- See https://tools.ietf.org/html/rfc6249
-
- See also https://lists.gnu.org/archive/html/bug-wget/2015-06/msg00034.html for
- an implematation of this in wget.
-
-18.14 --fail without --location should treat 3xx as a failure
-
- To allow a command line like this to detect a redirect and consider it a
- failure:
-
- curl -v --fail -O https://example.com/curl-7.48.0.tar.gz
-
- ... --fail must treat 3xx responses as failures too. The least problematic
- way to implement this is probably to add that new logic in the command line
- tool only and not in the underlying CURLOPT_FAILONERROR logic.
-
18.15 --retry should resume
When --retry is used and curl actually retries transfer, it should use the
@@ -1202,17 +1000,17 @@ that doesn't exist on the server, just like --ftp-create-dirs.
20.5 Add support for concurrent connections
- Tests 836, 882 and 938 were designed to verify that separate connections aren't
- used when using different login credentials in protocols that shouldn't re-use
- a connection under such circumstances.
+ Tests 836, 882 and 938 were designed to verify that separate connections
+ aren't used when using different login credentials in protocols that
+ shouldn't re-use a connection under such circumstances.
Unfortunately, ftpserver.pl doesn't appear to support multiple concurrent
- connections. The read while() loop seems to loop until it receives a disconnect
- from the client, where it then enters the waiting for connections loop. When
- the client opens a second connection to the server, the first connection hasn't
- been dropped (unless it has been forced - which we shouldn't do in these tests)
- and thus the wait for connections loop is never entered to receive the second
- connection.
+ connections. The read while() loop seems to loop until it receives a
+ disconnect from the client, where it then enters the waiting for connections
+ loop. When the client opens a second connection to the server, the first
+ connection hasn't been dropped (unless it has been forced - which we
+ shouldn't do in these tests) and thus the wait for connections loop is never
+ entered to receive the second connection.
20.6 Use the RFC6265 test suite
diff --git a/docs/cmdline-opts/Makefile.inc b/docs/cmdline-opts/Makefile.inc
index 7a8af6f9e..6b4387475 100644
--- a/docs/cmdline-opts/Makefile.inc
+++ b/docs/cmdline-opts/Makefile.inc
@@ -65,6 +65,7 @@ DPAGES = \
http1.0.d \
http1.1.d http2.d \
http2-prior-knowledge.d \
+ http3.d \
ignore-content-length.d \
include.d \
insecure.d \
@@ -100,7 +101,10 @@ DPAGES = \
noproxy.d \
ntlm.d ntlm-wb.d \
oauth2-bearer.d \
- output.d pass.d \
+ output.d \
+ pass.d \
+ parallel.d \
+ parallel-max.d \
path-as-is.d \
pinnedpubkey.d \
post301.d \
@@ -154,6 +158,7 @@ DPAGES = \
retry-delay.d \
retry-max-time.d \
retry.d \
+ sasl-authzid.d \
sasl-ir.d \
service-name.d \
show-error.d \
diff --git a/docs/cmdline-opts/config.d b/docs/cmdline-opts/config.d
index ef9894b8e..df3d39220 100644
--- a/docs/cmdline-opts/config.d
+++ b/docs/cmdline-opts/config.d
@@ -40,7 +40,7 @@ Unix-like systems (which returns the home dir given the current user in your
system). On Windows, it then checks for the APPDATA variable, or as a last
resort the '%USERPROFILE%\\Application Data'.
-2) On windows, if there is no _curlrc file in the home dir, it checks for one
+2) On windows, if there is no .curlrc file in the home dir, it checks for one
in the same dir the curl executable is placed. On Unix-like systems, it will
simply try to load .curlrc from the determined home dir.
diff --git a/docs/cmdline-opts/http0.9.d b/docs/cmdline-opts/http0.9.d
index 33fe72d18..7e783f696 100644
--- a/docs/cmdline-opts/http0.9.d
+++ b/docs/cmdline-opts/http0.9.d
@@ -10,5 +10,4 @@ HTTP/0.9 is a completely headerless response and therefore you can also
connect with this to non-HTTP servers and still get a response since curl will
simply transparently downgrade - if allowed.
-A future curl version will deny continuing if the response isn't at least
-HTTP/1.0 unless this option is used.
+Since curl 7.66.0, HTTP/0.9 is disabled by default.
diff --git a/docs/cmdline-opts/http2.d b/docs/cmdline-opts/http2.d
index 04cff00a4..cf8f2988e 100644
--- a/docs/cmdline-opts/http2.d
+++ b/docs/cmdline-opts/http2.d
@@ -6,5 +6,6 @@ Mutexed: http1.1 http1.0 http2-prior-knowledge
Requires: HTTP/2
See-also: no-alpn
Help: Use HTTP 2
+See-also: http1.1 http3
---
Tells curl to use HTTP version 2.
diff --git a/docs/cmdline-opts/http3.d b/docs/cmdline-opts/http3.d
new file mode 100644
index 000000000..ca85e3a64
--- /dev/null
+++ b/docs/cmdline-opts/http3.d
@@ -0,0 +1,19 @@
+Long: http3
+Tags: Versions
+Protocols: HTTP
+Added: 7.66.0
+Mutexed: http1.1 http1.0 http2 http2-prior-knowledge
+Requires: HTTP/3
+Help: Use HTTP v3
+See-also: http1.1 http2
+---
+
+WARNING: this option is experiemental. Do not use in production.
+
+Tells curl to use HTTP version 3 directly to the host and port number used in
+the URL. A normal HTTP/3 transaction will be done to a host and then get
+redirected via Alt-SVc, but this option allows a user to circumvent that when
+you know that the target speaks HTTP/3 on the given host and port.
+
+This option will make curl fail if a QUIC connection cannot be established, it
+cannot fall back to a lower HTTP version on its own.
diff --git a/docs/cmdline-opts/parallel-max.d b/docs/cmdline-opts/parallel-max.d
new file mode 100644
index 000000000..a8c79c743
--- /dev/null
+++ b/docs/cmdline-opts/parallel-max.d
@@ -0,0 +1,9 @@
+Long: parallel-max
+Help: Maximum concurrency for parallel transfers
+Added: 7.66.0
+See-also: parallel
+---
+When asked to do parallel transfers, using --parallel, this option controls
+the maximum amount of transfers to do simultaneously.
+
+The default is 50.
diff --git a/docs/cmdline-opts/parallel.d b/docs/cmdline-opts/parallel.d
new file mode 100644
index 000000000..fac84e624
--- /dev/null
+++ b/docs/cmdline-opts/parallel.d
@@ -0,0 +1,7 @@
+Short: Z
+Long: parallel
+Help: Perform transfers in parallel
+Added: 7.66.0
+---
+Makes curl perform its transfers in parallel as compared to the regular serial
+manner.
diff --git a/docs/cmdline-opts/retry.d b/docs/cmdline-opts/retry.d
index 32d1c799b..3db89b71c 100644
--- a/docs/cmdline-opts/retry.d
+++ b/docs/cmdline-opts/retry.d
@@ -14,4 +14,7 @@ for all forthcoming retries it will double the waiting time until it reaches
using --retry-delay you disable this exponential backoff algorithm. See also
--retry-max-time to limit the total time allowed for retries.
+Since curl 7.66.0, curl will comply with the Retry-After: response header if
+one was present to know when to issue the next retry.
+
If this option is used several times, the last one will be used.
diff --git a/docs/cmdline-opts/sasl-authzid.d b/docs/cmdline-opts/sasl-authzid.d
new file mode 100644
index 000000000..b34db97fc
--- /dev/null
+++ b/docs/cmdline-opts/sasl-authzid.d
@@ -0,0 +1,11 @@
+Long: sasl-authzid
+Help: Use this identity to act as during SASL PLAIN authentication
+Added: 7.66.0
+---
+Use this authorisation identity (authzid), during SASL PLAIN authentication,
+in addition to the authentication identity (authcid) as specified by --user.
+
+If the option isn't specified, the server will derive the authzid from the
+authcid, but if specified, and depending on the server implementation, it may
+be used to access another user's inbox, that the user has been granted access
+to, or a shared mailbox for example.
diff --git a/docs/examples/Makefile.inc b/docs/examples/Makefile.inc
index 8dd55b9df..6fd8ecd76 100644
--- a/docs/examples/Makefile.inc
+++ b/docs/examples/Makefile.inc
@@ -5,7 +5,7 @@
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
-# Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+# Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
@@ -35,7 +35,8 @@ check_PROGRAMS = 10-at-a-time anyauthput cookie_interface debug fileupload \
http2-upload http2-serverpush getredirect ftpuploadfrommem \
ftpuploadresume sslbackend postit2-formadd multi-formadd \
shared-connection-cache sftpuploadresume http2-pushinmemory parseurl \
- urlapi
+ urlapi imap-authzid pop3-authzid smtp-authzid http3 altsvc \
+ http3-present
# These examples require external dependencies that may not be commonly
# available on POSIX systems, so don't bother attempting to compile them here.
diff --git a/docs/examples/altsvc.c b/docs/examples/altsvc.c
new file mode 100644
index 000000000..24ef42585
--- /dev/null
+++ b/docs/examples/altsvc.c
@@ -0,0 +1,56 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed 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.
+ *
+ ***************************************************************************/
+/* <DESC>
+ * HTTP with Alt-Svc support
+ * </DESC>
+ */
+#include <stdio.h>
+#include <curl/curl.h>
+
+int main(void)
+{
+ CURL *curl;
+ CURLcode res;
+
+ curl = curl_easy_init();
+ if(curl) {
+ curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
+
+ /* cache the alternatives in this file */
+ curl_easy_setopt(curl, CURLOPT_ALTSVC, "altsvc.txt");
+
+ /* restrict which HTTP versions to use alternatives */
+ curl_easy_setopt(curl, CURLOPT_ALTSVC_CTRL, (long)
+ CURLALTSVC_H1|CURLALTSVC_H2|CURLALTSVC_H3);
+
+ /* Perform the request, res will get the return code */
+ res = curl_easy_perform(curl);
+ /* Check for errors */
+ if(res != CURLE_OK)
+ fprintf(stderr, "curl_easy_perform() failed: %s\n",
+ curl_easy_strerror(res));
+
+ /* always cleanup */
+ curl_easy_cleanup(curl);
+ }
+ return 0;
+}
diff --git a/docs/examples/curlx.c b/docs/examples/curlx.c
index eb37a6a72..a4d59427a 100644
--- a/docs/examples/curlx.c
+++ b/docs/examples/curlx.c
@@ -277,7 +277,7 @@ int main(int argc, char **argv)
int tabLength = 100;
char *binaryptr;
- char *mimetype;
+ char *mimetype = NULL;
char *mimetypeaccept = NULL;
char *contenttype;
const char **pp;
@@ -294,7 +294,7 @@ int main(int argc, char **argv)
binaryptr = malloc(tabLength);
- p.verbose = 0;
+ memset(&p, '\0', sizeof(p));
p.errorbio = BIO_new_fp(stderr, BIO_NOCLOSE);
curl_global_init(CURL_GLOBAL_DEFAULT);
@@ -372,7 +372,7 @@ int main(int argc, char **argv)
args++;
}
- if(mimetype == NULL || mimetypeaccept == NULL)
+ if(mimetype == NULL || mimetypeaccept == NULL || p.p12file == NULL)
badarg = 1;
if(badarg) {
diff --git a/docs/examples/ephiperfifo.c b/docs/examples/ephiperfifo.c
index 4668c6ca3..9f89125a9 100644
--- a/docs/examples/ephiperfifo.c
+++ b/docs/examples/ephiperfifo.c
@@ -73,12 +73,6 @@ callback.
#include <gnurl/curl.h>
-#ifdef __GNUC__
-#define _Unused __attribute__((unused))
-#else
-#define _Unused
-#endif
-
#define MSG_OUT stdout /* Send info to stdout, change to stderr if you want */
@@ -114,7 +108,7 @@ typedef struct _SockInfo
GlobalInfo *global;
} SockInfo;
-#define __case(code) \
+#define mycase(code) \
case code: s = __STRING(code)
/* Die if we get a bad CURLMcode somewhere */
@@ -123,14 +117,14 @@ static void mcode_or_die(const char *where, CURLMcode code)
if(CURLM_OK != code) {
const char *s;
switch(code) {
- __case(CURLM_BAD_HANDLE); break;
- __case(CURLM_BAD_EASY_HANDLE); break;
- __case(CURLM_OUT_OF_MEMORY); break;
- __case(CURLM_INTERNAL_ERROR); break;
- __case(CURLM_UNKNOWN_OPTION); break;
- __case(CURLM_LAST); break;
+ mycase(CURLM_BAD_HANDLE); break;
+ mycase(CURLM_BAD_EASY_HANDLE); break;
+ mycase(CURLM_OUT_OF_MEMORY); break;
+ mycase(CURLM_INTERNAL_ERROR); break;
+ mycase(CURLM_UNKNOWN_OPTION); break;
+ mycase(CURLM_LAST); break;
default: s = "CURLM_unknown"; break;
- __case(CURLM_BAD_SOCKET);
+ mycase(CURLM_BAD_SOCKET);
fprintf(MSG_OUT, "ERROR: %s returns %s\n", where, s);
/* ignore this error */
return;
@@ -336,22 +330,21 @@ static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp)
/* CURLOPT_WRITEFUNCTION */
-static size_t write_cb(void *ptr _Unused, size_t size, size_t nmemb,
- void *data)
+static size_t write_cb(void *ptr, size_t size, size_t nmemb, void *data)
{
- size_t realsize = size * nmemb;
- (void)_Unused;
+ (void)ptr;
(void)data;
-
- return realsize;
+ return size * nmemb;
}
/* CURLOPT_PROGRESSFUNCTION */
-static int prog_cb(void *p, double dltotal, double dlnow, double ult _Unused,
- double uln _Unused)
+static int prog_cb(void *p, double dltotal, double dlnow, double ult,
+ double uln)
{
ConnInfo *conn = (ConnInfo *)p;
+ (void)ult;
+ (void)uln;
fprintf(MSG_OUT, "Progress: %s (%g/%g)\n", conn->url, dlnow, dltotal);
return 0;
@@ -469,12 +462,14 @@ void SignalHandler(int signo)
}
}
-int main(int argc _Unused, char **argv _Unused)
+int main(int argc, char **argv)
{
GlobalInfo g;
struct itimerspec its;
struct epoll_event ev;
struct epoll_event events[10];
+ (void)argc;
+ (void)argv;
g_should_exit_ = 0;
signal(SIGINT, SignalHandler);
@@ -547,5 +542,6 @@ int main(int argc _Unused, char **argv _Unused)
fflush(MSG_OUT);
curl_multi_cleanup(g.multi);
+ clean_fifo(&g);
return 0;
}
diff --git a/docs/examples/hiperfifo.c b/docs/examples/hiperfifo.c
index fb25259c2..a7f71125a 100644
--- a/docs/examples/hiperfifo.c
+++ b/docs/examples/hiperfifo.c
@@ -72,12 +72,6 @@ callback.
#include <errno.h>
#include <sys/cdefs.h>
-#ifdef __GNUC__
-#define _Unused __attribute__((unused))
-#else
-#define _Unused
-#endif
-
#define MSG_OUT stdout /* Send info to stdout, change to stderr if you want */
@@ -115,7 +109,7 @@ typedef struct _SockInfo
GlobalInfo *global;
} SockInfo;
-#define __case(code) \
+#define mycase(code) \
case code: s = __STRING(code)
/* Die if we get a bad CURLMcode somewhere */
@@ -124,14 +118,14 @@ static void mcode_or_die(const char *where, CURLMcode code)
if(CURLM_OK != code) {
const char *s;
switch(code) {
- __case(CURLM_BAD_HANDLE); break;
- __case(CURLM_BAD_EASY_HANDLE); break;
- __case(CURLM_OUT_OF_MEMORY); break;
- __case(CURLM_INTERNAL_ERROR); break;
- __case(CURLM_UNKNOWN_OPTION); break;
- __case(CURLM_LAST); break;
+ mycase(CURLM_BAD_HANDLE); break;
+ mycase(CURLM_BAD_EASY_HANDLE); break;
+ mycase(CURLM_OUT_OF_MEMORY); break;
+ mycase(CURLM_INTERNAL_ERROR); break;
+ mycase(CURLM_UNKNOWN_OPTION); break;
+ mycase(CURLM_LAST); break;
default: s = "CURLM_unknown"; break;
- __case(CURLM_BAD_SOCKET);
+ mycase(CURLM_BAD_SOCKET);
fprintf(MSG_OUT, "ERROR: %s returns %s\n", where, s);
/* ignore this error */
return;
@@ -143,9 +137,10 @@ static void mcode_or_die(const char *where, CURLMcode code)
/* Update the event timer after curl_multi library calls */
-static int multi_timer_cb(CURLM *multi _Unused, long timeout_ms, GlobalInfo *g)
+static int multi_timer_cb(CURLM *multi, long timeout_ms, GlobalInfo *g)
{
struct timeval timeout;
+ (void)multi;
timeout.tv_sec = timeout_ms/1000;
timeout.tv_usec = (timeout_ms%1000)*1000;
@@ -220,10 +215,12 @@ static void event_cb(int fd, short kind, void *userp)
/* Called by libevent when our timeout expires */
-static void timer_cb(int fd _Unused, short kind _Unused, void *userp)
+static void timer_cb(int fd, short kind, void *userp)
{
GlobalInfo *g = (GlobalInfo *)userp;
CURLMcode rc;
+ (void)fd;
+ (void)kind;
rc = curl_multi_socket_action(g->multi,
CURL_SOCKET_TIMEOUT, 0, &g->still_running);
@@ -303,22 +300,21 @@ static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp)
/* CURLOPT_WRITEFUNCTION */
-static size_t write_cb(void *ptr _Unused, size_t size, size_t nmemb,
- void *data)
+static size_t write_cb(void *ptr, size_t size, size_t nmemb, void *data)
{
- size_t realsize = size * nmemb;
- (void)_Unused;
+ (void)ptr;
(void)data;
-
- return realsize;
+ return size * nmemb;
}
/* CURLOPT_PROGRESSFUNCTION */
-static int prog_cb(void *p, double dltotal, double dlnow, double ult _Unused,
- double uln _Unused)
+static int prog_cb(void *p, double dltotal, double dlnow, double ult,
+ double uln)
{
ConnInfo *conn = (ConnInfo *)p;
+ (void)ult;
+ (void)uln;
fprintf(MSG_OUT, "Progress: %s (%g/%g)\n", conn->url, dlnow, dltotal);
return 0;
@@ -361,12 +357,14 @@ static void new_conn(char *url, GlobalInfo *g)
}
/* This gets called whenever data is received from the fifo */
-static void fifo_cb(int fd _Unused, short event _Unused, void *arg)
+static void fifo_cb(int fd, short event, void *arg)
{
char s[1024];
long int rv = 0;
int n = 0;
GlobalInfo *g = (GlobalInfo *)arg;
+ (void)fd;
+ (void)event;
do {
s[0]='\0';
@@ -427,9 +425,11 @@ static void clean_fifo(GlobalInfo *g)
unlink(fifo);
}
-int main(int argc _Unused, char **argv _Unused)
+int main(int argc, char **argv)
{
GlobalInfo g;
+ (void)argc;
+ (void)argv;
memset(&g, 0, sizeof(GlobalInfo));
g.evbase = event_base_new();
diff --git a/docs/examples/http3-present.c b/docs/examples/http3-present.c
new file mode 100644
index 000000000..857952dc7
--- /dev/null
+++ b/docs/examples/http3-present.c
@@ -0,0 +1,47 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed 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.
+ *
+ ***************************************************************************/
+/* <DESC>
+ * Checks if HTTP/3 support is present in libcurl.
+ * </DESC>
+ */
+#include <stdio.h>
+#include <curl/curl.h>
+
+int main(void)
+{
+ curl_version_info_data *ver;
+
+ curl_global_init(CURL_GLOBAL_ALL);
+
+ ver = curl_version_info(CURLVERSION_NOW);
+ if(ver->features & CURL_VERSION_HTTP2)
+ printf("HTTP/2 support is present\n");
+
+ if(ver->features & CURL_VERSION_HTTP3)
+ printf("HTTP/3 support is present\n");
+
+ if(ver->features & CURL_VERSION_ALTSVC)
+ printf("Alt-svc support is present\n");
+
+ curl_global_cleanup();
+ return 0;
+}
diff --git a/docs/examples/http3.c b/docs/examples/http3.c
new file mode 100644
index 000000000..240a7edd4
--- /dev/null
+++ b/docs/examples/http3.c
@@ -0,0 +1,54 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed 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.
+ *
+ ***************************************************************************/
+/* <DESC>
+ * Very simple HTTP/3 GET
+ * </DESC>
+ */
+#include <stdio.h>
+#include <curl/curl.h>
+
+int main(void)
+{
+ CURL *curl;
+ CURLcode res;
+
+ curl = curl_easy_init();
+ if(curl) {
+ curl_easy_setopt(curl, CURLOPT_URL, "https://example.com");
+
+ /* Forcing HTTP/3 will make the connection fail if the server isn't
+ accessible over QUIC + HTTP/3 on the given host and port.
+ Consider using CURLOPT_ALTSVC instead! */
+ curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, (long)CURL_HTTP_VERSION_3);
+
+ /* Perform the request, res will get the return code */
+ res = curl_easy_perform(curl);
+ /* Check for errors */
+ if(res != CURLE_OK)
+ fprintf(stderr, "curl_easy_perform() failed: %s\n",
+ curl_easy_strerror(res));
+
+ /* always cleanup */
+ curl_easy_cleanup(curl);
+ }
+ return 0;
+}
diff --git a/docs/examples/imap-authzid.c b/docs/examples/imap-authzid.c
new file mode 100644
index 000000000..bfe7d71d7
--- /dev/null
+++ b/docs/examples/imap-authzid.c
@@ -0,0 +1,71 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed 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.
+ *
+ ***************************************************************************/
+
+/* <DESC>
+ * IMAP example showing how to retreieve e-mails from a shared mailed box
+ * </DESC>
+ */
+
+#include <stdio.h>
+#include <curl/curl.h>
+
+/* This is a simple example showing how to fetch mail using libcurl's IMAP
+ * capabilities.
+ *
+ * Note that this example requires libcurl 7.66.0 or above.
+ */
+
+int main(void)
+{
+ CURL *curl;
+ CURLcode res = CURLE_OK;
+
+ curl = curl_easy_init();
+ if(curl) {
+ /* Set the username and password */
+ curl_easy_setopt(curl, CURLOPT_USERNAME, "user");
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret");
+
+ /* Set the authorisation identity (identity to act as) */
+ curl_easy_setopt(curl, CURLOPT_SASL_AUTHZID, "shared-mailbox");
+
+ /* Force PLAIN authentication */
+ curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, "AUTH=PLAIN");
+
+ /* This will fetch message 1 from the user's inbox */
+ curl_easy_setopt(curl, CURLOPT_URL,
+ "imap://imap.example.com/INBOX/;UID=1");
+
+ /* Perform the fetch */
+ res = curl_easy_perform(curl);
+
+ /* Check for errors */
+ if(res != CURLE_OK)
+ fprintf(stderr, "curl_easy_perform() failed: %s\n",
+ curl_easy_strerror(res));
+
+ /* Always cleanup */
+ curl_easy_cleanup(curl);
+ }
+
+ return (int)res;
+}
diff --git a/docs/examples/pop3-authzid.c b/docs/examples/pop3-authzid.c
new file mode 100644
index 000000000..57363579a
--- /dev/null
+++ b/docs/examples/pop3-authzid.c
@@ -0,0 +1,70 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed 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.
+ *
+ ***************************************************************************/
+
+/* <DESC>
+ * POP3 example showing how to retrieve e-mails from a shared mailbox
+ * </DESC>
+ */
+
+#include <stdio.h>
+#include <curl/curl.h>
+
+/* This is a simple example showing how to retrieve mail using libcurl's POP3
+ * capabilities.
+ *
+ * Note that this example requires libcurl 7.66.0 or above.
+ */
+
+int main(void)
+{
+ CURL *curl;
+ CURLcode res = CURLE_OK;
+
+ curl = curl_easy_init();
+ if(curl) {
+ /* Set the username and password */
+ curl_easy_setopt(curl, CURLOPT_USERNAME, "user");
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, "secret");
+
+ /* Set the authorisation identity (identity to act as) */
+ curl_easy_setopt(curl, CURLOPT_SASL_AUTHZID, "shared-mailbox");
+
+ /* Force PLAIN authentication */
+ curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, "AUTH=PLAIN");
+
+ /* This will retrieve message 1 from the user's mailbox */
+ curl_easy_setopt(curl, CURLOPT_URL, "pop3://pop.example.com/1");
+
+ /* Perform the retr */
+ res = curl_easy_perform(curl);
+
+ /* Check for errors */
+ if(res != CURLE_OK)
+ fprintf(stderr, "curl_easy_perform() failed: %s\n",
+ curl_easy_strerror(res));
+
+ /* Always cleanup */
+ curl_easy_cleanup(curl);
+ }
+
+ return (int)res;
+}
diff --git a/docs/examples/smtp-authzid.c b/docs/examples/smtp-authzid.c
new file mode 100644
index 000000000..decdb719d
--- /dev/null
+++ b/docs/examples/smtp-authzid.c
@@ -0,0 +1,161 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed 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.
+ *
+ ***************************************************************************/
+
+/* <DESC>
+ * Send e-mail on behalf of another user with SMTP
+ * </DESC>
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <curl/curl.h>
+
+/*
+ * This is a simple example show how to send an email using libcurl's SMTP
+ * capabilities.
+ *
+ * Note that this example requires libcurl 7.66.0 or above.
+ */
+
+/* The libcurl options want plain addresses, the viewable headers in the mail
+ * can very well get a full name as well.
+ */
+#define FROM_ADDR "<ursel@example.org>"
+#define SENDER_ADDR "<kurt@example.org>"
+#define TO_ADDR "<addressee@example.net>"
+
+#define FROM_MAIL "Ursel " FROM_ADDR
+#define SENDER_MAIL "Kurt " SENDER_ADDR
+#define TO_MAIL "A Receiver " TO_ADDR
+
+static const char *payload_text[] = {
+ "Date: Mon, 29 Nov 2010 21:54:29 +1100\r\n",
+ "To: " TO_MAIL "\r\n",
+ "From: " FROM_MAIL "\r\n",
+ "Sender: " SENDER_MAIL "\r\n",
+ "Message-ID: <dcd7cb36-11db-487a-9f3a-e652a9458efd@"
+ "rfcpedant.example.org>\r\n",
+ "Subject: SMTP example message\r\n",
+ "\r\n", /* empty line to divide headers from body, see RFC5322 */
+ "The body of the message starts here.\r\n",
+ "\r\n",
+ "It could be a lot of lines, could be MIME encoded, whatever.\r\n",
+ "Check RFC5322.\r\n",
+ NULL
+};
+
+struct upload_status {
+ int lines_read;
+};
+
+static size_t payload_source(void *ptr, size_t size, size_t nmemb, void *userp)
+{
+ struct upload_status *upload_ctx = (struct upload_status *)userp;
+ const char *data;
+
+ if((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) {
+ return 0;
+ }
+
+ data = payload_text[upload_ctx->lines_read];
+
+ if(data) {
+ size_t len = strlen(data);
+ memcpy(ptr, data, len);
+ upload_ctx->lines_read++;
+
+ return len;
+ }
+
+ return 0;
+}
+
+int main(void)
+{
+ CURL *curl;
+ CURLcode res = CURLE_OK;
+ struct curl_slist *recipients = NULL;
+ struct upload_status upload_ctx;
+
+ upload_ctx.lines_read = 0;
+
+ curl = curl_easy_init();
+ if(curl) {
+ /* This is the URL for your mailserver. In this example we connect to the
+ smtp-submission port as we require an authenticated connection. */
+ curl_easy_setopt(curl, CURLOPT_URL, "smtp://mail.example.com:587");
+
+ /* Set the username and password */
+ curl_easy_setopt(curl, CURLOPT_USERNAME, "kurt");
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, "xipj3plmq");
+
+ /* Set the authorisation identity (identity to act as) */
+ curl_easy_setopt(curl, CURLOPT_SASL_AUTHZID, "ursel");
+
+ /* Force PLAIN authentication */
+ curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, "AUTH=PLAIN");
+
+ /* Note that this option isn't strictly required, omitting it will result
+ * in libcurl sending the MAIL FROM command with empty sender data. All
+ * autoresponses should have an empty reverse-path, and should be directed
+ * to the address in the reverse-path which triggered them. Otherwise,
+ * they could cause an endless loop. See RFC 5321 Section 4.5.5 for more
+ * details.
+ */
+ curl_easy_setopt(curl, CURLOPT_MAIL_FROM, FROM_ADDR);
+
+ /* Add a recipient, in this particular case it corresponds to the
+ * To: addressee in the header. */
+ recipients = curl_slist_append(recipients, TO_ADDR);
+ curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
+
+ /* We're using a callback function to specify the payload (the headers and
+ * body of the message). You could just use the CURLOPT_READDATA option to
+ * specify a FILE pointer to read from. */
+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, payload_source);
+ curl_easy_setopt(curl, CURLOPT_READDATA, &upload_ctx);
+ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
+
+ /* Send the message */
+ res = curl_easy_perform(curl);
+
+ /* Check for errors */
+ if(res != CURLE_OK)
+ fprintf(stderr, "curl_easy_perform() failed: %s\n",
+ curl_easy_strerror(res));
+
+ /* Free the list of recipients */
+ curl_slist_free_all(recipients);
+
+ /* curl won't send the QUIT command until you call cleanup, so you should
+ * be able to re-use this connection for additional messages (setting
+ * CURLOPT_MAIL_FROM and CURLOPT_MAIL_RCPT as required, and calling
+ * curl_easy_perform() again. It may not be a good idea to keep the
+ * connection open for a very long time though (more than a few minutes
+ * may result in the server timing out the connection), and you do want to
+ * clean up in the end.
+ */
+ curl_easy_cleanup(curl);
+ }
+
+ return (int)res;
+}
diff --git a/docs/libcurl/Makefile.inc b/docs/libcurl/Makefile.inc
index e472ea37b..380c153b8 100644
--- a/docs/libcurl/Makefile.inc
+++ b/docs/libcurl/Makefile.inc
@@ -46,6 +46,7 @@ man_MANS = \
gnurl_multi_info_read.3 \
gnurl_multi_init.3 \
gnurl_multi_perform.3 \
+ gnurl_multi_poll.3 \
gnurl_multi_remove_handle.3 \
gnurl_multi_setopt.3 \
gnurl_multi_socket.3 \
diff --git a/docs/libcurl/curl_multi_poll.3 b/docs/libcurl/curl_multi_poll.3
new file mode 100644
index 000000000..9fc72c55d
--- /dev/null
+++ b/docs/libcurl/curl_multi_poll.3
@@ -0,0 +1,110 @@
+.\" **************************************************************************
+.\" * _ _ ____ _
+.\" * Project ___| | | | _ \| |
+.\" * / __| | | | |_) | |
+.\" * | (__| |_| | _ <| |___
+.\" * \___|\___/|_| \_\_____|
+.\" *
+.\" * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+.\" *
+.\" * This software is licensed 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.
+.\" *
+.\" **************************************************************************
+.TH curl_multi_poll 3 "29 Jul 2019" "libcurl 7.66.0" "libcurl Manual"
+.SH NAME
+curl_multi_poll - polls on all easy handles in a multi handle
+.SH SYNOPSIS
+.nf
+#include <curl/curl.h>
+
+CURLMcode curl_multi_poll(CURLM *multi_handle,
+ struct curl_waitfd extra_fds[],
+ unsigned int extra_nfds,
+ int timeout_ms,
+ int *numfds);
+.ad
+.SH DESCRIPTION
+\fIcurl_multi_poll(3)\fP polls all file descriptors used by the curl easy
+handles contained in the given multi handle set. It will block until activity
+is detected on at least one of the handles or \fItimeout_ms\fP has passed.
+Alternatively, if the multi handle has a pending internal timeout that has a
+shorter expiry time than \fItimeout_ms\fP, that shorter time will be used
+instead to make sure timeout accuracy is reasonably kept.
+
+The calling application may pass additional curl_waitfd structures which are
+similar to \fIpoll(2)\fP's pollfd structure to be waited on in the same call.
+
+On completion, if \fInumfds\fP is non-NULL, it will be populated with the
+total number of file descriptors on which interesting events occurred. This
+number can include both libcurl internal descriptors as well as descriptors
+provided in \fIextra_fds\fP.
+
+If no extra file descriptors are provided and libcurl has no file descriptor
+to offer to wait for, this function will instead wait during \fItimeout_ms\fP
+milliseconds (or shorter if an internal timer indicates so). This is the
+detail that makes this function different than \fIcurl_multi_wait(3)\fP.
+
+This function is encouraged to be used instead of select(3) when using the
+multi interface to allow applications to easier circumvent the common problem
+with 1024 maximum file descriptors.
+.SH curl_waitfd
+.nf
+struct curl_waitfd {
+ curl_socket_t fd;
+ short events;
+ short revents;
+};
+.fi
+.IP CURL_WAIT_POLLIN
+Bit flag to curl_waitfd.events indicating the socket should poll on read
+events such as new data received.
+.IP CURL_WAIT_POLLPRI
+Bit flag to curl_waitfd.events indicating the socket should poll on high
+priority read events such as out of band data.
+.IP CURL_WAIT_POLLOUT
+Bit flag to curl_waitfd.events indicating the socket should poll on write
+events such as the socket being clear to write without blocking.
+.SH EXAMPLE
+.nf
+CURL *easy_handle;
+CURLM *multi_handle;
+
+/* add the individual easy handle */
+curl_multi_add_handle(multi_handle, easy_handle);
+
+do {
+ CURLMcode mc;
+ int numfds;
+
+ mc = curl_multi_perform(multi_handle, &still_running);
+
+ if(mc == CURLM_OK) {
+ /* wait for activity or timeout */
+ mc = curl_multi_poll(multi_handle, NULL, 0, 1000, &numfds);
+ }
+
+ if(mc != CURLM_OK) {
+ fprintf(stderr, "curl_multi failed, code %d.\\n", mc);
+ break;
+ }
+
+} while(still_running);
+
+curl_multi_remove_handle(multi_handle, easy_handle);
+.fi
+.SH RETURN VALUE
+CURLMcode type, general libcurl multi interface error code. See
+\fIlibcurl-errors(3)\fP
+.SH AVAILABILITY
+This function was added in libcurl 7.66.0.
+.SH "SEE ALSO"
+.BR curl_multi_fdset "(3), " curl_multi_perform "(3), " curl_multi_wait "(3)"
diff --git a/docs/libcurl/gnurl_easy_getinfo.3 b/docs/libcurl/gnurl_easy_getinfo.3
index 3133c4f90..3c3d4779e 100644
--- a/docs/libcurl/gnurl_easy_getinfo.3
+++ b/docs/libcurl/gnurl_easy_getinfo.3
@@ -157,6 +157,9 @@ Upload size. See \fICURLINFO_CONTENT_LENGTH_UPLOAD_T(3)\fP
.IP CURLINFO_CONTENT_TYPE
Content type from the Content-Type header.
See \fICURLINFO_CONTENT_TYPE(3)\fP
+.IP CURLINFO_RETRY_AFTER
+The value from the from the Retry-After header.
+See \fICURLINFO_RETRY_AFTER(3)\fP
.IP CURLINFO_PRIVATE
User's private data pointer.
See \fICURLINFO_PRIVATE(3)\fP
diff --git a/docs/libcurl/gnurl_easy_setopt.3 b/docs/libcurl/gnurl_easy_setopt.3
index e8250b0e5..8e622fc17 100644
--- a/docs/libcurl/gnurl_easy_setopt.3
+++ b/docs/libcurl/gnurl_easy_setopt.3
@@ -256,6 +256,8 @@ TLS authentication methods. See \fICURLOPT_TLSAUTH_TYPE(3)\fP
Proxy TLS authentication methods. See \fICURLOPT_PROXY_TLSAUTH_TYPE(3)\fP
.IP CURLOPT_PROXYAUTH
HTTP proxy authentication methods. See \fICURLOPT_PROXYAUTH(3)\fP
+.IP CURLOPT_SASL_AUTHZID
+SASL authorisation identity (identity to act as). See \fICURLOPT_SASL_AUTHZID(3)\fP
.IP CURLOPT_SASL_IR
Enable SASL initial response. See \fICURLOPT_SASL_IR(3)\fP
.IP CURLOPT_XOAUTH2_BEARER
diff --git a/docs/libcurl/gnurl_global_init_mem.3 b/docs/libcurl/gnurl_global_init_mem.3
index d49d4b13d..ddd64db7f 100644
--- a/docs/libcurl/gnurl_global_init_mem.3
+++ b/docs/libcurl/gnurl_global_init_mem.3
@@ -5,7 +5,7 @@
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
-.\" * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
+.\" * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
@@ -59,6 +59,8 @@ to that man page for documentation.
.SH "CAUTION"
Manipulating these gives considerable powers to the application to severely
screw things up for libcurl. Take care!
+.SH AVAILABILITY
+Added in 7.12.0
.SH "SEE ALSO"
.BR curl_global_init "(3), "
.BR curl_global_cleanup "(3), "
diff --git a/docs/libcurl/gnurl_version_info.3 b/docs/libcurl/gnurl_version_info.3
index 09e0fe535..1ba8d8401 100644
--- a/docs/libcurl/gnurl_version_info.3
+++ b/docs/libcurl/gnurl_version_info.3
@@ -78,6 +78,15 @@ typedef struct {
(MAJOR << 24) | (MINOR << 12) | PATCH */
const char *brotli_version; /* human readable string. */
+ /* when 'age is CURLVERSION_SIXTH or alter (7.66.0 or later), these fields
+ also exist */
+ 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 */
+
} curl_version_info_data;
.fi
@@ -99,6 +108,40 @@ environment.
\fIfeatures\fP can have none, one or more bits set, and the currently defined
bits are:
.RS
+.IP CURL_VERSION_ALTSVC
+HTTP Alt-Svc parsing and the associated options (Added in 7.64.1)
+.IP CURL_VERSION_ASYNCHDNS
+libcurl was built with support for asynchronous name lookups, which allows
+more exact timeouts (even on Windows) and less blocking when using the multi
+interface. (added in 7.10.7)
+.IP CURL_VERSION_BROTLI
+supports HTTP Brotli content encoding using libbrotlidec (Added in 7.57.0)
+.IP CURL_VERSION_CONV
+libcurl was built with support for character conversions, as provided by the
+CURLOPT_CONV_* callbacks. (Added in 7.15.4)
+.IP CURL_VERSION_CURLDEBUG
+libcurl was built with memory tracking debug capabilities. This is mainly of
+interest for libcurl hackers. (added in 7.19.6)
+.IP CURL_VERSION_DEBUG
+libcurl was built with debug capabilities (added in 7.10.6)
+.IP CURL_VERSION_GSSAPI
+libcurl was built with support for GSS-API. This makes libcurl use provided
+functions for Kerberos and SPNEGO authentication. It also allows libcurl
+to use the current user credentials without the app having to pass them on.
+(Added in 7.38.0)
+.IP CURL_VERSION_GSSNEGOTIATE
+supports HTTP GSS-Negotiate (added in 7.10.6)
+.IP CURL_VERSION_HTTPS_PROXY
+libcurl was built with support for HTTPS-proxy.
+(Added in 7.52.0)
+.IP CURL_VERSION_HTTP2
+libcurl was built with support for HTTP2.
+(Added in 7.33.0)
+.IP CURL_VERSION_HTTP3
+HTTP/3 and QUIC support are built-in (Added in 7.66.0)
+.IP CURL_VERSION_IDN
+libcurl was built with support for IDNA, domain names with international
+letters. (Added in 7.12.0)
.IP CURL_VERSION_IPV6
supports IPv6
.IP CURL_VERSION_KERBEROS4
@@ -106,68 +149,38 @@ supports Kerberos V4 (when using FTP)
.IP CURL_VERSION_KERBEROS5
supports Kerberos V5 authentication for FTP, IMAP, POP3, SMTP and SOCKSv5 proxy
(Added in 7.40.0)
-.IP CURL_VERSION_SSL
-supports SSL (HTTPS/FTPS) (Added in 7.10)
+.IP CURL_VERSION_LARGEFILE
+libcurl was built with support for large files. (Added in 7.11.1)
.IP CURL_VERSION_LIBZ
supports HTTP deflate using libz (Added in 7.10)
+.IP CURL_VERSION_MULTI_SSL
+libcurl was built with multiple SSL backends. For details, see
+\fIcurl_global_sslset(3)\fP.
+(Added in 7.56.0)
.IP CURL_VERSION_NTLM
supports HTTP NTLM (added in 7.10.6)
-.IP CURL_VERSION_GSSNEGOTIATE
-supports HTTP GSS-Negotiate (added in 7.10.6)
-.IP CURL_VERSION_DEBUG
-libcurl was built with debug capabilities (added in 7.10.6)
-.IP CURL_VERSION_CURLDEBUG
-libcurl was built with memory tracking debug capabilities. This is mainly of
-interest for libcurl hackers. (added in 7.19.6)
-.IP CURL_VERSION_ASYNCHDNS
-libcurl was built with support for asynchronous name lookups, which allows
-more exact timeouts (even on Windows) and less blocking when using the multi
-interface. (added in 7.10.7)
+.IP CURL_VERSION_NTLM_WB
+libcurl was built with support for NTLM delegation to a winbind helper.
+(Added in 7.22.0)
+.IP CURL_VERSION_PSL
+libcurl was built with support for Mozilla's Public Suffix List. This makes
+libcurl ignore cookies with a domain that's on the list.
+(Added in 7.47.0)
.IP CURL_VERSION_SPNEGO
libcurl was built with support for SPNEGO authentication (Simple and Protected
GSS-API Negotiation Mechanism, defined in RFC 2478.) (added in 7.10.8)
-.IP CURL_VERSION_LARGEFILE
-libcurl was built with support for large files. (Added in 7.11.1)
-.IP CURL_VERSION_IDN
-libcurl was built with support for IDNA, domain names with international
-letters. (Added in 7.12.0)
+.IP CURL_VERSION_SSL
+supports SSL (HTTPS/FTPS) (Added in 7.10)
.IP CURL_VERSION_SSPI
libcurl was built with support for SSPI. This is only available on Windows and
makes libcurl use Windows-provided functions for Kerberos, NTLM, SPNEGO and
Digest authentication. It also allows libcurl to use the current user
credentials without the app having to pass them on. (Added in 7.13.2)
-.IP CURL_VERSION_GSSAPI
-libcurl was built with support for GSS-API. This makes libcurl use provided
-functions for Kerberos and SPNEGO authentication. It also allows libcurl
-to use the current user credentials without the app having to pass them on.
-(Added in 7.38.0)
-.IP CURL_VERSION_CONV
-libcurl was built with support for character conversions, as provided by the
-CURLOPT_CONV_* callbacks. (Added in 7.15.4)
.IP CURL_VERSION_TLSAUTH_SRP
libcurl was built with support for TLS-SRP. (Added in 7.21.4)
-.IP CURL_VERSION_NTLM_WB
-libcurl was built with support for NTLM delegation to a winbind helper.
-(Added in 7.22.0)
-.IP CURL_VERSION_HTTP2
-libcurl was built with support for HTTP2.
-(Added in 7.33.0)
.IP CURL_VERSION_UNIX_SOCKETS
libcurl was built with support for Unix domain sockets.
(Added in 7.40.0)
-.IP CURL_VERSION_PSL
-libcurl was built with support for Mozilla's Public Suffix List. This makes
-libcurl ignore cookies with a domain that's on the list.
-(Added in 7.47.0)
-.IP CURL_VERSION_HTTPS_PROXY
-libcurl was built with support for HTTPS-proxy.
-(Added in 7.52.0)
-.IP CURL_VERSION_MULTI_SSL
-libcurl was built with multiple SSL backends. For details, see
-\fIcurl_global_sslset(3)\fP.
-(Added in 7.56.0)
-.IP CURL_VERSION_BROTLI
-supports HTTP Brotli content encoding using libbrotlidec (Added in 7.57.0)
.RE
\fIssl_version\fP is an ASCII string for the TLS library name + version
used. If libcurl has no SSL support, this is NULL. For example "Schannel",
diff --git a/docs/libcurl/libgnurl-errors.3 b/docs/libcurl/libgnurl-errors.3
index 26def4fec..2697efd5b 100644
--- a/docs/libcurl/libgnurl-errors.3
+++ b/docs/libcurl/libgnurl-errors.3
@@ -254,6 +254,8 @@ Status returned failure when asked with \fICURLOPT_SSL_VERIFYSTATUS(3)\fP.
Stream error in the HTTP/2 framing layer.
.IP "CURLE_RECURSIVE_API_CALL (93)"
An API function was called from inside a callback.
+.IP "CURLE_AUTH_ERROR (94)"
+An authentication function returned an error.
.IP "CURLE_OBSOLETE*"
These error codes will never be returned. They were used in an old libcurl
version and are currently unused.
diff --git a/docs/libcurl/opts/CURLINFO_RETRY_AFTER.3 b/docs/libcurl/opts/CURLINFO_RETRY_AFTER.3
new file mode 100644
index 000000000..9e58ca62d
--- /dev/null
+++ b/docs/libcurl/opts/CURLINFO_RETRY_AFTER.3
@@ -0,0 +1,63 @@
+.\" **************************************************************************
+.\" * _ _ ____ _
+.\" * Project ___| | | | _ \| |
+.\" * / __| | | | |_) | |
+.\" * | (__| |_| | _ <| |___
+.\" * \___|\___/|_| \_\_____|
+.\" *
+.\" * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+.\" *
+.\" * This software is licensed 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.
+.\" *
+.\" **************************************************************************
+.\"
+.TH CURLINFO_RETRY_AFTER 3 "6 Aug 2019" "libcurl 7.66.0" "curl_easy_getinfo options"
+.SH NAME
+CURLINFO_RETRY_AFTER \- returns the Retry-After retry delay
+.SH SYNOPSIS
+#include <curl/curl.h>
+
+CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_RETRY_AFTER, curl_off_t *retry);
+.SH DESCRIPTION
+Pass a pointer to a curl_off_t variable to receive the number of seconds the
+HTTP server suggesets the client should wait until the next request is
+issued. The information from the "Retry-After:" header.
+
+While the HTTP header might contain a fixed date string, the
+\fICURLINFO_RETRY_AFTER(3)\fP will alwaus return number of seconds to wait -
+or zero if there was no header or the header couldn't be parsed.
+.SH DEFAULT
+Returns zero delay if there was no header.
+.SH PROTOCOLS
+HTTP(S)
+.SH EXAMPLE
+.nf
+CURL *curl = curl_easy_init();
+if(curl) {
+ CURLcode res;
+ curl_easy_setopt(curl, CURLOPT_URL, "http://example.com");
+ res = curl_easy_perform(curl);
+ if(res == CURLE_OK) {
+ curl_off_t wait = 0;
+ curl_easy_getinfo(curl, CURLINFO_RETRY_AFTER, &wait);
+ if(wait)
+ printf("Wait for %" CURL_FORMAT_CURL_OFF_T " seconds\\n", wait);
+ }
+ curl_easy_cleanup(curl);
+}
+.fi
+.SH AVAILABILITY
+Added in curl 7.66.0
+.SH RETURN VALUE
+Returns CURLE_OK if the option is supported, and CURLE_UNKNOWN_OPTION if not.
+.SH "SEE ALSO"
+.BR CURLOPT_STDERR "(3), " CURLOPT_HEADERFUNCTION "(3), "
diff --git a/docs/libcurl/opts/CURLOPT_SASL_AUTHZID.3 b/docs/libcurl/opts/CURLOPT_SASL_AUTHZID.3
new file mode 100644
index 000000000..65445475d
--- /dev/null
+++ b/docs/libcurl/opts/CURLOPT_SASL_AUTHZID.3
@@ -0,0 +1,64 @@
+.\" **************************************************************************
+.\" * _ _ ____ _
+.\" * Project ___| | | | _ \| |
+.\" * / __| | | | |_) | |
+.\" * | (__| |_| | _ <| |___
+.\" * \___|\___/|_| \_\_____|
+.\" *
+.\" * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+.\" *
+.\" * This software is licensed 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.
+.\" *
+.\" **************************************************************************
+.\"
+.TH CURLOPT_SASL_AUTHZID 3 "11 Sep 2019" "libcurl 7.66.0" "curl_easy_setopt options"
+.SH NAME
+CURLOPT_SASL_AUTHZID \- authorisation identity (identity to act as)
+.SH SYNOPSIS
+#include <curl/curl.h>
+
+CURLcode curl_easy_setopt(CURL *handle, CURLOPT_SASL_AUTHZID, char *authzid);
+.SH DESCRIPTION
+Pass a char * as parameter, which should be pointing to the zero terminated
+authorisation identity (authzid) for the transfer. Only applicable to the PLAIN
+SASL authentication mechanism where it is optional.
+
+When not specified only the authentication identity (authcid) as specified by
+the username will be sent to the server, along with the password. The server
+will derive a authzid from the authcid when not provided, which it will then
+uses internally.
+
+When the authzid is specified, the use of which is server dependent, it can be
+used to access another user's inbox, that the user has been granted access to,
+or a shared mailbox for example.
+.SH DEFAULT
+blank
+.SH PROTOCOLS
+IMAP, POP3 and SMTP
+.SH EXAMPLE
+.nf
+CURL *curl = curl_easy_init();
+if(curl) {
+ curl_easy_setopt(curl, CURLOPT_URL, "imap://example.com/");
+ curl_easy_setopt(curl, CURLOPT_USERNAME, "Kurt");
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, "xipj3plmq");
+ curl_easy_setopt(curl, CURLOPT_SASL_AUTHZID, "Ursel");
+ ret = curl_easy_perform(curl);
+ curl_easy_cleanup(curl);
+}
+.fi
+.SH AVAILABILITY
+Added in 7.66.0
+.SH RETURN VALUE
+Returns CURLE_OK if the option is supported, and CURLE_UNKNOWN_OPTION if not.
+.SH "SEE ALSO"
+.BR CURLOPT_USERNAME "(3), " CURLOPT_PASSWORD "(3), ".BR CURLOPT_USERPWD "(3)"
diff --git a/docs/libcurl/opts/GNURLINFO_APPCONNECT_TIME.3 b/docs/libcurl/opts/GNURLINFO_APPCONNECT_TIME.3
index ce04fa6f4..03ade4a3c 100644
--- a/docs/libcurl/opts/GNURLINFO_APPCONNECT_TIME.3
+++ b/docs/libcurl/opts/GNURLINFO_APPCONNECT_TIME.3
@@ -5,7 +5,7 @@
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
-.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+.\" * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
@@ -34,6 +34,8 @@ This time is most often very near to the \fICURLINFO_PRETRANSFER_TIME(3)\fP
time, except for cases such as HTTP pipelining where the pretransfer time can
be delayed due to waits in line for the pipeline and more.
+When a redirect is followed, the time from each request is added together.
+
See also the TIMES overview in the \fIcurl_easy_getinfo(3)\fP man page.
.SH PROTOCOLS
All
diff --git a/docs/libcurl/opts/GNURLINFO_APPCONNECT_TIME_T.3 b/docs/libcurl/opts/GNURLINFO_APPCONNECT_TIME_T.3
index e218fcc37..1ae9e6c9e 100644
--- a/docs/libcurl/opts/GNURLINFO_APPCONNECT_TIME_T.3
+++ b/docs/libcurl/opts/GNURLINFO_APPCONNECT_TIME_T.3
@@ -5,7 +5,7 @@
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
-.\" * Copyright (C) 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+.\" * Copyright (C) 2018 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
@@ -35,6 +35,8 @@ This time is most often very near to the \fICURLINFO_PRETRANSFER_TIME_T(3)\fP
time, except for cases such as HTTP pipelining where the pretransfer time can
be delayed due to waits in line for the pipeline and more.
+When a redirect is followed, the time from each request is added together.
+
See also the TIMES overview in the \fIcurl_easy_getinfo(3)\fP man page.
.SH PROTOCOLS
All
diff --git a/docs/libcurl/opts/GNURLINFO_CONNECT_TIME.3 b/docs/libcurl/opts/GNURLINFO_CONNECT_TIME.3
index 3767cd84d..651850cde 100644
--- a/docs/libcurl/opts/GNURLINFO_CONNECT_TIME.3
+++ b/docs/libcurl/opts/GNURLINFO_CONNECT_TIME.3
@@ -5,7 +5,7 @@
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
-.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+.\" * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
@@ -31,6 +31,8 @@ CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_CONNECT_TIME, double *timep);
Pass a pointer to a double to receive the total time in seconds from the start
until the connection to the remote host (or proxy) was completed.
+When a redirect is followed, the time from each request is added together.
+
See also the TIMES overview in the \fIcurl_easy_getinfo(3)\fP man page.
.SH PROTOCOLS
All
diff --git a/docs/libcurl/opts/GNURLINFO_CONNECT_TIME_T.3 b/docs/libcurl/opts/GNURLINFO_CONNECT_TIME_T.3
index eaa6f551e..3479cc851 100644
--- a/docs/libcurl/opts/GNURLINFO_CONNECT_TIME_T.3
+++ b/docs/libcurl/opts/GNURLINFO_CONNECT_TIME_T.3
@@ -5,7 +5,7 @@
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
-.\" * Copyright (C) 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+.\" * Copyright (C) 2018 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
@@ -30,6 +30,9 @@ CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_CONNECT_TIME_T, curl_off_t *ti
.SH DESCRIPTION
Pass a pointer to a curl_off_t to receive the total time in microseconds
from the start until the connection to the remote host (or proxy) was completed.
+
+When a redirect is followed, the time from each request is added together.
+
See also the TIMES overview in the \fIcurl_easy_getinfo(3)\fP man page.
.SH PROTOCOLS
All
diff --git a/docs/libcurl/opts/GNURLINFO_HTTP_VERSION.3 b/docs/libcurl/opts/GNURLINFO_HTTP_VERSION.3
index 3af6665f0..caa43c725 100644
--- a/docs/libcurl/opts/GNURLINFO_HTTP_VERSION.3
+++ b/docs/libcurl/opts/GNURLINFO_HTTP_VERSION.3
@@ -5,7 +5,7 @@
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
-.\" * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
+.\" * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
@@ -28,9 +28,10 @@ CURLINFO_HTTP_VERSION \- get the http version used in the connection
CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_HTTP_VERSION, long *p);
.SH DESCRIPTION
-Pass a pointer to a long to receive the version used in the last http connection.
-The returned value will be CURL_HTTP_VERSION_1_0, CURL_HTTP_VERSION_1_1, or
-CURL_HTTP_VERSION_2_0, or 0 if the version can't be determined.
+Pass a pointer to a long to receive the version used in the last http
+connection. The returned value will be CURL_HTTP_VERSION_1_0,
+CURL_HTTP_VERSION_1_1, CURL_HTTP_VERSION_2_0, CURL_HTTP_VERSION_3 or 0 if the
+version can't be determined.
.SH PROTOCOLS
HTTP
.SH EXAMPLE
diff --git a/docs/libcurl/opts/GNURLINFO_NAMELOOKUP_TIME.3 b/docs/libcurl/opts/GNURLINFO_NAMELOOKUP_TIME.3
index ecb93050c..52d374d1f 100644
--- a/docs/libcurl/opts/GNURLINFO_NAMELOOKUP_TIME.3
+++ b/docs/libcurl/opts/GNURLINFO_NAMELOOKUP_TIME.3
@@ -5,7 +5,7 @@
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
-.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+.\" * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
@@ -31,6 +31,8 @@ CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_NAMELOOKUP_TIME, double *timep
Pass a pointer to a double to receive the total time in seconds from the start
until the name resolving was completed.
+When a redirect is followed, the time from each request is added together.
+
See also the TIMES overview in the \fIcurl_easy_getinfo(3)\fP man page.
.SH PROTOCOLS
All
diff --git a/docs/libcurl/opts/GNURLINFO_NAMELOOKUP_TIME_T.3 b/docs/libcurl/opts/GNURLINFO_NAMELOOKUP_TIME_T.3
index 012cd7343..542df9736 100644
--- a/docs/libcurl/opts/GNURLINFO_NAMELOOKUP_TIME_T.3
+++ b/docs/libcurl/opts/GNURLINFO_NAMELOOKUP_TIME_T.3
@@ -5,7 +5,7 @@
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
-.\" * Copyright (C) 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+.\" * Copyright (C) 2018 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
@@ -31,6 +31,8 @@ CURLcode curl_easy_getinfo(CURL *handle, CURLINFO_NAMELOOKUP_TIME_T, curl_off_t
Pass a pointer to a curl_off_t to receive the total time in microseconds
from the start until the name resolving was completed.
+When a redirect is followed, the time from each request is added together.
+
See also the TIMES overview in the \fIcurl_easy_getinfo(3)\fP man page.
.SH PROTOCOLS
All
diff --git a/docs/libcurl/opts/GNURLINFO_PRETRANSFER_TIME.3 b/docs/libcurl/opts/GNURLINFO_PRETRANSFER_TIME.3
index 8026f82e2..515293439 100644
--- a/docs/libcurl/opts/GNURLINFO_PRETRANSFER_TIME.3
+++ b/docs/libcurl/opts/GNURLINFO_PRETRANSFER_TIME.3
@@ -5,7 +5,7 @@
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
-.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+.\" * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
@@ -34,6 +34,8 @@ pre-transfer commands and negotiations that are specific to the particular
protocol(s) involved. It does \fInot\fP involve the sending of the protocol-
specific request that triggers a transfer.
+When a redirect is followed, the time from each request is added together.
+
See also the TIMES overview in the \fIcurl_easy_getinfo(3)\fP man page.
.SH PROTOCOLS
All
diff --git a/docs/libcurl/opts/GNURLINFO_PRETRANSFER_TIME_T.3 b/docs/libcurl/opts/GNURLINFO_PRETRANSFER_TIME_T.3
index e67fab94b..1cccdef70 100644
--- a/docs/libcurl/opts/GNURLINFO_PRETRANSFER_TIME_T.3
+++ b/docs/libcurl/opts/GNURLINFO_PRETRANSFER_TIME_T.3
@@ -5,7 +5,7 @@
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
-.\" * Copyright (C) 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+.\" * Copyright (C) 2018 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
@@ -35,6 +35,8 @@ pre-transfer commands and negotiations that are specific to the particular
protocol(s) involved. It does \fInot\fP involve the sending of the protocol-
specific request that triggers a transfer.
+When a redirect is followed, the time from each request is added together.
+
See also the TIMES overview in the \fIcurl_easy_getinfo(3)\fP man page.
.SH PROTOCOLS
All
diff --git a/docs/libcurl/opts/GNURLINFO_STARTTRANSFER_TIME.3 b/docs/libcurl/opts/GNURLINFO_STARTTRANSFER_TIME.3
index af7fc5dd9..6ac4707c8 100644
--- a/docs/libcurl/opts/GNURLINFO_STARTTRANSFER_TIME.3
+++ b/docs/libcurl/opts/GNURLINFO_STARTTRANSFER_TIME.3
@@ -5,7 +5,7 @@
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
-.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+.\" * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
@@ -33,6 +33,8 @@ start until the first byte is received by libcurl. This includes
\fICURLINFO_PRETRANSFER_TIME(3)\fP and also the time the server needs to
calculate the result.
+When a redirect is followed, the time from each request is added together.
+
See also the TIMES overview in the \fIcurl_easy_getinfo(3)\fP man page.
.SH PROTOCOLS
All
diff --git a/docs/libcurl/opts/GNURLINFO_STARTTRANSFER_TIME_T.3 b/docs/libcurl/opts/GNURLINFO_STARTTRANSFER_TIME_T.3
index 5e19ab590..db71fd8e2 100644
--- a/docs/libcurl/opts/GNURLINFO_STARTTRANSFER_TIME_T.3
+++ b/docs/libcurl/opts/GNURLINFO_STARTTRANSFER_TIME_T.3
@@ -5,7 +5,7 @@
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
-.\" * Copyright (C) 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+.\" * Copyright (C) 2018 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
@@ -34,6 +34,8 @@ start until the first byte is received by libcurl. This includes
\fICURLINFO_PRETRANSFER_TIME_T(3)\fP and also the time the server needs to
calculate the result.
+When a redirect is followed, the time from each request is added together.
+
See also the TIMES overview in the \fIcurl_easy_getinfo(3)\fP man page.
.SH PROTOCOLS
All
diff --git a/docs/libcurl/opts/GNURLINFO_TOTAL_TIME.3 b/docs/libcurl/opts/GNURLINFO_TOTAL_TIME.3
index da1ae6465..bab982cdc 100644
--- a/docs/libcurl/opts/GNURLINFO_TOTAL_TIME.3
+++ b/docs/libcurl/opts/GNURLINFO_TOTAL_TIME.3
@@ -5,7 +5,7 @@
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
-.\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+.\" * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
@@ -32,6 +32,8 @@ Pass a pointer to a double to receive the total time in seconds for the
previous transfer, including name resolving, TCP connect etc. The double
represents the time in seconds, including fractions.
+When a redirect is followed, the time from each request is added together.
+
See also the TIMES overview in the \fIcurl_easy_getinfo(3)\fP man page.
.SH PROTOCOLS
All
diff --git a/docs/libcurl/opts/GNURLINFO_TOTAL_TIME_T.3 b/docs/libcurl/opts/GNURLINFO_TOTAL_TIME_T.3
index 3796e8fc7..70cd7e567 100644
--- a/docs/libcurl/opts/GNURLINFO_TOTAL_TIME_T.3
+++ b/docs/libcurl/opts/GNURLINFO_TOTAL_TIME_T.3
@@ -5,7 +5,7 @@
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
-.\" * Copyright (C) 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+.\" * Copyright (C) 2018 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
@@ -32,6 +32,8 @@ Pass a pointer to a curl_off_t to receive the total time in microseconds
for the previous transfer, including name resolving, TCP connect etc.
The curl_off_t represents the time in microseconds.
+When a redirect is followed, the time from each request is added together.
+
See also the TIMES overview in the \fIcurl_easy_getinfo(3)\fP man page.
.SH PROTOCOLS
All
diff --git a/docs/libcurl/opts/GNURLOPT_ALTSVC.3 b/docs/libcurl/opts/GNURLOPT_ALTSVC.3
index 156f4e979..d1d44629e 100644
--- a/docs/libcurl/opts/GNURLOPT_ALTSVC.3
+++ b/docs/libcurl/opts/GNURLOPT_ALTSVC.3
@@ -39,6 +39,8 @@ Pass in a pointer to a \fIfilename\fP to instruct libcurl to use that file as
the Alt-Svc cache to read existing cache contents from and possibly also write
it back to a after a transfer, unless \fBCURLALTSVC_READONLYFILE\fP is set in
\fICURLOPT_ALTSVC_CTRL(3)\fP.
+
+Specify a blank file name ("") to make libcurl not load from a file at all.
.SH DEFAULT
NULL. The alt-svc cache is not read nor written to file.
.SH PROTOCOLS
diff --git a/docs/libcurl/opts/GNURLOPT_ALTSVC_CTRL.3 b/docs/libcurl/opts/GNURLOPT_ALTSVC_CTRL.3
index aed1253dd..fa8e88967 100644
--- a/docs/libcurl/opts/GNURLOPT_ALTSVC_CTRL.3
+++ b/docs/libcurl/opts/GNURLOPT_ALTSVC_CTRL.3
@@ -28,7 +28,6 @@ CURLOPT_ALTSVC_CTRL \- control alt-svc behavior
#include <gnurl/curl.h>
#define CURLALTSVC_IMMEDIATELY (1<<0)
-#define CURLALTSVC_ALTUSED (1<<1)
#define CURLALTSVC_READONLYFILE (1<<2)
#define CURLALTSVC_H1 (1<<3)
#define CURLALTSVC_H2 (1<<4)
@@ -53,10 +52,8 @@ sure both the source and the destination are legitimate.
Setting any bit will enable the alt-svc engine.
.IP "CURLALTSVC_IMMEDIATELY"
If an Alt-Svc: header is received, this instructs libcurl to switch to one of
-those alternatives asap rather than to save it and use for the next request.
-.IP "CURLALTSVC_ALTUSED"
-Issue the Alt-Used: header in all requests that have been redirected by
-alt-svc.
+those alternatives asap rather than to save it and use for the next
+request. (Not currently supported).
.IP "CURLALTSVC_READONLYFILE"
Do not write the alt-svc cache back to the file specified with
\fICURLOPT_ALTSVC(3)\fP even if it gets updated. By default a file specified
diff --git a/docs/libcurl/opts/GNURLOPT_HEADERFUNCTION.3 b/docs/libcurl/opts/GNURLOPT_HEADERFUNCTION.3
index 48bdbdaaf..5a569fef9 100644
--- a/docs/libcurl/opts/GNURLOPT_HEADERFUNCTION.3
+++ b/docs/libcurl/opts/GNURLOPT_HEADERFUNCTION.3
@@ -52,7 +52,7 @@ an error to the library. This will cause the transfer to get aborted and the
libcurl function in progress will return \fICURLE_WRITE_ERROR\fP.
A complete HTTP header that is passed to this function can be up to
-\fICURL_MAX_HTTP_HEADER\fP (100K) bytes.
+\fICURL_MAX_HTTP_HEADER\fP (100K) bytes and includes the final line terminator.
If this option is not set, or if it is set to NULL, but
\fICURLOPT_HEADERDATA(3)\fP is set to anything but NULL, the function used to
@@ -67,6 +67,9 @@ negotiation. If you need to operate on only the headers from the final
response, you will need to collect headers in the callback yourself and use
HTTP status lines, for example, to delimit response boundaries.
+For an HTTP transfer, the status line and the blank line preceding the response
+body are both included as headers and passed to this function.
+
When a server sends a chunked encoded transfer, it may contain a trailer. That
trailer is identical to an HTTP header and if such a trailer is received it is
passed to the application using this callback as well. There are several ways
diff --git a/docs/libcurl/opts/GNURLOPT_HTTP09_ALLOWED.3 b/docs/libcurl/opts/GNURLOPT_HTTP09_ALLOWED.3
index e1a658072..8856c1a19 100644
--- a/docs/libcurl/opts/GNURLOPT_HTTP09_ALLOWED.3
+++ b/docs/libcurl/opts/GNURLOPT_HTTP09_ALLOWED.3
@@ -5,7 +5,7 @@
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
-.\" * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+.\" * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
@@ -31,12 +31,12 @@ CURLcode curl_easy_setopt(CURL *handle, CURLOPT_HTTP09_ALLOWED, long allowed);
Pass the long argument \fIallowed\fP set to 1L to allow HTTP/0.9 responses.
A HTTP/0.9 response is a server response entirely without headers and only a
-body, while you can connect to lots of random TCP services and still get a
-response that curl might consider to be HTTP/0.9.
+body. You can connect to lots of random TCP services and still get a response
+that curl might consider to be HTTP/0.9!
.SH DEFAULT
-curl allows HTTP/0.9 responses by default.
+curl allowed HTTP/0.9 responses by default before 7.66.0
-A future curl version will require this option to be set to allow HTTP/0.9
+Since 7.66.0, libcurl requires this option set to 1L to allow HTTP/0.9
responses.
.SH PROTOCOLS
HTTP
diff --git a/docs/libcurl/opts/GNURLOPT_HTTP_VERSION.3 b/docs/libcurl/opts/GNURLOPT_HTTP_VERSION.3
index 3716ff933..260363e16 100644
--- a/docs/libcurl/opts/GNURLOPT_HTTP_VERSION.3
+++ b/docs/libcurl/opts/GNURLOPT_HTTP_VERSION.3
@@ -5,7 +5,7 @@
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
-.\" * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+.\" * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
@@ -57,6 +57,14 @@ Issue non-TLS HTTP requests using HTTP/2 without HTTP/1.1 Upgrade. It requires
prior knowledge that the server supports HTTP/2 straight away. HTTPS requests
will still do HTTP/2 the standard way with negotiated protocol version in the
TLS handshake. (Added in 7.49.0)
+.IP CURL_HTTP_VERSION_3
+(Added in 7.66.0) Setting this value will make libcurl attempt to use HTTP/3
+directly to server given in the URL. Note that this cannot gracefully
+downgrade to earlier HTTP version if the server doesn't support HTTP/3.
+
+For more reliably upgrading to HTTP/3, set the prefered version to something
+lower and let the server announce its HTTP/3 support via Alt-Svc:. See
+\fICURLOPT_ALTSVC(3)\fP.
.SH DEFAULT
Since curl 7.62.0: CURL_HTTP_VERSION_2TLS
@@ -82,4 +90,4 @@ Along with HTTP
Returns CURLE_OK if HTTP is supported, and CURLE_UNKNOWN_OPTION if not.
.SH "SEE ALSO"
.BR CURLOPT_SSLVERSION "(3), " CURLOPT_HTTP200ALIASES "(3), "
-.BR CURLOPT_HTTP09_ALLOWED "(3), "
+.BR CURLOPT_HTTP09_ALLOWED "(3), " CURLOPT_ALTSVC "(3) "
diff --git a/docs/libcurl/opts/GNURLOPT_POST.3 b/docs/libcurl/opts/GNURLOPT_POST.3
index 70f3da8db..0b3080e0d 100644
--- a/docs/libcurl/opts/GNURLOPT_POST.3
+++ b/docs/libcurl/opts/GNURLOPT_POST.3
@@ -5,7 +5,7 @@
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
-.\" * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+.\" * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
@@ -55,7 +55,8 @@ If you use POST to an HTTP 1.1 server, you can send data without knowing the
size before starting the POST if you use chunked encoding. You enable this by
adding a header like "Transfer-Encoding: chunked" with
\fICURLOPT_HTTPHEADER(3)\fP. With HTTP 1.0 or without chunked transfer, you
-must specify the size in the request.
+must specify the size in the request. (Since 7.66.0, libcurl will
+automatically use chunked encoding for POSTs if the size is unknown.)
When setting \fICURLOPT_POST(3)\fP to 1, libcurl will automatically set
\fICURLOPT_NOBODY(3)\fP and \fICURLOPT_HTTPGET(3)\fP to 0.
diff --git a/docs/libcurl/opts/GNURLOPT_PROXY_SSL_VERIFYHOST.3 b/docs/libcurl/opts/GNURLOPT_PROXY_SSL_VERIFYHOST.3
index b68aea1ca..afcc51413 100644
--- a/docs/libcurl/opts/GNURLOPT_PROXY_SSL_VERIFYHOST.3
+++ b/docs/libcurl/opts/GNURLOPT_PROXY_SSL_VERIFYHOST.3
@@ -5,7 +5,7 @@
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
-.\" * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
+.\" * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
@@ -42,8 +42,15 @@ Curl considers the proxy the intended one when the Common Name field or a
Subject Alternate Name field in the certificate matches the host name in the
proxy string which you told curl to use.
-When the \fIverify\fP value is 1L, \fIcurl_easy_setopt\fP will return an error
-and the option value will not be changed due to old legacy reasons.
+If \fIverify\fP value is set to 1:
+
+In 7.28.0 and earlier: treated as a debug option of some sorts, not supported
+anymore due to frequently leading to programmer mistakes.
+
+From 7.28.1 to 7.65.3: setting it to 1 made curl_easy_setopt() return an error
+and leaving the flag untouched.
+
+From 7.66.0: treats 1 and 2 the same.
When the \fIverify\fP value is 0L, the connection succeeds regardless of the
names used in the certificate. Use that ability with caution!
diff --git a/docs/libcurl/opts/GNURLOPT_READFUNCTION.3 b/docs/libcurl/opts/GNURLOPT_READFUNCTION.3
index 412a9cd70..3bd7fc2ce 100644
--- a/docs/libcurl/opts/GNURLOPT_READFUNCTION.3
+++ b/docs/libcurl/opts/GNURLOPT_READFUNCTION.3
@@ -69,8 +69,37 @@ The default internal read callback is fread().
.SH PROTOCOLS
This is used for all protocols when doing uploads.
.SH EXAMPLE
-Here's an example setting a read callback for reading that to upload to an FTP
-site: https://curl.haxx.se/libcurl/c/ftpupload.html
+.nf
+size_t read_callback(void *ptr, size_t size, size_t nmemb, void *userdata)
+{
+ FILE *readhere = (FILE *)userdata;
+ curl_off_t nread;
+
+ /* copy as much data as possible into the 'ptr' buffer, but no more than
+ 'size' * 'nmemb' bytes! */
+ size_t retcode = fread(ptr, size, nmemb, readhere);
+
+ nread = (curl_off_t)retcode;
+
+ fprintf(stderr, "*** We read %" CURL_FORMAT_CURL_OFF_T
+ " bytes from file\\n", nread);
+ return retcode;
+}
+
+void setup(char *uploadthis)
+{
+ FILE *file = fopen("rb", uploadthis);
+ CURLcode result;
+
+ /* set callback to use */
+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
+
+ /* pass in suitable argument to callback */
+ curl_easy_setopt(curl, CURLOPT_READDATA, uploadthis);
+
+ result = curl_easy_perform(curl);
+}
+.fi
.SH AVAILABILITY
CURL_READFUNC_PAUSE return code was added in 7.18.0 and CURL_READFUNC_ABORT
was added in 7.12.1.
diff --git a/docs/libcurl/opts/GNURLOPT_SSL_VERIFYHOST.3 b/docs/libcurl/opts/GNURLOPT_SSL_VERIFYHOST.3
index c680d2e37..9513c65b1 100644
--- a/docs/libcurl/opts/GNURLOPT_SSL_VERIFYHOST.3
+++ b/docs/libcurl/opts/GNURLOPT_SSL_VERIFYHOST.3
@@ -5,7 +5,7 @@
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
-.\" * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
+.\" * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
@@ -45,11 +45,15 @@ Curl considers the server the intended one when the Common Name field or a
Subject Alternate Name field in the certificate matches the host name in the
URL to which you told Curl to connect.
-When the \fIverify\fP value is 1, \fIcurl_easy_setopt\fP will return an error
-and the option value will not be changed. It was previously (in 7.28.0 and
-earlier) a debug option of some sorts, but it is no longer supported due to
-frequently leading to programmer mistakes. Future versions will stop returning
-an error for 1 and just treat 1 and 2 the same.
+If \fIverify\fP value is set to 1:
+
+In 7.28.0 and earlier: treated as a debug option of some sorts, not supported
+anymore due to frequently leading to programmer mistakes.
+
+From 7.28.1 to 7.65.3: setting it to 1 made curl_easy_setopt() return an error
+and leaving the flag untouched.
+
+From 7.66.0: treats 1 and 2 the same.
When the \fIverify\fP value is 0, the connection succeeds regardless of the
names in the certificate. Use that ability with caution!
diff --git a/docs/libcurl/opts/Makefile.inc b/docs/libcurl/opts/Makefile.inc
index a0b8bd95e..58b58dc41 100644
--- a/docs/libcurl/opts/Makefile.inc
+++ b/docs/libcurl/opts/Makefile.inc
@@ -43,6 +43,7 @@ man_MANS = \
GNURLINFO_REDIRECT_URL.3 \
GNURLINFO_REQUEST_SIZE.3 \
GNURLINFO_RESPONSE_CODE.3 \
+ GNURLINFO_RETRY_AFTER.3 \
GNURLINFO_RTSP_CLIENT_CSEQ.3 \
GNURLINFO_RTSP_CSEQ_RECV.3 \
GNURLINFO_RTSP_SERVER_CSEQ.3 \
@@ -272,6 +273,7 @@ man_MANS = \
GNURLOPT_RTSP_SESSION_ID.3 \
GNURLOPT_RTSP_STREAM_URI.3 \
GNURLOPT_RTSP_TRANSPORT.3 \
+ GNURLOPT_SASL_AUTHZID.3 \
GNURLOPT_SASL_IR.3 \
GNURLOPT_SEEKDATA.3 \
GNURLOPT_SEEKFUNCTION.3 \
diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions
index 5244a7cdb..9daad949f 100644
--- a/docs/libcurl/symbols-in-versions
+++ b/docs/libcurl/symbols-in-versions
@@ -12,7 +12,6 @@
Name Introduced Deprecated Removed
-CURLALTSVC_ALTUSED 7.64.1
CURLALTSVC_H1 7.64.1
CURLALTSVC_H2 7.64.1
CURLALTSVC_H3 7.64.1
@@ -40,6 +39,7 @@ CURLCLOSEPOLICY_SLOWEST 7.7
CURLE_ABORTED_BY_CALLBACK 7.1
CURLE_AGAIN 7.18.2
CURLE_ALREADY_COMPLETE 7.7.2
+CURLE_AUTH_ERROR 7.66.0
CURLE_BAD_CALLING_ORDER 7.1 7.17.0
CURLE_BAD_CONTENT_ENCODING 7.10
CURLE_BAD_DOWNLOAD_RESUME 7.10
@@ -266,6 +266,7 @@ CURLINFO_REDIRECT_TIME_T 7.61.0
CURLINFO_REDIRECT_URL 7.18.2
CURLINFO_REQUEST_SIZE 7.4.1
CURLINFO_RESPONSE_CODE 7.10.8
+CURLINFO_RETRY_AFTER 7.66.0
CURLINFO_RTSP_CLIENT_CSEQ 7.20.0
CURLINFO_RTSP_CSEQ_RECV 7.20.0
CURLINFO_RTSP_SERVER_CSEQ 7.20.0
@@ -554,6 +555,7 @@ CURLOPT_RTSP_SERVER_CSEQ 7.20.0
CURLOPT_RTSP_SESSION_ID 7.20.0
CURLOPT_RTSP_STREAM_URI 7.20.0
CURLOPT_RTSP_TRANSPORT 7.20.0
+CURLOPT_SASL_AUTHZID 7.66.0
CURLOPT_SASL_IR 7.31.0
CURLOPT_SEEKDATA 7.18.0
CURLOPT_SEEKFUNCTION 7.18.0
@@ -786,6 +788,7 @@ CURLVERSION_FOURTH 7.16.1
CURLVERSION_NOW 7.10
CURLVERSION_SECOND 7.11.1
CURLVERSION_THIRD 7.12.0
+CURLVERSION_SIXTH 7.66.0
CURL_CHUNK_BGN_FUNC_FAIL 7.21.0
CURL_CHUNK_BGN_FUNC_OK 7.21.0
CURL_CHUNK_BGN_FUNC_SKIP 7.21.0
@@ -830,6 +833,7 @@ CURL_HTTP_VERSION_2 7.43.0
CURL_HTTP_VERSION_2TLS 7.47.0
CURL_HTTP_VERSION_2_0 7.33.0
CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE 7.49.0
+CURL_HTTP_VERSION_3 7.66.0
CURL_HTTP_VERSION_NONE 7.9.1
CURL_IPRESOLVE_V4 7.10.8
CURL_IPRESOLVE_V6 7.10.8
@@ -924,6 +928,7 @@ CURL_VERSION_DEBUG 7.10.6
CURL_VERSION_GSSAPI 7.38.0
CURL_VERSION_GSSNEGOTIATE 7.10.6 7.38.0
CURL_VERSION_HTTP2 7.33.0
+CURL_VERSION_HTTP3 7.66.0
CURL_VERSION_HTTPS_PROXY 7.52.0
CURL_VERSION_IDN 7.12.0
CURL_VERSION_IPV6 7.10
diff --git a/include/gnurl/curl.h b/include/gnurl/curl.h
index 19f6c0b5a..ff0c77496 100644
--- a/include/gnurl/curl.h
+++ b/include/gnurl/curl.h
@@ -1,5 +1,5 @@
-#ifndef __CURL_CURL_H
-#define __CURL_CURL_H
+#ifndef CURLINC_CURL_H
+#define CURLINC_CURL_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@@ -284,10 +284,7 @@ typedef enum {
#define CURLFINFOFLAG_KNOWN_SIZE (1<<6)
#define CURLFINFOFLAG_KNOWN_HLINKCOUNT (1<<7)
-/* Content of this structure depends on information which is known and is
- achievable (e.g. by FTP LIST parsing). Please see the url_easy_setopt(3) man
- page for callbacks returning this structure -- some fields are mandatory,
- some others are optional. The FLAG field has special meaning. */
+/* Information about a single file, used when doing FTP wildcard matching */
struct curl_fileinfo {
char *filename;
curlfiletype filetype;
@@ -603,6 +600,8 @@ typedef enum {
*/
CURLE_RECURSIVE_API_CALL, /* 93 - an api function was called from
inside a callback */
+ CURLE_AUTH_ERROR, /* 94 - an authentication function returned an
+ error */
CURL_LAST /* never use! */
} CURLcode;
@@ -886,7 +885,7 @@ typedef enum {
/* CURLALTSVC_* are bits for the CURLOPT_ALTSVC_CTRL option */
#define CURLALTSVC_IMMEDIATELY (1<<0)
-#define CURLALTSVC_ALTUSED (1<<1)
+
#define CURLALTSVC_READONLYFILE (1<<2)
#define CURLALTSVC_H1 (1<<3)
#define CURLALTSVC_H2 (1<<4)
@@ -1925,6 +1924,9 @@ typedef enum {
/* maximum age of a connection to consider it for reuse (in seconds) */
CINIT(MAXAGE_CONN, LONG, 288),
+ /* SASL authorisation identity */
+ CINIT(SASL_AUTHZID, STRINGPOINT, 289),
+
CURLOPT_LASTENTRY /* the last unused */
} CURLoption;
@@ -1978,7 +1980,8 @@ enum {
CURL_HTTP_VERSION_2TLS, /* use version 2 for HTTPS, version 1.1 for HTTP */
CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE, /* please use HTTP 2 without HTTP/1.1
Upgrade */
-
+ CURL_HTTP_VERSION_3 = 30, /* Makes use of explicit HTTP/3 without fallback.
+ Use CURLOPT_ALTSVC to enable HTTP/3 upgrade */
CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */
};
@@ -2614,8 +2617,9 @@ typedef enum {
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_LASTONE = 56
+ CURLINFO_LASTONE = 57
} CURLINFO;
/* CURLINFO_RESPONSE_CODE is the new name for the option previously known as
@@ -2714,6 +2718,7 @@ typedef enum {
CURLVERSION_THIRD,
CURLVERSION_FOURTH,
CURLVERSION_FIFTH,
+ CURLVERSION_SIXTH,
CURLVERSION_LAST /* never actually use this */
} CURLversion;
@@ -2722,7 +2727,7 @@ typedef enum {
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_FIFTH
+#define CURLVERSION_NOW CURLVERSION_SIXTH
typedef struct {
CURLversion age; /* age of the returned struct */
@@ -2751,11 +2756,16 @@ typedef struct {
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 */
} curl_version_info_data;
#define CURL_VERSION_IPV6 (1<<0) /* IPv6-enabled */
@@ -2788,6 +2798,7 @@ typedef struct {
#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 */
/*
* NAME curl_version_info()
@@ -2868,4 +2879,4 @@ CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask);
#endif /* __STDC__ >= 1 */
#endif /* gcc >= 4.3 && !__cplusplus */
-#endif /* __CURL_CURL_H */
+#endif /* CURLINC_CURL_H */
diff --git a/include/gnurl/curlver.h b/include/gnurl/curlver.h
index 846b9796e..54fa2cb94 100644
--- a/include/gnurl/curlver.h
+++ b/include/gnurl/curlver.h
@@ -1,5 +1,5 @@
-#ifndef __CURL_CURLVER_H
-#define __CURL_CURLVER_H
+#ifndef CURLINC_CURLVER_H
+#define CURLINC_CURLVER_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@@ -30,13 +30,13 @@
/* This is the version number of the libcurl package from which this header
file origins: */
-#define LIBCURL_VERSION "7.65.3-DEV"
+#define LIBCURL_VERSION "7.66.0-DEV"
/* The numeric version number is also available "in parts" by using these
defines: */
#define LIBCURL_VERSION_MAJOR 7
-#define LIBCURL_VERSION_MINOR 65
-#define LIBCURL_VERSION_PATCH 3
+#define LIBCURL_VERSION_MINOR 66
+#define LIBCURL_VERSION_PATCH 0
/* This is the numeric version of the libcurl version number, meant for easier
parsing and comparions by programs. The LIBCURL_VERSION_NUM define will
@@ -57,7 +57,7 @@
CURL_VERSION_BITS() macro since curl's own configure script greps for it
and needs it to contain the full number.
*/
-#define LIBCURL_VERSION_NUM 0x074103
+#define LIBCURL_VERSION_NUM 0x074200
/*
* This is the date and time when the full source package was created. The
@@ -74,4 +74,4 @@
#define CURL_AT_LEAST_VERSION(x,y,z) \
(LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z))
-#endif /* __CURL_CURLVER_H */
+#endif /* CURLINC_CURLVER_H */
diff --git a/include/gnurl/easy.h b/include/gnurl/easy.h
index f42a8a969..592f5d3c1 100644
--- a/include/gnurl/easy.h
+++ b/include/gnurl/easy.h
@@ -1,5 +1,5 @@
-#ifndef __CURL_EASY_H
-#define __CURL_EASY_H
+#ifndef CURLINC_EASY_H
+#define CURLINC_EASY_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
diff --git a/include/gnurl/mprintf.h b/include/gnurl/mprintf.h
index e20f546e1..f615ed7d6 100644
--- a/include/gnurl/mprintf.h
+++ b/include/gnurl/mprintf.h
@@ -1,5 +1,5 @@
-#ifndef __CURL_MPRINTF_H
-#define __CURL_MPRINTF_H
+#ifndef CURLINC_MPRINTF_H
+#define CURLINC_MPRINTF_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -47,4 +47,4 @@ CURL_EXTERN char *curl_mvaprintf(const char *format, va_list args);
}
#endif
-#endif /* __CURL_MPRINTF_H */
+#endif /* CURLINC_MPRINTF_H */
diff --git a/include/gnurl/multi.h b/include/gnurl/multi.h
index eab23c08e..f10932244 100644
--- a/include/gnurl/multi.h
+++ b/include/gnurl/multi.h
@@ -1,5 +1,5 @@
-#ifndef __CURL_MULTI_H
-#define __CURL_MULTI_H
+#ifndef CURLINC_MULTI_H
+#define CURLINC_MULTI_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -173,6 +173,20 @@ CURL_EXTERN CURLMcode curl_multi_wait(CURLM *multi_handle,
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_perform()
*
diff --git a/include/gnurl/stdcheaders.h b/include/gnurl/stdcheaders.h
index 027b6f421..a6bdc1a25 100644
--- a/include/gnurl/stdcheaders.h
+++ b/include/gnurl/stdcheaders.h
@@ -1,5 +1,5 @@
-#ifndef __STDC_HEADERS_H
-#define __STDC_HEADERS_H
+#ifndef CURLINC_STDCHEADERS_H
+#define CURLINC_STDCHEADERS_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -30,4 +30,4 @@ 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 /* __STDC_HEADERS_H */
+#endif /* CURLINC_STDCHEADERS_H */
diff --git a/include/gnurl/system.h b/include/gnurl/system.h
index 1e555ec19..cd37c2bf5 100644
--- a/include/gnurl/system.h
+++ b/include/gnurl/system.h
@@ -1,5 +1,5 @@
-#ifndef __CURL_SYSTEM_H
-#define __CURL_SYSTEM_H
+#ifndef CURLINC_SYSTEM_H
+#define CURLINC_SYSTEM_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -473,21 +473,21 @@
*/
#if defined(__BORLANDC__) && (__BORLANDC__ == 0x0551)
-# define __CURL_OFF_T_C_HLPR2(x) x
-# define __CURL_OFF_T_C_HLPR1(x) __CURL_OFF_T_C_HLPR2(x)
-# define CURL_OFF_T_C(Val) __CURL_OFF_T_C_HLPR1(Val) ## \
- __CURL_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_T)
-# define CURL_OFF_TU_C(Val) __CURL_OFF_T_C_HLPR1(Val) ## \
- __CURL_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_TU)
+# 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 __CURL_OFF_T_C_HLPR2(Val,Suffix) Val ## Suffix
+# define CURLINC_OFF_T_C_HLPR2(Val,Suffix) Val ## Suffix
# else
-# define __CURL_OFF_T_C_HLPR2(Val,Suffix) Val/**/Suffix
+# define CURLINC_OFF_T_C_HLPR2(Val,Suffix) Val/**/Suffix
# endif
-# define __CURL_OFF_T_C_HLPR1(Val,Suffix) __CURL_OFF_T_C_HLPR2(Val,Suffix)
-# define CURL_OFF_T_C(Val) __CURL_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_T)
-# define CURL_OFF_TU_C(Val) __CURL_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_TU)
+# 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 /* __CURL_SYSTEM_H */
+#endif /* CURLINC_SYSTEM_H */
diff --git a/include/gnurl/typecheck-gcc.h b/include/gnurl/typecheck-gcc.h
index eeb36abc0..03c84fc85 100644
--- a/include/gnurl/typecheck-gcc.h
+++ b/include/gnurl/typecheck-gcc.h
@@ -1,5 +1,5 @@
-#ifndef __CURL_TYPECHECK_GCC_H
-#define __CURL_TYPECHECK_GCC_H
+#ifndef CURLINC_TYPECHECK_GCC_H
+#define CURLINC_TYPECHECK_GCC_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@@ -25,10 +25,10 @@
/* wraps curl_easy_setopt() with typechecking */
/* To add a new kind of warning, add an
- * if(_curl_is_sometype_option(_curl_opt))
- * if(!_curl_is_sometype(value))
+ * if(curlcheck_sometype_option(_curl_opt))
+ * if(!curlcheck_sometype(value))
* _curl_easy_setopt_err_sometype();
- * block and define _curl_is_sometype_option, _curl_is_sometype and
+ * 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
@@ -38,112 +38,112 @@
* To add an option that uses the same type as an existing option, you'll just
* need to extend the appropriate _curl_*_option macro
*/
-#define curl_easy_setopt(handle, option, value) \
-__extension__ ({ \
- __typeof__(option) _curl_opt = option; \
- if(__builtin_constant_p(_curl_opt)) { \
- if(_curl_is_long_option(_curl_opt)) \
- if(!_curl_is_long(value)) \
- _curl_easy_setopt_err_long(); \
- if(_curl_is_off_t_option(_curl_opt)) \
- if(!_curl_is_off_t(value)) \
- _curl_easy_setopt_err_curl_off_t(); \
- if(_curl_is_string_option(_curl_opt)) \
- if(!_curl_is_string(value)) \
- _curl_easy_setopt_err_string(); \
- if(_curl_is_write_cb_option(_curl_opt)) \
- if(!_curl_is_write_cb(value)) \
- _curl_easy_setopt_err_write_callback(); \
- if((_curl_opt) == CURLOPT_RESOLVER_START_FUNCTION) \
- if(!_curl_is_resolver_start_callback(value)) \
- _curl_easy_setopt_err_resolver_start_callback(); \
- if((_curl_opt) == CURLOPT_READFUNCTION) \
- if(!_curl_is_read_cb(value)) \
- _curl_easy_setopt_err_read_cb(); \
- if((_curl_opt) == CURLOPT_IOCTLFUNCTION) \
- if(!_curl_is_ioctl_cb(value)) \
- _curl_easy_setopt_err_ioctl_cb(); \
- if((_curl_opt) == CURLOPT_SOCKOPTFUNCTION) \
- if(!_curl_is_sockopt_cb(value)) \
- _curl_easy_setopt_err_sockopt_cb(); \
- if((_curl_opt) == CURLOPT_OPENSOCKETFUNCTION) \
- if(!_curl_is_opensocket_cb(value)) \
- _curl_easy_setopt_err_opensocket_cb(); \
- if((_curl_opt) == CURLOPT_PROGRESSFUNCTION) \
- if(!_curl_is_progress_cb(value)) \
- _curl_easy_setopt_err_progress_cb(); \
- if((_curl_opt) == CURLOPT_DEBUGFUNCTION) \
- if(!_curl_is_debug_cb(value)) \
- _curl_easy_setopt_err_debug_cb(); \
- if((_curl_opt) == CURLOPT_SSL_CTX_FUNCTION) \
- if(!_curl_is_ssl_ctx_cb(value)) \
- _curl_easy_setopt_err_ssl_ctx_cb(); \
- if(_curl_is_conv_cb_option(_curl_opt)) \
- if(!_curl_is_conv_cb(value)) \
- _curl_easy_setopt_err_conv_cb(); \
- if((_curl_opt) == CURLOPT_SEEKFUNCTION) \
- if(!_curl_is_seek_cb(value)) \
- _curl_easy_setopt_err_seek_cb(); \
- if(_curl_is_cb_data_option(_curl_opt)) \
- if(!_curl_is_cb_data(value)) \
- _curl_easy_setopt_err_cb_data(); \
- if((_curl_opt) == CURLOPT_ERRORBUFFER) \
- if(!_curl_is_error_buffer(value)) \
- _curl_easy_setopt_err_error_buffer(); \
- if((_curl_opt) == CURLOPT_STDERR) \
- if(!_curl_is_FILE(value)) \
- _curl_easy_setopt_err_FILE(); \
- if(_curl_is_postfields_option(_curl_opt)) \
- if(!_curl_is_postfields(value)) \
- _curl_easy_setopt_err_postfields(); \
- if((_curl_opt) == CURLOPT_HTTPPOST) \
- if(!_curl_is_arr((value), struct curl_httppost)) \
- _curl_easy_setopt_err_curl_httpost(); \
- if((_curl_opt) == CURLOPT_MIMEPOST) \
- if(!_curl_is_ptr((value), curl_mime)) \
- _curl_easy_setopt_err_curl_mimepost(); \
- if(_curl_is_slist_option(_curl_opt)) \
- if(!_curl_is_arr((value), struct curl_slist)) \
- _curl_easy_setopt_err_curl_slist(); \
- if((_curl_opt) == CURLOPT_SHARE) \
- if(!_curl_is_ptr((value), CURLSH)) \
- _curl_easy_setopt_err_CURLSH(); \
- } \
- curl_easy_setopt(handle, _curl_opt, value); \
-})
+#define curl_easy_setopt(handle, option, value) \
+ __extension__({ \
+ __typeof__(option) _curl_opt = option; \
+ if(__builtin_constant_p(_curl_opt)) { \
+ 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__ ({ \
- __typeof__(info) _curl_info = info; \
- if(__builtin_constant_p(_curl_info)) { \
- if(_curl_is_string_info(_curl_info)) \
- if(!_curl_is_arr((arg), char *)) \
- _curl_easy_getinfo_err_string(); \
- if(_curl_is_long_info(_curl_info)) \
- if(!_curl_is_arr((arg), long)) \
- _curl_easy_getinfo_err_long(); \
- if(_curl_is_double_info(_curl_info)) \
- if(!_curl_is_arr((arg), double)) \
- _curl_easy_getinfo_err_double(); \
- if(_curl_is_slist_info(_curl_info)) \
- if(!_curl_is_arr((arg), struct curl_slist *)) \
- _curl_easy_getinfo_err_curl_slist(); \
- if(_curl_is_tlssessioninfo_info(_curl_info)) \
- if(!_curl_is_arr((arg), struct curl_tlssessioninfo *)) \
- _curl_easy_getinfo_err_curl_tlssesssioninfo(); \
- if(_curl_is_certinfo_info(_curl_info)) \
- if(!_curl_is_arr((arg), struct curl_certinfo *)) \
- _curl_easy_getinfo_err_curl_certinfo(); \
- if(_curl_is_socket_info(_curl_info)) \
- if(!_curl_is_arr((arg), curl_socket_t)) \
- _curl_easy_getinfo_err_curl_socket(); \
- if(_curl_is_off_t_info(_curl_info)) \
- if(!_curl_is_arr((arg), curl_off_t)) \
- _curl_easy_getinfo_err_curl_off_t(); \
- } \
- curl_easy_getinfo(handle, _curl_info, arg); \
-})
+#define curl_easy_getinfo(handle, info, arg) \
+ __extension__({ \
+ __typeof__(info) _curl_info = info; \
+ if(__builtin_constant_p(_curl_info)) { \
+ 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
@@ -156,83 +156,83 @@ __extension__ ({ \
* functions */
/* To define a new warning, use _CURL_WARNING(identifier, "message") */
-#define _CURL_WARNING(id, message) \
- static void __attribute__((__warning__(message))) \
- __attribute__((__unused__)) __attribute__((__noinline__)) \
+#define CURLWARNING(id, message) \
+ static void __attribute__((__warning__(message))) \
+ __attribute__((__unused__)) __attribute__((__noinline__)) \
id(void) { __asm__(""); }
-_CURL_WARNING(_curl_easy_setopt_err_long,
+CURLWARNING(_curl_easy_setopt_err_long,
"curl_easy_setopt expects a long argument for this option")
-_CURL_WARNING(_curl_easy_setopt_err_curl_off_t,
+CURLWARNING(_curl_easy_setopt_err_curl_off_t,
"curl_easy_setopt expects a curl_off_t argument for this option")
-_CURL_WARNING(_curl_easy_setopt_err_string,
+CURLWARNING(_curl_easy_setopt_err_string,
"curl_easy_setopt expects a "
"string ('char *' or char[]) argument for this option"
)
-_CURL_WARNING(_curl_easy_setopt_err_write_callback,
+CURLWARNING(_curl_easy_setopt_err_write_callback,
"curl_easy_setopt expects a curl_write_callback argument for this option")
-_CURL_WARNING(_curl_easy_setopt_err_resolver_start_callback,
+CURLWARNING(_curl_easy_setopt_err_resolver_start_callback,
"curl_easy_setopt expects a "
"curl_resolver_start_callback argument for this option"
)
-_CURL_WARNING(_curl_easy_setopt_err_read_cb,
+CURLWARNING(_curl_easy_setopt_err_read_cb,
"curl_easy_setopt expects a curl_read_callback argument for this option")
-_CURL_WARNING(_curl_easy_setopt_err_ioctl_cb,
+CURLWARNING(_curl_easy_setopt_err_ioctl_cb,
"curl_easy_setopt expects a curl_ioctl_callback argument for this option")
-_CURL_WARNING(_curl_easy_setopt_err_sockopt_cb,
+CURLWARNING(_curl_easy_setopt_err_sockopt_cb,
"curl_easy_setopt expects a curl_sockopt_callback argument for this option")
-_CURL_WARNING(_curl_easy_setopt_err_opensocket_cb,
+CURLWARNING(_curl_easy_setopt_err_opensocket_cb,
"curl_easy_setopt expects a "
"curl_opensocket_callback argument for this option"
)
-_CURL_WARNING(_curl_easy_setopt_err_progress_cb,
+CURLWARNING(_curl_easy_setopt_err_progress_cb,
"curl_easy_setopt expects a curl_progress_callback argument for this option")
-_CURL_WARNING(_curl_easy_setopt_err_debug_cb,
+CURLWARNING(_curl_easy_setopt_err_debug_cb,
"curl_easy_setopt expects a curl_debug_callback argument for this option")
-_CURL_WARNING(_curl_easy_setopt_err_ssl_ctx_cb,
+CURLWARNING(_curl_easy_setopt_err_ssl_ctx_cb,
"curl_easy_setopt expects a curl_ssl_ctx_callback argument for this option")
-_CURL_WARNING(_curl_easy_setopt_err_conv_cb,
+CURLWARNING(_curl_easy_setopt_err_conv_cb,
"curl_easy_setopt expects a curl_conv_callback argument for this option")
-_CURL_WARNING(_curl_easy_setopt_err_seek_cb,
+CURLWARNING(_curl_easy_setopt_err_seek_cb,
"curl_easy_setopt expects a curl_seek_callback argument for this option")
-_CURL_WARNING(_curl_easy_setopt_err_cb_data,
+CURLWARNING(_curl_easy_setopt_err_cb_data,
"curl_easy_setopt expects a "
"private data pointer as argument for this option")
-_CURL_WARNING(_curl_easy_setopt_err_error_buffer,
+CURLWARNING(_curl_easy_setopt_err_error_buffer,
"curl_easy_setopt expects a "
"char buffer of CURL_ERROR_SIZE as argument for this option")
-_CURL_WARNING(_curl_easy_setopt_err_FILE,
+CURLWARNING(_curl_easy_setopt_err_FILE,
"curl_easy_setopt expects a 'FILE *' argument for this option")
-_CURL_WARNING(_curl_easy_setopt_err_postfields,
+CURLWARNING(_curl_easy_setopt_err_postfields,
"curl_easy_setopt expects a 'void *' or 'char *' argument for this option")
-_CURL_WARNING(_curl_easy_setopt_err_curl_httpost,
+CURLWARNING(_curl_easy_setopt_err_curl_httpost,
"curl_easy_setopt expects a 'struct curl_httppost *' "
"argument for this option")
-_CURL_WARNING(_curl_easy_setopt_err_curl_mimepost,
+CURLWARNING(_curl_easy_setopt_err_curl_mimepost,
"curl_easy_setopt expects a 'curl_mime *' "
"argument for this option")
-_CURL_WARNING(_curl_easy_setopt_err_curl_slist,
+CURLWARNING(_curl_easy_setopt_err_curl_slist,
"curl_easy_setopt expects a 'struct curl_slist *' argument for this option")
-_CURL_WARNING(_curl_easy_setopt_err_CURLSH,
+CURLWARNING(_curl_easy_setopt_err_CURLSH,
"curl_easy_setopt expects a CURLSH* argument for this option")
-_CURL_WARNING(_curl_easy_getinfo_err_string,
+CURLWARNING(_curl_easy_getinfo_err_string,
"curl_easy_getinfo expects a pointer to 'char *' for this info")
-_CURL_WARNING(_curl_easy_getinfo_err_long,
+CURLWARNING(_curl_easy_getinfo_err_long,
"curl_easy_getinfo expects a pointer to long for this info")
-_CURL_WARNING(_curl_easy_getinfo_err_double,
+CURLWARNING(_curl_easy_getinfo_err_double,
"curl_easy_getinfo expects a pointer to double for this info")
-_CURL_WARNING(_curl_easy_getinfo_err_curl_slist,
+CURLWARNING(_curl_easy_getinfo_err_curl_slist,
"curl_easy_getinfo expects a pointer to 'struct curl_slist *' for this info")
-_CURL_WARNING(_curl_easy_getinfo_err_curl_tlssesssioninfo,
+CURLWARNING(_curl_easy_getinfo_err_curl_tlssesssioninfo,
"curl_easy_getinfo expects a pointer to "
"'struct curl_tlssessioninfo *' for this info")
-_CURL_WARNING(_curl_easy_getinfo_err_curl_certinfo,
+CURLWARNING(_curl_easy_getinfo_err_curl_certinfo,
"curl_easy_getinfo expects a pointer to "
"'struct curl_certinfo *' for this info")
-_CURL_WARNING(_curl_easy_getinfo_err_curl_socket,
+CURLWARNING(_curl_easy_getinfo_err_curl_socket,
"curl_easy_getinfo expects a pointer to curl_socket_t for this info")
-_CURL_WARNING(_curl_easy_getinfo_err_curl_off_t,
+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 */
@@ -244,14 +244,14 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_off_t,
*/
/* evaluates to true if option takes a long argument */
-#define _curl_is_long_option(option) \
+#define curlcheck_long_option(option) \
(0 < (option) && (option) < CURLOPTTYPE_OBJECTPOINT)
-#define _curl_is_off_t_option(option) \
+#define curlcheck_off_t_option(option) \
((option) > CURLOPTTYPE_OFF_T)
/* evaluates to true if option takes a char* argument */
-#define _curl_is_string_option(option) \
+#define curlcheck_string_option(option) \
((option) == CURLOPT_ABSTRACT_UNIX_SOCKET || \
(option) == CURLOPT_ACCEPT_ENCODING || \
(option) == CURLOPT_ALTSVC || \
@@ -311,6 +311,7 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_off_t,
(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 || \
@@ -336,18 +337,18 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_off_t,
0)
/* evaluates to true if option takes a curl_write_callback argument */
-#define _curl_is_write_cb_option(option) \
- ((option) == CURLOPT_HEADERFUNCTION || \
+#define curlcheck_write_cb_option(option) \
+ ((option) == CURLOPT_HEADERFUNCTION || \
(option) == CURLOPT_WRITEFUNCTION)
/* evaluates to true if option takes a curl_conv_callback argument */
-#define _curl_is_conv_cb_option(option) \
- ((option) == CURLOPT_CONV_TO_NETWORK_FUNCTION || \
- (option) == CURLOPT_CONV_FROM_NETWORK_FUNCTION || \
+#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 _curl_is_cb_data_option(option) \
+#define curlcheck_cb_data_option(option) \
((option) == CURLOPT_CHUNK_DATA || \
(option) == CURLOPT_CLOSESOCKETDATA || \
(option) == CURLOPT_DEBUGDATA || \
@@ -369,13 +370,13 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_off_t,
0)
/* evaluates to true if option takes a POST data argument (void* or char*) */
-#define _curl_is_postfields_option(option) \
+#define curlcheck_postfields_option(option) \
((option) == CURLOPT_POSTFIELDS || \
(option) == CURLOPT_COPYPOSTFIELDS || \
0)
/* evaluates to true if option takes a struct curl_slist * argument */
-#define _curl_is_slist_option(option) \
+#define curlcheck_slist_option(option) \
((option) == CURLOPT_HTTP200ALIASES || \
(option) == CURLOPT_HTTPHEADER || \
(option) == CURLOPT_MAIL_RCPT || \
@@ -391,40 +392,40 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_off_t,
/* 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 _curl_is_string_info(info) \
+#define curlcheck_string_info(info) \
(CURLINFO_STRING < (info) && (info) < CURLINFO_LONG)
/* evaluates to true if info expects a pointer to long argument */
-#define _curl_is_long_info(info) \
+#define curlcheck_long_info(info) \
(CURLINFO_LONG < (info) && (info) < CURLINFO_DOUBLE)
/* evaluates to true if info expects a pointer to double argument */
-#define _curl_is_double_info(info) \
+#define curlcheck_double_info(info) \
(CURLINFO_DOUBLE < (info) && (info) < CURLINFO_SLIST)
/* true if info expects a pointer to struct curl_slist * argument */
-#define _curl_is_slist_info(info) \
+#define curlcheck_slist_info(info) \
(((info) == CURLINFO_SSL_ENGINES) || ((info) == CURLINFO_COOKIELIST))
/* true if info expects a pointer to struct curl_tlssessioninfo * argument */
-#define _curl_is_tlssessioninfo_info(info) \
+#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 _curl_is_certinfo_info(info) ((info) == CURLINFO_CERTINFO)
+#define curlcheck_certinfo_info(info) ((info) == CURLINFO_CERTINFO)
/* true if info expects a pointer to struct curl_socket_t argument */
-#define _curl_is_socket_info(info) \
+#define curlcheck_socket_info(info) \
(CURLINFO_SOCKET < (info) && (info) < CURLINFO_OFF_T)
/* true if info expects a pointer to curl_off_t argument */
-#define _curl_is_off_t_info(info) \
+#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 _curl_is_ptr/_curl_is_arr macros,
+/* 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
@@ -434,35 +435,35 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_off_t,
*/
/* XXX: should evaluate to true if expr is a pointer */
-#define _curl_is_any_ptr(expr) \
+#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 _curl_is_NULL(expr) \
+#define curlcheck_NULL(expr) \
(__builtin_types_compatible_p(__typeof__(expr), __typeof__(NULL)))
/* evaluates to true if expr is type*, const type* or NULL */
-#define _curl_is_ptr(expr, type) \
- (_curl_is_NULL(expr) || \
- __builtin_types_compatible_p(__typeof__(expr), type *) || \
+#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 _curl_is_arr(expr, type) \
- (_curl_is_ptr((expr), 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 _curl_is_string(expr) \
- (_curl_is_arr((expr), char) || \
- _curl_is_arr((expr), signed char) || \
- _curl_is_arr((expr), unsigned char))
+#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 _curl_is_long(expr) \
+#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) || \
@@ -477,59 +478,59 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_off_t,
__builtin_types_compatible_p(__typeof__(expr), unsigned char))
/* evaluates to true if expr is of type curl_off_t */
-#define _curl_is_off_t(expr) \
+#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 _curl_is_error_buffer(expr) \
- (_curl_is_NULL(expr) || \
- __builtin_types_compatible_p(__typeof__(expr), char *) || \
+#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 _curl_is_cb_data(expr) \
- (_curl_is_ptr((expr), void) || \
- _curl_is_ptr((expr), FILE))
+#define curlcheck_cb_data(expr) \
+ (curlcheck_ptr((expr), void) || \
+ curlcheck_ptr((expr), FILE))
#else /* be less strict */
-#define _curl_is_cb_data(expr) \
- _curl_is_any_ptr(expr)
+#define curlcheck_cb_data(expr) \
+ curlcheck_any_ptr(expr)
#endif
/* evaluates to true if expr is of type FILE* */
-#define _curl_is_FILE(expr) \
- (_curl_is_NULL(expr) || \
+#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 _curl_is_postfields(expr) \
- (_curl_is_ptr((expr), void) || \
- _curl_is_arr((expr), char) || \
- _curl_is_arr((expr), unsigned 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 _curl_callback_compatible(func, type) \
- (__builtin_types_compatible_p(__typeof__(func), type) || \
+#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 _curl_is_resolver_start_callback(expr) \
- (_curl_is_NULL(expr) || \
- _curl_callback_compatible((expr), 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 _curl_is_read_cb(expr) \
- (_curl_is_NULL(expr) || \
- _curl_callback_compatible((expr), __typeof__(fread) *) || \
- _curl_callback_compatible((expr), curl_read_callback) || \
- _curl_callback_compatible((expr), _curl_read_callback1) || \
- _curl_callback_compatible((expr), _curl_read_callback2) || \
- _curl_callback_compatible((expr), _curl_read_callback3) || \
- _curl_callback_compatible((expr), _curl_read_callback4) || \
- _curl_callback_compatible((expr), _curl_read_callback5) || \
- _curl_callback_compatible((expr), _curl_read_callback6))
+#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 *);
@@ -538,16 +539,16 @@ 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 _curl_is_write_cb(expr) \
- (_curl_is_read_cb(expr) || \
- _curl_callback_compatible((expr), __typeof__(fwrite) *) || \
- _curl_callback_compatible((expr), curl_write_callback) || \
- _curl_callback_compatible((expr), _curl_write_callback1) || \
- _curl_callback_compatible((expr), _curl_write_callback2) || \
- _curl_callback_compatible((expr), _curl_write_callback3) || \
- _curl_callback_compatible((expr), _curl_write_callback4) || \
- _curl_callback_compatible((expr), _curl_write_callback5) || \
- _curl_callback_compatible((expr), _curl_write_callback6))
+#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 *);
@@ -558,37 +559,37 @@ typedef size_t (*_curl_write_callback5)(const void *, size_t, size_t,
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 _curl_is_ioctl_cb(expr) \
- (_curl_is_NULL(expr) || \
- _curl_callback_compatible((expr), curl_ioctl_callback) || \
- _curl_callback_compatible((expr), _curl_ioctl_callback1) || \
- _curl_callback_compatible((expr), _curl_ioctl_callback2) || \
- _curl_callback_compatible((expr), _curl_ioctl_callback3) || \
- _curl_callback_compatible((expr), _curl_ioctl_callback4))
+#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 _curl_is_sockopt_cb(expr) \
- (_curl_is_NULL(expr) || \
- _curl_callback_compatible((expr), curl_sockopt_callback) || \
- _curl_callback_compatible((expr), _curl_sockopt_callback1) || \
- _curl_callback_compatible((expr), _curl_sockopt_callback2))
+#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 _curl_is_opensocket_cb(expr) \
- (_curl_is_NULL(expr) || \
- _curl_callback_compatible((expr), curl_opensocket_callback) || \
- _curl_callback_compatible((expr), _curl_opensocket_callback1) || \
- _curl_callback_compatible((expr), _curl_opensocket_callback2) || \
- _curl_callback_compatible((expr), _curl_opensocket_callback3) || \
- _curl_callback_compatible((expr), _curl_opensocket_callback4))
+#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)
@@ -599,28 +600,28 @@ 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 _curl_is_progress_cb(expr) \
- (_curl_is_NULL(expr) || \
- _curl_callback_compatible((expr), curl_progress_callback) || \
- _curl_callback_compatible((expr), _curl_progress_callback1) || \
- _curl_callback_compatible((expr), _curl_progress_callback2))
+#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 _curl_is_debug_cb(expr) \
- (_curl_is_NULL(expr) || \
- _curl_callback_compatible((expr), curl_debug_callback) || \
- _curl_callback_compatible((expr), _curl_debug_callback1) || \
- _curl_callback_compatible((expr), _curl_debug_callback2) || \
- _curl_callback_compatible((expr), _curl_debug_callback3) || \
- _curl_callback_compatible((expr), _curl_debug_callback4) || \
- _curl_callback_compatible((expr), _curl_debug_callback5) || \
- _curl_callback_compatible((expr), _curl_debug_callback6) || \
- _curl_callback_compatible((expr), _curl_debug_callback7) || \
- _curl_callback_compatible((expr), _curl_debug_callback8))
+#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 *,
@@ -640,17 +641,17 @@ typedef int (*_curl_debug_callback8) (CURL *,
/* evaluates to true if expr is of type curl_ssl_ctx_callback or "similar" */
/* this is getting even messier... */
-#define _curl_is_ssl_ctx_cb(expr) \
- (_curl_is_NULL(expr) || \
- _curl_callback_compatible((expr), curl_ssl_ctx_callback) || \
- _curl_callback_compatible((expr), _curl_ssl_ctx_callback1) || \
- _curl_callback_compatible((expr), _curl_ssl_ctx_callback2) || \
- _curl_callback_compatible((expr), _curl_ssl_ctx_callback3) || \
- _curl_callback_compatible((expr), _curl_ssl_ctx_callback4) || \
- _curl_callback_compatible((expr), _curl_ssl_ctx_callback5) || \
- _curl_callback_compatible((expr), _curl_ssl_ctx_callback6) || \
- _curl_callback_compatible((expr), _curl_ssl_ctx_callback7) || \
- _curl_callback_compatible((expr), _curl_ssl_ctx_callback8))
+#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 *);
@@ -673,26 +674,26 @@ typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback8;
#endif
/* evaluates to true if expr is of type curl_conv_callback or "similar" */
-#define _curl_is_conv_cb(expr) \
- (_curl_is_NULL(expr) || \
- _curl_callback_compatible((expr), curl_conv_callback) || \
- _curl_callback_compatible((expr), _curl_conv_callback1) || \
- _curl_callback_compatible((expr), _curl_conv_callback2) || \
- _curl_callback_compatible((expr), _curl_conv_callback3) || \
- _curl_callback_compatible((expr), _curl_conv_callback4))
+#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 _curl_is_seek_cb(expr) \
- (_curl_is_NULL(expr) || \
- _curl_callback_compatible((expr), curl_seek_callback) || \
- _curl_callback_compatible((expr), _curl_seek_callback1) || \
- _curl_callback_compatible((expr), _curl_seek_callback2))
+#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 /* __CURL_TYPECHECK_GCC_H */
+#endif /* CURLINC_TYPECHECK_GCC_H */
diff --git a/include/gnurl/urlapi.h b/include/gnurl/urlapi.h
index 58e89d85c..0f2f152f1 100644
--- a/include/gnurl/urlapi.h
+++ b/include/gnurl/urlapi.h
@@ -1,5 +1,5 @@
-#ifndef __CURL_URLAPI_H
-#define __CURL_URLAPI_H
+#ifndef CURLINC_URLAPI_H
+#define CURLINC_URLAPI_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@@ -120,4 +120,4 @@ CURL_EXTERN CURLUcode curl_url_set(CURLU *handle, CURLUPart what,
} /* end of extern "C" */
#endif
-#endif
+#endif /* CURLINC_URLAPI_H */
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 6e8a83fad..7003f99cc 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -23,8 +23,8 @@ AUTOMAKE_OPTIONS = foreign nostdinc
CMAKE_DIST = CMakeLists.txt curl_config.h.cmake
-EXTRA_DIST = Makefile.m32 config-win32.h \
- config-win32ce.h config-riscos.h config-mac.h curl_config.h.in \
+EXTRA_DIST = Makefile.m32 config-win32.h config-win32ce.h \
+ config-plan9.h config-riscos.h config-mac.h curl_config.h.in \
makefile.dj config-dos.h libgnurl.plist libcurl.rc config-amigaos.h \
makefile.amiga Makefile.netware nwlib.c nwos.c config-win32ce.h \
config-os400.h setup-os400.h config-symbian.h Makefile.Watcom \
@@ -65,7 +65,7 @@ endif
# Prevent LIBS from being used for all link targets
LIBS = $(BLANK_AT_MAKETIME)
-VERSIONINFO=-version-info 9:0:5
+VERSIONINFO=-version-info 10:0:6
# 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.
@@ -138,7 +138,7 @@ CS_ = $(CS_0)
checksrc:
$(CHECKSRC)(@PERL@ $(srcdir)/checksrc.pl -D$(srcdir) -W$(srcdir)/curl_config.h \
- $(srcdir)/*.[ch] $(srcdir)/vauth/*.[ch] $(srcdir)/vtls/*.[ch])
+ $(srcdir)/*.[ch] $(srcdir)/vauth/*.[ch] $(srcdir)/vtls/*.[ch] $(srcdir)/vquic/*.[ch] $(srcdir)/vssh/*.[ch])
if CURLDEBUG
# for debug builds, we scan the sources on all regular make invokes
diff --git a/lib/Makefile.inc b/lib/Makefile.inc
index 37f702681..3e3a385c5 100644
--- a/lib/Makefile.inc
+++ b/lib/Makefile.inc
@@ -37,6 +37,12 @@ LIB_VTLS_HFILES = vtls/openssl.h vtls/vtls.h vtls/gtls.h \
vtls/wolfssl.h vtls/schannel.h vtls/sectransp.h vtls/gskit.h \
vtls/mbedtls.h vtls/mesalink.h
+LIB_VQUIC_CFILES = vquic/ngtcp2.c vquic/quiche.c
+
+LIB_VQUIC_HFILES = vquic/ngtcp2.h vquic/quiche.h
+
+LIB_VSSH_CFILES = vssh/libssh2.c vssh/libssh.c
+
LIB_CFILES = file.c timeval.c base64.c hostip.c progress.c formdata.c \
cookie.c http.c sendf.c ftp.c url.c dict.c if2ip.c speedcheck.c \
ldap.c version.c getenv.c escape.c mprintf.c telnet.c netrc.c \
@@ -46,7 +52,7 @@ LIB_CFILES = file.c timeval.c base64.c hostip.c progress.c formdata.c \
http_digest.c md4.c md5.c http_negotiate.c inet_pton.c strtoofft.c \
strerror.c amigaos.c hostasyn.c hostip4.c hostip6.c hostsyn.c \
inet_ntop.c parsedate.c select.c tftp.c splay.c strdup.c socks.c \
- ssh.c ssh-libssh.c curl_addrinfo.c socks_gssapi.c socks_sspi.c \
+ curl_addrinfo.c socks_gssapi.c socks_sspi.c \
curl_sspi.c slist.c nonblock.c curl_memrchr.c imap.c pop3.c smtp.c \
pingpong.c rtsp.c curl_threads.c warnless.c hmac.c curl_rtmp.c \
openldap.c curl_gethostname.c gopher.c idn_win32.c \
@@ -76,9 +82,11 @@ LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \
x509asn1.h http2.h sigpipe.h smb.h curl_endian.h curl_des.h \
curl_printf.h system_win32.h rand.h mime.h curl_sha256.h setopt.h \
curl_path.h curl_ctype.h curl_range.h psl.h doh.h urlapi-int.h \
- curl_get_line.h altsvc.h
+ curl_get_line.h altsvc.h quic.h
LIB_RCFILES = libcurl.rc
-CSOURCES = $(LIB_CFILES) $(LIB_VAUTH_CFILES) $(LIB_VTLS_CFILES)
-HHEADERS = $(LIB_HFILES) $(LIB_VAUTH_HFILES) $(LIB_VTLS_HFILES)
+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)
diff --git a/lib/altsvc.c b/lib/altsvc.c
index 73cd5d734..c803773cd 100644
--- a/lib/altsvc.c
+++ b/lib/altsvc.c
@@ -54,10 +54,13 @@ static enum alpnid alpn2alpnid(char *name)
return ALPN_h1;
if(strcasecompare(name, "h2"))
return ALPN_h2;
- if(strcasecompare(name, "h2c"))
- return ALPN_h2c;
+#if (defined(USE_QUICHE) || defined(USE_NGHTTP2)) && !defined(UNITTESTS)
+ if(strcasecompare(name, "h3-22"))
+ return ALPN_h3;
+#else
if(strcasecompare(name, "h3"))
return ALPN_h3;
+#endif
return ALPN_none; /* unknown, probably rubbish input */
}
@@ -69,10 +72,12 @@ const char *Curl_alpnid2str(enum alpnid id)
return "h1";
case ALPN_h2:
return "h2";
- case ALPN_h2c:
- return "h2c";
case ALPN_h3:
+#if (defined(USE_QUICHE) || defined(USE_NGHTTP2)) && !defined(UNITTESTS)
+ return "h3-22";
+#else
return "h3";
+#endif
default:
return ""; /* bad */
}
@@ -81,8 +86,8 @@ const char *Curl_alpnid2str(enum alpnid id)
static void altsvc_free(struct altsvc *as)
{
- free(as->srchost);
- free(as->dsthost);
+ free(as->src.host);
+ free(as->dst.host);
free(as);
}
@@ -97,17 +102,17 @@ static struct altsvc *altsvc_createid(const char *srchost,
if(!as)
return NULL;
- as->srchost = strdup(srchost);
- if(!as->srchost)
+ as->src.host = strdup(srchost);
+ if(!as->src.host)
goto error;
- as->dsthost = strdup(dsthost);
- if(!as->dsthost)
+ as->dst.host = strdup(dsthost);
+ if(!as->dst.host)
goto error;
- as->srcalpnid = srcalpnid;
- as->dstalpnid = dstalpnid;
- as->srcport = curlx_ultous(srcport);
- as->dstport = curlx_ultous(dstport);
+ as->src.alpnid = srcalpnid;
+ as->dst.alpnid = dstalpnid;
+ as->src.port = curlx_ultous(srcport);
+ as->dst.port = curlx_ultous(dstport);
return as;
error:
@@ -226,8 +231,8 @@ static CURLcode altsvc_out(struct altsvc *as, FILE *fp)
"\"%d%02d%02d "
"%02d:%02d:%02d\" "
"%u %d\n",
- Curl_alpnid2str(as->srcalpnid), as->srchost, as->srcport,
- Curl_alpnid2str(as->dstalpnid), as->dsthost, as->dstport,
+ Curl_alpnid2str(as->src.alpnid), as->src.host, as->src.port,
+ Curl_alpnid2str(as->dst.alpnid), as->dst.host, 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);
@@ -252,7 +257,7 @@ struct altsvcinfo *Curl_altsvc_init(void)
#ifdef USE_NGHTTP2
| CURLALTSVC_H2
#endif
-#ifdef USE_HTTP3
+#ifdef ENABLE_QUIC
| CURLALTSVC_H3
#endif
;
@@ -343,7 +348,7 @@ static CURLcode getalnum(const char **ptr, char *alpnbuf, size_t buflen)
while(*p && ISBLANK(*p))
p++;
protop = p;
- while(*p && ISALNUM(*p))
+ while(*p && !ISBLANK(*p) && (*p != ';') && (*p != '='))
p++;
len = p - protop;
@@ -365,9 +370,9 @@ static void altsvc_flush(struct altsvcinfo *asi, enum alpnid srcalpnid,
for(e = asi->list.head; e; e = n) {
struct altsvc *as = e->ptr;
n = e->next;
- if((srcalpnid == as->srcalpnid) &&
- (srcport == as->srcport) &&
- strcasecompare(srchost, as->srchost)) {
+ if((srcalpnid == as->src.alpnid) &&
+ (srcport == as->src.port) &&
+ strcasecompare(srchost, as->src.host)) {
Curl_llist_remove(&asi->list, e, NULL);
altsvc_free(as);
asi->num--;
@@ -535,31 +540,31 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data,
bool Curl_altsvc_lookup(struct altsvcinfo *asi,
enum alpnid srcalpnid, const char *srchost,
int srcport,
- enum alpnid *dstalpnid, const char **dsthost,
- int *dstport)
+ 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(dsthost);
+ 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->srcalpnid == srcalpnid) &&
- strcasecompare(as->srchost, srchost) &&
- as->srcport == srcport) {
+ if((as->src.alpnid == srcalpnid) &&
+ strcasecompare(as->src.host, srchost) &&
+ (as->src.port == srcport) &&
+ (versions & as->dst.alpnid)) {
/* match */
- *dstalpnid = as->dstalpnid;
- *dsthost = as->dsthost;
- *dstport = as->dstport;
+ *dstentry = as;
return TRUE;
}
}
diff --git a/lib/altsvc.h b/lib/altsvc.h
index 83e647b87..8f09aa3c5 100644
--- a/lib/altsvc.h
+++ b/lib/altsvc.h
@@ -28,20 +28,21 @@
#include "llist.h"
enum alpnid {
- ALPN_none,
- ALPN_h1,
- ALPN_h2,
- ALPN_h2c,
- ALPN_h3
+ 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 {
- char *srchost;
- char *dsthost;
- unsigned short srcport;
- unsigned short dstport;
- enum alpnid srcalpnid;
- enum alpnid dstalpnid;
+ struct althost src;
+ struct althost dst;
time_t expires;
bool persist;
int prio;
@@ -68,8 +69,8 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data,
bool Curl_altsvc_lookup(struct altsvcinfo *asi,
enum alpnid srcalpnid, const char *srchost,
int srcport,
- enum alpnid *dstalpnid, const char **dsthost,
- int *dstport);
+ struct altsvc **dstentry,
+ int versions); /* one or more CURLALTSVC_H* bits */
#else
/* disabled */
#define Curl_altsvc_save(a,b)
diff --git a/lib/asyn-ares.c b/lib/asyn-ares.c
index 6b14aa698..835cfa48f 100644
--- a/lib/asyn-ares.c
+++ b/lib/asyn-ares.c
@@ -253,16 +253,14 @@ static void destroy_async_data(struct Curl_async *async)
*/
int Curl_resolver_getsock(struct connectdata *conn,
- curl_socket_t *socks,
- int numsocks)
-
+ curl_socket_t *socks)
{
struct timeval maxtime;
struct timeval timebuf;
struct timeval *timeout;
long milli;
int max = ares_getsock((ares_channel)conn->data->state.resolver,
- (ares_socket_t *)socks, numsocks);
+ (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE);
maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
maxtime.tv_usec = 0;
diff --git a/lib/asyn-thread.c b/lib/asyn-thread.c
index 55e0811c5..24da74885 100644..100755
--- a/lib/asyn-thread.c
+++ b/lib/asyn-thread.c
@@ -163,6 +163,10 @@ struct thread_sync_data {
char *hostname; /* hostname to resolve, Curl_async.hostname
duplicate */
int port;
+#ifdef HAVE_SOCKETPAIR
+ struct connectdata *conn;
+ curl_socket_t sock_pair[2]; /* socket pair */
+#endif
int sock_error;
Curl_addrinfo *res;
#ifdef HAVE_GETADDRINFO
@@ -197,6 +201,15 @@ void destroy_thread_sync_data(struct thread_sync_data * tsd)
if(tsd->res)
Curl_freeaddrinfo(tsd->res);
+#ifdef HAVE_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) {
+ sclose(tsd->sock_pair[1]);
+ }
+#endif
memset(tsd, 0, sizeof(*tsd));
}
@@ -230,6 +243,14 @@ int init_thread_sync_data(struct thread_data * td,
Curl_mutex_init(tsd->mtx);
+#ifdef HAVE_SOCKETPAIR
+ /* create socket pair */
+ if(socketpair(AF_LOCAL, SOCK_STREAM, 0, &tsd->sock_pair[0]) < 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
@@ -276,6 +297,9 @@ static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg)
struct thread_data *td = tsd->td;
char service[12];
int rc;
+#ifdef HAVE_SOCKETPAIR
+ char buf[1];
+#endif
msnprintf(service, sizeof(service), "%d", tsd->port);
@@ -298,6 +322,16 @@ static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg)
free(td);
}
else {
+#ifdef HAVE_SOCKETPAIR
+ if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
+ /* DNS has been resolved, signal client task */
+ buf[0] = 1;
+ if(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);
}
@@ -348,6 +382,10 @@ static void destroy_async_data(struct Curl_async *async)
if(async->os_specific) {
struct thread_data *td = (struct thread_data*) async->os_specific;
int done;
+#ifdef HAVE_SOCKETPAIR
+ curl_socket_t sock_rd = td->tsd.sock_pair[0];
+ struct connectdata *conn = td->tsd.conn;
+#endif
/*
* if the thread is still blocking in the resolve syscall, detach it and
@@ -369,6 +407,15 @@ static void destroy_async_data(struct Curl_async *async)
free(async->os_specific);
}
+#ifdef HAVE_SOCKETPAIR
+ /*
+ * ensure CURLMOPT_SOCKETFUNCTION fires CURL_POLL_REMOVE
+ * before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL
+ */
+ if(conn)
+ Curl_multi_closed(conn->data, sock_rd);
+ sclose(sock_rd);
+#endif
}
async->os_specific = NULL;
@@ -569,8 +616,9 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
}
else {
/* poll for name lookup done with exponential backoff up to 250ms */
- timediff_t elapsed = Curl_timediff(Curl_now(),
- data->progress.t_startsingle);
+ /* should be fine even if this converts to 32 bit */
+ time_t elapsed = (time_t)Curl_timediff(Curl_now(),
+ data->progress.t_startsingle);
if(elapsed < 0)
elapsed = 0;
@@ -592,26 +640,45 @@ CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
}
int Curl_resolver_getsock(struct connectdata *conn,
- curl_socket_t *socks,
- int numsocks)
+ curl_socket_t *socks)
{
+ int ret_val = 0;
time_t milli;
timediff_t ms;
struct Curl_easy *data = conn->data;
struct resdata *reslv = (struct resdata *)data->state.resolver;
+#ifdef HAVE_SOCKETPAIR
+ struct thread_data *td = (struct thread_data*)conn->async.os_specific;
+#else
(void)socks;
- (void)numsocks;
- 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);
- return 0;
+#endif
+
+#ifdef HAVE_SOCKETPAIR
+ if(td) {
+ /* return read fd to client for polling the DNS resolution status */
+ socks[0] = td->tsd.sock_pair[0];
+ DEBUGASSERT(td->tsd.conn == conn || !td->tsd.conn);
+ td->tsd.conn = conn;
+ ret_val = GETSOCK_READSOCK(0);
+ }
+ else {
+#endif
+ ms = Curl_timediff(Curl_now(), reslv->start);
+ if(ms < 3)
+ milli = 0;
+ else if(ms <= 50)
+ milli = (time_t)ms/3;
+ else if(ms <= 250)
+ milli = 50;
+ else
+ milli = 200;
+ Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
+#ifdef HAVE_SOCKETPAIR
+ }
+#endif
+
+
+ return ret_val;
}
#ifndef HAVE_GETADDRINFO
@@ -706,7 +773,8 @@ Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
memset(&hints, 0, sizeof(hints));
hints.ai_family = pf;
- hints.ai_socktype = conn->socktype;
+ hints.ai_socktype = (conn->transport == TRNSPRT_TCP)?
+ SOCK_STREAM : SOCK_DGRAM;
msnprintf(sbuf, sizeof(sbuf), "%d", port);
diff --git a/lib/asyn.h b/lib/asyn.h
index ccd4b1f7e..081c3fef0 100644
--- a/lib/asyn.h
+++ b/lib/asyn.h
@@ -114,8 +114,7 @@ void Curl_resolver_kill(struct connectdata *conn);
* return bitmask indicating what file descriptors (referring to array indexes
* in the 'sock' array) to wait for, read/write.
*/
-int Curl_resolver_getsock(struct connectdata *conn, curl_socket_t *sock,
- int numsocks);
+int Curl_resolver_getsock(struct connectdata *conn, curl_socket_t *sock);
/*
* Curl_resolver_is_resolved()
diff --git a/lib/base64.c b/lib/base64.c
index fb081a6bb..643cef625 100644
--- a/lib/base64.c
+++ b/lib/base64.c
@@ -24,8 +24,8 @@
#include "curl_setup.h"
-#if !defined(CURL_DISABLE_HTTP_AUTH) || defined(USE_LIBSSH2) || \
- defined(USE_LIBSSH) || !defined(CURL_DISABLE_LDAP) || \
+#if !defined(CURL_DISABLE_HTTP_AUTH) || defined(USE_SSH) || \
+ !defined(CURL_DISABLE_LDAP) || \
!defined(CURL_DISABLE_DOH) || defined(USE_SSL)
#include "urldata.h" /* for the Curl_easy definition */
diff --git a/lib/config-os400.h b/lib/config-os400.h
index a741e9185..d14bd3391 100644
--- a/lib/config-os400.h
+++ b/lib/config-os400.h
@@ -434,6 +434,9 @@
/* Define to enable alt-svc support (experimental) */
#undef USE_ALTSVC
+/* Define to enable HTTP3 support (experimental, requires NGTCP2 or QUICHE) */
+#undef ENABLE_QUIC
+
/* Version number of package */
#undef VERSION
diff --git a/lib/config-plan9.h b/lib/config-plan9.h
new file mode 100644
index 000000000..70833a51d
--- /dev/null
+++ b/lib/config-plan9.h
@@ -0,0 +1,217 @@
+#ifndef HEADER_CURL_CONFIG_PLAN9_H
+#define HEADER_CURL_CONFIG_PLAN9_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed 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.
+ *
+ ***************************************************************************/
+
+#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 ENABLE_IPV6 1
+#define CURL_DISABLE_LDAP 1
+
+#define NEED_REENTRANT 1
+#define OS "plan9"
+#define PACKAGE "curl"
+#define PACKAGE_NAME "curl"
+#define PACKAGE_BUGREPORT "a suitable mailing list: https://curl.haxx.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 RETSIGTYPE void
+
+#define STDC_HEADERS 1
+
+#ifdef _BITS64
+#error not implement
+#else
+#define SIZEOF_INT 4
+#define SIZEOF_SHORT 2
+#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_GETNAMEINFO 1
+#define GETNAMEINFO_QUAL_ARG1 const
+#define GETNAMEINFO_TYPE_ARG1 struct sockaddr *
+#define GETNAMEINFO_TYPE_ARG2 int
+#define GETNAMEINFO_TYPE_ARG46 long
+#define GETNAMEINFO_TYPE_ARG7 int
+
+#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_RECVFROM 1
+#define RECVFROM_TYPE_ARG1 int
+#define RECVFROM_TYPE_ARG2 void
+#define RECVFROM_TYPE_ARG2_IS_VOID 1
+#define RECVFROM_TYPE_ARG3 int
+#define RECVFROM_TYPE_ARG4 int
+#define RECVFROM_TYPE_ARG5 void
+#define RECVFROM_TYPE_ARG5_IS_VOID 1
+#define RECVFROM_TYPE_ARG6 int
+#define RECVFROM_TYPE_ARG6_IS_VOID 1
+#define RECVFROM_TYPE_RETV int
+
+#define HAVE_SELECT 1
+#define SELECT_TYPE_ARG1 int
+#define SELECT_TYPE_ARG234 fd_set *
+#define SELECT_TYPE_ARG5 struct timeval *
+#define SELECT_TYPE_RETV int
+
+#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_ASSERT_H 1
+#define HAVE_BASENAME 1
+#define HAVE_BOOL_T 1
+#define HAVE_CRYPTO_CLEANUP_ALL_EX_DATA 1
+#define HAVE_ENGINE_LOAD_BUILTIN_ENGINES 1
+#define HAVE_ERRNO_H 1
+#define HAVE_FCNTL 1
+#define HAVE_FCNTL_H 1
+#define HAVE_FDOPEN 1
+#define HAVE_FORK 1
+#define HAVE_FREEADDRINFO 1
+#define HAVE_FTRUNCATE 1
+#define HAVE_GETADDRINFO 1
+#define HAVE_GETEUID 1
+#define HAVE_GETHOSTBYADDR 1
+#define HAVE_GETHOSTBYNAME 1
+#define HAVE_GETHOSTNAME 1
+#define HAVE_GETPPID 1
+#define HAVE_GETPROTOBYNAME 1
+#define HAVE_GETPWUID 1
+#define HAVE_GETTIMEOFDAY 1
+#define HAVE_GMTIME_R 1
+#define HAVE_INET_ADDR 1
+#define HAVE_INET_NTOP 1
+#define HAVE_INET_PTON 1
+#define HAVE_INTTYPES_H 1
+#define HAVE_IOCTL 1
+#define HAVE_LIBGEN_H 1
+#define HAVE_LIBSSL 1
+#define HAVE_LIBZ 1
+#define HAVE_LL 1
+#define HAVE_LOCALE_H 1
+#define HAVE_LOCALTIME_R 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_OPENSSL_CRYPTO_H 1
+#define HAVE_OPENSSL_ENGINE_H 1
+#define HAVE_OPENSSL_ERR_H 1
+#define HAVE_OPENSSL_PEM_H 1
+#define HAVE_OPENSSL_PKCS12_H 1
+#define HAVE_OPENSSL_RSA_H 1
+#define HAVE_OPENSSL_SSL_H 1
+#define HAVE_OPENSSL_X509_H 1
+
+#define HAVE_PERROR 1
+#define HAVE_PIPE 1
+#define HAVE_POLL 1
+#define HAVE_POLL_FINE 1
+#define HAVE_POLL_H 1
+#define HAVE_PTHREAD_H 1
+#define HAVE_RAND_STATUS 1
+#define HAVE_SETJMP_H 1
+#define HAVE_SETLOCALE 1
+
+#define HAVE_SETSOCKOPT 1
+#define HAVE_SOCK_OPTS 1 /* for /sys/include/ape/sys/socket.h */
+
+#define HAVE_SIGACTION 1
+#define HAVE_SIGNAL 1
+#define HAVE_SIGNAL_H 1
+#define HAVE_SIGSETJMP 1
+#define HAVE_SIG_ATOMIC_T 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_STDINT_H 1
+#define HAVE_STDIO_H 1
+#define HAVE_STDLIB_H 1
+#define HAVE_STRCASECMP 1
+#define HAVE_STRDUP 1
+#define HAVE_STRING_H 1
+#define HAVE_STRNCASECMP 1
+#define HAVE_STRSTR 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_UIO_H 1
+#define HAVE_SYS_UN_H 1
+#define HAVE_TERMIOS_H 1
+#define HAVE_TIME_H 1
+#define HAVE_UNAME 1
+#define HAVE_UNISTD_H 1
+#define HAVE_UTIME 1
+#define HAVE_UTIME_H 1
+#define HAVE_WRITEV 1
+
+#define HAVE_ZLIB_H 1
+
+#define HAVE_POSIX_STRERROR_R 1
+#define HAVE_STRERROR_R 1
+#define STRERROR_R_TYPE_ARG3 int
+
+#define TIME_WITH_SYS_TIME 1
+#define USE_BLOCKING_SOCKETS 1
+#define USE_MANUAL 1
+
+#define __attribute__(x)
+
+#ifndef __cplusplus
+#undef inline
+#endif
+
+#endif /* HEADER_CURL_CONFIG_PLAN9_H */
diff --git a/lib/connect.c b/lib/connect.c
index 4a1f2c640..77196250d 100644
--- a/lib/connect.c
+++ b/lib/connect.c
@@ -75,6 +75,8 @@
#include "conncache.h"
#include "multihandle.h"
#include "system_win32.h"
+#include "quic.h"
+#include "socks.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -165,7 +167,7 @@ tcpkeepalive(struct Curl_easy *data,
static CURLcode
singleipconnect(struct connectdata *conn,
const Curl_addrinfo *ai, /* start connecting to this */
- curl_socket_t *sock);
+ int sockindex); /* 0 or 1 among the temp ones */
/*
* Curl_timeleft() returns the amount of milliseconds left allowed for the
@@ -595,7 +597,7 @@ static CURLcode trynextip(struct connectdata *conn,
}
if(ai) {
- result = singleipconnect(conn, ai, &conn->tempsock[tempindex]);
+ result = singleipconnect(conn, ai, tempindex);
if(result == CURLE_COULDNT_CONNECT) {
ai = ai->ai_next;
continue;
@@ -625,13 +627,10 @@ void Curl_persistconninfo(struct connectdata *conn)
conn->data->info.conn_local_port = conn->local_port;
}
-UNITTEST bool getaddressinfo(struct sockaddr *sa, char *addr,
- long *port);
-
/* retrieves ip address and port from a sockaddr structure.
note it calls Curl_inet_ntop which sets errno on fail, not SOCKERRNO. */
-UNITTEST bool getaddressinfo(struct sockaddr *sa, char *addr,
- long *port)
+bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
+ char *addr, long *port)
{
struct sockaddr_in *si = NULL;
#ifdef ENABLE_IPV6
@@ -639,6 +638,8 @@ UNITTEST bool getaddressinfo(struct sockaddr *sa, char *addr,
#endif
#if defined(HAVE_SYS_UN_H) && defined(AF_UNIX)
struct sockaddr_un *su = NULL;
+#else
+ (void)salen;
#endif
switch(sa->sa_family) {
@@ -664,8 +665,12 @@ UNITTEST bool getaddressinfo(struct sockaddr *sa, char *addr,
#endif
#if defined(HAVE_SYS_UN_H) && defined(AF_UNIX)
case AF_UNIX:
- su = (struct sockaddr_un*)sa;
- msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path);
+ if(salen > sizeof(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
@@ -683,8 +688,8 @@ UNITTEST bool getaddressinfo(struct sockaddr *sa, char *addr,
connection */
void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd)
{
- if(conn->socktype == SOCK_DGRAM)
- /* there's no connection! */
+ if(conn->transport != TRNSPRT_TCP)
+ /* there's no TCP connection! */
return;
#if defined(HAVE_GETPEERNAME) || defined(HAVE_GETSOCKNAME)
@@ -693,10 +698,11 @@ void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd)
char buffer[STRERROR_LEN];
struct Curl_sockaddr_storage ssrem;
struct Curl_sockaddr_storage ssloc;
- curl_socklen_t len;
+ curl_socklen_t plen;
+ curl_socklen_t slen;
#ifdef HAVE_GETPEERNAME
- len = sizeof(struct Curl_sockaddr_storage);
- if(getpeername(sockfd, (struct sockaddr*) &ssrem, &len)) {
+ plen = sizeof(struct Curl_sockaddr_storage);
+ if(getpeername(sockfd, (struct sockaddr*) &ssrem, &plen)) {
int error = SOCKERRNO;
failf(data, "getpeername() failed with errno %d: %s",
error, Curl_strerror(error, buffer, sizeof(buffer)));
@@ -704,9 +710,9 @@ void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd)
}
#endif
#ifdef HAVE_GETSOCKNAME
- len = sizeof(struct Curl_sockaddr_storage);
+ slen = sizeof(struct Curl_sockaddr_storage);
memset(&ssloc, 0, sizeof(ssloc));
- if(getsockname(sockfd, (struct sockaddr*) &ssloc, &len)) {
+ if(getsockname(sockfd, (struct sockaddr*) &ssloc, &slen)) {
int error = SOCKERRNO;
failf(data, "getsockname() failed with errno %d: %s",
error, Curl_strerror(error, buffer, sizeof(buffer)));
@@ -714,8 +720,8 @@ void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd)
}
#endif
#ifdef HAVE_GETPEERNAME
- if(!getaddressinfo((struct sockaddr*)&ssrem,
- conn->primary_ip, &conn->primary_port)) {
+ if(!Curl_addr2string((struct sockaddr*)&ssrem, plen,
+ conn->primary_ip, &conn->primary_port)) {
failf(data, "ssrem inet_ntop() failed with errno %d: %s",
errno, Curl_strerror(errno, buffer, sizeof(buffer)));
return;
@@ -723,8 +729,8 @@ void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd)
memcpy(conn->ip_addr_str, conn->primary_ip, MAX_IPADR_LEN);
#endif
#ifdef HAVE_GETSOCKNAME
- if(!getaddressinfo((struct sockaddr*)&ssloc,
- conn->local_ip, &conn->local_port)) {
+ if(!Curl_addr2string((struct sockaddr*)&ssloc, slen,
+ conn->local_ip, &conn->local_port)) {
failf(data, "ssloc inet_ntop() failed with errno %d: %s",
errno, Curl_strerror(errno, buffer, sizeof(buffer)));
return;
@@ -739,6 +745,58 @@ void Curl_updateconninfo(struct connectdata *conn, curl_socket_t sockfd)
Curl_persistconninfo(conn);
}
+/* after a TCP connection to the proxy has been verified, this function does
+ the next magic step.
+
+ Note: this function's sub-functions call failf()
+
+*/
+static CURLcode connected_proxy(struct connectdata *conn, int sockindex)
+{
+ CURLcode result = CURLE_OK;
+
+ if(conn->bits.socksproxy) {
+#ifndef CURL_DISABLE_PROXY
+ /* for the secondary socket (FTP), use the "connect to host"
+ * but ignore the "connect to port" (use the secondary port)
+ */
+ const char * const host = conn->bits.httpproxy ?
+ conn->http_proxy.host.name :
+ conn->bits.conn_to_host ?
+ conn->conn_to_host.name :
+ sockindex == SECONDARYSOCKET ?
+ conn->secondaryhostname : conn->host.name;
+ const int port = conn->bits.httpproxy ? (int)conn->http_proxy.port :
+ sockindex == SECONDARYSOCKET ? conn->secondary_port :
+ conn->bits.conn_to_port ? conn->conn_to_port :
+ conn->remote_port;
+ conn->bits.socksproxy_connecting = TRUE;
+ switch(conn->socks_proxy.proxytype) {
+ case CURLPROXY_SOCKS5:
+ case CURLPROXY_SOCKS5_HOSTNAME:
+ result = Curl_SOCKS5(conn->socks_proxy.user, conn->socks_proxy.passwd,
+ host, port, sockindex, conn);
+ break;
+
+ case CURLPROXY_SOCKS4:
+ case CURLPROXY_SOCKS4A:
+ result = Curl_SOCKS4(conn->socks_proxy.user, host, port, sockindex,
+ conn);
+ break;
+
+ default:
+ failf(conn->data, "unknown proxytype option given");
+ result = CURLE_COULDNT_CONNECT;
+ } /* switch proxytype */
+ conn->bits.socksproxy_connecting = FALSE;
+#else
+ (void)sockindex;
+#endif /* CURL_DISABLE_PROXY */
+ }
+
+ return result;
+}
+
/*
* Curl_is_connected() checks if the socket has connected.
*/
@@ -781,6 +839,24 @@ CURLcode Curl_is_connected(struct connectdata *conn,
if(conn->tempsock[i] == CURL_SOCKET_BAD)
continue;
+#ifdef ENABLE_QUIC
+ if(conn->transport == TRNSPRT_QUIC) {
+ result = Curl_quic_is_connected(conn, i, connected);
+ if(result) {
+ error = SOCKERRNO;
+ goto error;
+ }
+ if(*connected) {
+ /* use this socket from now on */
+ conn->sock[sockindex] = conn->tempsock[i];
+ conn->ip_addr = conn->tempaddr[i];
+ conn->tempsock[i] = CURL_SOCKET_BAD;
+ connkeep(conn, "HTTP/3 default");
+ }
+ return result;
+ }
+#endif
+
#ifdef mpeix
/* Call this function once now, and ignore the results. We do this to
"clear" the error state on the socket so that we can later read it
@@ -794,8 +870,8 @@ CURLcode Curl_is_connected(struct connectdata *conn,
if(rc == 0) { /* no connection yet */
error = 0;
if(Curl_timediff(now, conn->connecttime) >= conn->timeoutms_per_addr) {
- infof(data, "After %ldms connect time, move on!\n",
- conn->timeoutms_per_addr);
+ infof(data, "After %" CURL_FORMAT_TIMEDIFF_T
+ "ms connect time, move on!\n", conn->timeoutms_per_addr);
error = ETIMEDOUT;
}
@@ -825,7 +901,7 @@ CURLcode Curl_is_connected(struct connectdata *conn,
}
/* see if we need to do any proxy magic first once we connected */
- result = Curl_connected_proxy(conn, sockindex);
+ result = connected_proxy(conn, sockindex);
if(result)
return result;
@@ -844,6 +920,9 @@ CURLcode Curl_is_connected(struct connectdata *conn,
else if(rc & CURL_CSELECT_ERR)
(void)verifyconnect(conn->tempsock[i], &error);
+#ifdef ENABLE_QUIC
+ error:
+#endif
/*
* The connection failed here, we should attempt to connect to the "next
* address" for the given host. But first remember the latest error.
@@ -861,11 +940,11 @@ CURLcode Curl_is_connected(struct connectdata *conn,
Curl_strerror(error, buffer, sizeof(buffer)));
conn->timeoutms_per_addr = conn->tempaddr[i]->ai_next == NULL ?
- allow : allow / 2;
+ allow : allow / 2;
status = trynextip(conn, sockindex, i);
- if(status != CURLE_COULDNT_CONNECT
- || conn->tempsock[other] == CURL_SOCKET_BAD)
+ if((status != CURLE_COULDNT_CONNECT) ||
+ conn->tempsock[other] == CURL_SOCKET_BAD)
/* the last attempt failed and no other sockets remain open */
result = status;
}
@@ -1004,7 +1083,7 @@ void Curl_sndbufset(curl_socket_t sockfd)
*/
static CURLcode singleipconnect(struct connectdata *conn,
const Curl_addrinfo *ai,
- curl_socket_t *sockp)
+ int sockindex)
{
struct Curl_sockaddr_ex addr;
int rc = -1;
@@ -1020,7 +1099,7 @@ static CURLcode singleipconnect(struct connectdata *conn,
int optval = 1;
#endif
char buffer[STRERROR_LEN];
-
+ curl_socket_t *sockp = &conn->tempsock[sockindex];
*sockp = CURL_SOCKET_BAD;
result = Curl_socket(conn, ai, &addr, &sockfd);
@@ -1031,8 +1110,8 @@ static CURLcode singleipconnect(struct connectdata *conn,
return CURLE_OK;
/* store remote address and port used in this connection attempt */
- if(!getaddressinfo((struct sockaddr*)&addr.sa_addr,
- ipaddress, &port)) {
+ if(!Curl_addr2string((struct sockaddr*)&addr.sa_addr, addr.addrlen,
+ ipaddress, &port)) {
/* malformed address or bug in inet_ntop, try next address */
failf(data, "sa_addr inet_ntop() failed with errno %d: %s",
errno, Curl_strerror(errno, buffer, sizeof(buffer)));
@@ -1099,8 +1178,8 @@ static CURLcode singleipconnect(struct connectdata *conn,
if(conn->num_addr > 1)
Curl_expire(data, conn->timeoutms_per_addr, EXPIRE_DNS_PER_NAME);
- /* Connect TCP sockets, bind UDP */
- if(!isconnected && (conn->socktype == SOCK_STREAM)) {
+ /* Connect TCP and QUIC sockets */
+ if(!isconnected && (conn->transport != TRNSPRT_UDP)) {
if(conn->bits.tcp_fastopen) {
#if defined(CONNECT_DATA_IDEMPOTENT) /* Darwin */
# if defined(HAVE_BUILTIN_AVAILABLE)
@@ -1146,6 +1225,16 @@ static CURLcode singleipconnect(struct connectdata *conn,
if(-1 == rc)
error = SOCKERRNO;
+#ifdef ENABLE_QUIC
+ else if(conn->transport == TRNSPRT_QUIC) {
+ /* pass in 'sockfd' separately since it hasn't been put into the
+ tempsock array at this point */
+ result = Curl_quic_connect(conn, sockfd, sockindex,
+ &addr.sa_addr, addr.addrlen);
+ if(result)
+ error = SOCKERRNO;
+ }
+#endif
}
else {
*sockp = sockfd;
@@ -1219,7 +1308,7 @@ CURLcode Curl_connecthost(struct connectdata *conn, /* context */
/* start connecting to first IP */
while(conn->tempaddr[0]) {
- result = singleipconnect(conn, conn->tempaddr[0], &(conn->tempsock[0]));
+ result = singleipconnect(conn, conn->tempaddr[0], 0);
if(!result)
break;
conn->tempaddr[0] = conn->tempaddr[0]->ai_next;
@@ -1386,8 +1475,9 @@ CURLcode Curl_socket(struct connectdata *conn,
*/
addr->family = ai->ai_family;
- addr->socktype = conn->socktype;
- addr->protocol = conn->socktype == SOCK_DGRAM?IPPROTO_UDP:ai->ai_protocol;
+ addr->socktype = (conn->transport == TRNSPRT_TCP) ? SOCK_STREAM : SOCK_DGRAM;
+ addr->protocol = conn->transport != TRNSPRT_TCP ? IPPROTO_UDP :
+ ai->ai_protocol;
addr->addrlen = ai->ai_addrlen;
if(addr->addrlen > sizeof(struct Curl_sockaddr_storage))
diff --git a/lib/connect.h b/lib/connect.h
index 6a5c755cc..b23085a98 100644
--- a/lib/connect.h
+++ b/lib/connect.h
@@ -51,6 +51,9 @@ timediff_t Curl_timeleft(struct Curl_easy *data,
curl_socket_t Curl_getconnectinfo(struct Curl_easy *data,
struct connectdata **connp);
+bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
+ char *addr, long *port);
+
/*
* Check if a connection seems to be alive.
*/
diff --git a/lib/cookie.c b/lib/cookie.c
index 9a9e14d01..53ca40237 100644
--- a/lib/cookie.c
+++ b/lib/cookie.c
@@ -819,22 +819,14 @@ Curl_cookie_add(struct Curl_easy *data,
badcookie = TRUE;
break;
case 1:
- /* This field got its explanation on the 23rd of May 2001 by
- Andrés García:
-
- flag: A TRUE/FALSE value indicating if all machines within a given
- domain can access the variable. This value is set automatically by
- the browser, depending on the value you set for the domain.
-
- As far as I can see, it is set to true when the cookie says
+ /* 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:
- /* It turns out, that sometimes the file format allows the path
- field to remain not filled in, we try to detect this and work
- around it! Andrés García made us aware of this... */
+ /* The file format allows the path field to remain not filled in */
if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) {
/* only if the path doesn't look like a boolean option! */
co->path = strdup(ptr);
diff --git a/lib/curl_md4.h b/lib/curl_md4.h
index 392203f9e..82df708ce 100644
--- a/lib/curl_md4.h
+++ b/lib/curl_md4.h
@@ -24,14 +24,12 @@
#include "curl_setup.h"
-#if defined(USE_NSS) || defined(USE_OS400CRYPTO) || \
- (defined(USE_OPENSSL) && defined(OPENSSL_NO_MD4)) || \
- (defined(USE_MBEDTLS) && !defined(MBEDTLS_MD4_C))
+#if !defined(CURL_DISABLE_CRYPTO_AUTH)
+
+#define MD4_DIGEST_LENGTH 16
void Curl_md4it(unsigned char *output, const unsigned char *input, size_t len);
-#endif /* defined(USE_NSS) || defined(USE_OS400CRYPTO) ||
- (defined(USE_OPENSSL) && defined(OPENSSL_NO_MD4)) ||
- (defined(USE_MBEDTLS) && !defined(MBEDTLS_MD4_C)) */
+#endif /* !defined(CURL_DISABLE_CRYPTO_AUTH) */
#endif /* HEADER_CURL_MD4_H */
diff --git a/lib/curl_ntlm_core.c b/lib/curl_ntlm_core.c
index 9e6fdcab2..19f9b61d8 100644
--- a/lib/curl_ntlm_core.c
+++ b/lib/curl_ntlm_core.c
@@ -55,11 +55,6 @@
#ifdef USE_OPENSSL
# include <openssl/des.h>
-# ifndef OPENSSL_NO_MD4
-# include <openssl/md4.h>
-# else
-# include "curl_md4.h"
-# endif
# include <openssl/md5.h>
# include <openssl/ssl.h>
# include <openssl/rand.h>
@@ -79,29 +74,23 @@
#elif defined(USE_GNUTLS_NETTLE)
# include <nettle/des.h>
-# include <nettle/md4.h>
#elif defined(USE_GNUTLS)
# include <gcrypt.h>
# define MD5_DIGEST_LENGTH 16
-# define MD4_DIGEST_LENGTH 16
#elif defined(USE_NSS)
# include <nss.h>
# include <pk11pub.h>
# include <hasht.h>
-# include "curl_md4.h"
# define MD5_DIGEST_LENGTH MD5_LENGTH
#elif defined(USE_MBEDTLS)
# include <mbedtls/des.h>
-# include <mbedtls/md4.h>
-# if !defined(MBEDTLS_MD4_C)
-# include "curl_md4.h"
-# endif
+# include "curl_md4.h"
#elif defined(USE_SECTRANSP)
@@ -110,7 +99,6 @@
#elif defined(USE_OS400CRYPTO)
# include "cipher.mih" /* mih/cipher */
-# include "curl_md4.h"
#elif defined(USE_WIN32_CRYPTO)
# include <wincrypt.h>
#else
@@ -126,6 +114,7 @@
#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"
@@ -552,7 +541,7 @@ CURLcode Curl_ntlm_core_mk_nt_hash(struct Curl_easy *data,
CURLcode result;
if(len > SIZE_T_MAX/2) /* avoid integer overflow */
return CURLE_OUT_OF_MEMORY;
- pw = len ? malloc(len * 2) : strdup("");
+ pw = len ? malloc(len * 2) : (unsigned char *)strdup("");
if(!pw)
return CURLE_OUT_OF_MEMORY;
@@ -566,57 +555,10 @@ CURLcode Curl_ntlm_core_mk_nt_hash(struct Curl_easy *data,
if(result)
return result;
- {
- /* Create NT hashed password. */
-#ifdef USE_OPENSSL
-#if !defined(OPENSSL_NO_MD4)
- MD4_CTX MD4pw;
- MD4_Init(&MD4pw);
- MD4_Update(&MD4pw, pw, 2 * len);
- MD4_Final(ntbuffer, &MD4pw);
-#else
- Curl_md4it(ntbuffer, pw, 2 * len);
-#endif
-#elif defined(USE_GNUTLS_NETTLE)
- struct md4_ctx MD4pw;
- md4_init(&MD4pw);
- md4_update(&MD4pw, (unsigned int)(2 * len), pw);
- md4_digest(&MD4pw, MD4_DIGEST_SIZE, ntbuffer);
-#elif defined(USE_GNUTLS)
- gcry_md_hd_t MD4pw;
- gcry_md_open(&MD4pw, GCRY_MD_MD4, 0);
- gcry_md_write(MD4pw, pw, 2 * len);
- memcpy(ntbuffer, gcry_md_read(MD4pw, 0), MD4_DIGEST_LENGTH);
- gcry_md_close(MD4pw);
-#elif defined(USE_NSS)
- Curl_md4it(ntbuffer, pw, 2 * len);
-#elif defined(USE_MBEDTLS)
-#if defined(MBEDTLS_MD4_C)
- mbedtls_md4(pw, 2 * len, ntbuffer);
-#else
- Curl_md4it(ntbuffer, pw, 2 * len);
-#endif
-#elif defined(USE_SECTRANSP)
- (void)CC_MD4(pw, (CC_LONG)(2 * len), ntbuffer);
-#elif defined(USE_OS400CRYPTO)
- Curl_md4it(ntbuffer, pw, 2 * len);
-#elif defined(USE_WIN32_CRYPTO)
- HCRYPTPROV hprov;
- if(CryptAcquireContext(&hprov, NULL, NULL, PROV_RSA_FULL,
- CRYPT_VERIFYCONTEXT)) {
- HCRYPTHASH hhash;
- if(CryptCreateHash(hprov, CALG_MD4, 0, 0, &hhash)) {
- DWORD length = 16;
- CryptHashData(hhash, pw, (unsigned int)len * 2, 0);
- CryptGetHashParam(hhash, HP_HASHVAL, ntbuffer, &length, 0);
- CryptDestroyHash(hhash);
- }
- CryptReleaseContext(hprov, 0);
- }
-#endif
+ /* Create NT hashed password. */
+ Curl_md4it(ntbuffer, pw, 2 * len);
- memset(ntbuffer + 16, 0, 21 - 16);
- }
+ memset(ntbuffer + 16, 0, 21 - 16);
free(pw);
diff --git a/lib/curl_path.c b/lib/curl_path.c
index 3e1fe2a7c..1ae4082a8 100644
--- a/lib/curl_path.c
+++ b/lib/curl_path.c
@@ -55,7 +55,7 @@ CURLcode Curl_getworkingpath(struct connectdata *conn,
}
if((working_path_len > 3) && (!memcmp(working_path, "/~/", 3)))
/* It is referenced to the home directory, so strip the leading '/~/' */
- memcpy(real_path, working_path + 3, 4 + working_path_len-3);
+ memcpy(real_path, working_path + 3, working_path_len - 2);
else
memcpy(real_path, working_path, 1 + working_path_len);
}
diff --git a/lib/curl_rtmp.c b/lib/curl_rtmp.c
index d92b0b39d..cd49e2224 100644
--- a/lib/curl_rtmp.c
+++ b/lib/curl_rtmp.c
@@ -199,13 +199,13 @@ static CURLcode rtmp_setup_connection(struct connectdata *conn)
RTMP_Free(r);
return CURLE_URL_MALFORMAT;
}
- conn->proto.generic = r;
+ conn->proto.rtmp = r;
return CURLE_OK;
}
static CURLcode rtmp_connect(struct connectdata *conn, bool *done)
{
- RTMP *r = conn->proto.generic;
+ RTMP *r = conn->proto.rtmp;
SET_RCVTIMEO(tv, 10);
r->m_sb.sb_socket = (int)conn->sock[FIRSTSOCKET];
@@ -240,7 +240,7 @@ static CURLcode rtmp_connect(struct connectdata *conn, bool *done)
static CURLcode rtmp_do(struct connectdata *conn, bool *done)
{
struct Curl_easy *data = conn->data;
- RTMP *r = conn->proto.generic;
+ RTMP *r = conn->proto.rtmp;
if(!RTMP_ConnectStream(r, 0))
return CURLE_FAILED_INIT;
@@ -268,10 +268,10 @@ static CURLcode rtmp_done(struct connectdata *conn, CURLcode status,
static CURLcode rtmp_disconnect(struct connectdata *conn,
bool dead_connection)
{
- RTMP *r = conn->proto.generic;
+ RTMP *r = conn->proto.rtmp;
(void)dead_connection;
if(r) {
- conn->proto.generic = NULL;
+ conn->proto.rtmp = NULL;
RTMP_Close(r);
RTMP_Free(r);
}
@@ -281,7 +281,7 @@ static CURLcode rtmp_disconnect(struct connectdata *conn,
static ssize_t rtmp_recv(struct connectdata *conn, int sockindex, char *buf,
size_t len, CURLcode *err)
{
- RTMP *r = conn->proto.generic;
+ RTMP *r = conn->proto.rtmp;
ssize_t nread;
(void)sockindex; /* unused */
@@ -302,7 +302,7 @@ static ssize_t rtmp_recv(struct connectdata *conn, int sockindex, char *buf,
static ssize_t rtmp_send(struct connectdata *conn, int sockindex,
const void *buf, size_t len, CURLcode *err)
{
- RTMP *r = conn->proto.generic;
+ RTMP *r = conn->proto.rtmp;
ssize_t num;
(void)sockindex; /* unused */
diff --git a/lib/curl_sasl.c b/lib/curl_sasl.c
index e8a940211..30ee45438 100644
--- a/lib/curl_sasl.c
+++ b/lib/curl_sasl.c
@@ -370,8 +370,9 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct connectdata *conn,
sasl->authused = SASL_MECH_PLAIN;
if(force_ir || data->set.sasl_ir)
- result = Curl_auth_create_plain_message(data, NULL, conn->user,
- conn->passwd, &resp, &len);
+ result = Curl_auth_create_plain_message(data, conn->sasl_authzid,
+ conn->user, conn->passwd,
+ &resp, &len);
}
else if(enabledmechs & SASL_MECH_LOGIN) {
mech = SASL_MECH_STRING_LOGIN;
@@ -453,8 +454,9 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct connectdata *conn,
*progress = SASL_DONE;
return result;
case SASL_PLAIN:
- result = Curl_auth_create_plain_message(data, NULL, conn->user,
- conn->passwd, &resp, &len);
+ result = Curl_auth_create_plain_message(data, conn->sasl_authzid,
+ conn->user, conn->passwd,
+ &resp, &len);
break;
case SASL_LOGIN:
result = Curl_auth_create_login_message(data, conn->user, &resp, &len);
diff --git a/lib/curl_setup.h b/lib/curl_setup.h
index 19c7e0b79..8e5c064dd 100644
--- a/lib/curl_setup.h
+++ b/lib/curl_setup.h
@@ -96,6 +96,10 @@
# include "config-vxworks.h"
#endif
+#ifdef __PLAN9__
+# include "config-plan9.h"
+#endif
+
#endif /* HAVE_CONFIG_H */
/* ================================================================ */
@@ -482,7 +486,6 @@
#ifdef WIN32
# define DIR_CHAR "\\"
-# define DOT_CHAR "_"
#else /* WIN32 */
@@ -508,14 +511,6 @@
# endif
# define DIR_CHAR "/"
-# ifndef DOT_CHAR
-# define DOT_CHAR "."
-# endif
-
-# ifdef MSDOS
-# undef DOT_CHAR
-# define DOT_CHAR "_"
-# endif
# ifndef fileno /* sunos 4 have this as a macro! */
int fileno(FILE *stream);
@@ -827,4 +822,8 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
#define UNITTEST static
#endif
+#if defined(USE_NGTCP2) || defined(USE_QUICHE)
+#define ENABLE_QUIC
+#endif
+
#endif /* HEADER_CURL_SETUP_H */
diff --git a/lib/doh.h b/lib/doh.h
index 34bfa6f2b..f522d3308 100644
--- a/lib/doh.h
+++ b/lib/doh.h
@@ -40,8 +40,7 @@ Curl_addrinfo *Curl_doh(struct connectdata *conn,
CURLcode Curl_doh_is_resolved(struct connectdata *conn,
struct Curl_dns_entry **dns);
-int Curl_doh_getsock(struct connectdata *conn, curl_socket_t *socks,
- int numsocks);
+int Curl_doh_getsock(struct connectdata *conn, curl_socket_t *socks);
typedef enum {
DOH_OK,
diff --git a/lib/easy.c b/lib/easy.c
index 58aeec2cf..cc670dcb3 100644
--- a/lib/easy.c
+++ b/lib/easy.c
@@ -187,16 +187,8 @@ static CURLcode global_init(long flags, bool memoryfuncs)
(void)Curl_ipv6works();
-#if defined(USE_LIBSSH2) && defined(HAVE_LIBSSH2_INIT)
- if(libssh2_init(0)) {
- DEBUGF(fprintf(stderr, "Error: libssh2_init failed\n"));
- return CURLE_FAILED_INIT;
- }
-#endif
-
-#if defined(USE_LIBSSH)
- if(ssh_init()) {
- DEBUGF(fprintf(stderr, "Error: libssh_init failed\n"));
+#if defined(USE_SSH)
+ if(Curl_ssh_init()) {
return CURLE_FAILED_INIT;
}
#endif
@@ -274,13 +266,7 @@ void curl_global_cleanup(void)
Curl_amiga_cleanup();
-#if defined(USE_LIBSSH2) && defined(HAVE_LIBSSH2_EXIT)
- (void)libssh2_exit();
-#endif
-
-#if defined(USE_LIBSSH)
- (void)ssh_finalize();
-#endif
+ Curl_ssh_cleanup();
init_flags = 0;
}
@@ -602,27 +588,11 @@ static CURLcode easy_transfer(struct Curl_multi *multi)
while(!done && !mcode) {
int still_running = 0;
- bool gotsocket = FALSE;
-
- mcode = Curl_multi_wait(multi, NULL, 0, 1000, NULL, &gotsocket);
-
- if(!mcode) {
- if(!gotsocket) {
- long sleep_ms;
-
- /* If it returns without any filedescriptor instantly, we need to
- avoid busy-looping during periods where it has nothing particular
- to wait for */
- curl_multi_timeout(multi, &sleep_ms);
- if(sleep_ms) {
- if(sleep_ms > 1000)
- sleep_ms = 1000;
- Curl_wait_ms((int)sleep_ms);
- }
- }
+ 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) {
@@ -942,6 +912,8 @@ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data)
*/
void curl_easy_reset(struct Curl_easy *data)
{
+ long old_buffer_size = data->set.buffer_size;
+
Curl_free_request_state(data);
/* zero out UserDefined data: */
@@ -965,6 +937,18 @@ void curl_easy_reset(struct Curl_easy *data)
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
Curl_http_auth_cleanup_digest(data);
#endif
+
+ /* resize receive buffer */
+ if(old_buffer_size != data->set.buffer_size) {
+ char *newbuff = realloc(data->state.buffer, data->set.buffer_size + 1);
+ if(!newbuff) {
+ DEBUGF(fprintf(stderr, "Error: realloc of buffer failed\n"));
+ /* nothing we can do here except use the old size */
+ data->set.buffer_size = old_buffer_size;
+ }
+ else
+ data->state.buffer = newbuff;
+ }
}
/*
@@ -1141,6 +1125,35 @@ CURLcode curl_easy_send(struct Curl_easy *data, const void *buffer,
}
/*
+ * Wrapper to call functions in Curl_conncache_foreach()
+ *
+ * Returns always 0.
+ */
+static int conn_upkeep(struct connectdata *conn,
+ void *param)
+{
+ /* Param is unused. */
+ (void)param;
+
+ if(conn->handler->connection_check) {
+ /* Do a protocol-specific keepalive check on the connection. */
+ conn->handler->connection_check(conn, CONNCHECK_KEEPALIVE);
+ }
+
+ return 0; /* continue iteration */
+}
+
+static CURLcode upkeep(struct conncache *conn_cache, void *data)
+{
+ /* Loop over every connection and make connection alive. */
+ Curl_conncache_foreach(data,
+ conn_cache,
+ data,
+ conn_upkeep);
+ return CURLE_OK;
+}
+
+/*
* Performs connection upkeep for the given session handle.
*/
CURLcode curl_easy_upkeep(struct Curl_easy *data)
@@ -1151,7 +1164,7 @@ CURLcode curl_easy_upkeep(struct Curl_easy *data)
if(data->multi_easy) {
/* Use the common function to keep connections alive. */
- return Curl_upkeep(&data->multi_easy->conn_cache, data);
+ return upkeep(&data->multi_easy->conn_cache, data);
}
else {
/* No connections, so just return success */
diff --git a/lib/ftp.c b/lib/ftp.c
index a3c69d549..4a005027b 100644
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -132,10 +132,8 @@ static CURLcode ftp_connect(struct connectdata *conn, bool *done);
static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection);
static CURLcode ftp_do_more(struct connectdata *conn, int *completed);
static CURLcode ftp_multi_statemach(struct connectdata *conn, bool *done);
-static int ftp_getsock(struct connectdata *conn, curl_socket_t *socks,
- int numsocks);
-static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks,
- int numsocks);
+static int ftp_getsock(struct connectdata *conn, curl_socket_t *socks);
+static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks);
static CURLcode ftp_doing(struct connectdata *conn,
bool *dophase_done);
static CURLcode ftp_setup_connection(struct connectdata * conn);
@@ -382,7 +380,7 @@ static CURLcode ReceivedServerConnect(struct connectdata *conn, bool *received)
struct ftp_conn *ftpc = &conn->proto.ftpc;
struct pingpong *pp = &ftpc->pp;
int result;
- time_t timeout_ms;
+ timediff_t timeout_ms;
ssize_t nread;
int ftpcode;
@@ -493,7 +491,7 @@ static CURLcode InitiateTransfer(struct connectdata *conn)
static CURLcode AllowServerConnect(struct connectdata *conn, bool *connected)
{
struct Curl_easy *data = conn->data;
- time_t timeout_ms;
+ timediff_t timeout_ms;
CURLcode result = CURLE_OK;
*connected = FALSE;
@@ -810,21 +808,16 @@ static CURLcode ftp_state_pwd(struct connectdata *conn)
/* For the FTP "protocol connect" and "doing" phases only */
static int ftp_getsock(struct connectdata *conn,
- curl_socket_t *socks,
- int numsocks)
+ curl_socket_t *socks)
{
- return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks);
+ return Curl_pp_getsock(&conn->proto.ftpc.pp, socks);
}
/* For the FTP "DO_MORE" phase only */
-static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks,
- int numsocks)
+static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks)
{
struct ftp_conn *ftpc = &conn->proto.ftpc;
- if(!numsocks)
- return GETSOCK_BLANK;
-
/* 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.
@@ -856,7 +849,7 @@ static int ftp_domore_getsock(struct connectdata *conn, curl_socket_t *socks,
return bits;
}
- return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks);
+ return Curl_pp_getsock(&conn->proto.ftpc.pp, socks);
}
/* This is called after the FTP_QUOTE state is passed.
diff --git a/lib/getenv.c b/lib/getenv.c
index 6acf70760..fa2abe3af 100644
--- a/lib/getenv.c
+++ b/lib/getenv.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -35,7 +35,7 @@ char *GetEnv(const char *variable)
return NULL;
#else
#ifdef WIN32
- char env[MAX_PATH]; /* MAX_PATH is from windef.h */
+ char env[4096];
char *temp = getenv(variable);
env[0] = '\0';
if(temp != NULL)
diff --git a/lib/getinfo.c b/lib/getinfo.c
index 5305090b4..b383bfefd 100644
--- a/lib/getinfo.c
+++ b/lib/getinfo.c
@@ -235,6 +235,9 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info,
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;
@@ -243,7 +246,6 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info,
case CURLINFO_PROTOCOL:
*param_longp = data->info.conn_protocol;
break;
-
default:
return CURLE_UNKNOWN_OPTION;
}
@@ -301,7 +303,9 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info,
case CURLINFO_REDIRECT_TIME_T:
*param_offt = data->progress.t_redirect;
break;
-
+ case CURLINFO_RETRY_AFTER:
+ *param_offt = data->info.retry_after;
+ break;
default:
return CURLE_UNKNOWN_OPTION;
}
diff --git a/lib/hostip.c b/lib/hostip.c
index cf33ed8f4..bd532a891 100644
--- a/lib/hostip.c
+++ b/lib/hostip.c
@@ -624,7 +624,7 @@ int Curl_resolv_timeout(struct connectdata *conn,
const char *hostname,
int port,
struct Curl_dns_entry **entry,
- time_t timeoutms)
+ timediff_t timeoutms)
{
#ifdef USE_ALARM_TIMEOUT
#ifdef HAVE_SIGACTION
@@ -1027,19 +1027,17 @@ CURLcode Curl_resolv_check(struct connectdata *conn,
}
int Curl_resolv_getsock(struct connectdata *conn,
- curl_socket_t *socks,
- int numsocks)
+ curl_socket_t *socks)
{
#ifdef CURLRES_ASYNCH
if(conn->data->set.doh)
/* nothing to wait for during DOH resolve, those handles have their own
sockets */
return GETSOCK_BLANK;
- return Curl_resolver_getsock(conn, socks, numsocks);
+ return Curl_resolver_getsock(conn, socks);
#else
(void)conn;
(void)socks;
- (void)numsocks;
return GETSOCK_BLANK;
#endif
}
diff --git a/lib/hostip.h b/lib/hostip.h
index 1bda524be..e0597ea96 100644
--- a/lib/hostip.h
+++ b/lib/hostip.h
@@ -25,6 +25,7 @@
#include "curl_setup.h"
#include "hash.h"
#include "curl_addrinfo.h"
+#include "timeval.h" /* for timediff_t */
#include "asyn.h"
#ifdef HAVE_SETJMP_H
@@ -89,7 +90,7 @@ int Curl_resolv(struct connectdata *conn,
struct Curl_dns_entry **dnsentry);
int Curl_resolv_timeout(struct connectdata *conn, const char *hostname,
int port, struct Curl_dns_entry **dnsentry,
- time_t timeoutms);
+ timediff_t timeoutms);
#ifdef CURLRES_IPV6
/*
@@ -240,7 +241,6 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data);
CURLcode Curl_resolv_check(struct connectdata *conn,
struct Curl_dns_entry **dns);
int Curl_resolv_getsock(struct connectdata *conn,
- curl_socket_t *socks,
- int numsocks);
+ curl_socket_t *socks);
#endif /* HEADER_CURL_HOSTIP_H */
diff --git a/lib/hostip6.c b/lib/hostip6.c
index 5511f1aab..e0e0c58df 100644
--- a/lib/hostip6.c
+++ b/lib/hostip6.c
@@ -165,7 +165,8 @@ Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
memset(&hints, 0, sizeof(hints));
hints.ai_family = pf;
- hints.ai_socktype = conn->socktype;
+ hints.ai_socktype = (conn->transport == TRNSPRT_TCP) ?
+ SOCK_STREAM : SOCK_DGRAM;
#ifndef USE_RESOLVE_ON_IPS
/*
diff --git a/lib/http.c b/lib/http.c
index db5e85b4b..4ef9c66a6 100644
--- a/lib/http.c
+++ b/lib/http.c
@@ -88,8 +88,7 @@
*/
static int http_getsock_do(struct connectdata *conn,
- curl_socket_t *socks,
- int numsocks);
+ curl_socket_t *socks);
static int http_should_fail(struct connectdata *conn);
#ifndef CURL_DISABLE_PROXY
@@ -99,8 +98,7 @@ static CURLcode add_haproxy_protocol_header(struct connectdata *conn);
#ifdef USE_SSL
static CURLcode https_connecting(struct connectdata *conn, bool *done);
static int https_getsock(struct connectdata *conn,
- curl_socket_t *socks,
- int numsocks);
+ curl_socket_t *socks);
#else
#define https_connecting(x,y) CURLE_COULDNT_CONNECT
#endif
@@ -171,10 +169,22 @@ static CURLcode http_setup_conn(struct connectdata *conn)
Curl_mime_initpart(&http->form, conn->data);
data->req.protop = http;
- if(!CONN_INUSE(conn))
- /* if not already multi-using, setup connection details */
- Curl_http2_setup_conn(conn);
- Curl_http2_setup_req(data);
+ if(data->set.httpversion == CURL_HTTP_VERSION_3) {
+ if(conn->handler->flags & PROTOPT_SSL)
+ /* Only go HTTP/3 directly on HTTPS URLs. It needs a UDP socket and does
+ the QUIC dance. */
+ conn->transport = TRNSPRT_QUIC;
+ else {
+ failf(data, "HTTP/3 requested for non-HTTPS URL");
+ return CURLE_URL_MALFORMAT;
+ }
+ }
+ else {
+ if(!CONN_INUSE(conn))
+ /* if not already multi-using, setup connection details */
+ Curl_http2_setup_conn(conn);
+ Curl_http2_setup_req(data);
+ }
return CURLE_OK;
}
@@ -1136,10 +1146,14 @@ Curl_send_buffer *Curl_add_buffer_init(void)
*/
void Curl_add_buffer_free(Curl_send_buffer **inp)
{
- Curl_send_buffer *in = *inp;
- if(in) /* deal with NULL input */
+ Curl_send_buffer *in;
+ if(!inp)
+ return;
+ in = *inp;
+ if(in) { /* deal with NULL input */
free(in->buffer);
- free(in);
+ free(in);
+ }
*inp = NULL;
}
@@ -1497,11 +1511,9 @@ CURLcode Curl_http_connect(struct connectdata *conn, bool *done)
interface and then we're always _sending_ a request and thus we wait for
the single socket to become writable only */
static int http_getsock_do(struct connectdata *conn,
- curl_socket_t *socks,
- int numsocks)
+ curl_socket_t *socks)
{
/* write mode */
- (void)numsocks; /* unused, we trust it to be at least 1 */
socks[0] = conn->sock[FIRSTSOCKET];
return GETSOCK_WRITESOCK(0);
}
@@ -1555,6 +1567,13 @@ static CURLcode https_connecting(struct connectdata *conn, bool *done)
CURLcode result;
DEBUGASSERT((conn) && (conn->handler->flags & PROTOPT_SSL));
+#ifdef ENABLE_QUIC
+ if(conn->transport == TRNSPRT_QUIC) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+#endif
+
/* perform SSL initialization for this socket */
result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, done);
if(result)
@@ -1564,11 +1583,10 @@ static CURLcode https_connecting(struct connectdata *conn, bool *done)
}
static int https_getsock(struct connectdata *conn,
- curl_socket_t *socks,
- int numsocks)
+ curl_socket_t *socks)
{
if(conn->handler->flags & PROTOPT_SSL)
- return Curl_ssl_getsock(conn, socks, numsocks);
+ return Curl_ssl_getsock(conn, socks);
return GETSOCK_BLANK;
}
#endif /* USE_SSL */
@@ -1650,6 +1668,12 @@ static bool use_http_1_1plus(const struct Curl_easy *data,
static const char *get_http_string(const struct Curl_easy *data,
const struct connectdata *conn)
{
+#ifdef ENABLE_QUIC
+ if((data->set.httpversion == CURL_HTTP_VERSION_3) ||
+ (conn->httpversion == 30))
+ return "3";
+#endif
+
#ifdef USE_NGHTTP2
if(conn->proto.httpc.h2)
return "2";
@@ -1670,7 +1694,7 @@ static CURLcode expect100(struct Curl_easy *data,
data->state.expect100header = FALSE; /* default to false unless it is set
to TRUE below */
if(use_http_1_1plus(data, conn) &&
- (conn->httpversion != 20)) {
+ (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) */
@@ -1700,7 +1724,7 @@ enum proxy_use {
will return an error code if one of the headers is
not formatted correctly */
CURLcode Curl_http_compile_trailers(struct curl_slist *trailers,
- Curl_send_buffer *buffer,
+ Curl_send_buffer **buffer,
struct Curl_easy *handle)
{
char *ptr = NULL;
@@ -1726,7 +1750,7 @@ CURLcode Curl_http_compile_trailers(struct curl_slist *trailers,
/* only add correctly formatted trailers */
ptr = strchr(trailers->data, ':');
if(ptr && *(ptr + 1) == ' ') {
- result = Curl_add_bufferf(&buffer, "%s%s", trailers->data,
+ result = Curl_add_bufferf(buffer, "%s%s", trailers->data,
endofline_native);
if(result)
return result;
@@ -1735,7 +1759,7 @@ CURLcode Curl_http_compile_trailers(struct curl_slist *trailers,
infof(handle, "Malformatted trailing header ! Skipping trailer.");
trailers = trailers->next;
}
- result = Curl_add_buffer(&buffer, endofline_network,
+ result = Curl_add_buffer(buffer, endofline_network,
strlen(endofline_network));
return result;
}
@@ -1851,7 +1875,7 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn,
Connection: */
checkprefix("Connection:", compare))
;
- else if((conn->httpversion == 20) &&
+ else if((conn->httpversion >= 20) &&
checkprefix("Transfer-Encoding:", compare))
/* HTTP/2 doesn't support chunked requests */
;
@@ -1982,55 +2006,57 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
const char *httpstring;
Curl_send_buffer *req_buffer;
curl_off_t postsize = 0; /* curl_off_t to handle large file sizes */
+ char *altused = NULL;
/* 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;
- if(conn->httpversion < 20) { /* unless the connection is re-used and already
- http2 */
- switch(conn->negnpn) {
- case CURL_HTTP_VERSION_2:
- conn->httpversion = 20; /* we know we're on HTTP/2 now */
-
- result = Curl_http2_switched(conn, NULL, 0);
- if(result)
- return result;
- break;
- case CURL_HTTP_VERSION_1_1:
- /* continue with HTTP/1.1 when explicitly requested */
- break;
- default:
- /* Check if user wants to use HTTP/2 with clear TCP*/
-#ifdef USE_NGHTTP2
- if(conn->data->set.httpversion ==
- CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) {
- if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
- /* We don't support HTTP/2 proxies yet. Also it's debatable whether
- or not this setting should apply to HTTP/2 proxies. */
- infof(data, "Ignoring HTTP/2 prior knowledge due to proxy\n");
- break;
- }
-
- DEBUGF(infof(data, "HTTP/2 over clean TCP\n"));
- conn->httpversion = 20;
+ if(conn->transport != TRNSPRT_QUIC) {
+ if(conn->httpversion < 20) { /* unless the connection is re-used and
+ already http2 */
+ switch(conn->negnpn) {
+ case CURL_HTTP_VERSION_2:
+ conn->httpversion = 20; /* we know we're on HTTP/2 now */
result = Curl_http2_switched(conn, NULL, 0);
if(result)
return result;
- }
+ break;
+ case CURL_HTTP_VERSION_1_1:
+ /* continue with HTTP/1.1 when explicitly requested */
+ break;
+ default:
+ /* Check if user wants to use HTTP/2 with clear TCP*/
+#ifdef USE_NGHTTP2
+ if(conn->data->set.httpversion ==
+ CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) {
+ if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
+ /* We don't support HTTP/2 proxies yet. Also it's debatable
+ whether or not this setting should apply to HTTP/2 proxies. */
+ infof(data, "Ignoring HTTP/2 prior knowledge due to proxy\n");
+ break;
+ }
+
+ DEBUGF(infof(data, "HTTP/2 over clean TCP\n"));
+ conn->httpversion = 20;
+
+ result = Curl_http2_switched(conn, NULL, 0);
+ if(result)
+ return result;
+ }
#endif
- break;
+ break;
+ }
+ }
+ else {
+ /* prepare for a http2 request */
+ result = Curl_http2_setup(conn);
+ if(result)
+ return result;
}
}
- else {
- /* prepare for a http2 request */
- result = Curl_http2_setup(conn);
- if(result)
- return result;
- }
-
http = data->req.protop;
DEBUGASSERT(http);
@@ -2226,14 +2252,16 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
else {
if((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
(((httpreq == HTTPREQ_POST_MIME || httpreq == HTTPREQ_POST_FORM) &&
- http->postsize < 0) ||
- (data->set.upload && data->state.infilesize == -1))) {
+ http->postsize < 0) ||
+ ((data->set.upload || httpreq == HTTPREQ_POST) &&
+ data->state.infilesize == -1))) {
if(conn->bits.authneg)
/* don't enable chunked during auth neg */
;
else if(use_http_1_1plus(data, conn)) {
- /* HTTP, upload, unknown file size and not HTTP 1.0 */
- data->req.upload_chunky = TRUE;
+ if(conn->httpversion < 20)
+ /* HTTP, upload, unknown file size and not HTTP 1.0 */
+ data->req.upload_chunky = TRUE;
}
else {
failf(data, "Chunky upload is not supported by HTTP 1.0");
@@ -2334,7 +2362,6 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
/* and no fragment part */
CURLUcode uc;
- char *url;
CURLU *h = curl_url_dup(data->state.uh);
if(!h)
return CURLE_OUT_OF_MEMORY;
@@ -2365,19 +2392,15 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
return CURLE_OUT_OF_MEMORY;
}
}
- /* now extract the new version of the URL */
- uc = curl_url_get(h, CURLUPART_URL, &url, 0);
+ /* Extract the the URL to use in the request. Store in STRING_TEMP_URL for
+ clean-up reasons if the function returns before the free() further
+ down. */
+ uc = curl_url_get(h, CURLUPART_URL, &data->set.str[STRING_TEMP_URL], 0);
if(uc) {
curl_url_cleanup(h);
return CURLE_OUT_OF_MEMORY;
}
- if(data->change.url_alloc)
- free(data->change.url);
-
- data->change.url = url;
- data->change.url_alloc = TRUE;
-
curl_url_cleanup(h);
if(strcasecompare("ftp", data->state.up.scheme)) {
@@ -2556,12 +2579,16 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
query = NULL;
}
+#ifndef CURL_DISABLE_PROXY
/* url */
if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
- char *url = data->change.url;
+ char *url = data->set.str[STRING_TEMP_URL];
result = Curl_add_buffer(&req_buffer, url, strlen(url));
+ Curl_safefree(data->set.str[STRING_TEMP_URL]);
}
- else if(paste_ftp_userpwd)
+ else
+#endif
+ if(paste_ftp_userpwd)
result = Curl_add_bufferf(&req_buffer, "ftp://%s:%s@%s",
conn->user, conn->passwd,
path + sizeof("ftp://") - 1);
@@ -2575,6 +2602,14 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
if(result)
return result;
+#ifdef USE_ALTSVC
+ if(conn->bits.altused && !Curl_checkheaders(conn, "Alt-Used")) {
+ altused = aprintf("Alt-Used: %s:%d\r\n",
+ conn->conn_to_host.name, conn->conn_to_port);
+ if(!altused)
+ return CURLE_OUT_OF_MEMORY;
+ }
+#endif
result =
Curl_add_bufferf(&req_buffer,
"%s" /* ftp typecode (;type=x) */
@@ -2589,7 +2624,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
"%s" /* accept-encoding */
"%s" /* referer */
"%s" /* Proxy-Connection */
- "%s",/* transfer-encoding */
+ "%s" /* transfer-encoding */
+ "%s",/* Alt-Used */
ftp_typecode,
httpstring,
@@ -2615,13 +2651,15 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
!conn->bits.tunnel_proxy &&
!Curl_checkProxyheaders(conn, "Proxy-Connection"))?
"Proxy-Connection: Keep-Alive\r\n":"",
- te
+ te,
+ altused ? altused : ""
);
/* clear userpwd and proxyuserpwd to avoid re-using old credentials
* from re-used connections */
Curl_safefree(conn->allocptr.userpwd);
Curl_safefree(conn->allocptr.proxyuserpwd);
+ free(altused);
if(result)
return result;
@@ -3660,6 +3698,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
* guarantees on future behaviors since it isn't within the protocol.
*/
char separator;
+ char twoorthree[2];
nc = sscanf(HEADER1,
" HTTP/%1d.%1d%c%3d",
&httpversion_major,
@@ -3667,8 +3706,8 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
&separator,
&k->httpcode);
- if(nc == 1 && httpversion_major == 2 &&
- 1 == sscanf(HEADER1, " HTTP/2 %d", &k->httpcode)) {
+ if(nc == 1 && httpversion_major >= 2 &&
+ 2 == sscanf(HEADER1, " HTTP/%1[23] %d", twoorthree, &k->httpcode)) {
conn->httpversion = 0;
nc = 4;
separator = ' ';
@@ -3706,7 +3745,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
}
}
else {
- failf(data, "Unsupported HTTP version in response\n");
+ failf(data, "Unsupported HTTP version in response");
return CURLE_UNSUPPORTED_PROTOCOL;
}
}
@@ -3935,6 +3974,19 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
if(result)
return result;
}
+ else if(checkprefix("Retry-After:", k->p)) {
+ /* Retry-After = HTTP-date / delay-seconds */
+ curl_off_t retry_after = 0; /* zero for unknown or "now" */
+ time_t date = curl_getdate(&k->p[12], NULL);
+ if(-1 == date) {
+ /* not a date, try it as a decimal number */
+ (void)curlx_strtoofft(&k->p[12], NULL, 10, &retry_after);
+ }
+ else
+ /* convert date to number of seconds into the future */
+ retry_after = date - time(NULL);
+ data->info.retry_after = retry_after; /* store it */
+ }
else if(!k->http_bodyless && checkprefix("Content-Range:", k->p)) {
/* Content-Range: bytes [num]-
Content-Range: bytes: [num]-
diff --git a/lib/http.h b/lib/http.h
index de3769f1e..b27c3c861 100644
--- a/lib/http.h
+++ b/lib/http.h
@@ -75,7 +75,7 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn,
bool is_connect,
Curl_send_buffer *req_buffer);
CURLcode Curl_http_compile_trailers(struct curl_slist *trailers,
- Curl_send_buffer *buffer,
+ Curl_send_buffer **buffer,
struct Curl_easy *handle);
/* protocol-specific functions set up to be called by the main engine */
@@ -126,6 +126,10 @@ CURLcode Curl_http_auth_act(struct connectdata *conn);
#endif /* CURL_DISABLE_HTTP */
+#ifdef USE_NGHTTP3
+struct h3out; /* see ngtcp2 */
+#endif
+
/****************************************************************************
* HTTP unique setup
***************************************************************************/
@@ -172,19 +176,34 @@ struct HTTP {
int status_code; /* HTTP status code */
const uint8_t *pausedata; /* pointer to data received in on_data_chunk */
size_t pauselen; /* the number of bytes left in data */
- bool closed; /* TRUE on HTTP2 stream close */
bool close_handled; /* TRUE if stream closure is handled by libcurl */
+
+ char **push_headers; /* allocated array */
+ size_t push_headers_used; /* number of entries filled in */
+ size_t push_headers_alloc; /* number of entries allocated */
+#endif
+#if defined(USE_NGHTTP2) || defined(USE_NGHTTP3)
+ bool closed; /* TRUE on HTTP2 stream close */
char *mem; /* points to a buffer in memory to store received data */
size_t len; /* size of the buffer 'mem' points to */
size_t memlen; /* size of data copied to mem */
-
+#endif
+#if defined(USE_NGHTTP2) || defined(ENABLE_QUIC)
+ /* fields used by both HTTP/2 and HTTP/3 */
const uint8_t *upload_mem; /* points to a buffer to read from */
size_t upload_len; /* size of the buffer 'upload_mem' points to */
curl_off_t upload_left; /* number of bytes left to upload */
+#endif
- char **push_headers; /* allocated array */
- size_t push_headers_used; /* number of entries filled in */
- size_t push_headers_alloc; /* number of entries allocated */
+#ifdef ENABLE_QUIC
+ /*********** for HTTP/3 we store stream-local data here *************/
+ int64_t stream3_id; /* stream we are interested in */
+ bool firstbody; /* FALSE until body arrives */
+ bool h3req; /* FALSE until request is issued */
+ bool upload_done;
+#endif
+#ifdef USE_NGHTTP3
+ struct h3out *h3out; /* per-stream buffers for upload */
#endif
};
diff --git a/lib/http2.c b/lib/http2.c
index c2a45cded..8cdcf968c 100644
--- a/lib/http2.c
+++ b/lib/http2.c
@@ -100,16 +100,11 @@ void Curl_http2_init_userset(struct UserDefined *set)
}
static int http2_perform_getsock(const struct connectdata *conn,
- curl_socket_t *sock, /* points to
- numsocks
- number of
- sockets */
- int numsocks)
+ curl_socket_t *sock)
{
const struct http_conn *c = &conn->proto.httpc;
struct SingleRequest *k = &conn->data->req;
int bitmap = GETSOCK_BLANK;
- (void)numsocks;
sock[0] = conn->sock[FIRSTSOCKET];
@@ -126,11 +121,9 @@ static int http2_perform_getsock(const struct connectdata *conn,
}
static int http2_getsock(struct connectdata *conn,
- curl_socket_t *sock, /* points to numsocks
- number of sockets */
- int numsocks)
+ curl_socket_t *socks)
{
- return http2_perform_getsock(conn, sock, numsocks);
+ return http2_perform_getsock(conn, socks);
}
/*
@@ -240,7 +233,7 @@ static unsigned int http2_conncheck(struct connectdata *check,
if(checks_to_perform & CONNCHECK_KEEPALIVE) {
struct curltime now = Curl_now();
- time_t elapsed = Curl_timediff(now, check->keepalive);
+ timediff_t elapsed = Curl_timediff(now, check->keepalive);
if(elapsed > check->upkeep_interval_ms) {
/* Perform an HTTP/2 PING */
@@ -1566,6 +1559,11 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex,
if(should_close_session(httpc)) {
H2BUGF(infof(data,
"http2_recv: nothing to do in this session\n"));
+ if(conn->bits.close) {
+ /* already marked for closure, return OK and we're done */
+ *err = CURLE_OK;
+ return 0;
+ }
*err = CURLE_HTTP2;
return -1;
}
@@ -1755,6 +1753,9 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex,
else if(!stream->closed) {
drained_transfer(data, httpc);
}
+ else
+ /* this stream is closed, trigger a another read ASAP to detect that */
+ Curl_expire(data, 0, EXPIRE_RUN_NOW);
return retlen;
}
diff --git a/lib/http_negotiate.c b/lib/http_negotiate.c
index c8f406444..8e1f3bf68 100644
--- a/lib/http_negotiate.c
+++ b/lib/http_negotiate.c
@@ -148,10 +148,10 @@ CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy)
}
if(!neg_ctx->context) {
result = Curl_input_negotiate(conn, proxy, "Negotiate");
- if(result == CURLE_LOGIN_DENIED) {
+ 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 */
- conn->data->state.authproblem = TRUE;
+ authp->done = TRUE;
return CURLE_OK;
}
else if(result)
diff --git a/lib/imap.c b/lib/imap.c
index 14ea9ae57..ee9e04aa1 100644
--- a/lib/imap.c
+++ b/lib/imap.c
@@ -95,8 +95,7 @@ static CURLcode imap_done(struct connectdata *conn, CURLcode status,
static CURLcode imap_connect(struct connectdata *conn, bool *done);
static CURLcode imap_disconnect(struct connectdata *conn, bool dead);
static CURLcode imap_multi_statemach(struct connectdata *conn, bool *done);
-static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
- int numsocks);
+static int imap_getsock(struct connectdata *conn, curl_socket_t *socks);
static CURLcode imap_doing(struct connectdata *conn, bool *dophase_done);
static CURLcode imap_setup_connection(struct connectdata *conn);
static char *imap_atom(const char *str, bool escape_only);
@@ -1392,10 +1391,9 @@ static CURLcode imap_init(struct connectdata *conn)
}
/* For the IMAP "protocol connect" and "doing" phases only */
-static int imap_getsock(struct connectdata *conn, curl_socket_t *socks,
- int numsocks)
+static int imap_getsock(struct connectdata *conn, curl_socket_t *socks)
{
- return Curl_pp_getsock(&conn->proto.imapc.pp, socks, numsocks);
+ return Curl_pp_getsock(&conn->proto.imapc.pp, socks);
}
/***********************************************************************
diff --git a/lib/md4.c b/lib/md4.c
index 0b4ea9a3c..bbf897508 100644
--- a/lib/md4.c
+++ b/lib/md4.c
@@ -1,5 +1,223 @@
-/*
- * !checksrc! disable COPYRIGHT
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed 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.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#if !defined(CURL_DISABLE_CRYPTO_AUTH)
+
+#include "curl_md4.h"
+#include "warnless.h"
+
+#ifdef USE_OPENSSL
+#include <openssl/opensslconf.h>
+#endif
+#ifdef USE_MBEDTLS
+#include <mbedtls/config.h>
+#endif
+
+#if defined(USE_GNUTLS_NETTLE)
+
+#include <nettle/md4.h>
+
+#include "curl_memory.h"
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+typedef struct md4_ctx MD4_CTX;
+
+static void MD4_Init(MD4_CTX *ctx)
+{
+ md4_init(ctx);
+}
+
+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_GNUTLS)
+
+#include <gcrypt.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+typedef struct gcry_md_hd_t MD4_CTX;
+
+static void MD4_Init(MD4_CTX *ctx)
+{
+ gcry_md_open(ctx, GCRY_MD_MD4, 0);
+}
+
+static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size)
+{
+ gcry_md_write(*ctx, data, size);
+}
+
+static void MD4_Final(unsigned char *result, MD4_CTX *ctx)
+{
+ memcpy(result, gcry_md_read(ctx, 0), MD4_DIGEST_LENGTH);
+ gcry_md_close(ctx);
+}
+
+#elif defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4)
+/* When OpenSSL is available we use the MD4-functions from OpenSSL */
+#include <openssl/md4.h>
+
+#elif defined(USE_SECTRANSP)
+
+#include <CommonCrypto/CommonDigest.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+typedef struct {
+ void *data;
+ unsigned long size;
+} MD4_CTX;
+
+static void MD4_Init(MD4_CTX *ctx)
+{
+ ctx->data = NULL;
+ ctx->size = 0;
+}
+
+static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size)
+{
+ if(ctx->data == NULL) {
+ ctx->data = malloc(size);
+ if(ctx->data != NULL) {
+ memcpy(ctx->data, data, size);
+ ctx->size = size;
+ }
+ }
+}
+
+static void MD4_Final(unsigned char *result, MD4_CTX *ctx)
+{
+ if(ctx->data != NULL) {
+ (void)CC_MD4(ctx->data, (CC_LONG) ctx->size, result);
+
+ Curl_safefree(ctx->data);
+ ctx->size = 0;
+ }
+}
+
+#elif defined(USE_WIN32_CRYPTO)
+
+#include <wincrypt.h>
+
+#include "curl_memory.h"
+ /* The last #include file should be: */
+#include "memdebug.h"
+
+typedef struct {
+ HCRYPTPROV hCryptProv;
+ HCRYPTHASH hHash;
+} MD4_CTX;
+
+static void MD4_Init(MD4_CTX *ctx)
+{
+ ctx->hCryptProv = 0;
+ ctx->hHash = 0;
+
+ if(CryptAcquireContext(&ctx->hCryptProv, NULL, NULL, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT)) {
+ CryptCreateHash(ctx->hCryptProv, CALG_MD4, 0, 0, &ctx->hHash);
+ }
+}
+
+static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size)
+{
+ CryptHashData(ctx->hHash, 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))
+
+#include <mbedtls/md4.h>
+
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+typedef struct {
+ void *data;
+ unsigned long size;
+} MD4_CTX;
+
+static void MD4_Init(MD4_CTX *ctx)
+{
+ ctx->data = NULL;
+ ctx->size = 0;
+}
+
+static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size)
+{
+ if(ctx->data == NULL) {
+ ctx->data = malloc(size);
+ if(ctx->data != NULL) {
+ memcpy(ctx->data, data, size);
+ ctx->size = size;
+ }
+ }
+}
+
+static void MD4_Final(unsigned char *result, MD4_CTX *ctx)
+{
+ if(ctx->data != NULL) {
+ mbedtls_md4(ctx->data, ctx->size, result);
+
+ Curl_safefree(ctx->data);
+ ctx->size = 0;
+ }
+}
+
+#else
+/* When no other crypto library is available, or the crypto library doesn't
+ * 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).
*
@@ -36,26 +254,6 @@
* compile-time configuration.
*/
-#include "curl_setup.h"
-
-#ifdef USE_OPENSSL
-#include <openssl/opensslconf.h>
-#endif
-#ifdef USE_MBEDTLS
-#include <mbedtls/config.h>
-#endif
-
-/* The NSS, OS/400, and when not included, OpenSSL and mbed TLS crypto
- * libraries do not provide the MD4 hash algorithm, so we use this
- * implementation of it */
-#if defined(USE_NSS) || defined(USE_OS400CRYPTO) || \
- (defined(USE_OPENSSL) && defined(OPENSSL_NO_MD4)) || \
- (defined(USE_MBEDTLS) && !defined(MBEDTLS_MD4_C))
-
-#include "curl_md4.h"
-#include "warnless.h"
-
-#ifndef HAVE_OPENSSL
#include <string.h>
@@ -305,7 +503,7 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx)
memset(ctx, 0, sizeof(*ctx));
}
-#endif
+#endif /* CRYPTO LIBS */
void Curl_md4it(unsigned char *output, const unsigned char *input, size_t len)
{
@@ -315,6 +513,4 @@ void Curl_md4it(unsigned char *output, const unsigned char *input, size_t len)
MD4_Final(output, &ctx);
}
-#endif /* defined(USE_NSS) || defined(USE_OS400CRYPTO) ||
- (defined(USE_OPENSSL) && defined(OPENSSL_NO_MD4)) ||
- (defined(USE_MBEDTLS) && !defined(MBEDTLS_MD4_C)) */
+#endif /* CURL_DISABLE_CRYPTO_AUTH */
diff --git a/lib/multi.c b/lib/multi.c
index e5761fb7c..5fe6c58a4 100644..100755
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -818,25 +818,27 @@ void Curl_attach_connnection(struct Curl_easy *data,
}
static int waitconnect_getsock(struct connectdata *conn,
- curl_socket_t *sock,
- int numsocks)
+ curl_socket_t *sock)
{
int i;
int s = 0;
int rc = 0;
- if(!numsocks)
- return GETSOCK_BLANK;
-
#ifdef USE_SSL
if(CONNECT_FIRSTSOCKET_PROXY_SSL())
- return Curl_ssl_getsock(conn, sock, numsocks);
+ return Curl_ssl_getsock(conn, sock);
#endif
for(i = 0; i<2; i++) {
if(conn->tempsock[i] != CURL_SOCKET_BAD) {
sock[s] = conn->tempsock[i];
- rc |= GETSOCK_WRITESOCK(s++);
+ rc |= GETSOCK_WRITESOCK(s);
+#ifdef ENABLE_QUIC
+ if(conn->transport == TRNSPRT_QUIC)
+ /* when connecting QUIC, we want to read the socket too */
+ rc |= GETSOCK_READSOCK(s);
+#endif
+ s++;
}
}
@@ -844,12 +846,8 @@ static int waitconnect_getsock(struct connectdata *conn,
}
static int waitproxyconnect_getsock(struct connectdata *conn,
- curl_socket_t *sock,
- int numsocks)
+ curl_socket_t *sock)
{
- if(!numsocks)
- return GETSOCK_BLANK;
-
sock[0] = conn->sock[FIRSTSOCKET];
/* when we've sent a CONNECT to a proxy, we should rather wait for the
@@ -861,19 +859,37 @@ static int waitproxyconnect_getsock(struct connectdata *conn,
}
static int domore_getsock(struct connectdata *conn,
- curl_socket_t *socks,
- int numsocks)
+ curl_socket_t *socks)
{
if(conn && conn->handler->domore_getsock)
- return conn->handler->domore_getsock(conn, socks, numsocks);
+ return conn->handler->domore_getsock(conn, socks);
+ return GETSOCK_BLANK;
+}
+
+static int doing_getsock(struct connectdata *conn,
+ curl_socket_t *socks)
+{
+ if(conn && conn->handler->doing_getsock)
+ return conn->handler->doing_getsock(conn, socks);
return GETSOCK_BLANK;
}
-/* returns bitmapped flags for this handle and its sockets */
+static int protocol_getsock(struct connectdata *conn,
+ curl_socket_t *socks)
+{
+ if(conn->handler->proto_getsock)
+ return conn->handler->proto_getsock(conn, socks);
+ /* Backup getsock logic. Since there is a live socket in use, we must wait
+ for it or it will be removed from watching when the multi_socket API is
+ used. */
+ socks[0] = conn->sock[FIRSTSOCKET];
+ return GETSOCK_READSOCK(0) | GETSOCK_WRITESOCK(0);
+}
+
+/* returns bitmapped flags for this handle and its sockets. The 'socks[]'
+ array contains MAX_SOCKSPEREASYHANDLE entries. */
static int multi_getsock(struct Curl_easy *data,
- curl_socket_t *socks, /* points to numsocks number
- of sockets */
- int numsocks)
+ curl_socket_t *socks)
{
/* The no connection case can happen when this is called from
curl_multi_remove_handle() => singlesocket() => multi_getsock().
@@ -905,30 +921,30 @@ static int multi_getsock(struct Curl_easy *data,
return 0;
case CURLM_STATE_WAITRESOLVE:
- return Curl_resolv_getsock(data->conn, socks, numsocks);
+ return Curl_resolv_getsock(data->conn, socks);
case CURLM_STATE_PROTOCONNECT:
case CURLM_STATE_SENDPROTOCONNECT:
- return Curl_protocol_getsock(data->conn, socks, numsocks);
+ return protocol_getsock(data->conn, socks);
case CURLM_STATE_DO:
case CURLM_STATE_DOING:
- return Curl_doing_getsock(data->conn, socks, numsocks);
+ return doing_getsock(data->conn, socks);
case CURLM_STATE_WAITPROXYCONNECT:
- return waitproxyconnect_getsock(data->conn, socks, numsocks);
+ return waitproxyconnect_getsock(data->conn, socks);
case CURLM_STATE_WAITCONNECT:
- return waitconnect_getsock(data->conn, socks, numsocks);
+ return waitconnect_getsock(data->conn, socks);
case CURLM_STATE_DO_MORE:
- return domore_getsock(data->conn, socks, numsocks);
+ return domore_getsock(data->conn, socks);
case CURLM_STATE_DO_DONE: /* since is set after DO is completed, we switch
to waiting for the same as the *PERFORM
states */
case CURLM_STATE_PERFORM:
- return Curl_single_getsock(data->conn, socks, numsocks);
+ return Curl_single_getsock(data->conn, socks);
}
}
@@ -954,7 +970,7 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi,
data = multi->easyp;
while(data) {
- int bitmap = multi_getsock(data, sockbunch, MAX_SOCKSPEREASYHANDLE);
+ int bitmap = multi_getsock(data, sockbunch);
for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) {
curl_socket_t s = CURL_SOCKET_BAD;
@@ -984,12 +1000,12 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi,
#define NUM_POLLS_ON_STACK 10
-CURLMcode Curl_multi_wait(struct Curl_multi *multi,
- struct curl_waitfd extra_fds[],
- unsigned int extra_nfds,
- int timeout_ms,
- int *ret,
- bool *gotsocket) /* if any socket was checked */
+static CURLMcode Curl_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 */
{
struct Curl_easy *data;
curl_socket_t sockbunch[MAX_SOCKSPEREASYHANDLE];
@@ -1003,9 +1019,6 @@ CURLMcode Curl_multi_wait(struct Curl_multi *multi,
struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
struct pollfd *ufds = &a_few_on_stack[0];
- if(gotsocket)
- *gotsocket = FALSE;
-
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
@@ -1015,7 +1028,7 @@ CURLMcode Curl_multi_wait(struct Curl_multi *multi,
/* Count up how many fds we have from the multi handle */
data = multi->easyp;
while(data) {
- bitmap = multi_getsock(data, sockbunch, MAX_SOCKSPEREASYHANDLE);
+ bitmap = multi_getsock(data, sockbunch);
for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) {
curl_socket_t s = CURL_SOCKET_BAD;
@@ -1065,7 +1078,7 @@ CURLMcode Curl_multi_wait(struct Curl_multi *multi,
/* Add the curl handles to our pollfds first */
data = multi->easyp;
while(data) {
- bitmap = multi_getsock(data, sockbunch, MAX_SOCKSPEREASYHANDLE);
+ bitmap = multi_getsock(data, sockbunch);
for(i = 0; i< MAX_SOCKSPEREASYHANDLE; i++) {
curl_socket_t s = CURL_SOCKET_BAD;
@@ -1134,9 +1147,19 @@ CURLMcode Curl_multi_wait(struct Curl_multi *multi,
free(ufds);
if(ret)
*ret = retcode;
- if(gotsocket && (extra_fds || curlfds))
+ if(!extrawait || extra_fds || curlfds)
/* if any socket was checked */
- *gotsocket = TRUE;
+ ;
+ else {
+ long sleep_ms = 0;
+
+ /* Avoid busy-looping when there's nothing particular to wait for */
+ if(!curl_multi_timeout(multi, &sleep_ms) && sleep_ms) {
+ if(sleep_ms > timeout_ms)
+ sleep_ms = timeout_ms;
+ Curl_wait_ms((int)sleep_ms);
+ }
+ }
return CURLM_OK;
}
@@ -1147,7 +1170,16 @@ CURLMcode curl_multi_wait(struct Curl_multi *multi,
int timeout_ms,
int *ret)
{
- return Curl_multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, NULL);
+ return Curl_multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, FALSE);
+}
+
+CURLMcode curl_multi_poll(struct Curl_multi *multi,
+ struct curl_waitfd extra_fds[],
+ unsigned int extra_nfds,
+ int timeout_ms,
+ int *ret)
+{
+ return Curl_multi_wait(multi, extra_fds, extra_nfds, timeout_ms, ret, TRUE);
}
/*
@@ -1247,6 +1279,109 @@ static CURLcode multi_do_more(struct connectdata *conn, int *complete)
return result;
}
+/*
+ * 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 connectdata *conn,
+ bool *done)
+{
+ CURLcode result = CURLE_OK;
+
+ if(conn && conn->handler->connecting) {
+ *done = FALSE;
+ result = conn->handler->connecting(conn, 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 connectdata *conn, bool *done)
+{
+ CURLcode result = CURLE_OK;
+
+ if(conn && conn->handler->doing) {
+ *done = FALSE;
+ result = conn->handler->doing(conn, 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 connectdata *conn,
+ bool *protocol_done)
+{
+ CURLcode result = CURLE_OK;
+
+ DEBUGASSERT(conn);
+ DEBUGASSERT(protocol_done);
+
+ *protocol_done = FALSE;
+
+ if(conn->bits.tcpconnect[FIRSTSOCKET] && conn->bits.protoconnstart) {
+ /* We already are connected, get back. This may happen when the connect
+ worked fine in the first call, like when we connect to a local server
+ or proxy. Note that we don't know if the protocol is actually done.
+
+ Unless this protocol doesn't have any protocol-connect callback, as
+ then we know we're done. */
+ if(!conn->handler->connecting)
+ *protocol_done = TRUE;
+
+ return CURLE_OK;
+ }
+
+ if(!conn->bits.protoconnstart) {
+
+ result = Curl_proxy_connect(conn, FIRSTSOCKET);
+ if(result)
+ return result;
+
+ if(CONNECT_FIRSTSOCKET_PROXY_SSL())
+ /* wait for HTTPS proxy SSL initialization to complete */
+ return CURLE_OK;
+
+ if(conn->bits.tunnel_proxy && conn->bits.httpproxy &&
+ Curl_connect_ongoing(conn))
+ /* when using an HTTP tunnel proxy, await complete tunnel establishment
+ before proceeding further. Return CURLE_OK so we'll be called again */
+ return CURLE_OK;
+
+ if(conn->handler->connect_it) {
+ /* is there a protocol-specific connect() procedure? */
+
+ /* Call the protocol-specific connect function */
+ result = conn->handler->connect_it(conn, protocol_done);
+ }
+ else
+ *protocol_done = TRUE;
+
+ /* it has started, possibly even completed but that knowledge isn't stored
+ in this bit! */
+ if(!result)
+ conn->bits.protoconnstart = TRUE;
+ }
+
+ return result; /* pass back status */
+}
+
+
static CURLMcode multi_runsingle(struct Curl_multi *multi,
struct curltime now,
struct Curl_easy *data)
@@ -1254,7 +1389,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
struct Curl_message *msg = NULL;
bool connected;
bool async;
- bool protocol_connect = FALSE;
+ bool protocol_connected = FALSE;
bool dophase_done = FALSE;
bool done = FALSE;
CURLMcode rc;
@@ -1373,7 +1508,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
if(data->set.connecttimeout)
Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT);
- result = Curl_connect(data, &async, &protocol_connect);
+ result = Curl_connect(data, &async, &protocol_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. */
@@ -1401,7 +1536,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
WAITDO or DO! */
rc = CURLM_CALL_MULTI_PERFORM;
- if(protocol_connect)
+ if(protocol_connected)
multistate(data, CURLM_STATE_DO);
else {
#ifndef CURL_DISABLE_HTTP
@@ -1456,7 +1591,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
if(dns) {
/* Perform the next step in the connection phase, and then move on
to the WAITCONNECT state */
- result = Curl_once_resolved(data->conn, &protocol_connect);
+ result = Curl_once_resolved(data->conn, &protocol_connected);
if(result)
/* if Curl_once_resolved() returns failure, the connection struct
@@ -1465,7 +1600,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
else {
/* call again please so that we get the next socket setup */
rc = CURLM_CALL_MULTI_PERFORM;
- if(protocol_connect)
+ if(protocol_connected)
multistate(data, CURLM_STATE_DO);
else {
#ifndef CURL_DISABLE_HTTP
@@ -1490,7 +1625,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
case CURLM_STATE_WAITPROXYCONNECT:
/* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */
DEBUGASSERT(data->conn);
- result = Curl_http_connect(data->conn, &protocol_connect);
+ result = Curl_http_connect(data->conn, &protocol_connected);
if(data->conn->bits.proxy_connect_closed) {
rc = CURLM_CALL_MULTI_PERFORM;
@@ -1541,8 +1676,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
break;
case CURLM_STATE_SENDPROTOCONNECT:
- result = Curl_protocol_connect(data->conn, &protocol_connect);
- if(!result && !protocol_connect)
+ result = protocol_connect(data->conn, &protocol_connected);
+ if(!result && !protocol_connected)
/* switch to waiting state */
multistate(data, CURLM_STATE_PROTOCONNECT);
else if(!result) {
@@ -1560,8 +1695,8 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
case CURLM_STATE_PROTOCONNECT:
/* protocol-specific connect phase */
- result = Curl_protocol_connecting(data->conn, &protocol_connect);
- if(!result && protocol_connect) {
+ result = protocol_connecting(data->conn, &protocol_connected);
+ if(!result && protocol_connected) {
/* after the connect has completed, go WAITDO or DO */
multistate(data, CURLM_STATE_DO);
rc = CURLM_CALL_MULTI_PERFORM;
@@ -1683,8 +1818,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
case CURLM_STATE_DOING:
/* we continue DOING until the DO phase is complete */
DEBUGASSERT(data->conn);
- result = Curl_protocol_doing(data->conn,
- &dophase_done);
+ result = protocol_doing(data->conn, &dophase_done);
if(!result) {
if(dophase_done) {
/* after DO, go DO_DONE or DO_MORE */
@@ -2012,13 +2146,15 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
if(stream_error) {
/* Don't attempt to send data over a connection that timed out */
bool dead_connection = result == CURLE_OPERATION_TIMEDOUT;
- /* disconnect properly */
- Curl_disconnect(data, data->conn, dead_connection);
+ struct connectdata *conn = data->conn;
/* This is where we make sure that the conn pointer is reset.
We don't have to do this in every case block above where a
failure is detected */
detach_connnection(data);
+
+ /* disconnect properly */
+ Curl_disconnect(data, conn, dead_connection);
}
}
else if(data->mstate == CURLM_STATE_CONNECT) {
@@ -2234,7 +2370,7 @@ static CURLMcode singlesocket(struct Curl_multi *multi,
/* Fill in the 'current' struct with the state as it is now: what sockets to
supervise and for what actions */
- curraction = multi_getsock(data, socks, MAX_SOCKSPEREASYHANDLE);
+ curraction = multi_getsock(data, socks);
/* 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
@@ -2846,7 +2982,7 @@ multi_addtimeout(struct Curl_easy *data,
*
* Expire replaces a former timeout using the same id if already set.
*/
-void Curl_expire(struct Curl_easy *data, time_t milli, expire_id id)
+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;
@@ -2860,7 +2996,7 @@ void Curl_expire(struct Curl_easy *data, time_t milli, expire_id id)
DEBUGASSERT(id < EXPIRE_LAST);
set = Curl_now();
- set.tv_sec += milli/1000;
+ set.tv_sec += (time_t)(milli/1000); /* might be a 64 to 32 bit conversion */
set.tv_usec += (unsigned int)(milli%1000)*1000;
if(set.tv_usec >= 1000000) {
diff --git a/lib/multiif.h b/lib/multiif.h
index a64455867..0755a7cd2 100644
--- a/lib/multiif.h
+++ b/lib/multiif.h
@@ -27,7 +27,7 @@
*/
void Curl_updatesocket(struct Curl_easy *data);
-void Curl_expire(struct Curl_easy *data, time_t milli, expire_id);
+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);
void Curl_update_timer(struct Curl_multi *multi);
@@ -89,11 +89,4 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi,
struct Curl_easy *data,
struct connectdata *conn);
-CURLMcode Curl_multi_wait(struct Curl_multi *multi,
- struct curl_waitfd extra_fds[],
- unsigned int extra_nfds,
- int timeout_ms,
- int *ret,
- bool *gotsocket); /* if any socket was checked */
-
#endif /* HEADER_CURL_MULTIIF_H */
diff --git a/lib/netrc.c b/lib/netrc.c
index a9722df2f..dcc702619 100644
--- a/lib/netrc.c
+++ b/lib/netrc.c
@@ -45,27 +45,27 @@ enum host_lookup_state {
HOSTVALID /* this is "our" machine! */
};
+#define NETRC_FILE_MISSING 1
+#define NETRC_FAILED -1
+#define NETRC_SUCCESS 0
+
/*
- * @unittest: 1304
- *
- * *loginp and *passwordp MUST be allocated if they aren't NULL when passed
- * in.
+ * Returns zero on success.
*/
-int Curl_parsenetrc(const char *host,
- char **loginp,
- char **passwordp,
- bool *login_changed,
- bool *password_changed,
- char *netrcfile)
+static int parsenetrc(const char *host,
+ char **loginp,
+ char **passwordp,
+ bool *login_changed,
+ bool *password_changed,
+ char *netrcfile)
{
FILE *file;
- int retcode = 1;
+ 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;
- bool netrc_alloc = FALSE;
enum host_lookup_state state = NOTHING;
char state_login = 0; /* Found a login keyword */
@@ -73,51 +73,9 @@ int Curl_parsenetrc(const char *host,
int state_our_login = FALSE; /* With specific_login, found *our* login
name */
-#define NETRC DOT_CHAR "netrc"
-
- if(!netrcfile) {
- bool home_alloc = FALSE;
- char *home = curl_getenv("HOME"); /* portable environment reader */
- if(home) {
- home_alloc = TRUE;
-#if defined(HAVE_GETPWUID_R) && defined(HAVE_GETEUID)
- }
- else {
- struct passwd pw, *pw_res;
- char pwbuf[1024];
- if(!getpwuid_r(geteuid(), &pw, pwbuf, sizeof(pwbuf), &pw_res)
- && pw_res) {
- home = strdup(pw.pw_dir);
- if(!home)
- return -1;
- home_alloc = TRUE;
- }
-#elif defined(HAVE_GETPWUID) && defined(HAVE_GETEUID)
- }
- else {
- struct passwd *pw;
- pw = getpwuid(geteuid());
- if(pw) {
- home = pw->pw_dir;
- }
-#endif
- }
-
- if(!home)
- return retcode; /* no home directory found (or possibly out of memory) */
-
- netrcfile = curl_maprintf("%s%s%s", home, DIR_CHAR, NETRC);
- if(home_alloc)
- free(home);
- if(!netrcfile) {
- return -1;
- }
- netrc_alloc = TRUE;
- }
+ DEBUGASSERT(netrcfile);
file = fopen(netrcfile, FOPEN_READTEXT);
- if(netrc_alloc)
- free(netrcfile);
if(file) {
char *tok;
char *tok_buf;
@@ -148,14 +106,14 @@ int Curl_parsenetrc(const char *host,
}
else if(strcasecompare("default", tok)) {
state = HOSTVALID;
- retcode = 0; /* we did find our host */
+ retcode = NETRC_SUCCESS; /* we did find our host */
}
break;
case HOSTFOUND:
if(strcasecompare(host, tok)) {
/* and yes, this is our host! */
state = HOSTVALID;
- retcode = 0; /* we did find our host */
+ retcode = NETRC_SUCCESS; /* we did find our host */
}
else
/* not our host */
@@ -174,7 +132,7 @@ int Curl_parsenetrc(const char *host,
}
login = strdup(tok);
if(!login) {
- retcode = -1; /* allocation failed */
+ retcode = NETRC_FAILED; /* allocation failed */
goto out;
}
login_alloc = TRUE;
@@ -190,7 +148,7 @@ int Curl_parsenetrc(const char *host,
}
password = strdup(tok);
if(!password) {
- retcode = -1; /* allocation failed */
+ retcode = NETRC_FAILED; /* allocation failed */
goto out;
}
password_alloc = TRUE;
@@ -215,6 +173,7 @@ int Curl_parsenetrc(const char *host,
out:
if(!retcode) {
+ /* success */
*login_changed = FALSE;
*password_changed = FALSE;
if(login_alloc) {
@@ -242,4 +201,78 @@ int Curl_parsenetrc(const char *host,
return retcode;
}
+/*
+ * @unittest: 1304
+ *
+ * *loginp and *passwordp MUST be allocated if they aren't NULL when passed
+ * in.
+ */
+int Curl_parsenetrc(const char *host,
+ char **loginp,
+ char **passwordp,
+ bool *login_changed,
+ bool *password_changed,
+ char *netrcfile)
+{
+ int retcode = 1;
+ char *filealloc = NULL;
+
+ if(!netrcfile) {
+ 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;
+ char pwbuf[1024];
+ 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;
+ }
+#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, login_changed,
+ password_changed, 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, login_changed,
+ password_changed, filealloc);
+ free(filealloc);
+ }
+#endif
+ free(homea);
+ }
+ else
+ retcode = parsenetrc(host, loginp, passwordp, login_changed,
+ password_changed, netrcfile);
+ return retcode;
+}
+
#endif
diff --git a/lib/openldap.c b/lib/openldap.c
index cf95f1afa..2b2ecb235 100644
--- a/lib/openldap.c
+++ b/lib/openldap.c
@@ -151,7 +151,7 @@ static const char *url_errs[] = {
"bad or missing extensions"
};
-typedef struct ldapconninfo {
+struct ldapconninfo {
LDAP *ld;
Curl_recv *recv; /* for stacking SSL handler */
Curl_send *send;
@@ -160,7 +160,7 @@ typedef struct ldapconninfo {
bool ssldone;
bool sslinst;
bool didbind;
-} ldapconninfo;
+};
typedef struct ldapreqinfo {
int msgid;
@@ -169,7 +169,7 @@ typedef struct ldapreqinfo {
static CURLcode ldap_setup_connection(struct connectdata *conn)
{
- ldapconninfo *li;
+ struct ldapconninfo *li;
LDAPURLDesc *lud;
struct Curl_easy *data = conn->data;
int rc, proto;
@@ -190,11 +190,11 @@ static CURLcode ldap_setup_connection(struct connectdata *conn)
proto = ldap_pvt_url_scheme2proto(lud->lud_scheme);
ldap_free_urldesc(lud);
- li = calloc(1, sizeof(ldapconninfo));
+ li = calloc(1, sizeof(struct ldapconninfo));
if(!li)
return CURLE_OUT_OF_MEMORY;
li->proto = proto;
- conn->proto.generic = li;
+ conn->proto.ldapc = li;
connkeep(conn, "OpenLDAP default");
return CURLE_OK;
}
@@ -205,7 +205,7 @@ static Sockbuf_IO ldapsb_tls;
static CURLcode ldap_connect(struct connectdata *conn, bool *done)
{
- ldapconninfo *li = conn->proto.generic;
+ struct ldapconninfo *li = conn->proto.ldapc;
struct Curl_easy *data = conn->data;
int rc, proto = LDAP_VERSION3;
char hosturl[1024];
@@ -252,7 +252,7 @@ static CURLcode ldap_connect(struct connectdata *conn, bool *done)
static CURLcode ldap_connecting(struct connectdata *conn, bool *done)
{
- ldapconninfo *li = conn->proto.generic;
+ struct ldapconninfo *li = conn->proto.ldapc;
struct Curl_easy *data = conn->data;
LDAPMessage *msg = NULL;
struct timeval tv = {0, 1}, *tvp;
@@ -357,7 +357,7 @@ static CURLcode ldap_connecting(struct connectdata *conn, bool *done)
static CURLcode ldap_disconnect(struct connectdata *conn, bool dead_connection)
{
- ldapconninfo *li = conn->proto.generic;
+ struct ldapconninfo *li = conn->proto.ldapc;
(void) dead_connection;
if(li) {
@@ -365,7 +365,7 @@ static CURLcode ldap_disconnect(struct connectdata *conn, bool dead_connection)
ldap_unbind_ext(li->ld, NULL, NULL);
li->ld = NULL;
}
- conn->proto.generic = NULL;
+ conn->proto.ldapc = NULL;
free(li);
}
return CURLE_OK;
@@ -373,7 +373,7 @@ static CURLcode ldap_disconnect(struct connectdata *conn, bool dead_connection)
static CURLcode ldap_do(struct connectdata *conn, bool *done)
{
- ldapconninfo *li = conn->proto.generic;
+ struct ldapconninfo *li = conn->proto.ldapc;
ldapreqinfo *lr;
CURLcode status = CURLE_OK;
int rc = 0;
@@ -427,7 +427,7 @@ static CURLcode ldap_done(struct connectdata *conn, CURLcode res,
if(lr) {
/* if there was a search in progress, abandon it */
if(lr->msgid) {
- ldapconninfo *li = conn->proto.generic;
+ struct ldapconninfo *li = conn->proto.ldapc;
ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL);
lr->msgid = 0;
}
@@ -441,7 +441,7 @@ static CURLcode ldap_done(struct connectdata *conn, CURLcode res,
static ssize_t ldap_recv(struct connectdata *conn, int sockindex, char *buf,
size_t len, CURLcode *err)
{
- ldapconninfo *li = conn->proto.generic;
+ struct ldapconninfo *li = conn->proto.ldapc;
struct Curl_easy *data = conn->data;
ldapreqinfo *lr = data->req.protop;
int rc, ret;
@@ -718,7 +718,7 @@ static ber_slen_t
ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
{
struct connectdata *conn = sbiod->sbiod_pvt;
- ldapconninfo *li = conn->proto.generic;
+ struct ldapconninfo *li = conn->proto.ldapc;
ber_slen_t ret;
CURLcode err = CURLE_RECV_ERROR;
@@ -733,7 +733,7 @@ static ber_slen_t
ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
{
struct connectdata *conn = sbiod->sbiod_pvt;
- ldapconninfo *li = conn->proto.generic;
+ struct ldapconninfo *li = conn->proto.ldapc;
ber_slen_t ret;
CURLcode err = CURLE_SEND_ERROR;
diff --git a/lib/pingpong.c b/lib/pingpong.c
index e9568ee3d..d0710053b 100644
--- a/lib/pingpong.c
+++ b/lib/pingpong.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -60,12 +60,12 @@ time_t Curl_pp_state_timeout(struct pingpong *pp, bool disconnecting)
/* 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 */
+ (time_t)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 */
time_t timeout2_ms = data->set.timeout - /* timeout time */
- Curl_timediff(Curl_now(), conn->now); /* spent time */
+ (time_t)Curl_timediff(Curl_now(), conn->now); /* spent time */
/* pick the lowest number */
timeout_ms = CURLMIN(timeout_ms, timeout2_ms);
@@ -463,14 +463,9 @@ CURLcode Curl_pp_readresp(curl_socket_t sockfd,
}
int Curl_pp_getsock(struct pingpong *pp,
- curl_socket_t *socks,
- int numsocks)
+ curl_socket_t *socks)
{
struct connectdata *conn = pp->conn;
-
- if(!numsocks)
- return GETSOCK_BLANK;
-
socks[0] = conn->sock[FIRSTSOCKET];
if(pp->sendleft) {
diff --git a/lib/pingpong.h b/lib/pingpong.h
index dbe1f8d3d..849a7c0ff 100644
--- a/lib/pingpong.h
+++ b/lib/pingpong.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -135,8 +135,7 @@ CURLcode Curl_pp_flushsend(struct pingpong *pp);
/* call this when a pingpong connection is disconnected */
CURLcode Curl_pp_disconnect(struct pingpong *pp);
-int Curl_pp_getsock(struct pingpong *pp, curl_socket_t *socks,
- int numsocks);
+int Curl_pp_getsock(struct pingpong *pp, curl_socket_t *socks);
/***********************************************************************
diff --git a/lib/pop3.c b/lib/pop3.c
index e06cda1ca..bc1560061 100644
--- a/lib/pop3.c
+++ b/lib/pop3.c
@@ -95,8 +95,7 @@ static CURLcode pop3_done(struct connectdata *conn, CURLcode status,
static CURLcode pop3_connect(struct connectdata *conn, bool *done);
static CURLcode pop3_disconnect(struct connectdata *conn, bool dead);
static CURLcode pop3_multi_statemach(struct connectdata *conn, bool *done);
-static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
- int numsocks);
+static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks);
static CURLcode pop3_doing(struct connectdata *conn, bool *dophase_done);
static CURLcode pop3_setup_connection(struct connectdata *conn);
static CURLcode pop3_parse_url_options(struct connectdata *conn);
@@ -1055,10 +1054,9 @@ static CURLcode pop3_init(struct connectdata *conn)
}
/* For the POP3 "protocol connect" and "doing" phases only */
-static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks,
- int numsocks)
+static int pop3_getsock(struct connectdata *conn, curl_socket_t *socks)
{
- return Curl_pp_getsock(&conn->proto.pop3c.pp, socks, numsocks);
+ return Curl_pp_getsock(&conn->proto.pop3c.pp, socks);
}
/***********************************************************************
diff --git a/lib/progress.c b/lib/progress.c
index 9f953ef5a..2aa929599 100644
--- a/lib/progress.c
+++ b/lib/progress.c
@@ -26,6 +26,7 @@
#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. */
@@ -168,7 +169,7 @@ void Curl_pgrsResetTransferSizes(struct Curl_easy *data)
void Curl_pgrsTime(struct Curl_easy *data, timerid timer)
{
struct curltime now = Curl_now();
- time_t *delta = NULL;
+ timediff_t *delta = NULL;
switch(timer) {
default:
@@ -238,6 +239,8 @@ void Curl_pgrsStartNow(struct Curl_easy *data)
data->progress.ul_limit_start.tv_usec = 0;
data->progress.dl_limit_start.tv_sec = 0;
data->progress.dl_limit_start.tv_usec = 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);
@@ -268,8 +271,8 @@ timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize,
struct curltime now)
{
curl_off_t size = cursize - startsize;
- time_t minimum;
- time_t actual;
+ timediff_t minimum;
+ timediff_t actual;
if(!limit || !size)
return 0;
@@ -282,10 +285,10 @@ timediff_t Curl_pgrsLimitWaitTime(curl_off_t cursize,
minimum = (time_t) (CURL_OFF_T_C(1000) * size / limit);
else {
minimum = (time_t) (size / limit);
- if(minimum < TIME_T_MAX/1000)
+ if(minimum < TIMEDIFF_T_MAX/1000)
minimum *= 1000;
else
- minimum = TIME_T_MAX;
+ minimum = TIMEDIFF_T_MAX;
}
/*
diff --git a/lib/quic.h b/lib/quic.h
new file mode 100644
index 000000000..6c132a324
--- /dev/null
+++ b/lib/quic.h
@@ -0,0 +1,53 @@
+#ifndef HEADER_CURL_QUIC_H
+#define HEADER_CURL_QUIC_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed 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.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef ENABLE_QUIC
+#ifdef USE_NGTCP2
+#include "vquic/ngtcp2.h"
+#endif
+#ifdef USE_QUICHE
+#include "vquic/quiche.h"
+#endif
+
+#include "urldata.h"
+
+/* functions provided by the specific backends */
+CURLcode Curl_quic_connect(struct connectdata *conn,
+ curl_socket_t sockfd,
+ int sockindex,
+ const struct sockaddr *addr,
+ socklen_t addrlen);
+CURLcode Curl_quic_is_connected(struct connectdata *conn,
+ curl_socket_t sockfd,
+ bool *connected);
+int Curl_quic_ver(char *p, size_t len);
+CURLcode Curl_quic_done_sending(struct connectdata *conn);
+
+#else /* ENABLE_QUIC */
+#define Curl_quic_done_sending(x)
+#endif /* !ENABLE_QUIC */
+
+#endif /* HEADER_CURL_QUIC_H */
diff --git a/lib/rtsp.c b/lib/rtsp.c
index abeb61707..ee375456b 100644
--- a/lib/rtsp.c
+++ b/lib/rtsp.c
@@ -52,10 +52,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done);
static CURLcode rtsp_done(struct connectdata *conn, CURLcode, bool premature);
static CURLcode rtsp_connect(struct connectdata *conn, bool *done);
static CURLcode rtsp_disconnect(struct connectdata *conn, bool dead);
-
-static int rtsp_getsock_do(struct connectdata *conn,
- curl_socket_t *socks,
- int numsocks);
+static int rtsp_getsock_do(struct connectdata *conn, curl_socket_t *socks);
/*
* Parse and write out any available RTP data.
@@ -77,11 +74,9 @@ static unsigned int rtsp_conncheck(struct connectdata *check,
interface and then we're always _sending_ a request and thus we wait for
the single socket to become writable only */
static int rtsp_getsock_do(struct connectdata *conn,
- curl_socket_t *socks,
- int numsocks)
+ curl_socket_t *socks)
{
/* write mode */
- (void)numsocks; /* unused, we trust it to be at least 1 */
socks[0] = conn->sock[FIRSTSOCKET];
return GETSOCK_WRITESOCK(0);
}
diff --git a/lib/security.c b/lib/security.c
index 550ea2da8..c5e4e135d 100644
--- a/lib/security.c
+++ b/lib/security.c
@@ -191,7 +191,6 @@ static CURLcode read_data(struct connectdata *conn,
struct krb5buffer *buf)
{
int len;
- void *tmp = NULL;
CURLcode result;
result = socket_read(fd, &len, sizeof(len));
@@ -201,12 +200,11 @@ static CURLcode read_data(struct connectdata *conn,
if(len) {
/* only realloc if there was a length */
len = ntohl(len);
- tmp = Curl_saferealloc(buf->data, len);
+ buf->data = Curl_saferealloc(buf->data, len);
}
- if(tmp == NULL)
+ if(!len || !buf->data)
return CURLE_OUT_OF_MEMORY;
- buf->data = tmp;
result = socket_read(fd, buf->data, len);
if(result)
return result;
diff --git a/lib/select.h b/lib/select.h
index 9a1ba45a7..f5652a74f 100644
--- a/lib/select.h
+++ b/lib/select.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -77,9 +77,9 @@ int Curl_socket_check(curl_socket_t readfd, curl_socket_t readfd2,
time_t timeout_ms);
#define SOCKET_READABLE(x,z) \
- Curl_socket_check(x, CURL_SOCKET_BAD, CURL_SOCKET_BAD, z)
+ Curl_socket_check(x, CURL_SOCKET_BAD, CURL_SOCKET_BAD, (time_t)z)
#define SOCKET_WRITABLE(x,z) \
- Curl_socket_check(CURL_SOCKET_BAD, CURL_SOCKET_BAD, x, z)
+ Curl_socket_check(CURL_SOCKET_BAD, CURL_SOCKET_BAD, x, (time_t)z)
int Curl_poll(struct pollfd ufds[], unsigned int nfds, int timeout_ms);
diff --git a/lib/setopt.c b/lib/setopt.c
index 1dbf00faf..8909035a9 100644
--- a/lib/setopt.c
+++ b/lib/setopt.c
@@ -809,6 +809,11 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
arg = va_arg(param, long);
if(arg < CURL_HTTP_VERSION_NONE)
return CURLE_BAD_FUNCTION_ARGUMENT;
+#ifdef ENABLE_QUIC
+ if(arg == CURL_HTTP_VERSION_3)
+ ;
+ else
+#endif
#ifndef USE_NGHTTP2
if(arg >= CURL_HTTP_VERSION_2)
return CURLE_UNSUPPORTED_PROTOCOL;
@@ -1778,16 +1783,9 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
arg = va_arg(param, long);
/* Obviously people are not reading documentation and too many thought
- this argument took a boolean when it wasn't and misused it. We thus ban
- 1 as a sensible input and we warn about its use. Then we only have the
- 2 action internally stored as TRUE. */
-
- if(1 == arg) {
- failf(data, "CURLOPT_SSL_VERIFYHOST no longer supports 1 as value!");
- return CURLE_BAD_FUNCTION_ARGUMENT;
- }
-
- data->set.ssl.primary.verifyhost = (0 != arg) ? TRUE : FALSE;
+ this argument took a boolean when it wasn't and misused it.
+ Treat 1 and 2 the same */
+ data->set.ssl.primary.verifyhost = (bool)((arg & 3) ? TRUE : FALSE);
/* Update the current connection ssl_config. */
if(data->conn) {
@@ -1802,17 +1800,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
*/
arg = va_arg(param, long);
- /* Obviously people are not reading documentation and too many thought
- this argument took a boolean when it wasn't and misused it. We thus ban
- 1 as a sensible input and we warn about its use. Then we only have the
- 2 action internally stored as TRUE. */
-
- if(1 == arg) {
- failf(data, "CURLOPT_SSL_VERIFYHOST no longer supports 1 as value!");
- return CURLE_BAD_FUNCTION_ARGUMENT;
- }
-
- data->set.proxy_ssl.primary.verifyhost = (0 != arg)?TRUE:FALSE;
+ /* 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. */
if(data->conn) {
@@ -2402,6 +2391,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
break;
#endif
+ case CURLOPT_SASL_AUTHZID:
+ /* Authorisation 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)) ? TRUE : FALSE;
diff --git a/lib/smb.c b/lib/smb.c
index 870244cb7..f66c05ca4 100644
--- a/lib/smb.c
+++ b/lib/smb.c
@@ -6,7 +6,7 @@
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2014, Bill Nagel <wnagel@tycoint.com>, Exacq Technologies
- * Copyright (C) 2016-2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 2016-2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -64,8 +64,7 @@ static CURLcode smb_request_state(struct connectdata *conn, bool *done);
static CURLcode smb_done(struct connectdata *conn, CURLcode status,
bool premature);
static CURLcode smb_disconnect(struct connectdata *conn, bool dead);
-static int smb_getsock(struct connectdata *conn, curl_socket_t *socks,
- int numsocks);
+static int smb_getsock(struct connectdata *conn, curl_socket_t *socks);
static CURLcode smb_parse_url_path(struct connectdata *conn);
/*
@@ -607,6 +606,7 @@ static CURLcode smb_send_and_recv(struct connectdata *conn, void **msg)
{
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) {
@@ -936,12 +936,8 @@ static CURLcode smb_disconnect(struct connectdata *conn, bool dead)
return CURLE_OK;
}
-static int smb_getsock(struct connectdata *conn, curl_socket_t *socks,
- int numsocks)
+static int smb_getsock(struct connectdata *conn, curl_socket_t *socks)
{
- if(!numsocks)
- return GETSOCK_BLANK;
-
socks[0] = conn->sock[FIRSTSOCKET];
return GETSOCK_READSOCK(0) | GETSOCK_WRITESOCK(0);
}
diff --git a/lib/smtp.c b/lib/smtp.c
index 8ef5a04be..9d3cfbf49 100644
--- a/lib/smtp.c
+++ b/lib/smtp.c
@@ -95,8 +95,7 @@ static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
static CURLcode smtp_connect(struct connectdata *conn, bool *done);
static CURLcode smtp_disconnect(struct connectdata *conn, bool dead);
static CURLcode smtp_multi_statemach(struct connectdata *conn, bool *done);
-static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
- int numsocks);
+static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks);
static CURLcode smtp_doing(struct connectdata *conn, bool *dophase_done);
static CURLcode smtp_setup_connection(struct connectdata *conn);
static CURLcode smtp_parse_url_options(struct connectdata *conn);
@@ -715,7 +714,7 @@ static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
result = CURLE_REMOTE_ACCESS_DENIED;
}
}
- else {
+ else if(len >= 4) {
line += 4;
len -= 4;
@@ -786,6 +785,10 @@ static CURLcode smtp_state_ehlo_resp(struct connectdata *conn, int smtpcode,
result = smtp_perform_authentication(conn);
}
}
+ else {
+ failf(data, "Unexpectedly short EHLO response");
+ result = CURLE_WEIRD_SERVER_REPLY;
+ }
return result;
}
@@ -1114,10 +1117,9 @@ static CURLcode smtp_init(struct connectdata *conn)
}
/* For the SMTP "protocol connect" and "doing" phases only */
-static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks,
- int numsocks)
+static int smtp_getsock(struct connectdata *conn, curl_socket_t *socks)
{
- return Curl_pp_getsock(&conn->proto.smtpc.pp, socks, numsocks);
+ return Curl_pp_getsock(&conn->proto.smtpc.pp, socks);
}
/***********************************************************************
diff --git a/lib/ssh.h b/lib/ssh.h
index 0620aac32..3213c5a52 100644
--- a/lib/ssh.h
+++ b/lib/ssh.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -239,7 +239,16 @@ extern const struct Curl_handler Curl_handler_sftp;
extern const struct Curl_handler Curl_handler_scp;
extern const struct Curl_handler Curl_handler_sftp;
-
#endif /* USE_LIBSSH2 */
+#ifdef USE_SSH
+/* generic SSH backend functions */
+CURLcode Curl_ssh_init(void);
+void Curl_ssh_cleanup(void);
+size_t Curl_ssh_version(char *buffer, size_t buflen);
+#else
+/* for non-SSH builds */
+#define Curl_ssh_cleanup()
+#endif
+
#endif /* HEADER_CURL_SSH_H */
diff --git a/lib/strerror.c b/lib/strerror.c
index bf027fc0c..e3be16397 100644
--- a/lib/strerror.c
+++ b/lib/strerror.c
@@ -311,6 +311,9 @@ curl_easy_strerror(CURLcode error)
case CURLE_RECURSIVE_API_CALL:
return "API function called from within callback";
+ case CURLE_AUTH_ERROR:
+ return "An authentication function returned an error";
+
/* error codes not used by current libcurl */
case CURLE_OBSOLETE20:
case CURLE_OBSOLETE24:
diff --git a/lib/tftp.c b/lib/tftp.c
index 4713332d2..9b6ba72d2 100644
--- a/lib/tftp.c
+++ b/lib/tftp.c
@@ -157,8 +157,7 @@ static CURLcode tftp_done(struct connectdata *conn,
static CURLcode tftp_setup_connection(struct connectdata * conn);
static CURLcode tftp_multi_statemach(struct connectdata *conn, bool *done);
static CURLcode tftp_doing(struct connectdata *conn, bool *dophase_done);
-static int tftp_getsock(struct connectdata *conn, curl_socket_t *socks,
- int numsocks);
+static int tftp_getsock(struct connectdata *conn, curl_socket_t *socks);
static CURLcode tftp_translate_code(tftp_error_t error);
@@ -404,13 +403,14 @@ static CURLcode tftp_parse_option_ack(tftp_state_data_t *state,
return CURLE_OK;
}
-static size_t tftp_option_add(tftp_state_data_t *state, size_t csize,
- char *buf, const char *option)
+static CURLcode tftp_option_add(tftp_state_data_t *state, size_t *csize,
+ char *buf, const char *option)
{
- if(( strlen(option) + csize + 1) > (size_t)state->blksize)
- return 0;
+ if(( strlen(option) + *csize + 1) > (size_t)state->blksize)
+ return CURLE_TFTP_ILLEGAL;
strcpy(buf, option);
- return strlen(option) + 1;
+ *csize += strlen(option) + 1;
+ return CURLE_OK;
}
static CURLcode tftp_connect_for_tx(tftp_state_data_t *state,
@@ -511,26 +511,38 @@ static CURLcode tftp_send_first(tftp_state_data_t *state, tftp_event_t event)
else
strcpy(buf, "0"); /* the destination is large enough */
- sbytes += tftp_option_add(state, sbytes,
- (char *)state->spacket.data + sbytes,
- TFTP_OPTION_TSIZE);
- sbytes += tftp_option_add(state, sbytes,
- (char *)state->spacket.data + sbytes, buf);
+ 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);
- sbytes += tftp_option_add(state, sbytes,
- (char *)state->spacket.data + sbytes,
- TFTP_OPTION_BLKSIZE);
- sbytes += tftp_option_add(state, sbytes,
- (char *)state->spacket.data + sbytes, buf);
+ 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);
- sbytes += tftp_option_add(state, sbytes,
- (char *)state->spacket.data + sbytes,
- TFTP_OPTION_INTERVAL);
- sbytes += tftp_option_add(state, sbytes,
- (char *)state->spacket.data + sbytes, buf);
+ 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
@@ -973,6 +985,7 @@ static CURLcode tftp_connect(struct connectdata *conn, bool *done)
{
tftp_state_data_t *state;
int blksize;
+ int need_blksize;
blksize = TFTP_BLKSIZE_DEFAULT;
@@ -987,15 +1000,20 @@ static CURLcode tftp_connect(struct connectdata *conn, bool *done)
return CURLE_TFTP_ILLEGAL;
}
+ 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, blksize + 2 + 2);
+ 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, blksize + 2 + 2);
+ state->spacket.data = calloc(1, need_blksize + 2 + 2);
if(!state->spacket.data)
return CURLE_OUT_OF_MEMORY;
@@ -1009,7 +1027,7 @@ static CURLcode tftp_connect(struct connectdata *conn, bool *done)
state->sockfd = state->conn->sock[FIRSTSOCKET];
state->state = TFTP_STATE_START;
state->error = TFTP_ERR_NONE;
- state->blksize = blksize;
+ state->blksize = TFTP_BLKSIZE_DEFAULT; /* Unless updated by OACK response */
state->requested_blksize = blksize;
((struct sockaddr *)&state->local_addr)->sa_family =
@@ -1082,14 +1100,9 @@ static CURLcode tftp_done(struct connectdata *conn, CURLcode status,
* The getsock callback
*
**********************************************************/
-static int tftp_getsock(struct connectdata *conn, curl_socket_t *socks,
- int numsocks)
+static int tftp_getsock(struct connectdata *conn, curl_socket_t *socks)
{
- if(!numsocks)
- return GETSOCK_BLANK;
-
socks[0] = conn->sock[FIRSTSOCKET];
-
return GETSOCK_READSOCK(0);
}
@@ -1376,7 +1389,7 @@ static CURLcode tftp_setup_connection(struct connectdata * conn)
struct Curl_easy *data = conn->data;
char *type;
- conn->socktype = SOCK_DGRAM; /* UDP datagram based */
+ conn->transport = TRNSPRT_UDP;
/* TFTP URLs support an extension like ";mode=<typecode>" that
* we'll try to get now! */
diff --git a/lib/timeval.c b/lib/timeval.c
index e2bd7fd14..9b05cf051 100644
--- a/lib/timeval.c
+++ b/lib/timeval.c
@@ -174,14 +174,6 @@ struct curltime Curl_now(void)
#endif
-#if SIZEOF_TIME_T < 8
-#define TIME_MAX INT_MAX
-#define TIME_MIN INT_MIN
-#else
-#define TIME_MAX 9223372036854775807LL
-#define TIME_MIN -9223372036854775807LL
-#endif
-
/*
* Returns: time difference in number of milliseconds. For too large diffs it
* returns max value.
@@ -191,10 +183,10 @@ struct curltime Curl_now(void)
timediff_t Curl_timediff(struct curltime newer, struct curltime older)
{
timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec;
- if(diff >= (TIME_MAX/1000))
- return TIME_MAX;
- else if(diff <= (TIME_MIN/1000))
- return TIME_MIN;
+ 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;
}
@@ -205,9 +197,9 @@ timediff_t Curl_timediff(struct curltime newer, struct curltime older)
timediff_t Curl_timediff_us(struct curltime newer, struct curltime older)
{
timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec;
- if(diff >= (TIME_MAX/1000000))
- return TIME_MAX;
- else if(diff <= (TIME_MIN/1000000))
- return TIME_MIN;
+ 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/lib/timeval.h b/lib/timeval.h
index 96867d713..53e063607 100644
--- a/lib/timeval.h
+++ b/lib/timeval.h
@@ -24,13 +24,13 @@
#include "curl_setup.h"
-#if SIZEOF_TIME_T < 8
-typedef int timediff_t;
-#define CURL_FORMAT_TIMEDIFF_T "d"
-#else
+/* 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
-#endif
+
+#define TIMEDIFF_T_MAX CURL_OFF_T_MAX
+#define TIMEDIFF_T_MIN CURL_OFF_T_MIN
struct curltime {
time_t tv_sec; /* seconds */
diff --git a/lib/transfer.c b/lib/transfer.c
index e10e6c9f9..e5e74711d 100644
--- a/lib/transfer.c
+++ b/lib/transfer.c
@@ -176,7 +176,7 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes,
#ifndef CURL_DISABLE_HTTP
if(data->state.trailers_state == TRAILERS_INITIALIZED) {
struct curl_slist *trailers = NULL;
- CURLcode c;
+ CURLcode result;
int trailers_ret_code;
/* at this point we already verified that the callback exists
@@ -195,17 +195,18 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes,
data->set.trailer_data);
Curl_set_in_callback(data, false);
if(trailers_ret_code == CURL_TRAILERFUNC_OK) {
- c = Curl_http_compile_trailers(trailers, data->state.trailers_buf, data);
+ result = Curl_http_compile_trailers(trailers, &data->state.trailers_buf,
+ data);
}
else {
failf(data, "operation aborted by trailing headers callback");
*nreadp = 0;
- c = CURLE_ABORTED_BY_CALLBACK;
+ result = CURLE_ABORTED_BY_CALLBACK;
}
- if(c != CURLE_OK) {
+ if(result) {
Curl_add_buffer_free(&data->state.trailers_buf);
curl_slist_free_all(trailers);
- return c;
+ return result;
}
infof(data, "Successfully compiled trailers.\r\n");
curl_slist_free_all(trailers);
@@ -497,7 +498,7 @@ static int data_pending(const struct connectdata *conn)
TRUE. The thing is if we read everything, then http2_recv won't
be called and we cannot signal the HTTP/2 stream has closed. As
a workaround, we return nonzero here to call http2_recv. */
- ((conn->handler->protocol&PROTO_FAMILY_HTTP) && conn->httpversion == 20);
+ ((conn->handler->protocol&PROTO_FAMILY_HTTP) && conn->httpversion >= 20);
#else
Curl_ssl_data_pending(conn, FIRSTSOCKET);
#endif
@@ -601,7 +602,7 @@ static CURLcode readwrite_data(struct Curl_easy *data,
nread = 0;
}
- if((k->bytecount == 0) && (k->writebytecount == 0)) {
+ if(!k->bytecount) {
Curl_pgrsTime(data, TIMER_STARTTRANSFER);
if(k->exp100 > EXP100_SEND_DATA)
/* set time stamp to compare with when waiting for the 100 */
@@ -942,7 +943,9 @@ CURLcode Curl_done_sending(struct connectdata *conn,
{
k->keepon &= ~KEEP_SEND; /* we're done writing */
+ /* These functions should be moved into the handler struct! */
Curl_http2_done_sending(conn);
+ Curl_quic_done_sending(conn);
if(conn->bits.rewindaftersend) {
CURLcode result = Curl_readrewind(conn);
@@ -1354,20 +1357,14 @@ CURLcode Curl_readwrite(struct connectdata *conn,
* in the proper state to have this information available.
*/
int Curl_single_getsock(const struct connectdata *conn,
- curl_socket_t *sock, /* points to numsocks number
- of sockets */
- int numsocks)
+ curl_socket_t *sock)
{
const struct Curl_easy *data = conn->data;
int bitmap = GETSOCK_BLANK;
unsigned sockindex = 0;
if(conn->handler->perform_getsock)
- return conn->handler->perform_getsock(conn, sock, numsocks);
-
- if(numsocks < 2)
- /* simple check but we might need two slots */
- return GETSOCK_BLANK;
+ return conn->handler->perform_getsock(conn, sock);
/* don't include HOLD and PAUSE connections */
if((data->req.keepon & KEEP_RECVBITS) == KEEP_RECV) {
diff --git a/lib/transfer.h b/lib/transfer.h
index da36f19f8..67fd91f25 100644
--- a/lib/transfer.h
+++ b/lib/transfer.h
@@ -47,8 +47,7 @@ CURLcode Curl_readwrite(struct connectdata *conn,
struct Curl_easy *data, bool *done,
bool *comeback);
int Curl_single_getsock(const struct connectdata *conn,
- curl_socket_t *socks,
- int numsocks);
+ curl_socket_t *socks);
CURLcode Curl_readrewind(struct connectdata *conn);
CURLcode Curl_fillreadbuffer(struct connectdata *conn, size_t bytes,
size_t *nreadp);
diff --git a/lib/url.c b/lib/url.c
index 2b47b235d..b7cf7bedd 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -112,7 +112,6 @@ bool curl_win32_idn_to_ascii(const char *in, char **out);
#include "connect.h"
#include "inet_ntop.h"
#include "http_ntlm.h"
-#include "socks.h"
#include "curl_rtmp.h"
#include "gopher.h"
#include "http_proxy.h"
@@ -546,7 +545,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
set->upkeep_interval_ms = CURL_UPKEEP_INTERVAL_DEFAULT;
set->maxconnects = DEFAULT_CONNCACHE_SIZE; /* for easy handles */
set->maxage_conn = 118;
- set->http09_allowed = TRUE;
+ set->http09_allowed = FALSE;
set->httpversion =
#ifdef USE_NGHTTP2
CURL_HTTP_VERSION_2TLS
@@ -715,6 +714,7 @@ static void conn_free(struct connectdata *conn)
Curl_safefree(conn->user);
Curl_safefree(conn->passwd);
Curl_safefree(conn->oauth_bearer);
+ Curl_safefree(conn->sasl_authzid);
Curl_safefree(conn->options);
Curl_safefree(conn->http_proxy.user);
Curl_safefree(conn->socks_proxy.user);
@@ -972,7 +972,8 @@ static int call_extract_if_dead(struct connectdata *conn, void *param)
static void prune_dead_connections(struct Curl_easy *data)
{
struct curltime now = Curl_now();
- time_t elapsed = Curl_timediff(now, data->state.conn_cache->last_cleanup);
+ timediff_t elapsed =
+ Curl_timediff(now, data->state.conn_cache->last_cleanup);
if(elapsed >= 1000L) {
struct prunedead prune;
@@ -1371,58 +1372,6 @@ ConnectionExists(struct Curl_easy *data,
return FALSE; /* no matching connecting exists */
}
-/* after a TCP connection to the proxy has been verified, this function does
- the next magic step.
-
- Note: this function's sub-functions call failf()
-
-*/
-CURLcode Curl_connected_proxy(struct connectdata *conn, int sockindex)
-{
- CURLcode result = CURLE_OK;
-
- if(conn->bits.socksproxy) {
-#ifndef CURL_DISABLE_PROXY
- /* for the secondary socket (FTP), use the "connect to host"
- * but ignore the "connect to port" (use the secondary port)
- */
- const char * const host = conn->bits.httpproxy ?
- conn->http_proxy.host.name :
- conn->bits.conn_to_host ?
- conn->conn_to_host.name :
- sockindex == SECONDARYSOCKET ?
- conn->secondaryhostname : conn->host.name;
- const int port = conn->bits.httpproxy ? (int)conn->http_proxy.port :
- sockindex == SECONDARYSOCKET ? conn->secondary_port :
- conn->bits.conn_to_port ? conn->conn_to_port :
- conn->remote_port;
- conn->bits.socksproxy_connecting = TRUE;
- switch(conn->socks_proxy.proxytype) {
- case CURLPROXY_SOCKS5:
- case CURLPROXY_SOCKS5_HOSTNAME:
- result = Curl_SOCKS5(conn->socks_proxy.user, conn->socks_proxy.passwd,
- host, port, sockindex, conn);
- break;
-
- case CURLPROXY_SOCKS4:
- case CURLPROXY_SOCKS4A:
- result = Curl_SOCKS4(conn->socks_proxy.user, host, port, sockindex,
- conn);
- break;
-
- default:
- failf(conn->data, "unknown proxytype option given");
- result = CURLE_COULDNT_CONNECT;
- } /* switch proxytype */
- conn->bits.socksproxy_connecting = FALSE;
-#else
- (void)sockindex;
-#endif /* CURL_DISABLE_PROXY */
- }
-
- return result;
-}
-
/*
* verboseconnect() displays verbose information after a connect
*/
@@ -1439,127 +1388,6 @@ void Curl_verboseconnect(struct connectdata *conn)
}
#endif
-int Curl_protocol_getsock(struct connectdata *conn,
- curl_socket_t *socks,
- int numsocks)
-{
- if(conn->handler->proto_getsock)
- return conn->handler->proto_getsock(conn, socks, numsocks);
- /* Backup getsock logic. Since there is a live socket in use, we must wait
- for it or it will be removed from watching when the multi_socket API is
- used. */
- socks[0] = conn->sock[FIRSTSOCKET];
- return GETSOCK_READSOCK(0) | GETSOCK_WRITESOCK(0);
-}
-
-int Curl_doing_getsock(struct connectdata *conn,
- curl_socket_t *socks,
- int numsocks)
-{
- if(conn && conn->handler->doing_getsock)
- return conn->handler->doing_getsock(conn, socks, numsocks);
- return GETSOCK_BLANK;
-}
-
-/*
- * 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.
- */
-
-CURLcode Curl_protocol_connecting(struct connectdata *conn,
- bool *done)
-{
- CURLcode result = CURLE_OK;
-
- if(conn && conn->handler->connecting) {
- *done = FALSE;
- result = conn->handler->connecting(conn, 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.
- */
-
-CURLcode Curl_protocol_doing(struct connectdata *conn, bool *done)
-{
- CURLcode result = CURLE_OK;
-
- if(conn && conn->handler->doing) {
- *done = FALSE;
- result = conn->handler->doing(conn, done);
- }
- else
- *done = TRUE;
-
- return result;
-}
-
-/*
- * We have discovered that the TCP connection has been successful, we can now
- * proceed with some action.
- *
- */
-CURLcode Curl_protocol_connect(struct connectdata *conn,
- bool *protocol_done)
-{
- CURLcode result = CURLE_OK;
-
- *protocol_done = FALSE;
-
- if(conn->bits.tcpconnect[FIRSTSOCKET] && conn->bits.protoconnstart) {
- /* We already are connected, get back. This may happen when the connect
- worked fine in the first call, like when we connect to a local server
- or proxy. Note that we don't know if the protocol is actually done.
-
- Unless this protocol doesn't have any protocol-connect callback, as
- then we know we're done. */
- if(!conn->handler->connecting)
- *protocol_done = TRUE;
-
- return CURLE_OK;
- }
-
- if(!conn->bits.protoconnstart) {
-
- result = Curl_proxy_connect(conn, FIRSTSOCKET);
- if(result)
- return result;
-
- if(CONNECT_FIRSTSOCKET_PROXY_SSL())
- /* wait for HTTPS proxy SSL initialization to complete */
- return CURLE_OK;
-
- if(conn->bits.tunnel_proxy && conn->bits.httpproxy &&
- Curl_connect_ongoing(conn))
- /* when using an HTTP tunnel proxy, await complete tunnel establishment
- before proceeding further. Return CURLE_OK so we'll be called again */
- return CURLE_OK;
-
- if(conn->handler->connect_it) {
- /* is there a protocol-specific connect() procedure? */
-
- /* Call the protocol-specific connect function */
- result = conn->handler->connect_it(conn, protocol_done);
- }
- else
- *protocol_done = TRUE;
-
- /* it has started, possibly even completed but that knowledge isn't stored
- in this bit! */
- if(!result)
- conn->bits.protoconnstart = TRUE;
- }
-
- return result; /* pass back status */
-}
-
/*
* Helpers for IDNA conversions.
*/
@@ -1774,6 +1602,7 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
conn->proxy_ssl_config.verifyhost = data->set.proxy_ssl.primary.verifyhost;
conn->ip_version = data->set.ipver;
conn->bits.connect_only = data->set.connect_only;
+ conn->transport = TRNSPRT_TCP; /* most of them are TCP streams */
#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \
defined(NTLM_WB_ENABLED)
@@ -2110,7 +1939,6 @@ static CURLcode setup_connection_internals(struct connectdata *conn)
{
const struct Curl_handler * p;
CURLcode result;
- conn->socktype = SOCK_STREAM; /* most of them are TCP streams */
/* Perform setup complement if some. */
p = conn->handler;
@@ -2860,8 +2688,7 @@ static CURLcode override_login(struct Curl_easy *data,
&netrc_user_changed, &netrc_passwd_changed,
data->set.str[STRING_NETRC_FILE]);
if(ret > 0) {
- infof(data, "Couldn't find host %s in the "
- DOT_CHAR "netrc file; using defaults\n",
+ infof(data, "Couldn't find host %s in the .netrc file; using defaults\n",
conn->host.name);
}
else if(ret < 0) {
@@ -3158,26 +2985,65 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data,
if(data->asi && !host && (port == -1) &&
(conn->handler->protocol == CURLPROTO_HTTPS)) {
/* no connect_to match, try alt-svc! */
- const char *nhost;
- int nport;
- enum alpnid nalpnid;
+ enum alpnid srcalpnid;
bool hit;
+ struct altsvc *as;
+ const int allowed_versions = ( ALPN_h1
+#ifdef USE_NGHTTP2
+ | ALPN_h2
+#endif
+#ifdef ENABLE_QUIC
+ | ALPN_h3
+#endif
+ ) & data->asi->flags;
+
host = conn->host.rawalloc;
+#ifdef USE_NGHTTP2
+ /* with h2 support, check that first */
+ srcalpnid = ALPN_h2;
hit = Curl_altsvc_lookup(data->asi,
- ALPN_h1, host, conn->remote_port, /* from */
- &nalpnid, &nhost, &nport /* to */);
+ 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 *)nhost);
+ 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 = nport;
+ 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\n",
- Curl_alpnid2str(ALPN_h1), host, conn->remote_port,
- Curl_alpnid2str(nalpnid), hostd, nport);
+ 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: /* shouldn't be possible */
+ break;
+ }
+ }
}
}
#endif
@@ -3464,6 +3330,14 @@ static CURLcode create_conn(struct Curl_easy *data,
}
}
+ 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;
+ }
+ }
+
#ifdef USE_UNIX_SOCKETS
if(data->set.str[STRING_UNIX_SOCKET_PATH]) {
conn->unix_domain_socket = strdup(data->set.str[STRING_UNIX_SOCKET_PATH]);
@@ -4162,34 +4036,3 @@ static unsigned int get_protocol_family(unsigned int protocol)
return family;
}
-
-
-/*
- * Wrapper to call functions in Curl_conncache_foreach()
- *
- * Returns always 0.
- */
-static int conn_upkeep(struct connectdata *conn,
- void *param)
-{
- /* Param is unused. */
- (void)param;
-
- if(conn->handler->connection_check) {
- /* Do a protocol-specific keepalive check on the connection. */
- conn->handler->connection_check(conn, CONNCHECK_KEEPALIVE);
- }
-
- return 0; /* continue iteration */
-}
-
-CURLcode Curl_upkeep(struct conncache *conn_cache,
- void *data)
-{
- /* Loop over every connection and make connection alive. */
- Curl_conncache_foreach(data,
- conn_cache,
- data,
- conn_upkeep);
- return CURLE_OK;
-}
diff --git a/lib/url.h b/lib/url.h
index 7c87432c9..f4d611add 100644
--- a/lib/url.h
+++ b/lib/url.h
@@ -53,23 +53,12 @@ CURLcode Curl_close(struct Curl_easy *data); /* opposite of curl_open() */
CURLcode Curl_connect(struct Curl_easy *, bool *async, bool *protocol_connect);
CURLcode Curl_disconnect(struct Curl_easy *data,
struct connectdata *, bool dead_connection);
-CURLcode Curl_protocol_connect(struct connectdata *conn, bool *done);
-CURLcode Curl_protocol_connecting(struct connectdata *conn, bool *done);
-CURLcode Curl_protocol_doing(struct connectdata *conn, bool *done);
CURLcode Curl_setup_conn(struct connectdata *conn,
bool *protocol_done);
void Curl_free_request_state(struct Curl_easy *data);
-
-int Curl_protocol_getsock(struct connectdata *conn,
- curl_socket_t *socks,
- int numsocks);
-int Curl_doing_getsock(struct connectdata *conn,
- curl_socket_t *socks,
- int numsocks);
CURLcode Curl_parse_login_details(const char *login, const size_t len,
char **userptr, char **passwdptr,
char **optionsptr);
-CURLcode Curl_upkeep(struct conncache *conn_cache, void *data);
const struct Curl_handler *Curl_builtin_scheme(const char *scheme);
@@ -77,8 +66,6 @@ const struct Curl_handler *Curl_builtin_scheme(const char *scheme);
#define CURL_DEFAULT_HTTPS_PROXY_PORT 443 /* default https proxy port unless
specified */
-CURLcode Curl_connected_proxy(struct connectdata *conn, int sockindex);
-
#ifdef CURL_DISABLE_VERBOSE_STRINGS
#define Curl_verboseconnect(x) Curl_nop_stmt
#else
diff --git a/lib/urlapi.c b/lib/urlapi.c
index d07e4f5df..a0ee331da 100644
--- a/lib/urlapi.c
+++ b/lib/urlapi.c
@@ -29,6 +29,7 @@
#include "url.h"
#include "escape.h"
#include "curl_ctype.h"
+#include "inet_pton.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -591,20 +592,22 @@ static CURLUcode junkscan(char *part)
static CURLUcode hostname_check(struct Curl_URL *u, char *hostname)
{
- const char *l = NULL; /* accepted characters */
size_t len;
size_t hlen = strlen(hostname);
if(hostname[0] == '[') {
+ char dest[16]; /* fits a binary IPv6 address */
+ const char *l = "0123456789abcdefABCDEF:.";
hostname++;
- l = "0123456789abcdefABCDEF::.";
hlen -= 2;
- }
- if(l) {
+ if(hostname[hlen] != ']')
+ return CURLUE_MALFORMED_INPUT;
+
/* only valid letters are ok */
len = strspn(hostname, l);
if(hlen != len) {
+ hlen = len;
if(hostname[len] == '%') {
/* this could now be '%[zone id]' */
char zoneid[16];
@@ -628,6 +631,12 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname)
return CURLUE_MALFORMED_INPUT;
/* hostname is fine */
}
+#ifdef ENABLE_IPV6
+ hostname[hlen] = 0; /* end the address there */
+ if(1 != Curl_inet_pton(AF_INET6, hostname, dest))
+ return CURLUE_MALFORMED_INPUT;
+ hostname[hlen] = ']'; /* restore ending bracket */
+#endif
}
else {
/* letters from the second string is not ok */
diff --git a/lib/urldata.h b/lib/urldata.h
index a489ff495..2c7c1fb4e 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -129,6 +129,7 @@ typedef ssize_t (Curl_recv)(struct connectdata *conn, /* connection data */
#include "smb.h"
#include "wildcard.h"
#include "multihandle.h"
+#include "quic.h"
#ifdef HAVE_GSSAPI
# ifdef HAVE_GSSGNU
@@ -405,6 +406,7 @@ struct ConnectBits {
the first time on the first connect function call */
bit close:1; /* if set, we close the connection after this request */
bit reuse:1; /* if set, this is a re-used connection */
+ bit altused:1; /* this is an alt-svc "redirect" */
bit conn_to_host:1; /* if set, this connection has a "connect to host"
that overrides the host in the URL */
bit conn_to_port:1; /* if set, this connection has a "connect to port"
@@ -664,27 +666,23 @@ struct Curl_handler {
/* Called from the multi interface during the PROTOCONNECT phase, and it
should then return a proper fd set */
int (*proto_getsock)(struct connectdata *conn,
- curl_socket_t *socks,
- int numsocks);
+ 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 connectdata *conn,
- curl_socket_t *socks,
- int numsocks);
+ 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 connectdata *conn,
- curl_socket_t *socks,
- int numsocks);
+ 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)(const struct connectdata *conn,
- curl_socket_t *socks,
- int numsocks);
+ 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
@@ -783,6 +781,8 @@ struct http_connect_state {
bit close_connection:1;
};
+struct ldapconninfo;
+
/*
* The connectdata struct contains all fields and variables that should be
* unique for an entire connection.
@@ -832,7 +832,16 @@ struct connectdata {
unsigned int scope_id; /* Scope id for IPv6 */
- int socktype; /* SOCK_STREAM or SOCK_DGRAM */
+ enum {
+ TRNSPRT_TCP = 3,
+ TRNSPRT_UDP = 4,
+ TRNSPRT_QUIC = 5
+ } transport;
+
+#ifdef ENABLE_QUIC
+ struct quicsocket hequic[2]; /* two, for happy eyeballs! */
+ struct quicsocket *quic;
+#endif
struct hostname host;
char *hostname_resolve; /* host name to resolve to address, allocated */
@@ -871,7 +880,8 @@ struct connectdata {
char *passwd; /* password string, allocated */
char *options; /* options string, allocated */
- char *oauth_bearer; /* bearer token for OAuth 2.0, allocated */
+ char *oauth_bearer; /* bearer token for OAuth 2.0, allocated */
+ char *sasl_authzid; /* authorisation identity string, allocated */
int httpversion; /* the HTTP version*10 reported by the server */
int rtspversion; /* the RTSP version*10 reported by the server */
@@ -905,8 +915,8 @@ struct connectdata {
struct curltime connecttime;
/* The two fields below get set in Curl_connecthost */
int num_addr; /* number of addresses to try to connect to */
- time_t timeoutms_per_addr; /* how long time in milliseconds to spend on
- trying to connect to each IP address */
+ timediff_t timeoutms_per_addr; /* how long time in milliseconds to spend on
+ trying to connect to each IP address */
const struct Curl_handler *handler; /* Connection's protocol handler */
const struct Curl_handler *given; /* The protocol first given */
@@ -1011,7 +1021,8 @@ struct connectdata {
struct smtp_conn smtpc;
struct rtsp_conn rtspc;
struct smb_conn smbc;
- void *generic; /* RTMP and LDAP use this */
+ void *rtmp;
+ struct ldapconninfo *ldapc;
} proto;
int cselect_bits; /* bitmask of socket events */
@@ -1066,6 +1077,7 @@ struct PureInfo {
long numconnects; /* how many new connection did libcurl created */
char *contenttype; /* the content type of the object */
char *wouldredirect; /* URL this would've been redirected to if asked to */
+ curl_off_t retry_after; /* info from Retry-After: header */
/* PureInfo members 'conn_primary_ip', 'conn_primary_port', 'conn_local_ip'
and, 'conn_local_port' are copied over from the connectdata struct in
@@ -1085,7 +1097,6 @@ struct PureInfo {
OpenSSL, GnuTLS, Schannel, NSS and GSKit
builds. Asked for with CURLOPT_CERTINFO
/ CURLINFO_CERTINFO */
-
bit timecond:1; /* set to TRUE if the time condition didn't match, which
thus made the document NOT get fetched */
};
@@ -1104,17 +1115,17 @@ struct Progress {
int width; /* screen width at download start */
int flags; /* see progress.h */
- time_t timespent;
+ timediff_t timespent;
curl_off_t dlspeed;
curl_off_t ulspeed;
- time_t t_nslookup;
- time_t t_connect;
- time_t t_appconnect;
- time_t t_pretransfer;
- time_t t_starttransfer;
- time_t t_redirect;
+ 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;
@@ -1222,6 +1233,7 @@ typedef enum {
EXPIRE_SPEEDCHECK,
EXPIRE_TIMEOUT,
EXPIRE_TOOFAST,
+ EXPIRE_QUIC,
EXPIRE_LAST /* not an actual timer, used as a marker only */
} expire_id;
@@ -1494,6 +1506,10 @@ enum dupstring {
#ifdef USE_ALTSVC
STRING_ALTSVC, /* CURLOPT_ALTSVC */
#endif
+ STRING_SASL_AUTHZID, /* CURLOPT_SASL_AUTHZID */
+#ifndef CURL_DISABLE_PROXY
+ STRING_TEMP_URL, /* temp URL storage for proxy use */
+#endif
/* -- end of zero-terminated strings -- */
STRING_LASTZEROTERMINATED,
diff --git a/lib/vauth/digest_sspi.c b/lib/vauth/digest_sspi.c
index d9db534c4..a9016928e 100644
--- a/lib/vauth/digest_sspi.c
+++ b/lib/vauth/digest_sspi.c
@@ -61,6 +61,11 @@ bool Curl_auth_is_digest_supported(void)
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);
}
@@ -220,7 +225,10 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
free(output_token);
free(input_token);
- return CURLE_RECV_ERROR;
+ if(status == SEC_E_INSUFFICIENT_MEMORY)
+ return CURLE_OUT_OF_MEMORY;
+
+ return CURLE_AUTH_ERROR;
}
/* Base64 encode the response */
@@ -607,7 +615,10 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
Curl_safefree(digest->http_context);
- return CURLE_OUT_OF_MEMORY;
+ if(status == SEC_E_INSUFFICIENT_MEMORY)
+ return CURLE_OUT_OF_MEMORY;
+
+ return CURLE_AUTH_ERROR;
}
output_token_len = resp_buf.cbBuffer;
diff --git a/lib/vauth/krb5_gssapi.c b/lib/vauth/krb5_gssapi.c
index 1e4c16f98..b7e64712b 100644
--- a/lib/vauth/krb5_gssapi.c
+++ b/lib/vauth/krb5_gssapi.c
@@ -121,7 +121,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
free(spn);
- return CURLE_OUT_OF_MEMORY;
+ return CURLE_AUTH_ERROR;
}
free(spn);
@@ -168,7 +168,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
Curl_gss_log_error(data, "gss_init_sec_context() failed: ",
major_status, minor_status);
- return CURLE_RECV_ERROR;
+ return CURLE_AUTH_ERROR;
}
if(output_token.value && output_token.length) {
@@ -252,7 +252,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
free(chlg);
- return CURLE_OUT_OF_MEMORY;
+ return CURLE_AUTH_ERROR;
}
/* Convert the username from internal format to a displayable token */
@@ -264,7 +264,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
free(chlg);
- return CURLE_OUT_OF_MEMORY;
+ return CURLE_AUTH_ERROR;
}
/* Setup the challenge "input" security buffer */
@@ -355,7 +355,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
free(message);
- return CURLE_OUT_OF_MEMORY;
+ return CURLE_AUTH_ERROR;
}
/* Base64 encode the response */
diff --git a/lib/vauth/krb5_sspi.c b/lib/vauth/krb5_sspi.c
index 097914e82..44dda135f 100644
--- a/lib/vauth/krb5_sspi.c
+++ b/lib/vauth/krb5_sspi.c
@@ -58,6 +58,11 @@ bool Curl_auth_is_gssapi_supported(void)
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);
}
@@ -217,8 +222,12 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
/* Free the decoded challenge as it is not required anymore */
free(chlg);
+ if(status == SEC_E_INSUFFICIENT_MEMORY) {
+ return CURLE_OUT_OF_MEMORY;
+ }
+
if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) {
- return CURLE_RECV_ERROR;
+ return CURLE_AUTH_ERROR;
}
if(memcmp(&context, krb5->context, sizeof(context))) {
@@ -309,7 +318,10 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
if(status != SEC_E_OK) {
free(chlg);
- return CURLE_OUT_OF_MEMORY;
+ if(status == SEC_E_INSUFFICIENT_MEMORY)
+ return CURLE_OUT_OF_MEMORY;
+
+ return CURLE_AUTH_ERROR;
}
/* Get the fully qualified username back from the context */
@@ -319,7 +331,10 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
if(status != SEC_E_OK) {
free(chlg);
- return CURLE_RECV_ERROR;
+ if(status == SEC_E_INSUFFICIENT_MEMORY)
+ return CURLE_OUT_OF_MEMORY;
+
+ return CURLE_AUTH_ERROR;
}
/* Setup the "input" security buffer */
@@ -438,7 +453,10 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
free(message);
free(trailer);
- return CURLE_OUT_OF_MEMORY;
+ if(status == SEC_E_INSUFFICIENT_MEMORY)
+ return CURLE_OUT_OF_MEMORY;
+
+ return CURLE_AUTH_ERROR;
}
/* Allocate the encryption (wrap) buffer */
diff --git a/lib/vauth/ntlm_sspi.c b/lib/vauth/ntlm_sspi.c
index feebdb0e3..f086e52b8 100644
--- a/lib/vauth/ntlm_sspi.c
+++ b/lib/vauth/ntlm_sspi.c
@@ -56,6 +56,11 @@ bool Curl_auth_is_ntlm_supported(void)
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);
}
@@ -169,8 +174,10 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data,
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_RECV_ERROR;
+ return CURLE_AUTH_ERROR;
/* Base64 encode the response */
return Curl_base64_encode(data, (char *) ntlm->output_token,
@@ -316,7 +323,10 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
infof(data, "NTLM handshake failure (type-3 message): Status=%x\n",
status);
- return CURLE_RECV_ERROR;
+ if(status == SEC_E_INSUFFICIENT_MEMORY)
+ return CURLE_OUT_OF_MEMORY;
+
+ return CURLE_AUTH_ERROR;
}
/* Base64 encode the response */
diff --git a/lib/vauth/spnego_gssapi.c b/lib/vauth/spnego_gssapi.c
index 9c3faa4a9..afd765db7 100644
--- a/lib/vauth/spnego_gssapi.c
+++ b/lib/vauth/spnego_gssapi.c
@@ -121,7 +121,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
free(spn);
- return CURLE_OUT_OF_MEMORY;
+ return CURLE_AUTH_ERROR;
}
free(spn);
@@ -170,14 +170,14 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
Curl_gss_log_error(data, "gss_init_sec_context() failed: ",
major_status, minor_status);
- return CURLE_LOGIN_DENIED;
+ return CURLE_AUTH_ERROR;
}
if(!output_token.value || !output_token.length) {
if(output_token.value)
gss_release_buffer(&unused_status, &output_token);
- return CURLE_OUT_OF_MEMORY;
+ return CURLE_AUTH_ERROR;
}
/* Free previous token */
diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c
index 735988480..82b96b8c1 100644
--- a/lib/vauth/spnego_sspi.c
+++ b/lib/vauth/spnego_sspi.c
@@ -59,6 +59,12 @@ bool Curl_auth_is_spnego_supported(void)
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);
}
@@ -165,7 +171,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
nego->p_identity, NULL, NULL,
nego->credentials, &expiry);
if(nego->status != SEC_E_OK)
- return CURLE_LOGIN_DENIED;
+ return CURLE_AUTH_ERROR;
/* Allocate our new context handle */
nego->context = calloc(1, sizeof(CtxtHandle));
@@ -251,14 +257,25 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
char buffer[STRERROR_LEN];
failf(data, "InitializeSecurityContext failed: %s",
Curl_sspi_strerror(nego->status, buffer, sizeof(buffer)));
- return CURLE_OUT_OF_MEMORY;
+
+ 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 = s_pSecFn->CompleteAuthToken(nego->context, &resp_desc);
if(GSS_ERROR(nego->status)) {
- return CURLE_RECV_ERROR;
+ char buffer[STRERROR_LEN];
+ failf(data, "CompleteAuthToken failed: %s",
+ Curl_sspi_strerror(nego->status, buffer, sizeof(buffer)));
+
+ if(nego->status == (DWORD)SEC_E_INSUFFICIENT_MEMORY)
+ return CURLE_OUT_OF_MEMORY;
+
+ return CURLE_AUTH_ERROR;
}
}
diff --git a/lib/version.c b/lib/version.c
index d938730a1..caefef919 100644
--- a/lib/version.c
+++ b/lib/version.c
@@ -27,6 +27,7 @@
#include "vtls/vtls.h"
#include "http2.h"
#include "ssh.h"
+#include "quic.h"
#include "curl_printf.h"
#ifdef USE_ARES
@@ -53,18 +54,6 @@
#include <librtmp/rtmp.h>
#endif
-#ifdef USE_LIBSSH2
-#include <libssh2.h>
-#endif
-
-#ifdef HAVE_LIBSSH2_VERSION
-/* get it run-time if possible */
-#define CURL_LIBSSH2_VERSION libssh2_version(0)
-#else
-/* use build-time if run-time not possible */
-#define CURL_LIBSSH2_VERSION LIBSSH2_VERSION
-#endif
-
#ifdef HAVE_ZLIB_H
#include <zlib.h>
#ifdef __SYMBIAN32__
@@ -102,7 +91,7 @@ static size_t brotli_version(char *buf, size_t bufsz)
char *curl_version(void)
{
static bool initialized;
- static char version[200];
+ static char version[250];
char *ptr = version;
size_t len;
size_t left = sizeof(version);
@@ -172,18 +161,22 @@ char *curl_version(void)
left -= len;
ptr += len;
#endif
-#ifdef USE_LIBSSH2
- len = msnprintf(ptr, left, " libssh2/%s", CURL_LIBSSH2_VERSION);
+#ifdef USE_SSH
+ if(left) {
+ *ptr++=' ';
+ left--;
+ }
+ len = Curl_ssh_version(ptr, left);
left -= len;
ptr += len;
#endif
-#ifdef USE_LIBSSH
- len = msnprintf(ptr, left, " libssh/%s", CURL_LIBSSH_VERSION);
+#ifdef USE_NGHTTP2
+ len = Curl_http2_ver(ptr, left);
left -= len;
ptr += len;
#endif
-#ifdef USE_NGHTTP2
- len = Curl_http2_ver(ptr, left);
+#ifdef ENABLE_QUIC
+ len = Curl_quic_ver(ptr, left);
left -= len;
ptr += len;
#endif
@@ -358,6 +351,9 @@ static curl_version_info_data version_info = {
#if defined(USE_NGHTTP2)
| CURL_VERSION_HTTP2
#endif
+#if defined(ENABLE_QUIC)
+ | CURL_VERSION_HTTP3
+#endif
#if defined(USE_UNIX_SOCKETS)
| CURL_VERSION_UNIX_SOCKETS
#endif
@@ -385,6 +381,9 @@ static curl_version_info_data version_info = {
NULL, /* ssh lib version */
0, /* brotli_ver_num */
NULL, /* brotli version */
+ 0, /* nghttp2 version number */
+ NULL, /* nghttp2 version string */
+ NULL /* quic library string */
};
curl_version_info_data *curl_version_info(CURLversion stamp)
@@ -446,11 +445,8 @@ curl_version_info_data *curl_version_info(CURLversion stamp)
#endif /* _LIBICONV_VERSION */
#endif
-#if defined(USE_LIBSSH2)
- msnprintf(ssh_buffer, sizeof(ssh_buffer), "libssh2/%s", LIBSSH2_VERSION);
- version_info.libssh_version = ssh_buffer;
-#elif defined(USE_LIBSSH)
- msnprintf(ssh_buffer, sizeof(ssh_buffer), "libssh/%s", CURL_LIBSSH_VERSION);
+#if defined(USE_SSH)
+ Curl_ssh_version(ssh_buffer, sizeof(ssh_buffer));
version_info.libssh_version = ssh_buffer;
#endif
@@ -460,6 +456,22 @@ curl_version_info_data *curl_version_info(CURLversion stamp)
version_info.brotli_version = brotli_buffer;
#endif
+#ifdef USE_NGHTTP2
+ {
+ nghttp2_info *h2 = nghttp2_version(0);
+ version_info.nghttp2_ver_num = h2->version_num;
+ version_info.nghttp2_version = h2->version_str;
+ }
+#endif
+
+#ifdef ENABLE_QUIC
+ {
+ static char quicbuffer[80];
+ Curl_quic_ver(quicbuffer, sizeof(quicbuffer));
+ version_info.quic_version = quicbuffer;
+ }
+#endif
+
(void)stamp; /* avoid compiler warnings, we don't use this */
initialized = true;
diff --git a/lib/vquic/ngtcp2.c b/lib/vquic/ngtcp2.c
new file mode 100644
index 000000000..6b3d53ee0
--- /dev/null
+++ b/lib/vquic/ngtcp2.c
@@ -0,0 +1,1600 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed 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.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_NGTCP2
+#include <ngtcp2/ngtcp2.h>
+#include <ngtcp2/ngtcp2_crypto.h>
+#include <nghttp3/nghttp3.h>
+#include <openssl/err.h>
+#include "urldata.h"
+#include "sendf.h"
+#include "strdup.h"
+#include "rand.h"
+#include "ngtcp2.h"
+#include "multiif.h"
+#include "strcase.h"
+#include "connect.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"
+
+/* #define DEBUG_NGTCP2 */
+#define DEBUG_HTTP3
+#ifdef DEBUG_HTTP3
+#define H3BUGF(x) x
+#else
+#define H3BUGF(x) do { } WHILE_FALSE
+#endif
+
+/*
+ * This holds outgoing HTTP/3 stream data that is used by nghttp3 until acked.
+ * It is used as a circular buffer. Add new bytes at the end until it reaches
+ * the far end, then start over at index 0 again.
+ */
+
+#define H3_SEND_SIZE (20*1024)
+struct h3out {
+ uint8_t buf[H3_SEND_SIZE];
+ size_t used; /* number of bytes used in the buffer */
+ size_t windex; /* index in the buffer where to start writing the next
+ data block */
+};
+
+#define QUIC_MAX_STREAMS (256*1024)
+#define QUIC_MAX_DATA (1*1024*1024)
+#define QUIC_IDLE_TIMEOUT 60000 /* milliseconds */
+#define QUIC_CIPHERS \
+ "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \
+ "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
+#define QUIC_GROUPS "P-256:X25519:P-384:P-521"
+
+static CURLcode ng_process_ingress(struct connectdata *conn,
+ curl_socket_t sockfd,
+ struct quicsocket *qs);
+static CURLcode ng_flush_egress(struct connectdata *conn, int sockfd,
+ struct quicsocket *qs);
+static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id,
+ size_t datalen, void *user_data,
+ void *stream_user_data);
+
+static ngtcp2_tstamp timestamp(void)
+{
+ struct curltime ct = Curl_now();
+ return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS;
+}
+
+#ifdef DEBUG_NGTCP2
+static void quic_printf(void *user_data, const char *fmt, ...)
+{
+ va_list ap;
+ (void)user_data; /* TODO, use this to do infof() instead long-term */
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+}
+#endif
+
+static ngtcp2_crypto_level
+quic_from_ossl_level(OSSL_ENCRYPTION_LEVEL ossl_level)
+{
+ switch(ossl_level) {
+ case ssl_encryption_initial:
+ return NGTCP2_CRYPTO_LEVEL_INITIAL;
+ case ssl_encryption_early_data:
+ return NGTCP2_CRYPTO_LEVEL_EARLY;
+ case ssl_encryption_handshake:
+ return NGTCP2_CRYPTO_LEVEL_HANDSHAKE;
+ case ssl_encryption_application:
+ return NGTCP2_CRYPTO_LEVEL_APP;
+ default:
+ assert(0);
+ }
+}
+
+static int setup_initial_crypto_context(struct quicsocket *qs)
+{
+ const ngtcp2_cid *dcid = ngtcp2_conn_get_dcid(qs->qconn);
+
+ if(ngtcp2_crypto_derive_and_install_initial_key(
+ qs->qconn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, dcid,
+ NGTCP2_CRYPTO_SIDE_CLIENT) != 0)
+ return -1;
+
+ return 0;
+}
+
+static void quic_settings(ngtcp2_settings *s,
+ uint64_t stream_buffer_size)
+{
+ ngtcp2_settings_default(s);
+#ifdef DEBUG_NGTCP2
+ s->log_printf = quic_printf;
+#else
+ s->log_printf = NULL;
+#endif
+ s->initial_ts = timestamp();
+ s->max_stream_data_bidi_local = stream_buffer_size;
+ s->max_stream_data_bidi_remote = QUIC_MAX_STREAMS;
+ s->max_stream_data_uni = QUIC_MAX_STREAMS;
+ s->max_data = QUIC_MAX_DATA;
+ s->max_streams_bidi = 1;
+ s->max_streams_uni = 3;
+ s->idle_timeout = QUIC_IDLE_TIMEOUT;
+}
+
+static FILE *keylog_file; /* not thread-safe */
+static void keylog_callback(const SSL *ssl, const char *line)
+{
+ (void)ssl;
+ fputs(line, keylog_file);
+ fputc('\n', keylog_file);
+ fflush(keylog_file);
+}
+
+static int init_ngh3_conn(struct quicsocket *qs);
+
+static int quic_set_encryption_secrets(SSL *ssl,
+ OSSL_ENCRYPTION_LEVEL ossl_level,
+ const uint8_t *rx_secret,
+ const uint8_t *tx_secret,
+ size_t secretlen)
+{
+ struct quicsocket *qs = (struct quicsocket *)SSL_get_app_data(ssl);
+ int level = quic_from_ossl_level(ossl_level);
+
+ if(ngtcp2_crypto_derive_and_install_key(
+ qs->qconn, ssl, NULL, NULL, NULL, NULL, NULL, NULL, level, rx_secret,
+ tx_secret, secretlen, NGTCP2_CRYPTO_SIDE_CLIENT) != 0)
+ return 0;
+
+ if(level == NGTCP2_CRYPTO_LEVEL_APP && init_ngh3_conn(qs) != CURLE_OK)
+ return 0;
+
+ return 1;
+}
+
+static int quic_add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level,
+ const uint8_t *data, size_t len)
+{
+ struct quicsocket *qs = (struct quicsocket *)SSL_get_app_data(ssl);
+ struct quic_handshake *crypto_data;
+ ngtcp2_crypto_level level = quic_from_ossl_level(ossl_level);
+ int rv;
+
+ crypto_data = &qs->client_crypto_data[level];
+ if(crypto_data->buf == NULL) {
+ crypto_data->buf = malloc(4096);
+ crypto_data->alloclen = 4096;
+ /* TODO Explode if malloc failed */
+ }
+
+ /* TODO Just pretend that handshake does not grow more than 4KiB for
+ now */
+ assert(crypto_data->len + len <= crypto_data->alloclen);
+
+ memcpy(&crypto_data->buf[crypto_data->len], data, len);
+ crypto_data->len += len;
+
+ rv = ngtcp2_conn_submit_crypto_data(
+ qs->qconn, level, (uint8_t *)(&crypto_data->buf[crypto_data->len] - len),
+ len);
+ if(rv) {
+ fprintf(stderr, "write_client_handshake failed\n");
+ }
+ assert(0 == rv);
+
+ return 1;
+}
+
+static int quic_flush_flight(SSL *ssl)
+{
+ (void)ssl;
+ return 1;
+}
+
+static int quic_send_alert(SSL *ssl, enum ssl_encryption_level_t level,
+ uint8_t alert)
+{
+ struct quicsocket *qs = (struct quicsocket *)SSL_get_app_data(ssl);
+ (void)level;
+
+ qs->tls_alert = alert;
+ return 1;
+}
+
+static SSL_QUIC_METHOD quic_method = {quic_set_encryption_secrets,
+ quic_add_handshake_data,
+ quic_flush_flight, quic_send_alert};
+
+static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data)
+{
+ SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method());
+ const char *keylog_filename;
+
+ SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION);
+ SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION);
+
+ SSL_CTX_set_default_verify_paths(ssl_ctx);
+
+ if(SSL_CTX_set_ciphersuites(ssl_ctx, QUIC_CIPHERS) != 1) {
+ failf(data, "SSL_CTX_set_ciphersuites: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return NULL;
+ }
+
+ if(SSL_CTX_set1_groups_list(ssl_ctx, QUIC_GROUPS) != 1) {
+ failf(data, "SSL_CTX_set1_groups_list failed");
+ return NULL;
+ }
+
+ SSL_CTX_set_quic_method(ssl_ctx, &quic_method);
+
+ keylog_filename = getenv("SSLKEYLOGFILE");
+ if(keylog_filename) {
+ keylog_file = fopen(keylog_filename, "wb");
+ if(keylog_file) {
+ SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback);
+ }
+ }
+
+ return ssl_ctx;
+}
+
+/** SSL callbacks ***/
+
+static int quic_init_ssl(struct quicsocket *qs)
+{
+ const uint8_t *alpn = NULL;
+ size_t alpnlen = 0;
+ /* this will need some attention when HTTPS proxy over QUIC get fixed */
+ const char * const hostname = qs->conn->host.name;
+
+ if(qs->ssl)
+ SSL_free(qs->ssl);
+
+ qs->ssl = SSL_new(qs->sslctx);
+
+ SSL_set_app_data(qs->ssl, qs);
+ SSL_set_connect_state(qs->ssl);
+
+ switch(qs->version) {
+#ifdef NGTCP2_PROTO_VER
+ case NGTCP2_PROTO_VER:
+ alpn = (const uint8_t *)NGTCP2_ALPN_H3;
+ alpnlen = sizeof(NGTCP2_ALPN_H3) - 1;
+ break;
+#endif
+ }
+ if(alpn)
+ SSL_set_alpn_protos(qs->ssl, alpn, (int)alpnlen);
+
+ /* set SNI */
+ SSL_set_tlsext_host_name(qs->ssl, hostname);
+ return 0;
+}
+
+static int cb_initial(ngtcp2_conn *quic, void *user_data)
+{
+ struct quicsocket *qs = (struct quicsocket *)user_data;
+
+ if(ngtcp2_crypto_read_write_crypto_data(
+ quic, qs->ssl, NGTCP2_CRYPTO_LEVEL_INITIAL, NULL, 0) != 0)
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+
+ return 0;
+}
+
+static int
+cb_recv_crypto_data(ngtcp2_conn *tconn, ngtcp2_crypto_level crypto_level,
+ uint64_t offset,
+ const uint8_t *data, size_t datalen,
+ void *user_data)
+{
+ struct quicsocket *qs = (struct quicsocket *)user_data;
+ (void)offset;
+
+ if(ngtcp2_crypto_read_write_crypto_data(tconn, qs->ssl, crypto_level, data,
+ datalen) != 0)
+ return NGTCP2_ERR_CRYPTO;
+
+ return 0;
+}
+
+static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data)
+{
+ struct quicsocket *qs = (struct quicsocket *)user_data;
+ (void)tconn;
+ infof(qs->conn->data, "QUIC handshake is completed\n");
+
+ return 0;
+}
+
+static int cb_recv_stream_data(ngtcp2_conn *tconn, int64_t stream_id,
+ int fin, uint64_t offset,
+ const uint8_t *buf, size_t buflen,
+ void *user_data, void *stream_user_data)
+{
+ struct quicsocket *qs = (struct quicsocket *)user_data;
+ ssize_t nconsumed;
+ (void)offset;
+ (void)stream_user_data;
+
+ infof(qs->conn->data, "Received %ld bytes data on stream %u\n",
+ buflen, stream_id);
+
+ nconsumed =
+ nghttp3_conn_read_stream(qs->h3conn, stream_id, buf, buflen, fin);
+ if(nconsumed < 0) {
+ failf(qs->conn->data, "nghttp3_conn_read_stream returned error: %s\n",
+ nghttp3_strerror((int)nconsumed));
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, nconsumed);
+ ngtcp2_conn_extend_max_offset(tconn, nconsumed);
+
+ return 0;
+}
+
+static int
+cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id,
+ uint64_t offset, size_t datalen, void *user_data,
+ void *stream_user_data)
+{
+ struct quicsocket *qs = (struct quicsocket *)user_data;
+ int rv;
+ (void)stream_id;
+ (void)tconn;
+ (void)offset;
+ (void)datalen;
+ (void)stream_user_data;
+
+ rv = nghttp3_conn_add_ack_offset(qs->h3conn, stream_id, datalen);
+ if(rv != 0) {
+ failf(qs->conn->data, "nghttp3_conn_add_ack_offset returned error: %s\n",
+ nghttp3_strerror(rv));
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int cb_stream_close(ngtcp2_conn *tconn, int64_t stream_id,
+ uint64_t app_error_code,
+ void *user_data, void *stream_user_data)
+{
+ struct quicsocket *qs = (struct quicsocket *)user_data;
+ int rv;
+ (void)tconn;
+ (void)stream_user_data;
+ /* stream is closed... */
+
+ rv = nghttp3_conn_close_stream(qs->h3conn, stream_id,
+ app_error_code);
+ if(rv != 0) {
+ failf(qs->conn->data, "nghttp3_conn_close_stream returned error: %s\n",
+ nghttp3_strerror(rv));
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id,
+ uint64_t final_size, uint64_t app_error_code,
+ void *user_data, void *stream_user_data)
+{
+ struct quicsocket *qs = (struct quicsocket *)user_data;
+ int rv;
+ (void)tconn;
+ (void)final_size;
+ (void)app_error_code;
+ (void)stream_user_data;
+
+ rv = nghttp3_conn_reset_stream(qs->h3conn, stream_id);
+ if(rv != 0) {
+ failf(qs->conn->data, "nghttp3_conn_reset_stream returned error: %s\n",
+ nghttp3_strerror(rv));
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int cb_recv_retry(ngtcp2_conn *tconn, const ngtcp2_pkt_hd *hd,
+ const ngtcp2_pkt_retry *retry, void *user_data)
+{
+ /* Re-generate handshake secrets here because connection ID might change. */
+ struct quicsocket *qs = (struct quicsocket *)user_data;
+ (void)tconn;
+ (void)hd;
+ (void)retry;
+
+ setup_initial_crypto_context(qs);
+
+ return 0;
+}
+
+static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn,
+ uint64_t max_streams,
+ void *user_data)
+{
+ (void)tconn;
+ (void)max_streams;
+ (void)user_data;
+
+ return 0;
+}
+
+static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id,
+ uint64_t max_data, void *user_data,
+ void *stream_user_data)
+{
+ struct quicsocket *qs = (struct quicsocket *)user_data;
+ int rv;
+ (void)tconn;
+ (void)max_data;
+ (void)stream_user_data;
+
+ rv = nghttp3_conn_unblock_stream(qs->h3conn, stream_id);
+ if(rv != 0) {
+ failf(qs->conn->data, "nghttp3_conn_unblock_stream returned error: %s\n",
+ nghttp3_strerror(rv));
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ }
+
+ return 0;
+}
+
+static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid,
+ uint8_t *token, size_t cidlen,
+ void *user_data)
+{
+ struct quicsocket *qs = (struct quicsocket *)user_data;
+ CURLcode result;
+ (void)tconn;
+
+ result = Curl_rand(qs->conn->data, cid->data, cidlen);
+ if(result)
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+ cid->datalen = cidlen;
+
+ result = Curl_rand(qs->conn->data, token, NGTCP2_STATELESS_RESET_TOKENLEN);
+ if(result)
+ return NGTCP2_ERR_CALLBACK_FAILURE;
+
+ return 0;
+}
+
+static ngtcp2_conn_callbacks ng_callbacks = {
+ cb_initial,
+ NULL, /* recv_client_initial */
+ cb_recv_crypto_data,
+ cb_handshake_completed,
+ NULL, /* recv_version_negotiation */
+ ngtcp2_crypto_encrypt_cb,
+ ngtcp2_crypto_decrypt_cb,
+ ngtcp2_crypto_hp_mask_cb,
+ cb_recv_stream_data,
+ NULL, /* acked_crypto_offset */
+ cb_acked_stream_data_offset,
+ NULL, /* stream_open */
+ cb_stream_close,
+ NULL, /* recv_stateless_reset */
+ cb_recv_retry,
+ cb_extend_max_local_streams_bidi,
+ NULL, /* extend_max_local_streams_uni */
+ NULL, /* rand */
+ cb_get_new_connection_id,
+ NULL, /* remove_connection_id */
+ NULL, /* 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,
+};
+
+/*
+ * Might be called twice for happy eyeballs.
+ */
+CURLcode Curl_quic_connect(struct connectdata *conn,
+ curl_socket_t sockfd,
+ int sockindex,
+ const struct sockaddr *addr,
+ socklen_t addrlen)
+{
+ int rc;
+ int rv;
+ CURLcode result;
+ ngtcp2_path path; /* TODO: this must be initialized properly */
+ struct Curl_easy *data = conn->data;
+ struct quicsocket *qs = &conn->hequic[sockindex];
+ char ipbuf[40];
+ long port;
+ uint8_t paramsbuf[64];
+ ngtcp2_transport_params params;
+ ssize_t nwrite;
+
+ qs->conn = conn;
+
+ /* extract the used address as a string */
+ if(!Curl_addr2string((struct sockaddr*)addr, addrlen, ipbuf, &port)) {
+ char buffer[STRERROR_LEN];
+ failf(data, "ssrem inet_ntop() failed with errno %d: %s",
+ errno, Curl_strerror(errno, buffer, sizeof(buffer)));
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+
+ infof(data, "Connect socket %d over QUIC to %s:%ld\n",
+ sockfd, ipbuf, port);
+
+ qs->version = NGTCP2_PROTO_VER;
+ qs->sslctx = quic_ssl_ctx(data);
+ if(!qs->sslctx)
+ return CURLE_FAILED_INIT; /* TODO: better return code */
+
+ if(quic_init_ssl(qs))
+ return CURLE_FAILED_INIT; /* TODO: better return code */
+
+ qs->dcid.datalen = NGTCP2_MAX_CIDLEN;
+ result = Curl_rand(data, qs->dcid.data, NGTCP2_MAX_CIDLEN);
+ if(result)
+ return result;
+
+ qs->scid.datalen = NGTCP2_MAX_CIDLEN;
+ result = Curl_rand(data, qs->scid.data, NGTCP2_MAX_CIDLEN);
+ if(result)
+ return result;
+
+ quic_settings(&qs->settings, data->set.buffer_size);
+
+ qs->local_addrlen = sizeof(qs->local_addr);
+ rv = getsockname(sockfd, (struct sockaddr *)&qs->local_addr,
+ &qs->local_addrlen);
+ if(rv == -1)
+ return CURLE_FAILED_INIT;
+
+ ngtcp2_addr_init(&path.local, (uint8_t *)&qs->local_addr, qs->local_addrlen,
+ NULL);
+ ngtcp2_addr_init(&path.remote, (uint8_t*)addr, addrlen, NULL);
+
+#ifdef NGTCP2_PROTO_VER
+#define QUICVER NGTCP2_PROTO_VER
+#else
+#error "unsupported ngtcp2 version"
+#endif
+ rc = ngtcp2_conn_client_new(&qs->qconn, &qs->dcid, &qs->scid, &path, QUICVER,
+ &ng_callbacks, &qs->settings, NULL, qs);
+ if(rc)
+ return CURLE_FAILED_INIT; /* TODO: create a QUIC error code */
+
+ ngtcp2_conn_get_local_transport_params(qs->qconn, &params);
+ nwrite = ngtcp2_encode_transport_params(
+ paramsbuf, sizeof(paramsbuf), NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO,
+ &params);
+ if(nwrite < 0) {
+ fprintf(stderr, "ngtcp2_encode_transport_params: %s\n",
+ ngtcp2_strerror((int)nwrite));
+
+ return CURLE_FAILED_INIT;
+ }
+
+ if(!SSL_set_quic_transport_params(qs->ssl, paramsbuf, nwrite))
+ return CURLE_FAILED_INIT;
+
+ rc = setup_initial_crypto_context(qs);
+ if(rc)
+ return CURLE_FAILED_INIT; /* TODO: better return code */
+
+ return CURLE_OK;
+}
+
+/*
+ * Store ngtp2 version info in this buffer, Prefix with a space. Return total
+ * length written.
+ */
+int Curl_quic_ver(char *p, size_t len)
+{
+ ngtcp2_info *ng2 = ngtcp2_version(0);
+ nghttp3_info *ht3 = nghttp3_version(0);
+ return msnprintf(p, len, " ngtcp2/%s nghttp3/%s",
+ ng2->version_str, ht3->version_str);
+}
+
+static int ng_getsock(struct connectdata *conn, curl_socket_t *socks)
+{
+ struct SingleRequest *k = &conn->data->req;
+ int bitmap = GETSOCK_BLANK;
+
+ socks[0] = conn->sock[FIRSTSOCKET];
+
+ /* in a HTTP/2 connection we can basically always get a frame so we should
+ always be ready for one */
+ bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
+
+ /* we're still uploading or the HTTP/2 layer wants to send data */
+ if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND)
+ bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
+
+ return bitmap;
+}
+
+static int ng_perform_getsock(const struct connectdata *conn,
+ curl_socket_t *socks)
+{
+ return ng_getsock((struct connectdata *)conn, socks);
+}
+
+static CURLcode ng_disconnect(struct connectdata *conn,
+ bool dead_connection)
+{
+ (void)conn;
+ (void)dead_connection;
+ return CURLE_OK;
+}
+
+static unsigned int ng_conncheck(struct connectdata *conn,
+ unsigned int checks_to_perform)
+{
+ (void)conn;
+ (void)checks_to_perform;
+ return CONNRESULT_NONE;
+}
+
+static const struct Curl_handler Curl_handler_http3 = {
+ "HTTPS", /* scheme */
+ ZERO_NULL, /* setup_connection */
+ Curl_http, /* do_it */
+ Curl_http_done, /* done */
+ ZERO_NULL, /* do_more */
+ ZERO_NULL, /* connect_it */
+ ZERO_NULL, /* connecting */
+ ZERO_NULL, /* doing */
+ ng_getsock, /* proto_getsock */
+ ng_getsock, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ng_perform_getsock, /* perform_getsock */
+ ng_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ng_conncheck, /* connection_check */
+ PORT_HTTP, /* defport */
+ CURLPROTO_HTTPS, /* protocol */
+ PROTOPT_SSL | PROTOPT_STREAM /* flags */
+};
+
+static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t app_error_code, void *user_data,
+ void *stream_user_data)
+{
+ struct Curl_easy *data = stream_user_data;
+ struct HTTP *stream = data->req.protop;
+ (void)conn;
+ (void)stream_id;
+ (void)app_error_code;
+ (void)user_data;
+ fprintf(stderr, "cb_h3_stream_close CALLED\n");
+
+ stream->closed = TRUE;
+ Curl_expire(data, 0, EXPIRE_QUIC);
+ return 0;
+}
+
+static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream_id,
+ const uint8_t *buf, size_t buflen,
+ void *user_data, void *stream_user_data)
+{
+ struct quicsocket *qs = user_data;
+ size_t ncopy;
+ struct Curl_easy *data = stream_user_data;
+ struct HTTP *stream = data->req.protop;
+ (void)conn;
+ fprintf(stderr, "cb_h3_recv_data CALLED with %d bytes\n", buflen);
+
+ /* TODO: this needs to be handled properly */
+ DEBUGASSERT(buflen <= stream->len);
+
+ ncopy = CURLMIN(stream->len, buflen);
+ memcpy(stream->mem, buf, ncopy);
+ stream->len -= ncopy;
+ stream->memlen += ncopy;
+#if 0 /* extra debugging of incoming h3 data */
+ fprintf(stderr, "!! Copies %zd bytes to %p (total %zd)\n",
+ ncopy, stream->mem, stream->memlen);
+ {
+ size_t i;
+ for(i = 0; i < ncopy; i++) {
+ fprintf(stderr, "!! data[%d]: %02x '%c'\n", i, buf[i], buf[i]);
+ }
+ }
+#endif
+ stream->mem += ncopy;
+
+ ngtcp2_conn_extend_max_stream_offset(qs->qconn, stream_id, buflen);
+ ngtcp2_conn_extend_max_offset(qs->qconn, buflen);
+
+ return 0;
+}
+
+static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream_id,
+ size_t consumed, void *user_data,
+ void *stream_user_data)
+{
+ struct quicsocket *qs = user_data;
+ (void)conn;
+ (void)stream_user_data;
+ fprintf(stderr, "cb_h3_deferred_consume CALLED\n");
+
+ ngtcp2_conn_extend_max_stream_offset(qs->qconn, stream_id, consumed);
+ ngtcp2_conn_extend_max_offset(qs->qconn, consumed);
+
+ return 0;
+}
+
+/* Decode HTTP status code. Returns -1 if no valid status code was
+ decoded. (duplicate from http2.c) */
+static int decode_status_code(const uint8_t *value, size_t len)
+{
+ int i;
+ int res;
+
+ if(len != 3) {
+ return -1;
+ }
+
+ res = 0;
+
+ for(i = 0; i < 3; ++i) {
+ char c = value[i];
+
+ if(c < '0' || c > '9') {
+ return -1;
+ }
+
+ res *= 10;
+ res += c - '0';
+ }
+
+ return res;
+}
+
+static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id,
+ void *user_data, void *stream_user_data)
+{
+ struct Curl_easy *data = stream_user_data;
+ struct HTTP *stream = data->req.protop;
+ (void)conn;
+ (void)stream_id;
+ (void)user_data;
+
+ if(stream->memlen >= 2) {
+ memcpy(stream->mem, "\r\n", 2);
+ stream->len -= 2;
+ stream->memlen += 2;
+ stream->mem += 2;
+ }
+ return 0;
+}
+
+static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id,
+ int32_t token, nghttp3_rcbuf *name,
+ nghttp3_rcbuf *value, uint8_t flags,
+ void *user_data, void *stream_user_data)
+{
+ nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name);
+ nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value);
+ struct Curl_easy *data = stream_user_data;
+ struct HTTP *stream = data->req.protop;
+ size_t ncopy;
+ (void)conn;
+ (void)stream_id;
+ (void)token;
+ (void)flags;
+ (void)user_data;
+
+ fprintf(stderr, "cb_h3_recv_header called!\n");
+
+ if(h3name.len == sizeof(":status") - 1 &&
+ !memcmp(":status", h3name.base, h3name.len)) {
+ int status = decode_status_code(h3val.base, h3val.len);
+ DEBUGASSERT(status != -1);
+ msnprintf(stream->mem, stream->len, "HTTP/3 %03d \r\n", status);
+ }
+ else {
+ /* store as a HTTP1-style header */
+ msnprintf(stream->mem, stream->len, "%.*s: %.*s\n",
+ h3name.len, h3name.base, h3val.len, h3val.base);
+ }
+
+ ncopy = strlen(stream->mem);
+ stream->len -= ncopy;
+ stream->memlen += ncopy;
+ stream->mem += ncopy;
+ return 0;
+}
+
+static int cb_h3_send_stop_sending(nghttp3_conn *conn, int64_t stream_id,
+ uint64_t app_error_code,
+ void *user_data,
+ void *stream_user_data)
+{
+ (void)conn;
+ (void)stream_id;
+ (void)app_error_code;
+ (void)user_data;
+ (void)stream_user_data;
+ fprintf(stderr, "cb_h3_send_stop_sending CALLED\n");
+ return 0;
+}
+
+static nghttp3_conn_callbacks ngh3_callbacks = {
+ cb_h3_acked_stream_data, /* acked_stream_data */
+ cb_h3_stream_close,
+ cb_h3_recv_data,
+ cb_h3_deferred_consume,
+ NULL, /* begin_headers */
+ cb_h3_recv_header,
+ cb_h3_end_headers,
+ NULL, /* begin_trailers */
+ cb_h3_recv_header,
+ NULL, /* end_trailers */
+ NULL, /* http_begin_push_promise */
+ NULL, /* http_recv_push_promise */
+ NULL, /* http_end_push_promise */
+ NULL, /* http_cancel_push */
+ cb_h3_send_stop_sending,
+ NULL, /* push_stream */
+ NULL, /* end_stream */
+};
+
+static int init_ngh3_conn(struct quicsocket *qs)
+{
+ CURLcode result;
+ int rc;
+ int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id;
+
+ if(ngtcp2_conn_get_max_local_streams_uni(qs->qconn) < 3) {
+ failf(qs->conn->data, "too few available QUIC streams");
+ return CURLE_FAILED_INIT;
+ }
+
+ nghttp3_conn_settings_default(&qs->h3settings);
+
+ rc = nghttp3_conn_client_new(&qs->h3conn,
+ &ngh3_callbacks,
+ &qs->h3settings,
+ nghttp3_mem_default(),
+ qs);
+ if(rc) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+
+ rc = ngtcp2_conn_open_uni_stream(qs->qconn, &ctrl_stream_id, NULL);
+ if(rc) {
+ result = CURLE_FAILED_INIT;
+ goto fail;
+ }
+
+ rc = nghttp3_conn_bind_control_stream(qs->h3conn, ctrl_stream_id);
+ if(rc) {
+ result = CURLE_FAILED_INIT;
+ goto fail;
+ }
+
+ rc = ngtcp2_conn_open_uni_stream(qs->qconn, &qpack_enc_stream_id, NULL);
+ if(rc) {
+ result = CURLE_FAILED_INIT;
+ goto fail;
+ }
+
+ rc = ngtcp2_conn_open_uni_stream(qs->qconn, &qpack_dec_stream_id, NULL);
+ if(rc) {
+ result = CURLE_FAILED_INIT;
+ goto fail;
+ }
+
+ rc = nghttp3_conn_bind_qpack_streams(qs->h3conn, qpack_enc_stream_id,
+ qpack_dec_stream_id);
+ if(rc) {
+ result = CURLE_FAILED_INIT;
+ goto fail;
+ }
+
+ return CURLE_OK;
+ fail:
+
+ return result;
+}
+
+static Curl_recv ngh3_stream_recv;
+static Curl_send ngh3_stream_send;
+
+/* incoming data frames on the h3 stream */
+static ssize_t ngh3_stream_recv(struct connectdata *conn,
+ int sockindex,
+ char *buf,
+ size_t buffersize,
+ CURLcode *curlcode)
+{
+ curl_socket_t sockfd = conn->sock[sockindex];
+ struct HTTP *stream = conn->data->req.protop;
+ struct quicsocket *qs = conn->quic;
+
+ fprintf(stderr, "ngh3_stream_recv CALLED (easy %p, socket %d)\n",
+ conn->data, sockfd);
+
+ if(!stream->memlen) {
+ /* remember where to store incoming data for this stream and how big the
+ buffer is */
+ stream->mem = buf;
+ stream->len = buffersize;
+ }
+ /* else, there's data in the buffer already */
+
+ if(ng_process_ingress(conn, sockfd, qs)) {
+ *curlcode = CURLE_RECV_ERROR;
+ return -1;
+ }
+ if(ng_flush_egress(conn, sockfd, qs)) {
+ *curlcode = CURLE_SEND_ERROR;
+ return -1;
+ }
+
+ if(stream->memlen) {
+ ssize_t memlen = stream->memlen;
+ /* data arrived */
+ *curlcode = CURLE_OK;
+ /* reset to allow more data to come */
+ stream->memlen = 0;
+ stream->mem = buf;
+ stream->len = buffersize;
+ H3BUGF(infof(conn->data, "!! ngh3_stream_recv returns %zd bytes at %p\n",
+ memlen, buf));
+ return memlen;
+ }
+
+ if(stream->closed) {
+ *curlcode = CURLE_OK;
+ return 0;
+ }
+
+ infof(conn->data, "ngh3_stream_recv returns 0 bytes and EAGAIN\n");
+ *curlcode = CURLE_AGAIN;
+ return -1;
+}
+
+/* this amount of data has now been acked on this stream */
+static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id,
+ size_t datalen, void *user_data,
+ void *stream_user_data)
+{
+ struct Curl_easy *data = stream_user_data;
+ struct HTTP *stream = data->req.protop;
+ (void)conn;
+ (void)stream_id;
+ (void)user_data;
+
+ if(!data->set.postfields) {
+ stream->h3out->used -= datalen;
+ fprintf(stderr, "cb_h3_acked_stream_data, %zd bytes, %zd left unacked\n",
+ datalen, stream->h3out->used);
+ DEBUGASSERT(stream->h3out->used < H3_SEND_SIZE);
+ }
+ return 0;
+}
+
+static int cb_h3_readfunction(nghttp3_conn *conn, int64_t stream_id,
+ const uint8_t **pdata,
+ size_t *pdatalen, uint32_t *pflags,
+ void *user_data, void *stream_user_data)
+{
+ struct Curl_easy *data = stream_user_data;
+ size_t nread;
+ struct HTTP *stream = data->req.protop;
+ (void)conn;
+ (void)stream_id;
+ (void)user_data;
+
+ if(data->set.postfields) {
+ *pdata = data->set.postfields;
+ *pdatalen = data->state.infilesize;
+ *pflags = NGHTTP3_DATA_FLAG_EOF;
+ return 0;
+ }
+
+ nread = CURLMIN(stream->upload_len, H3_SEND_SIZE - stream->h3out->used);
+ if(nread > 0) {
+ /* nghttp3 wants us to hold on to the data until it tells us it is okay to
+ delete it. Append the data at the end of the h3out buffer. Since we can
+ only return consecutive data, copy the amount that fits and the next
+ part comes in next invoke. */
+ struct h3out *out = stream->h3out;
+ if(nread + out->windex > H3_SEND_SIZE)
+ nread = H3_SEND_SIZE - out->windex;
+
+ memcpy(&out->buf[out->windex], stream->upload_mem, nread);
+ out->windex += nread;
+ out->used += nread;
+
+ /* that's the chunk we return to nghttp3 */
+ *pdata = &out->buf[out->windex];
+ *pdatalen = nread;
+
+ if(out->windex == H3_SEND_SIZE)
+ out->windex = 0; /* wrap */
+ stream->upload_mem += nread;
+ stream->upload_len -= nread;
+ if(data->state.infilesize != -1) {
+ stream->upload_left -= nread;
+ if(!stream->upload_left)
+ *pflags = NGHTTP3_DATA_FLAG_EOF;
+ }
+ fprintf(stderr, "cb_h3_readfunction %zd bytes%s (at %zd unacked)\n",
+ nread, *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"",
+ out->used);
+ }
+ if(stream->upload_done && !stream->upload_len &&
+ (stream->upload_left <= 0)) {
+ H3BUGF(infof(data, "!!!!!!!!! cb_h3_readfunction sets EOF\n"));
+ *pdata = NULL;
+ *pdatalen = 0;
+ *pflags = NGHTTP3_DATA_FLAG_EOF;
+ }
+ else if(!nread) {
+ *pdatalen = 0;
+ return NGHTTP3_ERR_WOULDBLOCK;
+ }
+ return 0;
+}
+
+/* Index where :authority header field will appear in request header
+ field list. */
+#define AUTHORITY_DST_IDX 3
+
+static CURLcode http_request(struct connectdata *conn, const void *mem,
+ size_t len)
+{
+ struct HTTP *stream = conn->data->req.protop;
+ size_t nheader;
+ size_t i;
+ size_t authority_idx;
+ char *hdbuf = (char *)mem;
+ char *end, *line_end;
+ struct quicsocket *qs = conn->quic;
+ CURLcode result = CURLE_OK;
+ struct Curl_easy *data = conn->data;
+ nghttp3_nv *nva = NULL;
+ int64_t stream3_id;
+ int rc;
+ struct h3out *h3out = NULL;
+
+ rc = ngtcp2_conn_open_bidi_stream(qs->qconn, &stream3_id, NULL);
+ if(rc) {
+ failf(conn->data, "can get bidi streams");
+ result = CURLE_SEND_ERROR;
+ goto fail;
+ }
+
+ stream->stream3_id = stream3_id;
+ stream->h3req = TRUE; /* senf off! */
+
+ /* Calculate number of headers contained in [mem, mem + len). Assumes a
+ correctly generated HTTP header field block. */
+ nheader = 0;
+ for(i = 1; i < len; ++i) {
+ if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') {
+ ++nheader;
+ ++i;
+ }
+ }
+ if(nheader < 2)
+ goto fail;
+
+ /* We counted additional 2 \r\n in the first and last line. We need 3
+ new headers: :method, :path and :scheme. Therefore we need one
+ more space. */
+ nheader += 1;
+ nva = malloc(sizeof(nghttp3_nv) * nheader);
+ if(!nva) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+
+ /* Extract :method, :path from request line
+ We do line endings with CRLF so checking for CR is enough */
+ line_end = memchr(hdbuf, '\r', len);
+ if(!line_end) {
+ result = CURLE_BAD_FUNCTION_ARGUMENT; /* internal error */
+ goto fail;
+ }
+
+ /* Method does not contain spaces */
+ end = memchr(hdbuf, ' ', line_end - hdbuf);
+ if(!end || end == hdbuf)
+ goto fail;
+ nva[0].name = (unsigned char *)":method";
+ nva[0].namelen = strlen((char *)nva[0].name);
+ nva[0].value = (unsigned char *)hdbuf;
+ nva[0].valuelen = (size_t)(end - hdbuf);
+ nva[0].flags = NGHTTP3_NV_FLAG_NONE;
+
+ hdbuf = end + 1;
+
+ /* Path may contain spaces so scan backwards */
+ end = NULL;
+ for(i = (size_t)(line_end - hdbuf); i; --i) {
+ if(hdbuf[i - 1] == ' ') {
+ end = &hdbuf[i - 1];
+ break;
+ }
+ }
+ if(!end || end == hdbuf)
+ goto fail;
+ nva[1].name = (unsigned char *)":path";
+ nva[1].namelen = strlen((char *)nva[1].name);
+ nva[1].value = (unsigned char *)hdbuf;
+ nva[1].valuelen = (size_t)(end - hdbuf);
+ nva[1].flags = NGHTTP3_NV_FLAG_NONE;
+
+ nva[2].name = (unsigned char *)":scheme";
+ nva[2].namelen = strlen((char *)nva[2].name);
+ if(conn->handler->flags & PROTOPT_SSL)
+ nva[2].value = (unsigned char *)"https";
+ else
+ nva[2].value = (unsigned char *)"http";
+ nva[2].valuelen = strlen((char *)nva[2].value);
+ nva[2].flags = NGHTTP3_NV_FLAG_NONE;
+
+
+ authority_idx = 0;
+ i = 3;
+ while(i < nheader) {
+ size_t hlen;
+
+ hdbuf = line_end + 2;
+
+ /* check for next CR, but only within the piece of data left in the given
+ buffer */
+ line_end = memchr(hdbuf, '\r', len - (hdbuf - (char *)mem));
+ if(!line_end || (line_end == hdbuf))
+ goto fail;
+
+ /* header continuation lines are not supported */
+ if(*hdbuf == ' ' || *hdbuf == '\t')
+ goto fail;
+
+ for(end = hdbuf; end < line_end && *end != ':'; ++end)
+ ;
+ if(end == hdbuf || end == line_end)
+ goto fail;
+ hlen = end - hdbuf;
+
+ if(hlen == 4 && strncasecompare("host", hdbuf, 4)) {
+ authority_idx = i;
+ nva[i].name = (unsigned char *)":authority";
+ nva[i].namelen = strlen((char *)nva[i].name);
+ }
+ else {
+ nva[i].name = (unsigned char *)hdbuf;
+ nva[i].namelen = (size_t)(end - hdbuf);
+ }
+ nva[i].flags = NGHTTP3_NV_FLAG_NONE;
+ hdbuf = end + 1;
+ while(*hdbuf == ' ' || *hdbuf == '\t')
+ ++hdbuf;
+ end = line_end;
+
+#if 0 /* This should probably go in more or less like this */
+ switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf,
+ end - hdbuf)) {
+ case HEADERINST_IGNORE:
+ /* skip header fields prohibited by HTTP/2 specification. */
+ --nheader;
+ continue;
+ case HEADERINST_TE_TRAILERS:
+ nva[i].value = (uint8_t*)"trailers";
+ nva[i].value_len = sizeof("trailers") - 1;
+ break;
+ default:
+ nva[i].value = (unsigned char *)hdbuf;
+ nva[i].value_len = (size_t)(end - hdbuf);
+ }
+#endif
+ nva[i].value = (unsigned char *)hdbuf;
+ nva[i].valuelen = (size_t)(end - hdbuf);
+ nva[i].flags = NGHTTP3_NV_FLAG_NONE;
+
+ ++i;
+ }
+
+ /* :authority must come before non-pseudo header fields */
+ if(authority_idx != 0 && authority_idx != AUTHORITY_DST_IDX) {
+ nghttp3_nv authority = nva[authority_idx];
+ for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) {
+ nva[i] = nva[i - 1];
+ }
+ nva[i] = authority;
+ }
+
+ /* Warn stream may be rejected if cumulative length of headers is too
+ large. */
+#define MAX_ACC 60000 /* <64KB to account for some overhead */
+ {
+ size_t acc = 0;
+ for(i = 0; i < nheader; ++i)
+ acc += nva[i].namelen + nva[i].valuelen;
+
+ if(acc > MAX_ACC) {
+ infof(data, "http_request: Warning: The cumulative length of all "
+ "headers exceeds %zu bytes and that could cause the "
+ "stream to be rejected.\n", MAX_ACC);
+ }
+ }
+
+ switch(data->set.httpreq) {
+ case HTTPREQ_POST:
+ case HTTPREQ_POST_FORM:
+ case HTTPREQ_POST_MIME:
+ case HTTPREQ_PUT: {
+ nghttp3_data_reader data_reader;
+ if(data->state.infilesize != -1)
+ stream->upload_left = data->state.infilesize;
+ else
+ /* data sending without specifying the data amount up front */
+ stream->upload_left = -1; /* unknown, but not zero */
+
+ data_reader.read_data = cb_h3_readfunction;
+
+ h3out = calloc(sizeof(struct h3out), 1);
+ if(!h3out) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ stream->h3out = h3out;
+
+ rc = nghttp3_conn_submit_request(qs->h3conn, stream->stream3_id,
+ nva, nheader, &data_reader,
+ conn->data);
+ if(rc) {
+ result = CURLE_SEND_ERROR;
+ goto fail;
+ }
+ break;
+ }
+ default:
+ stream->upload_left = 0; /* nothing left to send */
+ rc = nghttp3_conn_submit_request(qs->h3conn, stream->stream3_id,
+ nva, nheader,
+ NULL, /* no body! */
+ conn->data);
+ if(rc) {
+ result = CURLE_SEND_ERROR;
+ goto fail;
+ }
+ break;
+ }
+
+ Curl_safefree(nva);
+
+ infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)\n",
+ stream3_id, (void *)data);
+
+ return CURLE_OK;
+
+fail:
+ free(nva);
+ return result;
+}
+static ssize_t ngh3_stream_send(struct connectdata *conn,
+ int sockindex,
+ const void *mem,
+ size_t len,
+ CURLcode *curlcode)
+{
+ ssize_t sent;
+ struct quicsocket *qs = conn->quic;
+ curl_socket_t sockfd = conn->sock[sockindex];
+ struct HTTP *stream = conn->data->req.protop;
+
+ if(!stream->h3req) {
+ CURLcode result = http_request(conn, mem, len);
+ if(result) {
+ *curlcode = CURLE_SEND_ERROR;
+ return -1;
+ }
+ sent = len;
+ }
+ else {
+ fprintf(stderr, "ngh3_stream_send() wants to send %zd bytes\n", len);
+ if(!stream->upload_len) {
+ stream->upload_mem = mem;
+ stream->upload_len = len;
+ (void)nghttp3_conn_resume_stream(qs->h3conn, stream->stream3_id);
+ sent = len;
+ }
+ else {
+ *curlcode = CURLE_AGAIN;
+ return -1;
+ }
+ }
+
+ if(ng_flush_egress(conn, sockfd, qs)) {
+ *curlcode = CURLE_SEND_ERROR;
+ return -1;
+ }
+
+ *curlcode = CURLE_OK;
+ return sent;
+}
+
+static void ng_has_connected(struct connectdata *conn, int tempindex)
+{
+ conn->recv[FIRSTSOCKET] = ngh3_stream_recv;
+ conn->send[FIRSTSOCKET] = ngh3_stream_send;
+ conn->handler = &Curl_handler_http3;
+ conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
+ conn->httpversion = 30;
+ conn->bundle->multiuse = BUNDLE_MULTIPLEX;
+ conn->quic = &conn->hequic[tempindex];
+ DEBUGF(infof(conn->data, "ngtcp2 established connection!\n"));
+}
+
+/*
+ * There can be multiple connection attempts going on in parallel.
+ */
+CURLcode Curl_quic_is_connected(struct connectdata *conn,
+ int sockindex,
+ bool *done)
+{
+ CURLcode result;
+ struct quicsocket *qs = &conn->hequic[sockindex];
+ curl_socket_t sockfd = conn->tempsock[sockindex];
+
+ result = ng_process_ingress(conn, sockfd, qs);
+ if(result)
+ return result;
+
+ result = ng_flush_egress(conn, sockfd, qs);
+ if(result)
+ return result;
+
+ if(ngtcp2_conn_get_handshake_completed(qs->qconn)) {
+ *done = TRUE;
+ ng_has_connected(conn, sockindex);
+ }
+
+ return result;
+}
+
+static CURLcode ng_process_ingress(struct connectdata *conn, int sockfd,
+ struct quicsocket *qs)
+{
+ ssize_t recvd;
+ int rv;
+ uint8_t buf[65536];
+ size_t bufsize = sizeof(buf);
+ struct sockaddr_storage remote_addr;
+ socklen_t remote_addrlen;
+ ngtcp2_path path;
+ ngtcp2_tstamp ts = timestamp();
+
+ for(;;) {
+ remote_addrlen = sizeof(remote_addr);
+ while((recvd = recvfrom(sockfd, buf, bufsize, MSG_DONTWAIT,
+ (struct sockaddr *)&remote_addr,
+ &remote_addrlen)) == -1 &&
+ errno == EINTR)
+ ;
+ if(recvd == -1) {
+ if(errno == EAGAIN || errno == EWOULDBLOCK)
+ break;
+
+ failf(conn->data, "ngtcp2: recvfrom() unexpectedly returned %d", recvd);
+ return CURLE_RECV_ERROR;
+ }
+
+ ngtcp2_addr_init(&path.local, (uint8_t *)&qs->local_addr,
+ qs->local_addrlen, NULL);
+ ngtcp2_addr_init(&path.remote, (uint8_t *)&remote_addr, remote_addrlen,
+ NULL);
+
+ rv = ngtcp2_conn_read_pkt(qs->qconn, &path, buf, recvd, ts);
+ if(rv != 0) {
+ /* TODO Send CONNECTION_CLOSE if possible */
+ return CURLE_RECV_ERROR;
+ }
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode ng_flush_egress(struct connectdata *conn, int sockfd,
+ struct quicsocket *qs)
+{
+ int rv;
+ ssize_t sent;
+ ssize_t outlen;
+ uint8_t out[NGTCP2_MAX_PKTLEN_IPV4];
+ size_t pktlen;
+ ngtcp2_path_storage ps;
+ ngtcp2_tstamp ts = timestamp();
+ struct sockaddr_storage remote_addr;
+ ngtcp2_tstamp expiry;
+ ngtcp2_duration timeout;
+ int64_t stream_id;
+ ssize_t veccnt;
+ int fin;
+ nghttp3_vec vec[16];
+ ssize_t ndatalen;
+
+ switch(qs->local_addr.ss_family) {
+ case AF_INET:
+ pktlen = NGTCP2_MAX_PKTLEN_IPV4;
+ break;
+ case AF_INET6:
+ pktlen = NGTCP2_MAX_PKTLEN_IPV6;
+ break;
+ default:
+ assert(0);
+ }
+
+ rv = ngtcp2_conn_handle_expiry(qs->qconn, ts);
+ if(rv != 0) {
+ failf(conn->data, "ngtcp2_conn_handle_expiry returned error: %s\n",
+ ngtcp2_strerror(rv));
+ return CURLE_SEND_ERROR;
+ }
+
+ ngtcp2_path_storage_zero(&ps);
+
+ for(;;) {
+ outlen = -1;
+ if(qs->h3conn && ngtcp2_conn_get_max_data_left(qs->qconn)) {
+ veccnt = nghttp3_conn_writev_stream(qs->h3conn, &stream_id, &fin, vec,
+ sizeof(vec) / sizeof(vec[0]));
+ if(veccnt < 0) {
+ failf(conn->data, "nghttp3_conn_writev_stream returned error: %s\n",
+ nghttp3_strerror((int)veccnt));
+ return CURLE_SEND_ERROR;
+ }
+ else if(veccnt > 0) {
+ outlen =
+ ngtcp2_conn_writev_stream(qs->qconn, &ps.path,
+ out, pktlen, &ndatalen,
+ NGTCP2_WRITE_STREAM_FLAG_MORE,
+ stream_id, fin,
+ (const ngtcp2_vec *)vec, veccnt, ts);
+ if(outlen == 0) {
+ break;
+ }
+ if(outlen < 0) {
+ if(outlen == NGTCP2_ERR_STREAM_DATA_BLOCKED ||
+ outlen == NGTCP2_ERR_STREAM_SHUT_WR) {
+ rv = nghttp3_conn_block_stream(qs->h3conn, stream_id);
+ if(rv != 0) {
+ failf(conn->data,
+ "nghttp3_conn_block_stream returned error: %s\n",
+ nghttp3_strerror(rv));
+ return CURLE_SEND_ERROR;
+ }
+ continue;
+ }
+ else if(outlen == NGTCP2_ERR_WRITE_STREAM_MORE) {
+ assert(ndatalen > 0);
+ rv = nghttp3_conn_add_write_offset(qs->h3conn, stream_id,
+ ndatalen);
+ if(rv != 0) {
+ failf(conn->data,
+ "nghttp3_conn_add_write_offset returned error: %s\n",
+ nghttp3_strerror(rv));
+ return CURLE_SEND_ERROR;
+ }
+ continue;
+ }
+ else {
+ failf(conn->data, "ngtcp2_conn_writev_stream returned error: %s\n",
+ ngtcp2_strerror((int)outlen));
+ return CURLE_SEND_ERROR;
+ }
+ }
+ else if(ndatalen > 0) {
+ rv = nghttp3_conn_add_write_offset(qs->h3conn, stream_id, ndatalen);
+ if(rv != 0) {
+ failf(conn->data,
+ "nghttp3_conn_add_write_offset returned error: %s\n",
+ nghttp3_strerror(rv));
+ return CURLE_SEND_ERROR;
+ }
+ }
+ }
+ }
+ if(outlen < 0) {
+ outlen = ngtcp2_conn_write_pkt(qs->qconn, &ps.path, out, pktlen, ts);
+ if(outlen < 0) {
+ failf(conn->data, "ngtcp2_conn_write_pkt returned error: %s\n",
+ ngtcp2_strerror((int)outlen));
+ return CURLE_SEND_ERROR;
+ }
+ if(outlen == 0)
+ break;
+ }
+
+ memcpy(&remote_addr, ps.path.remote.addr, ps.path.remote.addrlen);
+ while((sent = sendto(sockfd, out, outlen, MSG_DONTWAIT,
+ (struct sockaddr *)&remote_addr,
+ (socklen_t)ps.path.remote.addrlen)) == -1 &&
+ errno == EINTR)
+ ;
+
+ if(sent == -1) {
+ if(errno == EAGAIN || errno == EWOULDBLOCK) {
+ /* TODO Cache packet */
+ break;
+ }
+ else {
+ failf(conn->data, "sendto() returned %zd (errno %d)\n", sent,
+ SOCKERRNO);
+ return CURLE_SEND_ERROR;
+ }
+ }
+ }
+
+ expiry = ngtcp2_conn_get_expiry(qs->qconn);
+ if(expiry != UINT64_MAX) {
+ if(expiry <= ts) {
+ timeout = NGTCP2_MILLISECONDS;
+ }
+ else {
+ timeout = expiry - ts;
+ }
+ Curl_expire(conn->data, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC);
+ }
+
+ return CURLE_OK;
+}
+
+/*
+ * Called from transfer.c:done_sending when we stop HTTP/3 uploading.
+ */
+CURLcode Curl_quic_done_sending(struct connectdata *conn)
+{
+ if(conn->handler == &Curl_handler_http3) {
+ /* only for HTTP/3 transfers */
+ struct HTTP *stream = conn->data->req.protop;
+ struct quicsocket *qs = conn->quic;
+ fprintf(stderr, "!!! Curl_quic_done_sending stream %zu\n",
+ stream->stream3_id);
+ stream->upload_done = TRUE;
+ (void)nghttp3_conn_resume_stream(qs->h3conn, stream->stream3_id);
+ }
+
+ return CURLE_OK;
+}
+#endif
diff --git a/lib/vquic/ngtcp2.h b/lib/vquic/ngtcp2.h
new file mode 100644
index 000000000..5570fc7e7
--- /dev/null
+++ b/lib/vquic/ngtcp2.h
@@ -0,0 +1,63 @@
+#ifndef HEADER_CURL_VQUIC_NGTCP2_H
+#define HEADER_CURL_VQUIC_NGTCP2_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed 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.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_NGTCP2
+
+#include <ngtcp2/ngtcp2.h>
+#include <nghttp3/nghttp3.h>
+#include <openssl/ssl.h>
+
+struct quic_handshake {
+ char *buf; /* pointer to the buffer */
+ size_t alloclen; /* size of allocation */
+ size_t len; /* size of content in buffer */
+ size_t nread; /* how many bytes have been read */
+};
+
+struct quicsocket {
+ struct connectdata *conn; /* point back to the connection */
+ ngtcp2_conn *qconn;
+ ngtcp2_cid dcid;
+ ngtcp2_cid scid;
+ uint32_t version;
+ ngtcp2_settings settings;
+ SSL_CTX *sslctx;
+ SSL *ssl;
+ struct quic_handshake client_crypto_data[3];
+ /* the last TLS alert description generated by the local endpoint */
+ uint8_t tls_alert;
+ struct sockaddr_storage local_addr;
+ socklen_t local_addrlen;
+
+ nghttp3_conn *h3conn;
+ nghttp3_conn_settings h3settings;
+};
+
+#include "urldata.h"
+
+#endif
+
+#endif /* HEADER_CURL_VQUIC_NGTCP2_H */
diff --git a/lib/vquic/quiche.c b/lib/vquic/quiche.c
new file mode 100644
index 000000000..7f9b34a1e
--- /dev/null
+++ b/lib/vquic/quiche.c
@@ -0,0 +1,780 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed 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.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_QUICHE
+#include <quiche.h>
+#include <openssl/err.h>
+#include "urldata.h"
+#include "sendf.h"
+#include "strdup.h"
+#include "rand.h"
+#include "quic.h"
+#include "strcase.h"
+#include "multiif.h"
+#include "connect.h"
+#include "strerror.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#define DEBUG_HTTP3
+/* #define DEBUG_QUICHE */
+#ifdef DEBUG_HTTP3
+#define H3BUGF(x) x
+#else
+#define H3BUGF(x) do { } WHILE_FALSE
+#endif
+
+#define QUIC_MAX_STREAMS (256*1024)
+#define QUIC_MAX_DATA (1*1024*1024)
+#define QUIC_IDLE_TIMEOUT 60 * 1000 /* milliseconds */
+
+static CURLcode process_ingress(struct connectdata *conn,
+ curl_socket_t sockfd,
+ struct quicsocket *qs);
+
+static CURLcode flush_egress(struct connectdata *conn, curl_socket_t sockfd,
+ struct quicsocket *qs);
+
+static CURLcode http_request(struct connectdata *conn, const void *mem,
+ size_t len);
+static Curl_recv h3_stream_recv;
+static Curl_send h3_stream_send;
+
+
+static int quiche_getsock(struct connectdata *conn, curl_socket_t *socks)
+{
+ struct SingleRequest *k = &conn->data->req;
+ int bitmap = GETSOCK_BLANK;
+
+ socks[0] = conn->sock[FIRSTSOCKET];
+
+ /* in a HTTP/2 connection we can basically always get a frame so we should
+ always be ready for one */
+ bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
+
+ /* we're still uploading or the HTTP/2 layer wants to send data */
+ if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND)
+ bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
+
+ return bitmap;
+}
+
+static int quiche_perform_getsock(const struct connectdata *conn,
+ curl_socket_t *socks)
+{
+ return quiche_getsock((struct connectdata *)conn, socks);
+}
+
+static CURLcode quiche_disconnect(struct connectdata *conn,
+ bool dead_connection)
+{
+ struct quicsocket *qs = conn->quic;
+ (void)dead_connection;
+ quiche_h3_config_free(qs->h3config);
+ quiche_h3_conn_free(qs->h3c);
+ quiche_config_free(qs->cfg);
+ quiche_conn_free(qs->conn);
+ return CURLE_OK;
+}
+
+static unsigned int quiche_conncheck(struct connectdata *conn,
+ unsigned int checks_to_perform)
+{
+ (void)conn;
+ (void)checks_to_perform;
+ return CONNRESULT_NONE;
+}
+
+static CURLcode quiche_do(struct connectdata *conn, bool *done)
+{
+ struct HTTP *stream = conn->data->req.protop;
+ stream->h3req = FALSE; /* not sent */
+ return Curl_http(conn, done);
+}
+
+static const struct Curl_handler Curl_handler_http3 = {
+ "HTTPS", /* scheme */
+ ZERO_NULL, /* setup_connection */
+ quiche_do, /* do_it */
+ Curl_http_done, /* done */
+ ZERO_NULL, /* do_more */
+ ZERO_NULL, /* connect_it */
+ ZERO_NULL, /* connecting */
+ ZERO_NULL, /* doing */
+ quiche_getsock, /* proto_getsock */
+ quiche_getsock, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ quiche_perform_getsock, /* perform_getsock */
+ quiche_disconnect, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ quiche_conncheck, /* connection_check */
+ PORT_HTTP, /* defport */
+ CURLPROTO_HTTPS, /* protocol */
+ PROTOPT_SSL | PROTOPT_STREAM /* flags */
+};
+
+#ifdef DEBUG_QUICHE
+static void quiche_debug_log(const char *line, void *argp)
+{
+ (void)argp;
+ fprintf(stderr, "%s\n", line);
+}
+#endif
+
+CURLcode Curl_quic_connect(struct connectdata *conn, curl_socket_t sockfd,
+ int sockindex,
+ const struct sockaddr *addr, socklen_t addrlen)
+{
+ CURLcode result;
+ struct quicsocket *qs = &conn->hequic[sockindex];
+ struct Curl_easy *data = conn->data;
+
+#ifdef DEBUG_QUICHE
+ /* initialize debug log callback only once */
+ static int debug_log_init = 0;
+ if(!debug_log_init) {
+ quiche_enable_debug_logging(quiche_debug_log, NULL);
+ debug_log_init = 1;
+ }
+#endif
+
+ (void)addr;
+ (void)addrlen;
+
+ qs->cfg = quiche_config_new(QUICHE_PROTOCOL_VERSION);
+ if(!qs->cfg) {
+ failf(data, "can't create quiche config");
+ return CURLE_FAILED_INIT;
+ }
+
+ quiche_config_set_idle_timeout(qs->cfg, QUIC_IDLE_TIMEOUT);
+ quiche_config_set_initial_max_data(qs->cfg, QUIC_MAX_DATA);
+ quiche_config_set_initial_max_stream_data_bidi_local(qs->cfg, QUIC_MAX_DATA);
+ quiche_config_set_initial_max_stream_data_bidi_remote(qs->cfg,
+ QUIC_MAX_DATA);
+ quiche_config_set_initial_max_stream_data_uni(qs->cfg, QUIC_MAX_DATA);
+ quiche_config_set_initial_max_streams_bidi(qs->cfg, QUIC_MAX_STREAMS);
+ quiche_config_set_initial_max_streams_uni(qs->cfg, QUIC_MAX_STREAMS);
+ quiche_config_set_application_protos(qs->cfg,
+ (uint8_t *)
+ QUICHE_H3_APPLICATION_PROTOCOL,
+ sizeof(QUICHE_H3_APPLICATION_PROTOCOL)
+ - 1);
+
+ result = Curl_rand(data, qs->scid, sizeof(qs->scid));
+ if(result)
+ return result;
+
+ if(getenv("SSLKEYLOGFILE"))
+ quiche_config_log_keys(qs->cfg);
+
+ qs->conn = quiche_connect(conn->host.name, (const uint8_t *) qs->scid,
+ sizeof(qs->scid), qs->cfg);
+ if(!qs->conn) {
+ failf(data, "can't create quiche connection");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ result = flush_egress(conn, sockfd, qs);
+ if(result)
+ return result;
+
+#if 0
+ /* store the used address as a string */
+ if(!Curl_addr2string((struct sockaddr*)addr,
+ conn->primary_ip, &conn->primary_port)) {
+ char buffer[STRERROR_LEN];
+ failf(data, "ssrem inet_ntop() failed with errno %d: %s",
+ errno, Curl_strerror(errno, buffer, sizeof(buffer)));
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+ memcpy(conn->ip_addr_str, conn->primary_ip, MAX_IPADR_LEN);
+#endif
+ /* for connection reuse purposes: */
+ conn->ssl[FIRSTSOCKET].state = ssl_connection_complete;
+
+ infof(data, "Sent QUIC client Initial, ALPN: %s\n",
+ QUICHE_H3_APPLICATION_PROTOCOL + 1);
+
+ return CURLE_OK;
+}
+
+static CURLcode quiche_has_connected(struct connectdata *conn,
+ int sockindex,
+ int tempindex)
+{
+ CURLcode result;
+ struct quicsocket *qs = conn->quic = &conn->hequic[tempindex];
+
+ conn->recv[sockindex] = h3_stream_recv;
+ conn->send[sockindex] = h3_stream_send;
+ conn->handler = &Curl_handler_http3;
+ conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
+ conn->httpversion = 30;
+ conn->bundle->multiuse = BUNDLE_MULTIPLEX;
+
+ qs->h3config = quiche_h3_config_new(0, 1024, 0, 0);
+ if(!qs->h3config)
+ return CURLE_OUT_OF_MEMORY;
+
+ /* Create a new HTTP/3 connection on the QUIC connection. */
+ qs->h3c = quiche_h3_conn_new_with_transport(qs->conn, qs->h3config);
+ if(!qs->h3c) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+ if(conn->hequic[1-tempindex].cfg) {
+ qs = &conn->hequic[1-tempindex];
+ quiche_config_free(qs->cfg);
+ quiche_conn_free(qs->conn);
+ qs->cfg = NULL;
+ qs->conn = NULL;
+ }
+ return CURLE_OK;
+ fail:
+ quiche_h3_config_free(qs->h3config);
+ quiche_h3_conn_free(qs->h3c);
+ return result;
+}
+
+/*
+ * This function gets polled to check if this QUIC connection has connected.
+ */
+CURLcode Curl_quic_is_connected(struct connectdata *conn, int sockindex,
+ bool *done)
+{
+ CURLcode result;
+ struct quicsocket *qs = &conn->hequic[sockindex];
+ curl_socket_t sockfd = conn->tempsock[sockindex];
+
+ result = process_ingress(conn, sockfd, qs);
+ if(result)
+ return result;
+
+ result = flush_egress(conn, sockfd, qs);
+ if(result)
+ return result;
+
+ if(quiche_conn_is_established(qs->conn)) {
+ *done = TRUE;
+ result = quiche_has_connected(conn, 0, sockindex);
+ DEBUGF(infof(conn->data, "quiche established connection!\n"));
+ }
+
+ return result;
+}
+
+static CURLcode process_ingress(struct connectdata *conn, int sockfd,
+ struct quicsocket *qs)
+{
+ ssize_t recvd;
+ struct Curl_easy *data = conn->data;
+ uint8_t *buf = (uint8_t *)data->state.buffer;
+ size_t bufsize = data->set.buffer_size;
+
+ /* in case the timeout expired */
+ quiche_conn_on_timeout(qs->conn);
+
+ do {
+ recvd = recv(sockfd, buf, bufsize, 0);
+ if((recvd < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK)))
+ break;
+
+ if(recvd < 0) {
+ failf(conn->data, "quiche: recv() unexpectedly returned %d "
+ "(errno: %d, socket %d)", recvd, SOCKERRNO, sockfd);
+ return CURLE_RECV_ERROR;
+ }
+
+ recvd = quiche_conn_recv(qs->conn, buf, recvd);
+ if(recvd == QUICHE_ERR_DONE)
+ break;
+
+ if(recvd < 0) {
+ failf(conn->data, "quiche_conn_recv() == %d", recvd);
+ return CURLE_RECV_ERROR;
+ }
+ } while(1);
+
+ return CURLE_OK;
+}
+
+/*
+ * flush_egress drains the buffers and sends off data.
+ * Calls failf() on errors.
+ */
+static CURLcode flush_egress(struct connectdata *conn, int sockfd,
+ struct quicsocket *qs)
+{
+ ssize_t sent;
+ static uint8_t out[1200];
+ int64_t timeout_ns;
+
+ do {
+ sent = quiche_conn_send(qs->conn, out, sizeof(out));
+ if(sent == QUICHE_ERR_DONE)
+ break;
+
+ if(sent < 0) {
+ failf(conn->data, "quiche_conn_send returned %zd\n",
+ sent);
+ return CURLE_SEND_ERROR;
+ }
+
+ sent = send(sockfd, out, sent, 0);
+ if(sent < 0) {
+ failf(conn->data, "send() returned %zd\n", sent);
+ return CURLE_SEND_ERROR;
+ }
+ } while(1);
+
+ /* time until the next timeout event, as nanoseconds. */
+ timeout_ns = quiche_conn_timeout_as_nanos(qs->conn);
+ if(timeout_ns)
+ /* expire uses milliseconds */
+ Curl_expire(conn->data, (timeout_ns + 999999) / 1000000, EXPIRE_QUIC);
+
+ return CURLE_OK;
+}
+
+struct h3h1header {
+ char *dest;
+ size_t destlen; /* left to use */
+ size_t nlen; /* used */
+};
+
+static int cb_each_header(uint8_t *name, size_t name_len,
+ uint8_t *value, size_t value_len,
+ void *argp)
+{
+ struct h3h1header *headers = (struct h3h1header *)argp;
+ size_t olen = 0;
+
+ if((name_len == 7) && !strncmp(":status", (char *)name, 7)) {
+ msnprintf(headers->dest,
+ headers->destlen, "HTTP/3 %.*s\n",
+ (int) value_len, value);
+ }
+ else {
+ msnprintf(headers->dest,
+ headers->destlen, "%.*s: %.*s\n",
+ (int)name_len, name, (int) value_len, value);
+ }
+ olen = strlen(headers->dest);
+ headers->destlen -= olen;
+ headers->nlen += olen;
+ headers->dest += olen;
+ return 0;
+}
+
+static ssize_t h3_stream_recv(struct connectdata *conn,
+ int sockindex,
+ char *buf,
+ size_t buffersize,
+ CURLcode *curlcode)
+{
+ ssize_t recvd = -1;
+ ssize_t rcode;
+ struct quicsocket *qs = conn->quic;
+ curl_socket_t sockfd = conn->sock[sockindex];
+ quiche_h3_event *ev;
+ int rc;
+ struct h3h1header headers;
+ struct HTTP *stream = conn->data->req.protop;
+ headers.dest = buf;
+ headers.destlen = buffersize;
+ headers.nlen = 0;
+
+ if(process_ingress(conn, sockfd, qs)) {
+ infof(conn->data, "h3_stream_recv returns on ingress\n");
+ *curlcode = CURLE_RECV_ERROR;
+ return -1;
+ }
+
+ while(recvd < 0) {
+ int64_t s = quiche_h3_conn_poll(qs->h3c, qs->conn, &ev);
+ if(s < 0)
+ /* nothing more to do */
+ break;
+
+ if(s != stream->stream3_id) {
+ /* another transfer, ignore for now */
+ infof(conn->data, "Got h3 for stream %u, expects %u\n",
+ s, stream->stream3_id);
+ continue;
+ }
+
+ switch(quiche_h3_event_type(ev)) {
+ case QUICHE_H3_EVENT_HEADERS:
+ rc = quiche_h3_event_for_each_header(ev, cb_each_header, &headers);
+ if(rc) {
+ /* what do we do about this? */
+ }
+ recvd = headers.nlen;
+ break;
+ case QUICHE_H3_EVENT_DATA:
+ if(!stream->firstbody) {
+ /* add a header-body separator CRLF */
+ buf[0] = '\r';
+ buf[1] = '\n';
+ buf += 2;
+ buffersize -= 2;
+ stream->firstbody = TRUE;
+ recvd = 2; /* two bytes already */
+ }
+ else
+ recvd = 0;
+ rcode = quiche_h3_recv_body(qs->h3c, qs->conn, s, (unsigned char *)buf,
+ buffersize);
+ if(rcode <= 0) {
+ recvd = -1;
+ break;
+ }
+ recvd += rcode;
+ break;
+
+ case QUICHE_H3_EVENT_FINISHED:
+ if(quiche_conn_close(qs->conn, true, 0, NULL, 0) < 0) {
+ ;
+ }
+ recvd = 0; /* end of stream */
+ break;
+ default:
+ break;
+ }
+
+ quiche_h3_event_free(ev);
+ }
+ if(flush_egress(conn, sockfd, qs)) {
+ *curlcode = CURLE_SEND_ERROR;
+ return -1;
+ }
+
+ *curlcode = (-1 == recvd)? CURLE_AGAIN : CURLE_OK;
+ if(recvd >= 0)
+ /* Get this called again to drain the event queue */
+ Curl_expire(conn->data, 0, EXPIRE_QUIC);
+ return recvd;
+}
+
+static ssize_t h3_stream_send(struct connectdata *conn,
+ int sockindex,
+ const void *mem,
+ size_t len,
+ CURLcode *curlcode)
+{
+ ssize_t sent;
+ struct quicsocket *qs = conn->quic;
+ curl_socket_t sockfd = conn->sock[sockindex];
+ struct HTTP *stream = conn->data->req.protop;
+
+ if(!stream->h3req) {
+ CURLcode result = http_request(conn, mem, len);
+ if(result) {
+ *curlcode = CURLE_SEND_ERROR;
+ return -1;
+ }
+ sent = len;
+ }
+ else {
+ H3BUGF(infof(conn->data, "Pass on %zd body bytes to quiche\n",
+ len));
+ sent = quiche_h3_send_body(qs->h3c, qs->conn, stream->stream3_id,
+ (uint8_t *)mem, len, FALSE);
+ if(sent < 0) {
+ *curlcode = CURLE_SEND_ERROR;
+ return -1;
+ }
+ }
+
+ if(flush_egress(conn, sockfd, qs)) {
+ *curlcode = CURLE_SEND_ERROR;
+ return -1;
+ }
+
+ *curlcode = CURLE_OK;
+ return sent;
+}
+
+/*
+ * Store quiche version info in this buffer, Prefix with a space. Return total
+ * length written.
+ */
+int Curl_quic_ver(char *p, size_t len)
+{
+ return msnprintf(p, len, " quiche/%s", quiche_version());
+}
+
+/* Index where :authority header field will appear in request header
+ field list. */
+#define AUTHORITY_DST_IDX 3
+
+static CURLcode http_request(struct connectdata *conn, const void *mem,
+ size_t len)
+{
+ /*
+ */
+ struct HTTP *stream = conn->data->req.protop;
+ size_t nheader;
+ size_t i;
+ size_t authority_idx;
+ char *hdbuf = (char *)mem;
+ char *end, *line_end;
+ int64_t stream3_id;
+ quiche_h3_header *nva = NULL;
+ struct quicsocket *qs = conn->quic;
+ CURLcode result = CURLE_OK;
+ struct Curl_easy *data = conn->data;
+
+ stream->h3req = TRUE; /* senf off! */
+
+ /* Calculate number of headers contained in [mem, mem + len). Assumes a
+ correctly generated HTTP header field block. */
+ nheader = 0;
+ for(i = 1; i < len; ++i) {
+ if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') {
+ ++nheader;
+ ++i;
+ }
+ }
+ if(nheader < 2)
+ goto fail;
+
+ /* We counted additional 2 \r\n in the first and last line. We need 3
+ new headers: :method, :path and :scheme. Therefore we need one
+ more space. */
+ nheader += 1;
+ nva = malloc(sizeof(quiche_h3_header) * nheader);
+ if(!nva) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto fail;
+ }
+
+ /* Extract :method, :path from request line
+ We do line endings with CRLF so checking for CR is enough */
+ line_end = memchr(hdbuf, '\r', len);
+ if(!line_end) {
+ result = CURLE_BAD_FUNCTION_ARGUMENT; /* internal error */
+ goto fail;
+ }
+
+ /* Method does not contain spaces */
+ end = memchr(hdbuf, ' ', line_end - hdbuf);
+ if(!end || end == hdbuf)
+ goto fail;
+ nva[0].name = (unsigned char *)":method";
+ nva[0].name_len = strlen((char *)nva[0].name);
+ nva[0].value = (unsigned char *)hdbuf;
+ nva[0].value_len = (size_t)(end - hdbuf);
+
+ hdbuf = end + 1;
+
+ /* Path may contain spaces so scan backwards */
+ end = NULL;
+ for(i = (size_t)(line_end - hdbuf); i; --i) {
+ if(hdbuf[i - 1] == ' ') {
+ end = &hdbuf[i - 1];
+ break;
+ }
+ }
+ if(!end || end == hdbuf)
+ goto fail;
+ nva[1].name = (unsigned char *)":path";
+ nva[1].name_len = strlen((char *)nva[1].name);
+ nva[1].value = (unsigned char *)hdbuf;
+ nva[1].value_len = (size_t)(end - hdbuf);
+
+ nva[2].name = (unsigned char *)":scheme";
+ nva[2].name_len = strlen((char *)nva[2].name);
+ if(conn->handler->flags & PROTOPT_SSL)
+ nva[2].value = (unsigned char *)"https";
+ else
+ nva[2].value = (unsigned char *)"http";
+ nva[2].value_len = strlen((char *)nva[2].value);
+
+
+ authority_idx = 0;
+ i = 3;
+ while(i < nheader) {
+ size_t hlen;
+
+ hdbuf = line_end + 2;
+
+ /* check for next CR, but only within the piece of data left in the given
+ buffer */
+ line_end = memchr(hdbuf, '\r', len - (hdbuf - (char *)mem));
+ if(!line_end || (line_end == hdbuf))
+ goto fail;
+
+ /* header continuation lines are not supported */
+ if(*hdbuf == ' ' || *hdbuf == '\t')
+ goto fail;
+
+ for(end = hdbuf; end < line_end && *end != ':'; ++end)
+ ;
+ if(end == hdbuf || end == line_end)
+ goto fail;
+ hlen = end - hdbuf;
+
+ if(hlen == 4 && strncasecompare("host", hdbuf, 4)) {
+ authority_idx = i;
+ nva[i].name = (unsigned char *)":authority";
+ nva[i].name_len = strlen((char *)nva[i].name);
+ }
+ else {
+ nva[i].name = (unsigned char *)hdbuf;
+ nva[i].name_len = (size_t)(end - hdbuf);
+ }
+ hdbuf = end + 1;
+ while(*hdbuf == ' ' || *hdbuf == '\t')
+ ++hdbuf;
+ end = line_end;
+
+#if 0 /* This should probably go in more or less like this */
+ switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf,
+ end - hdbuf)) {
+ case HEADERINST_IGNORE:
+ /* skip header fields prohibited by HTTP/2 specification. */
+ --nheader;
+ continue;
+ case HEADERINST_TE_TRAILERS:
+ nva[i].value = (uint8_t*)"trailers";
+ nva[i].value_len = sizeof("trailers") - 1;
+ break;
+ default:
+ nva[i].value = (unsigned char *)hdbuf;
+ nva[i].value_len = (size_t)(end - hdbuf);
+ }
+#endif
+ nva[i].value = (unsigned char *)hdbuf;
+ nva[i].value_len = (size_t)(end - hdbuf);
+
+ ++i;
+ }
+
+ /* :authority must come before non-pseudo header fields */
+ if(authority_idx != 0 && authority_idx != AUTHORITY_DST_IDX) {
+ quiche_h3_header authority = nva[authority_idx];
+ for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) {
+ nva[i] = nva[i - 1];
+ }
+ nva[i] = authority;
+ }
+
+ /* Warn stream may be rejected if cumulative length of headers is too
+ large. */
+#define MAX_ACC 60000 /* <64KB to account for some overhead */
+ {
+ size_t acc = 0;
+
+ for(i = 0; i < nheader; ++i) {
+ acc += nva[i].name_len + nva[i].value_len;
+
+ H3BUGF(infof(data, "h3 [%.*s: %.*s]\n",
+ nva[i].name_len, nva[i].name,
+ nva[i].value_len, nva[i].value));
+ }
+
+ if(acc > MAX_ACC) {
+ infof(data, "http_request: Warning: The cumulative length of all "
+ "headers exceeds %zu bytes and that could cause the "
+ "stream to be rejected.\n", MAX_ACC);
+ }
+ }
+
+ switch(data->set.httpreq) {
+ case HTTPREQ_POST:
+ case HTTPREQ_POST_FORM:
+ case HTTPREQ_POST_MIME:
+ case HTTPREQ_PUT:
+ if(data->state.infilesize != -1)
+ stream->upload_left = data->state.infilesize;
+ else
+ /* data sending without specifying the data amount up front */
+ stream->upload_left = -1; /* unknown, but not zero */
+
+ stream3_id = quiche_h3_send_request(qs->h3c, qs->conn, nva, nheader,
+ stream->upload_left ? FALSE: TRUE);
+ if((stream3_id >= 0) && data->set.postfields) {
+ ssize_t sent = quiche_h3_send_body(qs->h3c, qs->conn, stream3_id,
+ (uint8_t *)data->set.postfields,
+ stream->upload_left, TRUE);
+ if(sent <= 0) {
+ failf(data, "quiche_h3_send_body failed!");
+ result = CURLE_SEND_ERROR;
+ }
+ stream->upload_left = 0; /* nothing left to send */
+ }
+ break;
+ default:
+ stream3_id = quiche_h3_send_request(qs->h3c, qs->conn, nva, nheader,
+ TRUE);
+ break;
+ }
+
+ Curl_safefree(nva);
+
+ if(stream3_id < 0) {
+ H3BUGF(infof(data, "quiche_h3_send_request returned %d\n",
+ stream3_id));
+ result = CURLE_SEND_ERROR;
+ goto fail;
+ }
+
+ infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)\n",
+ stream3_id, (void *)data);
+ stream->stream3_id = stream3_id;
+
+ return CURLE_OK;
+
+fail:
+ free(nva);
+ return result;
+}
+
+/*
+ * Called from transfer.c:done_sending when we stop HTTP/3 uploading.
+ */
+CURLcode Curl_quic_done_sending(struct connectdata *conn)
+{
+ if(conn->handler == &Curl_handler_http3) {
+ /* only for HTTP/3 transfers */
+ ssize_t sent;
+ struct HTTP *stream = conn->data->req.protop;
+ struct quicsocket *qs = conn->quic;
+ fprintf(stderr, "!!! Curl_quic_done_sending\n");
+ stream->upload_done = TRUE;
+ sent = quiche_h3_send_body(qs->h3c, qs->conn, stream->stream3_id,
+ NULL, 0, TRUE);
+ if(sent < 0)
+ return CURLE_SEND_ERROR;
+ }
+
+ return CURLE_OK;
+}
+
+#endif
diff --git a/lib/vquic/quiche.h b/lib/vquic/quiche.h
new file mode 100644
index 000000000..c8d1837b5
--- /dev/null
+++ b/lib/vquic/quiche.h
@@ -0,0 +1,49 @@
+#ifndef HEADER_CURL_VQUIC_QUICHE_H
+#define HEADER_CURL_VQUIC_QUICHE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed 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.
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_QUICHE
+
+#include <quiche.h>
+
+struct quic_handshake {
+ char *buf; /* pointer to the buffer */
+ size_t alloclen; /* size of allocation */
+ size_t len; /* size of content in buffer */
+ size_t nread; /* how many bytes have been read */
+};
+
+struct quicsocket {
+ quiche_config *cfg;
+ quiche_conn *conn;
+ quiche_h3_conn *h3c;
+ quiche_h3_config *h3config;
+ uint8_t scid[QUICHE_MAX_CONN_ID_LEN];
+ uint32_t version;
+};
+
+#endif
+
+#endif /* HEADER_CURL_VQUIC_QUICHE_H */
diff --git a/lib/ssh-libssh.c b/lib/vssh/libssh.c
index 4b6f60491..7b42b5578 100644
--- a/lib/ssh-libssh.c
+++ b/lib/vssh/libssh.c
@@ -126,13 +126,9 @@ CURLcode sftp_perform(struct connectdata *conn,
static void sftp_quote(struct connectdata *conn);
static void sftp_quote_stat(struct connectdata *conn);
-
-static int myssh_getsock(struct connectdata *conn, curl_socket_t *sock,
- int numsocks);
-
+static int myssh_getsock(struct connectdata *conn, curl_socket_t *sock);
static int myssh_perform_getsock(const struct connectdata *conn,
- curl_socket_t *sock,
- int numsocks);
+ curl_socket_t *sock);
static CURLcode myssh_setup_connection(struct connectdata *conn);
@@ -1119,7 +1115,7 @@ static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block)
flags = O_WRONLY|O_APPEND;
else
/* Clear file before writing (normal behaviour) */
- flags = O_WRONLY|O_APPEND|O_CREAT|O_TRUNC;
+ flags = O_WRONLY|O_CREAT|O_TRUNC;
if(sshc->sftp_file)
sftp_close(sshc->sftp_file);
@@ -1913,13 +1909,9 @@ static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block)
/* 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_perform_getsock(const struct connectdata *conn,
- curl_socket_t *sock, /* points to numsocks
- number of sockets */
- int numsocks)
+ curl_socket_t *sock)
{
int bitmap = GETSOCK_BLANK;
- (void) numsocks;
-
sock[0] = conn->sock[FIRSTSOCKET];
if(conn->waitfor & KEEP_RECV)
@@ -1934,13 +1926,11 @@ static int myssh_perform_getsock(const struct connectdata *conn,
/* 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 myssh_getsock(struct connectdata *conn,
- curl_socket_t *sock, /* points to numsocks
- number of sockets */
- int numsocks)
+ curl_socket_t *sock)
{
/* if we know the direction we can use the generic *_getsock() function even
for the protocol_connect and doing states */
- return myssh_perform_getsock(conn, sock, numsocks);
+ return myssh_perform_getsock(conn, sock);
}
static void myssh_block2waitfor(struct connectdata *conn, bool block)
@@ -2735,5 +2725,23 @@ static void sftp_quote_stat(struct connectdata *conn)
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();
+}
+
+size_t Curl_ssh_version(char *buffer, size_t buflen)
+{
+ return msnprintf(buffer, buflen, "libssh/%s", CURL_LIBSSH_VERSION);
+}
#endif /* USE_LIBSSH */
diff --git a/lib/ssh.c b/lib/vssh/libssh2.c
index 267080f12..b9cf0c808 100644
--- a/lib/ssh.c
+++ b/lib/vssh/libssh2.c
@@ -125,17 +125,9 @@ static
CURLcode sftp_perform(struct connectdata *conn,
bool *connected,
bool *dophase_done);
-
-static int ssh_getsock(struct connectdata *conn,
- curl_socket_t *sock, /* points to numsocks number
- of sockets */
- int numsocks);
-
+static int ssh_getsock(struct connectdata *conn, curl_socket_t *sock);
static int ssh_perform_getsock(const struct connectdata *conn,
- curl_socket_t *sock, /* points to numsocks
- number of sockets */
- int numsocks);
-
+ curl_socket_t *sock);
static CURLcode ssh_setup_connection(struct connectdata *conn);
/*
@@ -2700,13 +2692,10 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
/* 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_perform_getsock(const struct connectdata *conn,
- curl_socket_t *sock, /* points to numsocks
- number of sockets */
- int numsocks)
+ curl_socket_t *sock)
{
#ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
int bitmap = GETSOCK_BLANK;
- (void)numsocks;
sock[0] = conn->sock[FIRSTSOCKET];
@@ -2720,28 +2709,25 @@ static int ssh_perform_getsock(const struct connectdata *conn,
#else
/* if we don't know the direction we can use the generic *_getsock()
function even for the protocol_connect and doing states */
- return Curl_single_getsock(conn, sock, numsocks);
+ return Curl_single_getsock(conn, sock);
#endif
}
/* 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 ssh_getsock(struct connectdata *conn,
- curl_socket_t *sock, /* points to numsocks number
- of sockets */
- int numsocks)
+ curl_socket_t *sock)
{
#ifndef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
(void)conn;
(void)sock;
- (void)numsocks;
/* if we don't know any direction we can just play along as we used to and
not provide any sensible info */
return GETSOCK_BLANK;
#else
/* if we know the direction we can use the generic *_getsock() function even
for the protocol_connect and doing states */
- return ssh_perform_getsock(conn, sock, numsocks);
+ return ssh_perform_getsock(conn, sock);
#endif
}
@@ -3334,4 +3320,27 @@ static const char *sftp_libssh2_strerror(int err)
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
+}
+
+size_t Curl_ssh_version(char *buffer, size_t buflen)
+{
+ return msnprintf(buffer, buflen, "libssh2/%s", LIBSSH2_VERSION);
+}
+
#endif /* USE_LIBSSH2 */
diff --git a/lib/vtls/mesalink.c b/lib/vtls/mesalink.c
index 7ca4f0eac..9507888bd 100644
--- a/lib/vtls/mesalink.c
+++ b/lib/vtls/mesalink.c
@@ -73,6 +73,17 @@ struct ssl_backend_data
static Curl_recv mesalink_recv;
static Curl_send mesalink_send;
+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;
+}
+
/*
* This function loads all the client/CA certificates and CRLs. Setup the TLS
* layer and do all necessary magic.
@@ -83,9 +94,6 @@ mesalink_connect_step1(struct connectdata *conn, int sockindex)
char *ciphers;
struct Curl_easy *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
- const bool verifypeer = SSL_CONN_CONFIG(verifypeer);
- const char *const ssl_cafile = SSL_CONN_CONFIG(CAfile);
- const char *const ssl_capath = SSL_CONN_CONFIG(CApath);
struct in_addr addr4;
#ifdef ENABLE_IPV6
struct in6_addr addr6;
@@ -142,21 +150,25 @@ mesalink_connect_step1(struct connectdata *conn, int sockindex)
}
SSL_CTX_set_verify(
- BACKEND->ctx, verifypeer ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
+ BACKEND->ctx, SSL_CONN_CONFIG(verifypeer) ?
+ SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
- if(ssl_cafile || ssl_capath) {
- if(!SSL_CTX_load_verify_locations(BACKEND->ctx, ssl_cafile, ssl_capath)) {
- if(verifypeer) {
+ if(SSL_CONN_CONFIG(CAfile) || SSL_CONN_CONFIG(CApath)) {
+ if(!SSL_CTX_load_verify_locations(BACKEND->ctx, SSL_CONN_CONFIG(CAfile),
+ SSL_CONN_CONFIG(CApath))) {
+ if(SSL_CONN_CONFIG(verifypeer)) {
failf(data,
"error setting certificate verify locations:\n"
" CAfile: %s\n CApath: %s",
- ssl_cafile ? ssl_cafile : "none",
- ssl_capath ? ssl_capath : "none");
+ SSL_CONN_CONFIG(CAfile) ?
+ SSL_CONN_CONFIG(CAfile) : "none",
+ SSL_CONN_CONFIG(CApath) ?
+ SSL_CONN_CONFIG(CApath) : "none");
return CURLE_SSL_CACERT_BADFILE;
}
infof(data,
- "error setting certificate verify locations,"
- " continuing anyway:\n");
+ "error setting certificate verify locations,"
+ " continuing anyway:\n");
}
else {
infof(data, "successfully set certificate verify locations:\n");
@@ -164,8 +176,32 @@ mesalink_connect_step1(struct connectdata *conn, int sockindex)
infof(data,
" CAfile: %s\n"
" CApath: %s\n",
- ssl_cafile ? ssl_cafile : "none",
- ssl_capath ? ssl_capath : "none");
+ SSL_CONN_CONFIG(CAfile)?
+ SSL_CONN_CONFIG(CAfile): "none",
+ SSL_CONN_CONFIG(CApath)?
+ SSL_CONN_CONFIG(CApath): "none");
+ }
+
+ if(SSL_SET_OPTION(cert) && SSL_SET_OPTION(key)) {
+ int file_type = do_file_type(SSL_SET_OPTION(cert_type));
+
+ if(SSL_CTX_use_certificate_chain_file(BACKEND->ctx, SSL_SET_OPTION(cert),
+ file_type) != 1) {
+ failf(data, "unable to use client certificate (no key or wrong pass"
+ " phrase?)");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ file_type = do_file_type(SSL_SET_OPTION(key_type));
+ if(SSL_CTX_use_PrivateKey_file(BACKEND->ctx, SSL_SET_OPTION(key),
+ file_type) != 1) {
+ failf(data, "unable to set private key");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ infof(data,
+ "client cert: %s\n",
+ SSL_CONN_CONFIG(clientcert)?
+ SSL_CONN_CONFIG(clientcert): "none");
}
ciphers = SSL_CONN_CONFIG(cipher_list);
diff --git a/lib/vtls/nss.c b/lib/vtls/nss.c
index 482fd5e99..435f3e93a 100644
--- a/lib/vtls/nss.c
+++ b/lib/vtls/nss.c
@@ -1734,20 +1734,16 @@ static CURLcode nss_init_sslver(SSLVersionRange *sslver,
CURLcode result;
const long min = SSL_CONN_CONFIG(version);
const long max = SSL_CONN_CONFIG(version_max);
-
- /* map CURL_SSLVERSION_DEFAULT to NSS default */
- if(min == CURL_SSLVERSION_DEFAULT || max == CURL_SSLVERSION_MAX_DEFAULT) {
- /* map CURL_SSLVERSION_DEFAULT to NSS default */
- if(SSL_VersionRangeGetDefault(ssl_variant_stream, sslver) != SECSuccess)
- return CURLE_SSL_CONNECT_ERROR;
- /* ... but make sure we use at least TLSv1.0 according to libcurl API */
- if(sslver->min < SSL_LIBRARY_VERSION_TLS_1_0)
- sslver->min = SSL_LIBRARY_VERSION_TLS_1_0;
- }
+ SSLVersionRange vrange;
switch(min) {
case CURL_SSLVERSION_TLSv1:
case CURL_SSLVERSION_DEFAULT:
+ /* Bump our minimum TLS version if NSS has stricter requirements. */
+ if(SSL_VersionRangeGetDefault(ssl_variant_stream, &vrange) != SECSuccess)
+ return CURLE_SSL_CONNECT_ERROR;
+ if(sslver->min < vrange.min)
+ sslver->min = vrange.min;
break;
default:
result = nss_sslver_from_curl(&sslver->min, min);
diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c
index fb9f27123..385f28179 100644
--- a/lib/vtls/openssl.c
+++ b/lib/vtls/openssl.c
@@ -25,11 +25,6 @@
* but vtls.c should ever call or use these functions.
*/
-/*
- * The original SSLeay-using code for curl was written by Linas Vepstas and
- * Sampo Kellomaki 1998.
- */
-
#include "curl_setup.h"
#ifdef USE_OPENSSL
@@ -396,7 +391,11 @@ static const char *SSL_ERROR_to_str(int err)
*/
static char *ossl_strerror(unsigned long error, char *buf, size_t size)
{
+#ifdef OPENSSL_IS_BORINGSSL
+ ERR_error_string_n((uint32_t)error, buf, size);
+#else
ERR_error_string_n(error, buf, size);
+#endif
return buf;
}
@@ -1534,8 +1533,13 @@ static CURLcode verifyhost(struct connectdata *conn, X509 *server_cert)
altnames = X509_get_ext_d2i(server_cert, NID_subject_alt_name, NULL, NULL);
if(altnames) {
+#ifdef OPENSSL_IS_BORINGSSL
+ size_t numalts;
+ size_t i;
+#else
int numalts;
int i;
+#endif
bool dnsmatched = FALSE;
bool ipmatched = FALSE;
@@ -1565,11 +1569,10 @@ static CURLcode verifyhost(struct connectdata *conn, X509 *server_cert)
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 and IA5String the data will be ASCII"
+ type itself: for example for an IA5String the data will be ASCII"
- Gisle researched the OpenSSL sources:
- "I checked the 0.9.6 and 0.9.8 sources before my patch and
- it always 0-terminates an IA5String."
+ It has been however verified that in 0.9.6 and 0.9.7, IA5String
+ is always zero-terminated.
*/
if((altlen == strlen(altptr)) &&
/* if this isn't true, there was an embedded zero in the name
@@ -1633,8 +1636,7 @@ static CURLcode verifyhost(struct connectdata *conn, X509 *server_cert)
/* 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. Work-around
- brought by Alexis S. L. Carvalho. */
+ conditional in the future when OpenSSL has been fixed. */
if(tmp) {
if(ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) {
j = ASN1_STRING_length(tmp);
@@ -2154,9 +2156,96 @@ get_ssl_version_txt(SSL *ssl)
}
#endif
+#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */
+static CURLcode
+set_ssl_version_min_max(SSL_CTX *ctx, struct connectdata *conn)
+{
+ /* first, TLS min version... */
+ long curl_ssl_version_min = SSL_CONN_CONFIG(version);
+ long curl_ssl_version_max;
+
+ /* convert cURL min SSL version option to OpenSSL constant */
+ long ossl_ssl_version_min = 0;
+ long ossl_ssl_version_max = 0;
+ 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;
+#ifdef TLS1_3_VERSION
+ case CURL_SSLVERSION_TLSv1_3:
+ ossl_ssl_version_min = TLS1_3_VERSION;
+ break;
+#endif
+ }
+
+ /* CURL_SSLVERSION_DEFAULT means that no option was selected.
+ We don't 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 OS 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 = SSL_CONN_CONFIG(version_max);
+
+ /* convert cURL max SSL version option to OpenSSL constant */
+ ossl_ssl_version_max = 0;
+ 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
+
+#ifdef OPENSSL_IS_BORINGSSL
+typedef uint32_t ctx_option_t;
+#else
+typedef long ctx_option_t;
+#endif
+
+#if (OPENSSL_VERSION_NUMBER < 0x10100000L) /* 1.1.0 */
static CURLcode
-set_ssl_version_min_max(long *ctx_options, struct connectdata *conn,
- int sockindex)
+set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
+ struct connectdata *conn, int sockindex)
{
#if (OPENSSL_VERSION_NUMBER < 0x1000100FL) || !defined(TLS1_3_VERSION)
/* convoluted #if condition just to avoid compiler warnings on unused
@@ -2228,6 +2317,7 @@ set_ssl_version_min_max(long *ctx_options, struct connectdata *conn,
}
return CURLE_OK;
}
+#endif
/* 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.
@@ -2294,7 +2384,8 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
X509_LOOKUP *lookup = NULL;
curl_socket_t sockfd = conn->sock[sockindex];
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
- long ctx_options = 0;
+ ctx_option_t ctx_options = 0;
+
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
bool sni;
const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
@@ -2457,48 +2548,66 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
#endif
switch(ssl_version) {
- case CURL_SSLVERSION_SSLv3:
- ctx_options |= SSL_OP_NO_SSLv2;
- ctx_options |= SSL_OP_NO_TLSv1;
-#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
- ctx_options |= SSL_OP_NO_TLSv1_1;
- ctx_options |= SSL_OP_NO_TLSv1_2;
-#ifdef TLS1_3_VERSION
- ctx_options |= SSL_OP_NO_TLSv1_3;
-#endif
+ /* "--sslv2" option means SSLv2 only, disable all others */
+ case CURL_SSLVERSION_SSLv2:
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L /* 1.1.0 */
+ SSL_CTX_set_min_proto_version(BACKEND->ctx, SSL2_VERSION);
+ SSL_CTX_set_max_proto_version(BACKEND->ctx, SSL2_VERSION);
+#else
+ ctx_options |= SSL_OP_NO_SSLv3;
+ ctx_options |= SSL_OP_NO_TLSv1;
+# if OPENSSL_VERSION_NUMBER >= 0x1000100FL
+ ctx_options |= SSL_OP_NO_TLSv1_1;
+ ctx_options |= SSL_OP_NO_TLSv1_2;
+# ifdef TLS1_3_VERSION
+ ctx_options |= SSL_OP_NO_TLSv1_3;
+# endif
+# endif
#endif
- break;
-
- 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:
- /* 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;
- result = set_ssl_version_min_max(&ctx_options, conn, sockindex);
- if(result != CURLE_OK)
- return result;
- break;
+ break;
- case CURL_SSLVERSION_SSLv2:
- ctx_options |= SSL_OP_NO_SSLv3;
- ctx_options |= SSL_OP_NO_TLSv1;
-#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
- ctx_options |= SSL_OP_NO_TLSv1_1;
- ctx_options |= SSL_OP_NO_TLSv1_2;
-#ifdef TLS1_3_VERSION
- ctx_options |= SSL_OP_NO_TLSv1_3;
+ /* "--sslv3" option means SSLv3 only, disable all others */
+ case CURL_SSLVERSION_SSLv3:
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L /* 1.1.0 */
+ SSL_CTX_set_min_proto_version(BACKEND->ctx, SSL3_VERSION);
+ SSL_CTX_set_max_proto_version(BACKEND->ctx, SSL3_VERSION);
+#else
+ ctx_options |= SSL_OP_NO_SSLv2;
+ ctx_options |= SSL_OP_NO_TLSv1;
+# if OPENSSL_VERSION_NUMBER >= 0x1000100FL
+ ctx_options |= SSL_OP_NO_TLSv1_1;
+ ctx_options |= SSL_OP_NO_TLSv1_2;
+# ifdef TLS1_3_VERSION
+ ctx_options |= SSL_OP_NO_TLSv1_3;
+# endif
+# endif
#endif
+ break;
+
+ /* "--tlsv<x.y>" options mean TLS >= version <x.y> */
+ 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 = set_ssl_version_min_max(BACKEND->ctx, conn);
+#else
+ result = set_ssl_version_min_max_legacy(&ctx_options, conn, sockindex);
#endif
- break;
+ if(result != CURLE_OK)
+ return result;
+ break;
- default:
- failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
- return CURLE_SSL_CONNECT_ERROR;
+ default:
+ failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
+ return CURLE_SSL_CONNECT_ERROR;
}
SSL_CTX_set_options(BACKEND->ctx, ctx_options);
@@ -2654,11 +2763,11 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
}
/* 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 which
- gives us the same fix without as much of a performance hit (slight), so we
- prefer that if available.
- https://rt.openssl.org/Ticket/Display.html?id=3621&user=guest&pass=guest
+ problems with server-sent legacy intermediates. Newer versions of
+ OpenSSL do alternate chain checking by default which gives us the same
+ fix without as much of a performance hit (slight), so we prefer that if
+ available.
+ https://rt.openssl.org/Ticket/Display.html?id=3621&user=guest&pass=guest
*/
#if defined(X509_V_FLAG_TRUSTED_FIRST) && !defined(X509_V_FLAG_NO_ALT_CHAINS)
if(verifypeer) {
@@ -3038,6 +3147,12 @@ static int X509V3_ext(struct Curl_easy *data,
return 0; /* all is fine */
}
+#ifdef OPENSSL_IS_BORINGSSL
+typedef size_t numcert_t;
+#else
+typedef int numcert_t;
+#endif
+
static CURLcode get_cert_chain(struct connectdata *conn,
struct ssl_connect_data *connssl)
@@ -3046,7 +3161,7 @@ static CURLcode get_cert_chain(struct connectdata *conn,
STACK_OF(X509) *sk;
int i;
struct Curl_easy *data = conn->data;
- int numcerts;
+ numcert_t numcerts;
BIO *mem;
sk = SSL_get_peer_cert_chain(BACKEND->handle);
@@ -3056,14 +3171,14 @@ static CURLcode get_cert_chain(struct connectdata *conn,
numcerts = sk_X509_num(sk);
- result = Curl_ssl_init_certinfo(data, numcerts);
+ result = Curl_ssl_init_certinfo(data, (int)numcerts);
if(result) {
return result;
}
mem = BIO_new(BIO_s_mem());
- for(i = 0; i < numcerts; i++) {
+ for(i = 0; i < (int)numcerts; i++) {
ASN1_INTEGER *num;
X509 *x = sk_X509_value(sk, i);
EVP_PKEY *pubkey = NULL;
diff --git a/lib/vtls/vtls.c b/lib/vtls/vtls.c
index 6a0e60fb6..422819899 100644
--- a/lib/vtls/vtls.c
+++ b/lib/vtls/vtls.c
@@ -515,14 +515,10 @@ void Curl_ssl_close_all(struct Curl_easy *data)
#if defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_SCHANNEL) || \
defined(USE_SECTRANSP) || defined(USE_POLARSSL) || defined(USE_NSS) || \
defined(USE_MBEDTLS) || defined(USE_WOLFSSL)
-int Curl_ssl_getsock(struct connectdata *conn, curl_socket_t *socks,
- int numsocks)
+int Curl_ssl_getsock(struct connectdata *conn, curl_socket_t *socks)
{
struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET];
- if(!numsocks)
- return GETSOCK_BLANK;
-
if(connssl->connecting_state == ssl_connect_2_writing) {
/* write mode */
socks[0] = conn->sock[FIRSTSOCKET];
@@ -538,12 +534,10 @@ int Curl_ssl_getsock(struct connectdata *conn, curl_socket_t *socks,
}
#else
int Curl_ssl_getsock(struct connectdata *conn,
- curl_socket_t *socks,
- int numsocks)
+ curl_socket_t *socks)
{
(void)conn;
(void)socks;
- (void)numsocks;
return GETSOCK_BLANK;
}
/* USE_OPENSSL || USE_GNUTLS || USE_SCHANNEL || USE_SECTRANSP || USE_NSS */
diff --git a/lib/vtls/vtls.h b/lib/vtls/vtls.h
index 173d360de..61d8416c2 100644
--- a/lib/vtls/vtls.h
+++ b/lib/vtls/vtls.h
@@ -143,8 +143,7 @@ bool Curl_ssl_config_matches(struct ssl_primary_config* data,
bool Curl_clone_primary_ssl_config(struct ssl_primary_config *source,
struct ssl_primary_config *dest);
void Curl_free_primary_ssl_config(struct ssl_primary_config* sslc);
-int Curl_ssl_getsock(struct connectdata *conn, curl_socket_t *socks,
- int numsocks);
+int Curl_ssl_getsock(struct connectdata *conn, curl_socket_t *socks);
int Curl_ssl_backend(void);
diff --git a/packages/OS400/README.OS400 b/packages/OS400/README.OS400
index 132d1b92f..4ac8feca8 100644
--- a/packages/OS400/README.OS400
+++ b/packages/OS400/README.OS400
@@ -120,6 +120,7 @@ options:
CURLOPT_RTSP_SESSION_UID
CURLOPT_RTSP_STREAM_URI
CURLOPT_RTSP_TRANSPORT
+ CURLOPT_SASL_AUTHZID
CURLOPT_SERVICE_NAME
CURLOPT_SOCKS5_GSSAPI_SERVICE
CURLOPT_SSH_HOST_PUBLIC_KEY_MD5
diff --git a/packages/OS400/ccsidcurl.c b/packages/OS400/ccsidcurl.c
index 4b462a273..11e4c777e 100644
--- a/packages/OS400/ccsidcurl.c
+++ b/packages/OS400/ccsidcurl.c
@@ -1141,12 +1141,7 @@ curl_easy_setopt_ccsid(CURL *curl, CURLoption tag, ...)
if(testwarn) {
testwarn = 0;
- if(
-#ifdef USE_ALTSVC
- (int) STRING_LASTZEROTERMINATED != (int) STRING_ALTSVC + 1 ||
-#else
- (int) STRING_LASTZEROTERMINATED != (int) STRING_DOH + 1 ||
-#endif
+ if((int) STRING_LASTZEROTERMINATED != (int) STRING_SASL_AUTHZID + 1 ||
(int) STRING_LAST != (int) STRING_COPYPOSTFIELDS + 1)
curl_mfprintf(stderr,
"*** WARNING: curl_easy_setopt_ccsid() should be reworked ***\n");
@@ -1213,6 +1208,7 @@ curl_easy_setopt_ccsid(CURL *curl, CURLoption tag, ...)
case CURLOPT_RTSP_SESSION_ID:
case CURLOPT_RTSP_STREAM_URI:
case CURLOPT_RTSP_TRANSPORT:
+ case CURLOPT_SASL_AUTHZID:
case CURLOPT_SERVICE_NAME:
case CURLOPT_SOCKS5_GSSAPI_SERVICE:
case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5:
diff --git a/packages/OS400/ccsidcurl.h b/packages/OS400/ccsidcurl.h
index 895db7002..9594f6610 100644
--- a/packages/OS400/ccsidcurl.h
+++ b/packages/OS400/ccsidcurl.h
@@ -1,3 +1,5 @@
+#ifndef CURLINC_CCSIDCURL_H
+#define CURLINC_CCSIDCURL_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@@ -5,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -20,10 +22,6 @@
*
*
***************************************************************************/
-
-#ifndef __CURL_CCSIDCURL_H
-#define __CURL_CCSIDCURL_H
-
#include "curl.h"
#include "easy.h"
#include "multi.h"
diff --git a/packages/OS400/curl.inc.in b/packages/OS400/curl.inc.in
index 8e36bac3b..5a53b1b21 100644
--- a/packages/OS400/curl.inc.in
+++ b/packages/OS400/curl.inc.in
@@ -138,6 +138,8 @@
d c X'00800000'
d CURL_VERSION_ALTSVC...
d c X'01000000'
+ d CURL_VERSION_HTTP3...
+ d c X'02000000'
*
d CURL_HTTPPOST_FILENAME...
d c X'00000001'
@@ -1418,6 +1420,8 @@
d c 10287
d CURLOPT_MAXAGE_CONN...
d c 00288
+ d CURLOPT_SASL_AUTHZID...
+ d c 10289
*
/if not defined(CURL_NO_OLDIES)
d CURLOPT_FILE c 10001
diff --git a/plan9/BUILD.PLAN9.txt b/plan9/BUILD.PLAN9.txt
new file mode 100644
index 000000000..6df23d31a
--- /dev/null
+++ b/plan9/BUILD.PLAN9.txt
@@ -0,0 +1,55 @@
+Prerequirement
+==============
+This document describes how to compile, build and install curl and libcurl
+from sources using mk. To build it, you will require to install latest
+9legacy patches into Plan 9. Also Plan 9 still have no configuration option so
+both zlib and libopenssl are required too.
+
+The zlib that is available on Plan 9 can be downloaded from:
+
+ https://github.com/madler/zlib/pull/398
+
+LibreSSL Portable can be downloaded from:
+
+ https://github.com/libressl-portable/portable/pull/510
+
+Instruction
+===========
+First, you should construct namespace as like described below:
+
+% bind -ac ../lib lib
+% bind -ac ../src src
+% bind -ac ../include include
+% bind -ac .. .
+
+Then you will see as shown below (excerpt):
+
+ curl.git/
+ |_plan9
+ | |_BUILD.PLAN9.txt
+ | |_CHANGES
+ | |_CMake
+ | | :
+ | |_mkfile
+ | |_mkfile.proto
+ | |_include
+ | | |_Makefile.am
+ | | | :
+ | | |_mkfile
+ | |_lib
+ | | |_CMakeLists.txt
+ | | | :
+ | | |_mkfile
+ | | |_mkfile.inc
+ | |_src
+ | | |_CMakeLists.txt
+ | | | :
+ | | |_mkfile
+ | | |_mkfile.inc
+ |_lib
+ |_src
+
+After constructing namespace, you can run mk on plan9 directory.
+
+% mk
+% mk install
diff --git a/plan9/include/mkfile b/plan9/include/mkfile
new file mode 100644
index 000000000..c1ed850fa
--- /dev/null
+++ b/plan9/include/mkfile
@@ -0,0 +1,34 @@
+#***************************************************************************
+# _ _ ____ _
+# Project ___| | | | _ \| |
+# / __| | | | |_) | |
+# | (__| |_| | _ <| |___
+# \___|\___/|_| \_\_____|
+#
+# Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+#
+# This software is licensed 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.
+#
+###########################################################################
+
+DIR=/sys/include/ape/curl
+HFILES=`{ls curl/*.h}
+
+all:V: $HFILES
+
+install:V: all
+ mkdir -p $DIR
+ cp curl/*.h $DIR/
+
+clean:V: $HFILES # do nothing
+
+nuke:V: clean
diff --git a/plan9/lib/mkfile b/plan9/lib/mkfile
new file mode 100644
index 000000000..d7a7ac5e2
--- /dev/null
+++ b/plan9/lib/mkfile
@@ -0,0 +1,39 @@
+#***************************************************************************
+# _ _ ____ _
+# Project ___| | | | _ \| |
+# / __| | | | |_) | |
+# | (__| |_| | _ <| |___
+# \___|\___/|_| \_\_____|
+#
+# Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+#
+# This software is licensed 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.
+#
+###########################################################################
+
+<../mkfile.proto
+<|mkfile.inc
+
+CFLAGS=$CFLAGS -I../include -I. -c
+
+OFILES=${CSOURCES:%.c=%.$O}
+HFILES=$HHEADERS
+LIB=/$objtype/lib/ape/libcurl.a
+
+CLEANFILES=\
+ ${LIB_VAUTH_CFILES:%.c=%.$O}\
+ ${LIB_VTLS_CFILES:%.c=%.$O}\
+
+</sys/src/cmd/mklib
+
+%.$O: %.c
+ $CC $CFLAGS -o $target $stem.c
diff --git a/plan9/lib/mkfile.inc b/plan9/lib/mkfile.inc
new file mode 100755
index 000000000..0966d773e
--- /dev/null
+++ b/plan9/lib/mkfile.inc
@@ -0,0 +1,25 @@
+#!/bin/rc
+#***************************************************************************
+# _ _ ____ _
+# Project ___| | | | _ \| |
+# / __| | | | |_) | |
+# | (__| |_| | _ <| |___
+# \___|\___/|_| \_\_____|
+#
+# Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+#
+# This software is licensed 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.
+#
+###########################################################################
+
+# rename $(VAR) -> $VAR
+sed 's/\$\(([A-Z_]+)\)/$\1/g' Makefile.inc
diff --git a/plan9/mkfile b/plan9/mkfile
new file mode 100644
index 000000000..a9b4fe6da
--- /dev/null
+++ b/plan9/mkfile
@@ -0,0 +1,36 @@
+#***************************************************************************
+# _ _ ____ _
+# Project ___| | | | _ \| |
+# / __| | | | |_) | |
+# | (__| |_| | _ <| |___
+# \___|\___/|_| \_\_____|
+#
+# Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+#
+# This software is licensed 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.
+#
+###########################################################################
+
+</sys/src/ape/config
+
+DIRS=\
+ lib\
+ src\
+ include\
+
+default:V: all
+
+all install clean nuke:V:
+ for(i in $DIRS) @{
+ cd $i
+ mk $target
+ }
diff --git a/plan9/mkfile.proto b/plan9/mkfile.proto
new file mode 100644
index 000000000..edb79f50d
--- /dev/null
+++ b/plan9/mkfile.proto
@@ -0,0 +1,30 @@
+#***************************************************************************
+# _ _ ____ _
+# Project ___| | | | _ \| |
+# / __| | | | |_) | |
+# | (__| |_| | _ <| |___
+# \___|\___/|_| \_\_____|
+#
+# Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+#
+# This software is licensed 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.
+#
+###########################################################################
+
+</sys/src/ape/config
+
+CFLAGS=\
+ -D__PLAN9__\
+ -D_POSIX_SOURCE\
+ -D_BSD_EXTENSION\
+ -D_SUSV2_SOURCE\
+ -D_REENTRANT_SOURCE\
diff --git a/plan9/src/mkfile b/plan9/src/mkfile
new file mode 100644
index 000000000..889d06686
--- /dev/null
+++ b/plan9/src/mkfile
@@ -0,0 +1,45 @@
+#***************************************************************************
+# _ _ ____ _
+# Project ___| | | | _ \| |
+# / __| | | | |_) | |
+# | (__| |_| | _ <| |___
+# \___|\___/|_| \_\_____|
+#
+# Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+#
+# This software is licensed 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.
+#
+###########################################################################
+
+<../mkfile.proto
+<|mkfile.inc
+
+CFLAGS=$CFLAGS -I../include -I../lib -c
+
+OFILES=${CURL_CFILES:%.c=%.$O}
+HFILES=$CURL_HFILES
+
+LIB=\
+ /$objtype/lib/ape/libcurl.a\
+ /$objtype/lib/ape/libssl.a\
+ /$objtype/lib/ape/libcrypto.a\
+ /$objtype/lib/ape/libz.a\
+
+BIN=/$objtype/bin
+TARG=curl
+
+CLEANFILES=tool_hugehelp.c
+
+</sys/src/cmd/mkone
+
+tool_hugehelp.c:
+ echo 'void hugehelp(void) {}' >$target
diff --git a/plan9/src/mkfile.inc b/plan9/src/mkfile.inc
new file mode 100755
index 000000000..0966d773e
--- /dev/null
+++ b/plan9/src/mkfile.inc
@@ -0,0 +1,25 @@
+#!/bin/rc
+#***************************************************************************
+# _ _ ____ _
+# Project ___| | | | _ \| |
+# / __| | | | |_) | |
+# | (__| |_| | _ <| |___
+# \___|\___/|_| \_\_____|
+#
+# Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+#
+# This software is licensed 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.
+#
+###########################################################################
+
+# rename $(VAR) -> $VAR
+sed 's/\$\(([A-Z_]+)\)/$\1/g' Makefile.inc
diff --git a/projects/build-openssl.bat b/projects/build-openssl.bat
index 793a63be7..c9633b448 100644
--- a/projects/build-openssl.bat
+++ b/projects/build-openssl.bat
@@ -95,6 +95,19 @@ rem ***************************************************************************
) else (
set "VC_PATH=Microsoft Visual Studio\2017\Community\VC"
)
+ ) else if /i "%~1" == "vc14.2" (
+ set VC_VER=14.2
+ set VC_DESC=VC14.2
+
+ rem Determine the VC14.2 path based on the installed edition in descending
+ rem order (Enterprise, then Professional and finally Community)
+ if exist "%PF%\Microsoft Visual Studio\2019\Enterprise\VC" (
+ set "VC_PATH=Microsoft Visual Studio\2019\Enterprise\VC"
+ ) else if exist "%PF%\Microsoft Visual Studio\2019\Professional\VC" (
+ set "VC_PATH=Microsoft Visual Studio\2019\Professional\VC"
+ ) else (
+ set "VC_PATH=Microsoft Visual Studio\2019\Community\VC"
+ )
) else if /i "%~1%" == "x86" (
set BUILD_PLATFORM=x86
) else if /i "%~1%" == "x64" (
@@ -202,6 +215,7 @@ rem ***************************************************************************
if "%VC_VER%" == "12.0" set VCVARS_PLATFORM=amd64
if "%VC_VER%" == "14.0" set VCVARS_PLATFORM=amd64
if "%VC_VER%" == "14.1" set VCVARS_PLATFORM=amd64
+ if "%VC_VER%" == "14.2" set VCVARS_PLATFORM=amd64
)
if exist "%START_DIR%\ms\do_ms.bat" (
@@ -222,6 +236,8 @@ rem ***************************************************************************
call "%ABS_VC_PATH%\bin\vcvars32"
) else if "%VC_VER%" == "14.1" (
call "%ABS_VC_PATH%\Auxiliary\Build\vcvarsall" %VCVARS_PLATFORM%
+ ) else if "%VC_VER%" == "14.2" (
+ call "%ABS_VC_PATH%\Auxiliary\Build\vcvarsall" %VCVARS_PLATFORM%
) else (
call "%ABS_VC_PATH%\vcvarsall" %VCVARS_PLATFORM%
)
@@ -569,7 +585,7 @@ rem
if not exist "%OUTDIR%\DLL Release" (
mkdir "%OUTDIR%\DLL Release" 1>nul
)
-
+
move !build_dir!\lib\*.lib "%OUTDIR%\DLL Release" 1>nul
move !build_dir!\bin\*.dll "%OUTDIR%\DLL Release" 1>nul
move !build_dir!\bin\*.exe "%OUTDIR%\DLL Release" 1>nul
@@ -605,6 +621,7 @@ rem
echo vc12 - Use Visual Studio 2013
echo vc14 - Use Visual Studio 2015
echo vc14.1 - Use Visual Studio 2017
+ echo vc14.2 - Use Visual Studio 2019
echo.
echo Platform:
echo.
diff --git a/src/Makefile.am b/src/Makefile.am
index d5020093f..1350f6025 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -123,7 +123,7 @@ $(HUGE): $(MANPAGE) $(MKHELP)
else # HAVE_LIBZ
# This generates the tool_hugehelp.c file uncompressed only
$(HUGE): $(MANPAGE) $(MKHELP)
- $(HUGECMD)(echo '#include "tool_setup.h"' > $(HUGE): \
+ $(HUGECMD)(echo '#include "tool_setup.h"' > $(HUGE); \
$(NROFF) $(MANPAGE) | $(PERL) $(MKHELP) >> $(HUGE) )
endif
diff --git a/src/Makefile.inc b/src/Makefile.inc
index e1e8306bd..dd6b9d336 100644
--- a/src/Makefile.inc
+++ b/src/Makefile.inc
@@ -54,6 +54,7 @@ CURL_CFILES = \
tool_panykey.c \
tool_paramhlp.c \
tool_parsecfg.c \
+ tool_progress.c \
tool_strdup.c \
tool_setopt.c \
tool_sleep.c \
@@ -95,6 +96,7 @@ CURL_HFILES = \
tool_panykey.h \
tool_paramhlp.h \
tool_parsecfg.h \
+ tool_progress.h \
tool_sdecls.h \
tool_setopt.h \
tool_setup.h \
diff --git a/src/tool_cb_hdr.c b/src/tool_cb_hdr.c
index 3844904c9..b0880f186 100644
--- a/src/tool_cb_hdr.c
+++ b/src/tool_cb_hdr.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -32,6 +32,7 @@
#include "tool_msgs.h"
#include "tool_cb_hdr.h"
#include "tool_cb_wrt.h"
+#include "tool_operate.h"
#include "memdebug.h" /* keep this as LAST include */
@@ -54,9 +55,10 @@ static char *parse_filename(const char *ptr, size_t len);
size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
{
- struct HdrCbData *hdrcbdata = userdata;
- struct OutStruct *outs = hdrcbdata->outs;
- struct OutStruct *heads = hdrcbdata->heads;
+ struct per_transfer *per = userdata;
+ struct HdrCbData *hdrcbdata = &per->hdrcbdata;
+ struct OutStruct *outs = &per->outs;
+ struct OutStruct *heads = &per->heads;
const char *str = ptr;
const size_t cb = size * nmemb;
const char *end = (char *)ptr + cb;
@@ -100,7 +102,7 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
* Content-Disposition header specifying a filename property.
*/
- curl_easy_getinfo(outs->config->easy, CURLINFO_PROTOCOL, &protocol);
+ curl_easy_getinfo(per->curl, CURLINFO_PROTOCOL, &protocol);
if(hdrcbdata->honor_cd_filename &&
(cb > 20) && checkprefix("Content-disposition:", str) &&
(protocol & (CURLPROTO_HTTPS|CURLPROTO_HTTP))) {
diff --git a/src/tool_cb_wrt.c b/src/tool_cb_wrt.c
index 2f699f326..0f47b4d0f 100644
--- a/src/tool_cb_wrt.c
+++ b/src/tool_cb_wrt.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -28,6 +28,7 @@
#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 */
@@ -75,7 +76,8 @@ bool tool_create_output_file(struct OutStruct *outs)
size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata)
{
size_t rc;
- struct OutStruct *outs = userdata;
+ struct per_transfer *per = userdata;
+ struct OutStruct *outs = &per->outs;
struct OperationConfig *config = outs->config;
size_t bytes = sz * nmemb;
bool is_tty = config->global->isatty;
@@ -202,7 +204,7 @@ size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata)
if(config->readbusy) {
config->readbusy = FALSE;
- curl_easy_pause(config->easy, CURLPAUSE_CONT);
+ curl_easy_pause(per->curl, CURLPAUSE_CONT);
}
if(config->nobuffer) {
diff --git a/src/tool_cfgable.c b/src/tool_cfgable.c
index 7d178e47c..efa8c50b2 100644
--- a/src/tool_cfgable.c
+++ b/src/tool_cfgable.c
@@ -43,7 +43,7 @@ void config_init(struct OperationConfig* config)
config->proto_default = NULL;
config->tcp_nodelay = TRUE; /* enabled by default */
config->happy_eyeballs_timeout_ms = CURL_HET_DEFAULT;
- config->http09_allowed = TRUE;
+ config->http09_allowed = FALSE;
}
static void free_config_fields(struct OperationConfig *config)
@@ -133,6 +133,7 @@ static void free_config_fields(struct OperationConfig *config)
Curl_safefree(config->krblevel);
Curl_safefree(config->oauth_bearer);
+ Curl_safefree(config->sasl_authzid);
Curl_safefree(config->unix_socket_path);
Curl_safefree(config->writeout);
diff --git a/src/tool_cfgable.h b/src/tool_cfgable.h
index e374a7f0e..ff80f8eb8 100644
--- a/src/tool_cfgable.h
+++ b/src/tool_cfgable.h
@@ -38,7 +38,6 @@ typedef enum {
struct GlobalConfig;
struct OperationConfig {
- CURL *easy; /* A copy of the handle from GlobalConfig */
bool remote_time;
char *random_file;
char *egd_file;
@@ -97,6 +96,7 @@ struct OperationConfig {
char *mail_from;
struct curl_slist *mail_rcpt;
char *mail_auth;
+ char *sasl_authzid; /* Authorisation identity (identity to use) */
bool sasl_ir; /* Enable/disable SASL initial response */
bool proxytunnel;
bool ftp_append; /* APPE on ftp */
@@ -242,9 +242,6 @@ struct OperationConfig {
bool use_metalink; /* process given URLs as metalink XML file */
metalinkfile *metalinkfile_list; /* point to the first node */
metalinkfile *metalinkfile_last; /* point to the last/current node */
-#ifdef CURLDEBUG
- bool test_event_based;
-#endif
char *oauth_bearer; /* OAuth 2.0 bearer token */
bool nonpn; /* enable/disable TLS NPN extension */
bool noalpn; /* enable/disable TLS ALPN extension */
@@ -268,7 +265,6 @@ struct OperationConfig {
};
struct GlobalConfig {
- CURL *easy; /* Once we have one, we keep it here */
int showerror; /* -1 == unset, default => show errors
0 => -s is used to NOT show errors
1 => -S has been used to show errors */
@@ -286,6 +282,11 @@ struct GlobalConfig {
char *libcurl; /* Output libcurl code to this file name */
bool fail_early; /* exit on first transfer error */
bool styled_output; /* enable fancy output style detection */
+#ifdef CURLDEBUG
+ bool test_event_based;
+#endif
+ bool parallel;
+ long parallel_max;
struct OperationConfig *first;
struct OperationConfig *current;
struct OperationConfig *last; /* Always last in the struct */
diff --git a/src/tool_getparam.c b/src/tool_getparam.c
index b347121f8..2c1868383 100644
--- a/src/tool_getparam.c
+++ b/src/tool_getparam.c
@@ -40,6 +40,7 @@
#include "tool_msgs.h"
#include "tool_paramhlp.h"
#include "tool_parsecfg.h"
+#include "tool_main.h"
#include "memdebug.h" /* keep this as LAST include */
@@ -177,7 +178,8 @@ static const struct LongShort aliases[]= {
{"$H", "mail-auth", ARG_STRING},
{"$I", "post303", ARG_BOOL},
{"$J", "metalink", ARG_BOOL},
- {"$K", "sasl-ir", ARG_BOOL},
+ {"$6", "sasl-authzid", ARG_STRING},
+ {"$K", "sasl-ir", ARG_BOOL },
{"$L", "test-event", ARG_BOOL},
{"$M", "unix-socket", ARG_FILENAME},
{"$N", "path-as-is", ARG_BOOL},
@@ -199,6 +201,7 @@ static const struct LongShort aliases[]= {
{"01", "http1.1", ARG_NONE},
{"02", "http2", ARG_NONE},
{"03", "http2-prior-knowledge", ARG_NONE},
+ {"04", "http3", ARG_NONE},
{"09", "http0.9", ARG_BOOL},
{"1", "tlsv1", ARG_NONE},
{"10", "tlsv1.0", ARG_NONE},
@@ -316,6 +319,8 @@ static const struct LongShort aliases[]= {
{"Y", "speed-limit", ARG_STRING},
{"y", "speed-time", ARG_STRING},
{"z", "time-cond", ARG_STRING},
+ {"Z", "parallel", ARG_BOOL},
+ {"Zb", "parallel-max", ARG_STRING},
{"#", "progress-bar", ARG_BOOL},
{":", "next", ARG_NONE},
};
@@ -907,12 +912,12 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
config->retry_connrefused = toggle;
break;
case 'h': /* --retry-delay */
- err = str2unum(&config->retry_delay, nextarg);
+ err = str2unummax(&config->retry_delay, nextarg, LONG_MAX/1000);
if(err)
return err;
break;
case 'i': /* --retry-max-time */
- err = str2unum(&config->retry_maxtime, nextarg);
+ err = str2unummax(&config->retry_maxtime, nextarg, LONG_MAX/1000);
if(err)
return err;
break;
@@ -1099,12 +1104,15 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
#endif
break;
}
+ case '6': /* --sasl-authzid */
+ GetStr(&config->sasl_authzid, nextarg);
+ break;
case 'K': /* --sasl-ir */
config->sasl_ir = toggle;
break;
case 'L': /* --test-event */
#ifdef CURLDEBUG
- config->test_event_based = toggle;
+ global->test_event_based = toggle;
#else
warnf(global, "--test-event is ignored unless a debug build!\n");
#endif
@@ -1186,10 +1194,14 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
/* HTTP version 2.0 */
config->httpversion = CURL_HTTP_VERSION_2_0;
break;
- case '3':
+ case '3': /* --http2-prior-knowledge */
/* HTTP version 2.0 over clean TCP*/
config->httpversion = CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE;
break;
+ case '4': /* --http3 */
+ /* HTTP version 3 go over QUIC - at once */
+ config->httpversion = CURL_HTTP_VERSION_3;
+ break;
case '9':
/* Allow HTTP/0.9 responses! */
config->http09_allowed = toggle;
@@ -1356,7 +1368,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
size = 0;
}
else {
- char *enc = curl_easy_escape(config->easy, postdata, (int)size);
+ char *enc = curl_easy_escape(NULL, postdata, (int)size);
Curl_safefree(postdata); /* no matter if it worked or not */
if(enc) {
/* now make a string with the name from above and append the
@@ -2127,6 +2139,21 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
if(!config->low_speed_time)
config->low_speed_time = 30;
break;
+ case 'Z':
+ switch(subletter) {
+ case '\0': /* --parallel */
+ global->parallel = toggle;
+ break;
+ case 'b': /* --parallel-max */
+ err = str2unum(&global->parallel_max, nextarg);
+ if(err)
+ return err;
+ if((global->parallel_max > MAX_PARALLEL) ||
+ (global->parallel_max < 1))
+ global->parallel_max = PARALLEL_DEFAULT;
+ break;
+ }
+ break;
case 'z': /* time condition coming up */
switch(*nextarg) {
case '+':
@@ -2176,14 +2203,14 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
return PARAM_OK;
}
-ParameterError parse_args(struct GlobalConfig *config, int argc,
+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 *operation = config->first;
+ struct OperationConfig *config = global->first;
for(i = 1, stillflags = TRUE; i < argc && !result; i++) {
orig_opt = argv[i];
@@ -2199,31 +2226,28 @@ ParameterError parse_args(struct GlobalConfig *config, int argc,
else {
char *nextarg = (i < (argc - 1)) ? argv[i + 1] : NULL;
- result = getparameter(flag, nextarg, &passarg, config, operation);
+ result = getparameter(flag, nextarg, &passarg, global, config);
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(operation->url_list && operation->url_list->url) {
+ if(config->url_list && config->url_list->url) {
/* Allocate the next config */
- operation->next = malloc(sizeof(struct OperationConfig));
- if(operation->next) {
+ config->next = malloc(sizeof(struct OperationConfig));
+ if(config->next) {
/* Initialise the newly created config */
- config_init(operation->next);
-
- /* Copy the easy handle */
- operation->next->easy = config->easy;
+ config_init(config->next);
/* Set the global config pointer */
- operation->next->global = config;
+ config->next->global = global;
- /* Update the last operation pointer */
- config->last = operation->next;
+ /* Update the last config pointer */
+ global->last = config->next;
/* Move onto the new config */
- operation->next->prev = operation;
- operation = operation->next;
+ config->next->prev = config;
+ config = config->next;
}
else
result = PARAM_NO_MEM;
@@ -2237,8 +2261,8 @@ ParameterError parse_args(struct GlobalConfig *config, int argc,
bool used;
/* Just add the URL please */
- result = getparameter((char *)"--url", argv[i], &used, config,
- operation);
+ result = getparameter((char *)"--url", argv[i], &used, global,
+ config);
}
}
@@ -2249,9 +2273,9 @@ ParameterError parse_args(struct GlobalConfig *config, int argc,
const char *reason = param2text(result);
if(orig_opt && strcmp(":", orig_opt))
- helpf(config->errors, "option %s: %s\n", orig_opt, reason);
+ helpf(global->errors, "option %s: %s\n", orig_opt, reason);
else
- helpf(config->errors, "%s\n", reason);
+ helpf(global->errors, "%s\n", reason);
}
return result;
diff --git a/src/tool_help.c b/src/tool_help.c
index 9209a13dd..271439053 100644
--- a/src/tool_help.c
+++ b/src/tool_help.c
@@ -20,7 +20,7 @@
*
***************************************************************************/
#include "tool_setup.h"
-#ifdef HAVE_STRCASECMP
+#if defined(HAVE_STRCASECMP) && defined(HAVE_STRINGS_H)
#include <strings.h>
#endif
@@ -191,6 +191,8 @@ static const struct helptxt helptext[] = {
"Use HTTP 2"},
{" --http2-prior-knowledge",
"Use HTTP 2 without HTTP/1.1 Upgrade"},
+ {" --http3",
+ "Use HTTP v3"},
{" --ignore-content-length",
"Ignore the size of the remote resource"},
{"-i, --include",
@@ -273,6 +275,10 @@ static const struct helptxt helptext[] = {
"OAuth 2 Bearer Token"},
{"-o, --output <file>",
"Write to file instead of stdout"},
+ {"-Z, --parallel",
+ "Perform transfers in parallel"},
+ {" --parallel-max",
+ "Maximum concurrency for parallel transfers"},
{" --pass <phrase>",
"Pass phrase for the private key"},
{" --path-as-is",
@@ -385,6 +391,8 @@ static const struct helptxt helptext[] = {
"Wait time between retries"},
{" --retry-max-time <seconds>",
"Retry only within this period"},
+ {" --sasl-authzid <identity> ",
+ "Use this identity to act as during SASL PLAIN authentication"},
{" --sasl-ir",
"Enable initial response in SASL authentication"},
{" --service-name <name>",
@@ -526,6 +534,7 @@ static const struct feat feats[] = {
{"CharConv", CURL_VERSION_CONV},
{"TLS-SRP", CURL_VERSION_TLSAUTH_SRP},
{"HTTP2", CURL_VERSION_HTTP2},
+ {"HTTP3", CURL_VERSION_HTTP3},
{"UnixSockets", CURL_VERSION_UNIX_SOCKETS},
{"HTTPS-proxy", CURL_VERSION_HTTPS_PROXY},
{"MultiSSL", CURL_VERSION_MULTI_SSL},
@@ -602,8 +611,9 @@ void tool_version_info(void)
}
}
-void tool_list_engines(CURL *curl)
+void tool_list_engines(void)
{
+ CURL *curl = curl_easy_init();
struct curl_slist *engines = NULL;
/* Get the list of engines */
@@ -620,4 +630,5 @@ void tool_list_engines(CURL *curl)
/* Cleanup the list of engines */
curl_slist_free_all(engines);
+ curl_easy_cleanup(curl);
}
diff --git a/src/tool_help.h b/src/tool_help.h
index 0289f3015..bfb5dcdf3 100644
--- a/src/tool_help.h
+++ b/src/tool_help.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -24,7 +24,7 @@
#include "tool_setup.h"
void tool_help(void);
-void tool_list_engines(CURL *curl);
+void tool_list_engines(void);
void tool_version_info(void);
#endif /* HEADER_CURL_TOOL_HELP_H */
diff --git a/src/tool_main.c b/src/tool_main.c
index 7d1e62b79..4803adbb3 100644
--- a/src/tool_main.c
+++ b/src/tool_main.c
@@ -113,9 +113,9 @@ static void memory_tracking_init(void)
strcpy(fname, env);
curl_free(env);
curl_dbg_memdebug(fname);
- /* this weird stuff here is to make curl_free() get called
- before curl_memdebug() as otherwise memory tracking will
- log a free() without an alloc! */
+ /* this weird stuff here is to make curl_free() get called before
+ curl_gdb_memdebug() as otherwise memory tracking will log a free()
+ without an alloc! */
}
/* if CURL_MEMLIMIT is set, this enables fail-on-alloc-number-N feature */
env = curlx_getenv("CURL_MEMLIMIT");
@@ -149,6 +149,7 @@ static CURLcode main_init(struct GlobalConfig *config)
config->showerror = -1; /* Will show errors */
config->errors = stderr; /* Default errors to stderr */
config->styled_output = TRUE; /* enable detection */
+ config->parallel_max = PARALLEL_DEFAULT;
/* Allocate the initial operate config */
config->first = config->last = malloc(sizeof(struct OperationConfig));
@@ -160,19 +161,9 @@ static CURLcode main_init(struct GlobalConfig *config)
result = get_libcurl_info();
if(!result) {
- /* Get a curl handle to use for all forthcoming curl transfers */
- config->easy = curl_easy_init();
- if(config->easy) {
- /* Initialise the config */
- config_init(config->first);
- config->first->easy = config->easy;
- config->first->global = config;
- }
- else {
- helpf(stderr, "error initializing curl easy handle\n");
- result = CURLE_FAILED_INIT;
- free(config->first);
- }
+ /* Initialise the config */
+ config_init(config->first);
+ config->first->global = config;
}
else {
helpf(stderr, "error retrieving curl library information\n");
@@ -214,9 +205,6 @@ static void free_globalconfig(struct GlobalConfig *config)
static void main_free(struct GlobalConfig *config)
{
/* Cleanup the easy handle */
- curl_easy_cleanup(config->easy);
- config->easy = NULL;
-
/* Main cleanup */
curl_global_cleanup();
convert_cleanup();
diff --git a/src/tool_main.h b/src/tool_main.h
index 868818816..a68287ec8 100644
--- a/src/tool_main.h
+++ b/src/tool_main.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -28,6 +28,9 @@
#define RETRY_SLEEP_DEFAULT 1000L /* ms */
#define RETRY_SLEEP_MAX 600000L /* ms == 10 minutes */
+#define MAX_PARALLEL 300 /* conservative */
+#define PARALLEL_DEFAULT 50
+
#ifndef STDIN_FILENO
# define STDIN_FILENO fileno(stdin)
#endif
diff --git a/src/tool_metalink.c b/src/tool_metalink.c
index 28aa71707..0740407f9 100644
--- a/src/tool_metalink.c
+++ b/src/tool_metalink.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -104,6 +104,7 @@ struct win32_crypto_hash {
#include "tool_paramhlp.h"
#include "tool_cfgable.h"
#include "tool_metalink.h"
+#include "tool_operate.h"
#include "tool_msgs.h"
#include "memdebug.h" /* keep this as LAST include */
@@ -674,8 +675,9 @@ int metalink_check_hash(struct GlobalConfig *config,
return rv;
}
-static metalink_checksum *new_metalink_checksum_from_hex_digest
-(const metalink_digest_def *digest_def, const char *hex_digest)
+static metalink_checksum *
+checksum_from_hex_digest(const metalink_digest_def *digest_def,
+ const char *hex_digest)
{
metalink_checksum *chksum;
unsigned char *digest;
@@ -754,8 +756,8 @@ static metalinkfile *new_metalinkfile(metalink_file_t *fileinfo)
if(curl_strequal(digest_alias->alias_name, (*p)->type) &&
check_hex_digest((*p)->hash, digest_alias->digest_def)) {
f->checksum =
- new_metalink_checksum_from_hex_digest(digest_alias->digest_def,
- (*p)->hash);
+ checksum_from_hex_digest(digest_alias->digest_def,
+ (*p)->hash);
break;
}
}
@@ -891,7 +893,8 @@ int parse_metalink(struct OperationConfig *config, struct OutStruct *outs,
size_t metalink_write_cb(void *buffer, size_t sz, size_t nmemb,
void *userdata)
{
- struct OutStruct *outs = userdata;
+ struct per_transfer *per = userdata;
+ struct OutStruct *outs = &per->outs;
struct OperationConfig *config = outs->config;
int rv;
diff --git a/src/tool_metalink.h b/src/tool_metalink.h
index 7ee2736a6..1e367033c 100644
--- a/src/tool_metalink.h
+++ b/src/tool_metalink.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2014, 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -22,6 +22,7 @@
*
***************************************************************************/
#include "tool_setup.h"
+#include "tool_sdecls.h"
struct GlobalConfig;
struct OperationConfig;
diff --git a/src/tool_operate.c b/src/tool_operate.c
index bf9a9b8d8..d2ad9642d 100644
--- a/src/tool_operate.c
+++ b/src/tool_operate.c
@@ -29,6 +29,10 @@
# include <locale.h>
#endif
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+
#ifdef __VMS
# include <fabdef.h>
#endif
@@ -75,6 +79,7 @@
#include "tool_vms.h"
#include "tool_help.h"
#include "tool_hugehelp.h"
+#include "tool_progress.h"
#include "memdebug.h" /* keep this as LAST include */
@@ -98,6 +103,11 @@ CURLcode curl_easy_perform_ev(CURL *easy);
"this situation and\nhow to fix it, please visit the web page mentioned " \
"above.\n"
+static CURLcode create_transfers(struct GlobalConfig *global,
+ struct OperationConfig *config,
+ CURLSH *share,
+ bool capath_from_env);
+
static bool is_fatal_error(CURLcode code)
{
switch(code) {
@@ -187,135 +197,474 @@ static curl_off_t VmsSpecialSize(const char *name,
#define BUFFER_SIZE (100*1024)
-static CURLcode operate_do(struct GlobalConfig *global,
- struct OperationConfig *config)
+struct per_transfer *transfers; /* first node */
+static struct per_transfer *transfersl; /* last node */
+
+static CURLcode add_transfer(struct per_transfer **per)
{
- char errorbuffer[CURL_ERROR_SIZE];
- struct ProgressData progressbar;
- struct getout *urlnode;
+ struct per_transfer *p;
+ p = calloc(sizeof(struct per_transfer), 1);
+ if(!p)
+ return CURLE_OUT_OF_MEMORY;
+ if(!transfers)
+ /* first entry */
+ transfersl = transfers = p;
+ else {
+ /* make the last node point to the new node */
+ transfersl->next = p;
+ /* make the new node point back to the formerly last node */
+ p->prev = transfersl;
+ /* move the last node pointer to the new entry */
+ transfersl = p;
+ }
+ *per = p;
+ all_xfers++; /* count total number of transfers added */
+ return CURLE_OK;
+}
- struct HdrCbData hdrcbdata;
- struct OutStruct heads;
+/* Remove the specified transfer from the list (and free it), return the next
+ in line */
+static struct per_transfer *del_transfer(struct per_transfer *per)
+{
+ struct per_transfer *n;
+ struct per_transfer *p;
+ DEBUGASSERT(transfers);
+ DEBUGASSERT(transfersl);
+ DEBUGASSERT(per);
- metalinkfile *mlfile_last = NULL;
+ n = per->next;
+ p = per->prev;
- CURL *curl = config->easy;
- char *httpgetfields = NULL;
+ if(p)
+ p->next = n;
+ else
+ transfers = n;
+ if(n)
+ n->prev = p;
+ else
+ transfersl = p;
+
+ free(per);
+
+ return n;
+}
+
+static CURLcode pre_transfer(struct GlobalConfig *global,
+ struct per_transfer *per)
+{
+ curl_off_t uploadfilesize = -1;
+ struct_stat fileinfo;
CURLcode result = CURLE_OK;
- unsigned long li;
- bool capath_from_env;
- /* Save the values of noprogress and isatty to restore them later on */
- bool orig_noprogress = global->noprogress;
- bool orig_isatty = global->isatty;
+ if(per->separator_err)
+ fprintf(global->errors, "%s\n", per->separator_err);
+ if(per->separator)
+ printf("%s\n", per->separator);
+
+ if(per->uploadfile && !stdin_upload(per->uploadfile)) {
+ /* VMS Note:
+ *
+ * Reading binary from files can be a problem... Only FIXED, VAR
+ * etc WITHOUT implied CC will work Others need a \n appended to a
+ * line
+ *
+ * - Stat gives a size but this is UNRELIABLE in VMS As a f.e. a
+ * fixed file with implied CC needs to have a byte added for every
+ * record processed, this can by derived from Filesize & recordsize
+ * for VARiable record files the records need to be counted! for
+ * every record add 1 for linefeed and subtract 2 for the record
+ * header for VARIABLE header files only the bare record data needs
+ * to be considered with one appended if implied CC
+ */
+#ifdef __VMS
+ /* Calculate the real upload size for VMS */
+ per->infd = -1;
+ if(stat(per->uploadfile, &fileinfo) == 0) {
+ fileinfo.st_size = VmsSpecialSize(uploadfile, &fileinfo);
+ switch(fileinfo.st_fab_rfm) {
+ case FAB$C_VAR:
+ case FAB$C_VFC:
+ case FAB$C_STMCR:
+ per->infd = open(per->uploadfile, O_RDONLY | O_BINARY);
+ break;
+ default:
+ per->infd = open(per->uploadfile, O_RDONLY | O_BINARY,
+ "rfm=stmlf", "ctx=stm");
+ }
+ }
+ if(per->infd == -1)
+#else
+ per->infd = open(per->uploadfile, O_RDONLY | O_BINARY);
+ if((per->infd == -1) || fstat(per->infd, &fileinfo))
+#endif
+ {
+ helpf(global->errors, "Can't open '%s'!\n", per->uploadfile);
+ if(per->infd != -1) {
+ close(per->infd);
+ per->infd = STDIN_FILENO;
+ }
+ return CURLE_READ_ERROR;
+ }
+ per->infdopen = TRUE;
- errorbuffer[0] = '\0';
+ /* we ignore file size for char/block devices, sockets, etc. */
+ if(S_ISREG(fileinfo.st_mode))
+ uploadfilesize = fileinfo.st_size;
- /* default headers output stream is stdout */
- memset(&hdrcbdata, 0, sizeof(struct HdrCbData));
- memset(&heads, 0, sizeof(struct OutStruct));
- heads.stream = stdout;
- heads.config = config;
+ if(uploadfilesize != -1)
+ my_setopt(per->curl, CURLOPT_INFILESIZE_LARGE, uploadfilesize);
+ per->input.fd = per->infd;
+ }
+ show_error:
+ return result;
+}
- /*
- ** Beyond this point no return'ing from this function allowed.
- ** Jump to label 'quit_curl' in order to abandon this function
- ** from outside of nested loops further down below.
- */
+/*
+ * Call this after a transfer has completed.
+ */
+static CURLcode post_transfer(struct GlobalConfig *global,
+ CURLSH *share,
+ struct per_transfer *per,
+ CURLcode result,
+ bool *retryp)
+{
+ struct OutStruct *outs = &per->outs;
+ CURL *curl = per->curl;
+ struct OperationConfig *config = per->config;
- /* Check we have a url */
- if(!config->url_list || !config->url_list->url) {
- helpf(global->errors, "no URL specified!\n");
- result = CURLE_FAILED_INIT;
- goto quit_curl;
+ *retryp = FALSE;
+
+ if(per->infdopen)
+ close(per->infd);
+
+#ifdef __VMS
+ if(is_vms_shell()) {
+ /* VMS DCL shell behavior */
+ if(!global->showerror)
+ vms_show = VMSSTS_HIDE;
}
+ else
+#endif
+ if(config->synthetic_error) {
+ ;
+ }
+ else if(result && global->showerror) {
+ fprintf(global->errors, "curl: (%d) %s\n", result,
+ (per->errorbuffer[0]) ? per->errorbuffer :
+ curl_easy_strerror(result));
+ if(result == CURLE_PEER_FAILED_VERIFICATION)
+ fputs(CURL_CA_CERT_ERRORMSG, global->errors);
+ }
- /* On WIN32 we can't set the path to curl-ca-bundle.crt
- * at compile time. So we look here for the file in two ways:
- * 1: look at the environment variable CURL_CA_BUNDLE for a path
- * 2: if #1 isn't found, use the windows API function SearchPath()
- * to find it along the app's path (includes app's dir and CWD)
- *
- * We support the environment variable thing for non-Windows platforms
- * too. Just for the sake of it.
- */
- capath_from_env = false;
- if(!config->cacert &&
- !config->capath &&
- !config->insecure_ok) {
- struct curl_tlssessioninfo *tls_backend_info = NULL;
+ /* Set file extended attributes */
+ if(!result && config->xattr && outs->fopened && outs->stream) {
+ int rc = fwrite_xattr(curl, fileno(outs->stream));
+ if(rc)
+ warnf(config->global, "Error setting extended attributes: %s\n",
+ strerror(errno));
+ }
- /* With the addition of CAINFO support for Schannel, this search could find
- * a certificate bundle that was previously ignored. To maintain backward
- * compatibility, only perform this search if not using Schannel.
- */
- result = curl_easy_getinfo(config->easy,
- CURLINFO_TLS_SSL_PTR,
- &tls_backend_info);
- if(result) {
- goto quit_curl;
+ if(!result && !outs->stream && !outs->bytes) {
+ /* we have received no data despite the transfer was successful
+ ==> force cration of an empty output file (if an output file
+ was specified) */
+ long cond_unmet = 0L;
+ /* do not create (or even overwrite) the file in case we get no
+ data because of unmet condition */
+ curl_easy_getinfo(curl, CURLINFO_CONDITION_UNMET, &cond_unmet);
+ if(!cond_unmet && !tool_create_output_file(outs))
+ result = CURLE_WRITE_ERROR;
+ }
+
+ if(!outs->s_isreg && outs->stream) {
+ /* Dump standard stream buffered data */
+ int rc = fflush(outs->stream);
+ if(!result && rc) {
+ /* something went wrong in the writing process */
+ result = CURLE_WRITE_ERROR;
+ fprintf(global->errors, "(%d) Failed writing body\n", result);
}
+ }
- /* Set the CA cert locations specified in the environment. For Windows if
- * no environment-specified filename is found then check for CA bundle
- * default filename curl-ca-bundle.crt in the user's PATH.
- *
- * 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(tls_backend_info->backend != CURLSSLBACKEND_SCHANNEL) {
- char *env;
- env = curlx_getenv("CURL_CA_BUNDLE");
- if(env) {
- config->cacert = strdup(env);
- if(!config->cacert) {
- curl_free(env);
- helpf(global->errors, "out of memory\n");
- result = CURLE_OUT_OF_MEMORY;
- goto quit_curl;
+#ifdef USE_METALINK
+ if(per->metalink && !per->metalink_next_res)
+ fprintf(global->errors, "Metalink: fetching (%s) from (%s) OK\n",
+ per->mlfile->filename, per->this_url);
+
+ if(!per->metalink && config->use_metalink && result == CURLE_OK) {
+ int rv = parse_metalink(config, outs, per->this_url);
+ if(!rv) {
+ fprintf(config->global->errors, "Metalink: parsing (%s) OK\n",
+ per->this_url);
+ }
+ else if(rv == -1)
+ fprintf(config->global->errors, "Metalink: parsing (%s) FAILED\n",
+ per->this_url);
+ result = create_transfers(global, config, share, FALSE);
+ }
+ else if(per->metalink && result == CURLE_OK && !per->metalink_next_res) {
+ int rv;
+ (void)fflush(outs->stream);
+ rv = metalink_check_hash(global, per->mlfile, outs->filename);
+ if(!rv)
+ per->metalink_next_res = 1;
+ }
+#else
+ (void)share;
+#endif /* USE_METALINK */
+
+#ifdef USE_METALINK
+ if(outs->metalink_parser)
+ metalink_parser_context_delete(outs->metalink_parser);
+#endif /* USE_METALINK */
+
+ if(outs->is_cd_filename && outs->stream && !global->mute &&
+ outs->filename)
+ printf("curl: Saved to filename '%s'\n", outs->filename);
+
+ /* if retry-max-time is non-zero, make sure we haven't exceeded the
+ time */
+ if(per->retry_numretries &&
+ (!config->retry_maxtime ||
+ (tvdiff(tvnow(), per->retrystart) <
+ config->retry_maxtime*1000L)) ) {
+ enum {
+ RETRY_NO,
+ RETRY_TIMEOUT,
+ RETRY_CONNREFUSED,
+ RETRY_HTTP,
+ RETRY_FTP,
+ RETRY_LAST /* not used */
+ } retry = RETRY_NO;
+ long response;
+ if((CURLE_OPERATION_TIMEDOUT == result) ||
+ (CURLE_COULDNT_RESOLVE_HOST == result) ||
+ (CURLE_COULDNT_RESOLVE_PROXY == result) ||
+ (CURLE_FTP_ACCEPT_TIMEOUT == result))
+ /* retry timeout always */
+ retry = RETRY_TIMEOUT;
+ else if(config->retry_connrefused &&
+ (CURLE_COULDNT_CONNECT == result)) {
+ long oserrno;
+ curl_easy_getinfo(curl, CURLINFO_OS_ERRNO, &oserrno);
+ if(ECONNREFUSED == oserrno)
+ retry = RETRY_CONNREFUSED;
+ }
+ else if((CURLE_OK == result) ||
+ (config->failonerror &&
+ (CURLE_HTTP_RETURNED_ERROR == result))) {
+ /* If it returned OK. _or_ failonerror was enabled and it
+ returned due to such an error, check for HTTP transient
+ errors to retry on. */
+ long protocol;
+ curl_easy_getinfo(curl, CURLINFO_PROTOCOL, &protocol);
+ if((protocol == CURLPROTO_HTTP) || (protocol == CURLPROTO_HTTPS)) {
+ /* This was HTTP(S) */
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
+
+ switch(response) {
+ case 500: /* Internal Server Error */
+ case 502: /* Bad Gateway */
+ case 503: /* Service Unavailable */
+ case 504: /* Gateway Timeout */
+ retry = RETRY_HTTP;
+ /*
+ * At this point, we have already written data to the output
+ * file (or terminal). If we write to a file, we must rewind
+ * or close/re-open the file so that the next attempt starts
+ * over from the beginning.
+ *
+ * TODO: similar action for the upload case. We might need
+ * to start over reading from a previous point if we have
+ * uploaded something when this was returned.
+ */
+ break;
}
}
- else {
- env = curlx_getenv("SSL_CERT_DIR");
- if(env) {
- config->capath = strdup(env);
- if(!config->capath) {
- curl_free(env);
- helpf(global->errors, "out of memory\n");
- result = CURLE_OUT_OF_MEMORY;
- goto quit_curl;
- }
- capath_from_env = true;
- }
- else {
- env = curlx_getenv("SSL_CERT_FILE");
- if(env) {
- config->cacert = strdup(env);
- if(!config->cacert) {
- curl_free(env);
- helpf(global->errors, "out of memory\n");
- result = CURLE_OUT_OF_MEMORY;
- goto quit_curl;
- }
- }
+ } /* if CURLE_OK */
+ else if(result) {
+ long protocol;
+
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
+ curl_easy_getinfo(curl, CURLINFO_PROTOCOL, &protocol);
+
+ if((protocol == CURLPROTO_FTP || protocol == CURLPROTO_FTPS) &&
+ response / 100 == 4)
+ /*
+ * This is typically when the FTP server only allows a certain
+ * amount of users and we are not one of them. All 4xx codes
+ * are transient.
+ */
+ retry = RETRY_FTP;
+ }
+
+ if(retry) {
+ long sleeptime = 0;
+ curl_off_t retry_after = 0;
+ static const char * const m[]={
+ NULL,
+ "timeout",
+ "connection refused",
+ "HTTP error",
+ "FTP error"
+ };
+
+ sleeptime = per->retry_sleep;
+ if(RETRY_HTTP == retry) {
+ curl_easy_getinfo(curl, CURLINFO_RETRY_AFTER, &retry_after);
+ if(retry_after) {
+ /* store in a 'long', make sure it doesn't overflow */
+ if(retry_after > LONG_MAX/1000)
+ sleeptime = LONG_MAX;
+ else
+ sleeptime = (long)retry_after * 1000; /* milliseconds */
}
}
-
- if(env)
- curl_free(env);
-#ifdef WIN32
- else {
- result = FindWin32CACert(config, tls_backend_info->backend,
- "curl-ca-bundle.crt");
- if(result)
- goto quit_curl;
+ warnf(config->global, "Transient problem: %s "
+ "Will retry in %ld seconds. "
+ "%ld retries left.\n",
+ m[retry], per->retry_sleep/1000L, per->retry_numretries);
+
+ per->retry_numretries--;
+ tool_go_sleep(sleeptime);
+ if(!config->retry_delay) {
+ per->retry_sleep *= 2;
+ if(per->retry_sleep > RETRY_SLEEP_MAX)
+ per->retry_sleep = RETRY_SLEEP_MAX;
}
+ if(outs->bytes && outs->filename && outs->stream) {
+ int rc;
+ /* We have written data to a output file, we truncate file
+ */
+ if(!global->mute)
+ fprintf(global->errors, "Throwing away %"
+ CURL_FORMAT_CURL_OFF_T " bytes\n",
+ outs->bytes);
+ fflush(outs->stream);
+ /* truncate file at the position where we started appending */
+#ifdef HAVE_FTRUNCATE
+ if(ftruncate(fileno(outs->stream), outs->init)) {
+ /* when truncate fails, we can't just append as then we'll
+ create something strange, bail out */
+ if(!global->mute)
+ fprintf(global->errors,
+ "failed to truncate, exiting\n");
+ return CURLE_WRITE_ERROR;
+ }
+ /* now seek to the end of the file, the position where we
+ just truncated the file in a large file-safe way */
+ rc = fseek(outs->stream, 0, SEEK_END);
+#else
+ /* ftruncate is not available, so just reposition the file
+ to the location we would have truncated it. This won't
+ work properly with large files on 32-bit systems, but
+ most of those will have ftruncate. */
+ rc = fseek(outs->stream, (long)outs->init, SEEK_SET);
#endif
+ if(rc) {
+ if(!global->mute)
+ fprintf(global->errors,
+ "failed seeking to end of file, exiting\n");
+ return CURLE_WRITE_ERROR;
+ }
+ outs->bytes = 0; /* clear for next round */
+ }
+ *retryp = TRUE; /* curl_easy_perform loop */
+ return CURLE_OK;
+ }
+ } /* if retry_numretries */
+ else if(per->metalink) {
+ /* Metalink: Decide to try the next resource or not. Try the next resource
+ if download was not successful. */
+ long response;
+ if(CURLE_OK == result) {
+ /* TODO We want to try next resource when download was
+ not successful. How to know that? */
+ char *effective_url = NULL;
+ curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &effective_url);
+ if(effective_url &&
+ curl_strnequal(effective_url, "http", 4)) {
+ /* This was HTTP(S) */
+ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
+ if(response != 200 && response != 206) {
+ per->metalink_next_res = 1;
+ fprintf(global->errors,
+ "Metalink: fetching (%s) from (%s) FAILED "
+ "(HTTP status code %ld)\n",
+ per->mlfile->filename, per->this_url, response);
+ }
+ }
+ }
+ else {
+ per->metalink_next_res = 1;
+ fprintf(global->errors,
+ "Metalink: fetching (%s) from (%s) FAILED (%s)\n",
+ per->mlfile->filename, per->this_url,
+ curl_easy_strerror(result));
}
}
+ if((global->progressmode == CURL_PROGRESS_BAR) &&
+ per->progressbar.calls)
+ /* if the custom progress bar has been displayed, we output a
+ newline here */
+ fputs("\n", per->progressbar.out);
+
+ if(config->writeout)
+ ourWriteOut(per->curl, &per->outs, config->writeout);
+
+ /* Close the outs file */
+ if(outs->fopened && outs->stream) {
+ int rc = fclose(outs->stream);
+ if(!result && rc) {
+ /* something went wrong in the writing process */
+ result = CURLE_WRITE_ERROR;
+ fprintf(global->errors, "(%d) Failed writing body\n", result);
+ }
+ }
+
+ /* File time can only be set _after_ the file has been closed */
+ if(!result && config->remote_time && outs->s_isreg && outs->filename) {
+ /* Ask libcurl if we got a remote file time */
+ curl_off_t filetime = -1;
+ curl_easy_getinfo(curl, CURLINFO_FILETIME_T, &filetime);
+ setfiletime(filetime, outs->filename, config->global->errors);
+ }
+
+ /* Close function-local opened file descriptors */
+ if(per->heads.fopened && per->heads.stream)
+ fclose(per->heads.stream);
+
+ if(per->heads.alloc_filename)
+ Curl_safefree(per->heads.filename);
+
+ curl_easy_cleanup(per->curl);
+ if(outs->alloc_filename)
+ free(outs->filename);
+ free(per->this_url);
+ free(per->separator_err);
+ free(per->separator);
+ free(per->outfile);
+ free(per->uploadfile);
+
+ return CURLE_OK;
+}
+
+/* go through the list of URLs and configs and add transfers */
+
+static CURLcode create_transfers(struct GlobalConfig *global,
+ struct OperationConfig *config,
+ CURLSH *share,
+ bool capath_from_env)
+{
+ CURLcode result = CURLE_OK;
+ struct getout *urlnode;
+ metalinkfile *mlfile_last = NULL;
+ bool orig_noprogress = global->noprogress;
+ bool orig_isatty = global->isatty;
+ char *httpgetfields = NULL;
+
if(config->postfields) {
if(config->use_httpget) {
/* Use the postfields data for a http get */
@@ -341,44 +690,14 @@ static CURLcode operate_do(struct GlobalConfig *global,
}
}
- /* Single header file for all URLs */
- if(config->headerfile) {
- /* open file for output: */
- if(strcmp(config->headerfile, "-")) {
- FILE *newfile = fopen(config->headerfile, "wb");
- if(!newfile) {
- warnf(config->global, "Failed to open %s\n", config->headerfile);
- result = CURLE_WRITE_ERROR;
- goto quit_curl;
- }
- else {
- heads.filename = config->headerfile;
- heads.s_isreg = TRUE;
- heads.fopened = TRUE;
- heads.stream = newfile;
- }
- }
- else {
- /* always use binary mode for protocol header output */
- set_binmode(heads.stream);
- }
- }
-
- /*
- ** Nested loops start here.
- */
-
- /* loop through the list of given URLs */
-
for(urlnode = config->url_list; urlnode; urlnode = urlnode->next) {
-
+ unsigned long li;
unsigned long up; /* upload file counter within a single upload glob */
char *infiles; /* might be a glob pattern */
char *outfiles;
unsigned long infilenum;
URLGlob *inglob;
-
- int metalink = 0; /* nonzero for metalink download. */
+ bool metalink = FALSE; /* metalink download? */
metalinkfile *mlfile;
metalink_resource *mlres;
@@ -461,8 +780,6 @@ static CURLcode operate_do(struct GlobalConfig *global,
result = CURLE_OUT_OF_MEMORY;
}
}
- else
- uploadfile = NULL;
if(!uploadfile)
break;
}
@@ -490,65 +807,102 @@ static CURLcode operate_do(struct GlobalConfig *global,
/* Here's looping around each globbed URL */
for(li = 0 ; li < urlnum; li++) {
+ struct per_transfer *per;
+ struct OutStruct *outs;
+ struct InStruct *input;
+ struct OutStruct *heads;
+ struct HdrCbData *hdrcbdata = NULL;
+ CURL *curl = curl_easy_init();
+
+ result = add_transfer(&per);
+ if(result || !curl) {
+ free(uploadfile);
+ curl_easy_cleanup(curl);
+ result = CURLE_OUT_OF_MEMORY;
+ goto show_error;
+ }
+ per->config = config;
+ per->curl = curl;
+ per->uploadfile = uploadfile;
+
+ /* default headers output stream is stdout */
+ heads = &per->heads;
+ heads->stream = stdout;
+ heads->config = config;
+
+ /* Single header file for all URLs */
+ if(config->headerfile) {
+ /* open file for output: */
+ if(strcmp(config->headerfile, "-")) {
+ FILE *newfile = fopen(config->headerfile, "wb");
+ if(!newfile) {
+ warnf(config->global, "Failed to open %s\n", config->headerfile);
+ result = CURLE_WRITE_ERROR;
+ goto quit_curl;
+ }
+ else {
+ heads->filename = config->headerfile;
+ heads->s_isreg = TRUE;
+ heads->fopened = TRUE;
+ heads->stream = newfile;
+ }
+ }
+ else {
+ /* always use binary mode for protocol header output */
+ set_binmode(heads->stream);
+ }
+ }
+
- int infd;
- bool infdopen;
- char *outfile;
- struct OutStruct outs;
- struct InStruct input;
- struct timeval retrystart;
- curl_off_t uploadfilesize;
- long retry_numretries;
- long retry_sleep_default;
- long retry_sleep;
- char *this_url = NULL;
- int metalink_next_res = 0;
-
- outfile = NULL;
- infdopen = FALSE;
- infd = STDIN_FILENO;
- uploadfilesize = -1; /* -1 means unknown */
+ hdrcbdata = &per->hdrcbdata;
+
+ outs = &per->outs;
+ input = &per->input;
+
+ per->outfile = NULL;
+ per->infdopen = FALSE;
+ per->infd = STDIN_FILENO;
/* default output stream is stdout */
- memset(&outs, 0, sizeof(struct OutStruct));
- outs.stream = stdout;
- outs.config = config;
+ outs->stream = stdout;
+ outs->config = config;
if(metalink) {
/* For Metalink download, use name in Metalink file as
filename. */
- outfile = strdup(mlfile->filename);
- if(!outfile) {
+ per->outfile = strdup(mlfile->filename);
+ if(!per->outfile) {
result = CURLE_OUT_OF_MEMORY;
goto show_error;
}
- this_url = strdup(mlres->url);
- if(!this_url) {
+ per->this_url = strdup(mlres->url);
+ if(!per->this_url) {
result = CURLE_OUT_OF_MEMORY;
goto show_error;
}
+ per->mlfile = mlfile;
}
else {
if(urls) {
- result = glob_next_url(&this_url, urls);
+ result = glob_next_url(&per->this_url, urls);
if(result)
goto show_error;
}
else if(!li) {
- this_url = strdup(urlnode->url);
- if(!this_url) {
+ per->this_url = strdup(urlnode->url);
+ if(!per->this_url) {
result = CURLE_OUT_OF_MEMORY;
goto show_error;
}
}
else
- this_url = NULL;
- if(!this_url)
+ per->this_url = NULL;
+ if(!per->this_url)
break;
if(outfiles) {
- outfile = strdup(outfiles);
- if(!outfile) {
+ per->outfile = strdup(outfiles);
+ if(!per->outfile) {
result = CURLE_OUT_OF_MEMORY;
goto show_error;
}
@@ -556,7 +910,7 @@ static CURLcode operate_do(struct GlobalConfig *global,
}
if(((urlnode->flags&GETOUT_USEREMOTE) ||
- (outfile && strcmp("-", outfile))) &&
+ (per->outfile && strcmp("-", per->outfile))) &&
(metalink || !config->use_metalink)) {
/*
@@ -564,12 +918,12 @@ static CURLcode operate_do(struct GlobalConfig *global,
* decided we want to use the remote file name.
*/
- if(!outfile) {
+ if(!per->outfile) {
/* extract the file name from the URL */
- result = get_url_file_name(&outfile, this_url);
+ result = get_url_file_name(&per->outfile, per->this_url);
if(result)
goto show_error;
- if(!*outfile && !config->content_disposition) {
+ if(!*per->outfile && !config->content_disposition) {
helpf(global->errors, "Remote file name has no length!\n");
result = CURLE_WRITE_ERROR;
goto quit_urls;
@@ -577,8 +931,8 @@ static CURLcode operate_do(struct GlobalConfig *global,
}
else if(urls) {
/* fill '#1' ... '#9' terms from URL pattern */
- char *storefile = outfile;
- result = glob_match_url(&outfile, storefile, urls);
+ char *storefile = per->outfile;
+ result = glob_match_url(&per->outfile, storefile, urls);
Curl_safefree(storefile);
if(result) {
/* bad globbing */
@@ -591,7 +945,7 @@ static CURLcode operate_do(struct GlobalConfig *global,
file output call */
if(config->create_dirs || metalink) {
- result = create_dir_hierarchy(outfile, global->errors);
+ result = create_dir_hierarchy(per->outfile, global->errors);
/* create_dir_hierarchy shows error upon CURLE_WRITE_ERROR */
if(result == CURLE_WRITE_ERROR)
goto quit_urls;
@@ -603,7 +957,7 @@ static CURLcode operate_do(struct GlobalConfig *global,
if((urlnode->flags & GETOUT_USEREMOTE)
&& config->content_disposition) {
/* Our header callback MIGHT set the filename */
- DEBUGASSERT(!outs.filename);
+ DEBUGASSERT(!outs->filename);
}
if(config->resume_from_current) {
@@ -611,7 +965,7 @@ static CURLcode operate_do(struct GlobalConfig *global,
of the file as it is now and open it for append instead */
struct_stat fileinfo;
/* VMS -- Danger, the filesize is only valid for stream files */
- if(0 == stat(outfile, &fileinfo))
+ if(0 == stat(per->outfile, &fileinfo))
/* set offset to current file size: */
config->resume_from = fileinfo.st_size;
else
@@ -627,87 +981,36 @@ static CURLcode operate_do(struct GlobalConfig *global,
"ctx=stm", "rfm=stmlf", "rat=cr", "mrs=0");
#else
/* open file for output: */
- FILE *file = fopen(outfile, config->resume_from?"ab":"wb");
+ FILE *file = fopen(per->outfile, config->resume_from?"ab":"wb");
#endif
if(!file) {
- helpf(global->errors, "Can't open '%s'!\n", outfile);
+ helpf(global->errors, "Can't open '%s'!\n", per->outfile);
result = CURLE_WRITE_ERROR;
goto quit_urls;
}
- outs.fopened = TRUE;
- outs.stream = file;
- outs.init = config->resume_from;
+ outs->fopened = TRUE;
+ outs->stream = file;
+ outs->init = config->resume_from;
}
else {
- outs.stream = NULL; /* open when needed */
+ outs->stream = NULL; /* open when needed */
}
- outs.filename = outfile;
- outs.s_isreg = TRUE;
+ outs->filename = per->outfile;
+ outs->s_isreg = TRUE;
}
- if(uploadfile && !stdin_upload(uploadfile)) {
+ if(per->uploadfile && !stdin_upload(per->uploadfile)) {
/*
* We have specified a file to upload and it isn't "-".
*/
- struct_stat fileinfo;
-
- this_url = add_file_name_to_url(curl, this_url, uploadfile);
- if(!this_url) {
+ char *nurl = add_file_name_to_url(per->this_url, per->uploadfile);
+ if(!nurl) {
result = CURLE_OUT_OF_MEMORY;
goto show_error;
}
- /* VMS Note:
- *
- * Reading binary from files can be a problem... Only FIXED, VAR
- * etc WITHOUT implied CC will work Others need a \n appended to a
- * line
- *
- * - Stat gives a size but this is UNRELIABLE in VMS As a f.e. a
- * fixed file with implied CC needs to have a byte added for every
- * record processed, this can by derived from Filesize & recordsize
- * for VARiable record files the records need to be counted! for
- * every record add 1 for linefeed and subtract 2 for the record
- * header for VARIABLE header files only the bare record data needs
- * to be considered with one appended if implied CC
- */
-#ifdef __VMS
- /* Calculate the real upload size for VMS */
- infd = -1;
- if(stat(uploadfile, &fileinfo) == 0) {
- fileinfo.st_size = VmsSpecialSize(uploadfile, &fileinfo);
- switch(fileinfo.st_fab_rfm) {
- case FAB$C_VAR:
- case FAB$C_VFC:
- case FAB$C_STMCR:
- infd = open(uploadfile, O_RDONLY | O_BINARY);
- break;
- default:
- infd = open(uploadfile, O_RDONLY | O_BINARY,
- "rfm=stmlf", "ctx=stm");
- }
- }
- if(infd == -1)
-#else
- infd = open(uploadfile, O_RDONLY | O_BINARY);
- if((infd == -1) || fstat(infd, &fileinfo))
-#endif
- {
- helpf(global->errors, "Can't open '%s'!\n", uploadfile);
- if(infd != -1) {
- close(infd);
- infd = STDIN_FILENO;
- }
- result = CURLE_READ_ERROR;
- goto quit_urls;
- }
- infdopen = TRUE;
-
- /* we ignore file size for char/block devices, sockets, etc. */
- if(S_ISREG(fileinfo.st_mode))
- uploadfilesize = fileinfo.st_size;
-
+ per->this_url = nurl;
}
- else if(uploadfile && stdin_upload(uploadfile)) {
+ else if(per->uploadfile && stdin_upload(per->uploadfile)) {
/* count to see if there are more than one auth bit set
in the authtype field */
int authbits = 0;
@@ -733,22 +1036,22 @@ static CURLcode operate_do(struct GlobalConfig *global,
" file or a fixed auth type instead!\n");
}
- DEBUGASSERT(infdopen == FALSE);
- DEBUGASSERT(infd == STDIN_FILENO);
+ DEBUGASSERT(per->infdopen == FALSE);
+ DEBUGASSERT(per->infd == STDIN_FILENO);
set_binmode(stdin);
- if(!strcmp(uploadfile, ".")) {
- if(curlx_nonblock((curl_socket_t)infd, TRUE) < 0)
+ if(!strcmp(per->uploadfile, ".")) {
+ if(curlx_nonblock((curl_socket_t)per->infd, TRUE) < 0)
warnf(config->global,
- "fcntl failed on fd=%d: %s\n", infd, strerror(errno));
+ "fcntl failed on fd=%d: %s\n", per->infd, strerror(errno));
}
}
- if(uploadfile && config->resume_from_current)
+ if(per->uploadfile && config->resume_from_current)
config->resume_from = -1; /* -1 will then force get-it-yourself */
- if(output_expected(this_url, uploadfile) && outs.stream &&
- isatty(fileno(outs.stream)))
+ if(output_expected(per->this_url, per->uploadfile) && outs->stream &&
+ isatty(fileno(outs->stream)))
/* we send the output to a tty, therefore we switch off the progress
meter */
global->noprogress = global->isatty = TRUE;
@@ -760,20 +1063,22 @@ static CURLcode operate_do(struct GlobalConfig *global,
}
if(urlnum > 1 && !global->mute) {
- fprintf(global->errors, "\n[%lu/%lu]: %s --> %s\n",
- li + 1, urlnum, this_url, outfile ? outfile : "<stdout>");
+ per->separator_err =
+ aprintf("\n[%lu/%lu]: %s --> %s",
+ li + 1, urlnum, per->this_url,
+ per->outfile ? per->outfile : "<stdout>");
if(separator)
- printf("%s%s\n", CURLseparator, this_url);
+ per->separator = aprintf("%s%s", CURLseparator, per->this_url);
}
if(httpgetfields) {
char *urlbuffer;
/* Find out whether the url contains a file name */
- const char *pc = strstr(this_url, "://");
+ const char *pc = strstr(per->this_url, "://");
char sep = '?';
if(pc)
pc += 3;
else
- pc = this_url;
+ pc = per->this_url;
pc = strrchr(pc, '/'); /* check for a slash */
@@ -789,33 +1094,41 @@ static CURLcode operate_do(struct GlobalConfig *global,
* Then append ? followed by the get fields to the url.
*/
if(pc)
- urlbuffer = aprintf("%s%c%s", this_url, sep, httpgetfields);
+ urlbuffer = aprintf("%s%c%s", per->this_url, sep, httpgetfields);
else
/* Append / before the ? to create a well-formed url
if the url contains a hostname only
*/
- urlbuffer = aprintf("%s/?%s", this_url, httpgetfields);
+ urlbuffer = aprintf("%s/?%s", per->this_url, httpgetfields);
if(!urlbuffer) {
result = CURLE_OUT_OF_MEMORY;
goto show_error;
}
- Curl_safefree(this_url); /* free previous URL */
- this_url = urlbuffer; /* use our new URL instead! */
+ Curl_safefree(per->this_url); /* free previous URL */
+ per->this_url = urlbuffer; /* use our new URL instead! */
}
if(!global->errors)
global->errors = stderr;
- if((!outfile || !strcmp(outfile, "-")) && !config->use_ascii) {
+ if((!per->outfile || !strcmp(per->outfile, "-")) &&
+ !config->use_ascii) {
/* We get the output to stdout and we have not got the ASCII/text
flag, then set stdout to be binary */
set_binmode(stdout);
}
/* explicitly passed to stdout means okaying binary gunk */
- config->terminal_binary_ok = (outfile && !strcmp(outfile, "-"));
+ config->terminal_binary_ok =
+ (per->outfile && !strcmp(per->outfile, "-"));
+
+ /* avoid having this setopt added to the --libcurl source
+ output */
+ result = curl_easy_setopt(curl, CURLOPT_SHARE, share);
+ if(result)
+ goto show_error;
if(!config->tcp_nodelay)
my_setopt(curl, CURLOPT_TCP_NODELAY, 0L);
@@ -824,8 +1137,8 @@ static CURLcode operate_do(struct GlobalConfig *global,
my_setopt(curl, CURLOPT_TCP_FASTOPEN, 1L);
/* where to store */
- my_setopt(curl, CURLOPT_WRITEDATA, &outs);
- my_setopt(curl, CURLOPT_INTERLEAVEDATA, &outs);
+ my_setopt(curl, CURLOPT_WRITEDATA, per);
+ my_setopt(curl, CURLOPT_INTERLEAVEDATA, per);
if(metalink || !config->use_metalink)
/* what call to write */
@@ -838,8 +1151,7 @@ static CURLcode operate_do(struct GlobalConfig *global,
#endif /* USE_METALINK */
/* for uploads */
- input.fd = infd;
- input.config = config;
+ input->config = config;
/* Note that if CURLOPT_READFUNCTION is fread (the default), then
* lib/telnet.c will Curl_poll() on the input file descriptor
* rather then calling the READFUNCTION at regular intervals.
@@ -847,13 +1159,13 @@ static CURLcode operate_do(struct GlobalConfig *global,
* behaviour, by omitting to set the READFUNCTION & READDATA options,
* have not been determined.
*/
- my_setopt(curl, CURLOPT_READDATA, &input);
+ my_setopt(curl, CURLOPT_READDATA, input);
/* what call to read */
my_setopt(curl, CURLOPT_READFUNCTION, tool_read_cb);
/* in 7.18.0, the CURLOPT_SEEKFUNCTION/DATA pair is taking over what
CURLOPT_IOCTLFUNCTION/DATA pair previously provided for seeking */
- my_setopt(curl, CURLOPT_SEEKDATA, &input);
+ my_setopt(curl, CURLOPT_SEEKDATA, input);
my_setopt(curl, CURLOPT_SEEKFUNCTION, tool_seek_cb);
if(config->recvpersecond &&
@@ -863,10 +1175,7 @@ static CURLcode operate_do(struct GlobalConfig *global,
else
my_setopt(curl, CURLOPT_BUFFERSIZE, (long)BUFFER_SIZE);
- /* size of uploaded file: */
- if(uploadfilesize != -1)
- my_setopt(curl, CURLOPT_INFILESIZE_LARGE, uploadfilesize);
- my_setopt_str(curl, CURLOPT_URL, this_url); /* what to fetch */
+ my_setopt_str(curl, CURLOPT_URL, per->this_url);
my_setopt(curl, CURLOPT_NOPROGRESS, global->noprogress?1L:0L);
if(config->no_body)
my_setopt(curl, CURLOPT_NOBODY, 1L);
@@ -915,7 +1224,7 @@ static CURLcode operate_do(struct GlobalConfig *global,
my_setopt(curl, CURLOPT_FAILONERROR, config->failonerror?1L:0L);
my_setopt(curl, CURLOPT_REQUEST_TARGET, config->request_target);
- my_setopt(curl, CURLOPT_UPLOAD, uploadfile?1L:0L);
+ my_setopt(curl, CURLOPT_UPLOAD, per->uploadfile?1L:0L);
my_setopt(curl, CURLOPT_DIRLISTONLY, config->dirlistonly?1L:0L);
my_setopt(curl, CURLOPT_APPEND, config->ftp_append?1L:0L);
@@ -934,7 +1243,7 @@ static CURLcode operate_do(struct GlobalConfig *global,
my_setopt_str(curl, CURLOPT_LOGIN_OPTIONS, config->login_options);
my_setopt_str(curl, CURLOPT_USERPWD, config->userpwd);
my_setopt_str(curl, CURLOPT_RANGE, config->range);
- my_setopt(curl, CURLOPT_ERRORBUFFER, errorbuffer);
+ my_setopt(curl, CURLOPT_ERRORBUFFER, per->errorbuffer);
my_setopt(curl, CURLOPT_TIMEOUT_MS, (long)(config->timeout * 1000));
switch(config->httpreq) {
@@ -1182,7 +1491,7 @@ static CURLcode operate_do(struct GlobalConfig *global,
result = CURLE_OUT_OF_MEMORY;
home = homedir();
if(home) {
- file = aprintf("%s/%sssh/known_hosts", home, DOT_CHAR);
+ file = aprintf("%s/.ssh/known_hosts", home);
if(file) {
/* new in curl 7.19.6 */
result = res_setopt_str(curl, CURLOPT_SSH_KNOWNHOSTS, file);
@@ -1230,14 +1539,14 @@ static CURLcode operate_do(struct GlobalConfig *global,
/* three new ones in libcurl 7.3: */
my_setopt_str(curl, CURLOPT_INTERFACE, config->iface);
my_setopt_str(curl, CURLOPT_KRBLEVEL, config->krblevel);
+ progressbarinit(&per->progressbar, config);
- progressbarinit(&progressbar, config);
if((global->progressmode == CURL_PROGRESS_BAR) &&
!global->noprogress && !global->mute) {
/* we want the alternative style, then we have to implement it
ourselves! */
my_setopt(curl, CURLOPT_XFERINFOFUNCTION, tool_progress_cb);
- my_setopt(curl, CURLOPT_XFERINFODATA, &progressbar);
+ my_setopt(curl, CURLOPT_XFERINFODATA, &per->progressbar);
}
/* new in libcurl 7.24.0: */
@@ -1420,17 +1729,17 @@ static CURLcode operate_do(struct GlobalConfig *global,
if(config->content_disposition
&& (urlnode->flags & GETOUT_USEREMOTE))
- hdrcbdata.honor_cd_filename = TRUE;
+ hdrcbdata->honor_cd_filename = TRUE;
else
- hdrcbdata.honor_cd_filename = FALSE;
+ hdrcbdata->honor_cd_filename = FALSE;
- hdrcbdata.outs = &outs;
- hdrcbdata.heads = &heads;
- hdrcbdata.global = global;
- hdrcbdata.config = config;
+ hdrcbdata->outs = outs;
+ hdrcbdata->heads = heads;
+ hdrcbdata->global = global;
+ hdrcbdata->config = config;
my_setopt(curl, CURLOPT_HEADERFUNCTION, tool_header_cb);
- my_setopt(curl, CURLOPT_HEADERDATA, &hdrcbdata);
+ my_setopt(curl, CURLOPT_HEADERDATA, per);
if(config->resolve)
/* new in 7.21.3 */
@@ -1482,6 +1791,10 @@ static CURLcode operate_do(struct GlobalConfig *global,
if(config->mail_auth)
my_setopt_str(curl, CURLOPT_MAIL_AUTH, config->mail_auth);
+ /* new in 7.66.0 */
+ if(config->sasl_authzid)
+ my_setopt_str(curl, CURLOPT_SASL_AUTHZID, config->sasl_authzid);
+
/* new in 7.31.0 */
if(config->sasl_ir)
my_setopt(curl, CURLOPT_SASL_IR, 1L);
@@ -1505,6 +1818,7 @@ static CURLcode operate_do(struct GlobalConfig *global,
config->unix_socket_path);
}
}
+
/* new in 7.45.0 */
if(config->proto_default)
my_setopt_str(curl, CURLOPT_DEFAULT_PROTOCOL, config->proto_default);
@@ -1536,397 +1850,34 @@ static CURLcode operate_do(struct GlobalConfig *global,
my_setopt_str(curl, CURLOPT_ALTSVC, config->altsvc);
#endif
- /* initialize retry vars for loop below */
- retry_sleep_default = (config->retry_delay) ?
- config->retry_delay*1000L : RETRY_SLEEP_DEFAULT; /* ms */
-
- retry_numretries = config->req_retry;
- retry_sleep = retry_sleep_default; /* ms */
- retrystart = tvnow();
-
-#ifndef CURL_DISABLE_LIBCURL_OPTION
- if(global->libcurl) {
- result = easysrc_perform();
- if(result)
- goto show_error;
- }
-#endif
-
- for(;;) {
-#ifdef USE_METALINK
- if(!metalink && config->use_metalink) {
- /* If outs.metalink_parser is non-NULL, delete it first. */
- if(outs.metalink_parser)
- metalink_parser_context_delete(outs.metalink_parser);
- outs.metalink_parser = metalink_parser_context_new();
- if(outs.metalink_parser == NULL) {
- result = CURLE_OUT_OF_MEMORY;
- goto show_error;
- }
- fprintf(config->global->errors,
- "Metalink: parsing (%s) metalink/XML...\n", this_url);
- }
- else if(metalink)
- fprintf(config->global->errors,
- "Metalink: fetching (%s) from (%s)...\n",
- mlfile->filename, this_url);
-#endif /* USE_METALINK */
-
-#ifdef CURLDEBUG
- if(config->test_event_based)
- result = curl_easy_perform_ev(curl);
- else
-#endif
- result = curl_easy_perform(curl);
-
- if(!result && !outs.stream && !outs.bytes) {
- /* we have received no data despite the transfer was successful
- ==> force cration of an empty output file (if an output file
- was specified) */
- long cond_unmet = 0L;
- /* do not create (or even overwrite) the file in case we get no
- data because of unmet condition */
- curl_easy_getinfo(curl, CURLINFO_CONDITION_UNMET, &cond_unmet);
- if(!cond_unmet && !tool_create_output_file(&outs))
- result = CURLE_WRITE_ERROR;
- }
-
- if(outs.is_cd_filename && outs.stream && !global->mute &&
- outs.filename)
- printf("curl: Saved to filename '%s'\n", outs.filename);
-
- /* if retry-max-time is non-zero, make sure we haven't exceeded the
- time */
- if(retry_numretries &&
- (!config->retry_maxtime ||
- (tvdiff(tvnow(), retrystart) <
- config->retry_maxtime*1000L)) ) {
- enum {
- RETRY_NO,
- RETRY_TIMEOUT,
- RETRY_CONNREFUSED,
- RETRY_HTTP,
- RETRY_FTP,
- RETRY_LAST /* not used */
- } retry = RETRY_NO;
- long response;
- if((CURLE_OPERATION_TIMEDOUT == result) ||
- (CURLE_COULDNT_RESOLVE_HOST == result) ||
- (CURLE_COULDNT_RESOLVE_PROXY == result) ||
- (CURLE_FTP_ACCEPT_TIMEOUT == result))
- /* retry timeout always */
- retry = RETRY_TIMEOUT;
- else if(config->retry_connrefused &&
- (CURLE_COULDNT_CONNECT == result)) {
- long oserrno;
- curl_easy_getinfo(curl, CURLINFO_OS_ERRNO, &oserrno);
- if(ECONNREFUSED == oserrno)
- retry = RETRY_CONNREFUSED;
- }
- else if((CURLE_OK == result) ||
- (config->failonerror &&
- (CURLE_HTTP_RETURNED_ERROR == result))) {
- /* If it returned OK. _or_ failonerror was enabled and it
- returned due to such an error, check for HTTP transient
- errors to retry on. */
- char *effective_url = NULL;
- curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &effective_url);
- if(effective_url &&
- checkprefix("http", effective_url)) {
- /* This was HTTP(S) */
- curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
-
- switch(response) {
- case 408: /* Request Timeout */
- case 500: /* Internal Server Error */
- case 502: /* Bad Gateway */
- case 503: /* Service Unavailable */
- case 504: /* Gateway Timeout */
- retry = RETRY_HTTP;
- /*
- * At this point, we have already written data to the output
- * file (or terminal). If we write to a file, we must rewind
- * or close/re-open the file so that the next attempt starts
- * over from the beginning.
- */
- break;
- }
- }
- } /* if CURLE_OK */
- else if(result) {
- long protocol;
-
- curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
- curl_easy_getinfo(curl, CURLINFO_PROTOCOL, &protocol);
-
- if((protocol == CURLPROTO_FTP || protocol == CURLPROTO_FTPS) &&
- response / 100 == 4)
- /*
- * This is typically when the FTP server only allows a certain
- * amount of users and we are not one of them. All 4xx codes
- * are transient.
- */
- retry = RETRY_FTP;
- }
-
- if(retry) {
- static const char * const m[]={
- NULL,
- "timeout",
- "connection refused",
- "HTTP error",
- "FTP error"
- };
-
- warnf(config->global, "Transient problem: %s "
- "Will retry in %ld seconds. "
- "%ld retries left.\n",
- m[retry], retry_sleep/1000L, retry_numretries);
-
- tool_go_sleep(retry_sleep);
- retry_numretries--;
- if(!config->retry_delay) {
- retry_sleep *= 2;
- if(retry_sleep > RETRY_SLEEP_MAX)
- retry_sleep = RETRY_SLEEP_MAX;
- }
- if(outs.bytes && outs.filename && outs.stream) {
- int rc;
- /* We have written data to a output file, we truncate file
- */
- if(!global->mute)
- fprintf(global->errors, "Throwing away %"
- CURL_FORMAT_CURL_OFF_T " bytes\n",
- outs.bytes);
- fflush(outs.stream);
- /* truncate file at the position where we started appending */
-#ifdef HAVE_FTRUNCATE
- if(ftruncate(fileno(outs.stream), outs.init)) {
- /* when truncate fails, we can't just append as then we'll
- create something strange, bail out */
- if(!global->mute)
- fprintf(global->errors,
- "failed to truncate, exiting\n");
- result = CURLE_WRITE_ERROR;
- goto quit_urls;
- }
- /* now seek to the end of the file, the position where we
- just truncated the file in a large file-safe way */
- rc = fseek(outs.stream, 0, SEEK_END);
-#else
- /* ftruncate is not available, so just reposition the file
- to the location we would have truncated it. This won't
- work properly with large files on 32-bit systems, but
- most of those will have ftruncate. */
- rc = fseek(outs.stream, (long)outs.init, SEEK_SET);
-#endif
- if(rc) {
- if(!global->mute)
- fprintf(global->errors,
- "failed seeking to end of file, exiting\n");
- result = CURLE_WRITE_ERROR;
- goto quit_urls;
- }
- outs.bytes = 0; /* clear for next round */
- }
- continue; /* curl_easy_perform loop */
- }
- } /* if retry_numretries */
- else if(metalink) {
- /* Metalink: Decide to try the next resource or
- not. Basically, we want to try the next resource if
- download was not successful. */
- long response;
- if(CURLE_OK == result) {
- char *effective_url = NULL;
- curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &effective_url);
- if(effective_url &&
- curl_strnequal(effective_url, "http", 4)) {
- /* This was HTTP(S) */
- curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
- if(response != 200 && response != 206) {
- metalink_next_res = 1;
- fprintf(global->errors,
- "Metalink: fetching (%s) from (%s) FAILED "
- "(HTTP status code %ld)\n",
- mlfile->filename, this_url, response);
- }
- }
- }
- else {
- metalink_next_res = 1;
- fprintf(global->errors,
- "Metalink: fetching (%s) from (%s) FAILED (%s)\n",
- mlfile->filename, this_url,
- (errorbuffer[0]) ?
- errorbuffer : curl_easy_strerror(result));
- }
- }
- if(metalink && !metalink_next_res)
- fprintf(global->errors, "Metalink: fetching (%s) from (%s) OK\n",
- mlfile->filename, this_url);
-
- /* In all ordinary cases, just break out of loop here */
- break; /* curl_easy_perform loop */
-
- }
-
- if((global->progressmode == CURL_PROGRESS_BAR) &&
- progressbar.calls)
- /* if the custom progress bar has been displayed, we output a
- newline here */
- fputs("\n", progressbar.out);
-
- if(config->writeout)
- ourWriteOut(curl, &outs, config->writeout);
-
- /*
- ** Code within this loop may jump directly here to label 'show_error'
- ** in order to display an error message for CURLcode stored in 'res'
- ** variable and exit loop once that necessary writing and cleanup
- ** in label 'quit_urls' has been done.
- */
-
- show_error:
-
-#ifdef __VMS
- if(is_vms_shell()) {
- /* VMS DCL shell behavior */
- if(!global->showerror)
- vms_show = VMSSTS_HIDE;
- }
- else
-#endif
- if(config->synthetic_error) {
- ;
- }
- else if(result && global->showerror) {
- fprintf(global->errors, "curl: (%d) %s\n", result, (errorbuffer[0]) ?
- errorbuffer : curl_easy_strerror(result));
- if(result == CURLE_PEER_FAILED_VERIFICATION)
- fputs(CURL_CA_CERT_ERRORMSG, global->errors);
- }
-
- /* Fall through comment to 'quit_urls' label */
-
- /*
- ** Upon error condition and always that a message has already been
- ** displayed, code within this loop may jump directly here to label
- ** 'quit_urls' otherwise it should jump to 'show_error' label above.
- **
- ** When 'res' variable is _not_ CURLE_OK loop will exit once that
- ** all code following 'quit_urls' has been executed. Otherwise it
- ** will loop to the beginning from where it may exit if there are
- ** no more urls left.
- */
-
- quit_urls:
-
- /* Set file extended attributes */
- if(!result && config->xattr && outs.fopened && outs.stream) {
- int rc = fwrite_xattr(curl, fileno(outs.stream));
- if(rc)
- warnf(config->global, "Error setting extended attributes: %s\n",
- strerror(errno));
- }
-
- /* Close the file */
- if(outs.fopened && outs.stream) {
- int rc = fclose(outs.stream);
- if(!result && rc) {
- /* something went wrong in the writing process */
- result = CURLE_WRITE_ERROR;
- fprintf(global->errors, "(%d) Failed writing body\n", result);
- }
- }
- else if(!outs.s_isreg && outs.stream) {
- /* Dump standard stream buffered data */
- int rc = fflush(outs.stream);
- if(!result && rc) {
- /* something went wrong in the writing process */
- result = CURLE_WRITE_ERROR;
- fprintf(global->errors, "(%d) Failed writing body\n", result);
- }
- }
-
-#ifdef __AMIGA__
- if(!result && outs.s_isreg && outs.filename) {
- /* Set the url (up to 80 chars) as comment for the file */
- if(strlen(urlnode->url) > 78)
- urlnode->url[79] = '\0';
- SetComment(outs.filename, urlnode->url);
- }
-#endif
-
- /* File time can only be set _after_ the file has been closed */
- if(!result && config->remote_time && outs.s_isreg && outs.filename) {
- /* Ask libcurl if we got a remote file time */
- curl_off_t filetime = -1;
- curl_easy_getinfo(curl, CURLINFO_FILETIME_T, &filetime);
- setfiletime(filetime, outs.filename, config->global->errors);
- }
-
#ifdef USE_METALINK
- if(!metalink && config->use_metalink && result == CURLE_OK) {
- int rv = parse_metalink(config, &outs, this_url);
- if(rv == 0)
- fprintf(config->global->errors, "Metalink: parsing (%s) OK\n",
- this_url);
- else if(rv == -1)
- fprintf(config->global->errors, "Metalink: parsing (%s) FAILED\n",
- this_url);
- }
- else if(metalink && result == CURLE_OK && !metalink_next_res) {
- int rv = metalink_check_hash(global, mlfile, outs.filename);
- if(rv == 0) {
- metalink_next_res = 1;
+ if(!metalink && config->use_metalink) {
+ outs->metalink_parser = metalink_parser_context_new();
+ if(outs->metalink_parser == NULL) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto show_error;
}
+ fprintf(config->global->errors,
+ "Metalink: parsing (%s) metalink/XML...\n", per->this_url);
}
+ else if(metalink)
+ fprintf(config->global->errors,
+ "Metalink: fetching (%s) from (%s)...\n",
+ mlfile->filename, per->this_url);
#endif /* USE_METALINK */
- /* No more business with this output struct */
- if(outs.alloc_filename)
- Curl_safefree(outs.filename);
-#ifdef USE_METALINK
- if(outs.metalink_parser)
- metalink_parser_context_delete(outs.metalink_parser);
-#endif /* USE_METALINK */
- memset(&outs, 0, sizeof(struct OutStruct));
- hdrcbdata.outs = NULL;
-
- /* Free loop-local allocated memory and close loop-local opened fd */
-
- Curl_safefree(outfile);
- Curl_safefree(this_url);
-
- if(infdopen)
- close(infd);
-
- if(metalink) {
- /* Should exit if error is fatal. */
- if(is_fatal_error(result)) {
- break;
- }
- if(!metalink_next_res)
- break;
- mlres = mlres->next;
- if(mlres == NULL)
- break;
- }
- else if(urlnum > 1) {
- /* when url globbing, exit loop upon critical error */
- if(is_fatal_error(result))
- break;
- }
- else if(result)
- /* when not url globbing, exit loop upon any error */
- break;
+ per->metalink = metalink;
+ /* initialize retry vars for loop below */
+ per->retry_sleep_default = (config->retry_delay) ?
+ config->retry_delay*1000L : RETRY_SLEEP_DEFAULT; /* ms */
+ per->retry_numretries = config->req_retry;
+ per->retry_sleep = per->retry_sleep_default; /* ms */
+ per->retrystart = tvnow();
} /* loop to the next URL */
- /* Free loop-local allocated memory */
-
- Curl_safefree(uploadfile);
+ show_error:
+ quit_urls:
if(urls) {
/* Free list of remaining URLs */
@@ -1962,41 +1913,312 @@ static CURLcode operate_do(struct GlobalConfig *global,
Curl_safefree(urlnode->infile);
urlnode->flags = 0;
- /*
- ** Bail out upon critical errors or --fail-early
- */
- if(is_fatal_error(result) || (result && global->fail_early))
- goto quit_curl;
-
} /* for-loop through all URLs */
-
- /*
- ** Nested loops end here.
- */
-
quit_curl:
- /* Reset the global config variables */
- global->noprogress = orig_noprogress;
- global->isatty = orig_isatty;
-
/* Free function-local referenced allocated memory */
Curl_safefree(httpgetfields);
- /* Free list of given URLs */
- clean_getout(config);
+ return result;
+}
- hdrcbdata.heads = NULL;
+static long all_added; /* number of easy handles currently added */
- /* Close function-local opened file descriptors */
- if(heads.fopened && heads.stream)
- fclose(heads.stream);
+/*
+ * add_parallel_transfers() sets 'morep' to TRUE if there are more transfers
+ * to add even after this call returns. sets 'addedp' to TRUE if one or more
+ * transfers were added.
+ */
+static int add_parallel_transfers(struct GlobalConfig *global,
+ CURLM *multi,
+ bool *morep,
+ bool *addedp)
+{
+ struct per_transfer *per;
+ CURLcode result;
+ CURLMcode mcode;
+ *addedp = FALSE;
+ *morep = FALSE;
+ for(per = transfers; per && (all_added < global->parallel_max);
+ per = per->next) {
+ if(per->added)
+ /* already added */
+ continue;
+
+ result = pre_transfer(global, per);
+ if(result)
+ break;
+
+ (void)curl_easy_setopt(per->curl, CURLOPT_PRIVATE, per);
+ (void)curl_easy_setopt(per->curl, CURLOPT_XFERINFOFUNCTION, xferinfo_cb);
+ (void)curl_easy_setopt(per->curl, CURLOPT_XFERINFODATA, per);
+
+ mcode = curl_multi_add_handle(multi, per->curl);
+ if(mcode)
+ return CURLE_OUT_OF_MEMORY;
+ per->added = TRUE;
+ all_added++;
+ *addedp = TRUE;
+ }
+ *morep = per ? TRUE : FALSE;
+ return CURLE_OK;
+}
+
+static CURLcode parallel_transfers(struct GlobalConfig *global,
+ CURLSH *share)
+{
+ CURLM *multi;
+ bool done = FALSE;
+ CURLMcode mcode = CURLM_OK;
+ CURLcode result = CURLE_OK;
+ int still_running = 1;
+ struct timeval start = tvnow();
+ bool more_transfers;
+ bool added_transfers;
+
+ multi = curl_multi_init();
+ if(!multi)
+ return CURLE_OUT_OF_MEMORY;
+
+ result = add_parallel_transfers(global, multi,
+ &more_transfers, &added_transfers);
+ if(result)
+ return result;
+
+ while(!done && !mcode && (still_running || more_transfers)) {
+ mcode = curl_multi_poll(multi, NULL, 0, 1000, NULL);
+ if(!mcode)
+ mcode = curl_multi_perform(multi, &still_running);
+
+ progress_meter(global, &start, FALSE);
+
+ if(!mcode) {
+ int rc;
+ CURLMsg *msg;
+ bool removed = FALSE;
+ do {
+ msg = curl_multi_info_read(multi, &rc);
+ if(msg) {
+ bool retry;
+ struct per_transfer *ended;
+ CURL *easy = msg->easy_handle;
+ result = msg->data.result;
+ curl_easy_getinfo(easy, CURLINFO_PRIVATE, (void *)&ended);
+ curl_multi_remove_handle(multi, easy);
+
+ result = post_transfer(global, share, ended, result, &retry);
+ if(retry)
+ continue;
+ progress_finalize(ended); /* before it goes away */
+ all_added--; /* one fewer added */
+ removed = TRUE;
+ (void)del_transfer(ended);
+ }
+ } while(msg);
+ if(removed) {
+ /* one or more transfers completed, add more! */
+ (void)add_parallel_transfers(global, multi, &more_transfers,
+ &added_transfers);
+ if(added_transfers)
+ /* we added new ones, make sure the loop doesn't exit yet */
+ still_running = 1;
+ }
+ }
+ }
+
+ (void)progress_meter(global, &start, 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;
+ }
+
+ curl_multi_cleanup(multi);
- if(heads.alloc_filename)
- Curl_safefree(heads.filename);
+ return result;
+}
+
+static CURLcode serial_transfers(struct GlobalConfig *global,
+ CURLSH *share)
+{
+ CURLcode returncode = CURLE_OK;
+ CURLcode result = CURLE_OK;
+ struct per_transfer *per;
+ for(per = transfers; per;) {
+ bool retry;
+ result = pre_transfer(global, per);
+ if(result)
+ break;
+
+#ifndef CURL_DISABLE_LIBCURL_OPTION
+ if(global->libcurl) {
+ result = easysrc_perform();
+ if(result)
+ break;
+ }
+#endif
+#ifdef CURLDEBUG
+ if(global->test_event_based)
+ result = curl_easy_perform_ev(per->curl);
+ else
+#endif
+ result = curl_easy_perform(per->curl);
+
+ /* store the result of the actual transfer */
+ returncode = result;
+
+ result = post_transfer(global, share, per, result, &retry);
+ if(retry)
+ continue;
+ per = del_transfer(per);
+
+ /* Bail out upon critical errors or --fail-early */
+ if(result || is_fatal_error(returncode) ||
+ (returncode && global->fail_early))
+ break;
+ }
+ if(returncode)
+ /* returncode errors have priority */
+ result = returncode;
+ return result;
+}
+
+static CURLcode operate_do(struct GlobalConfig *global,
+ struct OperationConfig *config,
+ CURLSH *share)
+{
+ CURLcode result = CURLE_OK;
+ bool capath_from_env;
+
+ /* Check we have a url */
+ if(!config->url_list || !config->url_list->url) {
+ helpf(global->errors, "no URL specified!\n");
+ return CURLE_FAILED_INIT;
+ }
+
+ /* On WIN32 we can't set the path to curl-ca-bundle.crt
+ * at compile time. So we look here for the file in two ways:
+ * 1: look at the environment variable CURL_CA_BUNDLE for a path
+ * 2: if #1 isn't found, use the windows API function SearchPath()
+ * to find it along the app's path (includes app's dir and CWD)
+ *
+ * We support the environment variable thing for non-Windows platforms
+ * too. Just for the sake of it.
+ */
+ capath_from_env = false;
+ if(!config->cacert &&
+ !config->capath &&
+ !config->insecure_ok) {
+ CURL *curltls = curl_easy_init();
+ struct curl_tlssessioninfo *tls_backend_info = NULL;
+
+ /* With the addition of CAINFO support for Schannel, this search could find
+ * a certificate bundle that was previously ignored. To maintain backward
+ * compatibility, only perform this search if not using Schannel.
+ */
+ result = curl_easy_getinfo(curltls, CURLINFO_TLS_SSL_PTR,
+ &tls_backend_info);
+ if(result)
+ return result;
+
+ /* Set the CA cert locations specified in the environment. For Windows if
+ * no environment-specified filename is found then check for CA bundle
+ * default filename curl-ca-bundle.crt in the user's PATH.
+ *
+ * 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(tls_backend_info->backend != CURLSSLBACKEND_SCHANNEL) {
+ char *env;
+ env = curlx_getenv("CURL_CA_BUNDLE");
+ if(env) {
+ config->cacert = strdup(env);
+ if(!config->cacert) {
+ curl_free(env);
+ helpf(global->errors, "out of memory\n");
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ else {
+ env = curlx_getenv("SSL_CERT_DIR");
+ if(env) {
+ config->capath = strdup(env);
+ if(!config->capath) {
+ curl_free(env);
+ helpf(global->errors, "out of memory\n");
+ return CURLE_OUT_OF_MEMORY;
+ }
+ capath_from_env = true;
+ }
+ else {
+ env = curlx_getenv("SSL_CERT_FILE");
+ if(env) {
+ config->cacert = strdup(env);
+ if(!config->cacert) {
+ curl_free(env);
+ helpf(global->errors, "out of memory\n");
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ }
+ }
+
+ if(env)
+ curl_free(env);
+#ifdef WIN32
+ else {
+ result = FindWin32CACert(config, tls_backend_info->backend,
+ "curl-ca-bundle.crt");
+ }
+#endif
+ }
+ curl_easy_cleanup(curltls);
+ }
+
+ if(!result)
+ /* loop through the list of given URLs */
+ result = create_transfers(global, config, share, capath_from_env);
+
+ return result;
+}
+
+static CURLcode operate_transfers(struct GlobalConfig *global,
+ CURLSH *share,
+ CURLcode result)
+{
+ /* Save the values of noprogress and isatty to restore them later on */
+ bool orig_noprogress = global->noprogress;
+ bool orig_isatty = global->isatty;
+ struct per_transfer *per;
+
+ /* Time to actually do the transfers */
+ if(!result) {
+ if(global->parallel)
+ result = parallel_transfers(global, share);
+ else
+ result = serial_transfers(global, share);
+ }
+
+ /* cleanup if there are any left */
+ for(per = transfers; per;) {
+ bool retry;
+ (void)post_transfer(global, share, per, result, &retry);
+ /* Free list of given URLs */
+ clean_getout(per->config);
+
+ /* Release metalink related resources here */
+ clean_metalink(per->config);
+ per = del_transfer(per);
+ }
+
+ /* Reset the global config variables */
+ global->noprogress = orig_noprogress;
+ global->isatty = orig_isatty;
- /* Release metalink related resources here */
- clean_metalink(config);
return result;
}
@@ -2040,7 +2262,7 @@ CURLcode operate(struct GlobalConfig *config, int argc, argv_item_t argv[])
tool_version_info();
/* Check if we were asked to list the SSL engines */
else if(res == PARAM_ENGINES_REQUESTED)
- tool_list_engines(config->easy);
+ tool_list_engines();
else if(res == PARAM_LIBCURL_UNSUPPORTED_PROTOCOL)
result = CURLE_UNSUPPORTED_PROTOCOL;
else
@@ -2058,27 +2280,43 @@ CURLcode operate(struct GlobalConfig *config, int argc, argv_item_t argv[])
if(!result) {
size_t count = 0;
struct OperationConfig *operation = config->first;
+ CURLSH *share = curl_share_init();
+ if(!share) {
+#ifndef CURL_DISABLE_LIBCURL_OPTION
+ if(config->libcurl) {
+ /* Cleanup the libcurl source output */
+ easysrc_cleanup();
+ }
+#endif
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE);
+ curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
+ curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION);
+ curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT);
+ curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_PSL);
/* Get the required arguments for each operation */
- while(!result && operation) {
+ do {
result = get_args(operation, count++);
operation = operation->next;
- }
+ } while(!result && operation);
/* Set the current operation pointer */
config->current = config->first;
- /* Perform each operation */
+ /* Setup all transfers */
while(!result && config->current) {
- result = operate_do(config, config->current);
-
+ result = operate_do(config, config->current, share);
config->current = config->current->next;
-
- if(config->current && config->current->easy)
- curl_easy_reset(config->current->easy);
}
+ /* now run! */
+ result = operate_transfers(config, share, result);
+
+ curl_share_cleanup(share);
#ifndef CURL_DISABLE_LIBCURL_OPTION
if(config->libcurl) {
/* Cleanup the libcurl source output */
diff --git a/src/tool_operate.h b/src/tool_operate.h
index b84388bc5..60257fc60 100644
--- a/src/tool_operate.h
+++ b/src/tool_operate.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -22,7 +22,53 @@
*
***************************************************************************/
#include "tool_setup.h"
+#include "tool_cb_hdr.h"
+#include "tool_cb_prg.h"
+#include "tool_sdecls.h"
+
+struct per_transfer {
+ /* double linked */
+ struct per_transfer *next;
+ struct per_transfer *prev;
+ struct OperationConfig *config; /* for this transfer */
+ CURL *curl;
+ long retry_numretries;
+ long retry_sleep_default;
+ long retry_sleep;
+ struct timeval retrystart;
+ bool metalink; /* nonzero for metalink download. */
+ bool metalink_next_res;
+ metalinkfile *mlfile;
+ metalink_resource *mlres;
+ char *this_url;
+ char *outfile;
+ bool infdopen; /* TRUE if infd needs closing */
+ int infd;
+ struct ProgressData progressbar;
+ struct OutStruct outs;
+ struct OutStruct heads;
+ struct InStruct input;
+ struct HdrCbData hdrcbdata;
+ char errorbuffer[CURL_ERROR_SIZE];
+
+ bool added; /* set TRUE when added to the multi handle */
+
+ /* for parallel progress bar */
+ curl_off_t dltotal;
+ curl_off_t dlnow;
+ curl_off_t ultotal;
+ curl_off_t ulnow;
+ bool dltotal_added; /* if the total has been added from this */
+ bool ultotal_added;
+
+ /* NULL or malloced */
+ char *separator_err;
+ char *separator;
+ char *uploadfile;
+};
CURLcode operate(struct GlobalConfig *config, int argc, argv_item_t argv[]);
+extern struct per_transfer *transfers; /* first node */
+
#endif /* HEADER_CURL_TOOL_OPERATE_H */
diff --git a/src/tool_operhlp.c b/src/tool_operhlp.c
index c3a826278..f3fcc699f 100644
--- a/src/tool_operhlp.c
+++ b/src/tool_operhlp.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -71,10 +71,13 @@ bool stdin_upload(const char *uploadfile)
* Adds the file name to the URL if it doesn't already have one.
* url will be freed before return if the returned pointer is different
*/
-char *add_file_name_to_url(CURL *curl, char *url, const char *filename)
+char *add_file_name_to_url(char *url, const char *filename)
{
/* If no file name part is given in the URL, we add this file name */
char *ptr = strstr(url, "://");
+ CURL *curl = curl_easy_init(); /* for url escaping */
+ if(!curl)
+ return NULL; /* error! */
if(ptr)
ptr += 3;
else
@@ -120,6 +123,7 @@ char *add_file_name_to_url(CURL *curl, char *url, const char *filename)
else
Curl_safefree(url);
}
+ curl_easy_cleanup(curl);
return url;
}
diff --git a/src/tool_operhlp.h b/src/tool_operhlp.h
index 90c854929..1e2f02741 100644
--- a/src/tool_operhlp.h
+++ b/src/tool_operhlp.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -31,7 +31,7 @@ bool output_expected(const char *url, const char *uploadfile);
bool stdin_upload(const char *uploadfile);
-char *add_file_name_to_url(CURL *curl, char *url, const char *filename);
+char *add_file_name_to_url(char *url, const char *filename);
CURLcode get_url_file_name(char **filename, const char *url);
diff --git a/src/tool_paramhlp.c b/src/tool_paramhlp.c
index 3a4286c67..c9dac4f0f 100644
--- a/src/tool_paramhlp.c
+++ b/src/tool_paramhlp.c
@@ -198,6 +198,28 @@ ParameterError str2unum(long *val, const char *str)
}
/*
+ * Parse the string and write the long in the given address if it is below the
+ * maximum allowed value. Return PARAM_OK on success, otherwise a parameter
+ * error enum. ONLY ACCEPTS POSITIVE NUMBERS!
+ *
+ * Since this function gets called with the 'nextarg' pointer from within the
+ * getparameter a lot, we must check it for NULL before accessing the str
+ * data.
+ */
+
+ParameterError str2unummax(long *val, const char *str, long max)
+{
+ ParameterError result = str2unum(val, str);
+ if(result != PARAM_OK)
+ return result;
+ if(*val > max)
+ return PARAM_NUMBER_TOO_LARGE;
+
+ return PARAM_OK;
+}
+
+
+/*
* Parse the string and write the double in the given address. Return PARAM_OK
* on success, otherwise a parameter specific error enum.
*
diff --git a/src/tool_paramhlp.h b/src/tool_paramhlp.h
index 854f52256..f13a114fd 100644
--- a/src/tool_paramhlp.h
+++ b/src/tool_paramhlp.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -33,6 +33,7 @@ void cleanarg(char *str);
ParameterError str2num(long *val, const char *str);
ParameterError str2unum(long *val, const char *str);
+ParameterError str2unummax(long *val, const char *str, long max);
ParameterError str2udouble(double *val, const char *str, long max);
long proto2num(struct OperationConfig *config, long *val, const char *str);
diff --git a/src/tool_parsecfg.c b/src/tool_parsecfg.c
index 36c7bccf0..9b32e54a0 100644
--- a/src/tool_parsecfg.c
+++ b/src/tool_parsecfg.c
@@ -34,8 +34,6 @@
#include "memdebug.h" /* keep this as LAST include */
-#define CURLRC DOT_CHAR "curlrc"
-
/* only acknowledge colon or equals as separators if the option was not
specified with an initial dash! */
#define ISSEP(x,dash) (!dash && (((x) == '=') || ((x) == ':')))
@@ -43,74 +41,90 @@
static const char *unslashquote(const char *line, char *param);
static char *my_get_line(FILE *fp);
+#ifdef WIN32
+static FILE *execpath(const char *filename)
+{
+ char filebuffer[512];
+ /* Get the filename of our executable. GetModuleFileName is already declared
+ * via inclusions done in setup header file. We assume that we are using
+ * the ASCII version here.
+ */
+ unsigned long len = GetModuleFileNameA(0, filebuffer, sizeof(filebuffer));
+ if(len > 0 && len < sizeof(filebuffer)) {
+ /* We got a valid filename - get the directory part */
+ char *lastdirchar = strrchr(filebuffer, '\\');
+ if(lastdirchar) {
+ size_t remaining;
+ *lastdirchar = 0;
+ /* If we have enough space, build the RC filename */
+ remaining = sizeof(filebuffer) - strlen(filebuffer);
+ if(strlen(filename) < remaining - 1) {
+ msnprintf(lastdirchar, remaining, "%s%s", DIR_CHAR, filename);
+ return fopen(filebuffer, FOPEN_READTEXT);
+ }
+ }
+ }
+
+ return NULL;
+}
+#endif
+
+
/* return 0 on everything-is-fine, and non-zero otherwise */
int parseconfig(const char *filename, struct GlobalConfig *global)
{
FILE *file = NULL;
- char filebuffer[512];
bool usedarg = FALSE;
int rc = 0;
struct OperationConfig *operation = global->first;
+ char *pathalloc = NULL;
if(!filename || !*filename) {
/* NULL or no file name attempts to load .curlrc from the homedir! */
-#ifndef __AMIGA__
char *home = homedir(); /* portable homedir finder */
- filename = CURLRC; /* sensible default */
+#ifndef WIN32
if(home) {
- if(strlen(home) < (sizeof(filebuffer) - strlen(CURLRC))) {
- msnprintf(filebuffer, sizeof(filebuffer),
- "%s%s%s", home, DIR_CHAR, CURLRC);
-
-#ifdef WIN32
- /* Check if the file exists - if not, try CURLRC in the same
- * directory as our executable
- */
- file = fopen(filebuffer, FOPEN_READTEXT);
- if(file != NULL) {
- filename = filebuffer;
- }
- else {
- /* Get the filename of our executable. GetModuleFileName is
- * already declared via inclusions done in setup header file.
- * We assume that we are using the ASCII version here.
- */
- unsigned long len = GetModuleFileNameA(0, filebuffer,
- sizeof(filebuffer));
- if(len > 0 && len < sizeof(filebuffer)) {
- /* We got a valid filename - get the directory part */
- char *lastdirchar = strrchr(filebuffer, '\\');
- if(lastdirchar) {
- size_t remaining;
- *lastdirchar = 0;
- /* If we have enough space, build the RC filename */
- remaining = sizeof(filebuffer) - strlen(filebuffer);
- if(strlen(CURLRC) < remaining - 1) {
- msnprintf(lastdirchar, remaining,
- "%s%s", DIR_CHAR, CURLRC);
- /* Don't bother checking if it exists - we do that later */
- filename = filebuffer;
- }
- }
- }
- }
-#else /* WIN32 */
- filename = filebuffer;
-#endif /* WIN32 */
+ pathalloc = curl_maprintf("%s%s.curlrc", home, DIR_CHAR);
+ if(!pathalloc) {
+ free(home);
+ return 1; /* out of memory */
}
- Curl_safefree(home); /* we've used it, now free it */
+ filename = pathalloc;
}
+#else /* Windows */
+ if(home) {
+ int i = 0;
+ char prefix = '.';
+ do {
+ /* check for .curlrc then _curlrc in the home dir */
+ pathalloc = curl_maprintf("%s%s%ccurlrc", home, DIR_CHAR, prefix);
+ if(!pathalloc) {
+ free(home);
+ return 1; /* out of memory */
+ }
-# else /* __AMIGA__ */
- /* On AmigaOS all the config files are into env:
- */
- filename = "ENV:" CURLRC;
-
+ /* Check if the file exists - if not, try _curlrc */
+ file = fopen(pathalloc, FOPEN_READTEXT);
+ if(file) {
+ filename = pathalloc;
+ break;
+ }
+ prefix = '_';
+ } while(++i < 2);
+ }
+ if(!filename) {
+ /* check for .curlrc then _curlrc in the dir of the executable */
+ file = execpath(".curlrc");
+ if(!file)
+ file = execpath("_curlrc");
+ }
#endif
+
+ Curl_safefree(home); /* we've used it, now free it */
}
- if(!file) { /* WIN32: no need to fopen() again */
+ if(!file && filename) { /* no need to fopen() again */
if(strcmp(filename, "-"))
file = fopen(filename, FOPEN_READTEXT);
else
@@ -230,9 +244,6 @@ int parseconfig(const char *filename, struct GlobalConfig *global)
/* Initialise the newly created config */
config_init(operation->next);
- /* Copy the easy handle */
- operation->next->easy = global->easy;
-
/* Set the global config pointer */
operation->next->global = global;
@@ -274,6 +285,7 @@ int parseconfig(const char *filename, struct GlobalConfig *global)
else
rc = 1; /* couldn't open the file */
+ free(pathalloc);
return rc;
}
diff --git a/src/tool_progress.c b/src/tool_progress.c
new file mode 100644
index 000000000..a2667f38e
--- /dev/null
+++ b/src/tool_progress.c
@@ -0,0 +1,314 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed 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.
+ *
+ ***************************************************************************/
+#include "tool_setup.h"
+#include "tool_operate.h"
+#include "tool_progress.h"
+#include "tool_util.h"
+
+#define ENABLE_CURLX_PRINTF
+/* use our own printf() functions */
+#include "curlx.h"
+
+/* 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're 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)) );
+
+#if (CURL_SIZEOF_CURL_OFF_T > 4)
+
+ else if(bytes < CURL_OFF_T_C(10000) * ONE_MEGABYTE)
+ /* 'XXXXM' is good until we're 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. */
+
+#else
+
+ else
+ msnprintf(max5, 6, "%4" CURL_FORMAT_CURL_OFF_T "M", bytes/ONE_MEGABYTE);
+
+#endif
+
+ return max5;
+}
+
+int xferinfo_cb(void *clientp,
+ curl_off_t dltotal,
+ curl_off_t dlnow,
+ curl_off_t ultotal,
+ curl_off_t ulnow)
+{
+ struct per_transfer *per = clientp;
+ per->dltotal = dltotal;
+ per->dlnow = dlnow;
+ per->ultotal = ultotal;
+ per->ulnow = ulnow;
+ return 0;
+}
+
+/* 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);
+ }
+}
+
+static curl_off_t all_dltotal = 0;
+static curl_off_t all_ultotal = 0;
+static curl_off_t all_dlalready = 0;
+static curl_off_t all_ulalready = 0;
+
+curl_off_t all_xfers = 0; /* current total */
+
+struct speedcount {
+ curl_off_t dl;
+ curl_off_t ul;
+ struct timeval stamp;
+};
+#define SPEEDCNT 10
+static unsigned int speedindex;
+static bool indexwrapped;
+static struct speedcount speedstore[SPEEDCNT];
+
+/*
+ |DL% UL% Dled Uled Xfers Live Qd Total Current Left Speed
+ | 6 -- 9.9G 0 2 2 0 0:00:40 0:00:02 0:00:37 4087M
+*/
+bool progress_meter(struct GlobalConfig *global,
+ struct timeval *start,
+ bool final)
+{
+ static struct timeval stamp;
+ static bool header = FALSE;
+ struct timeval now;
+ long diff;
+
+ if(global->noprogress)
+ return FALSE;
+
+ now = tvnow();
+ diff = tvdiff(now, stamp);
+
+ if(!header) {
+ header = TRUE;
+ fputs("DL% UL% Dled Uled Xfers Live Qd "
+ "Total Current Left Speed\n",
+ global->errors);
+ }
+ if(final || (diff > 500)) {
+ char time_left[10];
+ char time_total[10];
+ char time_spent[10];
+ char buffer[3][6];
+ curl_off_t spent = tvdiff(now, *start)/1000;
+ char dlpercen[4]="--";
+ char ulpercen[4]="--";
+ struct per_transfer *per;
+ curl_off_t all_dlnow = 0;
+ curl_off_t all_ulnow = 0;
+ bool dlknown = TRUE;
+ bool ulknown = TRUE;
+ curl_off_t all_running = 0; /* in progress */
+ curl_off_t all_queued = 0; /* pending */
+ curl_off_t speed = 0;
+ unsigned int i;
+ stamp = now;
+
+ /* first add the amounts of the already completed transfers */
+ all_dlnow += all_dlalready;
+ all_ulnow += all_ulalready;
+
+ for(per = transfers; per; per = per->next) {
+ all_dlnow += per->dlnow;
+ all_ulnow += per->ulnow;
+ if(!per->dltotal)
+ dlknown = FALSE;
+ else if(!per->dltotal_added) {
+ /* only add this amount once */
+ all_dltotal += per->dltotal;
+ per->dltotal_added = TRUE;
+ }
+ if(!per->ultotal)
+ ulknown = FALSE;
+ else if(!per->ultotal_added) {
+ /* only add this amount once */
+ all_ultotal += per->ultotal;
+ per->ultotal_added = TRUE;
+ }
+ if(!per->added)
+ all_queued++;
+ else
+ all_running++;
+ }
+ if(dlknown && all_dltotal)
+ /* TODO: handle integer overflow */
+ msnprintf(dlpercen, sizeof(dlpercen), "%3d",
+ all_dlnow * 100 / all_dltotal);
+ if(ulknown && all_ultotal)
+ /* TODO: handle integer overflow */
+ msnprintf(ulpercen, sizeof(ulpercen), "%3d",
+ all_ulnow * 100 / all_ultotal);
+
+ /* get the transfer speed, the higher of the two */
+
+ i = speedindex;
+ speedstore[i].dl = all_dlnow;
+ speedstore[i].ul = all_ulnow;
+ speedstore[i].stamp = now;
+ if(++speedindex >= SPEEDCNT) {
+ indexwrapped = TRUE;
+ speedindex = 0;
+ }
+
+ {
+ long deltams;
+ curl_off_t dl;
+ curl_off_t ul;
+ curl_off_t dls;
+ curl_off_t uls;
+ if(indexwrapped) {
+ /* 'speedindex' is the oldest stored data */
+ deltams = tvdiff(now, speedstore[speedindex].stamp);
+ dl = all_dlnow - speedstore[speedindex].dl;
+ ul = all_ulnow - speedstore[speedindex].ul;
+ }
+ else {
+ /* since the beginning */
+ deltams = tvdiff(now, *start);
+ dl = all_dlnow;
+ ul = all_ulnow;
+ }
+ dls = (curl_off_t)((double)dl / ((double)deltams/1000.0));
+ uls = (curl_off_t)((double)ul / ((double)deltams/1000.0));
+ speed = dls > uls ? dls : uls;
+ }
+
+
+ if(dlknown && speed) {
+ curl_off_t est = all_dltotal / speed;
+ curl_off_t left = (all_dltotal - all_dlnow) / speed;
+ time2str(time_left, left);
+ time2str(time_total, est);
+ }
+ else {
+ time2str(time_left, 0);
+ time2str(time_total, 0);
+ }
+ time2str(time_spent, spent);
+
+ fprintf(global->errors,
+ "\r"
+ "%-3s " /* percent downloaded */
+ "%-3s " /* percent uploaded */
+ "%s " /* Dled */
+ "%s " /* Uled */
+ "%5" CURL_FORMAT_CURL_OFF_T " " /* Xfers */
+ "%5" CURL_FORMAT_CURL_OFF_T " " /* Live */
+ "%5" CURL_FORMAT_CURL_OFF_T " " /* Queued */
+ "%s " /* Total time */
+ "%s " /* Current time */
+ "%s " /* Time left */
+ "%s " /* Speed */
+ "%5s" /* final newline */,
+
+ dlpercen, /* 3 letters */
+ ulpercen, /* 3 letters */
+ max5data(all_dlnow, buffer[0]),
+ max5data(all_ulnow, buffer[1]),
+ all_xfers,
+ all_running,
+ all_queued,
+ time_total,
+ time_spent,
+ time_left,
+ max5data(speed, buffer[2]), /* speed */
+ final ? "\n" :"");
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void progress_finalize(struct per_transfer *per)
+{
+ /* get the numbers before this transfer goes away */
+ all_dlalready += per->dlnow;
+ all_ulalready += per->ulnow;
+}
diff --git a/src/tool_progress.h b/src/tool_progress.h
new file mode 100644
index 000000000..34b609816
--- /dev/null
+++ b/src/tool_progress.h
@@ -0,0 +1,39 @@
+#ifndef HEADER_CURL_TOOL_PROGRESS_H
+#define HEADER_CURL_TOOL_PROGRESS_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed 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.
+ *
+ ***************************************************************************/
+#include "tool_setup.h"
+
+int xferinfo_cb(void *clientp,
+ curl_off_t dltotal,
+ curl_off_t dlnow,
+ curl_off_t ultotal,
+ curl_off_t ulnow);
+
+bool progress_meter(struct GlobalConfig *global,
+ struct timeval *start,
+ bool final);
+void progress_finalize(struct per_transfer *per);
+
+extern curl_off_t all_xfers; /* total number */
+
+#endif /* HEADER_CURL_TOOL_PROGRESS_H */
diff --git a/src/tool_setopt.c b/src/tool_setopt.c
index b5486e6ef..4c98d9057 100644
--- a/src/tool_setopt.c
+++ b/src/tool_setopt.c
@@ -82,6 +82,7 @@ const NameValue setopt_nv_CURL_HTTP_VERSION[] = {
NV(CURL_HTTP_VERSION_1_1),
NV(CURL_HTTP_VERSION_2_0),
NV(CURL_HTTP_VERSION_2TLS),
+ NV(CURL_HTTP_VERSION_3),
NVEND,
};
@@ -823,6 +824,16 @@ bool tool_setopt_skip(CURLoption tag)
break;
}
#endif
+#ifdef CURL_DISABLE_NETRC
+#define USED_TAG
+ switch(tag) {
+ case CURLOPT_NETRC:
+ case CURLOPT_NETRC_FILE:
+ return TRUE;
+ default:
+ break;
+ }
+#endif
#ifndef USED_TAG
(void)tag;
diff --git a/src/tool_writeout.c b/src/tool_writeout.c
index d5b0bc44f..27b2ac50d 100644
--- a/src/tool_writeout.c
+++ b/src/tool_writeout.c
@@ -5,7 +5,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -314,6 +314,9 @@ void ourWriteOut(CURL *curl, struct OutStruct *outs, const char *writeinfo)
case CURL_HTTP_VERSION_2_0:
version = "2";
break;
+ case CURL_HTTP_VERSION_3:
+ version = "3";
+ break;
}
fprintf(stream, version);
diff --git a/tests/README b/tests/README
index 16a8f06e1..bfd234a10 100644
--- a/tests/README
+++ b/tests/README
@@ -216,27 +216,12 @@ The curl Test Suite
2.1 Test case numbering
- 1 - 99 HTTP
- 100 - 199 FTP
- 200 - 299 FILE
- 300 - 399 HTTPS
- 400 - 499 FTPS
- 500 - 599 libcurl source code tests, not using the curl command tool
- 600 - 699 SCP/SFTP
- 700 - 799 SOCKS4 (even numbers) and SOCK5 (odd numbers)
- 800 - 849 IMAP
- 850 - 899 POP3
- 900 - 999 SMTP
- 1000 - 1299 miscellaneous
- 1300 - 1399 unit tests
- 1400 - 1499 miscellaneous
- 1500 - 1599 libcurl source code tests, not using the curl command tool
- (same as 5xx)
- 1600 - 1699 unit tests
- 2000 - x multiple sequential protocols per test case
-
- There's nothing in the system that *requires* us to keep within these number
- series.
+ Test cases used to be numbered by category, but the ranges filled
+ up. Subsets of tests can now be selected by passing keywords to the
+ runtests.pl script via the make TFLAGS variable.
+
+ New tests should now be added by finding a free number in
+ tests/data/Makefile.inc.
3. Write tests
diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc
index dfedea3d8..e2a04e181 100644
--- a/tests/data/Makefile.inc
+++ b/tests/data/Makefile.inc
@@ -57,7 +57,7 @@ test298 test299 test300 test301 test302 test303 test304 test305 test306 \
test307 test308 test309 test310 test311 test312 test313 test314 test315 \
test316 test317 test318 test319 test320 test321 test322 test323 test324 \
test325 test326 test327 test328 test329 test330 test331 test332 test333 \
-test334 \
+test334 test335 \
test340 \
\
test350 test351 test352 test353 test354 test355 test356 \
@@ -93,20 +93,21 @@ test809 test810 test811 test812 test813 test814 test815 test816 test817 \
test818 test819 test820 test821 test822 test823 test824 test825 test826 \
test827 test828 test829 test830 test831 test832 test833 test834 test835 \
test836 test837 test838 test839 test840 test841 test842 test843 test844 \
-test845 test846 test847 \
+test845 test846 test847 test848 test849 \
\
test850 test851 test852 test853 test854 test855 test856 test857 test858 \
test859 test860 test861 test862 test863 test864 test865 test866 test867 \
test868 test869 test870 test871 test872 test873 test874 test875 test876 \
test877 test878 test879 test880 test881 test882 test883 test884 test885 \
-test886 test887 test888 test889 test890 test891 \
+test886 test887 test888 test889 test890 test891 test892 test893 \
\
test900 test901 test902 test903 test904 test905 test906 test907 test908 \
test909 test910 test911 test912 test913 test914 test915 test916 test917 \
test918 test919 test920 test921 test922 test923 test924 test925 test926 \
test927 test928 test929 test930 test931 test932 test933 test934 test935 \
test936 test937 test938 test939 test940 test941 test942 test943 test944 \
-test945 test946 test947 test948 test949 test950 test951 test952 \
+test945 test946 test947 test948 test949 test950 test951 test952 test953 \
+test954 \
\
test1000 test1001 test1002 test1003 test1004 test1005 test1006 test1007 \
test1008 test1009 test1010 test1011 test1012 test1013 test1014 test1015 \
@@ -129,7 +130,7 @@ test1136 test1137 test1138 test1141 test1142 test1143 \
test1144 test1145 test1146 test1147 test1148 test1149 test1150 test1151 \
test1152 test1153 test1154 test1155 test1156 test1157 test1158 test1159 \
test1160 test1161 test1162 test1163 test1164 test1165 \
-test1170 test1171 test1172 \
+test1170 test1171 test1172 test1174 \
\
test1200 test1201 test1202 test1203 test1204 test1205 test1206 test1207 \
test1208 test1209 test1210 test1211 test1212 test1213 test1214 test1215 \
@@ -140,7 +141,7 @@ test1236 test1237 test1238 test1239 test1240 test1241 test1242 test1243 \
test1244 test1245 test1246 test1247 test1248 test1249 test1250 test1251 \
test1252 test1253 test1254 test1255 test1256 test1257 test1258 test1259 \
test1260 test1261 test1262 test1263 test1264 test1265 test1266 test1267 \
-test1268 \
+test1268 test1269 \
\
test1280 test1281 test1282 test1283 test1284 test1285 test1286 test1287 \
test1288 test1289 test1290 test1291 test1292 \
@@ -177,7 +178,7 @@ test1540 test1541 \
test1550 test1551 test1552 test1553 test1554 test1555 test1556 test1557 \
test1558 test1559 test1560 test1561 test1562 test1563 \
\
-test1590 test1591 test1592 test1593 \
+test1590 test1591 test1592 test1593 test1594 \
\
test1600 test1601 test1602 test1603 test1604 test1605 test1606 test1607 \
test1608 test1609 test1620 test1621 \
@@ -199,7 +200,8 @@ test2040 test2041 test2042 test2043 test2044 test2045 test2046 test2047 \
test2048 test2049 test2050 test2051 test2052 test2053 test2054 test2055 \
test2056 test2057 test2058 test2059 test2060 test2061 test2062 test2063 \
test2064 test2065 test2066 test2067 test2068 test2069 \
- test2071 test2072 test2073 test2074 test2075 test2076 \
+ test2071 test2072 test2073 test2074 test2075 test2076 test2077 \
+test2078 \
test2080 \
test2100 \
\
diff --git a/tests/data/test1002 b/tests/data/test1002
index d12046e5e..c20995d90 100644
--- a/tests/data/test1002
+++ b/tests/data/test1002
@@ -103,6 +103,14 @@ Expect: 100-continue
st
GET http://%HOSTIP:%HTTPPORT/1002.upload2 HTTP/1.1
Host: %HOSTIP:%HTTPPORT
+Content-Range: bytes 2-4/5
+User-Agent: curl/7.16.1
+Accept: */*
+Proxy-Connection: Keep-Alive
+Content-Length: 0
+
+GET http://%HOSTIP:%HTTPPORT/1002.upload2 HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
Authorization: Digest username="auser", realm="testrealm", nonce="1053604144", uri="/1002.upload2", response="d711f0d2042786d930de635ba0d1a1d0"
Content-Range: bytes 2-4/5
User-Agent: curl/7.16.1
diff --git a/tests/data/test1135 b/tests/data/test1135
index 3591a543b..eca6860fb 100644
--- a/tests/data/test1135
+++ b/tests/data/test1135
@@ -91,6 +91,7 @@ CURL_EXTERN CURLMcode curl_multi_add_handle(CURLM *multi_handle,
CURL_EXTERN CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *multi_handle,
CURL_EXTERN CURLMcode curl_multi_wait(CURLM *multi_handle,
+CURL_EXTERN CURLMcode curl_multi_poll(CURLM *multi_handle,
CURL_EXTERN CURLMcode curl_multi_perform(CURLM *multi_handle,
CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle);
CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *multi_handle,
diff --git a/tests/data/test1174 b/tests/data/test1174
new file mode 100644
index 000000000..b316fde8c
--- /dev/null
+++ b/tests/data/test1174
@@ -0,0 +1,50 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP/0.9
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+<data>
+-foo- swsclose
+</data>
+<datacheck>
+</datacheck>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+http
+</server>
+ <name>
+HTTP/0.9 GET response denied by default
+ </name>
+ <command>
+http://%HOSTIP:%HTTPPORT/1174
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol>
+GET /1174 HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
+Accept: */*
+
+</protocol>
+# unsupported protocol
+<errorcode>
+1
+</errorcode>
+</verify>
+</testcase>
diff --git a/tests/data/test1269 b/tests/data/test1269
new file mode 100644
index 000000000..c77663633
--- /dev/null
+++ b/tests/data/test1269
@@ -0,0 +1,34 @@
+<testcase>
+<info>
+<keywords>
+--retry-delay
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+none
+</server>
+ <name>
+too large --retry-delay value
+ </name>
+ <command>
+--retry 3 --retry-delay 9223372036854776 http://%HOSTIP:%HTTPPORT/1269
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<errorcode>
+2
+</errorcode>
+</verify>
+</testcase>
diff --git a/tests/data/test1291 b/tests/data/test1291
index 3f1575184..a2e505fc9 100644
--- a/tests/data/test1291
+++ b/tests/data/test1291
@@ -21,7 +21,7 @@ HTTP PUT
none
</server>
<name>
-Attempt to upload 100K files but fail immediately
+Attempt to upload 1000 files but fail immediately
</name>
<command>
-K log/cmd1291 --fail-early
@@ -31,7 +31,7 @@ XXXXXXXx
</file>
# generate the config file
<precheck>
-perl -e 'for(1 .. 100000) { printf("upload-file=log/upload-this\nurl=htttttp://non-existing-host.haxx.se/upload/1291\n", $_);}' > log/cmd1291;
+perl -e 'for(1 .. 1000) { printf("upload-file=log/upload-this\nurl=htttttp://non-existing-host.haxx.se/upload/1291\n", $_);}' > log/cmd1291;
</precheck>
</client>
@@ -40,11 +40,5 @@ perl -e 'for(1 .. 100000) { printf("upload-file=log/upload-this\nurl=htttttp://n
<errorcode>
1
</errorcode>
-
-# we disable valgrind here since it takes 40+ seconds even on a fairly snappy
-# machine
-<valgrind>
-disable
-</valgrind>
</verify>
</testcase>
diff --git a/tests/data/test1401 b/tests/data/test1401
index 2cb43b7ee..c414c4751 100644
--- a/tests/data/test1401
+++ b/tests/data/test1401
@@ -88,7 +88,6 @@ int main(int argc, char *argv[])
curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, slist1);
curl_easy_setopt(hnd, CURLOPT_USERAGENT, "MyUA");
curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 50L);
- curl_easy_setopt(hnd, CURLOPT_HTTP09_ALLOWED, 1L);
curl_easy_setopt(hnd, CURLOPT_COOKIE, "chocolate=chip");
curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(hnd, CURLOPT_TCP_KEEPALIVE, 1L);
diff --git a/tests/data/test1402 b/tests/data/test1402
index a806a16ff..56861a4ba 100644
--- a/tests/data/test1402
+++ b/tests/data/test1402
@@ -80,7 +80,6 @@ int main(int argc, char *argv[])
curl_easy_setopt(hnd, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)16);
curl_easy_setopt(hnd, CURLOPT_USERAGENT, "stripped");
curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 50L);
- curl_easy_setopt(hnd, CURLOPT_HTTP09_ALLOWED, 1L);
curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(hnd, CURLOPT_TCP_KEEPALIVE, 1L);
diff --git a/tests/data/test1403 b/tests/data/test1403
index b73d93004..4237c07c8 100644
--- a/tests/data/test1403
+++ b/tests/data/test1403
@@ -75,7 +75,6 @@ int main(int argc, char *argv[])
curl_easy_setopt(hnd, CURLOPT_URL, "http://%HOSTIP:%HTTPPORT/we/want/1403?foo=bar&baz=quux");
curl_easy_setopt(hnd, CURLOPT_USERAGENT, "stripped");
curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 50L);
- curl_easy_setopt(hnd, CURLOPT_HTTP09_ALLOWED, 1L);
curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(hnd, CURLOPT_TCP_KEEPALIVE, 1L);
diff --git a/tests/data/test1404 b/tests/data/test1404
index 6a83e0043..35e1ee91b 100644
--- a/tests/data/test1404
+++ b/tests/data/test1404
@@ -146,7 +146,6 @@ int main(int argc, char *argv[])
curl_easy_setopt(hnd, CURLOPT_MIMEPOST, mime1);
curl_easy_setopt(hnd, CURLOPT_USERAGENT, "stripped");
curl_easy_setopt(hnd, CURLOPT_MAXREDIRS, 50L);
- curl_easy_setopt(hnd, CURLOPT_HTTP09_ALLOWED, 1L);
curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(hnd, CURLOPT_TCP_KEEPALIVE, 1L);
diff --git a/tests/data/test1406 b/tests/data/test1406
index fb134b1f3..dc8247dfa 100644
--- a/tests/data/test1406
+++ b/tests/data/test1406
@@ -76,13 +76,13 @@ int main(int argc, char *argv[])
hnd = curl_easy_init();
curl_easy_setopt(hnd, CURLOPT_BUFFERSIZE, 102400L);
- curl_easy_setopt(hnd, CURLOPT_INFILESIZE_LARGE, (curl_off_t)38);
curl_easy_setopt(hnd, CURLOPT_URL, "smtp://%HOSTIP:%SMTPPORT/1406");
curl_easy_setopt(hnd, CURLOPT_UPLOAD, 1L);
curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(hnd, CURLOPT_TCP_KEEPALIVE, 1L);
curl_easy_setopt(hnd, CURLOPT_MAIL_FROM, "sender@example.com");
curl_easy_setopt(hnd, CURLOPT_MAIL_RCPT, slist1);
+ curl_easy_setopt(hnd, CURLOPT_INFILESIZE_LARGE, (curl_off_t)38);
/* Here is a list of options the curl code used that cannot get generated
as source easily. You may select to either not use them or implement
diff --git a/tests/data/test1412 b/tests/data/test1412
index ae63290e9..36d3d1d93 100644
--- a/tests/data/test1412
+++ b/tests/data/test1412
@@ -25,6 +25,19 @@ Connection: close
This is not the real page
</data>
+# The second URL will get this response
+<data1>
+HTTP/1.1 401 Authorization Required swsclose
+Server: Apache/1.3.27 (Darwin) PHP/4.1.2
+WWW-Authenticate: Blackmagic realm="gimme all yer s3cr3ts"
+WWW-Authenticate: Basic realm="gimme all yer s3cr3ts"
+WWW-Authenticate: Digest realm="gimme all yer s3cr3ts", nonce="11223344"
+Content-Type: text/html; charset=iso-8859-1
+Connection: close
+
+This is not the real page
+</data1>
+
# This is supposed to be returned when the server gets a
# Authorization: Digest line passed-in from the client
<data1000>
@@ -109,6 +122,11 @@ Accept: */*
GET /14120001 HTTP/1.1
Host: %HOSTIP:%HTTPPORT
+User-Agent: curl/7.10.5 (i686-pc-linux-gnu) libcurl/7.10.5 OpenSSL/0.9.7a ipv6 zlib/1.1.3
+Accept: */*
+
+GET /14120001 HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
Authorization: Digest username="testuser", realm="gimme all yer s3cr3ts", nonce="11223344", uri="/14120001", response="0085df91870374c8bf4e94415e7fbf8e"
User-Agent: curl/7.10.5 (i686-pc-linux-gnu) libcurl/7.10.5 OpenSSL/0.9.7a ipv6 zlib/1.1.3
Accept: */*
diff --git a/tests/data/test1418 b/tests/data/test1418
index b3a2f23b2..c137b1c59 100644
--- a/tests/data/test1418
+++ b/tests/data/test1418
@@ -22,6 +22,15 @@ WWW-Authenticate: Basic
Please auth with me
</data>
+<data3>
+HTTP/1.1 401 Authentication please!
+Content-Length: 20
+WWW-Authenticate: Digest realm="loonie", nonce="314156592"
+WWW-Authenticate: Basic
+
+Please auth with me
+</data3>
+
# This is supposed to be returned when the server gets the second
# Authorization: NTLM line passed-in from the client
<data1000>
@@ -99,6 +108,10 @@ Accept: */*
GET /14180003 HTTP/1.1
Host: %HOSTIP:%HTTPPORT
+Accept: */*
+
+GET /14180003 HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
Authorization: Digest username="testuser", realm="loonie", nonce="314156592", uri="/14180003", response="1c6390a67bac3283a9b023402f3b3540"
Accept: */*
diff --git a/tests/data/test1420 b/tests/data/test1420
index b47510511..99d50c35b 100644
--- a/tests/data/test1420
+++ b/tests/data/test1420
@@ -66,7 +66,6 @@ int main(int argc, char *argv[])
curl_easy_setopt(hnd, CURLOPT_BUFFERSIZE, 102400L);
curl_easy_setopt(hnd, CURLOPT_URL, "imap://%HOSTIP:%IMAPPORT/1420/;MAILINDEX=1");
curl_easy_setopt(hnd, CURLOPT_USERPWD, "user:secret");
- curl_easy_setopt(hnd, CURLOPT_HTTP09_ALLOWED, 1L);
curl_easy_setopt(hnd, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(hnd, CURLOPT_TCP_KEEPALIVE, 1L);
diff --git a/tests/data/test1514 b/tests/data/test1514
index 38f5da61f..6c09ae3b0 100644
--- a/tests/data/test1514
+++ b/tests/data/test1514
@@ -4,13 +4,14 @@
HTTP
HTTP POST
Content-Length
+chunked Transfer-Encoding
</keywords>
</info>
# Server-side
<reply>
<data nocheck="yes">
-HTTP/1.1 411 Length Required
+HTTP/1.1 200 OK
Date: Sun, 19 Jan 2014 18:50:58 GMT
Server: test-server/fake swsclose
Connection: close
@@ -36,13 +37,27 @@ http://%HOSTIP:%HTTPPORT/1514
# Verify data after the test has been "shot"
<verify>
# Content-Length header is not present
+# Transfer-Encoding header is added automatically
<protocol>
POST /1514 HTTP/1.1
Host: %HOSTIP:%HTTPPORT
Accept: */*
+Transfer-Encoding: chunked
Content-Type: application/x-www-form-urlencoded
Expect: 100-continue
+1
+d
+1
+u
+1
+m
+1
+m
+1
+y
+0
+
</protocol>
</verify>
</testcase>
diff --git a/tests/data/test153 b/tests/data/test153
index f679de4ea..77f7adb01 100644
--- a/tests/data/test153
+++ b/tests/data/test153
@@ -9,7 +9,7 @@ HTTP Digest auth
# Server-side
<reply>
-# reply back and ask for Digest auth
+# First reply back and ask for Digest auth
<data1>
HTTP/1.1 401 Authorization Required swsclose
Server: Apache/1.3.27 (Darwin) PHP/4.1.2
@@ -20,6 +20,17 @@ Content-Length: 26
This is not the real page
</data1>
+# second reply back
+<data2>
+HTTP/1.1 401 Authorization Required swsclose
+Server: Apache/1.3.27 (Darwin) PHP/4.1.2
+WWW-Authenticate: Digest realm="testrealm", nonce="1053604145"
+Content-Type: text/html; charset=iso-8859-1
+Content-Length: 26
+
+This is not the real page
+</data2>
+
# This is supposed to be returned when the server gets a
# Authorization: Digest line passed-in from the client
<data1001>
@@ -93,6 +104,11 @@ Accept: */*
GET /1530002 HTTP/1.1
Host: %HOSTIP:%HTTPPORT
+User-Agent: curl/7.11.0-CVS (i686-pc-linux-gnu) libcurl/7.11.0-CVS OpenSSL/0.9.6b ipv6 zlib/1.1.4 GSS
+Accept: */*
+
+GET /1530002 HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
Authorization: Digest username="testuser", realm="testrealm", nonce="1053604145", uri="/1530002", response="f84511b014fdd0ba6494f42871079c32"
User-Agent: curl/7.11.0-CVS (i686-pc-linux-gnu) libcurl/7.11.0-CVS OpenSSL/0.9.6b ipv6 zlib/1.1.4 GSS
Accept: */*
@@ -117,6 +133,12 @@ Content-Type: text/html; charset=iso-8859-1
Content-Length: 23
This IS the real page!
+HTTP/1.1 401 Authorization Required swsclose
+Server: Apache/1.3.27 (Darwin) PHP/4.1.2
+WWW-Authenticate: Digest realm="testrealm", nonce="1053604145"
+Content-Type: text/html; charset=iso-8859-1
+Content-Length: 26
+
HTTP/1.1 401 Authorization re-negotiation please swsbounce
Server: Apache/1.3.27 (Darwin) PHP/4.1.2
WWW-Authenticate: Digest realm="testrealm", algorithm=MD5, nonce="999999", stale=true, qop="auth"
diff --git a/tests/data/test1538 b/tests/data/test1538
index 98d6731e9..9374debb7 100644
--- a/tests/data/test1538
+++ b/tests/data/test1538
@@ -126,7 +126,8 @@ e90: SSL public key does not match pinned public key
e91: SSL server certificate status verification FAILED
e92: Stream error in the HTTP/2 framing layer
e93: API function called from within callback
-e94: Unknown error
+e94: An authentication function returned an error
+e95: Unknown error
m-1: Please call curl_multi_perform() soon
m0: No error
m1: Invalid multi handle
diff --git a/tests/data/test1594 b/tests/data/test1594
new file mode 100644
index 000000000..4907ca2be
--- /dev/null
+++ b/tests/data/test1594
@@ -0,0 +1,52 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP GET
+HTTP replaced headers
+CURLOPT_TIMECONDITION
+If-Modified-Since
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data nocheck="yes">
+HTTP/1.1 503 Error
+Date: Thu, 11 Jul 2019 02:26:59 GMT
+Server: test-server/swsclose
+Retry-After: 22
+
+</data>
+</reply>
+# Client-side
+<client>
+<server>
+http
+</server>
+<name>
+HTTP Retry-After header parsing and extraction
+</name>
+<tool>
+lib1594
+</tool>
+<command>
+http://%HOSTIP:%HTTPPORT/1594
+</command>
+</client>
+
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol>
+GET /1594 HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
+Accept: */*
+
+</protocol>
+<stdout>
+Retry-After: 22
+</stdout>
+</verify>
+</testcase>
diff --git a/tests/data/test1595 b/tests/data/test1595
new file mode 100644
index 000000000..29b2b865f
--- /dev/null
+++ b/tests/data/test1595
@@ -0,0 +1,51 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP GET
+HTTP replaced headers
+CURLOPT_TIMECONDITION
+If-Modified-Since
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data nocheck="yes">
+HTTP/1.1 503 Error
+Date: Thu, 11 Jul 2019 02:26:59 GMT
+Server: test-server/swsclose
+
+</data>
+</reply>
+# Client-side
+<client>
+<server>
+http
+</server>
+<name>
+HTTP Retry-After header extraction (without header)
+</name>
+<tool>
+lib1594
+</tool>
+<command>
+http://%HOSTIP:%HTTPPORT/1595
+</command>
+</client>
+
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol>
+GET /1595 HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
+Accept: */*
+
+</protocol>
+<stdout>
+Retry-After: 0
+</stdout>
+</verify>
+</testcase>
diff --git a/tests/data/test1596 b/tests/data/test1596
new file mode 100644
index 000000000..9a8cb480e
--- /dev/null
+++ b/tests/data/test1596
@@ -0,0 +1,52 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP GET
+HTTP replaced headers
+CURLOPT_TIMECONDITION
+If-Modified-Since
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data nocheck="yes">
+HTTP/1.1 503 Error
+Date: Thu, 11 Jul 2019 02:26:59 GMT
+Server: test-server/swsclose
+Retry-After: Thu, 11 Jul 2024 02:26:59 GMT
+
+</data>
+</reply>
+# Client-side
+<client>
+<server>
+http
+</server>
+<name>
+HTTP Retry-After header parsing using a date
+</name>
+<tool>
+lib1596
+</tool>
+<command>
+http://%HOSTIP:%HTTPPORT/1596
+</command>
+</client>
+
+<verify>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol>
+GET /1596 HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
+Accept: */*
+
+</protocol>
+<stdout>
+Retry-After: 172066
+</stdout>
+</verify>
+</testcase>
diff --git a/tests/data/test1654 b/tests/data/test1654
index 175076c8a..5b32cb419 100644
--- a/tests/data/test1654
+++ b/tests/data/test1654
@@ -32,7 +32,7 @@ unit1654
<file name="log/1654" mode="text">
h2 example.com 443 h3 shiny.example.com 8443 "20191231 00:00:00" 0 1
# a comment
-h2c example.com 443 h3 shiny.example.com 8443 "20291231 23:30:00" 0 1
+h2 foo.example.com 443 h3 shiny.example.com 8443 "20291231 23:30:00" 0 1
h1 example.com 443 h3 shiny.example.com 8443 "20121231 00:00:01" 0 1
h3 example.com 443 h3 shiny.example.com 8443 "20131231 00:00:00" 0 1
# also a comment
@@ -45,14 +45,14 @@ rubbish
# Your alt-svc cache. https://curl.haxx.se/docs/alt-svc.html
# This file was generated by libcurl! Edit at your own risk.
h2 example.com 443 h3 shiny.example.com 8443 "20191231 00:00:00" 0 1
-h2c example.com 443 h3 shiny.example.com 8443 "20291231 23:30:00" 0 1
+h2 foo.example.com 443 h3 shiny.example.com 8443 "20291231 23:30:00" 0 1
h1 example.com 443 h3 shiny.example.com 8443 "20121231 00:00:01" 0 1
h3 example.com 443 h3 shiny.example.com 8443 "20131231 00:00:00" 0 1
h1 example.org 8080 h2 example.com 8080 "20190125 22:34:21" 0 0
h1 2.example.org 8080 h3 2.example.org 8080 "20190125 22:34:21" 0 0
h1 3.example.org 8080 h2 example.com 8080 "20190125 22:34:21" 0 0
h1 3.example.org 8080 h3 yesyes.com 8080 "20190125 22:34:21" 0 0
-h2c example.org 80 h2 example.com 443 "20190124 22:36:21" 0 0
+h2 example.org 80 h2 example.com 443 "20190124 22:36:21" 0 0
</file>
</verify>
</testcase>
diff --git a/tests/data/test2006 b/tests/data/test2006
index 3acbdaee2..4d08e0aad 100644
--- a/tests/data/test2006
+++ b/tests/data/test2006
@@ -86,10 +86,6 @@ Accept: */*
Some data delivered from an HTTP resource
</file1>
<file2 name="log/heads2006">
-Content-Length: 496
-Accept-ranges: bytes
-
-
HTTP/1.1 200 OK
Date: Thu, 21 Jun 2012 14:49:01 GMT
Server: test-server/fake
diff --git a/tests/data/test2007 b/tests/data/test2007
index b169c4906..bb4d5cde9 100644
--- a/tests/data/test2007
+++ b/tests/data/test2007
@@ -90,10 +90,6 @@ Something delivered from an HTTP resource
s/Last-Modified:.*//
</stripfile2>
<file2 name="log/heads2007">
-Content-Length: 496
-Accept-ranges: bytes
-
-
HTTP/1.1 200 OK
Date: Thu, 21 Jun 2012 14:50:02 GMT
Server: test-server/fake
diff --git a/tests/data/test2008 b/tests/data/test2008
index 012f221c4..d6bbf6b4b 100644
--- a/tests/data/test2008
+++ b/tests/data/test2008
@@ -82,10 +82,6 @@ Some stuff delivered from an HTTP resource
s/Last-Modified:.*//
</stripfile2>
<file2 name="log/heads2008">
-Content-Length: 496
-Accept-ranges: bytes
-
-
HTTP/1.1 200 OK
Date: Thu, 21 Jun 2012 15:23:48 GMT
Server: test-server/fake
diff --git a/tests/data/test2009 b/tests/data/test2009
index b0e5c6c66..1a9335851 100644
--- a/tests/data/test2009
+++ b/tests/data/test2009
@@ -83,10 +83,6 @@ Some contents delivered from an HTTP resource
s/Last-Modified:.*//
</stripfile2>
<file2 name="log/heads2009">
-Content-Length: 496
-Accept-ranges: bytes
-
-
HTTP/1.1 200 OK
Date: Thu, 21 Jun 2012 16:27:17 GMT
Server: test-server/fake
diff --git a/tests/data/test2010 b/tests/data/test2010
index 33bb309eb..1f5320fe9 100644
--- a/tests/data/test2010
+++ b/tests/data/test2010
@@ -82,10 +82,6 @@ Contents delivered from an HTTP resource
s/Last-Modified:.*//
</stripfile2>
<file2 name="log/heads2010">
-Content-Length: 496
-Accept-ranges: bytes
-
-
HTTP/1.1 200 OK
Date: Thu, 21 Jun 2012 17:37:27 GMT
Server: test-server/fake
diff --git a/tests/data/test2047 b/tests/data/test2047
index fc140486b..bc7350971 100644
--- a/tests/data/test2047
+++ b/tests/data/test2047
@@ -92,7 +92,7 @@ OK
1
1
3
-http://xn--4cab6c.se/20470001
+http://åäö.se/20470001
text/plain; charset=us-ascii
200
</stdout>
diff --git a/tests/data/test2077 b/tests/data/test2077
new file mode 100644
index 000000000..0c600f5c3
--- /dev/null
+++ b/tests/data/test2077
@@ -0,0 +1,42 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP GET
+GSS-API
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data>
+HTTP/1.1 200 OK swsclose
+Content-Length: 23
+
+This IS the real page!
+</data>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+<features>
+GSS-API
+</features>
+<name>
+curl --fail --negotiate to unauthenticated service fails
+</name>
+<command>
+http://%HOSTIP:%HTTPPORT/2077 -u : --fail --negotiate
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<errorcode>
+0
+</errorcode>
+</verify>
+</testcase>
diff --git a/tests/data/test2078 b/tests/data/test2078
new file mode 100644
index 000000000..99bc2dbee
--- /dev/null
+++ b/tests/data/test2078
@@ -0,0 +1,54 @@
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP GET
+GSS-API
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data>
+HTTP/1.1 200 OK swsclose
+Content-Length: 23
+
+This IS the real page!
+</data>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+<features>
+GSS-API
+</features>
+<name>
+curl --negotiate should not send empty POST request only
+</name>
+<command>
+http://%HOSTIP:%HTTPPORT/2078 -u : --negotiate --data name=value
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<errorcode>
+0
+</errorcode>
+<strip>
+^User-Agent:.*
+</strip>
+<protocol nonewline="yes">
+POST /2078 HTTP/1.1
+Host: 127.0.0.1:8990
+Accept: */*
+Content-Length: 10
+Content-Type: application/x-www-form-urlencoded
+
+name=value
+</protocol>
+</verify>
+</testcase>
diff --git a/tests/data/test335 b/tests/data/test335
new file mode 100644
index 000000000..4d54da980
--- /dev/null
+++ b/tests/data/test335
@@ -0,0 +1,102 @@
+# Mostly a duplicate of test168
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP GET
+HTTP proxy
+HTTP proxy Digest auth
+HTTP Digest auth
+HTTP auth in URL
+</keywords>
+</info>
+
+# Server-side
+<reply>
+
+# this is returned first since we get no proxy-auth
+<data>
+HTTP/1.1 407 Authorization Required to proxy me my dear swsclose
+Proxy-Authenticate: Digest realm="weirdorealm", nonce="12345"
+
+And you should ignore this data.
+</data>
+
+# then this is returned since we get no server-auth
+<data1000>
+HTTP/1.1 401 Authorization to the remote host as well swsbounce swsclose
+WWW-Authenticate: Digest realm="realmweirdo", nonce="123456"
+
+you should ignore this data too
+</data1000>
+
+<data1001>
+HTTP/1.1 200 OK swsclose
+Server: no
+Content-Length: 15
+
+Nice auth sir!
+</data1001>
+
+<datacheck>
+HTTP/1.1 407 Authorization Required to proxy me my dear swsclose
+Proxy-Authenticate: Digest realm="weirdorealm", nonce="12345"
+
+HTTP/1.1 401 Authorization to the remote host as well swsbounce swsclose
+WWW-Authenticate: Digest realm="realmweirdo", nonce="123456"
+
+HTTP/1.1 200 OK swsclose
+Server: no
+Content-Length: 15
+
+Nice auth sir!
+</datacheck>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+<features>
+!SSPI
+crypto
+</features>
+ <name>
+HTTP with proxy Digest and site Digest with creds in URLs
+ </name>
+ <command>
+http://digest:alot@data.from.server.requiring.digest.hohoho.com/335 --proxy http://foo:bar@%HOSTIP:%HTTPPORT --proxy-digest --digest
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<strip>
+^User-Agent: curl/.*
+</strip>
+<protocol>
+GET http://data.from.server.requiring.digest.hohoho.com/335 HTTP/1.1
+Host: data.from.server.requiring.digest.hohoho.com
+User-Agent: curl/7.12.0-CVS (i686-pc-linux-gnu) libcurl/7.12.0-CVS OpenSSL/0.9.6b zlib/1.1.4 c-ares/1.2.0 libidn/0.4.3
+Accept: */*
+Proxy-Connection: Keep-Alive
+
+GET http://data.from.server.requiring.digest.hohoho.com/335 HTTP/1.1
+Host: data.from.server.requiring.digest.hohoho.com
+Proxy-Authorization: Digest username="foo", realm="weirdorealm", nonce="12345", uri="/335", response="f61609cd8f5bb205ef4e169b2c5626cb"
+User-Agent: curl/7.12.0-CVS (i686-pc-linux-gnu) libcurl/7.12.0-CVS OpenSSL/0.9.6b zlib/1.1.4 c-ares/1.2.0 libidn/0.4.3
+Accept: */*
+Proxy-Connection: Keep-Alive
+
+GET http://data.from.server.requiring.digest.hohoho.com/335 HTTP/1.1
+Host: data.from.server.requiring.digest.hohoho.com
+Proxy-Authorization: Digest username="foo", realm="weirdorealm", nonce="12345", uri="/335", response="f61609cd8f5bb205ef4e169b2c5626cb"
+Authorization: Digest username="digest", realm="realmweirdo", nonce="123456", uri="/335", response="08a2e2e684047f4219a38ddc189ac00c"
+User-Agent: curl/7.12.0-CVS (i686-pc-linux-gnu) libcurl/7.12.0-CVS OpenSSL/0.9.6b ipv6 zlib/1.1.4 GSS libidn/0.4.3
+Accept: */*
+Proxy-Connection: Keep-Alive
+
+</protocol>
+</verify>
+</testcase>
diff --git a/tests/data/test356 b/tests/data/test356
index e2ac4860d..1be05fe6f 100644
--- a/tests/data/test356
+++ b/tests/data/test356
@@ -16,7 +16,7 @@ Content-Length: 6
Connection: close
Content-Type: text/html
Funny-head: yesyes
-Alt-Svc: h1="nowhere.foo:81"
+Alt-Svc: h1="nowhere.foo:81", un-kno22!wn=":82"
-foo-
</data>
diff --git a/tests/data/test848 b/tests/data/test848
new file mode 100644
index 000000000..2b4a30b2a
--- /dev/null
+++ b/tests/data/test848
@@ -0,0 +1,56 @@
+<testcase>
+<info>
+<keywords>
+IMAP
+SASL
+SASL AUTH PLAIN
+RFC4616
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+<servercmd>
+AUTH PLAIN
+REPLY AUTHENTICATE +
+REPLY c2hhcmVkLW1haWxib3gAdXNlcgBzZWNyZXQ= A002 OK AUTHENTICATE completed
+</servercmd>
+<data>
+From: me@somewhere
+To: fake@nowhere
+
+body
+
+--
+ yours sincerely
+</data>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+imap
+</server>
+ <name>
+IMAP plain authentication with alternative authorization identity
+ </name>
+ <command>
+'imap://%HOSTIP:%IMAPPORT/848/;MAILINDEX=1' -u user:secret --sasl-authzid shared-mailbox
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<protocol>
+A001 CAPABILITY
+A002 AUTHENTICATE PLAIN
+c2hhcmVkLW1haWxib3gAdXNlcgBzZWNyZXQ=
+A003 SELECT 848
+A004 FETCH 1 BODY[]
+A005 LOGOUT
+</protocol>
+</verify>
+</testcase>
diff --git a/tests/data/test849 b/tests/data/test849
new file mode 100644
index 000000000..8a4b39c00
--- /dev/null
+++ b/tests/data/test849
@@ -0,0 +1,51 @@
+<testcase>
+<info>
+<keywords>
+IMAP
+SASL
+SASL AUTH PLAIN
+RFC4616
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+<servercmd>
+AUTH PLAIN
+REPLY AUTHENTICATE +
+REPLY dXJzZWwAa3VydAB4aXBqM3BsbXE= A002 NO Not authorized
+</servercmd>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+imap
+</server>
+ <name>
+IMAP plain authentication with alternative authorization identity (Not authorized)
+ </name>
+ <command>
+'imap://%HOSTIP:%IMAPPORT/849/;MAILINDEX=1' -u kurt:xipj3plmq --sasl-authzid ursel
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+# 67 - CURLE_LOGIN_DENIED
+<errorcode>
+67
+</errorcode>
+#
+# The multi interface considers a broken "CONNECT" as a prematurely broken
+# transfer and such a connection will not get a "LOGOUT"
+<protocol>
+A001 CAPABILITY
+A002 AUTHENTICATE PLAIN
+dXJzZWwAa3VydAB4aXBqM3BsbXE=
+</protocol>
+</verify>
+</testcase>
diff --git a/tests/data/test892 b/tests/data/test892
new file mode 100644
index 000000000..4b8318292
--- /dev/null
+++ b/tests/data/test892
@@ -0,0 +1,57 @@
+<testcase>
+<info>
+<keywords>
+POP3
+SASL
+SASL AUTH PLAIN
+RFC1734
+RFC4616
+RFC5034
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+<servercmd>
+AUTH PLAIN
+REPLY AUTH +
+REPLY c2hhcmVkLW1haWxib3gAdXNlcgBzZWNyZXQ= +OK Login successful
+</servercmd>
+<data>
+From: me@somewhere
+To: fake@nowhere
+
+body
+
+--
+ yours sincerely
+</data>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+pop3
+</server>
+ <name>
+POP3 plain authentication with alternative authorization identity
+ </name>
+ <command>
+pop3://%HOSTIP:%POP3PORT/892 -u user:secret --sasl-authzid shared-mailbox
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<protocol>
+CAPA
+AUTH PLAIN
+c2hhcmVkLW1haWxib3gAdXNlcgBzZWNyZXQ=
+RETR 892
+QUIT
+</protocol>
+</verify>
+</testcase>
diff --git a/tests/data/test893 b/tests/data/test893
new file mode 100644
index 000000000..2a762fc21
--- /dev/null
+++ b/tests/data/test893
@@ -0,0 +1,53 @@
+<testcase>
+<info>
+<keywords>
+POP3
+SASL
+SASL AUTH PLAIN
+RFC1734
+RFC4616
+RFC5034
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+<servercmd>
+AUTH PLAIN
+REPLY AUTH +
+REPLY dXJzZWwAa3VydAB4aXBqM3BsbXE= -ERR Not authorized
+</servercmd>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+pop3
+</server>
+ <name>
+POP3 plain authentication with alternative authorization identity (Not authorized)
+ </name>
+ <command>
+pop3://%HOSTIP:%POP3PORT/893 -u kurt:xipj3plmq --sasl-authzid ursel
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+# 67 - CURLE_LOGIN_DENIED
+<errorcode>
+67
+</errorcode>
+#
+# The multi interface considers a broken "CONNECT" as a prematurely broken
+# transfer and such a connection will not get a "QUIT"
+<protocol>
+CAPA
+AUTH PLAIN
+dXJzZWwAa3VydAB4aXBqM3BsbXE=
+</protocol>
+</verify>
+</testcase>
diff --git a/tests/data/test953 b/tests/data/test953
new file mode 100644
index 000000000..4a70e1f31
--- /dev/null
+++ b/tests/data/test953
@@ -0,0 +1,56 @@
+<testcase>
+<info>
+<keywords>
+SMTP
+SASL
+SASL AUTH PLAIN
+RFC4616
+RFC4954
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+<servercmd>
+AUTH PLAIN
+REPLY AUTH 334 PLAIN supported
+REPLY dXJzZWwAa3VydAB4aXBqM3BsbXE= 235 Authenticated
+</servercmd>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+smtp
+</server>
+ <name>
+SMTP plain authentication with alternative authorization identity
+ </name>
+<stdin>
+mail body
+</stdin>
+ <command>
+smtp://%HOSTIP:%SMTPPORT/953 --mail-rcpt recipient@example.com --mail-from sender@example.com -u kurt:xipj3plmq --sasl-authzid ursel -T -
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<protocol>
+EHLO 953
+AUTH PLAIN
+dXJzZWwAa3VydAB4aXBqM3BsbXE=
+MAIL FROM:<sender@example.com>
+RCPT TO:<recipient@example.com>
+DATA
+QUIT
+</protocol>
+<upload>
+mail body
+.
+</upload>
+</verify>
+</testcase>
diff --git a/tests/data/test954 b/tests/data/test954
new file mode 100644
index 000000000..a5e6bb0d3
--- /dev/null
+++ b/tests/data/test954
@@ -0,0 +1,55 @@
+<testcase>
+<info>
+<keywords>
+SMTP
+SASL
+SASL AUTH PLAIN
+RFC4616
+RFC4954
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+<servercmd>
+AUTH PLAIN
+REPLY AUTH 334 PLAIN supported
+REPLY dXJzZWwAa3VydAB4aXBqM3BsbXE= 501 Not authorized
+</servercmd>
+</reply>
+
+#
+# Client-side
+<client>
+<server>
+smtp
+</server>
+ <name>
+SMTP plain authentication with alternative authorization identity (Not authorized)
+ </name>
+<stdin>
+mail body
+</stdin>
+ <command>
+smtp://%HOSTIP:%SMTPPORT/954 --mail-rcpt recipient@example.com --mail-from sender@example.com -u kurt:xipj3plmq --sasl-authzid ursel -T -
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+# 67 - CURLE_LOGIN_DENIED
+<errorcode>
+67
+</errorcode>
+#
+# The multi interface considers a broken "CONNECT" as a prematurely broken
+# transfer and such a connection will not get a "QUIT"
+<protocol>
+EHLO 954
+AUTH PLAIN
+dXJzZWwAa3VydAB4aXBqM3BsbXE=
+</protocol>
+</verify>
+</testcase>
diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc
index f5effd97d..4ea9cf2a7 100644
--- a/tests/libtest/Makefile.inc
+++ b/tests/libtest/Makefile.inc
@@ -32,7 +32,7 @@ noinst_PROGRAMS = chkhostname libauthretry libntlmconnect \
lib1540 lib1541 \
lib1550 lib1551 lib1552 lib1553 lib1554 lib1555 lib1556 lib1557 \
lib1558 lib1559 lib1560 \
- lib1591 lib1592 lib1593 \
+ lib1591 lib1592 lib1593 lib1594 lib1596 \
lib1900 lib1905 lib1906 \
lib2033
@@ -544,6 +544,13 @@ lib1592_CPPFLAGS = $(AM_CPPFLAGS) -DLIB1592
lib1593_SOURCES = lib1593.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
lib1593_LDADD = $(TESTUTIL_LIBS)
+lib1594_SOURCES = lib1594.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
+lib1594_LDADD = $(TESTUTIL_LIBS)
+
+lib1596_SOURCES = lib1594.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
+lib1596_LDADD = $(TESTUTIL_LIBS)
+lib1596_CPPFLAGS = $(AM_CPPFLAGS) -DLIB1596
+
lib1900_SOURCES = lib1900.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
lib1900_LDADD = $(TESTUTIL_LIBS)
lib1900_CPPFLAGS = $(AM_CPPFLAGS)
diff --git a/tests/libtest/first.c b/tests/libtest/first.c
index d687bf276..2731ef8db 100644
--- a/tests/libtest/first.c
+++ b/tests/libtest/first.c
@@ -97,9 +97,9 @@ static void memory_tracking_init(void)
strcpy(fname, env);
curl_free(env);
curl_dbg_memdebug(fname);
- /* this weird stuff here is to make curl_free() get called
- before curl_memdebug() as otherwise memory tracking will
- log a free() without an alloc! */
+ /* this weird stuff here is to make curl_free() get called before
+ curl_dbg_memdebug() as otherwise memory tracking will log a free()
+ without an alloc! */
}
/* if CURL_MEMLIMIT is set, this enables fail-on-alloc-number-N feature */
env = curl_getenv("CURL_MEMLIMIT");
diff --git a/tests/libtest/lib1560.c b/tests/libtest/lib1560.c
index 1185096d8..85884474e 100644
--- a/tests/libtest/lib1560.c
+++ b/tests/libtest/lib1560.c
@@ -140,6 +140,10 @@ static struct testcase get_parts_list[] ={
"file | [11] | [12] | [13] | [14] | [15] | C:\\programs\\foo | [16] | [17]",
CURLU_DEFAULT_SCHEME, 0, CURLUE_OK},
#endif
+ {"http://[ab.be:1]/x", "",
+ CURLU_DEFAULT_SCHEME, 0, CURLUE_MALFORMED_INPUT},
+ {"http://[ab.be]/x", "",
+ CURLU_DEFAULT_SCHEME, 0, CURLUE_MALFORMED_INPUT},
/* URL without host name */
{"http://a:b@/x", "",
CURLU_DEFAULT_SCHEME, 0, CURLUE_NO_HOST},
diff --git a/tests/libtest/lib1594.c b/tests/libtest/lib1594.c
new file mode 100644
index 000000000..a76971590
--- /dev/null
+++ b/tests/libtest/lib1594.c
@@ -0,0 +1,66 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed 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.
+ *
+ ***************************************************************************/
+
+/* Testing Retry-After header parser */
+
+#include "test.h"
+
+#include "memdebug.h"
+
+int test(char *URL)
+{
+ struct curl_slist *header = NULL;
+ curl_off_t retry;
+ CURL *curl = NULL;
+ int res = 0;
+
+ global_init(CURL_GLOBAL_ALL);
+
+ easy_init(curl);
+
+ easy_setopt(curl, CURLOPT_URL, URL);
+
+ res = curl_easy_perform(curl);
+ if(res)
+ goto test_cleanup;
+
+ res = curl_easy_getinfo(curl, CURLINFO_RETRY_AFTER, &retry);
+ if(res)
+ goto test_cleanup;
+
+#ifdef LIB1596
+ /* we get a relative number of seconds, so add the number of seconds
+ we're at to make it a somewhat stable number. Then remove accuracy. */
+ retry += time(NULL);
+ retry /= 10000;
+#endif
+ printf("Retry-After: %" CURL_FORMAT_CURL_OFF_T "\n", retry);
+
+test_cleanup:
+
+ /* always cleanup */
+ curl_easy_cleanup(curl);
+ curl_slist_free_all(header);
+ curl_global_cleanup();
+
+ return res;
+}
diff --git a/tests/symbol-scan.pl b/tests/symbol-scan.pl
index cfaffa792..c8d68147a 100755
--- a/tests/symbol-scan.pl
+++ b/tests/symbol-scan.pl
@@ -119,11 +119,13 @@ for my $e (sort @syms) {
# CURL_EXTERN - is a define used for libcurl functions that are external,
# public. No app or other code should ever use it.
#
+ # CURLINC_ - defines for header dual-include prevention, ignore those.
+ #
# *_LAST and *_LASTENTRY are just prefix for the placeholders used for the
# last entry in many enum series.
#
- if($e =~ /(OBSOLETE|^CURL_EXTERN|_LAST\z|_LASTENTRY\z)/) {
+ if($e =~ /(OBSOLETE|^CURL_EXTERN|^CURLINC_|_LAST\z|_LASTENTRY\z)/) {
$ignored++;
next;
}
diff --git a/tests/unit/unit1607.c b/tests/unit/unit1607.c
index 6e40be707..a8b0331ce 100644
--- a/tests/unit/unit1607.c
+++ b/tests/unit/unit1607.c
@@ -25,10 +25,6 @@
#include "connect.h"
#include "share.h"
-/* retrieves ip address and port from a sockaddr structure.
- note it calls Curl_inet_ntop which sets errno on fail, not SOCKERRNO. */
-bool getaddressinfo(struct sockaddr *sa, char *addr, long *port);
-
#include "memdebug.h" /* LAST include file */
static void unit_stop(void)
@@ -154,8 +150,8 @@ UNITTEST_START
if(tests[i].address[j] == &skip)
continue;
- if(addr && !getaddressinfo(addr->ai_addr,
- ipaddress, &port)) {
+ if(addr && !Curl_addr2string(addr->ai_addr, addr->ai_addrlen,
+ ipaddress, &port)) {
fprintf(stderr, "%s:%d tests[%d] failed. getaddressinfo failed.\n",
__FILE__, __LINE__, i);
problem = true;
diff --git a/tests/unit/unit1609.c b/tests/unit/unit1609.c
index 8d9bc6526..8223a147c 100644
--- a/tests/unit/unit1609.c
+++ b/tests/unit/unit1609.c
@@ -25,10 +25,6 @@
#include "connect.h"
#include "share.h"
-/* retrieves ip address and port from a sockaddr structure.
- note it calls Curl_inet_ntop which sets errno on fail, not SOCKERRNO. */
-bool getaddressinfo(struct sockaddr *sa, char *addr, long *port);
-
#include "memdebug.h" /* LAST include file */
static void unit_stop(void)
@@ -154,9 +150,9 @@ UNITTEST_START
if(!addr && !tests[i].address[j])
break;
- if(addr && !getaddressinfo(addr->ai_addr,
- ipaddress, &port)) {
- fprintf(stderr, "%s:%d tests[%d] failed. getaddressinfo failed.\n",
+ if(addr && !Curl_addr2string(addr->ai_addr, addr->ai_addrlen,
+ ipaddress, &port)) {
+ fprintf(stderr, "%s:%d tests[%d] failed. Curl_addr2string failed.\n",
__FILE__, __LINE__, i);
problem = true;
break;
diff --git a/tests/unit/unit1654.c b/tests/unit/unit1654.c
index 9d1a3e211..51fc5d16f 100644
--- a/tests/unit/unit1654.c
+++ b/tests/unit/unit1654.c
@@ -90,7 +90,7 @@ UNITTEST_START
fail_unless(asi->num == 8, "wrong number of entries");
result = Curl_altsvc_parse(curl, asi, "h2=\"example.com:443\"; ma = 120;",
- ALPN_h2c, "example.org", 80);
+ ALPN_h2, "example.org", 80);
if(result) {
fprintf(stderr, "Curl_altsvc_parse(4) failed!\n");
unitfail++;
diff --git a/winbuild/MakefileBuild.vc b/winbuild/MakefileBuild.vc
index 993ab38bc..b5742e109 100644
--- a/winbuild/MakefileBuild.vc
+++ b/winbuild/MakefileBuild.vc
@@ -559,6 +559,7 @@ $(LIB_DIROBJ):
@if not exist "$(LIB_DIROBJ)" mkdir $(LIB_DIROBJ)
@if not exist "$(LIB_DIROBJ)\vauth" mkdir $(LIB_DIROBJ)\vauth
@if not exist "$(LIB_DIROBJ)\vtls" mkdir $(LIB_DIROBJ)\vtls
+ @if not exist "$(LIB_DIROBJ)\vquic" mkdir $(LIB_DIROBJ)\vquic
$(CURL_DIROBJ):
@if not exist "$(CURL_DIROBJ)" mkdir $(CURL_DIROBJ)
@@ -577,6 +578,9 @@ $(CURL_DIROBJ):
{$(LIBCURL_SRC_DIR)\vtls\}.c{$(LIB_DIROBJ)\vtls\}.obj:
$(CURL_CC) $(CFLAGS) /Fo"$@" $<
+{$(LIBCURL_SRC_DIR)\vquic\}.c{$(LIB_DIROBJ)\vquic\}.obj:
+ $(CURL_CC) $(CFLAGS) /Fo"$@" $<
+
$(LIB_DIROBJ)\libcurl.res: $(LIBCURL_SRC_DIR)\libcurl.rc
$(RC) $(RC_FLAGS)