summaryrefslogtreecommitdiff
path: root/libs/libcurl/src
diff options
context:
space:
mode:
Diffstat (limited to 'libs/libcurl/src')
-rw-r--r--libs/libcurl/src/Makefile.in31
-rw-r--r--libs/libcurl/src/Makefile.inc4
-rw-r--r--libs/libcurl/src/Makefile.netware2
-rw-r--r--libs/libcurl/src/altsvc.c20
-rw-r--r--libs/libcurl/src/asyn-thread.c28
-rw-r--r--libs/libcurl/src/checksrc.pl2
-rw-r--r--libs/libcurl/src/config-amigaos.h4
-rw-r--r--libs/libcurl/src/config-os400.h6
-rw-r--r--libs/libcurl/src/config-plan9.h1
-rw-r--r--libs/libcurl/src/config-riscos.h8
-rw-r--r--libs/libcurl/src/config-symbian.h3
-rw-r--r--libs/libcurl/src/config-tpf.h6
-rw-r--r--libs/libcurl/src/config-vxworks.h3
-rw-r--r--libs/libcurl/src/config-win32.h4
-rw-r--r--libs/libcurl/src/conncache.c8
-rw-r--r--libs/libcurl/src/connect.c15
-rw-r--r--libs/libcurl/src/cookie.c19
-rw-r--r--libs/libcurl/src/cookie.h2
-rw-r--r--libs/libcurl/src/curl_config.h.cmake3
-rw-r--r--libs/libcurl/src/curl_config.h.in6
-rw-r--r--libs/libcurl/src/doh.c58
-rw-r--r--libs/libcurl/src/easy.c7
-rw-r--r--libs/libcurl/src/ftp.c396
-rw-r--r--libs/libcurl/src/ftp.h6
-rw-r--r--libs/libcurl/src/ftplistparser.c2
-rw-r--r--libs/libcurl/src/hostcheck.c2
-rw-r--r--libs/libcurl/src/hostip.c2
-rw-r--r--libs/libcurl/src/http.c17
-rw-r--r--libs/libcurl/src/http.h5
-rw-r--r--libs/libcurl/src/http2.c49
-rw-r--r--libs/libcurl/src/http_chunks.c28
-rw-r--r--libs/libcurl/src/http_chunks.h13
-rw-r--r--libs/libcurl/src/http_proxy.c9
-rw-r--r--libs/libcurl/src/imap.c5
-rw-r--r--libs/libcurl/src/ldap.c24
-rw-r--r--libs/libcurl/src/libcurl.plist6
-rw-r--r--libs/libcurl/src/mime.c19
-rw-r--r--libs/libcurl/src/mime.h6
-rw-r--r--libs/libcurl/src/multi.c18
-rw-r--r--libs/libcurl/src/multihandle.h1
-rw-r--r--libs/libcurl/src/multiif.h6
-rw-r--r--libs/libcurl/src/netrc.c2
-rw-r--r--libs/libcurl/src/non-ascii.c2
-rw-r--r--libs/libcurl/src/parsedate.c14
-rw-r--r--libs/libcurl/src/security.c2
-rw-r--r--libs/libcurl/src/setopt.c21
-rw-r--r--libs/libcurl/src/setup-os400.h6
-rw-r--r--libs/libcurl/src/smb.c3
-rw-r--r--libs/libcurl/src/socketpair.c118
-rw-r--r--libs/libcurl/src/socketpair.h36
-rw-r--r--libs/libcurl/src/socks.c64
-rw-r--r--libs/libcurl/src/strcase.c86
-rw-r--r--libs/libcurl/src/strcase.h4
-rw-r--r--libs/libcurl/src/transfer.c14
-rw-r--r--libs/libcurl/src/url.c61
-rw-r--r--libs/libcurl/src/url.h2
-rw-r--r--libs/libcurl/src/urlapi.c156
-rw-r--r--libs/libcurl/src/urldata.h393
-rw-r--r--libs/libcurl/src/vauth/vauth.h2
-rw-r--r--libs/libcurl/src/version.c15
-rw-r--r--libs/libcurl/src/vquic/ngtcp2.c1593
-rw-r--r--libs/libcurl/src/vquic/ngtcp2.h63
-rw-r--r--libs/libcurl/src/vquic/quiche.c783
-rw-r--r--libs/libcurl/src/vquic/quiche.h49
-rw-r--r--libs/libcurl/src/vssh/libssh.c6
-rw-r--r--libs/libcurl/src/vssh/libssh2.c4
-rw-r--r--libs/libcurl/src/vtls/gskit.c102
-rw-r--r--libs/libcurl/src/vtls/gtls.c6
-rw-r--r--libs/libcurl/src/vtls/mbedtls.c7
-rw-r--r--libs/libcurl/src/vtls/mesalink.c7
-rw-r--r--libs/libcurl/src/vtls/nss.c2
-rw-r--r--libs/libcurl/src/vtls/openssl.c32
-rw-r--r--libs/libcurl/src/vtls/polarssl.c4
-rw-r--r--libs/libcurl/src/vtls/schannel.c12
-rw-r--r--libs/libcurl/src/vtls/schannel_verify.c2
-rw-r--r--libs/libcurl/src/vtls/sectransp.c6
-rw-r--r--libs/libcurl/src/vtls/vtls.c5
77 files changed, 3656 insertions, 882 deletions
diff --git a/libs/libcurl/src/Makefile.in b/libs/libcurl/src/Makefile.in
index 28dfe93e43..27101a8eea 100644
--- a/libs/libcurl/src/Makefile.in
+++ b/libs/libcurl/src/Makefile.in
@@ -229,7 +229,7 @@ am__objects_1 = libcurl_la-file.lo libcurl_la-timeval.lo \
libcurl_la-curl_path.lo libcurl_la-curl_ctype.lo \
libcurl_la-curl_range.lo libcurl_la-psl.lo libcurl_la-doh.lo \
libcurl_la-urlapi.lo libcurl_la-curl_get_line.lo \
- libcurl_la-altsvc.lo
+ libcurl_la-altsvc.lo libcurl_la-socketpair.lo
am__dirstamp = $(am__leading_dot)dirstamp
am__objects_2 = vauth/libcurl_la-vauth.lo \
vauth/libcurl_la-cleartext.lo vauth/libcurl_la-cram.lo \
@@ -315,7 +315,8 @@ am__objects_9 = libcurlu_la-file.lo libcurlu_la-timeval.lo \
libcurlu_la-setopt.lo libcurlu_la-curl_path.lo \
libcurlu_la-curl_ctype.lo libcurlu_la-curl_range.lo \
libcurlu_la-psl.lo libcurlu_la-doh.lo libcurlu_la-urlapi.lo \
- libcurlu_la-curl_get_line.lo libcurlu_la-altsvc.lo
+ libcurlu_la-curl_get_line.lo libcurlu_la-altsvc.lo \
+ libcurlu_la-socketpair.lo
am__objects_10 = vauth/libcurlu_la-vauth.lo \
vauth/libcurlu_la-cleartext.lo vauth/libcurlu_la-cram.lo \
vauth/libcurlu_la-digest.lo vauth/libcurlu_la-digest_sspi.lo \
@@ -444,6 +445,7 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
./$(DEPDIR)/libcurl_la-share.Plo \
./$(DEPDIR)/libcurl_la-slist.Plo \
./$(DEPDIR)/libcurl_la-smb.Plo ./$(DEPDIR)/libcurl_la-smtp.Plo \
+ ./$(DEPDIR)/libcurl_la-socketpair.Plo \
./$(DEPDIR)/libcurl_la-socks.Plo \
./$(DEPDIR)/libcurl_la-socks_gssapi.Plo \
./$(DEPDIR)/libcurl_la-socks_sspi.Plo \
@@ -554,6 +556,7 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
./$(DEPDIR)/libcurlu_la-slist.Plo \
./$(DEPDIR)/libcurlu_la-smb.Plo \
./$(DEPDIR)/libcurlu_la-smtp.Plo \
+ ./$(DEPDIR)/libcurlu_la-socketpair.Plo \
./$(DEPDIR)/libcurlu_la-socks.Plo \
./$(DEPDIR)/libcurlu_la-socks_gssapi.Plo \
./$(DEPDIR)/libcurlu_la-socks_sspi.Plo \
@@ -1000,7 +1003,7 @@ LIB_CFILES = file.c timeval.c base64.c hostip.c progress.c formdata.c \
curl_multibyte.c hostcheck.c conncache.c dotdot.c \
x509asn1.c http2.c smb.c curl_endian.c curl_des.c system_win32.c \
mime.c sha256.c setopt.c curl_path.c curl_ctype.c curl_range.c psl.c \
- doh.c urlapi.c curl_get_line.c altsvc.c
+ doh.c urlapi.c curl_get_line.c altsvc.c socketpair.c
LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \
formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h if2ip.h \
@@ -1021,7 +1024,7 @@ 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 quic.h
+ curl_get_line.h altsvc.h quic.h socketpair.h
LIB_RCFILES = libcurl.rc
CSOURCES = $(LIB_CFILES) $(LIB_VAUTH_CFILES) $(LIB_VTLS_CFILES) \
@@ -1390,6 +1393,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-slist.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-smb.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-smtp.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-socketpair.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-socks.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-socks_gssapi.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-socks_sspi.Plo@am__quote@ # am--include-marker
@@ -1500,6 +1504,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-slist.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-smb.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-smtp.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-socketpair.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-socks.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-socks_gssapi.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-socks_sspi.Plo@am__quote@ # am--include-marker
@@ -2380,6 +2385,13 @@ libcurl_la-altsvc.lo: altsvc.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-altsvc.lo `test -f 'altsvc.c' || echo '$(srcdir)/'`altsvc.c
+libcurl_la-socketpair.lo: socketpair.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-socketpair.lo -MD -MP -MF $(DEPDIR)/libcurl_la-socketpair.Tpo -c -o libcurl_la-socketpair.lo `test -f 'socketpair.c' || echo '$(srcdir)/'`socketpair.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-socketpair.Tpo $(DEPDIR)/libcurl_la-socketpair.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socketpair.c' object='libcurl_la-socketpair.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-socketpair.lo `test -f 'socketpair.c' || echo '$(srcdir)/'`socketpair.c
+
vauth/libcurl_la-vauth.lo: vauth/vauth.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vauth/libcurl_la-vauth.lo -MD -MP -MF vauth/$(DEPDIR)/libcurl_la-vauth.Tpo -c -o vauth/libcurl_la-vauth.lo `test -f 'vauth/vauth.c' || echo '$(srcdir)/'`vauth/vauth.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurl_la-vauth.Tpo vauth/$(DEPDIR)/libcurl_la-vauth.Plo
@@ -3353,6 +3365,13 @@ libcurlu_la-altsvc.lo: altsvc.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-altsvc.lo `test -f 'altsvc.c' || echo '$(srcdir)/'`altsvc.c
+libcurlu_la-socketpair.lo: socketpair.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-socketpair.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-socketpair.Tpo -c -o libcurlu_la-socketpair.lo `test -f 'socketpair.c' || echo '$(srcdir)/'`socketpair.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-socketpair.Tpo $(DEPDIR)/libcurlu_la-socketpair.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='socketpair.c' object='libcurlu_la-socketpair.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-socketpair.lo `test -f 'socketpair.c' || echo '$(srcdir)/'`socketpair.c
+
vauth/libcurlu_la-vauth.lo: vauth/vauth.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vauth/libcurlu_la-vauth.lo -MD -MP -MF vauth/$(DEPDIR)/libcurlu_la-vauth.Tpo -c -o vauth/libcurlu_la-vauth.lo `test -f 'vauth/vauth.c' || echo '$(srcdir)/'`vauth/vauth.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vauth/$(DEPDIR)/libcurlu_la-vauth.Tpo vauth/$(DEPDIR)/libcurlu_la-vauth.Plo
@@ -3792,6 +3811,7 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/libcurl_la-slist.Plo
-rm -f ./$(DEPDIR)/libcurl_la-smb.Plo
-rm -f ./$(DEPDIR)/libcurl_la-smtp.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-socketpair.Plo
-rm -f ./$(DEPDIR)/libcurl_la-socks.Plo
-rm -f ./$(DEPDIR)/libcurl_la-socks_gssapi.Plo
-rm -f ./$(DEPDIR)/libcurl_la-socks_sspi.Plo
@@ -3902,6 +3922,7 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/libcurlu_la-slist.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-smb.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-smtp.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-socketpair.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-socks.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-socks_gssapi.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-socks_sspi.Plo
@@ -4115,6 +4136,7 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/libcurl_la-slist.Plo
-rm -f ./$(DEPDIR)/libcurl_la-smb.Plo
-rm -f ./$(DEPDIR)/libcurl_la-smtp.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-socketpair.Plo
-rm -f ./$(DEPDIR)/libcurl_la-socks.Plo
-rm -f ./$(DEPDIR)/libcurl_la-socks_gssapi.Plo
-rm -f ./$(DEPDIR)/libcurl_la-socks_sspi.Plo
@@ -4225,6 +4247,7 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/libcurlu_la-slist.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-smb.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-smtp.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-socketpair.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-socks.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-socks_gssapi.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-socks_sspi.Plo
diff --git a/libs/libcurl/src/Makefile.inc b/libs/libcurl/src/Makefile.inc
index 3e3a385c59..72ef428ee6 100644
--- a/libs/libcurl/src/Makefile.inc
+++ b/libs/libcurl/src/Makefile.inc
@@ -61,7 +61,7 @@ LIB_CFILES = file.c timeval.c base64.c hostip.c progress.c formdata.c \
curl_multibyte.c hostcheck.c conncache.c dotdot.c \
x509asn1.c http2.c smb.c curl_endian.c curl_des.c system_win32.c \
mime.c sha256.c setopt.c curl_path.c curl_ctype.c curl_range.c psl.c \
- doh.c urlapi.c curl_get_line.c altsvc.c
+ doh.c urlapi.c curl_get_line.c altsvc.c socketpair.c
LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \
formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h if2ip.h \
@@ -82,7 +82,7 @@ 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 quic.h
+ curl_get_line.h altsvc.h quic.h socketpair.h
LIB_RCFILES = libcurl.rc
diff --git a/libs/libcurl/src/Makefile.netware b/libs/libcurl/src/Makefile.netware
index 29885a19ea..a40534684d 100644
--- a/libs/libcurl/src/Makefile.netware
+++ b/libs/libcurl/src/Makefile.netware
@@ -640,8 +640,6 @@ ifdef WITH_SSL
@echo $(DL)#define HAVE_OPENSSL_ERR_H 1$(DL) >> $@
@echo $(DL)#define HAVE_OPENSSL_CRYPTO_H 1$(DL) >> $@
@echo $(DL)#define HAVE_OPENSSL_ENGINE_H 1$(DL) >> $@
- @echo $(DL)#define HAVE_LIBSSL 1$(DL) >> $@
- @echo $(DL)#define HAVE_LIBCRYPTO 1$(DL) >> $@
@echo $(DL)#define OPENSSL_NO_KRB5 1$(DL) >> $@
ifdef WITH_SRP
@echo $(DL)#define USE_TLS_SRP 1$(DL) >> $@
diff --git a/libs/libcurl/src/altsvc.c b/libs/libcurl/src/altsvc.c
index a649fefd80..64971a9f0f 100644
--- a/libs/libcurl/src/altsvc.c
+++ b/libs/libcurl/src/altsvc.c
@@ -54,8 +54,8 @@ static enum alpnid alpn2alpnid(char *name)
return ALPN_h1;
if(strcasecompare(name, "h2"))
return ALPN_h2;
-#if (defined(USE_QUICHE) || defined(USE_NGHTTP2)) && !defined(UNITTESTS)
- if(strcasecompare(name, "h3-22"))
+#if (defined(USE_QUICHE) || defined(USE_NGTCP2)) && !defined(UNITTESTS)
+ if(strcasecompare(name, "h3-23"))
return ALPN_h3;
#else
if(strcasecompare(name, "h3"))
@@ -73,8 +73,8 @@ const char *Curl_alpnid2str(enum alpnid id)
case ALPN_h2:
return "h2";
case ALPN_h3:
-#if (defined(USE_QUICHE) || defined(USE_NGHTTP2)) && !defined(UNITTESTS)
- return "h3-22";
+#if (defined(USE_QUICHE) || defined(USE_NGTCP2)) && !defined(UNITTESTS)
+ return "h3-23";
#else
return "h3";
#endif
@@ -442,6 +442,7 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data,
char option[32];
unsigned long num;
char *end_ptr;
+ bool quoted = FALSE;
semip++; /* pass the semicolon */
result = getalnum(&semip, option, sizeof(option));
if(result)
@@ -451,12 +452,21 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data,
if(*semip != '=')
continue;
semip++;
+ while(*semip && ISBLANK(*semip))
+ semip++;
+ if(*semip == '\"') {
+ /* quoted value */
+ semip++;
+ quoted = TRUE;
+ }
num = strtoul(semip, &end_ptr, 10);
- if(num < ULONG_MAX) {
+ if((end_ptr != semip) && num && (num < ULONG_MAX)) {
if(strcasecompare("ma", option))
maxage = num;
else if(strcasecompare("persist", option) && (num == 1))
persist = TRUE;
+ if(quoted && (*end_ptr == '\"'))
+ end_ptr++;
}
semip = end_ptr;
}
diff --git a/libs/libcurl/src/asyn-thread.c b/libs/libcurl/src/asyn-thread.c
index 24da748850..8c552baa9a 100644
--- a/libs/libcurl/src/asyn-thread.c
+++ b/libs/libcurl/src/asyn-thread.c
@@ -21,6 +21,7 @@
***************************************************************************/
#include "curl_setup.h"
+#include "socketpair.h"
/***********************************************************************
* Only for threaded name resolves builds
@@ -74,6 +75,7 @@
#include "inet_ntop.h"
#include "curl_threads.h"
#include "connect.h"
+#include "socketpair.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
@@ -163,7 +165,7 @@ struct thread_sync_data {
char *hostname; /* hostname to resolve, Curl_async.hostname
duplicate */
int port;
-#ifdef HAVE_SOCKETPAIR
+#ifdef USE_SOCKETPAIR
struct connectdata *conn;
curl_socket_t sock_pair[2]; /* socket pair */
#endif
@@ -201,7 +203,7 @@ void destroy_thread_sync_data(struct thread_sync_data * tsd)
if(tsd->res)
Curl_freeaddrinfo(tsd->res);
-#ifdef HAVE_SOCKETPAIR
+#ifdef USE_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.
@@ -243,9 +245,9 @@ 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) {
+#ifdef USE_SOCKETPAIR
+ /* create socket pair, avoid AF_LOCAL since it doesn't build on Solaris */
+ if(Curl_socketpair(AF_UNIX, 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;
@@ -297,7 +299,7 @@ static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg)
struct thread_data *td = tsd->td;
char service[12];
int rc;
-#ifdef HAVE_SOCKETPAIR
+#ifdef USE_SOCKETPAIR
char buf[1];
#endif
@@ -322,11 +324,11 @@ static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg)
free(td);
}
else {
-#ifdef HAVE_SOCKETPAIR
+#ifdef USE_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) {
+ if(swrite(tsd->sock_pair[1], buf, sizeof(buf)) < 0) {
/* update sock_erro to errno */
tsd->sock_error = SOCKERRNO;
}
@@ -382,7 +384,7 @@ 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
+#ifdef USE_SOCKETPAIR
curl_socket_t sock_rd = td->tsd.sock_pair[0];
struct connectdata *conn = td->tsd.conn;
#endif
@@ -407,7 +409,7 @@ static void destroy_async_data(struct Curl_async *async)
free(async->os_specific);
}
-#ifdef HAVE_SOCKETPAIR
+#ifdef USE_SOCKETPAIR
/*
* ensure CURLMOPT_SOCKETFUNCTION fires CURL_POLL_REMOVE
* before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL
@@ -647,13 +649,13 @@ int Curl_resolver_getsock(struct connectdata *conn,
timediff_t ms;
struct Curl_easy *data = conn->data;
struct resdata *reslv = (struct resdata *)data->state.resolver;
-#ifdef HAVE_SOCKETPAIR
+#ifdef USE_SOCKETPAIR
struct thread_data *td = (struct thread_data*)conn->async.os_specific;
#else
(void)socks;
#endif
-#ifdef HAVE_SOCKETPAIR
+#ifdef USE_SOCKETPAIR
if(td) {
/* return read fd to client for polling the DNS resolution status */
socks[0] = td->tsd.sock_pair[0];
@@ -673,7 +675,7 @@ int Curl_resolver_getsock(struct connectdata *conn,
else
milli = 200;
Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
-#ifdef HAVE_SOCKETPAIR
+#ifdef USE_SOCKETPAIR
}
#endif
diff --git a/libs/libcurl/src/checksrc.pl b/libs/libcurl/src/checksrc.pl
index 965f0bab1d..b2cfa83559 100644
--- a/libs/libcurl/src/checksrc.pl
+++ b/libs/libcurl/src/checksrc.pl
@@ -176,7 +176,7 @@ sub checkwarn {
$file = shift @ARGV;
-while(1) {
+while(defined $file) {
if($file =~ /-D(.*)/) {
$dir = $1;
diff --git a/libs/libcurl/src/config-amigaos.h b/libs/libcurl/src/config-amigaos.h
index 31cfc3afc2..12a87cf298 100644
--- a/libs/libcurl/src/config-amigaos.h
+++ b/libs/libcurl/src/config-amigaos.h
@@ -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
@@ -36,8 +36,6 @@
#define HAVE_INTTYPES_H 1
#define HAVE_IOCTLSOCKET_CAMEL 1
#define HAVE_IOCTLSOCKET_CAMEL_FIONBIO 1
-#define HAVE_LIBCRYPTO 1
-#define HAVE_LIBSSL 1
#define HAVE_LIBZ 1
#define HAVE_LONGLONG 1
#define HAVE_MALLOC_H 1
diff --git a/libs/libcurl/src/config-os400.h b/libs/libcurl/src/config-os400.h
index d14bd33917..a302828e26 100644
--- a/libs/libcurl/src/config-os400.h
+++ b/libs/libcurl/src/config-os400.h
@@ -160,9 +160,6 @@
/* Define if you have the <krb.h> header file. */
#undef HAVE_KRB_H
-/* Define if you have the `crypto' library (-lcrypto). */
-#undef HAVE_LIBCRYPTO
-
/* Define if you have the `nsl' library (-lnsl). */
#undef HAVE_LIBNSL
@@ -175,9 +172,6 @@
/* Define if you have the `socket' library (-lsocket). */
#undef HAVE_LIBSOCKET
-/* Define if you have the `ssl' library (-lssl). */
-#undef HAVE_LIBSSL
-
/* Define if you have GSS API. */
#define HAVE_GSSAPI
diff --git a/libs/libcurl/src/config-plan9.h b/libs/libcurl/src/config-plan9.h
index 70833a51dd..64bfbdea05 100644
--- a/libs/libcurl/src/config-plan9.h
+++ b/libs/libcurl/src/config-plan9.h
@@ -126,7 +126,6 @@
#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
diff --git a/libs/libcurl/src/config-riscos.h b/libs/libcurl/src/config-riscos.h
index 0379524fb3..4af94981c7 100644
--- a/libs/libcurl/src/config-riscos.h
+++ b/libs/libcurl/src/config-riscos.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2013, 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
@@ -164,9 +164,6 @@
/* Define if you have the <krb.h> header file. */
#undef HAVE_KRB_H
-/* Define if you have the `crypto' library (-lcrypto). */
-#undef HAVE_LIBCRYPTO
-
/* Define if you have the `nsl' library (-lnsl). */
#undef HAVE_LIBNSL
@@ -179,9 +176,6 @@
/* Define if you have the `socket' library (-lsocket). */
#undef HAVE_LIBSOCKET
-/* Define if you have the `ssl' library (-lssl). */
-#undef HAVE_LIBSSL
-
/* Define if you have the `ucb' library (-lucb). */
#undef HAVE_LIBUCB
diff --git a/libs/libcurl/src/config-symbian.h b/libs/libcurl/src/config-symbian.h
index b7b93c6f4c..cb2e96d5d1 100644
--- a/libs/libcurl/src/config-symbian.h
+++ b/libs/libcurl/src/config-symbian.h
@@ -315,9 +315,6 @@
/* Define to 1 if you have the <libssh2.h> header file. */
/*#define HAVE_LIBSSH2_H 1*/
-/* Define to 1 if you have the `ssl' library (-lssl). */
-/*#define HAVE_LIBSSL 1*/
-
/* if your compiler supports LL */
#define HAVE_LL 1
diff --git a/libs/libcurl/src/config-tpf.h b/libs/libcurl/src/config-tpf.h
index 778d9833fe..f0c095bb04 100644
--- a/libs/libcurl/src/config-tpf.h
+++ b/libs/libcurl/src/config-tpf.h
@@ -7,7 +7,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
@@ -277,10 +277,6 @@
/* Define to 1 if you have the `socket' library (-lsocket). */
/* #undef HAVE_LIBSOCKET */
-/* Define to 1 if you have the `ssl' library (-lssl). */
-/* #undef HAVE_LIBSSL */
-#define HAVE_LIBSSL 1
-
/* if zlib is available */
/* #undef HAVE_LIBZ */
diff --git a/libs/libcurl/src/config-vxworks.h b/libs/libcurl/src/config-vxworks.h
index 89af3525be..d352578e33 100644
--- a/libs/libcurl/src/config-vxworks.h
+++ b/libs/libcurl/src/config-vxworks.h
@@ -375,9 +375,6 @@
/* Define to 1 if you have the `libssh2_version' function. */
/* #undef HAVE_LIBSSH2_VERSION */
-/* Define to 1 if you have the `ssl' library (-lssl). */
-#define HAVE_LIBSSL 1
-
/* if zlib is available */
#define HAVE_LIBZ 1
diff --git a/libs/libcurl/src/config-win32.h b/libs/libcurl/src/config-win32.h
index 5b028f193f..90c1054765 100644
--- a/libs/libcurl/src/config-win32.h
+++ b/libs/libcurl/src/config-win32.h
@@ -246,10 +246,6 @@
/* Define if you have the socket function. */
#define HAVE_SOCKET 1
-/* Define if libSSH2 is in use */
-#define USE_LIBSSH2 1
-#define HAVE_LIBSSH2_H 1
-
/* Define if you have the strcasecmp function. */
/* #define HAVE_STRCASECMP 1 */
diff --git a/libs/libcurl/src/conncache.c b/libs/libcurl/src/conncache.c
index 2f4dd4bc3e..57d6061fda 100644
--- a/libs/libcurl/src/conncache.c
+++ b/libs/libcurl/src/conncache.c
@@ -143,10 +143,8 @@ int Curl_conncache_init(struct conncache *connc, int size)
rc = Curl_hash_init(&connc->hash, size, Curl_hash_str,
Curl_str_key_compare, free_bundle_hash_entry);
- if(rc) {
- Curl_close(connc->closure_handle);
- connc->closure_handle = NULL;
- }
+ if(rc)
+ Curl_close(&connc->closure_handle);
else
connc->closure_handle->state.conn_cache = connc;
@@ -595,7 +593,7 @@ void Curl_conncache_close_all_connections(struct conncache *connc)
Curl_hostcache_clean(connc->closure_handle,
connc->closure_handle->dns.hostcache);
- Curl_close(connc->closure_handle);
+ Curl_close(&connc->closure_handle);
sigpipe_restore(&pipe_st);
}
}
diff --git a/libs/libcurl/src/connect.c b/libs/libcurl/src/connect.c
index 77196250de..3b88a59623 100644
--- a/libs/libcurl/src/connect.c
+++ b/libs/libcurl/src/connect.c
@@ -665,7 +665,7 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
#endif
#if defined(HAVE_SYS_UN_H) && defined(AF_UNIX)
case AF_UNIX:
- if(salen > sizeof(sa_family_t)) {
+ if(salen > (curl_socklen_t)sizeof(sa_family_t)) {
su = (struct sockaddr_un*)sa;
msnprintf(addr, MAX_IPADR_LEN, "%s", su->sun_path);
}
@@ -976,6 +976,14 @@ CURLcode Curl_is_connected(struct connectdata *conn,
failf(data, "Failed to connect to %s port %ld: %s",
hostname, conn->port,
Curl_strerror(error, buffer, sizeof(buffer)));
+
+#ifdef WSAETIMEDOUT
+ if(WSAETIMEDOUT == data->state.os_errno)
+ result = CURLE_OPERATION_TIMEDOUT;
+#elif defined(ETIMEDOUT)
+ if(ETIMEDOUT == data->state.os_errno)
+ result = CURLE_OPERATION_TIMEDOUT;
+#endif
}
return result;
@@ -1508,6 +1516,11 @@ CURLcode Curl_socket(struct connectdata *conn,
/* no socket, no connection */
return CURLE_COULDNT_CONNECT;
+ if(conn->transport == TRNSPRT_QUIC) {
+ /* QUIC sockets need to be nonblocking */
+ (void)curlx_nonblock(*sockfd, TRUE);
+ }
+
#if defined(ENABLE_IPV6) && defined(HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID)
if(conn->scope_id && (addr->family == AF_INET6)) {
struct sockaddr_in6 * const sa6 = (void *)&addr->sa_addr;
diff --git a/libs/libcurl/src/cookie.c b/libs/libcurl/src/cookie.c
index 53ca40237f..f56bd85a93 100644
--- a/libs/libcurl/src/cookie.c
+++ b/libs/libcurl/src/cookie.c
@@ -1090,6 +1090,8 @@ Curl_cookie_add(struct Curl_easy *data,
*
* If 'newsession' is TRUE, discard all "session cookies" on read from file.
*
+ * Note that 'data' might be called as NULL pointer.
+ *
* Returns NULL on out of memory. Invalid cookies are ignored.
****************************************************************************/
struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
@@ -1160,6 +1162,8 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
}
c->running = TRUE; /* now, we're running */
+ if(data)
+ data->state.cookie_engine = TRUE;
return c;
@@ -1528,28 +1532,28 @@ static int cookie_output(struct CookieInfo *c, const char *dumphere)
if(c->numcookies) {
unsigned int i;
- unsigned int j;
+ size_t nvalid = 0;
struct Cookie **array;
- array = malloc(sizeof(struct Cookie *) * c->numcookies);
+ array = calloc(1, sizeof(struct Cookie *) * c->numcookies);
if(!array) {
if(!use_stdout)
fclose(out);
return 1;
}
- j = 0;
+ /* only sort the cookies with a domain property */
for(i = 0; i < COOKIE_HASH_SIZE; i++) {
for(co = c->cookies[i]; co; co = co->next) {
if(!co->domain)
continue;
- array[j++] = co;
+ array[nvalid++] = co;
}
}
- qsort(array, c->numcookies, sizeof(struct Cookie *), cookie_sort_ct);
+ qsort(array, nvalid, sizeof(struct Cookie *), cookie_sort_ct);
- for(i = 0; i < j; i++) {
+ for(i = 0; i < nvalid; i++) {
char *format_ptr = get_netscape_format(array[i]);
if(format_ptr == NULL) {
fprintf(out, "#\n# Fatal libcurl error\n");
@@ -1613,7 +1617,7 @@ struct curl_slist *Curl_cookie_list(struct Curl_easy *data)
return list;
}
-void Curl_flush_cookies(struct Curl_easy *data, int cleanup)
+void Curl_flush_cookies(struct Curl_easy *data, bool cleanup)
{
if(data->set.str[STRING_COOKIEJAR]) {
if(data->change.cookielist) {
@@ -1642,6 +1646,7 @@ void Curl_flush_cookies(struct Curl_easy *data, int cleanup)
if(cleanup && (!data->share || (data->cookies != data->share->cookies))) {
Curl_cookie_cleanup(data->cookies);
+ data->cookies = NULL;
}
Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
}
diff --git a/libs/libcurl/src/cookie.h b/libs/libcurl/src/cookie.h
index b2730cfb91..b3865e601a 100644
--- a/libs/libcurl/src/cookie.h
+++ b/libs/libcurl/src/cookie.h
@@ -109,7 +109,7 @@ void Curl_cookie_clearsess(struct CookieInfo *cookies);
#define Curl_cookie_cleanup(x) Curl_nop_stmt
#define Curl_flush_cookies(x,y) Curl_nop_stmt
#else
-void Curl_flush_cookies(struct Curl_easy *data, int cleanup);
+void Curl_flush_cookies(struct Curl_easy *data, bool cleanup);
void Curl_cookie_cleanup(struct CookieInfo *);
struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
const char *, struct CookieInfo *, bool);
diff --git a/libs/libcurl/src/curl_config.h.cmake b/libs/libcurl/src/curl_config.h.cmake
index 5458cbaca7..e0793a7ee4 100644
--- a/libs/libcurl/src/curl_config.h.cmake
+++ b/libs/libcurl/src/curl_config.h.cmake
@@ -407,9 +407,6 @@
/* Define to 1 if you have the <libssh2.h> header file. */
#cmakedefine HAVE_LIBSSH2_H 1
-/* Define to 1 if you have the `ssl' library (-lssl). */
-#cmakedefine HAVE_LIBSSL 1
-
/* if zlib is available */
#cmakedefine HAVE_LIBZ 1
diff --git a/libs/libcurl/src/curl_config.h.in b/libs/libcurl/src/curl_config.h.in
index e5abe77b9e..32a87bd293 100644
--- a/libs/libcurl/src/curl_config.h.in
+++ b/libs/libcurl/src/curl_config.h.in
@@ -628,6 +628,9 @@
/* Define to 1 if you have the `SSLv2_client_method' function. */
#undef HAVE_SSLV2_CLIENT_METHOD
+/* Define to 1 if you have the `SSL_get_esni_status' function. */
+#undef HAVE_SSL_GET_ESNI_STATUS
+
/* Define to 1 if you have the <ssl.h> header file. */
#undef HAVE_SSL_H
@@ -952,6 +955,9 @@
/* Define to enable c-ares support */
#undef USE_ARES
+/* if ESNI support is available */
+#undef USE_ESNI
+
/* if GnuTLS is enabled */
#undef USE_GNUTLS
diff --git a/libs/libcurl/src/doh.c b/libs/libcurl/src/doh.c
index 6d1f3303b5..d1795789e5 100644
--- a/libs/libcurl/src/doh.c
+++ b/libs/libcurl/src/doh.c
@@ -74,17 +74,26 @@ static const char *doh_strerror(DOHcode code)
#define UNITTEST static
#endif
+/* @unittest 1655
+ */
UNITTEST DOHcode doh_encode(const char *host,
DNStype dnstype,
unsigned char *dnsp, /* buffer */
size_t len, /* buffer size */
size_t *olen) /* output length */
{
- size_t hostlen = strlen(host);
+ const size_t hostlen = strlen(host);
unsigned char *orig = dnsp;
const char *hostp = host;
- if(len < (12 + hostlen + 4))
+ /* The expected output length does not depend on the number of dots within
+ * the host name. It will always be two more than the length of the host
+ * name, one for the size and one trailing null. In case there are dots,
+ * each dot adds one size but removes the need to store the dot, net zero.
+ */
+ const size_t expected_len = 12 + ( 1 + hostlen + 1) + 4;
+
+ if(len < expected_len)
return DOH_TOO_SMALL_BUFFER;
*dnsp++ = 0; /* 16 bit id */
@@ -126,12 +135,18 @@ UNITTEST DOHcode doh_encode(const char *host,
}
} while(1);
- *dnsp++ = '\0'; /* upper 8 bit TYPE */
- *dnsp++ = (unsigned char)dnstype;
+ /* There are assigned TYPE codes beyond 255: use range [1..65535] */
+ *dnsp++ = (unsigned char)(255 & (dnstype>>8)); /* upper 8 bit TYPE */
+ *dnsp++ = (unsigned char)(255 & dnstype); /* lower 8 bit TYPE */
+
*dnsp++ = '\0'; /* upper 8 bit CLASS */
*dnsp++ = DNS_CLASS_IN; /* IN - "the Internet" */
*olen = dnsp - orig;
+
+ /* verify that our assumption of length is valid, since
+ * this has lead to buffer overflows in this function */
+ DEBUGASSERT(*olen == expected_len);
return DOH_OK;
}
@@ -225,7 +240,10 @@ static CURLcode dohprobe(struct Curl_easy *data,
}
timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
+ if(timeout_ms <= 0) {
+ result = CURLE_OPERATION_TIMEDOUT;
+ goto error;
+ }
/* Curl_open() is the internal version of curl_easy_init() */
result = Curl_open(&doh);
if(!result) {
@@ -246,6 +264,9 @@ static CURLcode dohprobe(struct Curl_easy *data,
#ifndef CURLDEBUG
/* enforce HTTPS if not debug */
ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
+#else
+ /* in debug mode, also allow http */
+ ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS);
#endif
ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms);
if(data->set.verbose)
@@ -325,7 +346,7 @@ static CURLcode dohprobe(struct Curl_easy *data,
error:
free(nurl);
- Curl_close(doh);
+ Curl_close(&doh);
return result;
}
@@ -381,10 +402,8 @@ Curl_addrinfo *Curl_doh(struct connectdata *conn,
error:
curl_slist_free_all(data->req.doh.headers);
data->req.doh.headers = NULL;
- curl_easy_cleanup(data->req.doh.probe[0].easy);
- data->req.doh.probe[0].easy = NULL;
- curl_easy_cleanup(data->req.doh.probe[1].easy);
- data->req.doh.probe[1].easy = NULL;
+ Curl_close(&data->req.doh.probe[0].easy);
+ Curl_close(&data->req.doh.probe[1].easy);
return NULL;
}
@@ -419,8 +438,14 @@ static unsigned short get16bit(unsigned char *doh, int index)
static unsigned int get32bit(unsigned char *doh, int index)
{
- return (doh[index] << 24) | (doh[index + 1] << 16) |
- (doh[index + 2] << 8) | doh[index + 3];
+ /* make clang and gcc optimize this to bswap by incrementing
+ the pointer first. */
+ doh += index;
+
+ /* avoid undefined behaviour by casting to unsigned before shifting
+ 24 bits, possibly into the sign bit. codegen is same, but
+ ub sanitizer won't be upset */
+ return ( (unsigned)doh[0] << 24) | (doh[1] << 16) |(doh[2] << 8) | doh[3];
}
static DOHcode store_a(unsigned char *doh, int index, struct dohentry *d)
@@ -898,17 +923,16 @@ CURLcode Curl_doh_is_resolved(struct connectdata *conn,
struct dohentry de;
/* remove DOH handles from multi handle and close them */
curl_multi_remove_handle(data->multi, data->req.doh.probe[0].easy);
- Curl_close(data->req.doh.probe[0].easy);
+ Curl_close(&data->req.doh.probe[0].easy);
curl_multi_remove_handle(data->multi, data->req.doh.probe[1].easy);
- Curl_close(data->req.doh.probe[1].easy);
-
+ Curl_close(&data->req.doh.probe[1].easy);
/* parse the responses, create the struct and return it! */
init_dohentry(&de);
rc = doh_decode(data->req.doh.probe[0].serverdoh.memory,
data->req.doh.probe[0].serverdoh.size,
data->req.doh.probe[0].dnstype,
&de);
- free(data->req.doh.probe[0].serverdoh.memory);
+ Curl_safefree(data->req.doh.probe[0].serverdoh.memory);
if(rc) {
infof(data, "DOH: %s type %s for %s\n", doh_strerror(rc),
type2name(data->req.doh.probe[0].dnstype),
@@ -918,7 +942,7 @@ CURLcode Curl_doh_is_resolved(struct connectdata *conn,
data->req.doh.probe[1].serverdoh.size,
data->req.doh.probe[1].dnstype,
&de);
- free(data->req.doh.probe[1].serverdoh.memory);
+ Curl_safefree(data->req.doh.probe[1].serverdoh.memory);
if(rc2) {
infof(data, "DOH: %s type %s for %s\n", doh_strerror(rc2),
type2name(data->req.doh.probe[1].dnstype),
diff --git a/libs/libcurl/src/easy.c b/libs/libcurl/src/easy.c
index 0b0016be47..001648d49b 100644
--- a/libs/libcurl/src/easy.c
+++ b/libs/libcurl/src/easy.c
@@ -731,7 +731,7 @@ void curl_easy_cleanup(struct Curl_easy *data)
return;
sigpipe_ignore(data, &pipe_st);
- Curl_close(data);
+ Curl_close(&data);
sigpipe_restore(&pipe_st);
}
@@ -1020,9 +1020,8 @@ CURLcode curl_easy_pause(struct Curl_easy *data, int action)
/* if there's no error and we're not pausing both directions, we want
to have this handle checked soon */
- if(!result &&
- ((newstate&(KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) !=
- (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) ) {
+ if((newstate & (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) !=
+ (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) {
Curl_expire(data, 0, EXPIRE_RUN_NOW); /* get this handle going again */
if(data->multi)
Curl_update_timer(data->multi);
diff --git a/libs/libcurl/src/ftp.c b/libs/libcurl/src/ftp.c
index e807a2acde..8072a33d5d 100644
--- a/libs/libcurl/src/ftp.c
+++ b/libs/libcurl/src/ftp.c
@@ -523,7 +523,7 @@ static CURLcode AllowServerConnect(struct connectdata *conn, bool *connected)
}
else {
/* Add timeout to multi handle and break out of the loop */
- if(!result && *connected == FALSE) {
+ if(*connected == FALSE) {
Curl_expire(data, data->set.accepttimeout > 0 ?
data->set.accepttimeout: DEFAULT_ACCEPT_TIMEOUT, 0);
}
@@ -867,6 +867,10 @@ static CURLcode ftp_state_cwd(struct connectdata *conn)
/* already done and fine */
result = ftp_state_mdtm(conn);
else {
+ /* FTPFILE_NOCWD with full path: expect ftpc->cwddone! */
+ DEBUGASSERT((conn->data->set.ftp_filemethod != FTPFILE_NOCWD) ||
+ !(ftpc->dirdepth && ftpc->dirs[0][0] == '/'));
+
ftpc->count2 = 0; /* count2 counts failed CWDs */
/* count3 is set to allow a MKD to fail once. In the case when first CWD
@@ -874,10 +878,9 @@ static CURLcode ftp_state_cwd(struct connectdata *conn)
dir) this then allows for a second try to CWD to it */
ftpc->count3 = (conn->data->set.ftp_create_missing_dirs == 2)?1:0;
- if((conn->data->set.ftp_filemethod == FTPFILE_NOCWD) && !ftpc->cwdcount)
- /* No CWD necessary */
- result = ftp_state_mdtm(conn);
- else if(conn->bits.reuse && ftpc->entrypath) {
+ if(conn->bits.reuse && ftpc->entrypath &&
+ /* no need to go to entrypath when we have an absolute path */
+ !(ftpc->dirdepth && ftpc->dirs[0][0] == '/')) {
/* This is a re-used connection. Since we change directory to where the
transfer is taking place, we must first get back to the original dir
where we ended up after login: */
@@ -1436,31 +1439,37 @@ static CURLcode ftp_state_list(struct connectdata *conn)
servers either... */
/*
- if FTPFILE_NOCWD was specified, we are currently in
- the user's home directory, so we should add the path
+ if FTPFILE_NOCWD was specified, we should add the path
as argument for the LIST / NLST / or custom command.
Whether the server will support this, is uncertain.
The other ftp_filemethods will CWD into dir/dir/ first and
then just do LIST (in that case: nothing to do here)
*/
- char *cmd, *lstArg, *slashPos;
- const char *inpath = ftp->path;
-
- lstArg = NULL;
- if((data->set.ftp_filemethod == FTPFILE_NOCWD) &&
- inpath && inpath[0] && strchr(inpath, '/')) {
- size_t n = strlen(inpath);
-
- /* Check if path does not end with /, as then we cut off the file part */
- if(inpath[n - 1] != '/') {
- /* chop off the file part if format is dir/dir/file */
- slashPos = strrchr(inpath, '/');
- n = slashPos - inpath;
- }
- result = Curl_urldecode(data, inpath, n, &lstArg, NULL, TRUE);
+ char *lstArg = NULL;
+ char *cmd;
+
+ if((data->set.ftp_filemethod == FTPFILE_NOCWD) && ftp->path) {
+ /* url-decode before evaluation: e.g. paths starting/ending with %2f */
+ const char *slashPos = NULL;
+ char *rawPath = NULL;
+ result = Curl_urldecode(data, ftp->path, 0, &rawPath, NULL, TRUE);
if(result)
return result;
+
+ slashPos = strrchr(rawPath, '/');
+ if(slashPos) {
+ /* chop off the file part if format is dir/file otherwise remove
+ the trailing slash for dir/dir/ except for absolute path / */
+ size_t n = slashPos - rawPath;
+ if(n == 0)
+ ++n;
+
+ lstArg = rawPath;
+ lstArg[n] = '\0';
+ }
+ else
+ free(rawPath);
}
cmd = aprintf("%s%s%s",
@@ -1469,15 +1478,12 @@ static CURLcode ftp_state_list(struct connectdata *conn)
(data->set.ftp_list_only?"NLST":"LIST"),
lstArg? " ": "",
lstArg? lstArg: "");
+ free(lstArg);
- if(!cmd) {
- free(lstArg);
+ if(!cmd)
return CURLE_OUT_OF_MEMORY;
- }
result = Curl_pp_sendf(&conn->proto.ftpc.pp, "%s", cmd);
-
- free(lstArg);
free(cmd);
if(result)
@@ -2242,9 +2248,25 @@ static CURLcode ftp_state_size_resp(struct connectdata *conn,
char *buf = data->state.buffer;
/* get the size from the ascii string: */
- if(ftpcode == 213)
+ if(ftpcode == 213) {
+ /* To allow servers to prepend "rubbish" in the response string, we scan
+ for all the digits at the end of the response and parse only those as a
+ number. */
+ char *start = &buf[4];
+ char *fdigit = strchr(start, '\r');
+ if(fdigit) {
+ do
+ fdigit--;
+ while(ISDIGIT(*fdigit) && (fdigit > start));
+ if(!ISDIGIT(*fdigit))
+ fdigit++;
+ }
+ else
+ fdigit = start;
/* ignores parsing errors, which will make the size remain unknown */
- (void)curlx_strtoofft(buf + 4, NULL, 0, &filesize);
+ (void)curlx_strtoofft(fdigit, NULL, 0, &filesize);
+
+ }
if(instate == FTP_SIZE) {
#ifdef CURL_FTP_HTTPSTYLE_HEAD
@@ -3115,7 +3137,8 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
ssize_t nread;
int ftpcode;
CURLcode result = CURLE_OK;
- char *path = NULL;
+ char *rawPath = NULL;
+ size_t pathLen = 0;
if(!ftp)
return CURLE_OK;
@@ -3153,9 +3176,6 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
break;
}
- /* now store a copy of the directory we are in */
- free(ftpc->prevpath);
-
if(data->state.wildcardmatch) {
if(data->set.chunk_end && ftpc->file) {
Curl_set_in_callback(data, true);
@@ -3166,41 +3186,41 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
}
if(!result)
- /* get the "raw" path */
- result = Curl_urldecode(data, ftp->path, 0, &path, NULL, TRUE);
+ /* get the url-decoded "raw" path */
+ result = Curl_urldecode(data, ftp->path, 0, &rawPath, &pathLen, TRUE);
if(result) {
/* We can limp along anyway (and should try to since we may already be in
* the error path) */
ftpc->ctl_valid = FALSE; /* mark control connection as bad */
connclose(conn, "FTP: out of memory!"); /* mark for connection closure */
+ free(ftpc->prevpath);
ftpc->prevpath = NULL; /* no path remembering */
}
- else {
- size_t flen = ftpc->file?strlen(ftpc->file):0; /* file is "raw" already */
- size_t dlen = strlen(path)-flen;
- if(!ftpc->cwdfail) {
- ftpc->prevmethod = data->set.ftp_filemethod;
- if(dlen && (data->set.ftp_filemethod != FTPFILE_NOCWD)) {
- ftpc->prevpath = path;
- if(flen)
- /* if 'path' is not the whole string */
- ftpc->prevpath[dlen] = 0; /* terminate */
+ else { /* remember working directory for connection reuse */
+ if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/'))
+ free(rawPath); /* full path => no CWDs happened => keep ftpc->prevpath */
+ else {
+ free(ftpc->prevpath);
+
+ if(!ftpc->cwdfail) {
+ if(data->set.ftp_filemethod == FTPFILE_NOCWD)
+ pathLen = 0; /* relative path => working directory is FTP home */
+ else
+ pathLen -= ftpc->file?strlen(ftpc->file):0; /* file is url-decoded */
+
+ rawPath[pathLen] = '\0';
+ ftpc->prevpath = rawPath;
}
else {
- free(path);
- /* we never changed dir */
- ftpc->prevpath = strdup("");
- if(!ftpc->prevpath)
- return CURLE_OUT_OF_MEMORY;
+ free(rawPath);
+ ftpc->prevpath = NULL; /* no path */
}
- if(ftpc->prevpath)
- infof(data, "Remembering we are in dir \"%s\"\n", ftpc->prevpath);
- }
- else {
- ftpc->prevpath = NULL; /* no path */
- free(path);
}
+
+ if(ftpc->prevpath)
+ infof(data, "Remembering we are in dir \"%s\"\n", ftpc->prevpath);
}
+
/* free the dir tree and file parts */
freedirs(ftpc);
@@ -3513,14 +3533,13 @@ static CURLcode ftp_do_more(struct connectdata *conn, int *completep)
/* if we got an error or if we don't wait for a data connection return
immediately */
- if(result || (ftpc->wait_data_conn != TRUE))
+ if(result || !ftpc->wait_data_conn)
return result;
- if(ftpc->wait_data_conn)
- /* if we reach the end of the FTP state machine here, *complete will be
- TRUE but so is ftpc->wait_data_conn, which says we need to wait for
- the data connection and therefore we're not actually complete */
- *completep = 0;
+ /* if we reach the end of the FTP state machine here, *complete will be
+ TRUE but so is ftpc->wait_data_conn, which says we need to wait for the
+ data connection and therefore we're not actually complete */
+ *completep = 0;
}
if(ftp->transfer <= FTPTRANSFER_INFO) {
@@ -3554,13 +3573,8 @@ static CURLcode ftp_do_more(struct connectdata *conn, int *completep)
return result;
result = ftp_multi_statemach(conn, &complete);
- if(ftpc->wait_data_conn)
- /* if we reach the end of the FTP state machine here, *complete will be
- TRUE but so is ftpc->wait_data_conn, which says we need to wait for
- the data connection and therefore we're not actually complete */
- *completep = 0;
- else
- *completep = (int)complete;
+ /* ftpc->wait_data_conn is always false here */
+ *completep = (int)complete;
}
else {
/* download */
@@ -3600,10 +3614,8 @@ static CURLcode ftp_do_more(struct connectdata *conn, int *completep)
return result;
}
- if(!result && (ftp->transfer != FTPTRANSFER_BODY))
- /* no data to transfer. FIX: it feels like a kludge to have this here
- too! */
- Curl_setup_transfer(data, -1, -1, FALSE, -1);
+ /* no data to transfer */
+ Curl_setup_transfer(data, -1, -1, FALSE, -1);
if(!ftpc->wait_data_conn) {
/* no waiting for the data connection so this is now complete */
@@ -4080,186 +4092,142 @@ CURLcode ftp_parse_url_path(struct connectdata *conn)
/* the ftp struct is already inited in ftp_connect() */
struct FTP *ftp = data->req.protop;
struct ftp_conn *ftpc = &conn->proto.ftpc;
- const char *slash_pos; /* position of the first '/' char in curpos */
- const char *path_to_use = ftp->path;
- const char *cur_pos;
- const char *filename = NULL;
-
- cur_pos = path_to_use; /* current position in path. point at the begin of
- next path component */
+ const char *slashPos = NULL;
+ const char *fileName = NULL;
+ CURLcode result = CURLE_OK;
+ char *rawPath = NULL; /* url-decoded "raw" path */
+ size_t pathLen = 0;
ftpc->ctl_valid = FALSE;
ftpc->cwdfail = FALSE;
- switch(data->set.ftp_filemethod) {
- case FTPFILE_NOCWD:
- /* fastest, but less standard-compliant */
-
- /*
- The best time to check whether the path is a file or directory is right
- here. so:
+ /* url-decode ftp path before further evaluation */
+ result = Curl_urldecode(data, ftp->path, 0, &rawPath, &pathLen, TRUE);
+ if(result)
+ return result;
- the first condition in the if() right here, is there just in case
- someone decides to set path to NULL one day
- */
- if(path_to_use[0] &&
- (path_to_use[strlen(path_to_use) - 1] != '/') )
- filename = path_to_use; /* this is a full file path */
- /*
- else {
- ftpc->file is not used anywhere other than for operations on a file.
- In other words, never for directory operations.
- So we can safely leave filename as NULL here and use it as a
- argument in dir/file decisions.
- }
- */
- break;
+ switch(data->set.ftp_filemethod) {
+ case FTPFILE_NOCWD: /* fastest, but less standard-compliant */
- case FTPFILE_SINGLECWD:
- /* get the last slash */
- if(!path_to_use[0]) {
- /* no dir, no file */
- ftpc->dirdepth = 0;
+ if((pathLen > 0) && (rawPath[pathLen - 1] != '/'))
+ fileName = rawPath; /* this is a full file path */
+ /*
+ else: ftpc->file is not used anywhere other than for operations on
+ a file. In other words, never for directory operations.
+ So we can safely leave filename as NULL here and use it as a
+ argument in dir/file decisions.
+ */
break;
- }
- slash_pos = strrchr(cur_pos, '/');
- if(slash_pos || !*cur_pos) {
- size_t dirlen = slash_pos-cur_pos;
- CURLcode result;
- ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0]));
- if(!ftpc->dirs)
- return CURLE_OUT_OF_MEMORY;
+ case FTPFILE_SINGLECWD:
+ slashPos = strrchr(rawPath, '/');
+ if(slashPos) {
+ /* get path before last slash, except for / */
+ size_t dirlen = slashPos - rawPath;
+ if(dirlen == 0)
+ dirlen++;
+
+ ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0]));
+ if(!ftpc->dirs) {
+ free(rawPath);
+ return CURLE_OUT_OF_MEMORY;
+ }
- if(!dirlen)
- dirlen++;
+ ftpc->dirs[0] = calloc(1, dirlen + 1);
+ if(!ftpc->dirs[0]) {
+ free(rawPath);
+ return CURLE_OUT_OF_MEMORY;
+ }
- result = Curl_urldecode(conn->data, slash_pos ? cur_pos : "/",
- slash_pos ? dirlen : 1,
- &ftpc->dirs[0], NULL,
- TRUE);
- if(result) {
- freedirs(ftpc);
- return result;
+ strncpy(ftpc->dirs[0], rawPath, dirlen);
+ ftpc->dirdepth = 1; /* we consider it to be a single dir */
+ fileName = slashPos + 1; /* rest is file name */
}
- ftpc->dirdepth = 1; /* we consider it to be a single dir */
- filename = slash_pos ? slash_pos + 1 : cur_pos; /* rest is file name */
- }
- else
- filename = cur_pos; /* this is a file name only */
- break;
+ else
+ fileName = rawPath; /* file name only (or empty) */
+ break;
- default: /* allow pretty much anything */
- case FTPFILE_MULTICWD:
- ftpc->dirdepth = 0;
- ftpc->diralloc = 5; /* default dir depth to allocate */
- ftpc->dirs = calloc(ftpc->diralloc, sizeof(ftpc->dirs[0]));
- if(!ftpc->dirs)
- return CURLE_OUT_OF_MEMORY;
+ default: /* allow pretty much anything */
+ case FTPFILE_MULTICWD: {
+ /* current position: begin of next path component */
+ const char *curPos = rawPath;
+
+ int dirAlloc = 0; /* number of entries allocated for the 'dirs' array */
+ const char *str = rawPath;
+ for(; *str != 0; ++str)
+ if (*str == '/')
+ ++dirAlloc;
+
+ if(dirAlloc > 0) {
+ ftpc->dirs = calloc(dirAlloc, sizeof(ftpc->dirs[0]));
+ if(!ftpc->dirs) {
+ free(rawPath);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ /* parse the URL path into separate path components */
+ while((slashPos = strchr(curPos, '/')) != NULL) {
+ size_t compLen = slashPos - curPos;
+
+ /* path starts with a slash: add that as a directory */
+ if((compLen == 0) && (ftpc->dirdepth == 0))
+ ++compLen;
- /* we have a special case for listing the root dir only */
- if(!strcmp(path_to_use, "/")) {
- cur_pos++; /* make it point to the zero byte */
- ftpc->dirs[0] = strdup("/");
- ftpc->dirdepth++;
- }
- else {
- /* parse the URL path into separate path components */
- while((slash_pos = strchr(cur_pos, '/')) != NULL) {
- /* 1 or 0 pointer offset to indicate absolute directory */
- ssize_t absolute_dir = ((cur_pos - ftp->path > 0) &&
- (ftpc->dirdepth == 0))?1:0;
-
- /* seek out the next path component */
- if(slash_pos-cur_pos) {
/* we skip empty path components, like "x//y" since the FTP command
CWD requires a parameter and a non-existent parameter a) doesn't
work on many servers and b) has no effect on the others. */
- size_t len = slash_pos - cur_pos + absolute_dir;
- CURLcode result =
- Curl_urldecode(conn->data, cur_pos - absolute_dir, len,
- &ftpc->dirs[ftpc->dirdepth], NULL,
- TRUE);
- if(result) {
- freedirs(ftpc);
- return result;
- }
- }
- else {
- cur_pos = slash_pos + 1; /* jump to the rest of the string */
- if(!ftpc->dirdepth) {
- /* path starts with a slash, add that as a directory */
- ftpc->dirs[ftpc->dirdepth] = strdup("/");
- if(!ftpc->dirs[ftpc->dirdepth++]) { /* run out of memory ... */
- failf(data, "no memory");
- freedirs(ftpc);
+ if(compLen > 0) {
+ char *comp = calloc(1, compLen + 1);
+ if(!comp) {
+ free(rawPath);
return CURLE_OUT_OF_MEMORY;
}
+ strncpy(comp, curPos, compLen);
+ ftpc->dirs[ftpc->dirdepth++] = comp;
}
- continue;
- }
-
- cur_pos = slash_pos + 1; /* jump to the rest of the string */
- if(++ftpc->dirdepth >= ftpc->diralloc) {
- /* enlarge array */
- char **bigger;
- ftpc->diralloc *= 2; /* double the size each time */
- bigger = realloc(ftpc->dirs, ftpc->diralloc * sizeof(ftpc->dirs[0]));
- if(!bigger) {
- freedirs(ftpc);
- return CURLE_OUT_OF_MEMORY;
- }
- ftpc->dirs = bigger;
+ curPos = slashPos + 1;
}
}
+ DEBUGASSERT(ftpc->dirdepth <= dirAlloc);
+ fileName = curPos; /* the rest is the file name (or empty) */
}
- filename = cur_pos; /* the rest is the file name */
break;
} /* switch */
- if(filename && *filename) {
- CURLcode result =
- Curl_urldecode(conn->data, filename, 0, &ftpc->file, NULL, TRUE);
-
- if(result) {
- freedirs(ftpc);
- return result;
- }
- }
+ if(fileName && *fileName)
+ ftpc->file = strdup(fileName);
else
- ftpc->file = NULL; /* instead of point to a zero byte, we make it a NULL
- pointer */
+ ftpc->file = NULL; /* instead of point to a zero byte,
+ we make it a NULL pointer */
if(data->set.upload && !ftpc->file && (ftp->transfer == FTPTRANSFER_BODY)) {
/* We need a file name when uploading. Return error! */
failf(data, "Uploading to a URL without a file name!");
+ free(rawPath);
return CURLE_URL_MALFORMAT;
}
ftpc->cwddone = FALSE; /* default to not done */
- if(ftpc->prevpath) {
- /* prevpath is "raw" so we convert the input path before we compare the
- strings */
- size_t dlen;
- char *path;
- CURLcode result =
- Curl_urldecode(conn->data, ftp->path, 0, &path, &dlen, TRUE);
- if(result) {
- freedirs(ftpc);
- return result;
- }
+ if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/'))
+ ftpc->cwddone = TRUE; /* skip CWD for absolute paths */
+ else { /* newly created FTP connections are already in entry path */
+ const char *oldPath = conn->bits.reuse ? ftpc->prevpath : "";
+ if(oldPath) {
+ size_t n = pathLen;
+ if(data->set.ftp_filemethod == FTPFILE_NOCWD)
+ n = 0; /* CWD to entry for relative paths */
+ else
+ n -= ftpc->file?strlen(ftpc->file):0;
- dlen -= ftpc->file?strlen(ftpc->file):0;
- if((dlen == strlen(ftpc->prevpath)) &&
- !strncmp(path, ftpc->prevpath, dlen) &&
- (ftpc->prevmethod == data->set.ftp_filemethod)) {
- infof(data, "Request has same path as previous transfer\n");
- ftpc->cwddone = TRUE;
+ if((strlen(oldPath) == n) && !strncmp(rawPath, oldPath, n)) {
+ infof(data, "Request has same path as previous transfer\n");
+ ftpc->cwddone = TRUE;
+ }
}
- free(path);
}
+ free(rawPath);
return CURLE_OK;
}
diff --git a/libs/libcurl/src/ftp.h b/libs/libcurl/src/ftp.h
index 828d69a21d..2c88d568c1 100644
--- a/libs/libcurl/src/ftp.h
+++ b/libs/libcurl/src/ftp.h
@@ -121,8 +121,7 @@ struct ftp_conn {
char *entrypath; /* the PWD reply when we logged on */
char **dirs; /* realloc()ed array for path components */
int dirdepth; /* number of entries used in the 'dirs' array */
- int diralloc; /* number of entries allocated for the 'dirs' array */
- char *file; /* decoded file */
+ char *file; /* url-decoded file name (or path) */
bool dont_check; /* Set to TRUE to prevent the final (post-transfer)
file size and 226/250 status check. It should still
read the line, just ignore the result. */
@@ -135,8 +134,7 @@ struct ftp_conn {
bool cwdfail; /* set TRUE if a CWD command fails, as then we must prevent
caching the current directory */
bool wait_data_conn; /* this is set TRUE if data connection is waited */
- char *prevpath; /* conn->path from the previous transfer */
- curl_ftpfile prevmethod; /* ftp method in previous transfer */
+ char *prevpath; /* url-decoded conn->path from the previous transfer */
char transfertype; /* set by ftp_transfertype for use by Curl_client_write()a
and others (A/I or zero) */
int count1; /* general purpose counter for the state machine */
diff --git a/libs/libcurl/src/ftplistparser.c b/libs/libcurl/src/ftplistparser.c
index c4eb43732d..f399a4c272 100644
--- a/libs/libcurl/src/ftplistparser.c
+++ b/libs/libcurl/src/ftplistparser.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
diff --git a/libs/libcurl/src/hostcheck.c b/libs/libcurl/src/hostcheck.c
index 115d24b2e2..9e0db05fac 100644
--- a/libs/libcurl/src/hostcheck.c
+++ b/libs/libcurl/src/hostcheck.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
diff --git a/libs/libcurl/src/hostip.c b/libs/libcurl/src/hostip.c
index bd532a891e..d4e8f9366a 100644
--- a/libs/libcurl/src/hostip.c
+++ b/libs/libcurl/src/hostip.c
@@ -749,7 +749,7 @@ clean_up:
conn->created) / 1000;
/* the alarm period is counted in even number of seconds */
- unsigned long alarm_set = prev_alarm - elapsed_secs;
+ unsigned long alarm_set = (unsigned long)(prev_alarm - elapsed_secs);
if(!alarm_set ||
((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
diff --git a/libs/libcurl/src/http.c b/libs/libcurl/src/http.c
index 28d1fa607d..4631a7f36b 100644
--- a/libs/libcurl/src/http.c
+++ b/libs/libcurl/src/http.c
@@ -450,9 +450,6 @@ static CURLcode http_perhapsrewind(struct connectdata *conn)
/* figure out how much data we are expected to send */
switch(data->set.httpreq) {
case HTTPREQ_POST:
- if(data->state.infilesize != -1)
- expectsend = data->state.infilesize;
- break;
case HTTPREQ_PUT:
if(data->state.infilesize != -1)
expectsend = data->state.infilesize;
@@ -2679,7 +2676,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
struct Cookie *co = NULL; /* no cookies from start */
int count = 0;
- if(data->cookies) {
+ if(data->cookies && data->state.cookie_engine) {
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
co = Curl_cookie_getlist(data->cookies,
conn->allocptr.cookiehost?
@@ -3044,8 +3041,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
failf(data, "Failed sending HTTP request");
else
/* HTTP GET/HEAD download: */
- Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE,
- http->postdata?FIRSTSOCKET:-1);
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1);
}
if(result)
return result;
@@ -4017,7 +4013,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
data->state.resume_from = 0; /* get everything */
}
#if !defined(CURL_DISABLE_COOKIES)
- else if(data->cookies &&
+ else if(data->cookies && data->state.cookie_engine &&
checkprefix("Set-Cookie:", k->p)) {
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE,
CURL_LOCK_ACCESS_SINGLE);
@@ -4058,7 +4054,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
if(result)
return result;
}
- #ifdef USE_SPNEGO
+#ifdef USE_SPNEGO
else if(checkprefix("Persistent-Auth", k->p)) {
struct negotiatedata *negdata = &conn->negotiate;
struct auth *authp = &data->state.authhost;
@@ -4066,14 +4062,15 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
char *persistentauth = Curl_copy_header_value(k->p);
if(!persistentauth)
return CURLE_OUT_OF_MEMORY;
- negdata->noauthpersist = checkprefix("false", persistentauth);
+ negdata->noauthpersist = checkprefix("false", persistentauth)?
+ TRUE:FALSE;
negdata->havenoauthpersist = TRUE;
infof(data, "Negotiate: noauthpersist -> %d, header part: %s",
negdata->noauthpersist, persistentauth);
free(persistentauth);
}
}
- #endif
+#endif
else if((k->httpcode >= 300 && k->httpcode < 400) &&
checkprefix("Location:", k->p) &&
!data->req.location) {
diff --git a/libs/libcurl/src/http.h b/libs/libcurl/src/http.h
index f0ddec7590..a3a2757025 100644
--- a/libs/libcurl/src/http.h
+++ b/libs/libcurl/src/http.h
@@ -83,11 +83,6 @@ CURLcode Curl_http(struct connectdata *conn, bool *done);
CURLcode Curl_http_done(struct connectdata *, CURLcode, bool premature);
CURLcode Curl_http_connect(struct connectdata *conn, bool *done);
-/* The following functions are defined in http_chunks.c */
-void Curl_httpchunk_init(struct connectdata *conn);
-CHUNKcode Curl_httpchunk_read(struct connectdata *conn, char *datap,
- ssize_t length, ssize_t *wrote);
-
/* These functions are in http.c */
CURLcode Curl_http_input_auth(struct connectdata *conn, bool proxy,
const char *auth);
diff --git a/libs/libcurl/src/http2.c b/libs/libcurl/src/http2.c
index 31d2d698ad..6315fc4014 100644
--- a/libs/libcurl/src/http2.c
+++ b/libs/libcurl/src/http2.c
@@ -496,16 +496,14 @@ static struct Curl_easy *duphandle(struct Curl_easy *data)
/* setup the request struct */
struct HTTP *http = calloc(1, sizeof(struct HTTP));
if(!http) {
- (void)Curl_close(second);
- second = NULL;
+ (void)Curl_close(&second);
}
else {
second->req.protop = http;
http->header_recvbuf = Curl_add_buffer_init();
if(!http->header_recvbuf) {
free(http);
- (void)Curl_close(second);
- second = NULL;
+ (void)Curl_close(&second);
}
else {
Curl_http2_setup_req(second);
@@ -547,7 +545,7 @@ static int push_promise(struct Curl_easy *data,
stream = data->req.protop;
if(!stream) {
failf(data, "Internal NULL stream!\n");
- (void)Curl_close(newhandle);
+ (void)Curl_close(&newhandle);
rv = 1;
goto fail;
}
@@ -569,7 +567,7 @@ static int push_promise(struct Curl_easy *data,
/* denied, kill off the new handle again */
http2_stream_free(newhandle->req.protop);
newhandle->req.protop = NULL;
- (void)Curl_close(newhandle);
+ (void)Curl_close(&newhandle);
goto fail;
}
@@ -585,7 +583,7 @@ static int push_promise(struct Curl_easy *data,
infof(data, "failed to add handle to multi\n");
http2_stream_free(newhandle->req.protop);
newhandle->req.protop = NULL;
- Curl_close(newhandle);
+ Curl_close(&newhandle);
rv = 1;
goto fail;
}
@@ -848,6 +846,7 @@ static int on_stream_close(nghttp2_session *session, int32_t stream_id,
stream->closed = TRUE;
httpc = &conn->proto.httpc;
drain_this(data_s, httpc);
+ Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
httpc->error_code = error_code;
/* remove the entry from the hash as the stream is now gone */
@@ -967,7 +966,9 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
if(!check)
/* no memory */
return NGHTTP2_ERR_CALLBACK_FAILURE;
- if(!Curl_strcasecompare(check, (const char *)value)) {
+ if(!Curl_strcasecompare(check, (const char *)value) &&
+ ((conn->remote_port != conn->given->defport) ||
+ !Curl_strcasecompare(conn->host.name, (const char *)value))) {
/* This is push is not for the same authority that was asked for in
* the URL. RFC 7540 section 8.2 says: "A client MUST treat a
* PUSH_PROMISE for which the server is not authoritative as a stream
@@ -1157,7 +1158,7 @@ static void populate_settings(struct connectdata *conn,
nghttp2_settings_entry *iv = httpc->local_settings;
iv[0].settings_id = NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS;
- iv[0].value = 100;
+ iv[0].value = (uint32_t)Curl_multi_max_concurrent_streams(conn->data->multi);
iv[1].settings_id = NGHTTP2_SETTINGS_INITIAL_WINDOW_SIZE;
iv[1].value = HTTP2_HUGE_WINDOW_SIZE;
@@ -1535,6 +1536,7 @@ static int h2_session_send(struct Curl_easy *data,
H2BUGF(infof(data, "Queuing PRIORITY on stream %u (easy %p)\n",
stream->stream_id, data));
+ DEBUGASSERT(stream->stream_id != -1);
rv = nghttp2_submit_priority(h2, NGHTTP2_FLAG_NONE, stream->stream_id,
&pri_spec);
if(rv)
@@ -1659,6 +1661,9 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex,
socket is not read. But it seems that usually streams are
notified with its drain property, and socket is read again
quickly. */
+ if(stream->closed)
+ /* closed overrides paused */
+ return 0;
H2BUGF(infof(data, "stream %x is paused, pause id: %x\n",
stream->stream_id, httpc->pause_stream_id));
*err = CURLE_AGAIN;
@@ -1773,8 +1778,9 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex,
field list. */
#define AUTHORITY_DST_IDX 3
+/* USHRT_MAX is 65535 == 0xffff */
#define HEADER_OVERFLOW(x) \
- (x.namelen > (uint16_t)-1 || x.valuelen > (uint16_t)-1 - x.namelen)
+ (x.namelen > 0xffff || x.valuelen > 0xffff - x.namelen)
/*
* Check header memory for the token "trailers".
@@ -2024,8 +2030,10 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex,
nva[i].namelen = strlen((char *)nva[i].name);
}
else {
- nva[i].name = (unsigned char *)hdbuf;
nva[i].namelen = (size_t)(end - hdbuf);
+ /* Lower case the header name for HTTP/2 */
+ Curl_strntolower((char *)hdbuf, hdbuf, nva[i].namelen);
+ nva[i].name = (unsigned char *)hdbuf;
}
hdbuf = end + 1;
while(*hdbuf == ' ' || *hdbuf == '\t')
@@ -2135,17 +2143,14 @@ static ssize_t http2_send(struct connectdata *conn, int sockindex,
return -1;
}
- if(stream->stream_id != -1) {
- /* If whole HEADERS frame was sent off to the underlying socket,
- the nghttp2 library calls data_source_read_callback. But only
- it found that no data available, so it deferred the DATA
- transmission. Which means that nghttp2_session_want_write()
- returns 0 on http2_perform_getsock(), which results that no
- writable socket check is performed. To workaround this, we
- issue nghttp2_session_resume_data() here to bring back DATA
- transmission from deferred state. */
- nghttp2_session_resume_data(h2, stream->stream_id);
- }
+ /* If whole HEADERS frame was sent off to the underlying socket, the nghttp2
+ library calls data_source_read_callback. But only it found that no data
+ available, so it deferred the DATA transmission. Which means that
+ nghttp2_session_want_write() returns 0 on http2_perform_getsock(), which
+ results that no writable socket check is performed. To workaround this,
+ we issue nghttp2_session_resume_data() here to bring back DATA
+ transmission from deferred state. */
+ nghttp2_session_resume_data(h2, stream->stream_id);
return len;
diff --git a/libs/libcurl/src/http_chunks.c b/libs/libcurl/src/http_chunks.c
index 18dfcb2824..b6ffa41854 100644
--- a/libs/libcurl/src/http_chunks.c
+++ b/libs/libcurl/src/http_chunks.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
@@ -109,7 +109,8 @@ void Curl_httpchunk_init(struct connectdata *conn)
CHUNKcode Curl_httpchunk_read(struct connectdata *conn,
char *datap,
ssize_t datalen,
- ssize_t *wrotep)
+ ssize_t *wrotep,
+ CURLcode *extrap)
{
CURLcode result = CURLE_OK;
struct Curl_easy *data = conn->data;
@@ -125,8 +126,10 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn,
chunk read process, to properly calculate the content length*/
if(data->set.http_te_skip && !k->ignorebody) {
result = Curl_client_write(conn, CLIENTWRITE_BODY, datap, datalen);
- if(result)
- return CHUNKE_WRITE_ERROR;
+ if(result) {
+ *extrap = result;
+ return CHUNKE_PASSTHRU_ERROR;
+ }
}
while(length) {
@@ -197,8 +200,10 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn,
else
result = Curl_client_write(conn, CLIENTWRITE_BODY, datap, piece);
- if(result)
- return CHUNKE_WRITE_ERROR;
+ if(result) {
+ *extrap = result;
+ return CHUNKE_PASSTHRU_ERROR;
+ }
}
*wrote += piece;
@@ -244,8 +249,10 @@ CHUNKcode Curl_httpchunk_read(struct connectdata *conn,
if(!data->set.http_te_skip) {
result = Curl_client_write(conn, CLIENTWRITE_HEADER,
conn->trailer, conn->trlPos);
- if(result)
- return CHUNKE_WRITE_ERROR;
+ if(result) {
+ *extrap = result;
+ return CHUNKE_PASSTHRU_ERROR;
+ }
}
conn->trlPos = 0;
ch->state = CHUNK_TRAILER_CR;
@@ -339,8 +346,9 @@ const char *Curl_chunked_strerror(CHUNKcode code)
return "Illegal or missing hexadecimal sequence";
case CHUNKE_BAD_CHUNK:
return "Malformed encoding found";
- case CHUNKE_WRITE_ERROR:
- return "Write error";
+ case CHUNKE_PASSTHRU_ERROR:
+ DEBUGASSERT(0); /* never used */
+ return "";
case CHUNKE_BAD_ENCODING:
return "Bad content-encoding found";
case CHUNKE_OUT_OF_MEMORY:
diff --git a/libs/libcurl/src/http_chunks.h b/libs/libcurl/src/http_chunks.h
index b969c5590a..8f4a33c8e4 100644
--- a/libs/libcurl/src/http_chunks.h
+++ b/libs/libcurl/src/http_chunks.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
@@ -21,6 +21,9 @@
* KIND, either express or implied.
*
***************************************************************************/
+
+struct connectdata;
+
/*
* The longest possible hexadecimal number we support in a chunked transfer.
* Weird enough, RFC2616 doesn't set a maximum size! Since we use strtoul()
@@ -71,9 +74,9 @@ typedef enum {
CHUNKE_TOO_LONG_HEX = 1,
CHUNKE_ILLEGAL_HEX,
CHUNKE_BAD_CHUNK,
- CHUNKE_WRITE_ERROR,
CHUNKE_BAD_ENCODING,
CHUNKE_OUT_OF_MEMORY,
+ CHUNKE_PASSTHRU_ERROR, /* Curl_httpchunk_read() returns a CURLcode to use */
CHUNKE_LAST
} CHUNKcode;
@@ -87,4 +90,10 @@ struct Curl_chunker {
size_t dataleft; /* untouched data amount at the end of the last buffer */
};
+/* The following functions are defined in http_chunks.c */
+void Curl_httpchunk_init(struct connectdata *conn);
+CHUNKcode Curl_httpchunk_read(struct connectdata *conn, char *datap,
+ ssize_t length, ssize_t *wrote,
+ CURLcode *passthru);
+
#endif /* HEADER_CURL_HTTP_CHUNKS_H */
diff --git a/libs/libcurl/src/http_proxy.c b/libs/libcurl/src/http_proxy.c
index ba67b861bd..f095455a51 100644
--- a/libs/libcurl/src/http_proxy.c
+++ b/libs/libcurl/src/http_proxy.c
@@ -327,7 +327,7 @@ static CURLcode CONNECT(struct connectdata *conn,
{ /* READING RESPONSE PHASE */
int error = SELECT_OK;
- while(s->keepon && !error) {
+ while(s->keepon) {
ssize_t gotbytes;
/* make sure we have space to read more data */
@@ -384,11 +384,12 @@ static CURLcode CONNECT(struct connectdata *conn,
/* chunked-encoded body, so we need to do the chunked dance
properly to know when the end of the body is reached */
CHUNKcode r;
+ CURLcode extra;
ssize_t tookcareof = 0;
/* now parse the chunked piece of data so that we can
properly tell when the stream ends */
- r = Curl_httpchunk_read(conn, s->ptr, 1, &tookcareof);
+ r = Curl_httpchunk_read(conn, s->ptr, 1, &tookcareof, &extra);
if(r == CHUNKE_STOP) {
/* we're done reading chunks! */
infof(data, "chunk reading DONE\n");
@@ -455,6 +456,7 @@ static CURLcode CONNECT(struct connectdata *conn,
}
else if(s->chunked_encoding) {
CHUNKcode r;
+ CURLcode extra;
infof(data, "Ignore chunked response-body\n");
@@ -472,7 +474,8 @@ static CURLcode CONNECT(struct connectdata *conn,
/* now parse the chunked piece of data so that we can
properly tell when the stream ends */
- r = Curl_httpchunk_read(conn, s->line_start + 1, 1, &gotbytes);
+ r = Curl_httpchunk_read(conn, s->line_start + 1, 1, &gotbytes,
+ &extra);
if(r == CHUNKE_STOP) {
/* we're done reading chunks! */
infof(data, "chunk reading DONE\n");
diff --git a/libs/libcurl/src/imap.c b/libs/libcurl/src/imap.c
index ff9b629470..66172bddcc 100644
--- a/libs/libcurl/src/imap.c
+++ b/libs/libcurl/src/imap.c
@@ -1306,6 +1306,7 @@ static CURLcode imap_statemach_act(struct connectdata *conn)
break;
case IMAP_LIST:
+ case IMAP_SEARCH:
result = imap_state_listsearch_resp(conn, imapcode, imapc->state);
break;
@@ -1329,10 +1330,6 @@ static CURLcode imap_statemach_act(struct connectdata *conn)
result = imap_state_append_final_resp(conn, imapcode, imapc->state);
break;
- case IMAP_SEARCH:
- result = imap_state_listsearch_resp(conn, imapcode, imapc->state);
- break;
-
case IMAP_LOGOUT:
/* fallthrough, just stop! */
default:
diff --git a/libs/libcurl/src/ldap.c b/libs/libcurl/src/ldap.c
index fd31faa3e7..af3d61c57e 100644
--- a/libs/libcurl/src/ldap.c
+++ b/libs/libcurl/src/ldap.c
@@ -119,6 +119,12 @@ static void _ldap_free_urldesc(LDAPURLDesc *ludp);
#define LDAP_TRACE(x) Curl_nop_stmt
#endif
+#if defined(USE_WIN32_LDAP) && defined(ldap_err2string)
+/* Use ansi error strings in UNICODE builds */
+#undef ldap_err2string
+#define ldap_err2string ldap_err2stringA
+#endif
+
static CURLcode Curl_ldap(struct connectdata *conn, bool *done);
@@ -838,10 +844,10 @@ static bool split_str(char *str, char ***out, size_t *count)
static int _ldap_url_parse2(const struct connectdata *conn, LDAPURLDesc *ludp)
{
int rc = LDAP_SUCCESS;
- char *path;
- char *query;
char *p;
- char *q;
+ char *path;
+ char *q = NULL;
+ char *query = NULL;
size_t i;
if(!conn->data ||
@@ -859,11 +865,13 @@ static int _ldap_url_parse2(const struct connectdata *conn, LDAPURLDesc *ludp)
if(!path)
return LDAP_NO_MEMORY;
- /* Duplicate the query */
- q = query = strdup(conn->data->state.up.query);
- if(!query) {
- free(path);
- return LDAP_NO_MEMORY;
+ /* Duplicate the query if present */
+ if(conn->data->state.up.query) {
+ q = query = strdup(conn->data->state.up.query);
+ if(!query) {
+ free(path);
+ return LDAP_NO_MEMORY;
+ }
}
/* Parse the DN (Distinguished Name) */
diff --git a/libs/libcurl/src/libcurl.plist b/libs/libcurl/src/libcurl.plist
index 9db8aa3107..55c2ed494d 100644
--- a/libs/libcurl/src/libcurl.plist
+++ b/libs/libcurl/src/libcurl.plist
@@ -15,7 +15,7 @@
<string>se.haxx.curl.libcurl</string>
<key>CFBundleVersion</key>
- <string>7.66.0</string>
+ <string>7.67.0</string>
<key>CFBundleName</key>
<string>libcurl</string>
@@ -27,9 +27,9 @@
<string>????</string>
<key>CFBundleShortVersionString</key>
- <string>libcurl 7.66.0</string>
+ <string>libcurl 7.67.0</string>
<key>CFBundleGetInfoString</key>
- <string>libcurl.plist 7.66.0</string>
+ <string>libcurl.plist 7.67.0</string>
</dict>
</plist>
diff --git a/libs/libcurl/src/mime.c b/libs/libcurl/src/mime.c
index 2135f72c25..081e51e508 100644
--- a/libs/libcurl/src/mime.c
+++ b/libs/libcurl/src/mime.c
@@ -1135,6 +1135,8 @@ CURLcode Curl_mime_duppart(curl_mimepart *dst, const curl_mimepart *src)
const curl_mimepart *s;
CURLcode res = CURLE_OK;
+ DEBUGASSERT(dst);
+
/* Duplicate content. */
switch(src->kind) {
case MIMEKIND_NONE:
@@ -1184,20 +1186,18 @@ CURLcode Curl_mime_duppart(curl_mimepart *dst, const curl_mimepart *src)
}
}
- /* Duplicate other fields. */
- if(dst != NULL)
+ if(!res) {
+ /* Duplicate other fields. */
dst->encoder = src->encoder;
- else
- res = CURLE_WRITE_ERROR;
- if(!res)
res = curl_mime_type(dst, src->mimetype);
+ }
if(!res)
res = curl_mime_name(dst, src->name);
if(!res)
res = curl_mime_filename(dst, src->filename);
/* If an error occurred, rollback. */
- if(res && dst)
+ if(res)
Curl_mime_cleanpart(dst);
return res;
@@ -1901,4 +1901,11 @@ CURLcode curl_mime_headers(curl_mimepart *part,
return CURLE_NOT_BUILT_IN;
}
+CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...)
+{
+ (void)slp;
+ (void)fmt;
+ return CURLE_NOT_BUILT_IN;
+}
+
#endif /* if disabled */
diff --git a/libs/libcurl/src/mime.h b/libs/libcurl/src/mime.h
index 4c9a5fb71f..3241fdc1f7 100644
--- a/libs/libcurl/src/mime.h
+++ b/libs/libcurl/src/mime.h
@@ -127,7 +127,9 @@ struct curl_mimepart_s {
mime_encoder_state encstate; /* Data encoder state. */
};
-#if (!defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_MIME)) || \
+CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...);
+
+#if (!defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_MIME)) || \
!defined(CURL_DISABLE_SMTP) || !defined(CURL_DISABLE_IMAP)
/* Prototypes. */
@@ -144,7 +146,6 @@ curl_off_t Curl_mime_size(curl_mimepart *part);
size_t Curl_mime_read(char *buffer, size_t size, size_t nitems,
void *instream);
CURLcode Curl_mime_rewind(curl_mimepart *part);
-CURLcode Curl_mime_add_header(struct curl_slist **slp, const char *fmt, ...);
const char *Curl_mime_contenttype(const char *filename);
#else
@@ -157,7 +158,6 @@ const char *Curl_mime_contenttype(const char *filename);
#define Curl_mime_size(x) (curl_off_t) -1
#define Curl_mime_read NULL
#define Curl_mime_rewind(x) ((void)x, CURLE_NOT_BUILT_IN)
-#define Curl_mime_add_header(x,y,...) CURLE_NOT_BUILT_IN
#endif
diff --git a/libs/libcurl/src/multi.c b/libs/libcurl/src/multi.c
index 2e91e4ff35..6dfe8842e7 100644
--- a/libs/libcurl/src/multi.c
+++ b/libs/libcurl/src/multi.c
@@ -363,7 +363,7 @@ struct Curl_multi *Curl_multi_handle(int hashsize, /* socket hash */
Curl_llist_init(&multi->msglist, NULL);
Curl_llist_init(&multi->pending, NULL);
- multi->multiplexing = CURLPIPE_MULTIPLEX;
+ multi->multiplexing = TRUE;
/* -1 means it not set by user, use the default value */
multi->maxconnects = -1;
@@ -2772,6 +2772,16 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi,
break;
case CURLMOPT_PIPELINING_SERVER_BL:
break;
+ case CURLMOPT_MAX_CONCURRENT_STREAMS:
+ {
+ long streams = va_arg(param, long);
+ if(streams < 1)
+ streams = 100;
+ multi->max_concurrent_streams =
+ (streams > (long)INITIAL_MAX_CONCURRENT_STREAMS)?
+ (long)INITIAL_MAX_CONCURRENT_STREAMS : streams;
+ }
+ break;
default:
res = CURLM_UNKNOWN_OPTION;
break;
@@ -3210,3 +3220,9 @@ void Curl_multi_dump(struct Curl_multi *multi)
}
}
#endif
+
+size_t Curl_multi_max_concurrent_streams(struct Curl_multi *multi)
+{
+ return multi ? ((size_t)multi->max_concurrent_streams ?
+ (size_t)multi->max_concurrent_streams : 100) : 0;
+}
diff --git a/libs/libcurl/src/multihandle.h b/libs/libcurl/src/multihandle.h
index 279379ae0f..b65bd96386 100644
--- a/libs/libcurl/src/multihandle.h
+++ b/libs/libcurl/src/multihandle.h
@@ -133,6 +133,7 @@ struct Curl_multi {
struct curltime timer_lastcall; /* the fixed time for the timeout for the
previous callback */
bool in_callback; /* true while executing a callback */
+ long max_concurrent_streams; /* max concurrent streams client to support */
};
#endif /* HEADER_CURL_MULTIHANDLE_H */
diff --git a/libs/libcurl/src/multiif.h b/libs/libcurl/src/multiif.h
index 0755a7cd24..75025232c4 100644
--- a/libs/libcurl/src/multiif.h
+++ b/libs/libcurl/src/multiif.h
@@ -89,4 +89,10 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi,
struct Curl_easy *data,
struct connectdata *conn);
+
+/* Return the value of the CURLMOPT_MAX_CONCURRENT_STREAMS option
+ * If not specified or 0, default would be 100
+ */
+size_t Curl_multi_max_concurrent_streams(struct Curl_multi *multi);
+
#endif /* HEADER_CURL_MULTIIF_H */
diff --git a/libs/libcurl/src/netrc.c b/libs/libcurl/src/netrc.c
index c74065e813..1c9da31993 100644
--- a/libs/libcurl/src/netrc.c
+++ b/libs/libcurl/src/netrc.c
@@ -88,7 +88,7 @@ static int parsenetrc(const char *host,
if(tok && *tok == '#')
/* treat an initial hash as a comment line */
continue;
- while(!done && tok) {
+ while(tok) {
if((login && *login) && (password && *password)) {
done = TRUE;
diff --git a/libs/libcurl/src/non-ascii.c b/libs/libcurl/src/non-ascii.c
index 42beaec45d..a48e67db4a 100644
--- a/libs/libcurl/src/non-ascii.c
+++ b/libs/libcurl/src/non-ascii.c
@@ -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
diff --git a/libs/libcurl/src/parsedate.c b/libs/libcurl/src/parsedate.c
index 7ae5eb8cde..f4b18d091a 100644
--- a/libs/libcurl/src/parsedate.c
+++ b/libs/libcurl/src/parsedate.c
@@ -100,16 +100,20 @@ static int parsedate(const char *date, time_t *output);
#define PARSEDATE_LATER 1
#define PARSEDATE_SOONER 2
-#ifndef CURL_DISABLE_PARSEDATE
-
+#if !defined(CURL_DISABLE_PARSEDATE) || !defined(CURL_DISABLE_FTP) || \
+ !defined(CURL_DISABLE_FILE)
+/* These names are also used by FTP and FILE code */
const char * const Curl_wkday[] =
{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
-static const char * const weekday[] =
-{ "Monday", "Tuesday", "Wednesday", "Thursday",
- "Friday", "Saturday", "Sunday" };
const char * const Curl_month[]=
{ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+#endif
+
+#ifndef CURL_DISABLE_PARSEDATE
+static const char * const weekday[] =
+{ "Monday", "Tuesday", "Wednesday", "Thursday",
+ "Friday", "Saturday", "Sunday" };
struct tzinfo {
char name[5];
diff --git a/libs/libcurl/src/security.c b/libs/libcurl/src/security.c
index c5e4e135df..fbfa707413 100644
--- a/libs/libcurl/src/security.c
+++ b/libs/libcurl/src/security.c
@@ -236,7 +236,7 @@ static ssize_t sec_recv(struct connectdata *conn, int sockindex,
/* Handle clear text response. */
if(conn->sec_complete == 0 || conn->data_prot == PROT_CLEAR)
- return read(fd, buffer, len);
+ return sread(fd, buffer, len);
if(conn->in_buffer.eof_flag) {
conn->in_buffer.eof_flag = 0;
diff --git a/libs/libcurl/src/setopt.c b/libs/libcurl/src/setopt.c
index 8909035a97..64c29e3336 100644
--- a/libs/libcurl/src/setopt.c
+++ b/libs/libcurl/src/setopt.c
@@ -315,7 +315,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
* Parse the $HOME/.netrc file
*/
arg = va_arg(param, long);
- if((arg < CURL_NETRC_IGNORED) || (arg > CURL_NETRC_REQUIRED))
+ if((arg < CURL_NETRC_IGNORED) || (arg >= CURL_NETRC_LAST))
return CURLE_BAD_FUNCTION_ARGUMENT;
data->set.use_netrc = (enum CURL_NETRC_OPTION)arg;
break;
@@ -342,7 +342,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
* curl/curl.h header file.
*/
arg = va_arg(param, long);
- if((arg < CURL_TIMECOND_NONE) || (arg > CURL_TIMECOND_LASTMOD))
+ if((arg < CURL_TIMECOND_NONE) || (arg >= CURL_TIMECOND_LAST))
return CURLE_BAD_FUNCTION_ARGUMENT;
data->set.timecondition = (curl_TimeCond)arg;
break;
@@ -752,7 +752,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
}
else if(strcasecompare(argptr, "FLUSH")) {
/* flush cookies to file, takes care of the locking */
- Curl_flush_cookies(data, 0);
+ Curl_flush_cookies(data, FALSE);
}
else if(strcasecompare(argptr, "RELOAD")) {
/* reload cookies from file */
@@ -818,7 +818,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
if(arg >= CURL_HTTP_VERSION_2)
return CURLE_UNSUPPORTED_PROTOCOL;
#else
- if(arg > CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE)
+ if(arg >= CURL_HTTP_VERSION_LAST)
return CURLE_UNSUPPORTED_PROTOCOL;
if(arg == CURL_HTTP_VERSION_NONE)
arg = CURL_HTTP_VERSION_2TLS;
@@ -1109,7 +1109,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
* How do access files over FTP.
*/
arg = va_arg(param, long);
- if((arg < CURLFTPMETHOD_DEFAULT) || (arg > CURLFTPMETHOD_SINGLECWD))
+ if((arg < CURLFTPMETHOD_DEFAULT) || (arg >= CURLFTPMETHOD_LAST))
return CURLE_BAD_FUNCTION_ARGUMENT;
data->set.ftp_filemethod = (curl_ftpfile)arg;
break;
@@ -1136,7 +1136,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
case CURLOPT_FTP_SSL_CCC:
arg = va_arg(param, long);
- if((arg < CURLFTPSSL_CCC_NONE) || (arg > CURLFTPSSL_CCC_ACTIVE))
+ if((arg < CURLFTPSSL_CCC_NONE) || (arg >= CURLFTPSSL_CCC_LAST))
return CURLE_BAD_FUNCTION_ARGUMENT;
data->set.ftp_ccc = (curl_ftpccc)arg;
break;
@@ -1164,7 +1164,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
* Set a specific auth for FTP-SSL transfers.
*/
arg = va_arg(param, long);
- if((arg < CURLFTPAUTH_DEFAULT) || (arg > CURLFTPAUTH_TLS))
+ if((arg < CURLFTPAUTH_DEFAULT) || (arg >= CURLFTPAUTH_LAST))
return CURLE_BAD_FUNCTION_ARGUMENT;
data->set.ftpsslauth = (curl_ftpauth)arg;
break;
@@ -2123,7 +2123,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
* Make transfers attempt to use SSL/TLS.
*/
arg = va_arg(param, long);
- if((arg < CURLUSESSL_NONE) || (arg > CURLUSESSL_ALL))
+ if((arg < CURLUSESSL_NONE) || (arg >= CURLUSESSL_LAST))
return CURLE_BAD_FUNCTION_ARGUMENT;
data->set.use_ssl = (curl_usessl)arg;
break;
@@ -2500,7 +2500,7 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
case CURLOPT_RTSP_SERVER_CSEQ:
/* Same as the above, but for server-initiated requests */
- data->state.rtsp_next_client_CSeq = va_arg(param, long);
+ data->state.rtsp_next_server_CSeq = va_arg(param, long);
break;
case CURLOPT_INTERLEAVEDATA:
@@ -2725,7 +2725,8 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
result = Curl_setstropt(&data->set.str[STRING_ALTSVC], argptr);
if(result)
return result;
- (void)Curl_altsvc_load(data->asi, argptr);
+ if(argptr)
+ (void)Curl_altsvc_load(data->asi, argptr);
break;
case CURLOPT_ALTSVC_CTRL:
if(!data->asi) {
diff --git a/libs/libcurl/src/setup-os400.h b/libs/libcurl/src/setup-os400.h
index a3c2a7bdc9..629fd94c46 100644
--- a/libs/libcurl/src/setup-os400.h
+++ b/libs/libcurl/src/setup-os400.h
@@ -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
@@ -206,11 +206,15 @@ extern int Curl_os400_sendto(int sd, char *buffer, int buflen, int flags,
struct sockaddr * dstaddr, int addrlen);
extern int Curl_os400_recvfrom(int sd, char *buffer, int buflen, int flags,
struct sockaddr *fromaddr, int *addrlen);
+extern int Curl_os400_getpeername(int sd, struct sockaddr *addr, int *addrlen);
+extern int Curl_os400_getsockname(int sd, struct sockaddr *addr, int *addrlen);
#define connect Curl_os400_connect
#define bind Curl_os400_bind
#define sendto Curl_os400_sendto
#define recvfrom Curl_os400_recvfrom
+#define getpeername Curl_os400_getpeername
+#define getsockname Curl_os400_getsockname
#ifdef HAVE_LIBZ
#define zlibVersion Curl_os400_zlibVersion
diff --git a/libs/libcurl/src/smb.c b/libs/libcurl/src/smb.c
index f66c05ca42..12f99257fc 100644
--- a/libs/libcurl/src/smb.c
+++ b/libs/libcurl/src/smb.c
@@ -682,7 +682,8 @@ static CURLcode smb_connection_state(struct connectdata *conn, bool *done)
switch(smbc->state) {
case SMB_NEGOTIATE:
- if(h->status || smbc->got < sizeof(*nrsp) + sizeof(smbc->challenge) - 1) {
+ if((smbc->got < sizeof(*nrsp) + sizeof(smbc->challenge) - 1) ||
+ h->status) {
connclose(conn, "SMB: negotiation failed");
return CURLE_COULDNT_CONNECT;
}
diff --git a/libs/libcurl/src/socketpair.c b/libs/libcurl/src/socketpair.c
new file mode 100644
index 0000000000..1f0e2e4a4f
--- /dev/null
+++ b/libs/libcurl/src/socketpair.c
@@ -0,0 +1,118 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 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"
+#include "socketpair.h"
+
+#ifndef HAVE_SOCKETPAIR
+#ifdef WIN32
+/*
+ * This is a socketpair() implementation for Windows.
+ */
+#include <string.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <windows.h>
+#include <io.h>
+#else
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h> /* IPPROTO_TCP */
+#endif
+#ifndef INADDR_LOOPBACK
+#define INADDR_LOOPBACK 0x7f000001
+#endif /* !INADDR_LOOPBACK */
+#endif /* !WIN32 */
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+int Curl_socketpair(int domain, int type, int protocol,
+ curl_socket_t socks[2])
+{
+ union {
+ struct sockaddr_in inaddr;
+ struct sockaddr addr;
+ } a;
+ curl_socket_t listener;
+ curl_socklen_t addrlen = sizeof(a.inaddr);
+ int reuse = 1;
+ char data[2][12];
+ ssize_t dlen;
+ (void)domain;
+ (void)type;
+ (void)protocol;
+
+ listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if(listener == CURL_SOCKET_BAD)
+ return -1;
+
+ memset(&a, 0, sizeof(a));
+ a.inaddr.sin_family = AF_INET;
+ a.inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ a.inaddr.sin_port = 0;
+
+ socks[0] = socks[1] = CURL_SOCKET_BAD;
+
+ if(setsockopt(listener, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&reuse, (curl_socklen_t)sizeof(reuse)) == -1)
+ goto error;
+ if(bind(listener, &a.addr, sizeof(a.inaddr)) == -1)
+ goto error;
+ if(getsockname(listener, &a.addr, &addrlen) == -1)
+ goto error;
+ if(listen(listener, 1) == -1)
+ goto error;
+ socks[0] = socket(AF_INET, SOCK_STREAM, 0);
+ if(socks[0] == CURL_SOCKET_BAD)
+ goto error;
+ if(connect(socks[0], &a.addr, sizeof(a.inaddr)) == -1)
+ goto error;
+ socks[1] = accept(listener, NULL, NULL);
+ if(socks[1] == CURL_SOCKET_BAD)
+ goto error;
+
+ /* verify that nothing else connected */
+ msnprintf(data[0], sizeof(data[0]), "%p", socks);
+ dlen = strlen(data[0]);
+ if(swrite(socks[0], data[0], dlen) != dlen)
+ goto error;
+ if(sread(socks[1], data[1], sizeof(data[1])) != dlen)
+ goto error;
+ if(memcmp(data[0], data[1], dlen))
+ goto error;
+
+ sclose(listener);
+ return 0;
+
+ error:
+ sclose(listener);
+ sclose(socks[0]);
+ sclose(socks[1]);
+ return -1;
+}
+
+#endif /* ! HAVE_SOCKETPAIR */
diff --git a/libs/libcurl/src/socketpair.h b/libs/libcurl/src/socketpair.h
new file mode 100644
index 0000000000..be9fb24f97
--- /dev/null
+++ b/libs/libcurl/src/socketpair.h
@@ -0,0 +1,36 @@
+#ifndef HEADER_CURL_SOCKETPAIR_H
+#define HEADER_CURL_SOCKETPAIR_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 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"
+#ifndef HAVE_SOCKETPAIR
+int Curl_socketpair(int domain, int type, int protocol,
+ curl_socket_t socks[2]);
+#else
+#define Curl_socketpair(a,b,c,d) socketpair(a,b,c,d)
+#endif
+
+/* Defined here to allow specific build configs to disable it completely */
+#define USE_SOCKETPAIR 1
+
+#endif /* HEADER_CURL_SOCKETPAIR_H */
diff --git a/libs/libcurl/src/socks.c b/libs/libcurl/src/socks.c
index d8fcc3bbba..6ae98184d1 100644
--- a/libs/libcurl/src/socks.c
+++ b/libs/libcurl/src/socks.c
@@ -38,7 +38,9 @@
#include "timeval.h"
#include "socks.h"
-/* The last #include file should be: */
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
#include "memdebug.h"
/*
@@ -372,8 +374,9 @@ CURLcode Curl_SOCKS5(const char *proxy_user,
o REP Reply field:
o X'00' succeeded
*/
-
- unsigned char socksreq[600]; /* room for large user/pw (255 max each) */
+#define REQUEST_BUFSIZE 600 /* room for large user/pw (255 max each) */
+ unsigned char socksreq[REQUEST_BUFSIZE];
+ char dest[REQUEST_BUFSIZE] = "unknown"; /* printable hostname:port */
int idx;
ssize_t actualread;
ssize_t written;
@@ -605,6 +608,8 @@ CURLcode Curl_SOCKS5(const char *proxy_user,
socksreq[len++] = (char) hostname_len; /* address length */
memcpy(&socksreq[len], hostname, hostname_len); /* address str w/o NULL */
len += hostname_len;
+ msnprintf(dest, sizeof(dest), "%s:%d", hostname, remote_port);
+ infof(data, "SOCKS5 connect to %s (remotely resolved)\n", dest);
}
else {
struct Curl_dns_entry *dns;
@@ -628,8 +633,13 @@ CURLcode Curl_SOCKS5(const char *proxy_user,
if(dns)
hp = dns->addr;
if(hp) {
- char buf[64];
- Curl_printable_address(hp, buf, sizeof(buf));
+ if(Curl_printable_address(hp, dest, sizeof(dest))) {
+ size_t destlen = strlen(dest);
+ msnprintf(dest + destlen, sizeof(dest) - destlen, ":%d", remote_port);
+ }
+ else {
+ strcpy(dest, "unknown");
+ }
if(hp->ai_family == AF_INET) {
int i;
@@ -641,7 +651,7 @@ CURLcode Curl_SOCKS5(const char *proxy_user,
socksreq[len++] = ((unsigned char *)&saddr_in->sin_addr.s_addr)[i];
}
- infof(data, "SOCKS5 connect to IPv4 %s (locally resolved)\n", buf);
+ infof(data, "SOCKS5 connect to IPv4 %s (locally resolved)\n", dest);
}
#ifdef ENABLE_IPV6
else if(hp->ai_family == AF_INET6) {
@@ -655,13 +665,13 @@ CURLcode Curl_SOCKS5(const char *proxy_user,
((unsigned char *)&saddr_in6->sin6_addr.s6_addr)[i];
}
- infof(data, "SOCKS5 connect to IPv6 %s (locally resolved)\n", buf);
+ infof(data, "SOCKS5 connect to IPv6 %s (locally resolved)\n", dest);
}
#endif
else {
hp = NULL; /* fail! */
- failf(data, "SOCKS5 connection to %s not supported\n", buf);
+ failf(data, "SOCKS5 connection to %s not supported\n", dest);
}
Curl_resolv_unlock(data, dns); /* not used anymore from now on */
@@ -756,42 +766,8 @@ CURLcode Curl_SOCKS5(const char *proxy_user,
#endif
if(socksreq[1] != 0) { /* Anything besides 0 is an error */
- if(socksreq[3] == 1) {
- failf(data,
- "Can't complete SOCKS5 connection to %d.%d.%d.%d:%d. (%d)",
- (unsigned char)socksreq[4], (unsigned char)socksreq[5],
- (unsigned char)socksreq[6], (unsigned char)socksreq[7],
- (((unsigned char)socksreq[8] << 8) |
- (unsigned char)socksreq[9]),
- (unsigned char)socksreq[1]);
- }
- else if(socksreq[3] == 3) {
- unsigned char port_upper = (unsigned char)socksreq[len - 2];
- socksreq[len - 2] = 0;
- failf(data,
- "Can't complete SOCKS5 connection to %s:%d. (%d)",
- (char *)&socksreq[5],
- ((port_upper << 8) |
- (unsigned char)socksreq[len - 1]),
- (unsigned char)socksreq[1]);
- socksreq[len - 2] = port_upper;
- }
- else if(socksreq[3] == 4) {
- failf(data,
- "Can't complete SOCKS5 connection to %02x%02x:%02x%02x:"
- "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%d. (%d)",
- (unsigned char)socksreq[4], (unsigned char)socksreq[5],
- (unsigned char)socksreq[6], (unsigned char)socksreq[7],
- (unsigned char)socksreq[8], (unsigned char)socksreq[9],
- (unsigned char)socksreq[10], (unsigned char)socksreq[11],
- (unsigned char)socksreq[12], (unsigned char)socksreq[13],
- (unsigned char)socksreq[14], (unsigned char)socksreq[15],
- (unsigned char)socksreq[16], (unsigned char)socksreq[17],
- (unsigned char)socksreq[18], (unsigned char)socksreq[19],
- (((unsigned char)socksreq[20] << 8) |
- (unsigned char)socksreq[21]),
- (unsigned char)socksreq[1]);
- }
+ failf(data, "Can't complete SOCKS5 connection to %s. (%d)",
+ dest, (unsigned char)socksreq[1]);
return CURLE_COULDNT_CONNECT;
}
infof(data, "SOCKS5 request granted.\n");
diff --git a/libs/libcurl/src/strcase.c b/libs/libcurl/src/strcase.c
index 24bcca9327..a894712254 100644
--- a/libs/libcurl/src/strcase.c
+++ b/libs/libcurl/src/strcase.c
@@ -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
@@ -93,6 +93,75 @@ char Curl_raw_toupper(char in)
return in;
}
+
+/* Portable, consistent tolower (remember EBCDIC). Do not use tolower() because
+ its behavior is altered by the current locale. */
+char Curl_raw_tolower(char in)
+{
+#if !defined(CURL_DOES_CONVERSIONS)
+ if(in >= 'A' && in <= 'Z')
+ return (char)('a' + in - 'A');
+#else
+ switch(in) {
+ case 'A':
+ return 'a';
+ case 'B':
+ return 'b';
+ case 'C':
+ return 'c';
+ case 'D':
+ return 'd';
+ case 'E':
+ return 'e';
+ case 'F':
+ return 'f';
+ case 'G':
+ return 'g';
+ case 'H':
+ return 'h';
+ case 'I':
+ return 'i';
+ case 'J':
+ return 'j';
+ case 'K':
+ return 'k';
+ case 'L':
+ return 'l';
+ case 'M':
+ return 'm';
+ case 'N':
+ return 'n';
+ case 'O':
+ return 'o';
+ case 'P':
+ return 'p';
+ case 'Q':
+ return 'q';
+ case 'R':
+ return 'r';
+ case 'S':
+ return 's';
+ case 'T':
+ return 't';
+ case 'U':
+ return 'u';
+ case 'V':
+ return 'v';
+ case 'W':
+ return 'w';
+ case 'X':
+ return 'x';
+ case 'Y':
+ return 'y';
+ case 'Z':
+ return 'z';
+ }
+#endif
+
+ return in;
+}
+
+
/*
* Curl_strcasecompare() is for doing "raw" case insensitive strings. This is
* meant to be locale independent and only compare strings we know are safe
@@ -165,6 +234,21 @@ void Curl_strntoupper(char *dest, const char *src, size_t n)
} while(*src++ && --n);
}
+/* Copy a lower case version of the string from src to dest. The
+ * strings may overlap. No more than n characters of the string are copied
+ * (including any NUL) and the destination string will NOT be
+ * NUL-terminated if that limit is reached.
+ */
+void Curl_strntolower(char *dest, const char *src, size_t n)
+{
+ if(n < 1)
+ return;
+
+ do {
+ *dest++ = Curl_raw_tolower(*src);
+ } while(*src++ && --n);
+}
+
/* --- public functions --- */
int curl_strequal(const char *first, const char *second)
diff --git a/libs/libcurl/src/strcase.h b/libs/libcurl/src/strcase.h
index 6fee3840e6..baa768b2b2 100644
--- a/libs/libcurl/src/strcase.h
+++ b/libs/libcurl/src/strcase.h
@@ -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
@@ -40,11 +40,13 @@ int Curl_safe_strcasecompare(const char *first, const char *second);
int Curl_strncasecompare(const char *first, const char *second, size_t max);
char Curl_raw_toupper(char in);
+char Curl_raw_tolower(char in);
/* checkprefix() is a shorter version of the above, used when the first
argument is zero-byte terminated */
#define checkprefix(a,b) curl_strnequal(a,b,strlen(a))
void Curl_strntoupper(char *dest, const char *src, size_t n);
+void Curl_strntolower(char *dest, const char *src, size_t n);
#endif /* HEADER_CURL_STRCASE_H */
diff --git a/libs/libcurl/src/transfer.c b/libs/libcurl/src/transfer.c
index ef0d806384..d0d4aeb500 100644
--- a/libs/libcurl/src/transfer.c
+++ b/libs/libcurl/src/transfer.c
@@ -776,14 +776,14 @@ static CURLcode readwrite_data(struct Curl_easy *data,
* and writes away the data. The returned 'nread' holds the number
* of actual data it wrote to the client.
*/
-
+ CURLcode extra;
CHUNKcode res =
- Curl_httpchunk_read(conn, k->str, nread, &nread);
+ Curl_httpchunk_read(conn, k->str, nread, &nread, &extra);
if(CHUNKE_OK < res) {
- if(CHUNKE_WRITE_ERROR == res) {
- failf(data, "Failed writing data");
- return CURLE_WRITE_ERROR;
+ if(CHUNKE_PASSTHRU_ERROR == res) {
+ failf(data, "Failed reading the chunked-encoded stream");
+ return extra;
}
failf(data, "%s in chunked-encoding", Curl_chunked_strerror(res));
return CURLE_RECV_ERROR;
@@ -1510,6 +1510,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
}
}
#endif
+ Curl_http2_init_state(&data->state);
}
return result;
@@ -1591,7 +1592,8 @@ CURLcode Curl_follow(struct Curl_easy *data,
DEBUGASSERT(data->state.uh);
uc = curl_url_set(data->state.uh, CURLUPART_URL, newurl,
- (type == FOLLOW_FAKE) ? CURLU_NON_SUPPORT_SCHEME : 0);
+ (type == FOLLOW_FAKE) ? CURLU_NON_SUPPORT_SCHEME :
+ ((type == FOLLOW_REDIR) ? CURLU_URLENCODE : 0) );
if(uc) {
if(type != FOLLOW_FAKE)
return Curl_uc_to_curlcode(uc);
diff --git a/libs/libcurl/src/url.c b/libs/libcurl/src/url.c
index b7cf7bedd3..8285474fd7 100644
--- a/libs/libcurl/src/url.c
+++ b/libs/libcurl/src/url.c
@@ -317,13 +317,17 @@ static void up_free(struct Curl_easy *data)
* when curl_easy_perform() is invoked.
*/
-CURLcode Curl_close(struct Curl_easy *data)
+CURLcode Curl_close(struct Curl_easy **datap)
{
struct Curl_multi *m;
+ struct Curl_easy *data;
- if(!data)
+ if(!datap || !*datap)
return CURLE_OK;
+ data = *datap;
+ *datap = NULL;
+
Curl_expire_clear(data); /* shut off timers */
m = data->multi;
@@ -374,7 +378,7 @@ CURLcode Curl_close(struct Curl_easy *data)
Curl_safefree(data->state.buffer);
Curl_safefree(data->state.headerbuff);
Curl_safefree(data->state.ulbuf);
- Curl_flush_cookies(data, 1);
+ Curl_flush_cookies(data, TRUE);
#ifdef USE_ALTSVC
Curl_altsvc_save(data->asi, data->set.str[STRING_ALTSVC]);
Curl_altsvc_cleanup(data->asi);
@@ -399,6 +403,10 @@ CURLcode Curl_close(struct Curl_easy *data)
Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
}
+ free(data->req.doh.probe[0].serverdoh.memory);
+ free(data->req.doh.probe[1].serverdoh.memory);
+ curl_slist_free_all(data->req.doh.headers);
+
/* destruct wildcard structures if it is needed */
Curl_wildcard_dtor(&data->wildcard);
Curl_freeset(data);
@@ -612,8 +620,6 @@ CURLcode Curl_open(struct Curl_easy **curl)
data->progress.flags |= PGRS_HIDE;
data->state.current_speed = -1; /* init to negative == impossible */
-
- Curl_http2_init_state(&data->state);
}
}
@@ -1041,7 +1047,7 @@ ConnectionExists(struct Curl_easy *data,
/* We can't multiplex if we don't know anything about the server */
if(canmultiplex) {
if(bundle->multiuse == BUNDLE_UNKNOWN) {
- if((bundle->multiuse == BUNDLE_UNKNOWN) && data->set.pipewait) {
+ if(data->set.pipewait) {
infof(data, "Server doesn't support multiplex yet, wait\n");
*waitpipe = TRUE;
Curl_conncache_unlock(data);
@@ -1277,8 +1283,14 @@ ConnectionExists(struct Curl_easy *data,
partway through a handshake!) */
if(wantNTLMhttp) {
if(strcmp(needle->user, check->user) ||
- strcmp(needle->passwd, check->passwd))
+ strcmp(needle->passwd, check->passwd)) {
+
+ /* we prefer a credential match, but this is at least a connection
+ that can be reused and "upgraded" to NTLM */
+ if(check->http_ntlm_state == NTLMSTATE_NONE)
+ chosen = check;
continue;
+ }
}
else if(check->http_ntlm_state != NTLMSTATE_NONE) {
/* Connection is using NTLM auth but we don't want NTLM */
@@ -1787,6 +1799,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
}
if(!data->set.uh) {
+ char *newurl;
uc = curl_url_set(uh, CURLUPART_URL, data->change.url,
CURLU_GUESS_SCHEME |
CURLU_NON_SUPPORT_SCHEME |
@@ -1797,6 +1810,15 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
DEBUGF(infof(data, "curl_url_set rejected %s\n", data->change.url));
return Curl_uc_to_curlcode(uc);
}
+
+ /* after it was parsed, get the generated normalized version */
+ uc = curl_url_get(uh, CURLUPART_URL, &newurl, 0);
+ if(uc)
+ return Curl_uc_to_curlcode(uc);
+ if(data->change.url_alloc)
+ free(data->change.url);
+ data->change.url = newurl;
+ data->change.url_alloc = TRUE;
}
uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0);
@@ -1863,11 +1885,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
(void)curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query, 0);
hostname = data->state.up.hostname;
- if(!hostname)
- /* this is for file:// transfers, get a dummy made */
- hostname = (char *)"";
-
- if(hostname[0] == '[') {
+ if(hostname && hostname[0] == '[') {
/* This looks like an IPv6 address literal. See if there is an address
scope. */
size_t hlen;
@@ -1881,7 +1899,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
}
/* make sure the connect struct gets its own copy of the host name */
- conn->host.rawalloc = strdup(hostname);
+ conn->host.rawalloc = strdup(hostname ? hostname : "");
if(!conn->host.rawalloc)
return CURLE_OUT_OF_MEMORY;
conn->host.name = conn->host.rawalloc;
@@ -1969,6 +1987,8 @@ void Curl_free_request_state(struct Curl_easy *data)
{
Curl_safefree(data->req.protop);
Curl_safefree(data->req.newurl);
+ Curl_close(&data->req.doh.probe[0].easy);
+ Curl_close(&data->req.doh.probe[1].easy);
}
@@ -2754,13 +2774,6 @@ static CURLcode set_login(struct connectdata *conn)
result = CURLE_OUT_OF_MEMORY;
}
- /* if there's a user without password, consider password blank */
- if(conn->user && !conn->passwd) {
- conn->passwd = strdup("");
- if(!conn->passwd)
- result = CURLE_OUT_OF_MEMORY;
- }
-
return result;
}
@@ -3519,6 +3532,10 @@ static CURLcode create_conn(struct Curl_easy *data,
data->set.str[STRING_SSL_CIPHER13_LIST_ORIG];
data->set.proxy_ssl.primary.cipher_list13 =
data->set.str[STRING_SSL_CIPHER13_LIST_PROXY];
+ data->set.ssl.primary.pinned_key =
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG];
+ data->set.proxy_ssl.primary.pinned_key =
+ data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY];
data->set.ssl.CRLfile = data->set.str[STRING_SSL_CRLFILE_ORIG];
data->set.proxy_ssl.CRLfile = data->set.str[STRING_SSL_CRLFILE_PROXY];
@@ -3815,7 +3832,9 @@ CURLcode Curl_setup_conn(struct connectdata *conn,
}
else {
Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */
- Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */
+ if(conn->ssl[FIRSTSOCKET].use ||
+ (conn->handler->protocol & PROTO_FAMILY_SSH))
+ Curl_pgrsTime(data, TIMER_APPCONNECT); /* we're connected already */
conn->bits.tcpconnect[FIRSTSOCKET] = TRUE;
*protocol_done = TRUE;
Curl_updateconninfo(conn, conn->sock[FIRSTSOCKET]);
diff --git a/libs/libcurl/src/url.h b/libs/libcurl/src/url.h
index f4d611adda..053fbdffc2 100644
--- a/libs/libcurl/src/url.h
+++ b/libs/libcurl/src/url.h
@@ -49,7 +49,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data);
void Curl_freeset(struct Curl_easy * data);
CURLcode Curl_uc_to_curlcode(CURLUcode uc);
-CURLcode Curl_close(struct Curl_easy *data); /* opposite of curl_open() */
+CURLcode Curl_close(struct Curl_easy **datap); /* 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);
diff --git a/libs/libcurl/src/urlapi.c b/libs/libcurl/src/urlapi.c
index a0ee331dab..fa514bce53 100644
--- a/libs/libcurl/src/urlapi.c
+++ b/libs/libcurl/src/urlapi.c
@@ -64,6 +64,7 @@ struct Curl_URL {
char *fragment;
char *scratch; /* temporary scratch area */
+ char *temppath; /* temporary path pointer */
long portnum; /* the numerical version */
};
@@ -82,6 +83,7 @@ static void free_urlhandle(struct Curl_URL *u)
free(u->query);
free(u->fragment);
free(u->scratch);
+ free(u->temppath);
}
/* move the full contents of one handle onto another and
@@ -351,7 +353,7 @@ static char *concat_url(const char *base, const char *relurl)
else {
/* We got a new absolute path for this server */
- if((relurl[0] == '/') && (relurl[1] == '/')) {
+ if(relurl[1] == '/') {
/* the new URL starts with //, just keep the protocol part from the
original one */
*protsep = 0;
@@ -596,8 +598,12 @@ static CURLUcode hostname_check(struct Curl_URL *u, char *hostname)
size_t hlen = strlen(hostname);
if(hostname[0] == '[') {
+#ifdef ENABLE_IPV6
char dest[16]; /* fits a binary IPv6 address */
+#endif
const char *l = "0123456789abcdefABCDEF:.";
+ if(hlen < 5) /* '[::1]' is the shortest possible valid string */
+ return CURLUE_MALFORMED_INPUT;
hostname++;
hlen -= 2;
@@ -784,6 +790,7 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
if(junkscan(schemep))
return CURLUE_MALFORMED_INPUT;
+
}
else {
/* no scheme! */
@@ -804,11 +811,14 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
p++;
len = p - hostp;
- if(!len)
- return CURLUE_MALFORMED_INPUT;
-
- memcpy(hostname, hostp, len);
- hostname[len] = 0;
+ if(len) {
+ memcpy(hostname, hostp, len);
+ hostname[len] = 0;
+ }
+ else {
+ if(!(flags & CURLU_NO_AUTHORITY))
+ return CURLUE_MALFORMED_INPUT;
+ }
if((flags & CURLU_GUESS_SCHEME) && !schemep) {
/* legacy curl-style guess based on host name */
@@ -843,35 +853,60 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
if(junkscan(path))
return CURLUE_MALFORMED_INPUT;
- query = strchr(path, '?');
- if(query)
- *query++ = 0;
+ if((flags & CURLU_URLENCODE) && path[0]) {
+ /* worst case output length is 3x the original! */
+ char *newp = malloc(strlen(path) * 3);
+ if(!newp)
+ return CURLUE_OUT_OF_MEMORY;
+ path_alloced = TRUE;
+ strcpy_url(newp, path, TRUE); /* consider it relative */
+ u->temppath = path = newp;
+ }
- fragment = strchr(query?query:path, '#');
- if(fragment)
+ fragment = strchr(path, '#');
+ if(fragment) {
*fragment++ = 0;
+ if(fragment[0]) {
+ u->fragment = strdup(fragment);
+ if(!u->fragment)
+ return CURLUE_OUT_OF_MEMORY;
+ }
+ }
+
+ query = strchr(path, '?');
+ if(query) {
+ *query++ = 0;
+ /* done even if the query part is a blank string */
+ u->query = strdup(query);
+ if(!u->query)
+ return CURLUE_OUT_OF_MEMORY;
+ }
if(!path[0])
- /* if there's no path set, unset */
+ /* if there's no path left set, unset */
path = NULL;
- else if(!(flags & CURLU_PATH_AS_IS)) {
- /* sanitise paths and remove ../ and ./ sequences according to RFC3986 */
- char *newp = Curl_dedotdotify(path);
- if(!newp)
- return CURLUE_OUT_OF_MEMORY;
+ else {
+ if(!(flags & CURLU_PATH_AS_IS)) {
+ /* remove ../ and ./ sequences according to RFC3986 */
+ char *newp = Curl_dedotdotify(path);
+ if(!newp)
+ return CURLUE_OUT_OF_MEMORY;
- if(strcmp(newp, path)) {
- /* if we got a new version */
- path = newp;
- path_alloced = TRUE;
+ if(strcmp(newp, path)) {
+ /* if we got a new version */
+ if(path_alloced)
+ Curl_safefree(u->temppath);
+ u->temppath = path = newp;
+ path_alloced = TRUE;
+ }
+ else
+ free(newp);
}
- else
- free(newp);
- }
- if(path) {
+
u->path = path_alloced?path:strdup(path);
if(!u->path)
return CURLUE_OUT_OF_MEMORY;
+ u->temppath = NULL; /* used now */
}
if(hostname) {
@@ -889,28 +924,22 @@ static CURLUcode seturl(const char *url, CURLU *u, unsigned int flags)
if(result)
return result;
- result = hostname_check(u, hostname);
- if(result)
- return result;
+ if(0 == strlen(hostname) && (flags & CURLU_NO_AUTHORITY)) {
+ /* Skip hostname check, it's allowed to be empty. */
+ }
+ else {
+ result = hostname_check(u, hostname);
+ if(result)
+ return result;
+ }
u->host = strdup(hostname);
if(!u->host)
return CURLUE_OUT_OF_MEMORY;
}
- if(query) {
- u->query = strdup(query);
- if(!u->query)
- return CURLUE_OUT_OF_MEMORY;
- }
- if(fragment && fragment[0]) {
- u->fragment = strdup(fragment);
- if(!u->fragment)
- return CURLUE_OUT_OF_MEMORY;
- }
-
- free(u->scratch);
- u->scratch = NULL;
+ Curl_safefree(u->scratch);
+ Curl_safefree(u->temppath);
return CURLUE_OK;
}
@@ -1075,24 +1104,23 @@ CURLUcode curl_url_get(CURLU *u, CURLUPart what,
else
return CURLUE_NO_SCHEME;
- if(scheme) {
- h = Curl_builtin_scheme(scheme);
- if(!port && (flags & CURLU_DEFAULT_PORT)) {
- /* there's no stored port number, but asked to deliver
- a default one for the scheme */
- if(h) {
- msnprintf(portbuf, sizeof(portbuf), "%ld", h->defport);
- port = portbuf;
- }
- }
- else if(port) {
- /* there is a stored port number, but asked to inhibit if it matches
- the default one for the scheme */
- if(h && (h->defport == u->portnum) &&
- (flags & CURLU_NO_DEFAULT_PORT))
- port = NULL;
+ h = Curl_builtin_scheme(scheme);
+ if(!port && (flags & CURLU_DEFAULT_PORT)) {
+ /* there's no stored port number, but asked to deliver
+ a default one for the scheme */
+ if(h) {
+ msnprintf(portbuf, sizeof(portbuf), "%ld", h->defport);
+ port = portbuf;
}
}
+ else if(port) {
+ /* there is a stored port number, but asked to inhibit if it matches
+ the default one for the scheme */
+ if(h && (h->defport == u->portnum) &&
+ (flags & CURLU_NO_DEFAULT_PORT))
+ port = NULL;
+ }
+
if(h && !(h->flags & PROTOPT_URLOPTIONS))
options = NULL;
@@ -1340,7 +1368,8 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
default:
return CURLUE_UNKNOWN_PART;
}
- if(storep) {
+ DEBUGASSERT(storep);
+ {
const char *newp = part;
size_t nalloc = strlen(part);
@@ -1432,9 +1461,14 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
}
if(what == CURLUPART_HOST) {
- if(hostname_check(u, (char *)newp)) {
- free((char *)newp);
- return CURLUE_MALFORMED_INPUT;
+ if(0 == strlen(newp) && (flags & CURLU_NO_AUTHORITY)) {
+ /* Skip hostname check, it's allowed to be empty. */
+ }
+ else {
+ if(hostname_check(u, (char *)newp)) {
+ free((char *)newp);
+ return CURLUE_MALFORMED_INPUT;
+ }
}
}
diff --git a/libs/libcurl/src/urldata.h b/libs/libcurl/src/urldata.h
index acc1fd1b9d..f9365b2e68 100644
--- a/libs/libcurl/src/urldata.h
+++ b/libs/libcurl/src/urldata.h
@@ -68,6 +68,7 @@
#define PROTO_FAMILY_POP3 (CURLPROTO_POP3|CURLPROTO_POP3S)
#define PROTO_FAMILY_SMB (CURLPROTO_SMB|CURLPROTO_SMBS)
#define PROTO_FAMILY_SMTP (CURLPROTO_SMTP|CURLPROTO_SMTPS)
+#define PROTO_FAMILY_SSH (CURLPROTO_SCP|CURLPROTO_SFTP)
#define DEFAULT_CONNCACHE_SIZE 5
@@ -158,7 +159,13 @@ typedef ssize_t (Curl_recv)(struct connectdata *conn, /* connection data */
((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER))
/* the type we use for storing a single boolean bit */
+#ifdef _MSC_VER
+typedef bool bit;
+#define BIT(x) bool x
+#else
typedef unsigned int bit;
+#define BIT(x) bit x:1
+#endif
#ifdef HAVE_GSSAPI
/* Types needed for krb5-ftp connections */
@@ -166,7 +173,7 @@ struct krb5buffer {
void *data;
size_t size;
size_t index;
- bit eof_flag:1;
+ BIT(eof_flag);
};
enum protection_level {
@@ -209,7 +216,7 @@ struct ssl_connect_data {
#if defined(USE_SSL)
struct ssl_backend_data *backend;
#endif
- bit use:1;
+ BIT(use);
};
struct ssl_primary_config {
@@ -222,10 +229,11 @@ struct ssl_primary_config {
char *egdsocket; /* path to file containing the EGD daemon socket */
char *cipher_list; /* list of ciphers to use */
char *cipher_list13; /* list of TLS 1.3 cipher suites to use */
- bit verifypeer:1; /* set TRUE if this is desired */
- bit verifyhost:1; /* set TRUE if CN/SAN must match hostname */
- bit verifystatus:1; /* set TRUE if certificate status must be checked */
- bit sessionid:1; /* cache session IDs or not */
+ char *pinned_key;
+ BIT(verifypeer); /* set TRUE if this is desired */
+ BIT(verifyhost); /* set TRUE if CN/SAN must match hostname */
+ BIT(verifystatus); /* set TRUE if certificate status must be checked */
+ BIT(sessionid); /* cache session IDs or not */
};
struct ssl_config_data {
@@ -245,10 +253,10 @@ struct ssl_config_data {
char *password; /* TLS password (for, e.g., SRP) */
enum CURL_TLSAUTH authtype; /* TLS authentication type (default SRP) */
#endif
- bit certinfo:1; /* gather lots of certificate info */
- bit falsestart:1;
- bit enable_beast:1; /* allow this flaw for interoperability's sake*/
- bit no_revoke:1; /* disable SSL certificate revocation checks */
+ BIT(certinfo); /* gather lots of certificate info */
+ BIT(falsestart);
+ BIT(enable_beast); /* allow this flaw for interoperability's sake*/
+ BIT(no_revoke); /* disable SSL certificate revocation checks */
};
struct ssl_general_config {
@@ -291,8 +299,8 @@ struct digestdata {
char *qop;
char *algorithm;
int nc; /* nounce count */
- bit stale:1; /* set true for re-negotiation */
- bit userhash:1;
+ BIT(stale); /* set true for re-negotiation */
+ BIT(userhash);
#endif
};
@@ -386,10 +394,10 @@ struct negotiatedata {
size_t output_token_length;
#endif
#endif
- bool noauthpersist;
- bool havenoauthpersist;
- bool havenegdata;
- bool havemultiplerequests;
+ BIT(noauthpersist);
+ BIT(havenoauthpersist);
+ BIT(havenegdata);
+ BIT(havemultiplerequests);
};
#endif
@@ -403,64 +411,64 @@ struct ConnectBits {
is complete */
bool tcpconnect[2]; /* the TCP layer (or similar) is connected, this is set
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"
- that overrides the port in the URL (remote port) */
- bit proxy:1; /* if set, this transfer is done through a proxy - any type */
- bit httpproxy:1; /* if set, this transfer is done through a http proxy */
- bit socksproxy:1; /* if set, this transfer is done through a socks proxy */
- bit user_passwd:1; /* do we use user+password for this connection? */
- bit proxy_user_passwd:1; /* user+password for the proxy? */
- bit ipv6_ip:1; /* we communicate with a remote site specified with pure IPv6
- IP address */
- bit ipv6:1; /* we communicate with a site using an IPv6 address */
- bit do_more:1; /* this is set TRUE if the ->curl_do_more() function is
- supposed to be called, after ->curl_do() */
- bit protoconnstart:1;/* the protocol layer has STARTED its operation after
- the TCP layer connect */
- bit retry:1; /* this connection is about to get closed and then
- re-attempted at another connection. */
- bit tunnel_proxy:1; /* if CONNECT is used to "tunnel" through the proxy.
- This is implicit when SSL-protocols are used through
- proxies, but can also be enabled explicitly by
- apps */
- bit authneg:1; /* TRUE when the auth phase has started, which means
- that we are creating a request with an auth header,
- but it is not the final request in the auth
- negotiation. */
- bit rewindaftersend:1;/* TRUE when the sending couldn't be stopped even
- though it will be discarded. When the whole send
- operation is done, we must call the data rewind
- callback. */
+ BIT(close); /* if set, we close the connection after this request */
+ BIT(reuse); /* if set, this is a re-used connection */
+ BIT(altused); /* this is an alt-svc "redirect" */
+ BIT(conn_to_host); /* if set, this connection has a "connect to host"
+ that overrides the host in the URL */
+ BIT(conn_to_port); /* if set, this connection has a "connect to port"
+ that overrides the port in the URL (remote port) */
+ BIT(proxy); /* if set, this transfer is done through a proxy - any type */
+ BIT(httpproxy); /* if set, this transfer is done through a http proxy */
+ BIT(socksproxy); /* if set, this transfer is done through a socks proxy */
+ BIT(user_passwd); /* do we use user+password for this connection? */
+ BIT(proxy_user_passwd); /* user+password for the proxy? */
+ BIT(ipv6_ip); /* we communicate with a remote site specified with pure IPv6
+ IP address */
+ BIT(ipv6); /* we communicate with a site using an IPv6 address */
+ BIT(do_more); /* this is set TRUE if the ->curl_do_more() function is
+ supposed to be called, after ->curl_do() */
+ BIT(protoconnstart);/* the protocol layer has STARTED its operation after
+ the TCP layer connect */
+ BIT(retry); /* this connection is about to get closed and then
+ re-attempted at another connection. */
+ BIT(tunnel_proxy); /* if CONNECT is used to "tunnel" through the proxy.
+ This is implicit when SSL-protocols are used through
+ proxies, but can also be enabled explicitly by
+ apps */
+ BIT(authneg); /* TRUE when the auth phase has started, which means
+ that we are creating a request with an auth header,
+ but it is not the final request in the auth
+ negotiation. */
+ BIT(rewindaftersend);/* TRUE when the sending couldn't be stopped even
+ though it will be discarded. When the whole send
+ operation is done, we must call the data rewind
+ callback. */
#ifndef CURL_DISABLE_FTP
- bit ftp_use_epsv:1; /* As set with CURLOPT_FTP_USE_EPSV, but if we find out
- EPSV doesn't work we disable it for the forthcoming
- requests */
- bit ftp_use_eprt:1; /* As set with CURLOPT_FTP_USE_EPRT, but if we find out
- EPRT doesn't work we disable it for the forthcoming
- requests */
- bit ftp_use_data_ssl:1; /* Enabled SSL for the data connection */
+ BIT(ftp_use_epsv); /* As set with CURLOPT_FTP_USE_EPSV, but if we find out
+ EPSV doesn't work we disable it for the forthcoming
+ requests */
+ BIT(ftp_use_eprt); /* As set with CURLOPT_FTP_USE_EPRT, but if we find out
+ EPRT doesn't work we disable it for the forthcoming
+ requests */
+ BIT(ftp_use_data_ssl); /* Enabled SSL for the data connection */
#endif
- bit netrc:1; /* name+password provided by netrc */
- bit userpwd_in_url:1; /* name+password found in url */
- bit stream_was_rewound:1; /* The stream was rewound after a request read
- past the end of its response byte boundary */
- bit proxy_connect_closed:1; /* TRUE if a proxy disconnected the connection
- in a CONNECT request with auth, so that
- libcurl should reconnect and continue. */
- bit bound:1; /* set true if bind() has already been done on this socket/
- connection */
- bit type_set:1; /* type= was used in the URL */
- bit multiplex:1; /* connection is multiplexed */
- bit tcp_fastopen:1; /* use TCP Fast Open */
- bit tls_enable_npn:1; /* TLS NPN extension? */
- bit tls_enable_alpn:1; /* TLS ALPN extension? */
- bit socksproxy_connecting:1; /* connecting through a socks proxy */
- bit connect_only:1;
+ BIT(netrc); /* name+password provided by netrc */
+ BIT(userpwd_in_url); /* name+password found in url */
+ BIT(stream_was_rewound); /* The stream was rewound after a request read
+ past the end of its response byte boundary */
+ BIT(proxy_connect_closed); /* TRUE if a proxy disconnected the connection
+ in a CONNECT request with auth, so that
+ libcurl should reconnect and continue. */
+ BIT(bound); /* set true if bind() has already been done on this socket/
+ connection */
+ BIT(type_set); /* type= was used in the URL */
+ BIT(multiplex); /* connection is multiplexed */
+ BIT(tcp_fastopen); /* use TCP Fast Open */
+ BIT(tls_enable_npn); /* TLS NPN extension? */
+ BIT(tls_enable_alpn); /* TLS ALPN extension? */
+ BIT(socksproxy_connecting); /* connecting through a socks proxy */
+ BIT(connect_only);
};
struct hostname {
@@ -493,7 +501,7 @@ struct Curl_async {
struct Curl_dns_entry *dns;
int status; /* if done is TRUE, this is the status from the callback */
void *os_specific; /* 'struct thread_data' for Windows */
- bit done:1; /* set TRUE when the lookup is complete */
+ BIT(done); /* set TRUE when the lookup is complete */
};
#define FIRSTSOCKET 0
@@ -614,20 +622,20 @@ struct SingleRequest {
#ifndef CURL_DISABLE_DOH
struct dohdata doh; /* DoH specific data for this request */
#endif
- bit header:1; /* incoming data has HTTP header */
- bit content_range:1; /* set TRUE if Content-Range: was found */
- bit upload_done:1; /* set to TRUE when doing chunked transfer-encoding
- upload and we're uploading the last chunk */
- bit ignorebody:1; /* we read a response-body but we ignore it! */
- bit http_bodyless:1; /* HTTP response status code is between 100 and 199,
- 204 or 304 */
- bit chunk:1; /* if set, this is a chunked transfer-encoding */
- bit upload_chunky:1; /* set TRUE if we are doing chunked transfer-encoding
- on upload */
- bit getheader:1; /* TRUE if header parsing is wanted */
- bit forbidchunk:1; /* used only to explicitly forbid chunk-upload for
- specific upload buffers. See readmoredata() in http.c
- for details. */
+ BIT(header); /* incoming data has HTTP header */
+ BIT(content_range); /* set TRUE if Content-Range: was found */
+ BIT(upload_done); /* set to TRUE when doing chunked transfer-encoding
+ upload and we're uploading the last chunk */
+ BIT(ignorebody); /* we read a response-body but we ignore it! */
+ BIT(http_bodyless); /* HTTP response status code is between 100 and 199,
+ 204 or 304 */
+ BIT(chunk); /* if set, this is a chunked transfer-encoding */
+ BIT(upload_chunky); /* set TRUE if we are doing chunked transfer-encoding
+ on upload */
+ BIT(getheader); /* TRUE if header parsing is wanted */
+ BIT(forbidchunk); /* used only to explicitly forbid chunk-upload for
+ specific upload buffers. See readmoredata() in http.c
+ for details. */
};
/*
@@ -776,8 +784,8 @@ struct http_connect_state {
TUNNEL_CONNECT, /* CONNECT has been sent off */
TUNNEL_COMPLETE /* CONNECT response received completely */
} tunnel_state;
- bit chunked_encoding:1;
- bit close_connection:1;
+ BIT(chunked_encoding);
+ BIT(close_connection);
};
struct ldapconninfo;
@@ -952,7 +960,7 @@ struct connectdata {
} allocptr;
#ifdef HAVE_GSSAPI
- bit sec_complete:1; /* if Kerberos is enabled for this connection */
+ BIT(sec_complete); /* if Kerberos is enabled for this connection */
enum protection_level command_prot;
enum protection_level data_prot;
enum protection_level request_data_prot;
@@ -1045,16 +1053,16 @@ struct connectdata {
#ifdef USE_UNIX_SOCKETS
char *unix_domain_socket;
- bit abstract_unix_socket:1;
+ BIT(abstract_unix_socket);
#endif
- bit tls_upgraded:1;
+ BIT(tls_upgraded);
/* the two following *_inuse fields are only flags, not counters in any way.
If TRUE it means the channel is in use, and if FALSE it means the channel
is up for grabs by one. */
- bit readchannel_inuse:1; /* whether the read channel is in use by an easy
- handle */
- bit writechannel_inuse:1; /* whether the write channel is in use by an easy
- handle */
+ BIT(readchannel_inuse); /* whether the read channel is in use by an easy
+ handle */
+ BIT(writechannel_inuse); /* whether the write channel is in use by an easy
+ handle */
};
/* The end of connectdata. */
@@ -1096,8 +1104,8 @@ 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 */
+ BIT(timecond); /* set to TRUE if the time condition didn't match, which
+ thus made the document NOT get fetched */
};
@@ -1144,8 +1152,8 @@ struct Progress {
curl_off_t speeder[ CURR_TIME ];
struct curltime speeder_time[ CURR_TIME ];
int speeder_c;
- bit callback:1; /* set when progress callback is used */
- bit is_t_startransfer_set:1;
+ BIT(callback); /* set when progress callback is used */
+ BIT(is_t_startransfer_set);
};
typedef enum {
@@ -1193,12 +1201,12 @@ struct auth {
unsigned long picked;
unsigned long avail; /* Bitmask for what the server reports to support for
this resource */
- bit done:1; /* TRUE when the auth phase is done and ready to do the
- *actual* request */
- bit multipass:1; /* TRUE if this is not yet authenticated but within the
- auth multipass negotiation */
- bit iestyle:1; /* TRUE if digest should be done IE-style or FALSE if it
- should be RFC compliant */
+ BIT(done); /* TRUE when the auth phase is done and ready to do the
+ actual request */
+ BIT(multipass); /* TRUE if this is not yet authenticated but within the
+ auth multipass negotiation */
+ BIT(iestyle); /* TRUE if digest should be done IE-style or FALSE if it
+ should be RFC compliant */
};
struct Curl_http2_dep {
@@ -1328,7 +1336,7 @@ struct UrlState {
/* do FTP line-end conversions on most platforms */
#define CURL_DO_LINEEND_CONV
/* for FTP downloads: track CRLF sequences that span blocks */
- bit prev_block_had_trailing_cr:1;
+ BIT(prev_block_had_trailing_cr);
/* for FTP downloads: how many CRLFs did we converted to LFs? */
curl_off_t crlf_conversions;
#endif
@@ -1363,32 +1371,33 @@ struct UrlState {
trailers_state trailers_state; /* whether we are sending trailers
and what stage are we at */
#ifdef CURLDEBUG
- bit conncache_lock:1;
+ BIT(conncache_lock);
#endif
/* when curl_easy_perform() is called, the multi handle is "owned" by
the easy handle so curl_easy_cleanup() on such an easy handle will
also close the multi handle! */
- bit multi_owned_by_easy:1;
+ BIT(multi_owned_by_easy);
- bit this_is_a_follow:1; /* this is a followed Location: request */
- bit refused_stream:1; /* this was refused, try again */
- bit errorbuf:1; /* Set to TRUE if the error buffer is already filled in.
+ BIT(this_is_a_follow); /* this is a followed Location: request */
+ BIT(refused_stream); /* this was refused, try again */
+ BIT(errorbuf); /* Set to TRUE if the error buffer is already filled in.
This must be set to FALSE every time _easy_perform() is
called. */
- bit allow_port:1; /* Is set.use_port allowed to take effect or not. This
+ BIT(allow_port); /* Is set.use_port allowed to take effect or not. This
is always set TRUE when curl_easy_perform() is called. */
- bit authproblem:1; /* TRUE if there's some problem authenticating */
+ BIT(authproblem); /* TRUE if there's some problem authenticating */
/* set after initial USER failure, to prevent an authentication loop */
- bit ftp_trying_alternative:1;
- bit wildcardmatch:1; /* enable wildcard matching */
- bit expect100header:1; /* TRUE if we added Expect: 100-continue */
- bit use_range:1;
- bit rangestringalloc:1; /* the range string is malloc()'ed */
- bit done:1; /* set to FALSE when Curl_init_do() is called and set to TRUE
+ BIT(ftp_trying_alternative);
+ BIT(wildcardmatch); /* enable wildcard matching */
+ BIT(expect100header); /* TRUE if we added Expect: 100-continue */
+ BIT(use_range);
+ BIT(rangestringalloc); /* the range string is malloc()'ed */
+ BIT(done); /* set to FALSE when Curl_init_do() is called and set to TRUE
when multi_done() is called, to prevent multi_done() to get
invoked twice when the multi interface is used. */
- bit stream_depends_e:1; /* set or don't set the Exclusive bit */
- bit previouslypending:1; /* this transfer WAS in the multi->pending queue */
+ BIT(stream_depends_e); /* set or don't set the Exclusive bit */
+ BIT(previouslypending); /* this transfer WAS in the multi->pending queue */
+ BIT(cookie_engine);
};
@@ -1406,9 +1415,9 @@ struct DynamicStatic {
curl_easy_setopt(COOKIEFILE) calls */
struct curl_slist *resolve; /* set to point to the set.resolve list when
this should be dealt with in pretransfer */
- bit url_alloc:1; /* URL string is malloc()'ed */
- bit referer_alloc:1; /* referer string is malloc()ed */
- bit wildcard_resolve:1; /* Set to true if any resolve change is a
+ BIT(url_alloc); /* URL string is malloc()'ed */
+ BIT(referer_alloc); /* referer string is malloc()ed */
+ BIT(wildcard_resolve); /* Set to true if any resolve change is a
wildcard */
};
@@ -1688,84 +1697,82 @@ struct UserDefined {
CURLU *uh; /* URL handle for the current parsed URL */
void *trailer_data; /* pointer to pass to trailer data callback */
curl_trailer_callback trailer_callback; /* trailing data callback */
- bit is_fread_set:1; /* has read callback been set to non-NULL? */
- bit is_fwrite_set:1; /* has write callback been set to non-NULL? */
- bit free_referer:1; /* set TRUE if 'referer' points to a string we
+ BIT(is_fread_set); /* has read callback been set to non-NULL? */
+ BIT(is_fwrite_set); /* has write callback been set to non-NULL? */
+ BIT(free_referer); /* set TRUE if 'referer' points to a string we
allocated */
- bit tftp_no_options:1; /* do not send TFTP options requests */
- bit sep_headers:1; /* handle host and proxy headers separately */
- bit cookiesession:1; /* new cookie session? */
- bit crlf:1; /* convert crlf on ftp upload(?) */
- bit strip_path_slash:1; /* strip off initial slash from path */
- bit ssh_compression:1; /* enable SSH compression */
+ BIT(tftp_no_options); /* do not send TFTP options requests */
+ BIT(sep_headers); /* handle host and proxy headers separately */
+ BIT(cookiesession); /* new cookie session? */
+ BIT(crlf); /* convert crlf on ftp upload(?) */
+ BIT(strip_path_slash); /* strip off initial slash from path */
+ BIT(ssh_compression); /* enable SSH compression */
/* Here follows boolean settings that define how to behave during
this session. They are STATIC, set by libcurl users or at least initially
and they don't change during operations. */
- bit get_filetime:1; /* get the time and get of the remote file */
- bit tunnel_thru_httpproxy:1; /* use CONNECT through a HTTP proxy */
- bit prefer_ascii:1; /* ASCII rather than binary */
- bit ftp_append:1; /* append, not overwrite, on upload */
- bit ftp_list_only:1; /* switch FTP command for listing directories */
+ BIT(get_filetime); /* get the time and get of the remote file */
+ BIT(tunnel_thru_httpproxy); /* use CONNECT through a HTTP proxy */
+ BIT(prefer_ascii); /* ASCII rather than binary */
+ BIT(ftp_append); /* append, not overwrite, on upload */
+ BIT(ftp_list_only); /* switch FTP command for listing directories */
#ifndef CURL_DISABLE_FTP
- bit ftp_use_port:1; /* use the FTP PORT command */
- bit ftp_use_epsv:1; /* if EPSV is to be attempted or not */
- bit ftp_use_eprt:1; /* if EPRT is to be attempted or not */
- bit ftp_use_pret:1; /* if PRET is to be used before PASV or not */
- bit ftp_skip_ip:1; /* skip the IP address the FTP server passes on to
+ BIT(ftp_use_port); /* use the FTP PORT command */
+ BIT(ftp_use_epsv); /* if EPSV is to be attempted or not */
+ BIT(ftp_use_eprt); /* if EPRT is to be attempted or not */
+ BIT(ftp_use_pret); /* if PRET is to be used before PASV or not */
+ BIT(ftp_skip_ip); /* skip the IP address the FTP server passes on to
us */
#endif
- bit hide_progress:1; /* don't use the progress meter */
- bit http_fail_on_error:1; /* fail on HTTP error codes >= 400 */
- bit http_keep_sending_on_error:1; /* for HTTP status codes >= 300 */
- bit http_follow_location:1; /* follow HTTP redirects */
- bit http_transfer_encoding:1; /* request compressed HTTP
- transfer-encoding */
- bit allow_auth_to_other_hosts:1;
- bit include_header:1; /* include received protocol headers in data output */
- bit http_set_referer:1; /* is a custom referer used */
- bit http_auto_referer:1; /* set "correct" referer when following
- location: */
- bit opt_no_body:1; /* as set with CURLOPT_NOBODY */
- bit upload:1; /* upload request */
- bit verbose:1; /* output verbosity */
- bit krb:1; /* Kerberos connection requested */
- bit reuse_forbid:1; /* forbidden to be reused, close after use */
- bit reuse_fresh:1; /* do not re-use an existing connection */
-
- bit no_signal:1; /* do not use any signal/alarm handler */
- bit tcp_nodelay:1; /* whether to enable TCP_NODELAY or not */
- bit ignorecl:1; /* ignore content length */
- bit connect_only:1; /* make connection, let application use the socket */
- bit http_te_skip:1; /* pass the raw body data to the user, even when
- transfer-encoded (chunked, compressed) */
- bit http_ce_skip:1; /* pass the raw body data to the user, even when
- content-encoded (chunked, compressed) */
- bit proxy_transfer_mode:1; /* set transfer mode (;type=<a|i>) when doing
- FTP via an HTTP proxy */
+ BIT(hide_progress); /* don't use the progress meter */
+ BIT(http_fail_on_error); /* fail on HTTP error codes >= 400 */
+ BIT(http_keep_sending_on_error); /* for HTTP status codes >= 300 */
+ BIT(http_follow_location); /* follow HTTP redirects */
+ BIT(http_transfer_encoding); /* request compressed HTTP transfer-encoding */
+ BIT(allow_auth_to_other_hosts);
+ BIT(include_header); /* include received protocol headers in data output */
+ BIT(http_set_referer); /* is a custom referer used */
+ BIT(http_auto_referer); /* set "correct" referer when following
+ location: */
+ BIT(opt_no_body); /* as set with CURLOPT_NOBODY */
+ BIT(upload); /* upload request */
+ BIT(verbose); /* output verbosity */
+ BIT(krb); /* Kerberos connection requested */
+ BIT(reuse_forbid); /* forbidden to be reused, close after use */
+ BIT(reuse_fresh); /* do not re-use an existing connection */
+ BIT(no_signal); /* do not use any signal/alarm handler */
+ BIT(tcp_nodelay); /* whether to enable TCP_NODELAY or not */
+ BIT(ignorecl); /* ignore content length */
+ BIT(connect_only); /* make connection, let application use the socket */
+ BIT(http_te_skip); /* pass the raw body data to the user, even when
+ transfer-encoded (chunked, compressed) */
+ BIT(http_ce_skip); /* pass the raw body data to the user, even when
+ content-encoded (chunked, compressed) */
+ BIT(proxy_transfer_mode); /* set transfer mode (;type=<a|i>) when doing
+ FTP via an HTTP proxy */
#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
- bit socks5_gssapi_nec:1; /* Flag to support NEC SOCKS5 server */
+ BIT(socks5_gssapi_nec); /* Flag to support NEC SOCKS5 server */
#endif
- bit sasl_ir:1; /* Enable/disable SASL initial response */
- bit wildcard_enabled:1; /* enable wildcard matching */
- bit tcp_keepalive:1; /* use TCP keepalives */
- bit tcp_fastopen:1; /* use TCP Fast Open */
- bit ssl_enable_npn:1; /* TLS NPN extension? */
- bit ssl_enable_alpn:1;/* TLS ALPN extension? */
- bit path_as_is:1; /* allow dotdots? */
- bit pipewait:1; /* wait for multiplex status before starting a new
- connection */
- bit suppress_connect_headers:1; /* suppress proxy CONNECT response headers
- from user callbacks */
- bit dns_shuffle_addresses:1; /* whether to shuffle addresses before use */
- bit stream_depends_e:1; /* set or don't set the Exclusive bit */
- bit haproxyprotocol:1; /* whether to send HAProxy PROXY protocol v1
- header */
- bit abstract_unix_socket:1;
- bit disallow_username_in_url:1; /* disallow username in url */
- bit doh:1; /* DNS-over-HTTPS enabled */
- bit doh_get:1; /* use GET for DoH requests, instead of POST */
- bit http09_allowed:1; /* allow HTTP/0.9 responses */
+ BIT(sasl_ir); /* Enable/disable SASL initial response */
+ BIT(wildcard_enabled); /* enable wildcard matching */
+ BIT(tcp_keepalive); /* use TCP keepalives */
+ BIT(tcp_fastopen); /* use TCP Fast Open */
+ BIT(ssl_enable_npn); /* TLS NPN extension? */
+ BIT(ssl_enable_alpn);/* TLS ALPN extension? */
+ BIT(path_as_is); /* allow dotdots? */
+ BIT(pipewait); /* wait for multiplex status before starting a new
+ connection */
+ BIT(suppress_connect_headers); /* suppress proxy CONNECT response headers
+ from user callbacks */
+ BIT(dns_shuffle_addresses); /* whether to shuffle addresses before use */
+ BIT(stream_depends_e); /* set or don't set the Exclusive bit */
+ BIT(haproxyprotocol); /* whether to send HAProxy PROXY protocol v1
+ header */
+ BIT(abstract_unix_socket);
+ BIT(disallow_username_in_url); /* disallow username in url */
+ BIT(doh); /* DNS-over-HTTPS enabled */
+ BIT(doh_get); /* use GET for DoH requests, instead of POST */
+ BIT(http09_allowed); /* allow HTTP/0.9 responses */
};
struct Names {
diff --git a/libs/libcurl/src/vauth/vauth.h b/libs/libcurl/src/vauth/vauth.h
index 73bd25ed5e..a1a557d2a1 100644
--- a/libs/libcurl/src/vauth/vauth.h
+++ b/libs/libcurl/src/vauth/vauth.h
@@ -43,7 +43,7 @@ struct negotiatedata;
#endif
#if defined(USE_WINDOWS_SSPI)
-#define GSS_ERROR(status) (status & 0x80000000)
+#define GSS_ERROR(status) ((status) & 0x80000000)
#endif
/* This is used to build a SPN string */
diff --git a/libs/libcurl/src/version.c b/libs/libcurl/src/version.c
index ae2b09d310..cfd09e36d7 100644
--- a/libs/libcurl/src/version.c
+++ b/libs/libcurl/src/version.c
@@ -104,14 +104,12 @@ char *curl_version(void)
left -= len;
ptr += len;
- if(left > 1) {
- len = Curl_ssl_version(ptr + 1, left - 1);
+ len = Curl_ssl_version(ptr + 1, left - 1);
- if(len > 0) {
- *ptr = ' ';
- left -= ++len;
- ptr += len;
- }
+ if(len > 0) {
+ *ptr = ' ';
+ left -= ++len;
+ ptr += len;
}
#ifdef HAVE_LIBZ
@@ -369,6 +367,9 @@ static curl_version_info_data version_info = {
#if defined(USE_ALTSVC)
| CURL_VERSION_ALTSVC
#endif
+#ifdef USE_ESNI
+ | CURL_VERSION_ESNI
+#endif
,
NULL, /* ssl_version */
0, /* ssl_version_num, this is kept at zero */
diff --git a/libs/libcurl/src/vquic/ngtcp2.c b/libs/libcurl/src/vquic/ngtcp2.c
new file mode 100644
index 0000000000..c0f9b16e38
--- /dev/null
+++ b/libs/libcurl/src/vquic/ngtcp2.c
@@ -0,0 +1,1593 @@
+/***************************************************************************
+ * _ _ ____ _
+ * 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 */
+#ifdef CURLDEBUG
+#define DEBUG_HTTP3
+#endif
+#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->transport_params.initial_max_stream_data_bidi_local = stream_buffer_size;
+ s->transport_params.initial_max_stream_data_bidi_remote = QUIC_MAX_STREAMS;
+ s->transport_params.initial_max_stream_data_uni = QUIC_MAX_STREAMS;
+ s->transport_params.initial_max_data = QUIC_MAX_DATA;
+ s->transport_params.initial_max_streams_bidi = 1;
+ s->transport_params.initial_max_streams_uni = 3;
+ s->transport_params.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) {
+ H3BUGF(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",
+ SOCKERRNO, Curl_strerror(SOCKERRNO, 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) {
+ failf(data, "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;
+ H3BUGF(infof(data, "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;
+ H3BUGF(infof(data, "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;
+
+ 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;
+
+ 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;
+ 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;
+
+ 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;
+ H3BUGF(infof(data,
+ "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 ssize_t cb_h3_readfunction(nghttp3_conn *conn, int64_t stream_id,
+ nghttp3_vec *vec, size_t veccnt,
+ uint32_t *pflags, void *user_data,
+ void *stream_user_data)
+{
+ struct Curl_easy *data = stream_user_data;
+ size_t nread;
+ struct HTTP *stream = data->req.protop;
+ (void)conn;
+ (void)stream_id;
+ (void)user_data;
+ (void)veccnt;
+
+ if(data->set.postfields) {
+ vec[0].base = data->set.postfields;
+ vec[0].len = data->state.infilesize;
+ *pflags = NGHTTP3_DATA_FLAG_EOF;
+ return 1;
+ }
+
+ 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 */
+ vec[0].base = &out->buf[out->windex];
+ vec[0].len = nread;
+
+ if(out->windex == H3_SEND_SIZE)
+ out->windex = 0; /* wrap */
+ stream->upload_mem += nread;
+ stream->upload_len -= nread;
+ if(data->state.infilesize != -1) {
+ stream->upload_left -= nread;
+ if(!stream->upload_left)
+ *pflags = NGHTTP3_DATA_FLAG_EOF;
+ }
+ H3BUGF(infof(data, "cb_h3_readfunction %zd bytes%s (at %zd unacked)\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"));
+ *pflags = NGHTTP3_DATA_FLAG_EOF;
+ return 0;
+ }
+ else if(!nread) {
+ return NGHTTP3_ERR_WOULDBLOCK;
+ }
+ return 1;
+}
+
+/* Index where :authority header field will appear in request header
+ field list. */
+#define AUTHORITY_DST_IDX 3
+
+static CURLcode http_request(struct 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].namelen = (size_t)(end - hdbuf);
+ /* Lower case the header name for HTTP/3 */
+ Curl_strntolower((char *)hdbuf, hdbuf, nva[i].namelen);
+ nva[i].name = (unsigned char *)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 {
+ H3BUGF(infof(conn->data, "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, 0,
+ (struct sockaddr *)&remote_addr,
+ &remote_addrlen)) == -1 &&
+ SOCKERRNO == EINTR)
+ ;
+ if(recvd == -1) {
+ if(SOCKERRNO == EAGAIN || SOCKERRNO == 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 = send(sockfd, out, outlen, 0)) == -1 &&
+ SOCKERRNO == EINTR)
+ ;
+
+ if(sent == -1) {
+ if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
+ /* TODO Cache packet */
+ break;
+ }
+ else {
+ failf(conn->data, "send() 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;
+ stream->upload_done = TRUE;
+ (void)nghttp3_conn_resume_stream(qs->h3conn, stream->stream3_id);
+ }
+
+ return CURLE_OK;
+}
+#endif
diff --git a/libs/libcurl/src/vquic/ngtcp2.h b/libs/libcurl/src/vquic/ngtcp2.h
new file mode 100644
index 0000000000..5570fc7e78
--- /dev/null
+++ b/libs/libcurl/src/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/libs/libcurl/src/vquic/quiche.c b/libs/libcurl/src/vquic/quiche.c
new file mode 100644
index 0000000000..0ee360d07f
--- /dev/null
+++ b/libs/libcurl/src/vquic/quiche.c
@@ -0,0 +1,783 @@
+/***************************************************************************
+ * _ _ ____ _
+ * 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;
+
+ /* store the used address as a string */
+ if(!Curl_addr2string((struct sockaddr*)addr, addrlen,
+ conn->primary_ip, &conn->primary_port)) {
+ char buffer[STRERROR_LEN];
+ failf(data, "ssrem inet_ntop() failed with errno %d: %s",
+ SOCKERRNO, Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+ memcpy(conn->ip_addr_str, conn->primary_ip, MAX_IPADR_LEN);
+ Curl_persistconninfo(conn);
+
+ /* 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();
+ 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) && ((SOCKERRNO == EAGAIN) || (SOCKERRNO == 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 Curl_easy *data = conn->data;
+ struct HTTP *stream = data->req.protop;
+ headers.dest = buf;
+ headers.destlen = buffersize;
+ headers.nlen = 0;
+
+ if(process_ingress(conn, sockfd, qs)) {
+ infof(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(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:
+ streamclose(conn, "End of stream");
+ 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(data, 0, EXPIRE_QUIC);
+
+ data->state.drain = (recvd >= 0) ? 1 : 0;
+ 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_len = (size_t)(end - hdbuf);
+ /* Lower case the header name for HTTP/3 */
+ Curl_strntolower((char *)hdbuf, hdbuf, nva[i].name_len);
+ nva[i].name = (unsigned char *)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/libs/libcurl/src/vquic/quiche.h b/libs/libcurl/src/vquic/quiche.h
new file mode 100644
index 0000000000..c8d1837b56
--- /dev/null
+++ b/libs/libcurl/src/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/libs/libcurl/src/vssh/libssh.c b/libs/libcurl/src/vssh/libssh.c
index 76956a3c1f..cad8b37864 100644
--- a/libs/libcurl/src/vssh/libssh.c
+++ b/libs/libcurl/src/vssh/libssh.c
@@ -493,7 +493,7 @@ restart:
return SSH_ERROR;
nprompts = ssh_userauth_kbdint_getnprompts(sshc->ssh_session);
- if(nprompts == SSH_ERROR || nprompts != 1)
+ if(nprompts != 1)
return SSH_ERROR;
rc = ssh_userauth_kbdint_setanswer(sshc->ssh_session, 0, conn->passwd);
@@ -1356,7 +1356,7 @@ static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block)
break;
}
}
- else if(sshc->readdir_attrs == NULL && sftp_dir_eof(sshc->sftp_dir)) {
+ else if(sftp_dir_eof(sshc->sftp_dir)) {
state(conn, SSH_SFTP_READDIR_DONE);
break;
}
@@ -1999,7 +1999,7 @@ static CURLcode myssh_block_statemach(struct connectdata *conn,
}
}
- if(!result && block) {
+ if(block) {
curl_socket_t fd_read = conn->sock[FIRSTSOCKET];
/* wait for the socket to become ready */
(void) Curl_socket_check(fd_read, CURL_SOCKET_BAD,
diff --git a/libs/libcurl/src/vssh/libssh2.c b/libs/libcurl/src/vssh/libssh2.c
index 2b25a514f4..c71cfbc9fd 100644
--- a/libs/libcurl/src/vssh/libssh2.c
+++ b/libs/libcurl/src/vssh/libssh2.c
@@ -2811,7 +2811,7 @@ static CURLcode ssh_block_statemach(struct connectdata *conn,
}
#ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION
- if(!result && block) {
+ if(block) {
int dir = libssh2_session_block_directions(sshc->ssh_session);
curl_socket_t sock = conn->sock[FIRSTSOCKET];
curl_socket_t fd_read = CURL_SOCKET_BAD;
@@ -2822,7 +2822,7 @@ static CURLcode ssh_block_statemach(struct connectdata *conn,
fd_write = sock;
/* wait for the socket to become ready */
(void)Curl_socket_check(fd_read, CURL_SOCKET_BAD, fd_write,
- left>1000?1000:left); /* ignore result */
+ left>1000?1000:(time_t)left);
}
#endif
diff --git a/libs/libcurl/src/vtls/gskit.c b/libs/libcurl/src/vtls/gskit.c
index b93ff5d4f4..32153dd071 100644
--- a/libs/libcurl/src/vtls/gskit.c
+++ b/libs/libcurl/src/vtls/gskit.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
@@ -26,6 +26,8 @@
#include <gskssl.h>
#include <qsoasync.h>
+#undef HAVE_SOCKETPAIR /* because the native one isn't good enough */
+#include "socketpair.h"
/* Some symbols are undefined/unsupported on OS400 versions < V7R1. */
#ifndef GSK_SSL_EXTN_SERVERNAME_REQUEST
@@ -511,100 +513,6 @@ static void close_async_handshake(struct ssl_connect_data *connssl)
BACKEND->iocport = -1;
}
-/* SSL over SSL
- * Problems:
- * 1) GSKit can only perform SSL on an AF_INET or AF_INET6 stream socket. To
- * pipe an SSL stream into another, it is therefore needed to have a pair
- * of such communicating sockets and handle the pipelining explicitly.
- * 2) OS/400 socketpair() is only implemented for domain AF_UNIX, thus cannot
- * be used to produce the pipeline.
- * The solution is to simulate socketpair() for AF_INET with low-level API
- * listen(), bind() and connect().
- */
-
-static int
-inetsocketpair(int sv[2])
-{
- int lfd; /* Listening socket. */
- int sfd; /* Server socket. */
- int cfd; /* Client socket. */
- int len;
- struct sockaddr_in addr1;
- struct sockaddr_in addr2;
-
- /* Create listening socket on a local dynamic port. */
- lfd = socket(AF_INET, SOCK_STREAM, 0);
- if(lfd < 0)
- return -1;
- memset((char *) &addr1, 0, sizeof(addr1));
- addr1.sin_family = AF_INET;
- addr1.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- addr1.sin_port = 0;
- if(bind(lfd, (struct sockaddr *) &addr1, sizeof(addr1)) ||
- listen(lfd, 2) < 0) {
- close(lfd);
- return -1;
- }
-
- /* Get the allocated port. */
- len = sizeof(addr1);
- if(getsockname(lfd, (struct sockaddr *) &addr1, &len) < 0) {
- close(lfd);
- return -1;
- }
-
- /* Create the client socket. */
- cfd = socket(AF_INET, SOCK_STREAM, 0);
- if(cfd < 0) {
- close(lfd);
- return -1;
- }
-
- /* Request unblocking connection to the listening socket. */
- curlx_nonblock(cfd, TRUE);
- if(connect(cfd, (struct sockaddr *) &addr1, sizeof(addr1)) < 0 &&
- errno != EINPROGRESS) {
- close(lfd);
- close(cfd);
- return -1;
- }
-
- /* Get the client dynamic port for intrusion check below. */
- len = sizeof(addr2);
- if(getsockname(cfd, (struct sockaddr *) &addr2, &len) < 0) {
- close(lfd);
- close(cfd);
- return -1;
- }
-
- /* Accept the incoming connection and get the server socket. */
- curlx_nonblock(lfd, TRUE);
- for(;;) {
- len = sizeof(addr1);
- sfd = accept(lfd, (struct sockaddr *) &addr1, &len);
- if(sfd < 0) {
- close(lfd);
- close(cfd);
- return -1;
- }
-
- /* Check for possible intrusion from an external process. */
- if(addr1.sin_addr.s_addr == addr2.sin_addr.s_addr &&
- addr1.sin_port == addr2.sin_port)
- break;
-
- /* Intrusion: reject incoming connection. */
- close(sfd);
- }
-
- /* Done, return sockets and succeed. */
- close(lfd);
- curlx_nonblock(cfd, FALSE);
- sv[0] = cfd;
- sv[1] = sfd;
- return 0;
-}
-
static int pipe_ssloverssl(struct connectdata *conn, int sockindex,
int directions)
{
@@ -855,7 +763,7 @@ static CURLcode gskit_connect_step1(struct connectdata *conn, int sockindex)
/* Establish a pipelining socket pair for SSL over SSL. */
if(conn->proxy_ssl[sockindex].use) {
- if(inetsocketpair(sockpair))
+ if(Curl_socketpair(0, 0, 0, sockpair))
return CURLE_SSL_CONNECT_ERROR;
BACKEND->localfd = sockpair[0];
BACKEND->remotefd = sockpair[1];
@@ -1157,7 +1065,7 @@ static CURLcode gskit_connect_common(struct connectdata *conn, int sockindex,
{
struct Curl_easy *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
- long timeout_ms;
+ timediff_t timeout_ms;
CURLcode result = CURLE_OK;
*done = connssl->state == ssl_connection_complete;
diff --git a/libs/libcurl/src/vtls/gtls.c b/libs/libcurl/src/vtls/gtls.c
index 8693cdce3f..3737d7c685 100644
--- a/libs/libcurl/src/vtls/gtls.c
+++ b/libs/libcurl/src/vtls/gtls.c
@@ -288,7 +288,7 @@ static CURLcode handshake(struct connectdata *conn,
curl_socket_t sockfd = conn->sock[sockindex];
for(;;) {
- time_t timeout_ms;
+ timediff_t timeout_ms;
int rc;
/* check allowed time left */
@@ -311,7 +311,7 @@ static CURLcode handshake(struct connectdata *conn,
what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
nonblocking?0:
- timeout_ms?timeout_ms:1000);
+ timeout_ms?(time_t)timeout_ms:1000);
if(what < 0) {
/* fatal error */
failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
@@ -1608,7 +1608,7 @@ static ssize_t gtls_send(struct connectdata *conn,
static void close_one(struct ssl_connect_data *connssl)
{
if(BACKEND->session) {
- gnutls_bye(BACKEND->session, GNUTLS_SHUT_RDWR);
+ gnutls_bye(BACKEND->session, GNUTLS_SHUT_WR);
gnutls_deinit(BACKEND->session);
BACKEND->session = NULL;
}
diff --git a/libs/libcurl/src/vtls/mbedtls.c b/libs/libcurl/src/vtls/mbedtls.c
index 63d1f4c81b..e34ec9d13f 100644
--- a/libs/libcurl/src/vtls/mbedtls.c
+++ b/libs/libcurl/src/vtls/mbedtls.c
@@ -588,6 +588,9 @@ mbed_connect_step2(struct connectdata *conn,
else if(ret & MBEDTLS_X509_BADCERT_NOT_TRUSTED)
failf(data, "Cert verify failed: BADCERT_NOT_TRUSTED");
+ else if(ret & MBEDTLS_X509_BADCERT_FUTURE)
+ failf(data, "Cert verify failed: BADCERT_FUTURE");
+
return CURLE_PEER_FAILED_VERIFICATION;
}
@@ -884,7 +887,7 @@ mbed_connect_common(struct connectdata *conn,
struct Curl_easy *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
curl_socket_t sockfd = conn->sock[sockindex];
- long timeout_ms;
+ timediff_t timeout_ms;
int what;
/* check if the connection has already been established */
@@ -930,7 +933,7 @@ mbed_connect_common(struct connectdata *conn,
connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
- nonblocking ? 0 : timeout_ms);
+ nonblocking ? 0 : (time_t)timeout_ms);
if(what < 0) {
/* fatal error */
failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
diff --git a/libs/libcurl/src/vtls/mesalink.c b/libs/libcurl/src/vtls/mesalink.c
index 9507888bdf..cab1e390b4 100644
--- a/libs/libcurl/src/vtls/mesalink.c
+++ b/libs/libcurl/src/vtls/mesalink.c
@@ -6,7 +6,7 @@
* \___|\___/|_| \_\_____|
*
* Copyright (C) 2017 - 2018, Yiming Jing, <jingyiming@baidu.com>
- * 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
@@ -494,7 +494,7 @@ mesalink_connect_common(struct connectdata *conn, int sockindex,
struct Curl_easy *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
curl_socket_t sockfd = conn->sock[sockindex];
- time_t timeout_ms;
+ timediff_t timeout_ms;
int what;
/* check if the connection has already been established */
@@ -543,7 +543,8 @@ mesalink_connect_common(struct connectdata *conn, int sockindex,
: CURL_SOCKET_BAD;
what = Curl_socket_check(
- readfd, CURL_SOCKET_BAD, writefd, nonblocking ? 0 : timeout_ms);
+ readfd, CURL_SOCKET_BAD, writefd,
+ nonblocking ? 0 : (time_t)timeout_ms);
if(what < 0) {
/* fatal error */
failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
diff --git a/libs/libcurl/src/vtls/nss.c b/libs/libcurl/src/vtls/nss.c
index 435f3e93a1..a375f00da2 100644
--- a/libs/libcurl/src/vtls/nss.c
+++ b/libs/libcurl/src/vtls/nss.c
@@ -2127,7 +2127,7 @@ static CURLcode nss_do_connect(struct connectdata *conn, int sockindex)
/* check timeout situation */
- const time_t time_left = Curl_timeleft(data, NULL, TRUE);
+ const timediff_t time_left = Curl_timeleft(data, NULL, TRUE);
if(time_left < 0) {
failf(data, "timed out before SSL handshake");
result = CURLE_OPERATION_TIMEDOUT;
diff --git a/libs/libcurl/src/vtls/openssl.c b/libs/libcurl/src/vtls/openssl.c
index 385f281793..760758d234 100644
--- a/libs/libcurl/src/vtls/openssl.c
+++ b/libs/libcurl/src/vtls/openssl.c
@@ -44,6 +44,7 @@
#include "strcase.h"
#include "hostcheck.h"
#include "multiif.h"
+#include "strerror.h"
#include "curl_printf.h"
#include <openssl/ssl.h>
#include <openssl/rand.h>
@@ -2165,8 +2166,13 @@ set_ssl_version_min_max(SSL_CTX *ctx, struct connectdata *conn)
long curl_ssl_version_max;
/* convert cURL min SSL version option to OpenSSL constant */
+#if defined(OPENSSL_IS_BORINGSSL) || defined(LIBRESSL_VERSION_NUMBER)
+ uint16_t ossl_ssl_version_min = 0;
+ uint16_t ossl_ssl_version_max = 0;
+#else
long ossl_ssl_version_min = 0;
long ossl_ssl_version_max = 0;
+#endif
switch(curl_ssl_version_min) {
case CURL_SSLVERSION_TLSv1: /* TLS 1.x */
case CURL_SSLVERSION_TLSv1_0:
@@ -2186,10 +2192,10 @@ set_ssl_version_min_max(SSL_CTX *ctx, struct connectdata *conn)
}
/* 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
+ 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)) {
@@ -3649,7 +3655,7 @@ static CURLcode ossl_connect_common(struct connectdata *conn,
struct Curl_easy *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
curl_socket_t sockfd = conn->sock[sockindex];
- time_t timeout_ms;
+ timediff_t timeout_ms;
int what;
/* check if the connection has already been established */
@@ -3696,7 +3702,7 @@ static CURLcode ossl_connect_common(struct connectdata *conn,
connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
- nonblocking?0:timeout_ms);
+ nonblocking?0:(time_t)timeout_ms);
if(what < 0) {
/* fatal error */
failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
@@ -3820,8 +3826,8 @@ static ssize_t ossl_send(struct connectdata *conn,
*curlcode = CURLE_AGAIN;
return -1;
case SSL_ERROR_SYSCALL:
- failf(conn->data, "SSL_write() returned SYSCALL, errno = %d",
- SOCKERRNO);
+ Curl_strerror(SOCKERRNO, error_buffer, sizeof(error_buffer));
+ failf(conn->data, OSSL_PACKAGE " SSL_write: %s", error_buffer);
*curlcode = CURLE_SEND_ERROR;
return -1;
case SSL_ERROR_SSL:
@@ -3878,13 +3884,21 @@ static ssize_t ossl_recv(struct connectdata *conn, /* connection data */
break;
case SSL_ERROR_ZERO_RETURN: /* no more data */
/* close_notify alert */
- connclose(conn, "TLS close_notify");
+ if(num == FIRSTSOCKET)
+ /* mark the connection for close if it is indeed the control
+ connection */
+ connclose(conn, "TLS close_notify");
break;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE:
/* there's data pending, re-invoke SSL_read() */
*curlcode = CURLE_AGAIN;
return -1;
+ case SSL_ERROR_SYSCALL:
+ Curl_strerror(SOCKERRNO, error_buffer, sizeof(error_buffer));
+ failf(conn->data, OSSL_PACKAGE " SSL_read: %s", error_buffer);
+ *curlcode = CURLE_RECV_ERROR;
+ return -1;
default:
/* openssl/ssl.h for SSL_ERROR_SYSCALL says "look at error stack/return
value/errno" */
diff --git a/libs/libcurl/src/vtls/polarssl.c b/libs/libcurl/src/vtls/polarssl.c
index 7ea26b4425..9e7dd90437 100644
--- a/libs/libcurl/src/vtls/polarssl.c
+++ b/libs/libcurl/src/vtls/polarssl.c
@@ -734,7 +734,7 @@ polarssl_connect_common(struct connectdata *conn,
struct Curl_easy *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
curl_socket_t sockfd = conn->sock[sockindex];
- long timeout_ms;
+ timediff_t timeout_ms;
int what;
/* check if the connection has already been established */
@@ -781,7 +781,7 @@ polarssl_connect_common(struct connectdata *conn,
connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
- nonblocking?0:timeout_ms);
+ nonblocking?0:(time_t)timeout_ms);
if(what < 0) {
/* fatal error */
failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
diff --git a/libs/libcurl/src/vtls/schannel.c b/libs/libcurl/src/vtls/schannel.c
index 0f6f734fdc..bbd2fe921c 100644
--- a/libs/libcurl/src/vtls/schannel.c
+++ b/libs/libcurl/src/vtls/schannel.c
@@ -1181,6 +1181,7 @@ struct Adder_args
struct connectdata *conn;
CURLcode result;
int idx;
+ int certs_count;
};
static bool
@@ -1191,7 +1192,9 @@ add_cert_to_certinfo(const CERT_CONTEXT *ccert_context, void *raw_arg)
if(valid_cert_encoding(ccert_context)) {
const char *beg = (const char *) ccert_context->pbCertEncoded;
const char *end = beg + ccert_context->cbCertEncoded;
- args->result = Curl_extract_certinfo(args->conn, (args->idx)++, beg, end);
+ int insert_index = (args->certs_count - 1) - args->idx;
+ args->result = Curl_extract_certinfo(args->conn, insert_index, beg, end);
+ args->idx++;
}
return args->result == CURLE_OK;
}
@@ -1326,6 +1329,7 @@ schannel_connect_step3(struct connectdata *conn, int sockindex)
struct Adder_args args;
args.conn = conn;
args.idx = 0;
+ args.certs_count = certs_count;
traverse_cert_store(ccert_context, add_cert_to_certinfo, &args);
result = args.result;
}
@@ -1347,7 +1351,7 @@ schannel_connect_common(struct connectdata *conn, int sockindex,
struct Curl_easy *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
curl_socket_t sockfd = conn->sock[sockindex];
- time_t timeout_ms;
+ timediff_t timeout_ms;
int what;
/* check if the connection has already been established */
@@ -1394,7 +1398,7 @@ schannel_connect_common(struct connectdata *conn, int sockindex,
connssl->connecting_state ? sockfd : CURL_SOCKET_BAD;
what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
- nonblocking ? 0 : timeout_ms);
+ nonblocking ? 0 : (time_t)timeout_ms);
if(what < 0) {
/* fatal error */
failf(data, "select/poll on SSL/TLS socket, errno: %d", SOCKERRNO);
@@ -1544,7 +1548,7 @@ schannel_send(struct connectdata *conn, int sockindex,
/* send entire message or fail */
while(len > (size_t)written) {
ssize_t this_write;
- time_t timeleft;
+ timediff_t timeleft;
int what;
this_write = 0;
diff --git a/libs/libcurl/src/vtls/schannel_verify.c b/libs/libcurl/src/vtls/schannel_verify.c
index 5a09e969e8..1bdf50a55c 100644
--- a/libs/libcurl/src/vtls/schannel_verify.c
+++ b/libs/libcurl/src/vtls/schannel_verify.c
@@ -111,7 +111,7 @@ static CURLcode add_certs_to_store(HCERTSTORE trust_store,
*/
ca_file_handle = CreateFile(ca_file_tstr,
GENERIC_READ,
- 0,
+ FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
diff --git a/libs/libcurl/src/vtls/sectransp.c b/libs/libcurl/src/vtls/sectransp.c
index 3fb125ab56..4eece89d55 100644
--- a/libs/libcurl/src/vtls/sectransp.c
+++ b/libs/libcurl/src/vtls/sectransp.c
@@ -79,7 +79,7 @@
/* These macros mean "the following code is present to allow runtime backward
compatibility with at least this cat or earlier":
(You set this at build-time using the compiler command line option
- "-mmacos-version-min.") */
+ "-mmacosx-version-min.") */
#define CURL_SUPPORT_MAC_10_5 MAC_OS_X_VERSION_MIN_REQUIRED <= 1050
#define CURL_SUPPORT_MAC_10_6 MAC_OS_X_VERSION_MIN_REQUIRED <= 1060
#define CURL_SUPPORT_MAC_10_7 MAC_OS_X_VERSION_MIN_REQUIRED <= 1070
@@ -2805,7 +2805,7 @@ sectransp_connect_common(struct connectdata *conn,
struct Curl_easy *data = conn->data;
struct ssl_connect_data *connssl = &conn->ssl[sockindex];
curl_socket_t sockfd = conn->sock[sockindex];
- long timeout_ms;
+ timediff_t timeout_ms;
int what;
/* check if the connection has already been established */
@@ -2852,7 +2852,7 @@ sectransp_connect_common(struct connectdata *conn,
connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
- nonblocking?0:timeout_ms);
+ nonblocking?0:(time_t)timeout_ms);
if(what < 0) {
/* fatal error */
failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
diff --git a/libs/libcurl/src/vtls/vtls.c b/libs/libcurl/src/vtls/vtls.c
index 422819899e..e6d7562254 100644
--- a/libs/libcurl/src/vtls/vtls.c
+++ b/libs/libcurl/src/vtls/vtls.c
@@ -97,7 +97,8 @@ Curl_ssl_config_matches(struct ssl_primary_config* data,
Curl_safe_strcasecompare(data->random_file, needle->random_file) &&
Curl_safe_strcasecompare(data->egdsocket, needle->egdsocket) &&
Curl_safe_strcasecompare(data->cipher_list, needle->cipher_list) &&
- Curl_safe_strcasecompare(data->cipher_list13, needle->cipher_list13))
+ Curl_safe_strcasecompare(data->cipher_list13, needle->cipher_list13) &&
+ Curl_safe_strcasecompare(data->pinned_key, needle->pinned_key))
return TRUE;
return FALSE;
@@ -121,6 +122,7 @@ Curl_clone_primary_ssl_config(struct ssl_primary_config *source,
CLONE_STRING(egdsocket);
CLONE_STRING(cipher_list);
CLONE_STRING(cipher_list13);
+ CLONE_STRING(pinned_key);
return TRUE;
}
@@ -134,6 +136,7 @@ void Curl_free_primary_ssl_config(struct ssl_primary_config* sslc)
Curl_safefree(sslc->egdsocket);
Curl_safefree(sslc->cipher_list);
Curl_safefree(sslc->cipher_list13);
+ Curl_safefree(sslc->pinned_key);
}
#ifdef USE_SSL