summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/freeimage/src/FreeImage/MultiPage.cpp22
-rw-r--r--libs/libcurl/docs/COPYING2
-rw-r--r--libs/libcurl/docs/RELEASE-NOTES639
-rw-r--r--libs/libcurl/docs/THANKS900
-rw-r--r--libs/libcurl/include/curl/curl.h258
-rw-r--r--libs/libcurl/include/curl/curlver.h10
-rw-r--r--libs/libcurl/include/curl/easy.h2
-rw-r--r--libs/libcurl/include/curl/system.h168
-rw-r--r--libs/libcurl/include/curl/typecheck-gcc.h547
-rw-r--r--libs/libcurl/include/curl/websockets.h3
-rw-r--r--libs/libcurl/libcurl.vcxproj136
-rw-r--r--libs/libcurl/libcurl.vcxproj.filters200
-rw-r--r--libs/libcurl/src/.checksrc10
-rw-r--r--libs/libcurl/src/CMakeLists.txt115
-rw-r--r--libs/libcurl/src/Makefile.am53
-rw-r--r--libs/libcurl/src/Makefile.in1604
-rw-r--r--libs/libcurl/src/Makefile.inc88
-rw-r--r--libs/libcurl/src/altsvc.c425
-rw-r--r--libs/libcurl/src/altsvc.h11
-rw-r--r--libs/libcurl/src/amigaos.c7
-rw-r--r--libs/libcurl/src/asyn-ares.c851
-rw-r--r--libs/libcurl/src/asyn-base.c196
-rw-r--r--libs/libcurl/src/asyn-thrdd.c760
-rw-r--r--libs/libcurl/src/asyn-thread.c781
-rw-r--r--libs/libcurl/src/asyn.h262
-rw-r--r--libs/libcurl/src/bufq.c23
-rw-r--r--libs/libcurl/src/bufq.h8
-rw-r--r--libs/libcurl/src/bufref.c4
-rw-r--r--libs/libcurl/src/c-hyper.c1254
-rw-r--r--libs/libcurl/src/c-hyper.h63
-rw-r--r--libs/libcurl/src/cf-h1-proxy.c391
-rw-r--r--libs/libcurl/src/cf-h2-proxy.c16
-rw-r--r--libs/libcurl/src/cf-haproxy.c30
-rw-r--r--libs/libcurl/src/cf-https-connect.c399
-rw-r--r--libs/libcurl/src/cf-https-connect.h9
-rw-r--r--libs/libcurl/src/cf-socket.c293
-rw-r--r--libs/libcurl/src/cf-socket.h8
-rw-r--r--libs/libcurl/src/cfilters.c144
-rw-r--r--libs/libcurl/src/cfilters.h68
-rw-r--r--libs/libcurl/src/config-amigaos.h129
-rw-r--r--libs/libcurl/src/config-dos.h138
-rw-r--r--libs/libcurl/src/config-mac.h7
-rw-r--r--libs/libcurl/src/config-os400.h20
-rw-r--r--libs/libcurl/src/config-plan9.h10
-rw-r--r--libs/libcurl/src/config-riscos.h18
-rw-r--r--libs/libcurl/src/config-win32.h208
-rw-r--r--libs/libcurl/src/config-win32ce.h303
-rw-r--r--libs/libcurl/src/conncache.c1026
-rw-r--r--libs/libcurl/src/conncache.h87
-rw-r--r--libs/libcurl/src/connect.c196
-rw-r--r--libs/libcurl/src/connect.h11
-rw-r--r--libs/libcurl/src/content_encoding.c395
-rw-r--r--libs/libcurl/src/cookie.c351
-rw-r--r--libs/libcurl/src/cookie.h10
-rw-r--r--libs/libcurl/src/cshutdn.c581
-rw-r--r--libs/libcurl/src/cshutdn.h110
-rw-r--r--libs/libcurl/src/curl_addrinfo.c52
-rw-r--r--libs/libcurl/src/curl_config.h.cmake84
-rw-r--r--libs/libcurl/src/curl_config.h.in142
-rw-r--r--libs/libcurl/src/curl_ctype.h3
-rw-r--r--libs/libcurl/src/curl_fnmatch.c23
-rw-r--r--libs/libcurl/src/curl_get_line.c8
-rw-r--r--libs/libcurl/src/curl_get_line.h2
-rw-r--r--libs/libcurl/src/curl_gethostname.c4
-rw-r--r--libs/libcurl/src/curl_gssapi.c13
-rw-r--r--libs/libcurl/src/curl_hmac.h6
-rw-r--r--libs/libcurl/src/curl_krb5.h8
-rw-r--r--libs/libcurl/src/curl_memory.h14
-rw-r--r--libs/libcurl/src/curl_memrchr.c8
-rw-r--r--libs/libcurl/src/curl_memrchr.h6
-rw-r--r--libs/libcurl/src/curl_multibyte.c162
-rw-r--r--libs/libcurl/src/curl_ntlm_core.c61
-rw-r--r--libs/libcurl/src/curl_printf.h24
-rw-r--r--libs/libcurl/src/curl_range.c33
-rw-r--r--libs/libcurl/src/curl_rtmp.c60
-rw-r--r--libs/libcurl/src/curl_sasl.c108
-rw-r--r--libs/libcurl/src/curl_sasl.h2
-rw-r--r--libs/libcurl/src/curl_setup.h296
-rw-r--r--libs/libcurl/src/curl_setup_once.h162
-rw-r--r--libs/libcurl/src/curl_sha256.h2
-rw-r--r--libs/libcurl/src/curl_sha512_256.c83
-rw-r--r--libs/libcurl/src/curl_sspi.c16
-rw-r--r--libs/libcurl/src/curl_sspi.h259
-rw-r--r--libs/libcurl/src/curl_threads.c39
-rw-r--r--libs/libcurl/src/curl_threads.h4
-rw-r--r--libs/libcurl/src/curl_trc.c394
-rw-r--r--libs/libcurl/src/curl_trc.h151
-rw-r--r--libs/libcurl/src/curlx/base64.c (renamed from libs/libcurl/src/base64.c)72
-rw-r--r--libs/libcurl/src/curlx/base64.h (renamed from libs/libcurl/src/curl_base64.h)21
-rw-r--r--libs/libcurl/src/curlx/curlx.h (renamed from libs/libcurl/src/curlx.h)33
-rw-r--r--libs/libcurl/src/curlx/dynbuf.c (renamed from libs/libcurl/src/dynbuf.c)66
-rw-r--r--libs/libcurl/src/curlx/dynbuf.h (renamed from libs/libcurl/src/dynbuf.h)54
-rw-r--r--libs/libcurl/src/curlx/inet_pton.c (renamed from libs/libcurl/src/inet_pton.c)67
-rw-r--r--libs/libcurl/src/curlx/inet_pton.h (renamed from libs/libcurl/src/inet_pton.h)16
-rw-r--r--libs/libcurl/src/curlx/multibyte.c360
-rw-r--r--libs/libcurl/src/curlx/multibyte.h (renamed from libs/libcurl/src/curl_multibyte.h)13
-rw-r--r--libs/libcurl/src/curlx/nonblock.c (renamed from libs/libcurl/src/nonblock.c)14
-rw-r--r--libs/libcurl/src/curlx/nonblock.h (renamed from libs/libcurl/src/nonblock.h)0
-rw-r--r--libs/libcurl/src/curlx/strparse.c303
-rw-r--r--libs/libcurl/src/curlx/strparse.h112
-rw-r--r--libs/libcurl/src/curlx/timediff.c (renamed from libs/libcurl/src/timediff.c)4
-rw-r--r--libs/libcurl/src/curlx/timediff.h (renamed from libs/libcurl/src/timediff.h)2
-rw-r--r--libs/libcurl/src/curlx/timeval.c (renamed from libs/libcurl/src/timeval.c)54
-rw-r--r--libs/libcurl/src/curlx/timeval.h (renamed from libs/libcurl/src/timeval.h)15
-rw-r--r--libs/libcurl/src/curlx/version_win32.c (renamed from libs/libcurl/src/version_win32.c)100
-rw-r--r--libs/libcurl/src/curlx/version_win32.h (renamed from libs/libcurl/src/version_win32.h)4
-rw-r--r--libs/libcurl/src/curlx/warnless.c (renamed from libs/libcurl/src/warnless.c)65
-rw-r--r--libs/libcurl/src/curlx/warnless.h (renamed from libs/libcurl/src/warnless.h)10
-rw-r--r--libs/libcurl/src/curlx/winapi.c135
-rw-r--r--libs/libcurl/src/curlx/winapi.h (renamed from libs/libcurl/src/strtok.h)17
-rw-r--r--libs/libcurl/src/cw-out.c106
-rw-r--r--libs/libcurl/src/cw-out.h2
-rw-r--r--libs/libcurl/src/cw-pause.c242
-rw-r--r--libs/libcurl/src/cw-pause.h40
-rw-r--r--libs/libcurl/src/dict.c41
-rw-r--r--libs/libcurl/src/dllmain.c13
-rw-r--r--libs/libcurl/src/doh.c632
-rw-r--r--libs/libcurl/src/doh.h55
-rw-r--r--libs/libcurl/src/dynhds.c7
-rw-r--r--libs/libcurl/src/dynhds.h10
-rw-r--r--libs/libcurl/src/easy.c248
-rw-r--r--libs/libcurl/src/easy_lock.h6
-rw-r--r--libs/libcurl/src/easygetopt.c4
-rw-r--r--libs/libcurl/src/easyoptions.c7
-rw-r--r--libs/libcurl/src/easyoptions.h2
-rw-r--r--libs/libcurl/src/escape.c53
-rw-r--r--libs/libcurl/src/escape.h4
-rw-r--r--libs/libcurl/src/fake_addrinfo.c210
-rw-r--r--libs/libcurl/src/fake_addrinfo.h54
-rw-r--r--libs/libcurl/src/file.c108
-rw-r--r--libs/libcurl/src/file.h11
-rw-r--r--libs/libcurl/src/fileinfo.c2
-rw-r--r--libs/libcurl/src/fileinfo.h2
-rw-r--r--libs/libcurl/src/fopen.c18
-rw-r--r--libs/libcurl/src/formdata.c648
-rw-r--r--libs/libcurl/src/formdata.h10
-rw-r--r--libs/libcurl/src/ftp.c1046
-rw-r--r--libs/libcurl/src/ftp.h17
-rw-r--r--libs/libcurl/src/ftplistparser.c1193
-rw-r--r--libs/libcurl/src/functypes.h1
-rw-r--r--libs/libcurl/src/getenv.c2
-rw-r--r--libs/libcurl/src/getinfo.c45
-rw-r--r--libs/libcurl/src/gopher.c8
-rw-r--r--libs/libcurl/src/hash.c279
-rw-r--r--libs/libcurl/src/hash.h40
-rw-r--r--libs/libcurl/src/headers.c22
-rw-r--r--libs/libcurl/src/hmac.c7
-rw-r--r--libs/libcurl/src/hostasyn.c123
-rw-r--r--libs/libcurl/src/hostip.c965
-rw-r--r--libs/libcurl/src/hostip.h214
-rw-r--r--libs/libcurl/src/hostip4.c28
-rw-r--r--libs/libcurl/src/hostip6.c32
-rw-r--r--libs/libcurl/src/hostsyn.c104
-rw-r--r--libs/libcurl/src/hsts.c121
-rw-r--r--libs/libcurl/src/hsts.h4
-rw-r--r--libs/libcurl/src/http.c1741
-rw-r--r--libs/libcurl/src/http.h96
-rw-r--r--libs/libcurl/src/http1.c39
-rw-r--r--libs/libcurl/src/http1.h2
-rw-r--r--libs/libcurl/src/http2.c430
-rw-r--r--libs/libcurl/src/http2.h21
-rw-r--r--libs/libcurl/src/http_aws_sigv4.c797
-rw-r--r--libs/libcurl/src/http_aws_sigv4.h14
-rw-r--r--libs/libcurl/src/http_chunks.c37
-rw-r--r--libs/libcurl/src/http_chunks.h6
-rw-r--r--libs/libcurl/src/http_digest.c10
-rw-r--r--libs/libcurl/src/http_negotiate.c25
-rw-r--r--libs/libcurl/src/http_ntlm.c29
-rw-r--r--libs/libcurl/src/http_proxy.c170
-rw-r--r--libs/libcurl/src/http_proxy.h6
-rw-r--r--libs/libcurl/src/httpsrr.c213
-rw-r--r--libs/libcurl/src/httpsrr.h86
-rw-r--r--libs/libcurl/src/idn.c8
-rw-r--r--libs/libcurl/src/if2ip.c7
-rw-r--r--libs/libcurl/src/imap.c780
-rw-r--r--libs/libcurl/src/imap.h59
-rw-r--r--libs/libcurl/src/inet_ntop.c25
-rw-r--r--libs/libcurl/src/inet_ntop.h17
-rw-r--r--libs/libcurl/src/krb5.c115
-rw-r--r--libs/libcurl/src/ldap.c168
-rw-r--r--libs/libcurl/src/libcurl.def2
-rw-r--r--libs/libcurl/src/llist.c69
-rw-r--r--libs/libcurl/src/llist.h4
-rw-r--r--libs/libcurl/src/macos.c2
-rw-r--r--libs/libcurl/src/md4.c26
-rw-r--r--libs/libcurl/src/md5.c37
-rw-r--r--libs/libcurl/src/memdebug.c74
-rw-r--r--libs/libcurl/src/memdebug.h92
-rw-r--r--libs/libcurl/src/mime.c111
-rw-r--r--libs/libcurl/src/mprintf.c839
-rw-r--r--libs/libcurl/src/mqtt.c314
-rw-r--r--libs/libcurl/src/mqtt.h32
-rw-r--r--libs/libcurl/src/multi.c1711
-rw-r--r--libs/libcurl/src/multi_ev.c637
-rw-r--r--libs/libcurl/src/multi_ev.h83
-rw-r--r--libs/libcurl/src/multihandle.h47
-rw-r--r--libs/libcurl/src/multiif.h49
-rw-r--r--libs/libcurl/src/netrc.c268
-rw-r--r--libs/libcurl/src/netrc.h17
-rw-r--r--libs/libcurl/src/noproxy.c23
-rw-r--r--libs/libcurl/src/openldap.c343
-rw-r--r--libs/libcurl/src/optiontable.pl6
-rw-r--r--libs/libcurl/src/parsedate.c56
-rw-r--r--libs/libcurl/src/pingpong.c73
-rw-r--r--libs/libcurl/src/pingpong.h15
-rw-r--r--libs/libcurl/src/pop3.c445
-rw-r--r--libs/libcurl/src/pop3.h63
-rw-r--r--libs/libcurl/src/progress.c96
-rw-r--r--libs/libcurl/src/progress.h7
-rw-r--r--libs/libcurl/src/psl.c4
-rw-r--r--libs/libcurl/src/psl.h2
-rw-r--r--libs/libcurl/src/rand.c13
-rw-r--r--libs/libcurl/src/rename.c14
-rw-r--r--libs/libcurl/src/request.c55
-rw-r--r--libs/libcurl/src/request.h34
-rw-r--r--libs/libcurl/src/rtsp.c295
-rw-r--r--libs/libcurl/src/rtsp.h31
-rw-r--r--libs/libcurl/src/select.c65
-rw-r--r--libs/libcurl/src/select.h15
-rw-r--r--libs/libcurl/src/sendf.c76
-rw-r--r--libs/libcurl/src/sendf.h5
-rw-r--r--libs/libcurl/src/setopt.c494
-rw-r--r--libs/libcurl/src/setup-vms.h42
-rw-r--r--libs/libcurl/src/setup-win32.h17
-rw-r--r--libs/libcurl/src/sha256.c52
-rw-r--r--libs/libcurl/src/share.c51
-rw-r--r--libs/libcurl/src/share.h15
-rw-r--r--libs/libcurl/src/sigpipe.h2
-rw-r--r--libs/libcurl/src/smb.c406
-rw-r--r--libs/libcurl/src/smb.h24
-rw-r--r--libs/libcurl/src/smtp.c575
-rw-r--r--libs/libcurl/src/smtp.h54
-rw-r--r--libs/libcurl/src/socketpair.c35
-rw-r--r--libs/libcurl/src/socketpair.h19
-rw-r--r--libs/libcurl/src/socks.c78
-rw-r--r--libs/libcurl/src/socks_gssapi.c26
-rw-r--r--libs/libcurl/src/socks_sspi.c22
-rw-r--r--libs/libcurl/src/speedcheck.c2
-rw-r--r--libs/libcurl/src/speedcheck.h2
-rw-r--r--libs/libcurl/src/splay.c4
-rw-r--r--libs/libcurl/src/splay.h2
-rw-r--r--libs/libcurl/src/strcase.c65
-rw-r--r--libs/libcurl/src/strdup.c7
-rw-r--r--libs/libcurl/src/strequal.c88
-rw-r--r--libs/libcurl/src/strerror.c166
-rw-r--r--libs/libcurl/src/strerror.h3
-rw-r--r--libs/libcurl/src/strtok.c68
-rw-r--r--libs/libcurl/src/strtoofft.c240
-rw-r--r--libs/libcurl/src/strtoofft.h54
-rw-r--r--libs/libcurl/src/system_win32.c23
-rw-r--r--libs/libcurl/src/system_win32.h2
-rw-r--r--libs/libcurl/src/telnet.c266
-rw-r--r--libs/libcurl/src/tftp.c159
-rw-r--r--libs/libcurl/src/transfer.c72
-rw-r--r--libs/libcurl/src/transfer.h13
-rw-r--r--libs/libcurl/src/uint-bset.c238
-rw-r--r--libs/libcurl/src/uint-bset.h114
-rw-r--r--libs/libcurl/src/uint-hash.c246
-rw-r--r--libs/libcurl/src/uint-hash.h68
-rw-r--r--libs/libcurl/src/uint-spbset.c273
-rw-r--r--libs/libcurl/src/uint-spbset.h99
-rw-r--r--libs/libcurl/src/uint-table.c214
-rw-r--r--libs/libcurl/src/uint-table.h101
-rw-r--r--libs/libcurl/src/url.c1173
-rw-r--r--libs/libcurl/src/url.h25
-rw-r--r--libs/libcurl/src/urlapi-int.h2
-rw-r--r--libs/libcurl/src/urlapi.c878
-rw-r--r--libs/libcurl/src/urldata.h334
-rw-r--r--libs/libcurl/src/vauth/cleartext.c17
-rw-r--r--libs/libcurl/src/vauth/cram.c18
-rw-r--r--libs/libcurl/src/vauth/digest.c120
-rw-r--r--libs/libcurl/src/vauth/digest_sspi.c77
-rw-r--r--libs/libcurl/src/vauth/gsasl.c14
-rw-r--r--libs/libcurl/src/vauth/krb5_gssapi.c31
-rw-r--r--libs/libcurl/src/vauth/krb5_sspi.c43
-rw-r--r--libs/libcurl/src/vauth/ntlm.c142
-rw-r--r--libs/libcurl/src/vauth/ntlm.h143
-rw-r--r--libs/libcurl/src/vauth/ntlm_sspi.c40
-rw-r--r--libs/libcurl/src/vauth/oauth2.c14
-rw-r--r--libs/libcurl/src/vauth/spnego_gssapi.c43
-rw-r--r--libs/libcurl/src/vauth/spnego_sspi.c50
-rw-r--r--libs/libcurl/src/vauth/vauth.c28
-rw-r--r--libs/libcurl/src/vauth/vauth.h8
-rw-r--r--libs/libcurl/src/version.c43
-rw-r--r--libs/libcurl/src/vquic/curl_msh3.c177
-rw-r--r--libs/libcurl/src/vquic/curl_msh3.h2
-rw-r--r--libs/libcurl/src/vquic/curl_ngtcp2.c667
-rw-r--r--libs/libcurl/src/vquic/curl_ngtcp2.h7
-rw-r--r--libs/libcurl/src/vquic/curl_osslq.c337
-rw-r--r--libs/libcurl/src/vquic/curl_osslq.h4
-rw-r--r--libs/libcurl/src/vquic/curl_quiche.c350
-rw-r--r--libs/libcurl/src/vquic/curl_quiche.h2
-rw-r--r--libs/libcurl/src/vquic/vquic-tls.c263
-rw-r--r--libs/libcurl/src/vquic/vquic-tls.h33
-rw-r--r--libs/libcurl/src/vquic/vquic.c141
-rw-r--r--libs/libcurl/src/vquic/vquic.h13
-rw-r--r--libs/libcurl/src/vquic/vquic_int.h9
-rw-r--r--libs/libcurl/src/vssh/curl_path.c42
-rw-r--r--libs/libcurl/src/vssh/curl_path.h4
-rw-r--r--libs/libcurl/src/vssh/libssh.c1714
-rw-r--r--libs/libcurl/src/vssh/libssh2.c2933
-rw-r--r--libs/libcurl/src/vssh/ssh.h73
-rw-r--r--libs/libcurl/src/vssh/wolfssh.c256
-rw-r--r--libs/libcurl/src/vtls/bearssl.c181
-rw-r--r--libs/libcurl/src/vtls/bearssl.h2
-rw-r--r--libs/libcurl/src/vtls/cipher_suite.c16
-rw-r--r--libs/libcurl/src/vtls/cipher_suite.h2
-rw-r--r--libs/libcurl/src/vtls/gtls.c803
-rw-r--r--libs/libcurl/src/vtls/gtls.h35
-rw-r--r--libs/libcurl/src/vtls/hostcheck.c13
-rw-r--r--libs/libcurl/src/vtls/keylog.c39
-rw-r--r--libs/libcurl/src/vtls/keylog.h15
-rw-r--r--libs/libcurl/src/vtls/mbedtls.c408
-rw-r--r--libs/libcurl/src/vtls/mbedtls.h2
-rw-r--r--libs/libcurl/src/vtls/mbedtls_threadlock.c8
-rw-r--r--libs/libcurl/src/vtls/mbedtls_threadlock.h2
-rw-r--r--libs/libcurl/src/vtls/openssl.c1916
-rw-r--r--libs/libcurl/src/vtls/openssl.h51
-rw-r--r--libs/libcurl/src/vtls/rustls.c983
-rw-r--r--libs/libcurl/src/vtls/rustls.h2
-rw-r--r--libs/libcurl/src/vtls/schannel.c554
-rw-r--r--libs/libcurl/src/vtls/schannel.h10
-rw-r--r--libs/libcurl/src/vtls/schannel_int.h14
-rw-r--r--libs/libcurl/src/vtls/schannel_verify.c166
-rw-r--r--libs/libcurl/src/vtls/sectransp.c301
-rw-r--r--libs/libcurl/src/vtls/sectransp.h2
-rw-r--r--libs/libcurl/src/vtls/vtls.c952
-rw-r--r--libs/libcurl/src/vtls/vtls.h82
-rw-r--r--libs/libcurl/src/vtls/vtls_int.h88
-rw-r--r--libs/libcurl/src/vtls/vtls_scache.c1235
-rw-r--r--libs/libcurl/src/vtls/vtls_scache.h217
-rw-r--r--libs/libcurl/src/vtls/vtls_spack.c346
-rw-r--r--libs/libcurl/src/vtls/vtls_spack.h43
-rw-r--r--libs/libcurl/src/vtls/wolfssl.c1605
-rw-r--r--libs/libcurl/src/vtls/wolfssl.h61
-rw-r--r--libs/libcurl/src/vtls/x509asn1.c214
-rw-r--r--libs/libcurl/src/vtls/x509asn1.h18
-rw-r--r--libs/libcurl/src/ws.c719
-rw-r--r--libs/libcurl/src/ws.h49
-rw-r--r--libs/libjson/src/JSONChildren.cpp2
-rw-r--r--libs/libjson/src/JSONDebug.cpp2
-rw-r--r--libs/libjson/src/JSONIterators.cpp2
-rw-r--r--libs/libjson/src/JSONMemory.cpp2
-rw-r--r--libs/libjson/src/JSONNode.cpp2
-rw-r--r--libs/libjson/src/JSONNode_Mutex.cpp2
-rw-r--r--libs/libjson/src/JSONWorker.cpp2
-rw-r--r--libs/libjson/src/JSONWriter.cpp2
-rw-r--r--libs/libjson/src/internalJSONNode.cpp2
-rw-r--r--libs/libjson/src/internalJSONNode.h4
-rw-r--r--libs/libjson/src/libJSON.cpp2
-rw-r--r--libs/libjson/src/stdafx.cxx25
-rw-r--r--libs/libjson/src/stdafx.h2
-rw-r--r--libs/libsignal/src/stdafx.cxx2
-rw-r--r--libs/libsignal/src/stdafx.h2
-rw-r--r--libs/litehtml/include/litehtml.h1
-rw-r--r--libs/litehtml/include/litehtml/codepoint.h4
-rw-r--r--libs/litehtml/include/litehtml/css_parser.h1
-rw-r--r--libs/litehtml/include/litehtml/css_properties.h111
-rw-r--r--libs/litehtml/include/litehtml/css_selector.h3
-rw-r--r--libs/litehtml/include/litehtml/css_tokenizer.h31
-rw-r--r--libs/litehtml/include/litehtml/document.h25
-rw-r--r--libs/litehtml/include/litehtml/document_container.h9
-rw-r--r--libs/litehtml/include/litehtml/el_cdata.h2
-rw-r--r--libs/litehtml/include/litehtml/el_comment.h2
-rw-r--r--libs/litehtml/include/litehtml/el_image.h1
-rw-r--r--libs/litehtml/include/litehtml/el_script.h2
-rw-r--r--libs/litehtml/include/litehtml/el_space.h1
-rw-r--r--libs/litehtml/include/litehtml/el_style.h3
-rw-r--r--libs/litehtml/include/litehtml/el_text.h3
-rw-r--r--libs/litehtml/include/litehtml/element.h13
-rw-r--r--libs/litehtml/include/litehtml/encodings.h1
-rw-r--r--libs/litehtml/include/litehtml/flex_item.h2
-rw-r--r--libs/litehtml/include/litehtml/font_description.h45
-rw-r--r--libs/litehtml/include/litehtml/gradient.h11
-rw-r--r--libs/litehtml/include/litehtml/html.h24
-rw-r--r--libs/litehtml/include/litehtml/html_microsyntaxes.h20
-rw-r--r--libs/litehtml/include/litehtml/html_tag.h4
-rw-r--r--libs/litehtml/include/litehtml/iterators.h3
-rw-r--r--libs/litehtml/include/litehtml/line_box.h36
-rw-r--r--libs/litehtml/include/litehtml/media_query.h10
-rw-r--r--libs/litehtml/include/litehtml/render_item.h9
-rw-r--r--libs/litehtml/include/litehtml/string_id.h15
-rw-r--r--libs/litehtml/include/litehtml/style.h24
-rw-r--r--libs/litehtml/include/litehtml/stylesheet.h3
-rw-r--r--libs/litehtml/include/litehtml/tstring_view.h2
-rw-r--r--libs/litehtml/include/litehtml/types.h163
-rw-r--r--libs/litehtml/include/litehtml/url.h2
-rw-r--r--libs/litehtml/include/litehtml/url_path.h2
-rw-r--r--libs/litehtml/include/litehtml/web_color.h3
-rw-r--r--libs/litehtml/src/background.cpp5
-rw-r--r--libs/litehtml/src/css_parser.cpp92
-rw-r--r--libs/litehtml/src/css_properties.cpp126
-rw-r--r--libs/litehtml/src/css_selector.cpp32
-rw-r--r--libs/litehtml/src/css_tokenizer.cpp39
-rw-r--r--libs/litehtml/src/document.cpp159
-rw-r--r--libs/litehtml/src/document_container.cpp2
-rw-r--r--libs/litehtml/src/el_anchor.cpp2
-rw-r--r--libs/litehtml/src/el_base.cpp4
-rw-r--r--libs/litehtml/src/el_body.cpp1
-rw-r--r--libs/litehtml/src/el_break.cpp1
-rw-r--r--libs/litehtml/src/el_cdata.cpp1
-rw-r--r--libs/litehtml/src/el_comment.cpp1
-rw-r--r--libs/litehtml/src/el_div.cpp2
-rw-r--r--libs/litehtml/src/el_font.cpp2
-rw-r--r--libs/litehtml/src/el_image.cpp4
-rw-r--r--libs/litehtml/src/el_link.cpp4
-rw-r--r--libs/litehtml/src/el_para.cpp1
-rw-r--r--libs/litehtml/src/el_script.cpp1
-rw-r--r--libs/litehtml/src/el_style.cpp14
-rw-r--r--libs/litehtml/src/el_table.cpp79
-rw-r--r--libs/litehtml/src/el_td.cpp2
-rw-r--r--libs/litehtml/src/el_text.cpp3
-rw-r--r--libs/litehtml/src/el_title.cpp2
-rw-r--r--libs/litehtml/src/el_tr.cpp2
-rw-r--r--libs/litehtml/src/element.cpp6
-rw-r--r--libs/litehtml/src/encodings.cpp4
-rw-r--r--libs/litehtml/src/flex_item.cpp4
-rw-r--r--libs/litehtml/src/flex_line.cpp2
-rw-r--r--libs/litehtml/src/formatting_context.cpp1
-rw-r--r--libs/litehtml/src/gumbo/CMakeLists.txt2
-rw-r--r--libs/litehtml/src/gumbo/char_ref.c2
-rw-r--r--libs/litehtml/src/gumbo/char_ref.rl2
-rw-r--r--libs/litehtml/src/gumbo/include/gumbo.h5
-rw-r--r--libs/litehtml/src/gumbo/include/gumbo/tag_enum.h1
-rw-r--r--libs/litehtml/src/gumbo/include/gumbo/tag_gperf.h352
-rw-r--r--libs/litehtml/src/gumbo/include/gumbo/tag_sizes.h2
-rw-r--r--libs/litehtml/src/gumbo/include/gumbo/tag_strings.h1
-rw-r--r--libs/litehtml/src/gumbo/parser.c218
-rw-r--r--libs/litehtml/src/html_microsyntaxes.cpp1
-rw-r--r--libs/litehtml/src/html_tag.cpp33
-rw-r--r--libs/litehtml/src/iterators.cpp3
-rw-r--r--libs/litehtml/src/line_box.cpp404
-rw-r--r--libs/litehtml/src/media_query.cpp36
-rw-r--r--libs/litehtml/src/render_block.cpp5
-rw-r--r--libs/litehtml/src/render_block_context.cpp1
-rw-r--r--libs/litehtml/src/render_flex.cpp2
-rw-r--r--libs/litehtml/src/render_image.cpp3
-rw-r--r--libs/litehtml/src/render_inline_context.cpp7
-rw-r--r--libs/litehtml/src/render_item.cpp65
-rw-r--r--libs/litehtml/src/render_table.cpp1
-rw-r--r--libs/litehtml/src/string_id.cpp8
-rw-r--r--libs/litehtml/src/style.cpp223
-rw-r--r--libs/litehtml/src/stylesheet.cpp12
-rw-r--r--libs/litehtml/src/url.cpp1
-rw-r--r--libs/litehtml/src/utf8_strings.cpp3
-rw-r--r--libs/litehtml/src/web_color.cpp22
-rw-r--r--libs/mTextControl/src/stdafx.cxx2
-rw-r--r--libs/sqlite3/src/sqlite3.c10265
-rw-r--r--libs/sqlite3/src/sqlite3.h338
-rw-r--r--libs/win32/mir_app.libbin301558 -> 310302 bytes
-rw-r--r--libs/win32/mir_core.libbin499650 -> 501354 bytes
-rw-r--r--libs/win64/mir_app.libbin301606 -> 310522 bytes
-rw-r--r--libs/win64/mir_core.libbin504964 -> 506684 bytes
453 files changed, 41982 insertions, 33170 deletions
diff --git a/libs/freeimage/src/FreeImage/MultiPage.cpp b/libs/freeimage/src/FreeImage/MultiPage.cpp
index 2d9affb21e..5194a87de6 100644
--- a/libs/freeimage/src/FreeImage/MultiPage.cpp
+++ b/libs/freeimage/src/FreeImage/MultiPage.cpp
@@ -320,18 +320,18 @@ FreeImage_OpenMultiBitmap(FREE_IMAGE_FORMAT fif, const char *filename, BOOL crea
}
FIMULTIBITMAP * DLL_CALLCONV
-FreeImage_OpenMultiBitmapU(FREE_IMAGE_FORMAT fif, const wchar_t *filename, BOOL create_new, BOOL read_only, BOOL keep_cache_in_memory, int flags) {
-
- // convert to single character - no national chars in extensions
- char *extension = (char *)malloc(wcslen(filename)+1);
- unsigned int i=0;
- for (; i < wcslen(filename); i++) // convert 16-bit to 8-bit
- extension[i] = (char)(filename[i] & 0x00FF);
- // set terminating 0
- extension[i]=0;
- FIMULTIBITMAP *fRet = FreeImage_OpenMultiBitmap(fif, extension, create_new, read_only, keep_cache_in_memory, flags);
- free(extension);
+FreeImage_OpenMultiBitmapU(FREE_IMAGE_FORMAT fif, const wchar_t *filename, BOOL create_new, BOOL read_only, BOOL, int flags) {
+ if (!create_new && _waccess(filename, 0))
+ return NULL;
+ FreeImageIO io;
+ SetDefaultIO(&io);
+ FILE *f = _wfopen(filename, read_only ? L"rb" : L"wb");
+ if (!f)
+ return NULL;
+
+ auto *fRet = FreeImage_OpenMultiBitmapFromHandle(fif, &io, f, flags);
+ fclose(f);
return fRet;
}
diff --git a/libs/libcurl/docs/COPYING b/libs/libcurl/docs/COPYING
index 5f6dc0aef2..40fabe1a30 100644
--- a/libs/libcurl/docs/COPYING
+++ b/libs/libcurl/docs/COPYING
@@ -1,6 +1,6 @@
COPYRIGHT AND PERMISSION NOTICE
-Copyright (c) 1996 - 2024, Daniel Stenberg, <daniel@haxx.se>, and many
+Copyright (c) 1996 - 2025, Daniel Stenberg, <daniel@haxx.se>, and many
contributors, see the THANKS file.
All rights reserved.
diff --git a/libs/libcurl/docs/RELEASE-NOTES b/libs/libcurl/docs/RELEASE-NOTES
index ef4c109d18..c42a6b2113 100644
--- a/libs/libcurl/docs/RELEASE-NOTES
+++ b/libs/libcurl/docs/RELEASE-NOTES
@@ -1,291 +1,55 @@
-curl and libcurl 8.11.0
+curl and libcurl 8.14.1
- Public curl releases: 262
- Command line options: 266
- curl_easy_setopt() options: 306
- Public functions in libcurl: 94
- Contributors: 3267
+ Public curl releases: 268
+ Command line options: 269
+ curl_easy_setopt() options: 308
+ Public functions in libcurl: 96
+ Contributors: 3431
This release includes the following changes:
- o curl: --create-dirs works for --dump-header as well [4]
- o gtls: Add P12 format support [9]
- o ipfs: add options to disable [8]
- o TLS: TLSv1.3 earlydata support for curl [140]
- o WebSockets: make support official (non-experimental) [106]
This release includes the following bugfixes:
- o alt-svc: honor data->state.httpwant [19]
- o altsvc: avoid using local buffer and memcpy [124]
- o asyn-ares: remove typecast, fix expire [113]
- o autotools: add support for 'unity' builds, enable in CI [15]
- o bearssl: avoid strpcy() when generating TLS version log message [120]
- o bearssl: improved session handling, test exceptions [233]
- o bufq: unwrite fix [121]
- o build: add `ldap` to `libcurl.pc` `Requires:` [139]
- o build: add pytest targets [71]
- o build: clarify CA embed is for curl tool, mark default, improve summary [72]
- o build: detect and use `_setmode()` with Cygwin/MSYS, also use on Windows [136]
- o build: disable warning `-Wunreachable-code-break` [195]
- o build: fix clang-cl builds, add CI job [254]
- o build: fix cross-compile check for poll with bionic [70]
- o build: fix possible `-Wformat-overflow` in lib557 [85]
- o build: limit arc4random detection to no-SSL configs [43]
- o build: show if CA bundle to embed was found [83]
- o build: tidy up and improve versioned-symbols options [5]
- o build: tidy up deprecation suppression, enable warnings for clang [12]
- o certs: add missing `-CAcreateserial` option for LibreSSL [247]
- o checksrc: add check for spaces around logical AND operators [220]
- o checksrc: Added checks for colon operator in ternary expressions [77]
- o checksrc: check for spaces around '?', '>' and '<' [46]
- o ci: dump `curl_config.h` to log in all jobs [199]
- o CI: run with standard mod_http2 [214]
- o cmake, Makefile.mk: use -isystem for headers, silence BearSSL issues [37]
- o cmake/FindCares: fix version detection for c-ares 1.34.1 [209]
- o cmake/FindNGTCP2: use library path as hint for finding crypto module [40]
- o cmake: add missed variable to comment
- o cmake: add native `pkg-config` detection for mbedTLS, MSH3, Quiche, Rustls, wolfSSL [149]
- o cmake: allow building tests in unity mode [31]
- o cmake: apply `WIN32_LEAN_AND_MEAN` to all feature checks
- o cmake: avoid setting `BUILD_TESTING` [179]
- o cmake: clear package version after `pkg-config` detection [207]
- o cmake: delete unused NEED_LBER_H, HAVE_LDAP_H [38]
- o cmake: detect `HAVE_NETINET_IN6_H`, `HAVE_CLOSESOCKET_CAMEL`, `HAVE_PROTO_BSDSOCKET_H` [132]
- o cmake: detect GNU GSS [127]
- o cmake: disable default OpenSSL if BearSSL, GnuTLS or Rustls is enabled [44]
- o cmake: do not propagate unused `HAVE_GSSAPI_GSSAPI_KRB5_H` to C [131]
- o cmake: document `-D` and env build options [208]
- o cmake: drop obsolete items from `TODO` and `INSTALL-CMAKE` [228]
- o cmake: drop redundant assignments [49]
- o cmake: drop redundant zlib var, rename function (internals) [50]
- o cmake: expand CURL_USE_PKGCONFIG to non-cross MINGW [13]
- o cmake: fix broken dependency chain for cmdline-opts, tidy-ups [11]
- o cmake: fix compile warnings for clang-cl [218]
- o cmake: fix missing spacing in log message [205]
- o cmake: limit `CURL_STATIC_CRT` to MSVC [217]
- o cmake: make `test-ci` target skip building dependencies [88]
- o cmake: mark as advanced some internal Find* variables [212]
- o cmake: readd `generate-curl.1` dependency for `src` just in case [86]
- o cmake: rename LDAP dependency config variables to match Find modules [144]
- o cmake: replace `check_include_file_concat()` for LDAP and GSS detection [143]
- o cmake: replace `CURL_*_DIR` with `{PROJECT,CMAKE_CURRENT}_*_DIR` [211]
- o cmake: require quictls (or fork) when using msh3 on non-Windows [14]
- o cmake: separate target for examples, optimize CI, fix fallouts [16]
- o cmake: set version for `project()` and add CPack support [123]
- o cmake: stop adding dependency headers to global `CMAKE_REQUIRED_INCLUDES` [146]
- o cmake: sync torture test parallelism with autotools [35]
- o cmake: tidy up `CURL_DISABLE_FORM_API` initialization [225]
- o cmake: tidy up and shorten symbol hiding initialization [213]
- o cmake: tidy up line order
- o cmake: tidy up picky warning initialization [215]
- o cmake: tidy-ups and rebase fixups [191]
- o cmake: tweaks around debug mode and hidden symbols [194]
- o cmake: untangle feature detection interdependencies [198]
- o cmake: use `list(APPEND)` on `CURL_INCLUDES` [223]
- o cmake: use OpenSSL for LDAP detection only if available [102]
- o cmake: use the `BSD` variable [210]
- o config: rename the OS define to CURL_OS to reduce collision risk [256]
- o configure: add GSS to `libcurl.pc` `Depends:` [126]
- o configure: catch Apple in more target triplets [6]
- o configure: drop duplicate feature checks for `poll()`, `if_nametoindex()` [135]
- o configure: drop unused bare `socket.h` detection [133]
- o configure: improve help string for some options [78]
- o conncache: find bundle again in case it is removed [129]
- o conncache: more efficient implementation of cpool_remove_bundle [176]
- o cookie: overhaul and cleanup [142]
- o curl-rustls.m4: set linker flags to allow rustls build on macos [186]
- o curl.h: remove the struct pointer for CURL/CURLSH/CURLM typedefs [174]
- o curl: add build options for safe/no CA bundle search (Windows) [26]
- o curl: detect ECH support dynamically, not at build time [230]
- o curl_addrinfo: support operating systems with only getaddrinfo(3) [239]
- o curl_multi_perform.md: fix typo [224]
- o curl_trc: fix build with verbose messages disabled [79]
- o curl_url_set.md: document HOST handling when URL is parsed [2]
- o curl_ws_recv.md: the 'meta' pointer is only returned on success [221]
- o curl_ws_recv: return recv 0 and point meta to NULL on all errors [222]
- o CURLMOPT_PIPELINING.md: clarify that CURLPIPE_NOTHING is not default [54]
- o CURLOPT_APPEND.md: goes for SFTP as well [128]
- o CURLOPT_HEADERFUNCTION.md: do not modify the passed in buffer [107]
- o DISABLED: disable test 1060 with hyper [154]
- o DISTROS: avoid use of "very"
- o Dockerfile: update Docker digest to d830561 [226]
- o docs/cmdline-opts: GnuTLS supports PKCS#11 URI in --cert option [101]
- o docs: clarify FTP over HTTP proxy functionality somewhat [203]
- o docs: fix a typo in some cipher options
- o ech: spelling, whitespace, say `--ech` default config [137]
- o ftp: fix 0-length last write on upload from stdin [76]
- o ftp: move listen handling to socket filter [183]
- o GHA: optimize test prereq steps [188]
- o gnutls: use session cache for QUIC [196]
- o hsts: avoid the local buffer and memcpy on lookup [125]
- o hsts: improve subdomain handling [158]
- o hsts: support "implied LWS" properly around max-age [229]
- o http2: auto reset stream on server eos [147]
- o http_aws_sigv4: avoid local buffer and strcpy [92]
- o INSTALL-CMAKE.md: mention focus on shared libraries [73]
- o INSTALL-CMAKE: fix punctuation and a typo
- o INSTALL.md: fix a typo that slipped in to RISC OS
- o json.md: cli-option `--json` is an alias of `--data-binary` [89]
- o lib, src, tests: added space around ternary expressions [56]
- o lib/cw-out: initialize 'flush_all' directly [62]
- o lib/src: white space edits to comply better with code style [47]
- o lib: avoid assigning 'result' temporarily [97]
- o lib: fix disabled-verbose-strings + enable-debug build warnings
- o lib: fix unity builds with BearSSL, MSH3, Quiche, OmniOS [32]
- o lib: move curl_path.[ch] into vssh/ [182]
- o lib: msnprintf tidy-ups [245]
- o lib: remove Curl_ prefix from static functions [202]
- o lib: remove function pointer typecasts for hmac/sha256/md5 [175]
- o lib: use bool/TRUE/FALSE properly [48]
- o libcurl/opts: improve phrasing for connection cap related options [145]
- o libssh.c: handle EGAINS during proto-connect correctly [23]
- o libssh2: delete duplicate `break` [190]
- o libssh2: put the readdir buffers into struct [170]
- o libssh2: use the Curl_* memory functions to avoid memdebug [22]
- o libssh2: use the filename buffer when getting the homedir [169]
- o libtests: generate the lib1521 atomically [148]
- o mbedTLS: fix handling of TLSv1.3 sessions [184]
- o mbedtls: handle session as blobs [234]
- o mbedtls: remove failf() use from mbedtls_random [255]
- o mk-lib1521: fix the long return code check [204]
- o mprintf: do not ignore length modifiers of `%o`, `%x`, `%X` [164]
- o mprintf: treat `%o` as unsigned, add tests for `%o`, `%x`, `%X` [162]
- o mqtt: fix mqtt.md wording and add clearer explanation [172]
- o multi.c: make stronger check for paused transfer before asserting [24]
- o multi.c: warn/assert on stall only without timer [80]
- o multi: avoid reading whole struct pointer from pointer [10]
- o multi: convert Curl_follow to static multi_follow [141]
- o multi: make curl_multi_cleanup invalidate magic latter [159]
- o multi: make multi_handle_timeout use the connect timeout [98]
- o multi: split multi_runsingle into sub functions [200]
- o negotiate: conditional check around GSS & SSL specific code [1]
- o netrc: cache the netrc file in memory [138]
- o ngtcp2: do not loop on recv [251]
- o ngtcp2: set max window size to 10x of initial (128KB) [232]
- o openssl quic: populate x509 store before handshake [117]
- o openssl: convert a memcpy to dynbuf use [57]
- o openssl: extend the OpenSSL error messages [238]
- o openssl: improve retries on shutdown [151]
- o openssl: remove two strcpy() calls [64]
- o OS400: don't delete source files when building with debug [235]
- o packages/OS400/curlmain: remove the strncpy calls [155]
- o processhelp.pm: improve taskkill calls (Windows) [52]
- o pytest: fix run against multissl curl [236]
- o pytest: improve pytest_07_42a reliability [118]
- o pytest: include `buildinfo.txt` in the output [189]
- o pytest: include curl version string and python platform in log [242]
- o pytest: show curl features and protocols [150]
- o quic: use send/recvmmsg when available [93]
- o quic: use the session cache with wolfSSL as well [231]
- o request: on shutdown send, proceed normally on timeout [18]
- o runtests.md: suggest a value for -j for torture tests
- o runtests: add comment for handle64 pathsep requirement
- o runtests: drop unused code for old/classic-mingw support [87]
- o runtests: pass single backslashes with Windows Perl [243]
- o runtests: use deterministic sort for `TESTINFO` lines [201]
- o schannel: fix TLS cert verification by IP SAN [253]
- o schannel: ignore error on recv beyond close notify [167]
- o schannel: reclassify extra-verbose schannel_recv messages [153]
- o select: use poll() if existing, avoid poll() with no sockets [75]
- o sendf: add condition to max-filesize check [3]
- o server/mqttd: fix two memory leaks [178]
- o setopt: avoid superfluous length checks before strcmp() [105]
- o setopt: return error for bad input to CURLOPT_RTSP_REQUEST [240]
- o setopt_cptr: make overflow check only done when needed [241]
- o singleuse: make `git grep` faster, add Apple `nm` support [109]
- o smb: do not redefine `getpid` on Windows [187]
- o smb: replace use of strcpy() with snprintf() [122]
- o socks_gssapi: switch to dynbuf from buffer with strcpy [42]
- o source: avoid use of 'very' in comments
- o src/lib: remove redundant ternary operators [244]
- o src: guard for double declaration of `curl_ca_embed` in unity builds [166]
- o sws: fix unused static function with `TCP_NODELAY` undefined [134]
- o telnet: avoid two strcpy() by pointing to the strings instead [99]
- o test1035: convert host name back to utf8 as should be [63]
- o test1515: add tracing and more debug info [119]
- o test1540: add debug logging [58]
- o test190: replace %FTPTIME2 with a fixed value [34]
- o test1915: add tracing and connect timeout [114]
- o test1915: remove wrong comment
- o test2502: add libtest debug tracing [60]
- o test504: fix handling on pending connect [59]
- o testrun: explicitly set proper IP address for stunnel listen/connect [61]
- o tests/http: fix ubuntu GnuTLS CI failures [161]
- o tests/scorecard: allow remote server test [171]
- o tests/server/util.c: remove use of strncpy [156]
- o tests/valgrind.pm: fix warnings with no valgrind report to show [25]
- o tests/valgrind.supp: remove a travis suppression, add a Debian [116]
- o tests: add and use `%PERL` variable to refer to the Perl binary [82]
- o tests: add codeset-utf8 as a feature [66]
- o tests: add file: tests with existing files [45]
- o tests: allow pytests to run in out-of-tree builds [192]
- o tests: capture stdin to get the vsftpd version number [165]
- o tests: change Python code style to pass ruff checks
- o tests: check http/2 and http/3 server responsiveness [28]
- o tests: delete duplicate macro check [53]
- o tests: enable additional ruff Python lint options
- o tests: fix `%POSIX_PWD` on native Windows Perl [111]
- o tests: fix callback signatures to please UndefinedBehaviorSanitizer [173]
- o tests: Fix FILEFORMAT <file name=""> directive [206]
- o tests: fix keyword for test1411
- o tests: fix shell quoting on native Windows Perl [110]
- o tests: fix some Python typing issues
- o tests: fixup `checkcmd` `PATH` on non-unixy platforms [108]
- o tests: improve mqtt server handling [27]
- o tests: introduce %CLIENT6IP-NB [67]
- o tests: let openssl generate random cert serials [91]
- o tests: libtests and unit tests need explicit #include memdebug [7]
- o tests: make precheck for HTTP on 127.0.0.1 into a feature [68]
- o tests: Only log warnings or worse by default in smbserver [33]
- o tests: postcheck is now in verify [69]
- o tests: remove all valgrind disable instructions [21]
- o tests: remove debug requirement on 38 tests [100]
- o tests: remove the %FTPTIME3 variable [41]
- o tests: replace `%PWD` with `%FILE_PWD` for `file://` [84]
- o tests: replace `%PWD` with `%SSH_PWD` in SCP/SFTP tests [112]
- o tests: replace hard-coded `/dev/null` with variable [81]
- o tests: simplify `pathhelp.pm`, avoid using external tools [95]
- o tests: speed up builds with single-binary test bundles [29]
- o tests: testrunner fairness [39]
- o tests: testrunner reliability improvements [55]
- o tests: use '-4' where needed [17]
- o tests: use a set for several of the curl_props [249]
- o tftp: avoid two memcpy/strcpy [94]
- o tidy-up: rename CURL_WINDOWS_APP to CURL_WINDOWS_UWP [36]
- o tls: avoid abusing CURLE_SSL_ENGINE_INITFAILED [246]
- o tool: support --show-headers AND --remote-header-name [103]
- o tool_doswin: simplify; remove unused options and strncpy calls [65]
- o tool_getparam: drop unused time() call [177]
- o tool_getparam: replace two uses of strncpy(), ban strncpy [157]
- o tool_operate: make --skip-existing work for --parallel [180]
- o tool_operate: reuse the schannel backend check [130]
- o tool_xattr: create the user.creator xattr attribute [197]
- o unit1307: tidy up Apple OS detection [252]
- o unit1660: fix unreachable code warning in no-SSL builds [30]
- o url: connection reuse on h3 connections [20]
- o url: use same credentials on redirect [181]
- o urlapi: drop unused header [51]
- o urlapi: normalize the IPv6 address [115]
- o version: minor cleanups [152]
- o version: say quictls in MSH3 builds [219]
- o vquic: fix compiler warning with gcc + MUSL [168]
- o vquic: recv_mmsg, use fewer, but larger buffers [250]
- o vtls: convert Curl_pin_peer_pubkey to use dynbuf [74]
- o vtls: convert pubkey_pem_to_der to use dynbuf [90]
- o warnless: remove curlx_sktosi and curlx_sitosk [104]
- o winbuild/README: consolidate command prompt section [193]
- o winbuild/README: document how to clean a build [163]
- o winbuild: add initial wolfSSL support [227]
- o winbuild: drop `gen_resp_file.bat` [248]
- o wolfssl: convert malloc + memcpys to dynbuf for cipher string [96]
- o wolfSSL: fix handling of TLSv1.3 sessions [185]
- o wolfssl: no more use of the OpenSSL API [216]
- o wolfssl: use old version API without openssl extra [160]
+ o asyn-thrdd: fix cleanup when RR fails due to OOM [20]
+ o autotools: recognize more Linux targets when setting `-D_GNU_SOURCE` [35]
+ o BUG-BOUNTY.md. mention the medium bounty amount in 2025 [5]
+ o cmake: fix missed version number for multi-pkg-config detections [14]
+ o cmdline-docs: mention HTTP resumed uploads to be shaky [21]
+ o curl: make -N handled correctly [34]
+ o curl: upload from '.' fix [9]
+ o dllmain: exclude from Cygwin builds [32]
+ o docs/tests: remove mention of hyper [23]
+ o docs: fix typos [12]
+ o ftp: fix teardown of DATA connection in done [31]
+ o http: fail early when rewind of input failed when following redirects [2]
+ o license: update some copyright links to curl.se [24]
+ o memanalyze.pl: fix getaddrinfo/freeaddrinfo checks [25]
+ o misc: fix spelling [15]
+ o misc: we write *an* IPv6 address [10]
+ o multi: fix add_handle resizing [3]
+ o spelling: 'a' vs 'an' [8]
+ o spelling: call it null-terminate consistently [6]
+ o test1510: fix expectation [19]
+ o tests: await portfile to be complete [1]
+ o tests: fix checks for https-mtls proto [30]
+ o tests: improve server start reliability [18]
+ o tests: move test docs into /docs [16]
+ o tests: re-enable 1510, document heimdal memleak [22]
+ o tests: test mtls also w/ clientAuth EKU only [28]
+ o tests: test mtls with --insecure [29]
+ o tls BIOs: handle BIO_CTRL_EOF correctly [33]
+ o tool_getparam: make --no-anyauth not be accepted [13]
+ o tool_getparam: refactored, simplified [4]
+ o tool_getparam: remove two nextarg NULL checks [11]
+ o VULN-DISCLOSURE-POLICY.md: the distros list wants <= 7 days embargo [26]
+ o wolfssl: fix sending of early data [7]
+ o ws: handle blocked sends better [27]
+ o ws: tests and fixes [17]
This release includes the following known bugs:
- See docs/KNOWN_BUGS (https://curl.se/docs/knownbugs.html)
+ See https://curl.se/docs/knownbugs.html
For all changes ever done in curl:
@@ -293,283 +57,58 @@ For all changes ever done in curl:
Planned upcoming removals include:
- o Hyper support
- o TLS libraries not supporting TLS 1.3
+ o Support for the msh3 HTTP/3 backend
+ o Supporting curl builds using VS2008
+ o The Secure Transport and BearSSL TLS backends
+ o The winbuild build system
+ o Windows CE support
- See https://curl.se/dev/deprecate.html for details
+ See https://curl.se/dev/deprecate.html
This release would not have looked like this without help, code, reports and
advice from friends like these:
- ad-chaos on github, Aki Sakurai, Baruch Siach, Chris Stubbs, Colton Willey,
- Dan Fandrich, Daniel Stenberg, Denis Goleshchikhin, Deniz Sökmen,
- dependabot[bot], Dylam De La Torre, edmcln, elvinasp on github,
- Emanuel Komínek, Gabriel Marin, Ian Spence, Jeroen Ooms, jkamp-aws on github,
- John Haugabook, Jonas 'Sortie' Termansen, Jon Rumsey, Kai Pastor, Kazuho Oku,
- koujaz on github, lomberd2 on github, MacKenzie, Marwan Yassini, Max Dymond,
- Michael Kaufmann, Montg0mery on github, Moritz Knüsel, Nemos2024 on github,
- newfunction, Nicolas George, Pavel Kropachev, Pierre-Etienne Meunier,
- ralfjunker on github, Rasmus Melchior Jacobsen, Ray Satiro, renovate[bot],
- Robert Maynard, Sebastian Walz, Sinkevich Artem, Stefan Eissing, Tal Regev,
- Tatsuhiro Tsujikawa, Tobias Bora, Tobias Wendorff, Venkat Krishna R,
- Viktor Szakats, Vollstrecker on github, vvb2060 on github,
- wxiaoguang on github, Yedaya Katsman, zjyhjqs
- (55 contributors)
+ Calvin Ruocco, Dan Fandrich, Daniel Stenberg, denandz on github,
+ Ethan Everett, Jacob Mealey, Jeremy Drake, Jeroen Ooms, John Bampton,
+ Kadambini Nema, Michael Kaufmann, Rasmus Melchior Jacobsen, Ray Satiro,
+ Samuel Henrique, Stefan Eissing, Viktor Szakats, x-xiang on github,
+ Yedaya Katsman, Yuyi Wang, z2_
+ (20 contributors)
References to bug reports and discussions on issues:
- [1] = https://curl.se/bug/?i=14938
- [2] = https://curl.se/bug/?i=14942
- [3] = https://curl.se/bug/?i=14958
- [4] = https://curl.se/bug/?i=14941
- [5] = https://curl.se/bug/?i=14818
- [6] = https://curl.se/bug/?i=14728
- [7] = https://curl.se/bug/?i=15007
- [8] = https://curl.se/bug/?i=14824
- [9] = https://curl.se/bug/?i=14991
- [10] = https://curl.se/bug/?i=15063
- [11] = https://curl.se/bug/?i=14883
- [12] = https://curl.se/bug/?i=14789
- [13] = https://curl.se/bug/?i=14658
- [14] = https://curl.se/bug/?i=15003
- [15] = https://curl.se/bug/?i=14815
- [16] = https://curl.se/bug/?i=14906
- [17] = https://curl.se/bug/?i=15060
- [18] = https://curl.se/bug/?i=14843
- [19] = https://curl.se/bug/?i=14966
- [20] = https://curl.se/bug/?i=14890
- [21] = https://curl.se/bug/?i=14983
- [22] = https://curl.se/bug/?i=14984
- [23] = https://curl.se/bug/?i=14982
- [24] = https://curl.se/bug/?i=14981
- [25] = https://curl.se/bug/?i=14977
- [26] = https://curl.se/bug/?i=14582
- [27] = https://curl.se/bug/?i=15059
- [28] = https://curl.se/bug/?i=15058
- [29] = https://curl.se/bug/?i=14772
- [30] = https://curl.se/bug/?i=14971
- [31] = https://curl.se/bug/?i=14765
- [32] = https://curl.se/bug/?i=14932
- [33] = https://curl.se/bug/?i=14950
- [34] = https://curl.se/bug/?i=15056
- [35] = https://curl.se/bug/?i=14969
- [36] = https://curl.se/bug/?i=14881
- [37] = https://curl.se/bug/?i=14763
- [38] = https://curl.se/bug/?i=14690
- [39] = https://curl.se/bug/?i=14967
- [40] = https://github.com/curl/curl-for-win/blob/8b8909e1206de1dcca356a8dd33eb1e4ffeea7fd/curl.sh#L289
- [41] = https://curl.se/bug/?i=15064
- [42] = https://curl.se/bug/?i=15057
- [43] = https://curl.se/bug/?i=14909
- [44] = https://curl.se/bug/?i=14828
- [45] = https://curl.se/bug/?i=15045
- [46] = https://curl.se/bug/?i=14921
- [47] = https://curl.se/bug/?i=14921
- [48] = https://curl.se/bug/?i=15123
- [49] = https://curl.se/bug/?i=14924
- [50] = https://curl.se/bug/?i=14918
- [51] = https://curl.se/bug/?i=14867
- [52] = https://curl.se/bug/?i=14959
- [53] = https://curl.se/bug/?i=14963
- [54] = https://curl.se/bug/?i=14961
- [55] = https://curl.se/bug/?i=14960
- [56] = https://curl.se/bug/?i=14912
- [57] = https://curl.se/bug/?i=15049
- [58] = https://curl.se/bug/?i=15055
- [59] = https://curl.se/bug/?i=15054
- [60] = https://curl.se/bug/?i=15053
- [61] = https://curl.se/bug/?i=15051
- [62] = https://curl.se/bug/?i=15044
- [63] = https://curl.se/bug/?i=15050
- [64] = https://curl.se/bug/?i=15052
- [65] = https://curl.se/bug/?i=15047
- [66] = https://curl.se/bug/?i=15039
- [67] = https://curl.se/bug/?i=15039
- [68] = https://curl.se/bug/?i=15039
- [69] = https://curl.se/bug/?i=15046
- [70] = https://curl.se/bug/?i=15013
- [71] = https://curl.se/bug/?i=15034
- [72] = https://curl.se/bug/?i=15035
- [73] = https://curl.se/bug/?i=14845
- [74] = https://curl.se/bug/?i=15126
- [75] = https://curl.se/bug/?i=15096
- [76] = https://curl.se/bug/?i=15101
- [77] = https://curl.se/bug/?i=14990
- [78] = https://curl.se/bug/?i=15033
- [79] = https://curl.se/bug/?i=15026
- [80] = https://curl.se/bug/?i=15028
- [81] = https://curl.se/bug/?i=15098
- [82] = https://curl.se/bug/?i=15097
- [83] = https://curl.se/bug/?i=15027
- [84] = https://curl.se/bug/?i=15090
- [85] = https://curl.se/bug/?i=15012
- [86] = https://curl.se/bug/?i=15088
- [87] = https://curl.se/bug/?i=15087
- [88] = https://curl.se/bug/?i=15001
- [89] = https://curl.se/bug/?i=15197
- [90] = https://curl.se/bug/?i=15126
- [91] = https://curl.se/bug/?i=15129
- [92] = https://curl.se/bug/?i=15200
- [93] = https://curl.se/bug/?i=14880
- [94] = https://curl.se/bug/?i=15199
- [95] = https://curl.se/bug/?i=15111
- [96] = https://curl.se/bug/?i=15124
- [97] = https://curl.se/bug/?i=15122
- [98] = https://curl.se/bug/?i=15100
- [99] = https://curl.se/bug/?i=15198
- [100] = https://curl.se/bug/?i=15114
- [101] = https://curl.se/bug/?i=15075
- [102] = https://curl.se/bug/?i=15077
- [103] = https://curl.se/bug/?i=15110
- [104] = https://curl.se/bug/?i=15153
- [105] = https://curl.se/bug/?i=15192
- [106] = https://curl.se/bug/?i=14936
- [107] = https://curl.se/bug/?i=15148
- [108] = https://curl.se/bug/?i=15106
- [109] = https://curl.se/bug/?i=15070
- [110] = https://curl.se/bug/?i=15105
- [111] = https://curl.se/bug/?i=15104
- [112] = https://curl.se/bug/?i=15103
- [113] = https://curl.se/bug/?i=15145
- [114] = https://curl.se/bug/?i=15107
- [115] = https://curl.se/bug/?i=15143
- [116] = https://curl.se/bug/?i=15142
- [117] = https://curl.se/bug/?i=15137
- [118] = https://curl.se/bug/?i=15138
- [119] = https://curl.se/bug/?i=15140
- [120] = https://curl.se/bug/?i=15194
- [121] = https://curl.se/bug/?i=15136
- [122] = https://curl.se/bug/?i=15196
- [123] = https://curl.se/bug/?i=15281
- [124] = https://curl.se/bug/?i=15191
- [125] = https://curl.se/bug/?i=15190
- [126] = https://curl.se/bug/?i=15177
- [127] = https://curl.se/bug/?i=15176
- [128] = https://curl.se/bug/?i=15181
- [129] = https://curl.se/bug/?i=15185
- [130] = https://curl.se/bug/?i=15323
- [131] = https://curl.se/bug/?i=15174
- [132] = https://curl.se/bug/?i=15172
- [133] = https://curl.se/bug/?i=15173
- [134] = https://curl.se/bug/?i=15171
- [135] = https://curl.se/bug/?i=15170
- [136] = https://curl.se/bug/?i=15169
- [137] = https://curl.se/bug/?i=15167
- [138] = https://curl.se/bug/?i=15248
- [139] = https://curl.se/bug/?i=15163
- [140] = https://curl.se/bug/?i=15211
- [141] = https://curl.se/bug/?i=15260
- [142] = https://curl.se/bug/?i=15247
- [143] = https://curl.se/bug/?i=15157
- [144] = https://curl.se/bug/?i=15255
- [145] = https://curl.se/bug/?i=15324
- [146] = https://curl.se/bug/?i=15252
- [147] = https://curl.se/bug/?i=15316
- [148] = https://curl.se/bug/?i=15258
- [149] = https://curl.se/bug/?i=15193
- [150] = https://curl.se/bug/?i=15452
- [151] = https://curl.se/bug/?i=15321
- [152] = https://curl.se/bug/?i=15393
- [153] = https://curl.se/bug/?i=14826
- [154] = https://curl.se/bug/?i=15319
- [155] = https://curl.se/bug/?i=15214
- [156] = https://curl.se/bug/?i=15213
- [157] = https://curl.se/bug/?i=15212
- [158] = https://curl.se/bug/?i=15210
- [159] = https://curl.se/bug/?i=15201
- [160] = https://curl.se/bug/?i=15205
- [161] = https://curl.se/bug/?i=15310
- [162] = https://curl.se/bug/?i=15348
- [163] = https://curl.se/bug/?i=15291
- [164] = https://curl.se/bug/?i=15348
- [165] = https://curl.se/bug/?i=15278
- [166] = https://curl.se/bug/?i=15307
- [167] = https://curl.se/bug/?i=15381
- [168] = https://curl.se/bug/?i=15303
- [169] = https://curl.se/bug/?i=15285
- [170] = https://curl.se/bug/?i=15285
- [171] = https://curl.se/bug/?i=15415
- [172] = https://curl.se/bug/?i=15451
- [173] = https://curl.se/bug/?i=15289
- [174] = https://curl.se/bug/?i=15289
- [175] = https://curl.se/bug/?i=15289
- [176] = https://curl.se/bug/?i=15292
- [177] = https://curl.se/bug/?i=15420
- [178] = https://curl.se/bug/?i=15290
- [179] = https://curl.se/bug/?i=15351
- [180] = https://curl.se/bug/?i=15261
- [181] = https://curl.se/bug/?i=15262
- [182] = https://curl.se/bug/?i=15284
- [183] = https://curl.se/bug/?i=14798
- [184] = https://curl.se/bug/?i=15245
- [185] = https://curl.se/bug/?i=15243
- [186] = https://curl.se/bug/?i=15175
- [187] = https://curl.se/bug/?i=15263
- [188] = https://curl.se/bug/?i=15275
- [189] = https://curl.se/bug/?i=15279
- [190] = https://curl.se/bug/?i=15384
- [191] = https://curl.se/bug/?i=15271
- [192] = https://curl.se/bug/?i=15257
- [193] = https://curl.se/bug/?i=15313
- [194] = https://curl.se/bug/?i=15414
- [195] = https://curl.se/bug/?i=15416
- [196] = https://curl.se/bug/?i=15265
- [197] = https://curl.se/bug/?i=15237
- [198] = https://curl.se/bug/?i=15164
- [199] = https://curl.se/bug/?i=15266
- [200] = https://curl.se/bug/?i=15418
- [201] = https://curl.se/bug/?i=15374
- [202] = https://curl.se/bug/?i=15419
- [203] = https://curl.se/bug/?i=15417
- [204] = https://curl.se/bug/?i=15372
- [205] = https://curl.se/bug/?i=15411
- [206] = https://curl.se/bug/?i=15371
- [207] = https://curl.se/bug/?i=15409
- [208] = https://curl.se/bug/?i=14885
- [209] = https://curl.se/bug/?i=15368
- [210] = https://curl.se/bug/?i=15367
- [211] = https://curl.se/bug/?i=15331
- [212] = https://curl.se/bug/?i=15407
- [213] = https://curl.se/bug/?i=15400
- [214] = https://curl.se/bug/?i=15353
- [215] = https://curl.se/bug/?i=15404
- [216] = https://curl.se/bug/?i=15480
- [217] = https://curl.se/bug/?i=15403
- [218] = https://curl.se/bug/?i=15337
- [219] = https://curl.se/bug/?i=15335
- [220] = https://curl.se/bug/?i=15144
- [221] = https://curl.se/bug/?i=15340
- [222] = https://curl.se/bug/?i=15342
- [223] = https://curl.se/bug/?i=15399
- [224] = https://curl.se/bug/?i=15339
- [225] = https://curl.se/bug/?i=15394
- [226] = https://curl.se/bug/?i=15315
- [227] = https://curl.se/bug/?i=15264
- [228] = https://curl.se/bug/?i=15405
- [229] = https://curl.se/bug/?i=15330
- [230] = https://curl.se/bug/?i=15402
- [231] = https://curl.se/bug/?i=15358
- [232] = https://curl.se/bug/?i=15392
- [233] = https://curl.se/bug/?i=15395
- [234] = https://curl.se/bug/?i=15398
- [235] = https://curl.se/bug/?i=15445
- [236] = https://curl.se/bug/?i=15443
- [238] = https://curl.se/bug/?i=15473
- [239] = https://curl.se/bug/?i=15475
- [240] = https://curl.se/bug/?i=15440
- [241] = https://curl.se/bug/?i=15439
- [242] = https://curl.se/bug/?i=15470
- [243] = https://curl.se/bug/?i=15436
- [244] = https://curl.se/bug/?i=15435
- [245] = https://curl.se/bug/?i=15434
- [246] = https://curl.se/bug/?i=15430
- [247] = https://curl.se/bug/?i=15471
- [248] = https://curl.se/bug/?i=15463
- [249] = https://curl.se/bug/?i=15462
- [250] = https://curl.se/bug/?i=15267
- [251] = https://curl.se/bug/?i=15466
- [252] = https://curl.se/bug/?i=15461
- [253] = https://curl.se/bug/?i=15149
- [254] = https://curl.se/bug/?i=15449
- [255] = https://curl.se/bug/?i=15485
- [256] = https://curl.se/bug/?i=15455
+ [1] = https://curl.se/bug/?i=17492
+ [2] = https://curl.se/bug/?i=17472
+ [3] = https://curl.se/bug/?i=17473
+ [4] = https://curl.se/bug/?i=17448
+ [5] = https://curl.se/bug/?i=17470
+ [6] = https://curl.se/bug/?i=17489
+ [7] = https://curl.se/bug/?i=17481
+ [8] = https://curl.se/bug/?i=17487
+ [9] = https://curl.se/bug/?i=17513
+ [10] = https://curl.se/bug/?i=17484
+ [11] = https://curl.se/bug/?i=17483
+ [12] = https://curl.se/bug/?i=17480
+ [13] = https://curl.se/bug/?i=17508
+ [14] = https://curl.se/bug/?i=16980
+ [15] = https://curl.se/bug/?i=17478
+ [16] = https://curl.se/bug/?i=17463
+ [17] = https://curl.se/bug/?i=17136
+ [18] = https://curl.se/bug/?i=17516
+ [19] = https://curl.se/bug/?i=17515
+ [20] = https://curl.se/bug/?i=17507
+ [21] = https://curl.se/bug/?i=17521
+ [22] = https://curl.se/bug/?i=17462
+ [23] = https://curl.se/bug/?i=17500
+ [24] = https://curl.se/bug/?i=17502
+ [25] = https://curl.se/bug/?i=17503
+ [26] = https://curl.se/bug/?i=17497
+ [27] = https://curl.se/bug/?i=17496
+ [28] = https://curl.se/bug/?i=17493
+ [29] = https://curl.se/bug/?i=17493
+ [30] = https://curl.se/bug/?i=17493
+ [31] = https://curl.se/bug/?i=17482
+ [32] = https://curl.se/bug/?i=17262
+ [33] = https://curl.se/bug/?i=17471
+ [34] = https://curl.se/bug/?i=17527
+ [35] = https://curl.se/bug/?i=17512
diff --git a/libs/libcurl/docs/THANKS b/libs/libcurl/docs/THANKS
index 6687282748..81fb754f59 100644
--- a/libs/libcurl/docs/THANKS
+++ b/libs/libcurl/docs/THANKS
@@ -13,6 +13,7 @@
3eka on github
5533asdg on github
8U61ife on github
+9cel
a1346054 on github
Aaro Koskinen
Aaron Oneal
@@ -21,10 +22,10 @@ Aaron Scarisbrick
aasivov on github
Abdullah Alyan
Abhinav Singh
+Abhinav Singhal
Abram Pousada
accountantM on github
-AceCrow on Github
-ad-chaos on github
+AceCrow on github
ad0p on github
Adam Averay
Adam Barclay
@@ -38,19 +39,22 @@ Adam Piggott
Adam Rosenfield
Adam Sampson
Adam Tkac
+ad-chaos on github
+Aditya Garg
+Ádler Jonas Gross
Adnan Khan
adnn on github
Adrian Burcea
+Adriano Meirelles
Adrian Peniak
Adrian Schuur
-Adriano Meirelles
afrind on github
Aftab Alam
ahodesuka on github
ajak in #curl
Ajit Dhumale
-Akhil Kedia
Akhilesh Nema
+Akhil Kedia
Aki Koskinen
Aki Sakurai
Akos Pasztory
@@ -62,6 +66,7 @@ Alan Jenkins
Alan Pinstein
Albert Chin-A-Young
Albert Choy
+Alberto Leiva Popper
Albin Vass
Alejandro Alvarez Ayllon
Alejandro Colomar
@@ -76,30 +81,6 @@ Ales Novak
Alessandro Ghedini
Alessandro Vesely
Alex aka WindEagle
-Alex Baines
-Alex Bligh
-Alex Bozarth
-Alex Chan
-Alex Crichton
-Alex Fishman
-Alex Gaynor
-Alex Grebenschikov
-Alex Gruz
-Alex Kiernan
-Alex Klyubin
-Alex Konev
-Alex Malinovich
-Alex Mayorga
-Alex McLellan
-Alex Neblett
-Alex Nichols
-Alex Potapenko
-Alex Rousskov
-Alex Samorukov
-Alex Snast
-Alex Suykov
-Alex Vinnik
-Alex Xu
Alexander Bartel
Alexander Beedie
Alexander Chuykov
@@ -121,6 +102,11 @@ Alexander Zhuravlev
Alexandre Bury
Alexandre Ferrieux
Alexandre Pion
+Alex Baines
+Alex Bligh
+Alex Bozarth
+Alex Chan
+Alex Crichton
Alexey Borzov
Alexey Eremikhin
Alexey Larikov
@@ -129,14 +115,34 @@ Alexey Pesternikov
Alexey Savchuk
Alexey Simak
Alexey Zakhlestin
+Alex Fishman
+Alex Gaynor
+Alex Grebenschikov
+Alex Gruz
Alexis Carvalho
Alexis La Goutte
+Alexis Savin
Alexis Vachette
+Alex Kiernan
+Alex Klyubin
+Alex Konev
+Alex Malinovich
+Alex Mayorga
+Alex McLellan
+Alex Neblett
+Alex Nichols
+Alex Potapenko
+Alex Rousskov
+Alex Samorukov
+Alex Snast
+Alex Suykov
+Alex Vinnik
+Alex Xu
Alfonso Martone
Alfred Gebert
Ali Khodkar
-Ali Utku Selen
ALittleDruid on github
+Ali Utku Selen
Allen Pulsifer
Alois Klink
Alona Rossen
@@ -152,12 +158,10 @@ Anders Bakken
Anders Berg
Anders Gustafsson
Anders Havn
-Anders Roxell
Anderson Sasaki
Anderson Toshiyuki Sasaki
+Anders Roxell
Andi Jahja
-Andre Guibert de Bruet
-Andre Heinecke
Andrea Pappacoda
Andreas Damm
Andreas Falkenhahn
@@ -175,11 +179,16 @@ Andreas Schneider
Andreas Schuldei
Andreas Sommer
Andreas Streichardt
+Andreas Westin
Andreas Wurf
+Andre Guibert de Bruet
+Andre Heinecke
Andrei Benea
Andrei Bica
Andrei Cipu
+Andrei Florea
Andrei Karas
+Andrei Korshikov
Andrei Kurushin
Andrei Neculau
Andrei Rybak
@@ -187,7 +196,9 @@ Andrei Sedoi
Andrei Valeriu BICA
Andrei Virtosu
Andrej E Baranov
+Andrés García
Andrew
+Andrew Ayer
Andrew Barnert
Andrew Barnes
Andrew Benham
@@ -198,6 +209,7 @@ Andrew Francis
Andrew Fuller
Andrew Ishchuk
Andrew Kaster
+Andrew Kirillov
Andrew Krieger
Andrew Kurushin
Andrew Lambert
@@ -210,7 +222,6 @@ Andrey Gursky
Andrey Labunets
Andrii Moiseiev
Andrius Merkys
-Andrés García
Andy Alt
Andy Cedilnik
Andy Fiddaman
@@ -241,20 +252,24 @@ Antoine Pietri
Antoine Pitrou
Anton Bychkov
Anton Gerasimov
+Antonio Larrosa
+Antoni Villalonga
Anton Kalmykov
Anton Malov
-Anton Yabchinskiy
-Antoni Villalonga
-Antonio Larrosa
Antony74 on github
+Anton Yabchinskiy
Antti Hätälä
+antypanty on hackerone
Anubhav Rai
apparentorder on github
April King
+Aquila Macedo
arainchik on github
Archangel_SDY on github
+Arian van Putten
Arjan van de Ven
Arkadiusz Miskiewicz
+arlt on github
Armel Asselin
Arnaud Compan
Arnaud Ebalard
@@ -269,10 +284,11 @@ Arve Knudsen
Arvid Norberg
arvids-kokins-bidstack on github
asavah on github
+Asger Hautop Drewsen
Ashish Shukla
Ashwin Metpalli
-Ask Bjørn Hansen
Askar Safin
+Ask Bjørn Hansen
AtariDreams on github
Ates Goral
atjg on github
@@ -294,37 +310,33 @@ b9a1 on github
Bachue Zhou
Baitinq on github
Balaji Parasuram
-Balaji S Rao
Balaji Salunke
+Balaji S Rao
Balakrishnan Balasubramanian
Balazs Kovacsics
-balikalina on Github
+balikalina on github
Balint Szilakszi
+baranyaib90 on github
Barry Abrahamson
Barry Pollard
Bart Whiteley
Baruch Siach
Bas Mevissen
-Bas van Schaik
Bastian Krause
Bastien Bouclet
Basuke Suzuki
+Bas van Schaik
baumanj on github
bdry on github
beckenc on github
Ben
+Benau on github
+Ben Bodenmiller
Ben Boeckel
+Benbuck Nason
Ben Darnell
Ben Fritz
Ben Greear
-Ben Kohler
-Ben Madsen
-Ben Noordhuis
-Ben Van Hof
-Ben Voris
-Ben Winslow
-Benau on github
-Benbuck Nason
Benjamin Gerard
Benjamin Gilbert
Benjamin Johnson
@@ -333,9 +345,16 @@ Benjamin Loison
Benjamin Riefenstahl
Benjamin Ritcey
Benjamin Sergeant
+Ben Kohler
+Ben Madsen
+Ben Noordhuis
Benoit Neil
Benoit Pierre
Benoit Sigoure
+Ben Van Hof
+Ben Voris
+Ben Winslow
+Ben Zanin
Bernard Leak
Bernard Spil
Bernat Mut
@@ -353,10 +372,10 @@ Bhanu Prakash
Bill Doyle
Bill Egert
Bill Hoffman
+billionai on github
Bill Middlecamp
Bill Nagel
Bill Pyne
-billionai on github
Billyzou0741326 on github
Bin Lan
Bin Meng
@@ -372,11 +391,12 @@ Blaise Potard
Blake Burkhart
blankie
bnfp on github
+bo0tzz on github
Bo Anderson
+bobmitchell1956 on github
Bob Relyea
Bob Richmond
Bob Schader
-bobmitchell1956 on github
Bodo Bergmann
Bogdan Nicula
boilingoden
@@ -386,18 +406,21 @@ Boris Rasin
Boris Verkhovskiy
Brad Burdick
Brad Fitzpatrick
+Bradford Bruce
Brad Forschinger
Brad Harder
Brad Hards
+Brad House
Brad King
Brad Spencer
-Bradford Bruce
bramus on github
Brandon Casey
Brandon Dong
Brandon Wang
BratSinot on github
+Brendan Dolan-Gavitt
Brendan Jurd
+Brendon Smith
Brennan Kinney
Brent Beardsley
Brett Buddin
@@ -407,6 +430,7 @@ Brian Carpenter
Brian Chaplin
Brian Childs
Brian Chrisman
+Brian Chrzanowski
Brian Clemens
Brian Dessent
Brian E. Gallew
@@ -420,19 +444,21 @@ Brian Prodoehl
Brian R Duffy
Brian Ulm
Brock Noland
-Bru Rom
Bruce Mitchener
Bruce Stephens
+bruce.yoon
BrumBrum on hackerone
Bruno Baguette
Bruno de Carvalho
Bruno Grasselli
Bruno Henrique Batista Cruz da Silva
Bruno Thomsen
+Bru Rom
Bryan Henderson
Bryan Kemp
bsammon on github
bsergean on github
+bsr13 on hackerone
bubbleguuum on github
Bubu on github
buzo-ffm on github
@@ -441,34 +467,38 @@ Bylon2 on github
Byrial Jensen
Cajus Pollmeier
Caleb Raitto
-Calvin Buckley
calvin2021y on github
+Calvin Buckley
+Calvin Ruocco
Cameron Blomquist
Cameron Cawley
Cameron Kaiser
Cameron MacMinn
Cameron Will
Camille Moncelier
-Cao ZhenXiang
Caolan McNamara
+Cao ZhenXiang
Captain Basil
Carie Pointer
-Carl Zogheib
Carlo Alberto
Carlo Cabrera
Carlo Cannas
Carlo Marcelo Arenas Belón
-Carlo Teubner
-Carlo Wood
Carlos Henrique Lima Melara
Carlos ORyan
+Carlo Teubner
+Carlo Wood
+Carl Zogheib
Carsten Lange
Casey Bodley
Casey O'Donnell
Catalin Patulea
+Catena cyber
causal-agent on github
cbartl on github
cclauss on github
+Cédric Connes
+Cédric Deltheil
Cering on github
Cesar Eduardo Barros
Ch40zz on github
@@ -479,6 +509,7 @@ Charles Cazabon
Charles Kerr
Charles Romestant
Charlie C
+chemodax
Chen Prog
chensong1211 on github
Cherish98 on github
@@ -487,8 +518,8 @@ Chih-Chung Chang
Chih-Hsuan Yen
Chilledheart on github
Chloe Kudryavtsev
-Chris "Bob Bob"
Chris Araman
+Chris "Bob Bob"
Chris Carlmar
Chris Combes
Chris Conlon
@@ -505,14 +536,12 @@ Chris Smowton
Chris Stubbs
Chris Swan
Chris Talbot
-Chris Webb
-Chris Young
Christian Fillion
Christian Grothoff
+Christian Hägele
Christian Heimes
Christian Hesse
Christian Heusel
-Christian Hägele
Christian Krause
Christian Kurz
Christian Robottom Reis
@@ -520,20 +549,24 @@ Christian Schmitz
Christian Stewart
Christian Vogt
Christian Weisgerber
-Christoph Krey
-Christoph M. Becker
-Christoph Reiter
Christophe Demory
Christophe Dervieux
Christophe Legry
Christopher Conroy
+Christopher Dannemiller
Christopher Degawa
Christopher Head
Christopher Palow
-Christopher R. Palmer
Christopher Reid
+Christopher R. Palmer
Christopher Sauer
Christopher Stone
+Christoph Jabs
+Christoph Krey
+Christoph M. Becker
+Christoph Reiter
+Chris Webb
+Chris Young
chrysos349 on github
Chungtsun Li
Ciprian Badescu
@@ -544,11 +577,11 @@ Claudio Neves
claudiusaiz on github
clbr on github
Clemens Gruber
+Clément Notin
Cliff Crosland
Clifford Wolf
Clint Clayton
Cloudogu Siebels
-Clément Notin
CMD
cmfrolick on github
codesniffer13 on github
@@ -557,6 +590,7 @@ Cody Mack
COFFEETALES on github
coinhubs on github
Colby Ranger
+Cole Helbling
Colin Blair
Colin Cross
Colin Hogben
@@ -564,18 +598,19 @@ Colin Leroy
Colin Leroy-Mira
Colin O'Dell
Colin Watson
-Colm Buckley
Colman Mbuya
+Colm Buckley
Colton Willey
Constantine Sapuntzakis
consulion on github
coralw on github
+Corinna Brandt
correctmost on github
Cory Benfield
Cory Nelson
Costya Shulyupin
-Craig A West
Craig Andrews
+Craig A West
Craig Davison
Craig de Stigter
Craig Markwardt
@@ -584,16 +619,13 @@ Cris Bailiff
Cristian Greco
Cristian Morales Vega
Cristian Rodríguez
+CueXXIII on github
Curt Bogmine
Cynthia Coan
Cyril B
Cyrill Osterwalder
-Cédric Connes
-Cédric Deltheil
-D. Flinkmann
d4d on hackerone
d912e3 on github
-Da-Yoon Chung
daboul on github
Dag Ekengren
Dagfinn Ilmari Mannsåker
@@ -609,27 +641,21 @@ Dan Becker
Dan Cristian
Dan Donahue
Dan Fandrich
-Dan Johnson
-Dan Kenigsberg
-Dan Locks
-Dan McDonald
-Dan McNulty
-Dan Nelson
-Dan Petitt
-Dan Torop
-Dan Zitter
Daniel at touchtunes
+Dániel Bakai
Daniel Bankhead
Daniel Black
Daniel Carpenter
Daniel Cater
Daniel Egger
+Daniel Engberg
Daniel Faust
+Daniel Fosco
Daniel Gustafsson
Daniel Hallberg
Daniel Hwang
-Daniel J. H.
Daniel Jeliński
+Daniel J. H.
Daniel Johnson
Daniel Kahn Gillmor
Daniel Katz
@@ -641,6 +667,7 @@ Daniel Marjamäki
Daniel McCarney
Daniel Melani
Daniel Mentz
+Daniel Pouzzner
Daniel Romero
Daniel Schauenberg
Daniel Seither
@@ -652,12 +679,23 @@ Daniel Szmulewicz
Daniel Theron
Daniel Valenzuela
Daniel Woelfel
+Dan Johnson
+Dan Kenigsberg
+Dan Locks
+Dan McDonald
+Dan McNulty
+Dan Nelson
+Dan Petitt
+Dan Rosser
+Dan Torop
+Dan Zitter
Daphne Luong
+Darío Hereñú
Dario Nieuwenhuis
Dario Weißer
+Darren Banfi
Darryl House
Darshan Mody
-Darío Hereñú
dasimx on github
DasKutti on github
Dave Cottlehuber
@@ -665,6 +703,7 @@ Dave Dribin
Dave Halbakken
Dave Hamilton
Dave May
+Dave Nicolson
Dave Reisner
Dave Thompson
Dave Vasilevsky
@@ -679,16 +718,19 @@ David Carlier
David Cohen
David Cook
David Demelier
-David E. Narváez
David Earl
+Davide Cassioli
+davidedec on github
+Davide Masserut
+David E. Narváez
David Eriksson
David Garske
David Goerger
David Houlder
David Hu
David Hull
-David J Meyer
David James
+David J Meyer
David Kalnischkies
David Kierznowski
David Kimdon
@@ -715,13 +757,15 @@ David Walser
David Woodhouse
David Wright
David Yan
-Davide Cassioli
-Davide Masserut
-davidedec on github
+Da-Yoon Chung
dbrowndan on github
dEajL3kA on github
Deal(一线灵)
dekerser on github
+deliciouslytyped on github
+delogicsreal on github
+Demi Marie Obenour
+denandz on github
dengjfzh on github
Dengminwen
Denis Baručić
@@ -735,12 +779,14 @@ Dennis Clarke
Dennis Felsing
dependabot[bot]
Derek Higgins
+Derek Huang
Derzsi Dániel
Desmond O. Chang
destman on github
Detlef Schmier
Dexter Gerig
dfdity on github
+D. Flinkmann
Dheeraj Sangamkar
Didier Brisebourg
Diego Bes
@@ -761,10 +807,10 @@ Dion Williams
Dirk Eddelbuettel
Dirk Feytons
Dirk Hünniger
+Dirkjan Bussink
Dirk Manske
Dirk Rosenkranz
Dirk Wetter
-Dirkjan Bussink
Diven Qi
divinity76 on github
Divy Le Ray
@@ -790,20 +836,20 @@ Dmitry Wagin
dnivras on github
dogma
Dolbneff A.V
-Domen Kožar
Domenico Andreoli
+Domen Kožar
Dominick Meglio
Dominik Hölzl
Dominik Klemba
Dominik Piątkowski
Dominik Thalhammer
Dominique Leuenberger
-Don J Olmstead
Dongliang Mu
+Donguk Kim
+Don J Olmstead
Dorian Craps
Doron Behar
Doug Kaufman
-Doug Porter
Douglas Creager
Douglas E. Wegscheid
Douglas Kilpatrick
@@ -811,6 +857,7 @@ Douglas Mencken
Douglas R. Horner
Douglas R. Reno
Douglas Steinwand
+Doug Porter
Dov Murik
dpull on github
Drake Arconis
@@ -823,11 +870,11 @@ Dustin Howett
Dusty Mabe
Duy Phan Thanh
Dwarakanath Yadavalli
+dwickr
Dylam De La Torre
Dylan Anthony
Dylan Ellicott
Dylan Salisbury
-Dániel Bakai
eaglegai on github
Early Ehlinger
Earnestly on github
@@ -835,13 +882,13 @@ Eason-Yu on github
Ebe Janchivdorj
ebejan on github
Ebenezer Ikonne
-Ed Morley
ed0d2b2ce19451f2
Eddie Lumpkin
Edgaras Janušauskas
Edin Kadribasic
edmcln on github
Edmond Yu
+Ed Morley
Edoardo Lolletti
Eduard Bloch
Eduard Strehlau
@@ -858,8 +905,8 @@ Ehren Bendler
Eldar Zaitov
elelel on github
elephoenix on github
-Eli Schwartz
Elia Tufarolo
+Eli Schwartz
Elliot Killick
Elliot Saba
Elliott Balsley
@@ -870,27 +917,30 @@ Eloy Degen
elsamuko on github
elvinasp on github
emanruse on github
-Emanuel Komínek
Emanuele Bovisio
Emanuele Torre
+Emanuel Komínek
Emil Engler
-Emil Lerner
-Emil Romanus
-Emil Österlund
Emiliano Ida
Emilio Cobos Álvarez
Emilio López
+Emil Lerner
+Emil Österlund
+Emil Romanus
Emmanuel Tychon
Enno Boland
Enrico Scholz
Enrik Berkhan
enWILLYado on github
+epicmkirzinger on github
eppesuig
Eramoto Masaya
Eric Cooper
Eric Curtin
Eric Gallager
Eric Hu
+Eric Knibbe
+Erick Nuwendam
Eric Landes
Eric Lavigne
Eric Lubin
@@ -899,19 +949,20 @@ Eric Mertens
Eric Murphy
Eric Musser
Eric Norris
+Érico Nogueira
+Érico Nogueira Rolim
Eric Rautman
Eric Rescorla
Eric Ridge
Eric Rosenquist
-Eric S. Raymond
Eric Sauvageau
+Eric S. Raymond
Eric Thelin
Eric Vergnaud
Eric Vigeant
Eric Wong
Eric Wu
Eric Young
-Erick Nuwendam
Erik Jacobsen
Erik Janssen
Erik Johansson
@@ -925,11 +976,13 @@ Erwan Legrand
Erwin Authried
Esdras de Morais da Silva
Estanislau Augé-Pujadas
+Ethan Everett
Ethan Glasser Camp
+Ethan Wilkes
Etienne Simard
Eugene Kotlyarov
-Evan Jordan
Evangelos Foutras
+Evan Jordan
Even Rouault
Evert Pot
Evgeny Grin (Karlson2k)
@@ -954,6 +1007,7 @@ farazrbx on github
Farzin on github
Fata Nugraha
Fawad Mirza
+Fay Stegerman
FC Stegerman
fds242 on github
Federico Bianchi
@@ -976,6 +1030,7 @@ finkjsc on github
Firefox OS
Flameborn on github
Flavio Medeiros
+Florian Eckert
Florian Kohnhäuser
Florian Pritz
Florian Schoppmann
@@ -987,7 +1042,10 @@ Francisco Moraes
Francisco Munoz
Francisco Olarte
Francisco Sedano
+François Charlier
+François Michel
Francois Petitjean
+François Rigault
Francois Rivard
Frank Denis
Frank Gevaerts
@@ -998,18 +1056,15 @@ Frank Meier
Frank Ticheler
Frank Van Uffelen
František Kučera
-François Charlier
-François Michel
-François Rigault
Frazer Smith
-Fred Machado
-Fred New
-Fred Noz
-Fred Stluka
Frederic Lepied
Frederik B
Frederik Wedel-Heinen
+Fred Machado
+Fred New
+Fred Noz
Fredrik Thulin
+Fred Stluka
FuccDucc on github
Fujii Hironori
fullincome on github
@@ -1022,8 +1077,11 @@ Gabriel Marin
Gabriel Simmer
Gabriel Sjoberg
Gaelan Steele
+Gaël Portay
+galen11 on github
Gambit Communications
Ganesh Kamath
+Ganesh Viswanathan
gaoxingwang on github
Garrett Holmstrom
Garrett Squire
@@ -1034,17 +1092,16 @@ Gautam Mani
Gavin Wong
Gavrie Philipson
Gaz Iqbal
-Gaël Portay
gclinch on github
Gealber Morales
Geeknik Labs
Geoff Beier
Georeth Zhou
+George Liu
Georg Horn
Georg Huettenegger
Georg Lippitsch
Georg Wicherski
-George Liu
Gerd v. Egidy
Gergely Nagy
Gerhard Herre
@@ -1054,11 +1111,11 @@ Gerrit Renker
Ghennadi Procopciuc
Giancarlo Formicuccia
Giaslas Georgios
-Gil Weber
Gilad
Gilbert Ramirez Jr.
Gilles Blanc
Gilles Vollant
+Gil Weber
Giorgos Oikonomou
Gisle Vanem
git-bruh on github
@@ -1066,38 +1123,42 @@ GitYuanQu on github
Giuseppe Attardi
Giuseppe D'Ambrosio
Giuseppe Persico
+gkarracer on github
Gleb Ivanovsky
Glen A Johnson Jr.
Glen Nakamura
-Glen Scott
Glenn de boer
Glenn Sheridan
Glenn Strauss
+Glen Scott
godmar on github
Godwin Stewart
+Gökhan Şengün
Gonçalo Carvalho
Google Inc.
Gordon Marler
Gordon Parke
Gorilla Maguila
Goro FUJI
+Götz Babin-Ebell
Gou Lingfeng
Graham Campbell
+Graham Christensen
Grant Erickson
Grant Pannell
graywolf on github
Greg Hewgill
Greg Morse
Greg Onufer
-Greg Pratt
-Greg Rowe
-Greg Zavertnik
Gregor Jasny
Gregory Jefferis
Gregory Muchka
Gregory Nicholls
Gregory Panakkal
Gregory Szorc
+Greg Pratt
+Greg Rowe
+Greg Zavertnik
Griffin Downs
Grigory Entin
Grisha Levit
@@ -1116,10 +1177,8 @@ Gustavo Grieco
Gusted
Guy Poizat
GwanYeong Kim
-Gwen Shapira
Gwenole Beauchesne
-Gökhan Şengün
-Götz Babin-Ebell
+Gwen Shapira
h1zzz on github
H3RSKO on github
Hagai Auro
@@ -1128,18 +1187,18 @@ Hakan Sunay Halil
Hamish Mackenzie
hammlee96 on github
hamstergene on github
-Han Han
-Han Qiao
Hang Kin Lau
Hang Su
+Han Han
Hannah Schierling
Hannes Magnusson
Hanno Böck
Hanno Kranzhoff
-Hans Steegers
+Han Qiao
Hans-Christian Egtvedt
Hans-Christian Noren Egtvedt
Hans-Jurgen May
+Hans Steegers
Hao Wu
Hardeep Singh
Haris Okanovic
@@ -1152,11 +1211,11 @@ Harshal Pradhan
Hauke Duden
Haydar Alaidrus
Hayden Roche
-He Qin
Heikki Korpela
Heinrich Ko
Heinrich Schaefer
Helge Klein
+Helmut Grohne
Helmut K. C. Tessarek
Helwing Lutz
Hendrik Visage
@@ -1164,15 +1223,19 @@ Henning Schild
Henri Gomez
Henrik Gaßmann
Henrik Holst
+henrikjehgmti on github
Henrik Storner
Henry Ludemann
Henry Roeland
+He Qin
+Hermes Zhang
Herve Amblard
HexTheDragon
hgdagon on github
Hide Ishikawa
Hidemoto Nakada
highmtworks on github
+hiimmat on github
Himanshu Gupta
Hind Montassif
Hiroki Kurosawa
@@ -1206,6 +1269,7 @@ Ian Spence
Ian Turner
Ian Wilkes
iAroc on github
+IcedCoffeee on github
iconoclasthero
icy17 on github
Ignacio Vazquez-Abrams
@@ -1227,13 +1291,14 @@ Ilmari Lauhakangas
Ilya Kosarev
imilli on github
Immanuel Gregoire
-ImpatientHippo on GitHub
+ImpatientHippo on github
Inca R
infinnovation-dev on github
Ingmar Runge
Ingo Ralf Blum
Ingo Wilken
Inho Oh
+Int64x86 on github
Ionuț-Francisc Oancea
Irfan Adilovic
Ironbars13 on github
@@ -1241,22 +1306,23 @@ Irving Wolfe
Isaac Boukris
Isaiah Norton
Ishan SinghLevett
+İsmail Dönmez
Ithubg on github
Ivan
Ivan Avdeev
-Ivan Kuchin
-Ivan Tsybulin
ivanfywang
+Ivan Kuchin
IvanoG on github
+Ivan Tsybulin
Ivo Bellin Salarin
iz8mbw on github
-J. Bromley
-Jack Boos Yu
-Jack Zhang
Jackarain on github
JackBoosY on github
+Jack Boos Yu
Jacky Lam
+Jack Zhang
Jacob Barthelmeh
+Jacob Champion
Jacob Hoffman-Andrews
Jacob Mealey
Jacob Meuser
@@ -1265,6 +1331,7 @@ Jacob Tolar
Jactry Zeng
Jad Chamcham
Jaime Fullaondo
+Jake Yuesong Li
jakirkham on github
Jakob Hirsch
Jakub Bochenski
@@ -1293,24 +1360,25 @@ Jamie Lokier
Jamie Newton
Jamie Wilkinson
Jan Alexander Steffens
+JanB on github
Jan Chren
+janedenone on github
Jan Ehrhardt
Jan Engelhardt
Jan Koen Annot
+janko-js on github
Jan Kunder
Jan Macku
Jan Mazur
+Janne Blomqvist
+Janne Johansson
+János Fekete
+Jan-Piet Mens
Jan Schaumann
Jan Schmidt
Jan Van Boghout
Jan Venekamp
Jan Verbeek
-Jan-Piet Mens
-JanB on github
-janedenone on github
-janko-js on github
-Janne Blomqvist
-Janne Johansson
Jared Jennings
Jared Lundell
Jari Aalto
@@ -1331,18 +1399,20 @@ Javier Navarro
Javier Sixto
Jay Austin
Jay Dommaschk
+Jayesh A Shah
Jay Guerette
Jay Wu
-Jayesh A Shah
Jaz Fresh
JazJas on github
jbgoog on github
-Jean Fabrice
-Jean Gressmann
-Jean Jacques Drouin
+J. Bromley
+Jean-Christophe Amiel
Jean-Claude Chauve
+Jean Fabrice
Jean-Francois Bertrand
Jean-Francois Durand
+Jean Gressmann
+Jean Jacques Drouin
Jean-Louis Lemaire
Jean-Marc Ranger
Jean-Noël Rouvignac
@@ -1357,16 +1427,17 @@ Jeff Luszcz
Jeff Mears
Jeff Phillips
Jeff Pohlmeyer
-Jeff Weber
Jeffrey Tolar
Jeffrey Walton
jeffrson on github
+Jeff Weber
Jelle van der Waa
Jenny Heino
Jens Finkhaeuser
Jens Rantil
Jens Schleusener
Jeremie Rapin
+Jeremy Drake
Jeremy Falcon
Jeremy Friesner
Jeremy Huddleston
@@ -1374,10 +1445,13 @@ Jeremy Lainé
Jeremy Lin
Jeremy Maitin-Shepard
Jeremy Pearson
+Jérémy Rabasco
+Jérémy Rocher
Jeremy Tan
Jeremy Thibault
Jeroen Koekkoek
Jeroen Ooms
+Jérôme Leclercq
Jerome Mao
Jerome Muffat-Meridol
Jerome Robert
@@ -1387,10 +1461,11 @@ Jerry Krinock
Jerry Wu
Jes Badwal
Jesper Jensen
-Jess Lowe
Jesse Chisholm
Jesse Noller
Jesse Tan
+Jess Lowe
+Jesus Malo Poyatos
jethrogb on github
jhoyla on github
Jiacai Liu
@@ -1407,39 +1482,46 @@ Jim Hollinger
Jim King
Jim Meyering
Jimmy Gaussen
+Jimmy Sjölund
+Jiří Bok
Jiri Dvorak
Jiri Hruska
Jiri Jaburek
+Jiří Malák
+Jiri Stary
Jishan Shaikh
Jiwoo Park
-Jiří Bok
-Jiří Malák
+Jixinqi
jkamp-aws on github
jmdavitt on github
jnbr on github
Jocelyn Jaubert
Jochem Broekhoff
+Jochen Sprickerhof
Joe Birr-Pixton
+Joe Cise
Joe Halpin
-Joe Malicki
-Joe Mason
+JoelAtWisetech on github
Joel Chen
Joel Depooter
Joel Jakobsson
Joel Teichroeb
+Joe Malicki
+Joe Mason
joey-l-us on github
Jofell Gallardo
Johan Anderson
+Johan Eliasson
Johan Lantz
-Johan Nilsson
-Johan van Selst
-Johann Sebastian Schicho
Johann150 on github
Johannes Bauer
Johannes Ernst
Johannes G. Kristinsson
Johannes Lesr
Johannes Schindelin
+Johan Nilsson
+Johann Sebastian Schicho
+Johan van Selst
John A. Bristor
John Bampton
John Bradshaw
@@ -1452,10 +1534,10 @@ John Dennis
John Dunn
John E. Malmberg
John Gardiner Myers
-John H. Ayad
John Hascall
John Haugabook
John Hawthorn
+John H. Ayad
John Janssen
John Joseph Bachir
John Kelly
@@ -1464,8 +1546,10 @@ John Lask
John Levon
John Lightsey
John Marino
+John-Mark Bell
John Marshall
John McGowan
+Johnny Luong
John P. McCaskey
John Porter
John Schroeder
@@ -1478,27 +1562,13 @@ John Walker
John Wanghui
John Weismiller
John Wilkinson
-John-Mark Bell
-Johnny Luong
Jojojov on github
-Jon DeVree
-Jon Grubbs
-Jon Johnson Jr
-Jon Nelson
-Jon Rumsey
-Jon Sargeant
-Jon Seymour
-Jon Spencer
-Jon Torrey
-Jon Travis
-Jon Turner
-Jon Wilkes
-Jonas 'Sortie' Termansen
Jonas Bülow
Jonas Forsman
Jonas Haag
Jonas Minnberg
Jonas Schnelli
+Jonas 'Sortie' Termansen
Jonas Vautherin
Jonatan Lander
Jonatan Vela
@@ -1508,32 +1578,47 @@ Jonathan Matthews
Jonathan Moerman
Jonathan Nieder
Jonathan Perkin
+Jonathan Rosa
Jonathan Watt
Jonathan Wernberg
+Jon DeVree
Jongki Suwandi
+Jon Grubbs
+Jon Johnson Jr
+Jon Nelson
jonny112 on github
+Jon Rumsey
+Jon Sargeant
+Jon Seymour
+Jon Spencer
+Jon Torrey
+Jon Travis
+Jon Turner
+Jon Wilkes
Joombalaya on github
Joonas Kuorilehto
Jordan Brown
+Jörg Mueller-Tolk
+Jörn Hartroth
Jose Alf
-Jose Kahan
Josef Wolf
+José Joaquín Atria
+Jose Kahan
Joseph Chen
Joseph Tharayil
Josh Bialkowski
Josh Brobst
+joshhe on github
+Joshix-1 on github
Josh Kapell
Josh McCullough
Josh Soref
-joshhe on github
-Joshix-1 on github
Joshua Kwan
Joshua Root
Joshua Swink
Josie Huddleston
Josip Medved
Josue Andrade Gomes
-José Joaquín Atria
Jozef Kralik
Juan Barreto
Juan Cruz Viotti
@@ -1555,12 +1640,13 @@ Julien Chaffraix
Julien Nabet
Julien Royer
Juliusz Sosinowicz
-Jun Tseng
-Jun-ichiro itojun Hagino
-Jun-ya Kato
jungle-boogie on github
Junho Choi
+Jun-ichiro itojun Hagino
junsik on github
+Jun Tseng
+Jun-ya Kato
+Jürgen Gmach
Jurij Smakov
jurisuk on github
Juro Bystricky
@@ -1571,32 +1657,26 @@ Justin Ehlert
Justin Fletcher
Justin Karneges
Justin Maggard
+Justin Steventon
jveazey on github
jvreelanda on github
jvvprasad78 on github
jzinn on github
-János Fekete
-Jérémy Rabasco
-Jérémy Rocher
-Jérôme Leclercq
-Jörg Mueller-Tolk
-Jörn Hartroth
-Jürgen Gmach
-K. R. Walker
ka7 on github
+Kadambini Nema
Kael1117 on github
Kai Engert
+Kailun Qin
Kai Noda
Kai Pastor
Kai Sommerfeld
Kai-Uwe Rommel
-Kailun Qin
Kalle Vahlman
kalvdans on github
Kamil Dudka
Kane York
-Kang Lin
Kang-Jin Lee
+Kang Lin
Kantanat Wannapaka
Kareem
Kari Pahula
@@ -1609,6 +1689,7 @@ Kartik Mahajan
Kaspar Brand
Katie Wang
Katsuhiko YOSHIDA
+kayrus on github
Kazuho Oku
kchow-FTNT on github
Keerthi Timmaraju
@@ -1621,14 +1702,13 @@ Keith Mok
Kelly Kaoudis
Ken Brown
Ken Hirsch
-Ken Rastatter
Kenneth Davidson
Kenneth Myhra
Kenny To
+Ken Rastatter
Kent Boortz
Kerem Kat
Keshav Krity
-Kev Jackson
Kevin Adler
Kevin Baughman
Kevin Burke
@@ -1641,17 +1721,20 @@ Kevin Reed
Kevin Roth
Kevin Ryan
Kevin Smith
+Kevin Sun
Kevin Ushey
+Kev Jackson
Kim Minjoong
+Kimmo Kinnunen
Kim Rinnewitz
Kim Vandry
-Kimmo Kinnunen
kirbyn17 on hackerone
Kirill Efimov
Kirill Marchuk
kit-ty-kate on github
Kjell Ericson
Kjetil Jacobsen
+kkalganov on github
Klaus Crusius
Klaus Stein
Klevtsov Vadim
@@ -1670,14 +1753,17 @@ kouzhudong on github
Kovalkov Dmitrii
kpcyrd on github
kreshano on github
-Kris Kennaway
Krishnendu Majumdar
+Kris Kennaway
Krister Johansen
Kristian Gunstone
Kristian Köhntopp
Kristian Mide
Kristiyan Tsaklev
Kristoffer Gleditsch
+kriztalz
+K. R. Walker
+Kuan-Wei Chiu
Kunal Chandarana
Kunal Ekawde
Kurt Fankhauser
@@ -1686,11 +1772,11 @@ Kvarec Lezki
kwind on github
Kwon-Young Choi
Kyle Abramowitz
+kyled-dell on github
Kyle Edwards
Kyle J. McKay
Kyle L. Huff
Kyle Sallee
-kyled-dell on github
Kyohei Kadota
Kyselgov E.N
l00p3r on Hackerone
@@ -1708,15 +1794,17 @@ Lars Francke
Lars Gustafsson
Lars J. Aas
Lars Johannesen
+Lars Karlitski
Lars Kellogg-Stedman
Lars Nilsson
Lars Torben Wilson
Lau
Laurent Bonnans
Laurent Dufresne
+Laurențiu Nicola
Laurent Rabret
-Lauri Kasanen
Laurie Clark-Michalek
+Lauri Kasanen
Lawrence Gripper
Lawrence Matthews
Lawrence Wagerfield
@@ -1730,28 +1818,29 @@ Lehel Bernadt
Leif W
Leigh Purdie
Leith Bade
+Lenaic Lefever
Len Krause
Len Marinaccio
-Lenaic Lefever
Lenny Rachitsky
-Leo Neat
-Leon Breedt
-Leon Winter
Leonardo Rosati
Leonardo Taccari
+Leon Breedt
+Leo Neat
+Leon Timmermans
+Leon Winter
Leszek Kubik
-Li Xinwei
+lf- on github
Liam Healy
Liam Warfield
LigH-de on github
lijian996 on github
Lijo Antony
lilongyan-huawei on github
-Lin Sun
Linas Vepstas
Lindley French
Ling Thio
Linos Giannopoulos
+Lin Sun
Linus Lewandowski
Linus Nielsen Feltzing
Linus Nordberg
@@ -1759,6 +1848,7 @@ Lior Kaplan
Lisa Xu
Litter White
Liviu Chircu
+Li Xinwei
Liza Alenchery
lizhuang0630 on github
lkordos on github
@@ -1767,16 +1857,18 @@ Lloyd Fournier
Lluís Batlle i Rossell
locpyl-tidnyd on github
Loganaden Velvindron
+Logan Buth
Loic Dachary
+Loïc Yhuel
lolbinarycat on github
lomberd2 on github
LoRd_MuldeR
Loren Kirkby
Lorenzo Miniero
Louis Solofrizzo
-Loïc Yhuel
lRoccoon on github
Luan Cestari
+Luật Nguyễn
Luca Altea
Luca Boccassi
Luca Niccoli
@@ -1793,23 +1885,23 @@ Ludovico Cavedon
Ludwig Nussel
Lukas Ruzicka
Lukas Tribus
+Lukáš Zaoral
Lukasz Czekierda
+Łukasz Domeradzki
lukaszgn on github
Luke Amery
Luke Call
Luke Dashjr
Luke Granger-Brown
Luke Hamburg
-Lukáš Zaoral
luminixinc on github
Luo Jinghua
Luong Dinh Dung
Luz Paz
-Luật Nguyễn
lwthiker on github
Lyman Epp
Lyndon Hill
-M.R.T on github
+Maarten Billemont
Maciej Domanski
Maciej Karpiuk
Maciej Puzio
@@ -1819,44 +1911,47 @@ madblobfish on github
MaeIsBad on github
magisterquis on hackerone
Mahmoud Samir Fayed
-Maks Naumov
Maksim Arhipov
Maksim Kuzevanov
-Maksim Sciepanienka
+Maksim Ściepanienka
Maksim Stsepanenka
+Maks Naumov
Maksymilian Arciemowicz
Malik Idrees Hasan Khan
Mamoru Tasaka
Mamta Upadhyay
Mandy Wu
Manfred Schwarb
+Manuel Einfalt
Manuel Massing
+Manuel Strehl
Manuj Bhatia
Marc Aldorasi
+Marc-Antoine Perennou
Marc Boucher
Marc Deslauriers
Marc Doughty
-Marc Hesse
-Marc Hörsken
-Marc Kleine-Budde
-Marc Renault
-Marc Schlatter
-Marc-Antoine Perennou
-marc-groundctl on github
Marcel Hernandez
-Marcel Raad
-Marcel Roelofs
Marcelo Echeverria
Marcelo Juchem
+Marcel Raad
+Marcel Roelofs
+marc-groundctl on github
+Marc Hesse
+Marc Hörsken
Marcin Adamski
Marcin Gryszkalis
Marcin Konicki
Marcin Rataj
+Marc Kleine-Budde
Marco Deckel
Marco G. Salvagno
Marco Kamner
Marco Maggi
Marcos Diazr
+marcos-ng on github
+Marc Renault
+Marc Schlatter
Marcus Hoffmann
Marcus Klein
Marcus Müller
@@ -1867,6 +1962,8 @@ Margu
Marian Klymov
Marin Hannache
Mario Schroeder
+Marius Albrecht
+Marius Kleidl
Mark Brand
Mark Butler
Mark Davies
@@ -1879,6 +1976,7 @@ Mark Itzcovitz
Mark Karpeles
Mark Lentczner
Mark Nottingham
+Mark Phillips
Mark Roszko
Mark Salisbury
Mark Seuffert
@@ -1886,8 +1984,6 @@ Mark Sinkovics
Mark Snelling
Mark Swaanenburg
Mark Tully
-Mark W. Eichin
-Mark Wotton
Markus Duft
Markus Elfring
Markus Koetter
@@ -1896,10 +1992,13 @@ Markus Oberhumer
Markus Olsson
Markus Sommer
Markus Westerlind
+Mark W. Eichin
+Mark Wotton
Maros Priputen
Marquis de Muesli
marski on github
Martijn Koster
+Martin Ågren
Martin Ankerl
Martin Bašti
Martin C. Martin
@@ -1907,11 +2006,13 @@ Martin D'Aloia
Martin Dorey
Martin Drasar
Martin Dreher
+martinevsky
Martin Frodl
Martin Galvan
Martin Gartner
Martin Hager
Martin Halle
+Martin Harrigan
Martin Hedenfalk
Martin Howarth
Martin Jansen
@@ -1927,8 +2028,7 @@ Martin Strunz
Martin V
Martin Vejnár
Martin Waleczek
-Martin Ågren
-martinevsky
+Martxel
Marty Kuhrt
Maruko
Marwan Yassini
@@ -1937,31 +2037,25 @@ masbug on github
Massimiliano Fantuzzi
Massimiliano Ziccardi
Massimo Callegari
-Master Inspire
MasterInQuestion on github
+Master Inspire
Mateusz Loskot
Mathew Benson
Mathias Axelsson
Mathias Fuchs
Mathias Gumz
Mathieu Carbonneaux
+Mathieu Garaud
Mathieu Legare
Matias N. Goldberg
Mats Lidell
Mats Lindestam
Matt Arsenault
-Matt Ford
-Matt Holt
-Matt Jolly
-Matt Kraai
-Matt McClure
-Matt Veenstra
-Matt Witherspoon
-Matt Wixson
Matteo Baccan
Matteo Bignotti
Matteo Bignottignotti
Matteo Rocco
+Matt Ford
Matthew Blain
Matthew Clarke
Matthew Hall
@@ -1972,15 +2066,30 @@ Matthias Bolte
Matthias Gatto
Matthias Naegler
Matthieu Baerts
+Matt Holt
Mattias Fornander
+Matt Jolly
+Matt Kraai
+Matt McClure
+Matt Veenstra
+Matt Witherspoon
+Matt Wixson
Matus Uzak
+mauke
Maurice Barnum
+Maurício Meneghini Fauth
Mauricio Scheffer
Mauro Iorio
Mauro Rappa
-Maurício Meneghini Fauth
Max Dymond
+Max Eliaser
Max Faxälv
+Maxim Dzhura
+Maxime Larocque
+Maxime Legros
+Maxim Ivanov
+Maxim Perenesenko
+Maxim Prohorov
Max Katsev
Max Kellermann
Max Khon
@@ -1988,21 +2097,15 @@ Max Mehl
Max Peal
Max Savenkov
Max Zettlmeißl
-Maxim Dzhura
-Maxim Ivanov
-Maxim Perenesenko
-Maxim Prohorov
-Maxime Larocque
-Maxime Legros
mbeifuss on github
mccormickt12 on github
Median Median Stride
mehatzri on github
Mehmet Bozkurt
Mekonikum
-Mel Zuser
Melissa Mears
Melroy van den Berg
+Mel Zuser
Mert Yazıcıoğlu
Mettgut Jamalla
Micah Snyder
@@ -2029,8 +2132,8 @@ Michael Kalinin
Michael Kaufmann
Michael Kilburn
Michael Kolechkin
-Michael Kujawa
Michael König
+Michael Kujawa
Michael Lee
Michael Litwak
Michael Maltese
@@ -2041,6 +2144,7 @@ Michael O'Farrell
Michael Olbrich
Michael Osipov
Michael Schmid
+Michael Schuster
Michael Smith
Michael Stapelberg
Michael Steuer
@@ -2048,20 +2152,20 @@ Michael Stillwell
Michael Trebilcock
Michael Vittiglio
Michael Wallner
+Michał Antoniak
Michal Bonino
-Michal Marek
-Michal Rus
-Michal Trybus
Michal Čaplygin
-Michał Antoniak
Michał Fita
Michał Górny
Michał Janiszewski
Michał Kowalczyk
+Michal Marek
Michał Petryka
Michał Piechowski
-Michel Promonet
+Michal Rus
+Michal Trybus
Michele Bini
+Michel Promonet
Miguel Angel
Miguel Diaz
migueljcrum on github
@@ -2088,6 +2192,7 @@ Mike Revi
Mike Tzou
Mikhail Kuznetsov
Miklos Nemeth
+Milon Renatus
Miloš Ljumović
Mingliang Zhu
Mingtao Yang
@@ -2104,6 +2209,7 @@ Mohammad AlSaleh
Mohammad Hasbini
Mohammadreza Hendiani
Mohammed Naser
+Mohammed Sadiq
Mohun Biswas
momala454 on github
Momoka Yamamoto
@@ -2111,6 +2217,7 @@ MonkeybreadSoftware on github
Montg0mery on github
moohoorama on github
Morgan Willcock
+Moritz
Moritz Buhl
Moritz Knüsel
Morten Minde Neergaard
@@ -2118,6 +2225,8 @@ Mostyn Bramley-Moore
Moti Avrahami
MrdUkk on github
MrSorcus on github
+M.R.T on github
+mschroeder-fzj on github
Muhammad Herdiansyah
Muhammad Hussein Ammari
Muhammed Yavuz Nuzumlalı
@@ -2128,17 +2237,18 @@ Myk Taylor
n0name321 on github
Nach M. S.
Nagai H
-Nao Yonashiro
naost3rn on github
+Nao Yonashiro
Natanael Copa
Nate Prewitt
-Nathan Coulter
-Nathan Moinvaziri
-Nathan O'Sullivan
Nathanael Nerode
+Nathan Coulter
Nathaniel J. Smith
Nathaniel R. Lewis
Nathaniel Waisbrot
+Nathan Moinvaziri
+Nathan O'Sullivan
+na-trium-144 on github
Naveen Chandran
Naveen Noel
Neal McBurnett
@@ -2149,18 +2259,21 @@ Nehal J Wani
neheb on github
Neil Bowers
Neil Dunbar
+Neil Horman
+Neil Johari
Neil Kolban
Neil Spring
+NeimadTL
nekopsykose on github
Nemos2024 on github
neutric on github
nevv on HackerOne/curl
newfunction
+newfunction on hackerone
Niall McGee
Niall O'Reilly
nian6324 on github
nianxuejie on github
-Nic Roets
Nicholas Maniscalco
Nicholas Nethercote
Nick Banks
@@ -2168,25 +2281,29 @@ Nick Coghlan
Nick Draffen
Nick Gimbrone
Nick Humfrey
+Nicklas Avén
Nick Miyake
-Nick Zitzmann
nick-telia on github
-Nicklas Avén
-Nico Baggus
-Nico Rieck
+Nick Zitzmann
nico-abram on github
+Nico Baggus
Nicolas Berloquin
Nicolas Croiset
+Nicolas F.
Nicolas François
Nicolas George
Nicolas Grekas
Nicolas Guillier
Nicolas Morey-Chaisemartin
Nicolas Noben
-Nicolas Sterchele
Nicolás Ojeda Bär
+Nicolás San Martín
+Nicolas Sterchele
+Nico Rieck
+Nic Roets
Niels Martignène
Niels van Tongeren
+Nigel Brittain
Nikita Schmidt
Nikita Taranov
Nikitinskit Dmitriy
@@ -2195,12 +2312,13 @@ Niklas Hambüchen
Nikolai Kondrashov
Nikos Mavrogiannopoulos
Nikos Tsipinakis
+Nils Goroll
nimaje on github
niner on github
Ning Dong
-Nir Soffer
Niracler Li
Niranjan Hasabnis
+Nir Soffer
Nis Jorgensen
nk
Noam Moshe
@@ -2208,23 +2326,24 @@ nobedee on github
NobodyXu on github
Nobuhiro Ban
Nodak Sodak
+nono303 on github
nopjmp on github
Norbert Frese
Norbert Kett
-Norbert Novotny
norbertmm on github
+Norbert Novotny
nosajsnikta on github
-NTMan on Github
+NTMan on github
Nuru on github
Octavio Schroeder
odek86 on github
Ofer
ohyeaah on github
Okhin Vasilij
-Ola Mork
Olaf Flebbe
Olaf Hering
Olaf Stüben
+Ola Mork
Oleg Jukovec
Oleg Pudeyev
Oleguer Llopart
@@ -2235,15 +2354,16 @@ Oliver Chang
Oliver Gondža
Oliver Graute
Oliver Kuckertz
+oliverpool on github
Oliver Roberts
Oliver Schindler
Oliver Urbann
-oliverpool on github
Olivier Berger
Olivier Bonaventure
Olivier Brunel
Omar Ramadan
omau on github
+Ondřej Hlavatý
Ondřej Koláček
opensignature on github
opensslonzos-github on github
@@ -2265,7 +2385,6 @@ Oumph on github
Outvi V
ovidiu-benea on github
Ozan Cansel
-P R Schaffner
Pablo Busse
Palo Markovic
pandada8 on github
@@ -2277,9 +2396,9 @@ Pascal Gaudette
Pascal Terjan
Pasha Kuznetsov
Pasi Karkkainen
-Pat Ray
patelvivekv1993 on github
patnyb on github
+Pat Ray
Patrice Guerin
Patricia Muscalu
Patrick Bihan-Faou
@@ -2310,12 +2429,12 @@ Paul Marquis
Paul Moore
Paul Nolan
Paul Oliver
+Paulo Roberto Tomasi
Paul Querna
Paul Saab
Paul Seligman
Paul Vixie
Paul Wise
-Paulo Roberto Tomasi
Pavel Cenek
Pavel Gushchin
Pavel Kalyugin
@@ -2354,12 +2473,16 @@ Peter Gal
Peter Goodman
Peter Heuchert
Peter Hjalmarsson
+Pēteris Caune
+Peteris Krumins
+Peter Kokot
+Peter Körner
Peter Korsgaard
Peter Krefting
-Peter Körner
Peter Lamare
Peter Lamberg
Peter Laser
+Peter Marko
Peter O'Gorman
Peter Pentchev
Peter Piekarski
@@ -2374,44 +2497,45 @@ Peter Verhas
Peter Wang
Peter Wu
Peter Wullinger
-Peteris Krumins
Petr Bahula
Petr Novak
Petr Pisar
-Petr Voytsik
Petr Štetiar
+Petr Voytsik
Phil Blundell
Phil Crump
Phil E. Taylor
-Phil Karn
-Phil Lisiecki
-Phil Pellouchoud
Philip Chan
Philip Craig
Philip Gladstone
Philip Heiduck
Philip Langdale
-Philip Prindeville
-Philip Sanetra
-Philipp Engel
-Philipp Klaus Krause
-Philipp Waehnert
+Philippe Antoine
Philippe Antoine on HackerOne
Philippe Hameau
Philippe Marguinaud
+Philipp Engel
Philippe Raoult
Philippe Vaucher
+Philipp Klaus Krause
+Philip Prindeville
+Philipp Waehnert
+Philip Sanetra
+Phil Karn
+Phil Lisiecki
+Phil Pellouchoud
Pierre
Pierre Brico
Pierre Chapuis
+Pierre-Etienne Meunier
Pierre Joye
Pierre Yager
Pierre Ynard
-Pierre-Etienne Meunier
Pierre-Yves Bigourdan
Pierrick Charron
Piotr Dobrogost
Piotr Komborski
+PleaseJustDont
Po-Chuan Hsieh
Pontakorn Prasertsuk
Pontus Lundkvist
@@ -2426,30 +2550,32 @@ Priyanka Shah
ProceduralMan on github
promptfuzz_ on hackerone
Pronyushkin Petr
+prpr19xx on github
+P R Schaffner
Przemysław Tomaszewski
pszemus on github
pszlazak on github
puckipedia on github
Puneet Pawaia
-Pēteris Caune
+qhill
+qhill on github
qiandu2006 on github
+Qriist on github
Quagmire
Quanah Gibson-Mount
Quentin Balland
Quinn Slack
-r-a-sattarov on github
-R. Dennis Steed
Radek Brich
Radek Zajic
Radoslav Georgiev
Radu Hociung
Radu Simionescu
-Rafa Muyo
Rafael Antonio
-Rafael Sagula
-Rafayel Mkrtchyan
Rafaël Carré
+Rafael Sagula
Rafał Mikrut
+Rafa Muyo
+Rafayel Mkrtchyan
Rahul Krishna M
Rainer Canavan
Rainer Jung
@@ -2459,22 +2585,25 @@ RainRat
Raito Bezarius
Rajesh Naganathan
Rajkumar Mandal
-Ralf S. Engelschall
+Ralf A. Timmermann
ralfjunker on github
+Ralf S. Engelschall
Ralph Beckmann
Ralph Langendam
Ralph Mitchell
-Ram Krushna Mishra
+Ralph Sennhauser
Ramiro Garcia
+Ram Krushna Mishra
rampageX on github
ramsay-jones on github
-Ran Mozes
RanBarLavie on github
Randall S. Becker
Randolf J
Randy Armstrong
Randy McMurchy
+Ran Mozes
Raphael Gozzo
+r-a-sattarov on github
Rasmus Melchior Jacobsen
Rasmus Thomsen
Raul Onitza-Klugman
@@ -2485,6 +2614,7 @@ Ray Satiro
Razvan Cojocaru
Razvan Pricope
rcombs on github
+R. Dennis Steed
Red Hat Product Security
Reed Loden
Reinhard Max
@@ -2493,6 +2623,7 @@ RekGRpth on github
Remco van Hooff
Remi Gacogne
Remo E
+Rémy Léone
Renato Botelho
Renaud Allard
Renaud Chaillat
@@ -2510,15 +2641,8 @@ Rianov Viacheslav
riastradh on github
Ricardo Cadime
Ricardo Gomes
-Ricardo M. Correia
Ricardo Martins
-Rich Burridge
-Rich FitzJohn
-Rich Gray
-Rich Mirch
-Rich Rauenzahn
-Rich Salz
-Rich Turner
+Ricardo M. Correia
Richard Adams
Richard Alcock
Richard Archer
@@ -2538,41 +2662,46 @@ Richard Moore
Richard Prescott
Richard Silverman
Richard van den Berg
-Richard W.M. Jones
Richard Whitehouse
+Richard W.M. Jones
+Rich Burridge
+Rich FitzJohn
+Rich Gray
+Rich Mirch
+Rich Rauenzahn
+Rich Salz
+Rich Turner
Richy Kim
Rici Lake
+Rickard Hallerbäck
Rick Deist
+Ricki Hirner
Rick Jones
Rick Lane
Rick Richardson
Rick Welykochy
-Rickard Hallerbäck
-Ricki Hirner
Ricky Leverence
Ricky-Tigg on github
-Rider Linden
RiderALT on github
+Rider Linden
Rikard Falkeborn
rilysh
+Rinku Das
rl1987 on github
+rmg-x on github
Rob Boeckermann
+RobBotic1 on github
+Robby Simpson
Rob Cotrone
Rob Crittenden
Rob Davies
Rob de Wit
-Rob Jones
-Rob Sanders
-Rob Stanzel
-Rob Ward
-RobBotic1 on github
-Robby Simpson
Robert A. Monat
Robert B. Harris
Robert Brose
Robert Charles Muir
-Robert D. Young
Robert Dunaj
+Robert D. Young
Robert Foreman
Robert Iakobashvili
Robert Kolcun
@@ -2593,12 +2722,16 @@ Robin Douine
Robin Johnson
Robin Kay
Robin Marx
+Rob Jones
+Rob Sanders
Robson Braga Araujo
-Rod Widdowson
+Rob Stanzel
+Rob Ward
Rodger Combs
Rodney Simmons
Rodric Glaser
Rodrigo Silva
+Rod Widdowson
Roger Leigh
Roger Orr
Roger Young
@@ -2614,12 +2747,14 @@ Romain Geissler
romamik om github
Roman Koifman
Roman Mamedov
+Roman Zharkov
Romulo A. Ceccon
+Ronald Crane
+Ronan Pigott
Ron Eldor
+Ronnie Mose
Ron Parker
Ron Zapp
-Ronan Pigott
-Ronnie Mose
Rosen Penev
Rosimildo da Silva
Ross Burton
@@ -2627,6 +2762,7 @@ roughtex on github
Roy Bellingan
Roy Li
Roy Shan
+RubisetCie on github
Rudi Heitbaum
Rui LIU
Rui Pinheiro
@@ -2639,6 +2775,7 @@ Ruurd Beerstra
rwmjones on github
Ryan Beck-Buysse
Ryan Braud
+ryancaicse on github
Ryan Carsten Schmidt
Ryan Chan
Ryan Mast
@@ -2647,28 +2784,27 @@ Ryan Schmidt
Ryan Scott
Ryan Sleevi
Ryan Winograd
-ryancaicse on github
Ryuichi KAWAMATA
rzrymiak on github
-Rémy Léone
-S. Moonesamy
s0urc3_ on hackerone
+saimen
Sai Ram Kunala
Salah-Eddin Shaban
Saleem Abdulrasool
SaltyMilk
Salvador Dávila
Salvatore Sorrentino
+Samanta Navarro
Sam Deane
Sam Hurst
Sam James
Sam Jessup
+Sampo Kellomaki
Sam Roth
Sam Schanken
-Samanta Navarro
-Sampo Kellomaki
Samuel Chiang
Samuel Díaz García
+Samuel Dionne-Riel
Samuel Henrique
Samuel Listopad
Samuel Marks
@@ -2684,6 +2820,7 @@ Santhana Todatry
Santino Keupp
Saqib Ali
Sara Golemon
+Sarah Gooding
Saran Neti
Sascha Swiercy
Sascha Zengler
@@ -2693,6 +2830,7 @@ Saul good
saurabhsingh-dev on github
Saurav Babu
sayrer on github
+sbernatsky on github
SBKarr on github
Scarlett McAllister
Scott Bailey
@@ -2718,6 +2856,8 @@ Sebastian Pohlschmidt
Sebastian Rasmussen
Sebastian Sterk
Sebastian Walz
+Sébastien Helleu
+Sébastien Willemijns
selmelc on hackerone
SendSonS on github
Senthil Raja Velu
@@ -2737,8 +2877,8 @@ Sergio Ballestrero
Sergio Barresi
Sergio Borghese
Sergio Durigan Junior
-Sergio Mijatovic
Sergio-IME on github
+Sergio Mijatovic
sergio-nsk on github
Serj Kalichev
Sertonix
@@ -2748,7 +2888,6 @@ Seth Mos
Sevan Janiyan
sfan5 on github
Sgharat on github
-Sh Diao
Shachaf Ben-Kiki
ShadowZzj on github
Shailesh Kapse
@@ -2761,6 +2900,7 @@ Shaun Jackman
Shaun Mirani
Shawn Landden
Shawn Poulson
+Sh Diao
Sheshadri.V
Shikha Sharma
Shine Fan
@@ -2789,6 +2929,7 @@ Siva Sivaraman
Slaven Rezić
SLDiggie on github
Smackd0wn on github
+S. Moonesamy
smuellerDD on github
sn on hackerone
sofaboss on github
@@ -2796,6 +2937,7 @@ Sohom Datta
Somnath Kundu
Song Ma
Sonia Subramanian
+Sören Tempel
southernedge on github
Spacen Jasset
Spezifant on github
@@ -2807,11 +2949,11 @@ sspiri on github
sstruchtrup on github
Stadler Stephan
Stan Hu
-Stan van de Burgt
Stanislav Ivochkin
Stanislav Lange
Stanislav Zidek
Stanley Wucw
+Stan van de Burgt
Stathis Kapnidis
Stav Nir
steelman on github
@@ -2824,25 +2966,26 @@ Stefan Huber
Stefan Kanthak
Stefan Karpinski
Stefan Krause
+Štefan Kremeň
Stefan Neis
+Stefano Simonelli
Stefan Strogin
Stefan Talpalaru
Stefan Teleman
Stefan Tomanek
Stefan Ulrich
Stefan Yohansson
-Stefano Simonelli
Steffen Kieß
Steinar H. Gunderson
steini2000 on github
Stepan Broz
Stepan Efremov
Stephan Bergmann
+Stephane Pellegrino
Stephan Guilloux
Stephan Lagerholm
Stephan Mühlstrasser
Stephan Szabo
-Stephane Pellegrino
Stephen Boost
Stephen Brokenshire
Stephen Collyer
@@ -2853,23 +2996,24 @@ Stephen More
Stephen Toub
Sterling Hughes
Steve Green
-Steve H Truong
Steve Havelka
Steve Herrell
Steve Holme
+Steve H Truong
Steve Lhomme
Steve Little
Steve Marx
-Steve Oliphant
-Steve Roskowski
-Steve Walch
Steven Allen
Steven Bazyl
Steven G. Johnson
Steven Gu
Steven M. Schweda
+stevenpackardblp on github
Steven Parkes
Steven Penny
+Steve Oliphant
+Steve Roskowski
+Steve Walch
Stewart Gebbie
Stian Soiland-Reyes
Stoned Elipot
@@ -2881,6 +3025,7 @@ Sune Ahlgren
Sunny Bean
Sunny Purushe
SuperIlu on github
+SuperStormer on github
Sven Anders
Sven Blumenstein
Sven Neuhaus
@@ -2890,15 +3035,13 @@ swalkaus at yahoo.com
sylgal on github
Sylvestre Ledru
Symeon Paraschoudis
-Sébastien Helleu
-Sébastien Willemijns
-T. Bharath
-T. Yamada
T200proX7 on github
Tadej Vengust
Tae Hyoung Ahn
Taiyu Len
Tal Regev
+Tamás Bálint Misius
+Tamir Duberstein
Taneli Vähäkangas
Tanguy Fautre
Taras Kushnir
@@ -2906,21 +3049,27 @@ tarek112 on github
Tatsuhiko Miyagawa
Tatsuhiro Tsujikawa
tawmoto on github
+T. Bharath
tbugfinder on github
Ted Lyngmo
Teemu Yli-Elsila
+Teh Kok How
Temprimus
+Terence Eden
Terri Oda
Terry Wu
thanhchungbtc on github
-The Infinnovation team
TheAssassin on github
+The Infinnovation team
TheKnarf on github
Theo
Theodore Dubois
therealhirudo on github
Thiago Suchorski
+thisisgk on github
tholin on github
+Thomas
+Thomas1664 on github
Thomas Bouzerar
Thomas Braun
Thomas Danielsson
@@ -2930,8 +3079,8 @@ Thomas Glanzmann
Thomas Guillem
Thomas J. Moore
Thomas Klausner
-Thomas L. Shinnick
Thomas Lopatic
+Thomas L. Shinnick
Thomas M. DuBuisson
Thomas Petazzoni
Thomas Pyle
@@ -2942,9 +3091,9 @@ Thomas Tonino
Thomas van Hesteren
Thomas Vegas
Thomas Weißschuh
-Thomas1664 on github
Thorsten Klein
Thorsten Schöning
+Tianyi Song
Tiit Pikma
Till Maas
Till Wegmüller
@@ -2957,26 +3106,30 @@ Tim Harder
Tim Heckman
Tim Hill
Tim Mcdonough
+Timmy Schierling
Tim Newsome
+Timo Lange
+Timo Sirainen
+Timotej Lazar
+Timothe Litt
+Timothy Gu
+Timothy Polich
+Timo Tijhof
Tim Rühsen
Tim Sedlmeyer
Tim Sneddon
Tim Stack
Tim Starling
Tim Tassonis
+Timur Artikov
Tim Verhoeven
Tim Yuer
-Timmy Schierling
-Timo Lange
-Timo Sirainen
-Timotej Lazar
-Timothe Litt
-Timothy Gu
-Timothy Polich
-Timur Artikov
Tinus van den Berg
+tinyboxvk
+tiymat
TJ Saunders
Tk Xiong
+tkzv on github
tlahn on github
tmkk on github
Tobias Blomberg
@@ -2999,22 +3152,6 @@ Todd Kaufmann
Todd Kulesza
Todd Short
Todd Vierling
-Tom Benoist
-Tom Donovan
-Tom Eccles
-Tom G. Christensen
-Tom Grace
-Tom Greenslade
-Tom Lee
-Tom Mattison
-Tom Moers
-Tom Mueller
-Tom Regner
-Tom Seddon
-Tom Sparrow
-Tom van der Woerdt
-Tom Wright
-Tom Zerucha
Tomas Berger
Tomas Hoger
Tomas Jakobsson
@@ -3023,31 +3160,51 @@ Tomas Mraz
Tomas Pospisek
Tomas Szepe
Tomas Tomecek
+Tomas Volf
Tomasz Kojm
Tomasz Lacki
+Tom Benoist
+Tom Donovan
+Tom Eccles
+Tom G. Christensen
+Tom Grace
+Tom Greenslade
+Tom Lee
+Tom Mattison
Tommie Gannert
tommink[at]post.pl
+Tom Moers
+Tom Mueller
Tommy Chiang
Tommy Odom
Tommy Petty
Tommy Tam
+Tom Regner
+Tom Seddon
+Tom Sparrow
+Tom van der Woerdt
+Tom Wright
tomy2105 on github
-Ton Voon
+Tom Zerucha
Toni Moreno
+Ton Voon
Tony Kelman
-tonystz on Github
+tonystz on github
Toon Claes
Toon Verwaest
Tor Arntsen
Torben Dannhauer
Torben Dury
+Török Edwin
Torsten Foertsch
Toshio Kuratomi
Toshiyuki Maezawa
tpaukrt on github
Traian Nicolescu
Trail of Bits
+tranzystorekk on github
Travis Burtrum
+Travis Lane
Travis Obenhaus
Trivikram Kamat
Troels Walsted Hansen
@@ -3060,24 +3217,24 @@ Tuomas Siipola
Tuomo Rinne
Tupone Alfredo
Turiiya
+T. Yamada
Tyler Hall
-Török Edwin
u20221022 on github
Ulf Härnhammar
Ulf Samuelsson
Ulrich Doehner
Ulrich Telle
Ulrich Zadow
-UnicornZhang on Github
+UnicornZhang on github
updatede on github
UrsusArctos on github
User Sg
ustcqidi on github
Vadim Grinshpun
Valentin David
+Valentín Gutiérrez
Valentin Richter
Valentyn Korniienko
-Valentín Gutiérrez
Valerii Zapodovnikov
vanillajonathan on github
Varnavas Papaioannou
@@ -3086,14 +3243,14 @@ Vasiliy Ulyanov
Vasily Lobaskin
Vasy Okhin
Venkat Akella
-Venkat Krishna R
Venkataramana Mokkapati
+Venkat Krishna R
Vicente Garcia
Victor Kislov
Victor Magierski
Victor Snezhko
-Victor Vieux
VictorVG on github
+Victor Vieux
Vijay Panghal
Vikram Saxena
Viktor Petersson
@@ -3111,30 +3268,31 @@ Vincent Torri
violetlige on github
vitaha85 on github
Vitaly Varyvdin
+Vítor Galvão
vl409 on github
Vlad Grachov
-Vlad Ureche
Vladimir Grishchenko
Vladimir Kotal
Vladimir Lazarenko
Vladimir Panteleev
Vladimir Varlamov
+Vladislavs Sokurenko
+Vlad Ureche
Vlastimil Ovčáčík
vlkl-sap on github
vlubart on github
Vojtech Janota
-Vojtech Minarik
Vojtěch Král
+Vojtech Minarik
Volker Schmid
Vollstrecker on github
Vsevolod Novikov
vshmuk on hackerone
vulnerabilityspotter on hackerone
+Vulpes Vulpes
vuonganh1993 on github
vvb2060 on github
Vyron Tsingaras
-Vítor Galvão
-W. Mark Kubacki
w0x42 on hackerone
Waldek Kozba
Walter J. Mack
@@ -3144,6 +3302,7 @@ Warren Menzer
Wayne Haigh
Wei Chong Tan
Wenchao Li
+Weng Xuetian
Wenxiang Qian
Werner Koch
Werner Stolz
@@ -3156,17 +3315,19 @@ Wham Bang
Wilfredo Sanchez
Wilhelm von Thiele
Will Dietz
-Will Roberts
Willem Hoek
Willem Sparreboom
-William A. Rowe Jr
William Ahern
+William A. Rowe Jr
William Desportes
William Tang
+Will Roberts
Winni Neessen
+W. Mark Kubacki
wmsch on github
wncboy on github
Wojciech Zwiefka
+wolfsage on hackerone
Wolf Vollprecht
Wouter Van Rooy
Wu Yongzheng
@@ -3174,22 +3335,26 @@ Wu Zheng
wxiaoguang on github
Wyatt O'Day
Wyatt OʼDay
+x1sc0 on github
x2018 on github
Xavier Bouchoux
XhmikosR on github
XhstormR on github
-Xi Ruoyao
-Xiang Xiao
+xiadnoring on github
Xiangbin Li
xianghongai on github
+Xiang Xiao
+xiaofeng
Xiaoke Wang
Xiaoyin Liu
+Xì Gà
+Xi Ruoyao
XmiliaH on github
xnynx on github
xtonik on github
xwxbug on github
+x-xiang on github
XYenon
-Xì Gà
Yaakov Selkowitz
Yadhu Krishna M
Yair Lenga
@@ -3203,8 +3368,9 @@ Yedaya Katsman
Yehezkel Horowitz
Yehoshua Hershberg
ygthien on github
-Yi Huang
Yifei Kong
+Yihang Zhou
+Yi Huang
Yiming Jing
Yingwei Liu
yiyuaner on github
@@ -3215,15 +3381,16 @@ Yoshimasa Ohno
Younes El-karama
youngchopin on github
Yousuke Kimoto
-Yu Xin
Yukihiro Kawada
Yun SangHo
-Yuri Slobodyanyuk
Yurii Rashkovskii
+Yuri Slobodyanyuk
Yuriy Chernyshov
Yuriy Sosov
yushicheng7788 on github
Yusuke Nakamura
+Yu Xin
+Yuyi Wang
Yves Arrouye
Yves Lejeune
YX Hao
@@ -3237,10 +3404,12 @@ zengwei2000
Zenju on github
Zero King
Zespre Schmidt
-Zhang Xiuhua
zhanghu on xiaomi
-Zhao Yisha
+Zhang Wen
+Zhang Xiuhua
+Zhaoming Luo
Zhaoyang Wu
+Zhao Yisha
zhengqwe on github
Zhibiao Wu
zhihaoy on github
@@ -3250,14 +3419,9 @@ zjyhjqs
zloi-user on github
zmcx16 on github
Zmey Petroff
+zopsicle on github
Zvi Har'El
zzq1015 on github
-Ádler Jonas Gross
-Érico Nogueira
-Érico Nogueira Rolim
-İsmail Dönmez
-Łukasz Domeradzki
-Štefan Kremeň
Борис Верховский
Коваленко Анатолий Викторович
наб
diff --git a/libs/libcurl/include/curl/curl.h b/libs/libcurl/include/curl/curl.h
index e4a4d972c6..039f688be8 100644
--- a/libs/libcurl/include/curl/curl.h
+++ b/libs/libcurl/include/curl/curl.h
@@ -42,7 +42,7 @@
!defined(CURL_DISABLE_DEPRECATION) && !defined(BUILDING_LIBCURL)
#define CURL_DEPRECATED(version, message) \
__attribute__((deprecated("since " # version ". " message)))
-#if defined(__IAR_SYSTEMS_ICC__)
+#ifdef __IAR_SYSTEMS_ICC__
#define CURL_IGNORE_DEPRECATION(statements) \
_Pragma("diag_suppress=Pe1444") \
statements \
@@ -97,19 +97,11 @@
#include <sys/select.h>
#endif
-#if !defined(_WIN32) && !defined(_WIN32_WCE)
+#ifndef _WIN32
#include <sys/socket.h>
-#endif
-
-#if !defined(_WIN32)
#include <sys/time.h>
#endif
-/* Compatibility for non-Clang compilers */
-#ifndef __has_declspec_attribute
-# define __has_declspec_attribute(x) 0
-#endif
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -121,11 +113,17 @@ typedef void CURLSH;
* libcurl external API function linkage decorations.
*/
+#ifdef __has_declspec_attribute
+#define CURL_HAS_DECLSPEC_ATTRIBUTE(x) __has_declspec_attribute(x)
+#else
+#define CURL_HAS_DECLSPEC_ATTRIBUTE(x) 0
+#endif
+
#ifdef CURL_STATICLIB
# define CURL_EXTERN
#elif defined(_WIN32) || \
- (__has_declspec_attribute(dllexport) && \
- __has_declspec_attribute(dllimport))
+ (CURL_HAS_DECLSPEC_ATTRIBUTE(dllexport) && \
+ CURL_HAS_DECLSPEC_ATTRIBUTE(dllimport))
# if defined(BUILDING_LIBCURL)
# define CURL_EXTERN __declspec(dllexport)
# else
@@ -177,6 +175,16 @@ typedef enum {
#define CURLSSLBACKEND_CYASSL CURLSSLBACKEND_WOLFSSL
#define CURLSSLBACKEND_DARWINSSL CURLSSLBACKEND_SECURETRANSPORT
+/* bits for the CURLOPT_FOLLOWLOCATION option */
+#define CURLFOLLOW_ALL 1L /* generic follow redirects */
+
+/* Do not use the custom method in the follow-up request if the HTTP code
+ instructs so (301, 302, 303). */
+#define CURLFOLLOW_OBEYCODE 2L
+
+/* Only use the custom method in the first request, always reset in the next */
+#define CURLFOLLOW_FIRSTONLY 3L
+
struct curl_httppost {
struct curl_httppost *next; /* next entry in the list */
char *name; /* pointer to allocated name */
@@ -551,14 +559,14 @@ typedef enum {
CURLE_FTP_COULDNT_USE_REST, /* 31 - the REST command failed */
CURLE_OBSOLETE32, /* 32 - NOT USED */
CURLE_RANGE_ERROR, /* 33 - RANGE "command" did not work */
- CURLE_HTTP_POST_ERROR, /* 34 */
+ CURLE_OBSOLETE34, /* 34 */
CURLE_SSL_CONNECT_ERROR, /* 35 - wrong when connecting with SSL */
CURLE_BAD_DOWNLOAD_RESUME, /* 36 - could not resume download */
CURLE_FILE_COULDNT_READ_FILE, /* 37 */
CURLE_LDAP_CANNOT_BIND, /* 38 */
CURLE_LDAP_SEARCH_FAILED, /* 39 */
CURLE_OBSOLETE40, /* 40 - NOT USED */
- CURLE_FUNCTION_NOT_FOUND, /* 41 - NOT USED starting with 7.53.0 */
+ CURLE_OBSOLETE41, /* 41 - NOT USED starting with 7.53.0 */
CURLE_ABORTED_BY_CALLBACK, /* 42 */
CURLE_BAD_FUNCTION_ARGUMENT, /* 43 */
CURLE_OBSOLETE44, /* 44 - NOT USED */
@@ -637,12 +645,31 @@ typedef enum {
CURLE_UNRECOVERABLE_POLL, /* 99 - poll/select returned fatal error */
CURLE_TOO_LARGE, /* 100 - a value/data met its maximum */
CURLE_ECH_REQUIRED, /* 101 - ECH tried but failed */
- CURL_LAST /* never use! */
+ CURL_LAST, /* never use! */
+
+ CURLE_RESERVED115 = 115, /* 115-126 - used in tests */
+ CURLE_RESERVED116 = 116,
+ CURLE_RESERVED117 = 117,
+ CURLE_RESERVED118 = 118,
+ CURLE_RESERVED119 = 119,
+ CURLE_RESERVED120 = 120,
+ CURLE_RESERVED121 = 121,
+ CURLE_RESERVED122 = 122,
+ CURLE_RESERVED123 = 123,
+ CURLE_RESERVED124 = 124,
+ CURLE_RESERVED125 = 125,
+ CURLE_RESERVED126 = 126
} CURLcode;
#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all
the obsolete stuff removed! */
+/* removed in 7.53.0 */
+#define CURLE_FUNCTION_NOT_FOUND CURLE_OBSOLETE41
+
+/* removed in 7.56.0 */
+#define CURLE_HTTP_POST_ERROR CURLE_OBSOLETE34
+
/* Previously obsolete error code reused in 7.38.0 */
#define CURLE_OBSOLETE16 CURLE_HTTP2
@@ -900,12 +927,13 @@ typedef int
/* parameter for the CURLOPT_USE_SSL option */
+#define CURLUSESSL_NONE 0L /* do not attempt to use SSL */
+#define CURLUSESSL_TRY 1L /* try using SSL, proceed anyway otherwise */
+#define CURLUSESSL_CONTROL 2L /* SSL for the control connection or fail */
+#define CURLUSESSL_ALL 3L /* SSL for all communication or fail */
+
typedef enum {
- CURLUSESSL_NONE, /* do not attempt to use SSL */
- CURLUSESSL_TRY, /* try using SSL, proceed anyway otherwise */
- CURLUSESSL_CONTROL, /* SSL for the control connection or fail */
- CURLUSESSL_ALL, /* SSL for all communication or fail */
- CURLUSESSL_LAST /* not an option, never use */
+ CURLUSESSL_LAST = 4 /* not an option, never use */
} curl_usessl;
/* Definition of bits for the CURLOPT_SSL_OPTIONS argument: */
@@ -1009,6 +1037,12 @@ typedef enum {
#define CURLALTSVC_H2 (1<<4)
#define CURLALTSVC_H3 (1<<5)
+/* bitmask values for CURLOPT_UPLOAD_FLAGS */
+#define CURLULFLAG_ANSWERED (1L<<0)
+#define CURLULFLAG_DELETED (1L<<1)
+#define CURLULFLAG_DRAFT (1L<<2)
+#define CURLULFLAG_FLAGGED (1L<<3)
+#define CURLULFLAG_SEEN (1L<<4)
struct curl_hstsentry {
char *name;
@@ -1953,10 +1987,10 @@ typedef enum {
/* Set stream weight, 1 - 256 (default is 16) */
CURLOPT(CURLOPT_STREAM_WEIGHT, CURLOPTTYPE_LONG, 239),
- /* Set stream dependency on another CURL handle */
+ /* Set stream dependency on another curl handle */
CURLOPT(CURLOPT_STREAM_DEPENDS, CURLOPTTYPE_OBJECTPOINT, 240),
- /* Set E-xclusive stream dependency on another CURL handle */
+ /* Set E-xclusive stream dependency on another curl handle */
CURLOPT(CURLOPT_STREAM_DEPENDS_E, CURLOPTTYPE_OBJECTPOINT, 241),
/* Do not send any tftp option requests to the server */
@@ -2222,6 +2256,11 @@ typedef enum {
/* maximum number of keepalive probes (Linux, *BSD, macOS, etc.) */
CURLOPT(CURLOPT_TCP_KEEPCNT, CURLOPTTYPE_LONG, 326),
+ CURLOPT(CURLOPT_UPLOAD_FLAGS, CURLOPTTYPE_LONG, 327),
+
+ /* set TLS supported signature algorithms */
+ CURLOPT(CURLOPT_SSL_SIGNATURE_ALGORITHMS, CURLOPTTYPE_STRINGPOINT, 328),
+
CURLOPT_LASTENTRY /* the last unused */
} CURLoption;
@@ -2270,26 +2309,25 @@ typedef enum {
/* Convenient "aliases" */
#define CURLOPT_RTSPHEADER CURLOPT_HTTPHEADER
- /* These enums are for use with the CURLOPT_HTTP_VERSION option. */
-enum {
- CURL_HTTP_VERSION_NONE, /* setting this means we do not care, and that we
- would like the library to choose the best
- possible for us! */
- CURL_HTTP_VERSION_1_0, /* please use HTTP 1.0 in the request */
- CURL_HTTP_VERSION_1_1, /* please use HTTP 1.1 in the request */
- CURL_HTTP_VERSION_2_0, /* please use HTTP 2 in the request */
- CURL_HTTP_VERSION_2TLS, /* use version 2 for HTTPS, version 1.1 for HTTP */
- CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE, /* please use HTTP 2 without HTTP/1.1
- Upgrade */
- CURL_HTTP_VERSION_3 = 30, /* Use HTTP/3, fallback to HTTP/2 or HTTP/1 if
- needed. For HTTPS only. For HTTP, this option
- makes libcurl return error. */
- CURL_HTTP_VERSION_3ONLY = 31, /* Use HTTP/3 without fallback. For HTTPS
- only. For HTTP, this makes libcurl
- return error. */
-
- CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */
-};
+/* These constants are for use with the CURLOPT_HTTP_VERSION option. */
+#define CURL_HTTP_VERSION_NONE 0L /* setting this means we do not care, and
+ that we would like the library to choose
+ the best possible for us! */
+#define CURL_HTTP_VERSION_1_0 1L /* please use HTTP 1.0 in the request */
+#define CURL_HTTP_VERSION_1_1 2L /* please use HTTP 1.1 in the request */
+#define CURL_HTTP_VERSION_2_0 3L /* please use HTTP 2 in the request */
+#define CURL_HTTP_VERSION_2TLS 4L /* use version 2 for HTTPS, version 1.1 for
+ HTTP */
+#define CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE 5L /* please use HTTP 2 without
+ HTTP/1.1 Upgrade */
+#define CURL_HTTP_VERSION_3 30L /* Use HTTP/3, fallback to HTTP/2 or
+ HTTP/1 if needed. For HTTPS only. For
+ HTTP, this option makes libcurl
+ return error. */
+#define CURL_HTTP_VERSION_3ONLY 31L /* Use HTTP/3 without fallback. For
+ HTTPS only. For HTTP, this makes
+ libcurl return error. */
+#define CURL_HTTP_VERSION_LAST 32L /* *ILLEGAL* http version */
/* Convenience definition simple because the name of the version is HTTP/2 and
not 2.0. The 2_0 version of the enum name was set while the version was
@@ -2299,32 +2337,33 @@ enum {
/*
* Public API enums for RTSP requests
*/
-enum {
- CURL_RTSPREQ_NONE, /* first in list */
- CURL_RTSPREQ_OPTIONS,
- CURL_RTSPREQ_DESCRIBE,
- CURL_RTSPREQ_ANNOUNCE,
- CURL_RTSPREQ_SETUP,
- CURL_RTSPREQ_PLAY,
- CURL_RTSPREQ_PAUSE,
- CURL_RTSPREQ_TEARDOWN,
- CURL_RTSPREQ_GET_PARAMETER,
- CURL_RTSPREQ_SET_PARAMETER,
- CURL_RTSPREQ_RECORD,
- CURL_RTSPREQ_RECEIVE,
- CURL_RTSPREQ_LAST /* last in list */
-};
+
+#define CURL_RTSPREQ_NONE 0L
+#define CURL_RTSPREQ_OPTIONS 1L
+#define CURL_RTSPREQ_DESCRIBE 2L
+#define CURL_RTSPREQ_ANNOUNCE 3L
+#define CURL_RTSPREQ_SETUP 4L
+#define CURL_RTSPREQ_PLAY 5L
+#define CURL_RTSPREQ_PAUSE 6L
+#define CURL_RTSPREQ_TEARDOWN 7L
+#define CURL_RTSPREQ_GET_PARAMETER 8L
+#define CURL_RTSPREQ_SET_PARAMETER 9L
+#define CURL_RTSPREQ_RECORD 10L
+#define CURL_RTSPREQ_RECEIVE 11L
+#define CURL_RTSPREQ_LAST 12L /* not used */
/* These enums are for use with the CURLOPT_NETRC option. */
+#define CURL_NETRC_IGNORED 0L /* The .netrc will never be read.
+ This is the default. */
+#define CURL_NETRC_OPTIONAL 1L /* A user:password in the URL will be preferred
+ to one in the .netrc. */
+#define CURL_NETRC_REQUIRED 2L /* A user:password in the URL will be ignored.
+ Unless one is set programmatically, the
+ .netrc will be queried. */
enum CURL_NETRC_OPTION {
- CURL_NETRC_IGNORED, /* The .netrc will never be read.
- * This is the default. */
- CURL_NETRC_OPTIONAL, /* A user:password in the URL will be preferred
- * to one in the .netrc. */
- CURL_NETRC_REQUIRED, /* A user:password in the URL will be ignored.
- * Unless one is set programmatically, the .netrc
- * will be queried. */
- CURL_NETRC_LAST
+ /* we set a single member here, just to make sure we still provide the enum,
+ but the values to use are defined above with L suffixes */
+ CURL_NETRC_LAST = 3
};
#define CURL_SSLVERSION_DEFAULT 0
@@ -2348,10 +2387,13 @@ enum CURL_NETRC_OPTION {
/* never use, keep last */
#define CURL_SSLVERSION_MAX_LAST (CURL_SSLVERSION_LAST << 16)
+#define CURL_TLSAUTH_NONE 0L
+#define CURL_TLSAUTH_SRP 1L
+
enum CURL_TLSAUTH {
- CURL_TLSAUTH_NONE,
- CURL_TLSAUTH_SRP,
- CURL_TLSAUTH_LAST /* never use, keep last */
+ /* we set a single member here, just to make sure we still provide the enum,
+ but the values to use are defined above with L suffixes */
+ CURL_TLSAUTH_LAST = 2
};
/* symbols to use with CURLOPT_POSTREDIR.
@@ -2366,14 +2408,16 @@ enum CURL_TLSAUTH {
#define CURL_REDIR_POST_ALL \
(CURL_REDIR_POST_301|CURL_REDIR_POST_302|CURL_REDIR_POST_303)
-typedef enum {
- CURL_TIMECOND_NONE,
-
- CURL_TIMECOND_IFMODSINCE,
- CURL_TIMECOND_IFUNMODSINCE,
- CURL_TIMECOND_LASTMOD,
+#define CURL_TIMECOND_NONE 0L
+#define CURL_TIMECOND_IFMODSINCE 1L
+#define CURL_TIMECOND_IFUNMODSINCE 2L
+#define CURL_TIMECOND_LASTMOD 3L
- CURL_TIMECOND_LAST
+typedef enum {
+ /* we set a single member here, just to make sure we still provide
+ the enum typedef, but the values to use are defined above with L
+ suffixes */
+ CURL_TIMECOND_LAST = 4
} curl_TimeCond;
/* Special size_t value signaling a null-terminated string. */
@@ -2769,17 +2813,17 @@ struct curl_slist {
* *before* curl_global_init().
*
* The backend can be identified by the id (e.g. CURLSSLBACKEND_OPENSSL). The
- * backend can also be specified via the name parameter (passing -1 as id).
- * If both id and name are specified, the name will be ignored. If neither id
- * nor name are specified, the function will fail with
- * CURLSSLSET_UNKNOWN_BACKEND and set the "avail" pointer to the
- * NULL-terminated list of available backends.
+ * backend can also be specified via the name parameter (passing -1 as id). If
+ * both id and name are specified, the name will be ignored. If neither id nor
+ * name are specified, the function will fail with CURLSSLSET_UNKNOWN_BACKEND
+ * and set the "avail" pointer to the NULL-terminated list of available
+ * backends.
*
* Upon success, the function returns CURLSSLSET_OK.
*
* If the specified SSL backend is not available, the function returns
- * CURLSSLSET_UNKNOWN_BACKEND and sets the "avail" pointer to a NULL-terminated
- * list of available SSL backends.
+ * CURLSSLSET_UNKNOWN_BACKEND and sets the "avail" pointer to a
+ * NULL-terminated list of available SSL backends.
*
* The SSL backend can be set only once. If it has already been set, a
* subsequent attempt to change it will result in a CURLSSLSET_TOO_LATE.
@@ -2953,7 +2997,9 @@ typedef enum {
CURLINFO_USED_PROXY = CURLINFO_LONG + 66,
CURLINFO_POSTTRANSFER_TIME_T = CURLINFO_OFF_T + 67,
CURLINFO_EARLYDATA_SENT_T = CURLINFO_OFF_T + 68,
- CURLINFO_LASTONE = 68
+ CURLINFO_HTTPAUTH_USED = CURLINFO_LONG + 69,
+ CURLINFO_PROXYAUTH_USED = CURLINFO_LONG + 70,
+ CURLINFO_LASTONE = 70
} CURLINFO;
/* CURLINFO_RESPONSE_CODE is the new name for the option previously known as
@@ -3224,6 +3270,50 @@ CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask);
#define CURLPAUSE_ALL (CURLPAUSE_RECV|CURLPAUSE_SEND)
#define CURLPAUSE_CONT (CURLPAUSE_RECV_CONT|CURLPAUSE_SEND_CONT)
+/*
+ * NAME curl_easy_ssls_import()
+ *
+ * DESCRIPTION
+ *
+ * The curl_easy_ssls_import function adds a previously exported SSL session
+ * to the SSL session cache of the easy handle (or the underlying share).
+ */
+CURL_EXTERN CURLcode curl_easy_ssls_import(CURL *handle,
+ const char *session_key,
+ const unsigned char *shmac,
+ size_t shmac_len,
+ const unsigned char *sdata,
+ size_t sdata_len);
+
+/* This is the curl_ssls_export_cb callback prototype. It
+ * is passed to curl_easy_ssls_export() to extract SSL sessions/tickets. */
+typedef CURLcode curl_ssls_export_cb(CURL *handle,
+ void *userptr,
+ const char *session_key,
+ const unsigned char *shmac,
+ size_t shmac_len,
+ const unsigned char *sdata,
+ size_t sdata_len,
+ curl_off_t valid_until,
+ int ietf_tls_id,
+ const char *alpn,
+ size_t earlydata_max);
+
+/*
+ * NAME curl_easy_ssls_export()
+ *
+ * DESCRIPTION
+ *
+ * The curl_easy_ssls_export function iterates over all SSL sessions stored
+ * in the easy handle (or underlying share) and invokes the passed
+ * callback.
+ *
+ */
+CURL_EXTERN CURLcode curl_easy_ssls_export(CURL *handle,
+ curl_ssls_export_cb *export_fn,
+ void *userptr);
+
+
#ifdef __cplusplus
} /* end of extern "C" */
#endif
@@ -3236,9 +3326,7 @@ CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask);
#include "options.h"
#include "header.h"
#include "websockets.h"
-#ifndef CURL_SKIP_INCLUDE_MPRINTF
#include "mprintf.h"
-#endif
/* the typechecker does not work in C++ (yet) */
#if defined(__GNUC__) && defined(__GNUC_MINOR__) && \
diff --git a/libs/libcurl/include/curl/curlver.h b/libs/libcurl/include/curl/curlver.h
index 7efc6c21b9..1514abefb2 100644
--- a/libs/libcurl/include/curl/curlver.h
+++ b/libs/libcurl/include/curl/curlver.h
@@ -32,13 +32,13 @@
/* This is the version number of the libcurl package from which this header
file origins: */
-#define LIBCURL_VERSION "8.11.0"
+#define LIBCURL_VERSION "8.14.1"
/* The numeric version number is also available "in parts" by using these
defines: */
#define LIBCURL_VERSION_MAJOR 8
-#define LIBCURL_VERSION_MINOR 11
-#define LIBCURL_VERSION_PATCH 0
+#define LIBCURL_VERSION_MINOR 14
+#define LIBCURL_VERSION_PATCH 1
/* This is the numeric version of the libcurl version number, meant for easier
parsing and comparisons by programs. The LIBCURL_VERSION_NUM define will
@@ -59,7 +59,7 @@
CURL_VERSION_BITS() macro since curl's own configure script greps for it
and needs it to contain the full number.
*/
-#define LIBCURL_VERSION_NUM 0x080b00
+#define LIBCURL_VERSION_NUM 0x080e01
/*
* This is the date and time when the full source package was created. The
@@ -70,7 +70,7 @@
*
* "2007-11-23"
*/
-#define LIBCURL_TIMESTAMP "2024-11-06"
+#define LIBCURL_TIMESTAMP "2025-06-04"
#define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|(z))
#define CURL_AT_LEAST_VERSION(x,y,z) \
diff --git a/libs/libcurl/include/curl/easy.h b/libs/libcurl/include/curl/easy.h
index f21588d820..81c4b0f386 100644
--- a/libs/libcurl/include/curl/easy.h
+++ b/libs/libcurl/include/curl/easy.h
@@ -78,7 +78,7 @@ CURL_EXTERN CURL *curl_easy_duphandle(CURL *curl);
*
* DESCRIPTION
*
- * Re-initializes a CURL handle to the default values. This puts back the
+ * Re-initializes a curl handle to the default values. This puts back the
* handle to the same state as it was in when it was just created.
*
* It does keep: live connections, the Session ID cache, the DNS cache and the
diff --git a/libs/libcurl/include/curl/system.h b/libs/libcurl/include/curl/system.h
index 58e95943af..25a6b1a997 100644
--- a/libs/libcurl/include/curl/system.h
+++ b/libs/libcurl/include/curl/system.h
@@ -36,13 +36,10 @@
* curl_off_t
* ----------
*
- * For any given platform/compiler curl_off_t must be typedef'ed to a 64-bit
+ * For any given platform/compiler curl_off_t MUST be typedef'ed to a 64-bit
* wide signed integral data type. The width of this data type must remain
* constant and independent of any possible large file support settings.
*
- * As an exception to the above, curl_off_t shall be typedef'ed to a 32-bit
- * wide signed integral data type if there is no 64-bit type.
- *
* As a general rule, curl_off_t shall not be mapped to off_t. This rule shall
* only be violated if off_t is the only 64-bit data type available and the
* size of off_t is independent of large file support settings. Keep your
@@ -52,62 +49,24 @@
*
*/
-#if defined(__DJGPP__) || defined(__GO32__)
-# if defined(__DJGPP__) && (__DJGPP__ > 1)
-# define CURL_TYPEOF_CURL_OFF_T long long
-# define CURL_FORMAT_CURL_OFF_T "lld"
-# define CURL_FORMAT_CURL_OFF_TU "llu"
-# define CURL_SUFFIX_CURL_OFF_T LL
-# define CURL_SUFFIX_CURL_OFF_TU ULL
-# else
-# define CURL_TYPEOF_CURL_OFF_T long
-# define CURL_FORMAT_CURL_OFF_T "ld"
-# define CURL_FORMAT_CURL_OFF_TU "lu"
-# define CURL_SUFFIX_CURL_OFF_T L
-# define CURL_SUFFIX_CURL_OFF_TU UL
-# endif
-# define CURL_TYPEOF_CURL_SOCKLEN_T int
-
-#elif defined(__SALFORDC__)
-# define CURL_TYPEOF_CURL_OFF_T long
-# define CURL_FORMAT_CURL_OFF_T "ld"
-# define CURL_FORMAT_CURL_OFF_TU "lu"
-# define CURL_SUFFIX_CURL_OFF_T L
-# define CURL_SUFFIX_CURL_OFF_TU UL
+#ifdef __DJGPP__
+# define CURL_TYPEOF_CURL_OFF_T long long
+# define CURL_FORMAT_CURL_OFF_T "lld"
+# define CURL_FORMAT_CURL_OFF_TU "llu"
+# define CURL_SUFFIX_CURL_OFF_T LL
+# define CURL_SUFFIX_CURL_OFF_TU ULL
# define CURL_TYPEOF_CURL_SOCKLEN_T int
#elif defined(__BORLANDC__)
-# if (__BORLANDC__ < 0x520)
-# define CURL_TYPEOF_CURL_OFF_T long
-# define CURL_FORMAT_CURL_OFF_T "ld"
-# define CURL_FORMAT_CURL_OFF_TU "lu"
-# define CURL_SUFFIX_CURL_OFF_T L
-# define CURL_SUFFIX_CURL_OFF_TU UL
-# else
-# define CURL_TYPEOF_CURL_OFF_T __int64
-# define CURL_FORMAT_CURL_OFF_T "I64d"
-# define CURL_FORMAT_CURL_OFF_TU "I64u"
-# define CURL_SUFFIX_CURL_OFF_T i64
-# define CURL_SUFFIX_CURL_OFF_TU ui64
-# endif
-# define CURL_TYPEOF_CURL_SOCKLEN_T int
-
-#elif defined(__TURBOC__)
-# define CURL_TYPEOF_CURL_OFF_T long
-# define CURL_FORMAT_CURL_OFF_T "ld"
-# define CURL_FORMAT_CURL_OFF_TU "lu"
-# define CURL_SUFFIX_CURL_OFF_T L
-# define CURL_SUFFIX_CURL_OFF_TU UL
+# define CURL_TYPEOF_CURL_OFF_T __int64
+# define CURL_FORMAT_CURL_OFF_T "I64d"
+# define CURL_FORMAT_CURL_OFF_TU "I64u"
+# define CURL_SUFFIX_CURL_OFF_T i64
+# define CURL_SUFFIX_CURL_OFF_TU ui64
# define CURL_TYPEOF_CURL_SOCKLEN_T int
#elif defined(__POCC__)
-# if (__POCC__ < 280)
-# define CURL_TYPEOF_CURL_OFF_T long
-# define CURL_FORMAT_CURL_OFF_T "ld"
-# define CURL_FORMAT_CURL_OFF_TU "lu"
-# define CURL_SUFFIX_CURL_OFF_T L
-# define CURL_SUFFIX_CURL_OFF_TU UL
-# elif defined(_MSC_VER)
+# if defined(_MSC_VER)
# define CURL_TYPEOF_CURL_OFF_T __int64
# define CURL_FORMAT_CURL_OFF_T "I64d"
# define CURL_FORMAT_CURL_OFF_TU "I64u"
@@ -159,23 +118,38 @@
# define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int
#elif defined(__TANDEM)
-# if ! defined(__LP64)
- /* Required for 32-bit NonStop builds only. */
-# define CURL_TYPEOF_CURL_OFF_T long long
-# define CURL_FORMAT_CURL_OFF_T "lld"
-# define CURL_FORMAT_CURL_OFF_TU "llu"
-# define CURL_SUFFIX_CURL_OFF_T LL
-# define CURL_SUFFIX_CURL_OFF_TU ULL
-# define CURL_TYPEOF_CURL_SOCKLEN_T int
-# endif
+# if !defined(__LP64)
+# define CURL_TYPEOF_CURL_OFF_T long long
+# define CURL_FORMAT_CURL_OFF_T "lld"
+# define CURL_FORMAT_CURL_OFF_TU "llu"
+# define CURL_SUFFIX_CURL_OFF_T LL
+# define CURL_SUFFIX_CURL_OFF_TU ULL
+# define CURL_TYPEOF_CURL_SOCKLEN_T int
+# else
+# define CURL_TYPEOF_CURL_OFF_T long
+# define CURL_FORMAT_CURL_OFF_T "ld"
+# define CURL_FORMAT_CURL_OFF_TU "lu"
+# define CURL_SUFFIX_CURL_OFF_T L
+# define CURL_SUFFIX_CURL_OFF_TU UL
+# define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int
+# endif
-#elif defined(_WIN32_WCE)
-# define CURL_TYPEOF_CURL_OFF_T __int64
-# define CURL_FORMAT_CURL_OFF_T "I64d"
-# define CURL_FORMAT_CURL_OFF_TU "I64u"
-# define CURL_SUFFIX_CURL_OFF_T i64
-# define CURL_SUFFIX_CURL_OFF_TU ui64
-# define CURL_TYPEOF_CURL_SOCKLEN_T int
+#elif defined(UNDER_CE)
+# if defined(__MINGW32CE__)
+# define CURL_TYPEOF_CURL_OFF_T long long
+# define CURL_FORMAT_CURL_OFF_T "lld"
+# define CURL_FORMAT_CURL_OFF_TU "llu"
+# define CURL_SUFFIX_CURL_OFF_T LL
+# define CURL_SUFFIX_CURL_OFF_TU ULL
+# define CURL_TYPEOF_CURL_SOCKLEN_T int
+# else
+# define CURL_TYPEOF_CURL_OFF_T __int64
+# define CURL_FORMAT_CURL_OFF_T "I64d"
+# define CURL_FORMAT_CURL_OFF_TU "I64u"
+# define CURL_SUFFIX_CURL_OFF_T i64
+# define CURL_SUFFIX_CURL_OFF_TU ui64
+# define CURL_TYPEOF_CURL_SOCKLEN_T int
+# endif
#elif defined(__MINGW32__)
# include <inttypes.h>
@@ -220,13 +194,7 @@
# define CURL_FORMAT_CURL_OFF_TU "llu"
# define CURL_SUFFIX_CURL_OFF_T LL
# define CURL_SUFFIX_CURL_OFF_TU ULL
-# elif defined(_LP64)
-# define CURL_TYPEOF_CURL_OFF_T long
-# define CURL_FORMAT_CURL_OFF_T "ld"
-# define CURL_FORMAT_CURL_OFF_TU "lu"
-# define CURL_SUFFIX_CURL_OFF_T L
-# define CURL_SUFFIX_CURL_OFF_TU UL
-# else
+# else /* _LP64 and default */
# define CURL_TYPEOF_CURL_OFF_T long
# define CURL_FORMAT_CURL_OFF_T "ld"
# define CURL_FORMAT_CURL_OFF_TU "lu"
@@ -239,22 +207,13 @@
#elif defined(__370__)
# if defined(__IBMC__) || defined(__IBMCPP__)
-# if defined(_ILP32)
-# elif defined(_LP64)
-# endif
# if defined(_LONG_LONG)
# define CURL_TYPEOF_CURL_OFF_T long long
# define CURL_FORMAT_CURL_OFF_T "lld"
# define CURL_FORMAT_CURL_OFF_TU "llu"
# define CURL_SUFFIX_CURL_OFF_T LL
# define CURL_SUFFIX_CURL_OFF_TU ULL
-# elif defined(_LP64)
-# define CURL_TYPEOF_CURL_OFF_T long
-# define CURL_FORMAT_CURL_OFF_T "ld"
-# define CURL_FORMAT_CURL_OFF_TU "lu"
-# define CURL_SUFFIX_CURL_OFF_T L
-# define CURL_SUFFIX_CURL_OFF_TU UL
-# else
+# else /* _LP64 and default */
# define CURL_TYPEOF_CURL_OFF_T long
# define CURL_FORMAT_CURL_OFF_T "ld"
# define CURL_FORMAT_CURL_OFF_TU "lu"
@@ -349,24 +308,15 @@
#elif defined(_MSC_VER)
# if (_MSC_VER >= 1800)
# include <inttypes.h>
-# define CURL_TYPEOF_CURL_OFF_T __int64
# define CURL_FORMAT_CURL_OFF_T PRId64
# define CURL_FORMAT_CURL_OFF_TU PRIu64
-# define CURL_SUFFIX_CURL_OFF_T i64
-# define CURL_SUFFIX_CURL_OFF_TU ui64
-# elif (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64)
-# define CURL_TYPEOF_CURL_OFF_T __int64
+# else
# define CURL_FORMAT_CURL_OFF_T "I64d"
# define CURL_FORMAT_CURL_OFF_TU "I64u"
-# define CURL_SUFFIX_CURL_OFF_T i64
-# define CURL_SUFFIX_CURL_OFF_TU ui64
-# else
-# define CURL_TYPEOF_CURL_OFF_T long
-# define CURL_FORMAT_CURL_OFF_T "ld"
-# define CURL_FORMAT_CURL_OFF_TU "lu"
-# define CURL_SUFFIX_CURL_OFF_T L
-# define CURL_SUFFIX_CURL_OFF_TU UL
# endif
+# define CURL_TYPEOF_CURL_OFF_T __int64
+# define CURL_SUFFIX_CURL_OFF_T i64
+# define CURL_SUFFIX_CURL_OFF_TU ui64
# define CURL_TYPEOF_CURL_SOCKLEN_T int
/* ===================================== */
@@ -386,6 +336,8 @@
# define CURL_FORMAT_CURL_OFF_TU "llu"
# define CURL_SUFFIX_CURL_OFF_T LL
# define CURL_SUFFIX_CURL_OFF_TU ULL
+# define CURL_POPCOUNT64(x) __builtin_popcountll(x)
+# define CURL_CTZ64(x) __builtin_ctzll(x)
# elif defined(__LP64__) || \
defined(__x86_64__) || defined(__ppc64__) || defined(__sparc64__) || \
defined(__e2k__) || \
@@ -396,6 +348,8 @@
# define CURL_FORMAT_CURL_OFF_TU "lu"
# define CURL_SUFFIX_CURL_OFF_T L
# define CURL_SUFFIX_CURL_OFF_TU UL
+# define CURL_POPCOUNT64(x) __builtin_popcountl(x)
+# define CURL_CTZ64(x) __builtin_ctzl(x)
# endif
# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
# define CURL_PULL_SYS_TYPES_H 1
@@ -403,12 +357,12 @@
#else
/* generic "safe guess" on old 32-bit style */
-# define CURL_TYPEOF_CURL_OFF_T long
-# define CURL_FORMAT_CURL_OFF_T "ld"
-# define CURL_FORMAT_CURL_OFF_TU "lu"
-# define CURL_SUFFIX_CURL_OFF_T L
-# define CURL_SUFFIX_CURL_OFF_TU UL
-# define CURL_TYPEOF_CURL_SOCKLEN_T int
+# define CURL_TYPEOF_CURL_OFF_T long
+# define CURL_FORMAT_CURL_OFF_T "ld"
+# define CURL_FORMAT_CURL_OFF_TU "lu"
+# define CURL_SUFFIX_CURL_OFF_T L
+# define CURL_SUFFIX_CURL_OFF_TU UL
+# define CURL_TYPEOF_CURL_SOCKLEN_T int
#endif
#ifdef _AIX
@@ -462,7 +416,7 @@
#if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) || \
defined(__HP_aCC) || defined(__BORLANDC__) || defined(__LCC__) || \
- defined(__POCC__) || defined(__SALFORDC__) || defined(__HIGHC__) || \
+ defined(__POCC__) || defined(__HIGHC__) || \
defined(__ILEC400__)
/* This compiler is believed to have an ISO compatible preprocessor */
#define CURL_ISOCPP
diff --git a/libs/libcurl/include/curl/typecheck-gcc.h b/libs/libcurl/include/curl/typecheck-gcc.h
index 909fc6b7ff..4ef2b2f3ad 100644
--- a/libs/libcurl/include/curl/typecheck-gcc.h
+++ b/libs/libcurl/include/curl/typecheck-gcc.h
@@ -40,115 +40,157 @@
* To add an option that uses the same type as an existing option, you will
* just need to extend the appropriate _curl_*_option macro
*/
+
#define curl_easy_setopt(handle, option, value) \
__extension__({ \
- CURLoption _curl_opt = (option); \
- if(__builtin_constant_p(_curl_opt)) { \
+ if(__builtin_constant_p(option)) { \
CURL_IGNORE_DEPRECATION( \
- if(curlcheck_long_option(_curl_opt)) \
+ if(curlcheck_long_option(option)) \
if(!curlcheck_long(value)) \
_curl_easy_setopt_err_long(); \
- if(curlcheck_off_t_option(_curl_opt)) \
+ if(curlcheck_off_t_option(option)) \
if(!curlcheck_off_t(value)) \
_curl_easy_setopt_err_curl_off_t(); \
- if(curlcheck_string_option(_curl_opt)) \
+ if(curlcheck_string_option(option)) \
if(!curlcheck_string(value)) \
_curl_easy_setopt_err_string(); \
- if(curlcheck_write_cb_option(_curl_opt)) \
+ if((option) == CURLOPT_PRIVATE) { } \
+ if(curlcheck_write_cb_option(option)) \
if(!curlcheck_write_cb(value)) \
_curl_easy_setopt_err_write_callback(); \
- if((_curl_opt) == CURLOPT_RESOLVER_START_FUNCTION) \
+ if(curlcheck_curl_option(option)) \
+ if(!curlcheck_curl(value)) \
+ _curl_easy_setopt_err_curl(); \
+ if((option) == CURLOPT_RESOLVER_START_FUNCTION) \
if(!curlcheck_resolver_start_callback(value)) \
_curl_easy_setopt_err_resolver_start_callback(); \
- if((_curl_opt) == CURLOPT_READFUNCTION) \
+ if((option) == CURLOPT_READFUNCTION) \
if(!curlcheck_read_cb(value)) \
_curl_easy_setopt_err_read_cb(); \
- if((_curl_opt) == CURLOPT_IOCTLFUNCTION) \
+ if((option) == CURLOPT_IOCTLFUNCTION) \
if(!curlcheck_ioctl_cb(value)) \
_curl_easy_setopt_err_ioctl_cb(); \
- if((_curl_opt) == CURLOPT_SOCKOPTFUNCTION) \
+ if((option) == CURLOPT_SOCKOPTFUNCTION) \
if(!curlcheck_sockopt_cb(value)) \
_curl_easy_setopt_err_sockopt_cb(); \
- if((_curl_opt) == CURLOPT_OPENSOCKETFUNCTION) \
+ if((option) == CURLOPT_OPENSOCKETFUNCTION) \
if(!curlcheck_opensocket_cb(value)) \
_curl_easy_setopt_err_opensocket_cb(); \
- if((_curl_opt) == CURLOPT_PROGRESSFUNCTION) \
+ if((option) == CURLOPT_PROGRESSFUNCTION) \
if(!curlcheck_progress_cb(value)) \
_curl_easy_setopt_err_progress_cb(); \
- if((_curl_opt) == CURLOPT_DEBUGFUNCTION) \
+ if((option) == CURLOPT_XFERINFOFUNCTION) \
+ if(!curlcheck_xferinfo_cb(value)) \
+ _curl_easy_setopt_err_xferinfo_cb(); \
+ if((option) == CURLOPT_DEBUGFUNCTION) \
if(!curlcheck_debug_cb(value)) \
_curl_easy_setopt_err_debug_cb(); \
- if((_curl_opt) == CURLOPT_SSL_CTX_FUNCTION) \
+ if((option) == CURLOPT_SSL_CTX_FUNCTION) \
if(!curlcheck_ssl_ctx_cb(value)) \
_curl_easy_setopt_err_ssl_ctx_cb(); \
- if(curlcheck_conv_cb_option(_curl_opt)) \
+ if(curlcheck_conv_cb_option(option)) \
if(!curlcheck_conv_cb(value)) \
_curl_easy_setopt_err_conv_cb(); \
- if((_curl_opt) == CURLOPT_SEEKFUNCTION) \
+ if((option) == CURLOPT_SEEKFUNCTION) \
if(!curlcheck_seek_cb(value)) \
_curl_easy_setopt_err_seek_cb(); \
- if(curlcheck_cb_data_option(_curl_opt)) \
+ if((option) == CURLOPT_CHUNK_BGN_FUNCTION) \
+ if(!curlcheck_chunk_bgn_cb(value)) \
+ _curl_easy_setopt_err_chunk_bgn_cb(); \
+ if((option) == CURLOPT_CHUNK_END_FUNCTION) \
+ if(!curlcheck_chunk_end_cb(value)) \
+ _curl_easy_setopt_err_chunk_end_cb(); \
+ if((option) == CURLOPT_CLOSESOCKETFUNCTION) \
+ if(!curlcheck_close_socket_cb(value)) \
+ _curl_easy_setopt_err_close_socket_cb(); \
+ if((option) == CURLOPT_FNMATCH_FUNCTION) \
+ if(!curlcheck_fnmatch_cb(value)) \
+ _curl_easy_setopt_err_fnmatch_cb(); \
+ if((option) == CURLOPT_HSTSREADFUNCTION) \
+ if(!curlcheck_hstsread_cb(value)) \
+ _curl_easy_setopt_err_hstsread_cb(); \
+ if((option) == CURLOPT_HSTSWRITEFUNCTION) \
+ if(!curlcheck_hstswrite_cb(value)) \
+ _curl_easy_setopt_err_hstswrite_cb(); \
+ if((option) == CURLOPT_SSH_HOSTKEYFUNCTION) \
+ if(!curlcheck_ssh_hostkey_cb(value)) \
+ _curl_easy_setopt_err_ssh_hostkey_cb(); \
+ if((option) == CURLOPT_SSH_KEYFUNCTION) \
+ if(!curlcheck_ssh_key_cb(value)) \
+ _curl_easy_setopt_err_ssh_key_cb(); \
+ if((option) == CURLOPT_INTERLEAVEFUNCTION) \
+ if(!curlcheck_interleave_cb(value)) \
+ _curl_easy_setopt_err_interleave_cb(); \
+ if((option) == CURLOPT_PREREQFUNCTION) \
+ if(!curlcheck_prereq_cb(value)) \
+ _curl_easy_setopt_err_prereq_cb(); \
+ if((option) == CURLOPT_TRAILERFUNCTION) \
+ if(!curlcheck_trailer_cb(value)) \
+ _curl_easy_setopt_err_trailer_cb(); \
+ if(curlcheck_cb_data_option(option)) \
if(!curlcheck_cb_data(value)) \
_curl_easy_setopt_err_cb_data(); \
- if((_curl_opt) == CURLOPT_ERRORBUFFER) \
+ if((option) == CURLOPT_ERRORBUFFER) \
if(!curlcheck_error_buffer(value)) \
_curl_easy_setopt_err_error_buffer(); \
- if((_curl_opt) == CURLOPT_STDERR) \
+ if((option) == CURLOPT_CURLU) \
+ if(!curlcheck_ptr((value), CURLU)) \
+ _curl_easy_setopt_err_curlu(); \
+ if((option) == CURLOPT_STDERR) \
if(!curlcheck_FILE(value)) \
_curl_easy_setopt_err_FILE(); \
- if(curlcheck_postfields_option(_curl_opt)) \
+ if(curlcheck_postfields_option(option)) \
if(!curlcheck_postfields(value)) \
_curl_easy_setopt_err_postfields(); \
- if((_curl_opt) == CURLOPT_HTTPPOST) \
+ if((option) == CURLOPT_HTTPPOST) \
if(!curlcheck_arr((value), struct curl_httppost)) \
_curl_easy_setopt_err_curl_httpost(); \
- if((_curl_opt) == CURLOPT_MIMEPOST) \
+ if((option) == CURLOPT_MIMEPOST) \
if(!curlcheck_ptr((value), curl_mime)) \
_curl_easy_setopt_err_curl_mimepost(); \
- if(curlcheck_slist_option(_curl_opt)) \
+ if(curlcheck_slist_option(option)) \
if(!curlcheck_arr((value), struct curl_slist)) \
_curl_easy_setopt_err_curl_slist(); \
- if((_curl_opt) == CURLOPT_SHARE) \
+ if((option) == CURLOPT_SHARE) \
if(!curlcheck_ptr((value), CURLSH)) \
_curl_easy_setopt_err_CURLSH(); \
- ) \
- } \
- curl_easy_setopt(handle, _curl_opt, value); \
+ ) \
+ } \
+ curl_easy_setopt(handle, option, value); \
})
/* wraps curl_easy_getinfo() with typechecking */
#define curl_easy_getinfo(handle, info, arg) \
__extension__({ \
- CURLINFO _curl_info = (info); \
- if(__builtin_constant_p(_curl_info)) { \
+ if(__builtin_constant_p(info)) { \
CURL_IGNORE_DEPRECATION( \
- if(curlcheck_string_info(_curl_info)) \
+ if(curlcheck_string_info(info)) \
if(!curlcheck_arr((arg), char *)) \
_curl_easy_getinfo_err_string(); \
- if(curlcheck_long_info(_curl_info)) \
+ if(curlcheck_long_info(info)) \
if(!curlcheck_arr((arg), long)) \
_curl_easy_getinfo_err_long(); \
- if(curlcheck_double_info(_curl_info)) \
+ if(curlcheck_double_info(info)) \
if(!curlcheck_arr((arg), double)) \
_curl_easy_getinfo_err_double(); \
- if(curlcheck_slist_info(_curl_info)) \
+ if(curlcheck_slist_info(info)) \
if(!curlcheck_arr((arg), struct curl_slist *)) \
_curl_easy_getinfo_err_curl_slist(); \
- if(curlcheck_tlssessioninfo_info(_curl_info)) \
+ if(curlcheck_tlssessioninfo_info(info)) \
if(!curlcheck_arr((arg), struct curl_tlssessioninfo *)) \
- _curl_easy_getinfo_err_curl_tlssesssioninfo(); \
- if(curlcheck_certinfo_info(_curl_info)) \
+ _curl_easy_getinfo_err_curl_tlssessioninfo(); \
+ if(curlcheck_certinfo_info(info)) \
if(!curlcheck_arr((arg), struct curl_certinfo *)) \
_curl_easy_getinfo_err_curl_certinfo(); \
- if(curlcheck_socket_info(_curl_info)) \
+ if(curlcheck_socket_info(info)) \
if(!curlcheck_arr((arg), curl_socket_t)) \
_curl_easy_getinfo_err_curl_socket(); \
- if(curlcheck_off_t_info(_curl_info)) \
+ if(curlcheck_off_t_info(info)) \
if(!curlcheck_arr((arg), curl_off_t)) \
_curl_easy_getinfo_err_curl_off_t(); \
- ) \
- } \
- curl_easy_getinfo(handle, _curl_info, arg); \
+ ) \
+ } \
+ curl_easy_getinfo(handle, info, arg); \
})
/*
@@ -157,7 +199,6 @@
#define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param)
#define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param)
-
/* the actual warnings, triggered by calling the _curl_easy_setopt_err*
* functions */
@@ -168,187 +209,212 @@
id(void) { __asm__(""); }
CURLWARNING(_curl_easy_setopt_err_long,
- "curl_easy_setopt expects a long argument for this option")
+ "curl_easy_setopt expects a long argument")
CURLWARNING(_curl_easy_setopt_err_curl_off_t,
- "curl_easy_setopt expects a curl_off_t argument for this option")
+ "curl_easy_setopt expects a curl_off_t argument")
CURLWARNING(_curl_easy_setopt_err_string,
- "curl_easy_setopt expects a "
- "string ('char *' or char[]) argument for this option"
- )
+ "curl_easy_setopt expects a "
+ "string ('char *' or char[]) argument")
CURLWARNING(_curl_easy_setopt_err_write_callback,
- "curl_easy_setopt expects a curl_write_callback argument for this option")
+ "curl_easy_setopt expects a curl_write_callback argument")
CURLWARNING(_curl_easy_setopt_err_resolver_start_callback,
- "curl_easy_setopt expects a "
- "curl_resolver_start_callback argument for this option"
- )
+ "curl_easy_setopt expects a "
+ "curl_resolver_start_callback argument")
CURLWARNING(_curl_easy_setopt_err_read_cb,
- "curl_easy_setopt expects a curl_read_callback argument for this option")
+ "curl_easy_setopt expects a curl_read_callback argument")
CURLWARNING(_curl_easy_setopt_err_ioctl_cb,
- "curl_easy_setopt expects a curl_ioctl_callback argument for this option")
+ "curl_easy_setopt expects a curl_ioctl_callback argument")
CURLWARNING(_curl_easy_setopt_err_sockopt_cb,
- "curl_easy_setopt expects a curl_sockopt_callback argument for this option")
+ "curl_easy_setopt expects a curl_sockopt_callback argument")
CURLWARNING(_curl_easy_setopt_err_opensocket_cb,
- "curl_easy_setopt expects a "
- "curl_opensocket_callback argument for this option"
- )
+ "curl_easy_setopt expects a "
+ "curl_opensocket_callback argument")
CURLWARNING(_curl_easy_setopt_err_progress_cb,
- "curl_easy_setopt expects a curl_progress_callback argument for this option")
+ "curl_easy_setopt expects a curl_progress_callback argument")
+CURLWARNING(_curl_easy_setopt_err_xferinfo_cb,
+ "curl_easy_setopt expects a curl_xferinfo_callback argument")
CURLWARNING(_curl_easy_setopt_err_debug_cb,
- "curl_easy_setopt expects a curl_debug_callback argument for this option")
+ "curl_easy_setopt expects a curl_debug_callback argument")
CURLWARNING(_curl_easy_setopt_err_ssl_ctx_cb,
- "curl_easy_setopt expects a curl_ssl_ctx_callback argument for this option")
+ "curl_easy_setopt expects a curl_ssl_ctx_callback argument")
CURLWARNING(_curl_easy_setopt_err_conv_cb,
- "curl_easy_setopt expects a curl_conv_callback argument for this option")
+ "curl_easy_setopt expects a curl_conv_callback argument")
CURLWARNING(_curl_easy_setopt_err_seek_cb,
- "curl_easy_setopt expects a curl_seek_callback argument for this option")
+ "curl_easy_setopt expects a curl_seek_callback argument")
CURLWARNING(_curl_easy_setopt_err_cb_data,
- "curl_easy_setopt expects a "
- "private data pointer as argument for this option")
+ "curl_easy_setopt expects a "
+ "private data pointer as argument")
+CURLWARNING(_curl_easy_setopt_err_chunk_bgn_cb,
+ "curl_easy_setopt expects a curl_chunk_bgn_callback argument")
+CURLWARNING(_curl_easy_setopt_err_chunk_end_cb,
+ "curl_easy_setopt expects a curl_chunk_end_callback argument")
+CURLWARNING(_curl_easy_setopt_err_close_socket_cb,
+ "curl_easy_setopt expects a curl_closesocket_callback argument")
+CURLWARNING(_curl_easy_setopt_err_fnmatch_cb,
+ "curl_easy_setopt expects a curl_fnmatch_callback argument")
+CURLWARNING(_curl_easy_setopt_err_hstsread_cb,
+ "curl_easy_setopt expects a curl_hstsread_callback argument")
+CURLWARNING(_curl_easy_setopt_err_hstswrite_cb,
+ "curl_easy_setopt expects a curl_hstswrite_callback argument")
+CURLWARNING(_curl_easy_setopt_err_ssh_key_cb,
+ "curl_easy_setopt expects a curl_sshkeycallback argument")
+CURLWARNING(_curl_easy_setopt_err_ssh_hostkey_cb,
+ "curl_easy_setopt expects a curl_sshhostkeycallback argument")
+CURLWARNING(_curl_easy_setopt_err_interleave_cb,
+ "curl_easy_setopt expects a curl_interleave_callback argument")
+CURLWARNING(_curl_easy_setopt_err_prereq_cb,
+ "curl_easy_setopt expects a curl_prereq_callback argument")
+CURLWARNING(_curl_easy_setopt_err_trailer_cb,
+ "curl_easy_setopt expects a curl_trailerfunc_ok argument")
CURLWARNING(_curl_easy_setopt_err_error_buffer,
- "curl_easy_setopt expects a "
- "char buffer of CURL_ERROR_SIZE as argument for this option")
+ "curl_easy_setopt expects a "
+ "char buffer of CURL_ERROR_SIZE as argument")
+CURLWARNING(_curl_easy_setopt_err_curlu,
+ "curl_easy_setopt expects a 'CURLU *' argument")
+CURLWARNING(_curl_easy_setopt_err_curl,
+ "curl_easy_setopt expects a 'CURL *' argument")
CURLWARNING(_curl_easy_setopt_err_FILE,
- "curl_easy_setopt expects a 'FILE *' argument for this option")
+ "curl_easy_setopt expects a 'FILE *' argument")
CURLWARNING(_curl_easy_setopt_err_postfields,
- "curl_easy_setopt expects a 'void *' or 'char *' argument for this option")
+ "curl_easy_setopt expects a 'void *' or 'char *' argument")
CURLWARNING(_curl_easy_setopt_err_curl_httpost,
- "curl_easy_setopt expects a 'struct curl_httppost *' "
- "argument for this option")
+ "curl_easy_setopt expects a 'struct curl_httppost *' "
+ "argument")
CURLWARNING(_curl_easy_setopt_err_curl_mimepost,
- "curl_easy_setopt expects a 'curl_mime *' "
- "argument for this option")
+ "curl_easy_setopt expects a 'curl_mime *' "
+ "argument")
CURLWARNING(_curl_easy_setopt_err_curl_slist,
- "curl_easy_setopt expects a 'struct curl_slist *' argument for this option")
+ "curl_easy_setopt expects a 'struct curl_slist *' argument")
CURLWARNING(_curl_easy_setopt_err_CURLSH,
- "curl_easy_setopt expects a CURLSH* argument for this option")
-
+ "curl_easy_setopt expects a CURLSH* argument")
CURLWARNING(_curl_easy_getinfo_err_string,
- "curl_easy_getinfo expects a pointer to 'char *' for this info")
+ "curl_easy_getinfo expects a pointer to 'char *'")
CURLWARNING(_curl_easy_getinfo_err_long,
- "curl_easy_getinfo expects a pointer to long for this info")
+ "curl_easy_getinfo expects a pointer to long")
CURLWARNING(_curl_easy_getinfo_err_double,
- "curl_easy_getinfo expects a pointer to double for this info")
+ "curl_easy_getinfo expects a pointer to double")
CURLWARNING(_curl_easy_getinfo_err_curl_slist,
- "curl_easy_getinfo expects a pointer to 'struct curl_slist *' for this info")
-CURLWARNING(_curl_easy_getinfo_err_curl_tlssesssioninfo,
- "curl_easy_getinfo expects a pointer to "
- "'struct curl_tlssessioninfo *' for this info")
+ "curl_easy_getinfo expects a pointer to 'struct curl_slist *'")
+CURLWARNING(_curl_easy_getinfo_err_curl_tlssessioninfo,
+ "curl_easy_getinfo expects a pointer to "
+ "'struct curl_tlssessioninfo *'")
CURLWARNING(_curl_easy_getinfo_err_curl_certinfo,
- "curl_easy_getinfo expects a pointer to "
- "'struct curl_certinfo *' for this info")
+ "curl_easy_getinfo expects a pointer to "
+ "'struct curl_certinfo *'")
CURLWARNING(_curl_easy_getinfo_err_curl_socket,
- "curl_easy_getinfo expects a pointer to curl_socket_t for this info")
+ "curl_easy_getinfo expects a pointer to curl_socket_t")
CURLWARNING(_curl_easy_getinfo_err_curl_off_t,
- "curl_easy_getinfo expects a pointer to curl_off_t for this info")
+ "curl_easy_getinfo expects a pointer to curl_off_t")
/* groups of curl_easy_setops options that take the same type of argument */
-/* To add a new option to one of the groups, just add
- * (option) == CURLOPT_SOMETHING
- * to the or-expression. If the option takes a long or curl_off_t, you do not
- * have to do anything
- */
-
/* evaluates to true if option takes a long argument */
#define curlcheck_long_option(option) \
(0 < (option) && (option) < CURLOPTTYPE_OBJECTPOINT)
-#define curlcheck_off_t_option(option) \
+#define curlcheck_off_t_option(option) \
(((option) > CURLOPTTYPE_OFF_T) && ((option) < CURLOPTTYPE_BLOB))
+/* option takes a CURL * argument */
+#define curlcheck_curl_option(option) \
+ ((option) == CURLOPT_STREAM_DEPENDS || \
+ (option) == CURLOPT_STREAM_DEPENDS_E || \
+ 0)
+
/* evaluates to true if option takes a char* argument */
-#define curlcheck_string_option(option) \
- ((option) == CURLOPT_ABSTRACT_UNIX_SOCKET || \
- (option) == CURLOPT_ACCEPT_ENCODING || \
- (option) == CURLOPT_ALTSVC || \
- (option) == CURLOPT_CAINFO || \
- (option) == CURLOPT_CAPATH || \
- (option) == CURLOPT_COOKIE || \
- (option) == CURLOPT_COOKIEFILE || \
- (option) == CURLOPT_COOKIEJAR || \
- (option) == CURLOPT_COOKIELIST || \
- (option) == CURLOPT_CRLFILE || \
- (option) == CURLOPT_CUSTOMREQUEST || \
- (option) == CURLOPT_DEFAULT_PROTOCOL || \
- (option) == CURLOPT_DNS_INTERFACE || \
- (option) == CURLOPT_DNS_LOCAL_IP4 || \
- (option) == CURLOPT_DNS_LOCAL_IP6 || \
- (option) == CURLOPT_DNS_SERVERS || \
- (option) == CURLOPT_DOH_URL || \
- (option) == CURLOPT_ECH || \
- (option) == CURLOPT_EGDSOCKET || \
- (option) == CURLOPT_FTP_ACCOUNT || \
- (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER || \
- (option) == CURLOPT_FTPPORT || \
- (option) == CURLOPT_HSTS || \
- (option) == CURLOPT_HAPROXY_CLIENT_IP || \
- (option) == CURLOPT_INTERFACE || \
- (option) == CURLOPT_ISSUERCERT || \
- (option) == CURLOPT_KEYPASSWD || \
- (option) == CURLOPT_KRBLEVEL || \
- (option) == CURLOPT_LOGIN_OPTIONS || \
- (option) == CURLOPT_MAIL_AUTH || \
- (option) == CURLOPT_MAIL_FROM || \
- (option) == CURLOPT_NETRC_FILE || \
- (option) == CURLOPT_NOPROXY || \
- (option) == CURLOPT_PASSWORD || \
- (option) == CURLOPT_PINNEDPUBLICKEY || \
- (option) == CURLOPT_PRE_PROXY || \
- (option) == CURLOPT_PROTOCOLS_STR || \
- (option) == CURLOPT_PROXY || \
- (option) == CURLOPT_PROXY_CAINFO || \
- (option) == CURLOPT_PROXY_CAPATH || \
- (option) == CURLOPT_PROXY_CRLFILE || \
- (option) == CURLOPT_PROXY_ISSUERCERT || \
- (option) == CURLOPT_PROXY_KEYPASSWD || \
- (option) == CURLOPT_PROXY_PINNEDPUBLICKEY || \
- (option) == CURLOPT_PROXY_SERVICE_NAME || \
- (option) == CURLOPT_PROXY_SSL_CIPHER_LIST || \
- (option) == CURLOPT_PROXY_SSLCERT || \
- (option) == CURLOPT_PROXY_SSLCERTTYPE || \
- (option) == CURLOPT_PROXY_SSLKEY || \
- (option) == CURLOPT_PROXY_SSLKEYTYPE || \
- (option) == CURLOPT_PROXY_TLS13_CIPHERS || \
- (option) == CURLOPT_PROXY_TLSAUTH_PASSWORD || \
- (option) == CURLOPT_PROXY_TLSAUTH_TYPE || \
- (option) == CURLOPT_PROXY_TLSAUTH_USERNAME || \
- (option) == CURLOPT_PROXYPASSWORD || \
- (option) == CURLOPT_PROXYUSERNAME || \
- (option) == CURLOPT_PROXYUSERPWD || \
- (option) == CURLOPT_RANDOM_FILE || \
- (option) == CURLOPT_RANGE || \
- (option) == CURLOPT_REDIR_PROTOCOLS_STR || \
- (option) == CURLOPT_REFERER || \
- (option) == CURLOPT_REQUEST_TARGET || \
- (option) == CURLOPT_RTSP_SESSION_ID || \
- (option) == CURLOPT_RTSP_STREAM_URI || \
- (option) == CURLOPT_RTSP_TRANSPORT || \
- (option) == CURLOPT_SASL_AUTHZID || \
- (option) == CURLOPT_SERVICE_NAME || \
- (option) == CURLOPT_SOCKS5_GSSAPI_SERVICE || \
- (option) == CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 || \
- (option) == CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256 || \
- (option) == CURLOPT_SSH_KNOWNHOSTS || \
- (option) == CURLOPT_SSH_PRIVATE_KEYFILE || \
- (option) == CURLOPT_SSH_PUBLIC_KEYFILE || \
- (option) == CURLOPT_SSLCERT || \
- (option) == CURLOPT_SSLCERTTYPE || \
- (option) == CURLOPT_SSLENGINE || \
- (option) == CURLOPT_SSLKEY || \
- (option) == CURLOPT_SSLKEYTYPE || \
- (option) == CURLOPT_SSL_CIPHER_LIST || \
- (option) == CURLOPT_TLS13_CIPHERS || \
- (option) == CURLOPT_TLSAUTH_PASSWORD || \
- (option) == CURLOPT_TLSAUTH_TYPE || \
- (option) == CURLOPT_TLSAUTH_USERNAME || \
- (option) == CURLOPT_UNIX_SOCKET_PATH || \
- (option) == CURLOPT_URL || \
- (option) == CURLOPT_USERAGENT || \
- (option) == CURLOPT_USERNAME || \
- (option) == CURLOPT_AWS_SIGV4 || \
- (option) == CURLOPT_USERPWD || \
- (option) == CURLOPT_XOAUTH2_BEARER || \
- (option) == CURLOPT_SSL_EC_CURVES || \
+#define curlcheck_string_option(option) \
+ ((option) == CURLOPT_ABSTRACT_UNIX_SOCKET || \
+ (option) == CURLOPT_ACCEPT_ENCODING || \
+ (option) == CURLOPT_ALTSVC || \
+ (option) == CURLOPT_CAINFO || \
+ (option) == CURLOPT_CAPATH || \
+ (option) == CURLOPT_COOKIE || \
+ (option) == CURLOPT_COOKIEFILE || \
+ (option) == CURLOPT_COOKIEJAR || \
+ (option) == CURLOPT_COOKIELIST || \
+ (option) == CURLOPT_CRLFILE || \
+ (option) == CURLOPT_CUSTOMREQUEST || \
+ (option) == CURLOPT_DEFAULT_PROTOCOL || \
+ (option) == CURLOPT_DNS_INTERFACE || \
+ (option) == CURLOPT_DNS_LOCAL_IP4 || \
+ (option) == CURLOPT_DNS_LOCAL_IP6 || \
+ (option) == CURLOPT_DNS_SERVERS || \
+ (option) == CURLOPT_DOH_URL || \
+ (option) == CURLOPT_ECH || \
+ (option) == CURLOPT_EGDSOCKET || \
+ (option) == CURLOPT_FTP_ACCOUNT || \
+ (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER || \
+ (option) == CURLOPT_FTPPORT || \
+ (option) == CURLOPT_HAPROXY_CLIENT_IP || \
+ (option) == CURLOPT_HSTS || \
+ (option) == CURLOPT_INTERFACE || \
+ (option) == CURLOPT_ISSUERCERT || \
+ (option) == CURLOPT_KEYPASSWD || \
+ (option) == CURLOPT_KRBLEVEL || \
+ (option) == CURLOPT_LOGIN_OPTIONS || \
+ (option) == CURLOPT_MAIL_AUTH || \
+ (option) == CURLOPT_MAIL_FROM || \
+ (option) == CURLOPT_NETRC_FILE || \
+ (option) == CURLOPT_NOPROXY || \
+ (option) == CURLOPT_PASSWORD || \
+ (option) == CURLOPT_PINNEDPUBLICKEY || \
+ (option) == CURLOPT_PRE_PROXY || \
+ (option) == CURLOPT_PROTOCOLS_STR || \
+ (option) == CURLOPT_PROXY || \
+ (option) == CURLOPT_PROXY_CAINFO || \
+ (option) == CURLOPT_PROXY_CAPATH || \
+ (option) == CURLOPT_PROXY_CRLFILE || \
+ (option) == CURLOPT_PROXY_ISSUERCERT || \
+ (option) == CURLOPT_PROXY_KEYPASSWD || \
+ (option) == CURLOPT_PROXY_PINNEDPUBLICKEY || \
+ (option) == CURLOPT_PROXY_SERVICE_NAME || \
+ (option) == CURLOPT_PROXY_SSL_CIPHER_LIST || \
+ (option) == CURLOPT_PROXY_SSLCERT || \
+ (option) == CURLOPT_PROXY_SSLCERTTYPE || \
+ (option) == CURLOPT_PROXY_SSLKEY || \
+ (option) == CURLOPT_PROXY_SSLKEYTYPE || \
+ (option) == CURLOPT_PROXY_TLS13_CIPHERS || \
+ (option) == CURLOPT_PROXY_TLSAUTH_PASSWORD || \
+ (option) == CURLOPT_PROXY_TLSAUTH_TYPE || \
+ (option) == CURLOPT_PROXY_TLSAUTH_USERNAME || \
+ (option) == CURLOPT_PROXYPASSWORD || \
+ (option) == CURLOPT_PROXYUSERNAME || \
+ (option) == CURLOPT_PROXYUSERPWD || \
+ (option) == CURLOPT_RANDOM_FILE || \
+ (option) == CURLOPT_RANGE || \
+ (option) == CURLOPT_REDIR_PROTOCOLS_STR || \
+ (option) == CURLOPT_REFERER || \
+ (option) == CURLOPT_REQUEST_TARGET || \
+ (option) == CURLOPT_RTSP_SESSION_ID || \
+ (option) == CURLOPT_RTSP_STREAM_URI || \
+ (option) == CURLOPT_RTSP_TRANSPORT || \
+ (option) == CURLOPT_SASL_AUTHZID || \
+ (option) == CURLOPT_SERVICE_NAME || \
+ (option) == CURLOPT_SOCKS5_GSSAPI_SERVICE || \
+ (option) == CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 || \
+ (option) == CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256 || \
+ (option) == CURLOPT_SSH_KNOWNHOSTS || \
+ (option) == CURLOPT_SSH_PRIVATE_KEYFILE || \
+ (option) == CURLOPT_SSH_PUBLIC_KEYFILE || \
+ (option) == CURLOPT_SSLCERT || \
+ (option) == CURLOPT_SSLCERTTYPE || \
+ (option) == CURLOPT_SSLENGINE || \
+ (option) == CURLOPT_SSLKEY || \
+ (option) == CURLOPT_SSLKEYTYPE || \
+ (option) == CURLOPT_SSL_CIPHER_LIST || \
+ (option) == CURLOPT_SSL_EC_CURVES || \
+ (option) == CURLOPT_SSL_SIGNATURE_ALGORITHMS || \
+ (option) == CURLOPT_TLS13_CIPHERS || \
+ (option) == CURLOPT_TLSAUTH_PASSWORD || \
+ (option) == CURLOPT_TLSAUTH_TYPE || \
+ (option) == CURLOPT_TLSAUTH_USERNAME || \
+ (option) == CURLOPT_UNIX_SOCKET_PATH || \
+ (option) == CURLOPT_URL || \
+ (option) == CURLOPT_USERAGENT || \
+ (option) == CURLOPT_USERNAME || \
+ (option) == CURLOPT_AWS_SIGV4 || \
+ (option) == CURLOPT_USERPWD || \
+ (option) == CURLOPT_XOAUTH2_BEARER || \
0)
/* evaluates to true if option takes a curl_write_callback argument */
@@ -375,7 +441,7 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t,
(option) == CURLOPT_IOCTLDATA || \
(option) == CURLOPT_OPENSOCKETDATA || \
(option) == CURLOPT_PREREQDATA || \
- (option) == CURLOPT_PROGRESSDATA || \
+ (option) == CURLOPT_XFERINFODATA || \
(option) == CURLOPT_READDATA || \
(option) == CURLOPT_SEEKDATA || \
(option) == CURLOPT_SOCKOPTDATA || \
@@ -479,22 +545,36 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t,
curlcheck_arr((expr), signed char) || \
curlcheck_arr((expr), unsigned char))
+/* evaluates to true if expr is a CURL * */
+#define curlcheck_curl(expr) \
+ (curlcheck_NULL(expr) || \
+ __builtin_types_compatible_p(__typeof__(expr), CURL *))
+
+
/* evaluates to true if expr is a long (no matter the signedness)
* XXX: for now, int is also accepted (and therefore short and char, which
* are promoted to int when passed to a variadic function) */
-#define curlcheck_long(expr) \
- (__builtin_types_compatible_p(__typeof__(expr), long) || \
- __builtin_types_compatible_p(__typeof__(expr), signed long) || \
- __builtin_types_compatible_p(__typeof__(expr), unsigned long) || \
- __builtin_types_compatible_p(__typeof__(expr), int) || \
- __builtin_types_compatible_p(__typeof__(expr), signed int) || \
- __builtin_types_compatible_p(__typeof__(expr), unsigned int) || \
- __builtin_types_compatible_p(__typeof__(expr), short) || \
- __builtin_types_compatible_p(__typeof__(expr), signed short) || \
- __builtin_types_compatible_p(__typeof__(expr), unsigned short) || \
- __builtin_types_compatible_p(__typeof__(expr), char) || \
- __builtin_types_compatible_p(__typeof__(expr), signed char) || \
- __builtin_types_compatible_p(__typeof__(expr), unsigned char))
+#define curlcheck_long(expr) \
+ ( \
+ ((sizeof(long) != sizeof(int)) && \
+ (__builtin_types_compatible_p(__typeof__(expr), long) || \
+ __builtin_types_compatible_p(__typeof__(expr), signed long) || \
+ __builtin_types_compatible_p(__typeof__(expr), unsigned long))) \
+ || \
+ ((sizeof(long) == sizeof(int)) && \
+ (__builtin_types_compatible_p(__typeof__(expr), long) || \
+ __builtin_types_compatible_p(__typeof__(expr), signed long) || \
+ __builtin_types_compatible_p(__typeof__(expr), unsigned long) || \
+ __builtin_types_compatible_p(__typeof__(expr), int) || \
+ __builtin_types_compatible_p(__typeof__(expr), signed int) || \
+ __builtin_types_compatible_p(__typeof__(expr), unsigned int) || \
+ __builtin_types_compatible_p(__typeof__(expr), short) || \
+ __builtin_types_compatible_p(__typeof__(expr), signed short) || \
+ __builtin_types_compatible_p(__typeof__(expr), unsigned short) || \
+ __builtin_types_compatible_p(__typeof__(expr), char) || \
+ __builtin_types_compatible_p(__typeof__(expr), signed char) || \
+ __builtin_types_compatible_p(__typeof__(expr), unsigned char))) \
+ )
/* evaluates to true if expr is of type curl_off_t */
#define curlcheck_off_t(expr) \
@@ -629,6 +709,11 @@ typedef int (*_curl_progress_callback1)(void *,
typedef int (*_curl_progress_callback2)(const void *,
double, double, double, double);
+/* evaluates to true if expr is of type curl_xferinfo_callback */
+#define curlcheck_xferinfo_cb(expr) \
+ (curlcheck_NULL(expr) || \
+ curlcheck_cb_compatible((expr), curl_xferinfo_callback))
+
/* evaluates to true if expr is of type curl_debug_callback or "similar" */
#define curlcheck_debug_cb(expr) \
(curlcheck_NULL(expr) || \
@@ -714,5 +799,69 @@ typedef CURLcode (*_curl_conv_callback4)(const void *, size_t length);
typedef CURLcode (*_curl_seek_callback1)(void *, curl_off_t, int);
typedef CURLcode (*_curl_seek_callback2)(const void *, curl_off_t, int);
+/* evaluates to true if expr is of type curl_chunk_bgn_callback */
+#define curlcheck_chunk_bgn_cb(expr) \
+ (curlcheck_NULL(expr) || \
+ curlcheck_cb_compatible((expr), curl_chunk_bgn_callback) || \
+ curlcheck_cb_compatible((expr), _curl_chunk_bgn_callback1) || \
+ curlcheck_cb_compatible((expr), _curl_chunk_bgn_callback2))
+typedef long (*_curl_chunk_bgn_callback1)(struct curl_fileinfo *,
+ void *, int);
+typedef long (*_curl_chunk_bgn_callback2)(void *, void *, int);
+
+/* evaluates to true if expr is of type curl_chunk_end_callback */
+#define curlcheck_chunk_end_cb(expr) \
+ (curlcheck_NULL(expr) || \
+ curlcheck_cb_compatible((expr), curl_chunk_end_callback))
+
+/* evaluates to true if expr is of type curl_closesocket_callback */
+#define curlcheck_close_socket_cb(expr) \
+ (curlcheck_NULL(expr) || \
+ curlcheck_cb_compatible((expr), curl_closesocket_callback))
+
+/* evaluates to true if expr is of type curl_fnmatch_callback */
+#define curlcheck_fnmatch_cb(expr) \
+ (curlcheck_NULL(expr) || \
+ curlcheck_cb_compatible((expr), curl_fnmatch_callback))
+
+/* evaluates to true if expr is of type curl_hstsread_callback */
+#define curlcheck_hstsread_cb(expr) \
+ (curlcheck_NULL(expr) || \
+ curlcheck_cb_compatible((expr), curl_hstsread_callback))
+
+/* evaluates to true if expr is of type curl_hstswrite_callback */
+#define curlcheck_hstswrite_cb(expr) \
+ (curlcheck_NULL(expr) || \
+ curlcheck_cb_compatible((expr), curl_hstswrite_callback))
+
+/* evaluates to true if expr is of type curl_sshhostkeycallback */
+#define curlcheck_ssh_hostkey_cb(expr) \
+ (curlcheck_NULL(expr) || \
+ curlcheck_cb_compatible((expr), curl_sshhostkeycallback))
+
+/* evaluates to true if expr is of type curl_sshkeycallback */
+#define curlcheck_ssh_key_cb(expr) \
+ (curlcheck_NULL(expr) || \
+ curlcheck_cb_compatible((expr), curl_sshkeycallback))
+
+/* evaluates to true if expr is of type curl_interleave_callback */
+#define curlcheck_interleave_cb(expr) \
+ (curlcheck_NULL(expr) || \
+ curlcheck_cb_compatible((expr), _curl_interleave_callback1) || \
+ curlcheck_cb_compatible((expr), _curl_interleave_callback2))
+typedef size_t (*_curl_interleave_callback1)(void *p, size_t s,
+ size_t n, void *u);
+typedef size_t (*_curl_interleave_callback2)(char *p, size_t s,
+ size_t n, void *u);
+
+/* evaluates to true if expr is of type curl_prereq_callback */
+#define curlcheck_prereq_cb(expr) \
+ (curlcheck_NULL(expr) || \
+ curlcheck_cb_compatible((expr), curl_prereq_callback))
+
+/* evaluates to true if expr is of type curl_trailer_callback */
+#define curlcheck_trailer_cb(expr) \
+ (curlcheck_NULL(expr) || \
+ curlcheck_cb_compatible((expr), curl_trailer_callback))
#endif /* CURLINC_TYPECHECK_GCC_H */
diff --git a/libs/libcurl/include/curl/websockets.h b/libs/libcurl/include/curl/websockets.h
index a10652dc1c..65accb27c9 100644
--- a/libs/libcurl/include/curl/websockets.h
+++ b/libs/libcurl/include/curl/websockets.h
@@ -73,7 +73,8 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer,
unsigned int flags);
/* bits for the CURLOPT_WS_OPTIONS bitmask: */
-#define CURLWS_RAW_MODE (1<<0)
+#define CURLWS_RAW_MODE (1<<0)
+#define CURLWS_NOAUTOPONG (1<<1)
CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(CURL *curl);
diff --git a/libs/libcurl/libcurl.vcxproj b/libs/libcurl/libcurl.vcxproj
index ac9462d7fc..3a6871f922 100644
--- a/libs/libcurl/libcurl.vcxproj
+++ b/libs/libcurl/libcurl.vcxproj
@@ -35,10 +35,10 @@
<ClCompile Include="src\asyn-ares.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
- <ClCompile Include="src\asyn-thread.c">
+ <ClCompile Include="src\asyn-base.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
- <ClCompile Include="src\base64.c">
+ <ClCompile Include="src\asyn-thrdd.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="src\bufq.c">
@@ -47,9 +47,6 @@
<ClCompile Include="src\bufref.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
- <ClCompile Include="src\c-hyper.c">
- <PrecompiledHeader>NotUsing</PrecompiledHeader>
- </ClCompile>
<ClCompile Include="src\cf-https-connect.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
@@ -80,6 +77,9 @@
<ClCompile Include="src\cookie.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
+ <ClCompile Include="src\cshutdn.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
<ClCompile Include="src\curl_addrinfo.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
@@ -104,9 +104,6 @@
<ClCompile Include="src\curl_memrchr.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
- <ClCompile Include="src\curl_multibyte.c">
- <PrecompiledHeader>NotUsing</PrecompiledHeader>
- </ClCompile>
<ClCompile Include="src\curl_ntlm_core.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
@@ -131,9 +128,42 @@
<ClCompile Include="src\curl_trc.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
+ <ClCompile Include="src\curlx\base64.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="src\curlx\dynbuf.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="src\curlx\inet_pton.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="src\curlx\multibyte.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="src\curlx\nonblock.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="src\curlx\strparse.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="src\curlx\timediff.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="src\curlx\version_win32.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="src\curlx\warnless.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="src\curlx\winapi.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
<ClCompile Include="src\cw-out.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
+ <ClCompile Include="src\cw-pause.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
<ClCompile Include="src\dict.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
@@ -143,9 +173,6 @@
<ClCompile Include="src\doh.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
- <ClCompile Include="src\dynbuf.c">
- <PrecompiledHeader>NotUsing</PrecompiledHeader>
- </ClCompile>
<ClCompile Include="src\dynhds.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
@@ -161,6 +188,9 @@
<ClCompile Include="src\escape.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
+ <ClCompile Include="src\fake_addrinfo.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
<ClCompile Include="src\file.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
@@ -197,9 +227,6 @@
<ClCompile Include="src\hmac.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
- <ClCompile Include="src\hostasyn.c">
- <PrecompiledHeader>NotUsing</PrecompiledHeader>
- </ClCompile>
<ClCompile Include="src\hostip.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
@@ -209,9 +236,6 @@
<ClCompile Include="src\hostip6.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
- <ClCompile Include="src\hostsyn.c">
- <PrecompiledHeader>NotUsing</PrecompiledHeader>
- </ClCompile>
<ClCompile Include="src\hsts.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
@@ -242,6 +266,9 @@
<ClCompile Include="src\http_proxy.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
+ <ClCompile Include="src\httpsrr.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
<ClCompile Include="src\idn.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
@@ -254,9 +281,6 @@
<ClCompile Include="src\inet_ntop.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
- <ClCompile Include="src\inet_pton.c">
- <PrecompiledHeader>NotUsing</PrecompiledHeader>
- </ClCompile>
<ClCompile Include="src\krb5.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
@@ -290,10 +314,10 @@
<ClCompile Include="src\multi.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
- <ClCompile Include="src\netrc.c">
+ <ClCompile Include="src\multi_ev.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
- <ClCompile Include="src\nonblock.c">
+ <ClCompile Include="src\netrc.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="src\noproxy.c">
@@ -371,10 +395,16 @@
<ClCompile Include="src\splay.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
- <ClCompile Include="src\timediff.c">
+ <ClCompile Include="src\uint-bset.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="src\uint-hash.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
- <ClCompile Include="src\version_win32.c">
+ <ClCompile Include="src\uint-spbset.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="src\uint-table.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="src\vauth\cleartext.c">
@@ -476,13 +506,16 @@
<ClCompile Include="src\vtls\vtls.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
- <ClCompile Include="src\vtls\wolfssl.c">
+ <ClCompile Include="src\vtls\vtls_scache.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
- <ClCompile Include="src\vtls\x509asn1.c">
+ <ClCompile Include="src\vtls\vtls_spack.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="src\vtls\wolfssl.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
- <ClCompile Include="src\warnless.c">
+ <ClCompile Include="src\vtls\x509asn1.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="src\ws.c">
@@ -494,28 +527,24 @@
<ClInclude Include="src\asyn.h" />
<ClInclude Include="src\bufq.h" />
<ClInclude Include="src\bufref.h" />
- <ClInclude Include="src\c-hyper.h" />
<ClInclude Include="src\cf-h1-proxy.h" />
<ClInclude Include="src\cf-h2-proxy.h" />
<ClInclude Include="src\cf-haproxy.h" />
<ClInclude Include="src\cf-https-connect.h" />
<ClInclude Include="src\cf-socket.h" />
<ClInclude Include="src\cfilters.h" />
- <ClInclude Include="src\config-amigaos.h" />
- <ClInclude Include="src\config-dos.h" />
<ClInclude Include="src\config-mac.h" />
<ClInclude Include="src\config-os400.h" />
<ClInclude Include="src\config-plan9.h" />
<ClInclude Include="src\config-riscos.h" />
<ClInclude Include="src\config-win32.h" />
- <ClInclude Include="src\config-win32ce.h" />
<ClInclude Include="src\conncache.h" />
<ClInclude Include="src\connect.h" />
<ClInclude Include="src\content_encoding.h" />
<ClInclude Include="src\cookie.h" />
+ <ClInclude Include="src\cshutdn.h" />
<ClInclude Include="src\curlx.h" />
<ClInclude Include="src\curl_addrinfo.h" />
- <ClInclude Include="src\curl_base64.h" />
<ClInclude Include="src\curl_ctype.h" />
<ClInclude Include="src\curl_des.h" />
<ClInclude Include="src\curl_endian.h" />
@@ -530,7 +559,6 @@
<ClInclude Include="src\curl_md5.h" />
<ClInclude Include="src\curl_memory.h" />
<ClInclude Include="src\curl_memrchr.h" />
- <ClInclude Include="src\curl_multibyte.h" />
<ClInclude Include="src\curl_ntlm_core.h" />
<ClInclude Include="src\curl_printf.h" />
<ClInclude Include="src\curl_range.h" />
@@ -543,16 +571,29 @@
<ClInclude Include="src\curl_sspi.h" />
<ClInclude Include="src\curl_threads.h" />
<ClInclude Include="src\curl_trc.h" />
+ <ClInclude Include="src\curlx\base64.h" />
+ <ClInclude Include="src\curlx\curlx.h" />
+ <ClInclude Include="src\curlx\dynbuf.h" />
+ <ClInclude Include="src\curlx\inet_pton.h" />
+ <ClInclude Include="src\curlx\multibyte.h" />
+ <ClInclude Include="src\curlx\nonblock.h" />
+ <ClInclude Include="src\curlx\strparse.h" />
+ <ClInclude Include="src\curlx\timediff.h" />
+ <ClInclude Include="src\curlx\timeval.h" />
+ <ClInclude Include="src\curlx\version_win32.h" />
+ <ClInclude Include="src\curlx\warnless.h" />
+ <ClInclude Include="src\curlx\winapi.h" />
<ClInclude Include="src\cw-out.h" />
+ <ClInclude Include="src\cw-pause.h" />
<ClInclude Include="src\dict.h" />
<ClInclude Include="src\doh.h" />
<ClInclude Include="src\dotdot.h" />
- <ClInclude Include="src\dynbuf.h" />
<ClInclude Include="src\dynhds.h" />
<ClInclude Include="src\easyif.h" />
<ClInclude Include="src\easy_lock.h" />
<ClInclude Include="src\easyoptions.h" />
<ClInclude Include="src\escape.h" />
+ <ClInclude Include="src\fake_addrinfo.h" />
<ClInclude Include="src\file.h" />
<ClInclude Include="src\fileinfo.h" />
<ClInclude Include="src\fopen.h" />
@@ -575,20 +616,20 @@
<ClInclude Include="src\http_negotiate.h" />
<ClInclude Include="src\http_ntlm.h" />
<ClInclude Include="src\http_proxy.h" />
+ <ClInclude Include="src\httpsrr.h" />
<ClInclude Include="src\idn.h" />
<ClInclude Include="src\if2ip.h" />
<ClInclude Include="src\imap.h" />
<ClInclude Include="src\inet_ntop.h" />
- <ClInclude Include="src\inet_pton.h" />
<ClInclude Include="src\llist.h" />
<ClInclude Include="src\macos.h" />
<ClInclude Include="src\memdebug.h" />
<ClInclude Include="src\mime.h" />
<ClInclude Include="src\mqtt.h" />
+ <ClInclude Include="src\multi_ev.h" />
<ClInclude Include="src\multihandle.h" />
<ClInclude Include="src\multiif.h" />
<ClInclude Include="src\netrc.h" />
- <ClInclude Include="src\nonblock.h" />
<ClInclude Include="src\noproxy.h" />
<ClInclude Include="src\parsedate.h" />
<ClInclude Include="src\pingpong.h" />
@@ -618,28 +659,31 @@
<ClInclude Include="src\strcase.h" />
<ClInclude Include="src\strdup.h" />
<ClInclude Include="src\strerror.h" />
- <ClInclude Include="src\strtok.h" />
- <ClInclude Include="src\strtoofft.h" />
<ClInclude Include="src\system_win32.h" />
<ClInclude Include="src\telnet.h" />
<ClInclude Include="src\tftp.h" />
- <ClInclude Include="src\timediff.h" />
- <ClInclude Include="src\timeval.h" />
<ClInclude Include="src\transfer.h" />
+ <ClInclude Include="src\uint-bset.h" />
+ <ClInclude Include="src\uint-hash.h" />
+ <ClInclude Include="src\uint-spbset.h" />
+ <ClInclude Include="src\uint-table.h" />
<ClInclude Include="src\url.h" />
<ClInclude Include="src\urlapi-int.h" />
<ClInclude Include="src\urldata.h" />
- <ClInclude Include="src\version_win32.h" />
+ <ClInclude Include="src\vtls\vtls_scache.h" />
+ <ClInclude Include="src\vtls\vtls_spack.h" />
<ClInclude Include="src\vquic\curl_msh3.h" />
<ClInclude Include="src\vquic\curl_ngtcp2.h" />
<ClInclude Include="src\vquic\curl_quiche.h" />
<ClInclude Include="src\vquic\vquic.h" />
<ClInclude Include="src\vquic\vquic_int.h" />
<ClInclude Include="src\vssh\curl_path.h" />
- <ClInclude Include="src\warnless.h" />
<ClInclude Include="src\ws.h" />
</ItemGroup>
<ItemGroup>
+ <ClCompile Include="src\curlx\timeval.c">
+ <PrecompiledHeader>NotUsing</PrecompiledHeader>
+ </ClCompile>
<ClCompile Include="src\strcase.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
@@ -649,10 +693,7 @@
<ClCompile Include="src\strerror.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
- <ClCompile Include="src\strtok.c">
- <PrecompiledHeader>NotUsing</PrecompiledHeader>
- </ClCompile>
- <ClCompile Include="src\strtoofft.c">
+ <ClCompile Include="src\strequal.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
<ClCompile Include="src\system_win32.c">
@@ -664,9 +705,6 @@
<ClCompile Include="src\tftp.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
- <ClCompile Include="src\timeval.c">
- <PrecompiledHeader>NotUsing</PrecompiledHeader>
- </ClCompile>
<ClCompile Include="src\transfer.c">
<PrecompiledHeader>NotUsing</PrecompiledHeader>
</ClCompile>
diff --git a/libs/libcurl/libcurl.vcxproj.filters b/libs/libcurl/libcurl.vcxproj.filters
index 68ed1bc635..3df6603fdc 100644
--- a/libs/libcurl/libcurl.vcxproj.filters
+++ b/libs/libcurl/libcurl.vcxproj.filters
@@ -11,10 +11,10 @@
<ClCompile Include="src\asyn-ares.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\asyn-thread.c">
+ <ClCompile Include="src\asyn-base.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\base64.c">
+ <ClCompile Include="src\asyn-thrdd.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\bufq.c">
@@ -23,9 +23,6 @@
<ClCompile Include="src\bufref.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\c-hyper.c">
- <Filter>Source Files</Filter>
- </ClCompile>
<ClCompile Include="src\cf-h1-proxy.c">
<Filter>Source Files</Filter>
</ClCompile>
@@ -56,6 +53,9 @@
<ClCompile Include="src\cookie.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="src\cshutdn.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="src\curl_addrinfo.c">
<Filter>Source Files</Filter>
</ClCompile>
@@ -80,9 +80,6 @@
<ClCompile Include="src\curl_memrchr.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\curl_multibyte.c">
- <Filter>Source Files</Filter>
- </ClCompile>
<ClCompile Include="src\curl_ntlm_core.c">
<Filter>Source Files</Filter>
</ClCompile>
@@ -107,9 +104,45 @@
<ClCompile Include="src\curl_trc.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="src\curlx\base64.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\curlx\dynbuf.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClInclude Include="src\curlx\inet_pton.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClCompile Include="src\curlx\multibyte.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\curlx\nonblock.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\curlx\strparse.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\curlx\timediff.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\curlx\timeval.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\curlx\version_win32.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\curlx\warnless.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\curlx\winapi.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="src\cw-out.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="src\cw-pause.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="src\dict.c">
<Filter>Source Files</Filter>
</ClCompile>
@@ -119,9 +152,6 @@
<ClCompile Include="src\doh.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\dynbuf.c">
- <Filter>Source Files</Filter>
- </ClCompile>
<ClCompile Include="src\dynhds.c">
<Filter>Source Files</Filter>
</ClCompile>
@@ -137,6 +167,9 @@
<ClCompile Include="src\escape.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="src\fake_addrinfo.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="src\file.c">
<Filter>Source Files</Filter>
</ClCompile>
@@ -173,9 +206,6 @@
<ClCompile Include="src\hmac.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\hostasyn.c">
- <Filter>Source Files</Filter>
- </ClCompile>
<ClCompile Include="src\hostip.c">
<Filter>Source Files</Filter>
</ClCompile>
@@ -185,9 +215,6 @@
<ClCompile Include="src\hostip6.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\hostsyn.c">
- <Filter>Source Files</Filter>
- </ClCompile>
<ClCompile Include="src\hsts.c">
<Filter>Source Files</Filter>
</ClCompile>
@@ -218,6 +245,9 @@
<ClCompile Include="src\http_proxy.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="src\httpsrr.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="src\idn.c">
<Filter>Source Files</Filter>
</ClCompile>
@@ -266,10 +296,10 @@
<ClCompile Include="src\multi.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\netrc.c">
+ <ClCompile Include="src\multi_ev.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\nonblock.c">
+ <ClCompile Include="src\netrc.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\noproxy.c">
@@ -347,10 +377,7 @@
<ClCompile Include="src\splay.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\timediff.c">
- <Filter>Source Files</Filter>
- </ClCompile>
- <ClCompile Include="src\version_win32.c">
+ <ClCompile Include="src\strequal.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\vauth\cleartext.c">
@@ -443,13 +470,16 @@
<ClCompile Include="src\vtls\vtls.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\vtls\wolfssl.c">
+ <ClCompile Include="src\vtls\vtls_scache.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\vtls\x509asn1.c">
+ <ClCompile Include="src\vtls\vtls_spack.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\warnless.c">
+ <ClCompile Include="src\vtls\wolfssl.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\vtls\x509asn1.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\strcase.c">
@@ -461,25 +491,28 @@
<ClCompile Include="src\strerror.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\strtok.c">
+ <ClCompile Include="src\system_win32.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\strtoofft.c">
+ <ClCompile Include="src\telnet.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\system_win32.c">
+ <ClCompile Include="src\tftp.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\telnet.c">
+ <ClCompile Include="src\transfer.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\tftp.c">
+ <ClCompile Include="src\uint-bset.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\timeval.c">
+ <ClCompile Include="src\uint-hash.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="src\transfer.c">
+ <ClCompile Include="src\uint-spbset.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="src\uint-table.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\url.c">
@@ -523,9 +556,6 @@
<ClInclude Include="src\bufref.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\c-hyper.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="src\cf-h1-proxy.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -544,12 +574,6 @@
<ClInclude Include="src\cfilters.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\config-amigaos.h">
- <Filter>Header Files</Filter>
- </ClInclude>
- <ClInclude Include="src\config-dos.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="src\config-mac.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -565,9 +589,6 @@
<ClInclude Include="src\config-win32.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\config-win32ce.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="src\conncache.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -580,15 +601,12 @@
<ClInclude Include="src\cookie.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\curlx.h">
+ <ClInclude Include="src\cshutdn.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\curl_addrinfo.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\curl_base64.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="src\curl_ctype.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -631,9 +649,6 @@
<ClInclude Include="src\curl_memrchr.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\curl_multibyte.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="src\curl_ntlm_core.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -670,9 +685,48 @@
<ClInclude Include="src\curl_trc.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="src\curlx\base64.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\curlx\curlx.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\curlx\dynbuf.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\curlx\inet_pton.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\curlx\multibyte.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\curlx\nonblock.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\curlx\strparse.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\curlx\timediff.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\curlx\timeval.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\curlx\version_win32.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\curlx\warnless.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\curlx\winapi.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="src\cw-out.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="src\cw-pause.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="src\dict.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -682,9 +736,6 @@
<ClInclude Include="src\dotdot.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\dynbuf.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="src\dynhds.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -700,6 +751,9 @@
<ClInclude Include="src\escape.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="src\fake_addrinfo.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="src\file.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -766,6 +820,9 @@
<ClInclude Include="src\http_proxy.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="src\httpsrr.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="src\idn.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -778,9 +835,6 @@
<ClInclude Include="src\inet_ntop.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\inet_pton.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="src\llist.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -796,6 +850,9 @@
<ClInclude Include="src\mqtt.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="src\multi_ev.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="src\multihandle.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -805,9 +862,6 @@
<ClInclude Include="src\netrc.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\nonblock.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="src\noproxy.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -895,28 +949,28 @@
<ClInclude Include="src\strerror.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\strtok.h">
+ <ClInclude Include="src\system_win32.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\strtoofft.h">
+ <ClInclude Include="src\telnet.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\system_win32.h">
+ <ClInclude Include="src\tftp.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\telnet.h">
+ <ClInclude Include="src\transfer.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\tftp.h">
+ <ClInclude Include="src\uint-bset.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\timediff.h">
+ <ClInclude Include="src\uint-hash.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\timeval.h">
+ <ClInclude Include="src\uint-spbset.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\transfer.h">
+ <ClInclude Include="src\uint-table.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\url.h">
@@ -928,7 +982,10 @@
<ClInclude Include="src\urldata.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\version_win32.h">
+ <ClInclude Include="src\vtls\vtls_scache.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="src\vtls\vtls_spack.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\vquic\curl_msh3.h">
@@ -946,9 +1003,6 @@
<ClInclude Include="src\vquic\vquic_int.h">
<Filter>Header Files</Filter>
</ClInclude>
- <ClInclude Include="src\warnless.h">
- <Filter>Header Files</Filter>
- </ClInclude>
<ClInclude Include="src\ws.h">
<Filter>Header Files</Filter>
</ClInclude>
diff --git a/libs/libcurl/src/.checksrc b/libs/libcurl/src/.checksrc
index aa8bcffd6f..008da2160d 100644
--- a/libs/libcurl/src/.checksrc
+++ b/libs/libcurl/src/.checksrc
@@ -1,2 +1,8 @@
-enable STRERROR
-enable STRNCPY
+banfunc strerror
+banfunc strncpy
+banfunc sscanf
+banfunc snprintf
+banfunc vsnprint
+banfunc strtoul
+banfunc strtol
+banfunc strtok_r
diff --git a/libs/libcurl/src/CMakeLists.txt b/libs/libcurl/src/CMakeLists.txt
index 7ba1780edb..e37b62b031 100644
--- a/libs/libcurl/src/CMakeLists.txt
+++ b/libs/libcurl/src/CMakeLists.txt
@@ -21,27 +21,26 @@
# SPDX-License-Identifier: curl
#
###########################################################################
-set(LIB_NAME "libcurl")
-set(LIBCURL_OUTPUT_NAME "libcurl" CACHE STRING "Basename of the curl library")
-add_definitions("-DBUILDING_LIBCURL")
+
+set(LIBCURL_OUTPUT_NAME "${LIB_NAME}" CACHE STRING "Basename of the curl library")
+
+set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "BUILDING_LIBCURL")
+set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS "${CURL_DEBUG_MACROS}")
configure_file("curl_config.h.cmake" "${CMAKE_CURRENT_BINARY_DIR}/curl_config.h")
# Get 'CSOURCES', 'HHEADERS' variables
-transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake")
+curl_transform_makefile_inc("Makefile.inc" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake")
include("${CMAKE_CURRENT_BINARY_DIR}/Makefile.inc.cmake")
-# DllMain is added later for DLL builds only.
-list(REMOVE_ITEM CSOURCES "dllmain.c")
-
list(APPEND HHEADERS "${CMAKE_CURRENT_BINARY_DIR}/curl_config.h")
# The rest of the build
-include_directories(
- "${PROJECT_BINARY_DIR}/lib" # for "curl_config.h"
- "${PROJECT_SOURCE_DIR}/lib" # for "curl_setup.h"
+set_property(DIRECTORY APPEND PROPERTY INCLUDE_DIRECTORIES
+ "${PROJECT_BINARY_DIR}/lib" # for "curl_config.h"
)
+
if(USE_ARES)
include_directories(SYSTEM ${CARES_INCLUDE_DIRS})
endif()
@@ -53,24 +52,43 @@ if(CURL_BUILD_TESTING)
EXCLUDE_FROM_ALL
${HHEADERS} ${CSOURCES}
)
- target_compile_definitions(curlu PUBLIC "UNITTESTS" "CURL_STATICLIB")
+ target_compile_definitions(curlu PUBLIC "CURL_STATICLIB" "UNITTESTS")
target_link_libraries(curlu PRIVATE ${CURL_LIBS})
+ # There is plenty of parallelism when building the testdeps target.
+ # Override the curlu batch size with the maximum to optimize performance.
+ set_target_properties(curlu PROPERTIES UNITY_BUILD_BATCH_SIZE 0)
endif()
if(ENABLE_CURLDEBUG)
- # We must compile these sources separately to avoid memdebug.h redefinitions
- # applying to them.
- set_source_files_properties("memdebug.c" "curl_multibyte.c" PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
+ # We must compile this source separately to avoid memdebug.h redefinitions
+ # applying to it.
+ set_source_files_properties("memdebug.c" PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
endif()
## Library definition
-# Add "_imp" as a suffix before the extension to avoid conflicting with
-# the statically linked "libcurl.lib" (typically with MSVC)
-if(WIN32 AND
- NOT IMPORT_LIB_SUFFIX AND
- CMAKE_STATIC_LIBRARY_SUFFIX STREQUAL CMAKE_IMPORT_LIBRARY_SUFFIX)
- set(IMPORT_LIB_SUFFIX "_imp")
+if(NOT DEFINED IMPORT_LIB_SUFFIX)
+ # Suffix implib name with "_imp" by default, to avoid conflicting with
+ # the generated static "libcurl.lib" (typically with MSVC).
+ if(WIN32 AND BUILD_SHARED_LIBS AND
+ CMAKE_IMPORT_LIBRARY_SUFFIX STREQUAL CMAKE_STATIC_LIBRARY_SUFFIX)
+ set(IMPORT_LIB_SUFFIX "_imp")
+ else()
+ set(IMPORT_LIB_SUFFIX "")
+ endif()
+endif()
+if(NOT DEFINED STATIC_LIB_SUFFIX)
+ set(STATIC_LIB_SUFFIX "")
+endif()
+
+# Detect implib static lib filename collision
+if(WIN32 AND BUILD_STATIC_LIBS AND BUILD_SHARED_LIBS AND
+ "${IMPORT_LIB_SUFFIX}${CMAKE_IMPORT_LIBRARY_SUFFIX}" STREQUAL
+ "${STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}")
+ message(FATAL_ERROR "Library suffix is the same ('${STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}') "
+ "for the import and static '${LIBCURL_OUTPUT_NAME}' library. "
+ "Set IMPORT_LIB_SUFFIX and/or STATIC_LIB_SUFFIX to different values, "
+ "or disable building either the shared or static library to avoid the filename collision.")
endif()
# Whether to do a single compilation pass for libcurl sources and reuse these
@@ -87,9 +105,9 @@ if(NOT DEFINED SHARE_LIB_OBJECT)
endif()
endif()
-if(SHARE_LIB_OBJECT)
+if(SHARE_LIB_OBJECT AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.12)
set(LIB_OBJECT "libcurl_object")
- add_library(${LIB_OBJECT} OBJECT ${HHEADERS} ${CSOURCES})
+ add_library(${LIB_OBJECT} OBJECT ${HHEADERS} ${CSOURCES}) # Requires CMake 3.12
if(WIN32)
# Define CURL_STATICLIB always, to disable __declspec(dllexport) for
# exported libcurl symbols. We handle exports via libcurl.def instead.
@@ -105,9 +123,13 @@ if(SHARE_LIB_OBJECT)
set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS")
endif()
if(CURL_HAS_LTO)
- set_target_properties(${LIB_OBJECT} PROPERTIES
- INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE
- INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE)
+ if(CMAKE_CONFIGURATION_TYPES)
+ set_target_properties(${LIB_OBJECT} PROPERTIES
+ INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE
+ INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE)
+ else()
+ set_target_properties(${LIB_OBJECT} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
+ endif()
endif()
target_include_directories(${LIB_OBJECT} INTERFACE
@@ -125,22 +147,27 @@ if(BUILD_STATIC_LIBS)
add_library(${LIB_STATIC} STATIC ${LIB_SOURCE})
add_library(${PROJECT_NAME}::${LIB_STATIC} ALIAS ${LIB_STATIC})
if(WIN32)
- set_property(TARGET ${LIB_OBJECT} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB")
+ set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_STATICLIB")
endif()
target_link_libraries(${LIB_STATIC} PRIVATE ${CURL_LIBS})
# Remove the "lib" prefix since the library is already named "libcurl".
set_target_properties(${LIB_STATIC} PROPERTIES
PREFIX "" OUTPUT_NAME "${LIBCURL_OUTPUT_NAME}"
SUFFIX "${STATIC_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}"
- INTERFACE_COMPILE_DEFINITIONS "CURL_STATICLIB")
+ INTERFACE_COMPILE_DEFINITIONS "CURL_STATICLIB"
+ INTERFACE_LINK_DIRECTORIES "${CURL_LIBDIRS}")
if(CURL_HIDES_PRIVATE_SYMBOLS)
set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_FLAGS "${CURL_CFLAG_SYMBOLS_HIDE}")
set_property(TARGET ${LIB_STATIC} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS")
endif()
if(CURL_HAS_LTO)
- set_target_properties(${LIB_STATIC} PROPERTIES
- INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE
- INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE)
+ if(CMAKE_CONFIGURATION_TYPES)
+ set_target_properties(${LIB_OBJECT} PROPERTIES
+ INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE
+ INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE)
+ else()
+ set_target_properties(${LIB_OBJECT} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
+ endif()
endif()
target_include_directories(${LIB_STATIC} INTERFACE
@@ -152,15 +179,8 @@ if(BUILD_SHARED_LIBS)
list(APPEND libcurl_export ${LIB_SHARED})
add_library(${LIB_SHARED} SHARED ${LIB_SOURCE})
add_library(${PROJECT_NAME}::${LIB_SHARED} ALIAS ${LIB_SHARED})
- if(WIN32 OR CYGWIN)
- if(CYGWIN)
- # For Cygwin always compile dllmain.c as a separate unit since it
- # includes windows.h, which should not be included in other units.
- set_source_files_properties("dllmain.c" PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
- endif()
- set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "dllmain.c")
- endif()
if(WIN32)
+ set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "dllmain.c")
set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "libcurl.rc")
if(CURL_HIDES_PRIVATE_SYMBOLS)
set_property(TARGET ${LIB_SHARED} APPEND PROPERTY SOURCES "${PROJECT_SOURCE_DIR}/lib/libcurl.def")
@@ -177,9 +197,13 @@ if(BUILD_SHARED_LIBS)
set_property(TARGET ${LIB_SHARED} APPEND PROPERTY COMPILE_DEFINITIONS "CURL_HIDDEN_SYMBOLS")
endif()
if(CURL_HAS_LTO)
- set_target_properties(${LIB_SHARED} PROPERTIES
- INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE
- INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE)
+ if(CMAKE_CONFIGURATION_TYPES)
+ set_target_properties(${LIB_OBJECT} PROPERTIES
+ INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE
+ INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO TRUE)
+ else()
+ set_target_properties(${LIB_OBJECT} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
+ endif()
endif()
target_include_directories(${LIB_SHARED} INTERFACE
@@ -208,7 +232,7 @@ if(BUILD_SHARED_LIBS)
if(CURL_LIBCURL_SOVERSION OR CURL_LIBCURL_VERSIONED_SYMBOLS)
# Get 'VERSIONCHANGE', 'VERSIONADD', 'VERSIONDEL', 'VERSIONINFO' variables
- transform_makefile_inc("Makefile.soname" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake")
+ curl_transform_makefile_inc("Makefile.soname" "${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake")
include("${CMAKE_CURRENT_BINARY_DIR}/Makefile.soname.cmake")
math(EXPR _cmakesoname "${VERSIONCHANGE} - ${VERSIONDEL}")
@@ -255,8 +279,11 @@ if(BUILD_SHARED_LIBS)
set(CMAKE_REQUIRED_LINK_OPTIONS "-Wl,--version-script=${CMAKE_CURRENT_BINARY_DIR}/libcurl.vers")
check_c_source_compiles("int main(void) { return 0; }" HAVE_VERSIONED_SYMBOLS)
if(HAVE_VERSIONED_SYMBOLS)
- # Superseded by LINK_OPTIONS in CMake 3.13 and later.
- set_target_properties(${LIB_SHARED} PROPERTIES LINK_FLAGS "${CMAKE_REQUIRED_LINK_OPTIONS}")
+ if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.13)
+ set_target_properties(${LIB_SHARED} PROPERTIES LINK_OPTIONS "${CMAKE_REQUIRED_LINK_OPTIONS}")
+ else()
+ set_target_properties(${LIB_SHARED} PROPERTIES LINK_FLAGS "${CMAKE_REQUIRED_LINK_OPTIONS}")
+ endif()
else()
message(WARNING "Versioned symbols requested, but not supported by the toolchain.")
endif()
diff --git a/libs/libcurl/src/Makefile.am b/libs/libcurl/src/Makefile.am
index bb59de5af9..c91619fd6e 100644
--- a/libs/libcurl/src/Makefile.am
+++ b/libs/libcurl/src/Makefile.am
@@ -28,11 +28,9 @@ CMAKE_DIST = CMakeLists.txt curl_config.h.cmake
CHECKSRC_DIST = .checksrc vauth/.checksrc vquic/.checksrc vssh/.checksrc \
vtls/.checksrc
-EXTRA_DIST = Makefile.mk config-win32.h config-win32ce.h config-plan9.h \
- config-riscos.h config-mac.h curl_config.h.in config-dos.h libcurl.rc \
- config-amigaos.h config-win32ce.h config-os400.h setup-os400.h \
- $(CMAKE_DIST) setup-win32.h Makefile.soname optiontable.pl libcurl.def \
- $(CHECKSRC_DIST)
+EXTRA_DIST = config-mac.h config-os400.h config-plan9.h config-riscos.h \
+ config-win32.h curl_config.h.in libcurl.rc libcurl.def \
+ $(CMAKE_DIST) Makefile.soname optiontable.pl $(CHECKSRC_DIST)
lib_LTLIBRARIES = libcurl.la
@@ -52,11 +50,9 @@ CFLAGS += @CURL_CFLAG_EXTRAS@
#
# $(top_srcdir)/include is for libcurl's external include files
# $(top_builddir)/lib is for libcurl's generated lib/curl_config.h file
-# $(top_srcdir)/lib for libcurl's lib/curl_setup.h and other "private" files
-AM_CPPFLAGS = -I$(top_srcdir)/include \
- -I$(top_builddir)/lib \
- -I$(top_srcdir)/lib
+AM_CPPFLAGS = -I$(top_srcdir)/include \
+ -I$(top_builddir)/lib
# Prevent LIBS from being used for all link targets
LIBS = $(BLANK_AT_MAKETIME)
@@ -66,18 +62,28 @@ include Makefile.soname
AM_CPPFLAGS += -DBUILDING_LIBCURL
AM_LDFLAGS =
AM_CFLAGS =
+if DEBUGBUILD
+AM_CPPFLAGS += -DDEBUGBUILD
+endif
+if CURLDEBUG
+AM_CPPFLAGS += -DCURLDEBUG
+endif
# Makefile.inc provides the CSOURCES and HHEADERS defines
include Makefile.inc
+if DOING_NATIVE_WINDOWS
+CSOURCES += dllmain.c
+endif
+
if USE_UNITY
# Keep these separate to avoid duplicate definitions when linking libtests
# in static mode.
-curl_EXCLUDE = curl_threads.c timediff.c warnless.c
-if DEBUGBUILD
-# We must compile these sources separately to avoid memdebug.h redefinitions
-# applying to them.
-curl_EXCLUDE += memdebug.c curl_multibyte.c
+curl_EXCLUDE = curl_threads.c curlx/timediff.c curlx/warnless.c
+if CURLDEBUG
+# We must compile this source separately to avoid memdebug.h redefinitions
+# applying to it.
+curl_EXCLUDE += memdebug.c
endif
libcurl_unity.c: $(top_srcdir)/scripts/mk-unity.pl $(CSOURCES)
@PERL@ $(top_srcdir)/scripts/mk-unity.pl $(srcdir) $(CSOURCES) --exclude $(curl_EXCLUDE) > libcurl_unity.c
@@ -145,25 +151,30 @@ CS_1 =
CS_ = $(CS_0)
checksrc:
- $(CHECKSRC)(@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(srcdir) \
- -W$(srcdir)/curl_config.h $(srcdir)/*.[ch] $(srcdir)/vauth/*.[ch] \
- $(srcdir)/vtls/*.[ch] $(srcdir)/vquic/*.[ch] $(srcdir)/vssh/*.[ch])
+ $(CHECKSRC)(@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(srcdir) \
+ $(CSOURCES) $(HHEADERS))
+if NOT_CURL_CI
if DEBUGBUILD
# for debug builds, we scan the sources on all regular make invokes
all-local: checksrc
endif
+endif
# disable the tests that are mostly causing false positives
-TIDYFLAGS=-checks=-clang-analyzer-security.insecureAPI.strcpy,-clang-analyzer-optin.performance.Padding,-clang-analyzer-valist.Uninitialized,-clang-analyzer-core.NonNullParamChecker,-clang-analyzer-core.NullDereference -quiet
+TIDYFLAGS := -checks=-clang-analyzer-security.insecureAPI.strcpy,-clang-analyzer-optin.performance.Padding,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling -quiet
+if CURL_WERROR
+TIDYFLAGS += --warnings-as-errors=*
+endif
-TIDY:=clang-tidy
+TIDY := clang-tidy
tidy:
- $(TIDY) $(CSOURCES) $(TIDYFLAGS) -- $(AM_CPPFLAGS) $(CPPFLAGS) -DHAVE_CONFIG_H
+ (_csources=`echo ' $(CSOURCES)' | sed -E -e 's/ +$$//' -e 's/ +/ /g' -e 's| | $(srcdir)/|g'`; \
+ $(TIDY) $$_csources $(TIDYFLAGS) $(CURL_CLANG_TIDYFLAGS) -- $(AM_CPPFLAGS) $(CPPFLAGS) -DHAVE_CONFIG_H)
optiontable:
- perl optiontable.pl < $(top_srcdir)/include/curl/curl.h > easyoptions.c
+ @PERL@ $(srcdir)/optiontable.pl < $(top_srcdir)/include/curl/curl.h > $(srcdir)/easyoptions.c
if HAVE_WINDRES
.rc.lo:
diff --git a/libs/libcurl/src/Makefile.in b/libs/libcurl/src/Makefile.in
index abe6e4cdb7..b019dc913c 100644
--- a/libs/libcurl/src/Makefile.in
+++ b/libs/libcurl/src/Makefile.in
@@ -136,19 +136,25 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-# We must compile these sources separately to avoid memdebug.h redefinitions
-# applying to them.
-@DEBUGBUILD_TRUE@@USE_UNITY_TRUE@am__append_1 = memdebug.c curl_multibyte.c
-@CURL_LT_SHLIB_USE_VERSION_INFO_TRUE@am__append_2 = $(VERSIONINFO)
-@CURL_LT_SHLIB_USE_NO_UNDEFINED_TRUE@am__append_3 = -no-undefined
-@CURL_LT_SHLIB_USE_MIMPURE_TEXT_TRUE@am__append_4 = -mimpure-text
-@CURL_LT_SHLIB_USE_VERSIONED_SYMBOLS_TRUE@am__append_5 = -Wl,--version-script=libcurl.vers
+@DEBUGBUILD_TRUE@am__append_1 = -DDEBUGBUILD
+@CURLDEBUG_TRUE@am__append_2 = -DCURLDEBUG
+
+# Makefile.inc provides the CSOURCES and HHEADERS defines
+@DOING_NATIVE_WINDOWS_TRUE@am__append_3 = dllmain.c
+# We must compile this source separately to avoid memdebug.h redefinitions
+# applying to it.
+@CURLDEBUG_TRUE@@USE_UNITY_TRUE@am__append_4 = memdebug.c
+@CURL_LT_SHLIB_USE_VERSION_INFO_TRUE@am__append_5 = $(VERSIONINFO)
+@CURL_LT_SHLIB_USE_NO_UNDEFINED_TRUE@am__append_6 = -no-undefined
+@CURL_LT_SHLIB_USE_MIMPURE_TEXT_TRUE@am__append_7 = -mimpure-text
+@CURL_LT_SHLIB_USE_VERSIONED_SYMBOLS_TRUE@am__append_8 = -Wl,--version-script=libcurl.vers
# if symbol-hiding is enabled, hide them!
-@CURL_LT_SHLIB_USE_VERSIONED_SYMBOLS_FALSE@@DOING_CURL_SYMBOL_HIDING_TRUE@am__append_6 = -export-symbols-regex '^curl_.*'
-@USE_CPPFLAG_CURL_STATICLIB_TRUE@am__append_7 = -DCURL_STATICLIB
-@HAVE_WINDRES_TRUE@@USE_CPPFLAG_CURL_STATICLIB_FALSE@am__append_8 = $(LIB_RCFILES)
-@DOING_CURL_SYMBOL_HIDING_TRUE@am__append_9 = -DCURL_HIDDEN_SYMBOLS
-@DOING_CURL_SYMBOL_HIDING_TRUE@am__append_10 = $(CFLAG_CURL_SYMBOL_HIDING)
+@CURL_LT_SHLIB_USE_VERSIONED_SYMBOLS_FALSE@@DOING_CURL_SYMBOL_HIDING_TRUE@am__append_9 = -export-symbols-regex '^curl_.*'
+@USE_CPPFLAG_CURL_STATICLIB_TRUE@am__append_10 = -DCURL_STATICLIB
+@HAVE_WINDRES_TRUE@@USE_CPPFLAG_CURL_STATICLIB_FALSE@am__append_11 = $(LIB_RCFILES)
+@DOING_CURL_SYMBOL_HIDING_TRUE@am__append_12 = -DCURL_HIDDEN_SYMBOLS
+@DOING_CURL_SYMBOL_HIDING_TRUE@am__append_13 = $(CFLAG_CURL_SYMBOL_HIDING)
+@CURL_WERROR_TRUE@am__append_14 = --warnings-as-errors=*
subdir = lib
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/curl-amissl.m4 \
@@ -214,30 +220,30 @@ am__installdirs = "$(DESTDIR)$(libdir)"
LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES)
libcurl_la_LIBADD =
am__libcurl_la_SOURCES_DIST = altsvc.c amigaos.c asyn-ares.c \
- asyn-thread.c base64.c bufq.c bufref.c c-hyper.c cf-h1-proxy.c \
+ asyn-base.c asyn-thrdd.c bufq.c bufref.c cf-h1-proxy.c \
cf-h2-proxy.c cf-haproxy.c cf-https-connect.c cf-socket.c \
cfilters.c conncache.c connect.c content_encoding.c cookie.c \
- curl_addrinfo.c curl_des.c curl_endian.c curl_fnmatch.c \
- curl_get_line.c curl_gethostname.c curl_gssapi.c \
- curl_memrchr.c curl_multibyte.c curl_ntlm_core.c curl_range.c \
+ cshutdn.c curl_addrinfo.c curl_des.c curl_endian.c \
+ curl_fnmatch.c curl_get_line.c curl_gethostname.c \
+ curl_gssapi.c curl_memrchr.c curl_ntlm_core.c curl_range.c \
curl_rtmp.c curl_sasl.c curl_sha512_256.c curl_sspi.c \
- curl_threads.c curl_trc.c cw-out.c dict.c dllmain.c doh.c \
- dynbuf.c dynhds.c easy.c easygetopt.c easyoptions.c escape.c \
- file.c fileinfo.c fopen.c formdata.c ftp.c ftplistparser.c \
- getenv.c getinfo.c gopher.c hash.c headers.c hmac.c hostasyn.c \
- hostip.c hostip4.c hostip6.c hostsyn.c hsts.c http.c http1.c \
+ curl_threads.c curl_trc.c cw-out.c cw-pause.c dict.c doh.c \
+ dynhds.c easy.c easygetopt.c easyoptions.c escape.c \
+ fake_addrinfo.c file.c fileinfo.c fopen.c formdata.c ftp.c \
+ ftplistparser.c getenv.c getinfo.c gopher.c hash.c headers.c \
+ hmac.c hostip.c hostip4.c hostip6.c hsts.c http.c http1.c \
http2.c http_aws_sigv4.c http_chunks.c http_digest.c \
- http_negotiate.c http_ntlm.c http_proxy.c idn.c if2ip.c imap.c \
- inet_ntop.c inet_pton.c krb5.c ldap.c llist.c macos.c md4.c \
- md5.c memdebug.c mime.c mprintf.c mqtt.c multi.c netrc.c \
- nonblock.c noproxy.c openldap.c parsedate.c pingpong.c pop3.c \
+ http_negotiate.c http_ntlm.c http_proxy.c httpsrr.c idn.c \
+ if2ip.c imap.c inet_ntop.c krb5.c ldap.c llist.c macos.c md4.c \
+ md5.c memdebug.c mime.c mprintf.c mqtt.c multi.c multi_ev.c \
+ netrc.c noproxy.c openldap.c parsedate.c pingpong.c pop3.c \
progress.c psl.c rand.c rename.c request.c rtsp.c select.c \
sendf.c setopt.c sha256.c share.c slist.c smb.c smtp.c \
socketpair.c socks.c socks_gssapi.c socks_sspi.c speedcheck.c \
- splay.c strcase.c strdup.c strerror.c strtok.c strtoofft.c \
- system_win32.c telnet.c tftp.c timediff.c timeval.c transfer.c \
- url.c urlapi.c version.c version_win32.c warnless.c ws.c \
- vauth/cleartext.c vauth/cram.c vauth/digest.c \
+ splay.c strcase.c strdup.c strequal.c strerror.c \
+ system_win32.c telnet.c tftp.c transfer.c uint-bset.c \
+ uint-hash.c uint-spbset.c uint-table.c url.c urlapi.c \
+ version.c ws.c vauth/cleartext.c vauth/cram.c vauth/digest.c \
vauth/digest_sspi.c vauth/gsasl.c vauth/krb5_gssapi.c \
vauth/krb5_sspi.c vauth/ntlm.c vauth/ntlm_sspi.c \
vauth/oauth2.c vauth/spnego_gssapi.c vauth/spnego_sspi.c \
@@ -245,83 +251,90 @@ am__libcurl_la_SOURCES_DIST = altsvc.c amigaos.c asyn-ares.c \
vtls/hostcheck.c vtls/keylog.c vtls/mbedtls.c \
vtls/mbedtls_threadlock.c vtls/openssl.c vtls/rustls.c \
vtls/schannel.c vtls/schannel_verify.c vtls/sectransp.c \
- vtls/vtls.c vtls/wolfssl.c vtls/x509asn1.c vquic/curl_msh3.c \
+ vtls/vtls.c vtls/vtls_scache.c vtls/vtls_spack.c \
+ vtls/wolfssl.c vtls/x509asn1.c vquic/curl_msh3.c \
vquic/curl_ngtcp2.c vquic/curl_osslq.c vquic/curl_quiche.c \
vquic/vquic.c vquic/vquic-tls.c vssh/libssh.c vssh/libssh2.c \
- vssh/curl_path.c vssh/wolfssh.c altsvc.h amigaos.h \
- arpa_telnet.h asyn.h bufq.h bufref.h c-hyper.h cf-h1-proxy.h \
- cf-h2-proxy.h cf-haproxy.h cf-https-connect.h cf-socket.h \
- cfilters.h conncache.h connect.h content_encoding.h cookie.h \
- curl_addrinfo.h curl_base64.h curl_ctype.h curl_des.h \
- curl_endian.h curl_fnmatch.h curl_get_line.h \
- curl_gethostname.h curl_gssapi.h curl_hmac.h curl_krb5.h \
- curl_ldap.h curl_md4.h curl_md5.h curl_memory.h curl_memrchr.h \
- curl_multibyte.h curl_ntlm_core.h curl_printf.h curl_range.h \
- curl_rtmp.h curl_sasl.h curl_setup.h curl_setup_once.h \
- curl_sha256.h curl_sha512_256.h curl_sspi.h curl_threads.h \
- curl_trc.h curlx.h cw-out.h dict.h doh.h dynbuf.h dynhds.h \
- easy_lock.h easyif.h easyoptions.h escape.h file.h fileinfo.h \
- fopen.h formdata.h ftp.h ftplistparser.h functypes.h getinfo.h \
- gopher.h hash.h headers.h hostip.h hsts.h http.h http1.h \
- http2.h http_aws_sigv4.h http_chunks.h http_digest.h \
- http_negotiate.h http_ntlm.h http_proxy.h idn.h if2ip.h imap.h \
- inet_ntop.h inet_pton.h llist.h macos.h memdebug.h mime.h \
- mqtt.h multihandle.h multiif.h netrc.h nonblock.h noproxy.h \
+ vssh/curl_path.c vssh/wolfssh.c curlx/base64.c curlx/dynbuf.c \
+ curlx/inet_pton.c curlx/multibyte.c curlx/nonblock.c \
+ curlx/strparse.c curlx/timediff.c curlx/timeval.c \
+ curlx/version_win32.c curlx/warnless.c curlx/winapi.c \
+ dllmain.c altsvc.h amigaos.h arpa_telnet.h asyn.h bufq.h \
+ bufref.h cf-h1-proxy.h cf-h2-proxy.h cf-haproxy.h \
+ cf-https-connect.h cf-socket.h cfilters.h conncache.h \
+ cshutdn.h connect.h content_encoding.h cookie.h \
+ curl_addrinfo.h curl_ctype.h curl_des.h curl_endian.h \
+ curl_fnmatch.h curl_get_line.h curl_gethostname.h \
+ curl_gssapi.h curl_hmac.h curl_krb5.h curl_ldap.h curl_md4.h \
+ curl_md5.h curl_memory.h curl_memrchr.h curl_ntlm_core.h \
+ curl_printf.h curl_range.h curl_rtmp.h curl_sasl.h \
+ curl_setup.h curl_setup_once.h curl_sha256.h curl_sha512_256.h \
+ curl_sspi.h curl_threads.h curl_trc.h cw-out.h cw-pause.h \
+ dict.h doh.h dynhds.h easy_lock.h easyif.h easyoptions.h \
+ escape.h fake_addrinfo.h file.h fileinfo.h fopen.h formdata.h \
+ ftp.h ftplistparser.h functypes.h getinfo.h gopher.h hash.h \
+ headers.h hostip.h hsts.h http.h http1.h http2.h \
+ http_aws_sigv4.h http_chunks.h http_digest.h http_negotiate.h \
+ http_ntlm.h http_proxy.h httpsrr.h idn.h if2ip.h imap.h \
+ inet_ntop.h llist.h macos.h memdebug.h mime.h mqtt.h \
+ multihandle.h multi_ev.h multiif.h netrc.h noproxy.h \
parsedate.h pingpong.h pop3.h progress.h psl.h rand.h rename.h \
- request.h rtsp.h select.h sendf.h setopt.h setup-vms.h share.h \
- sigpipe.h slist.h smb.h smtp.h sockaddr.h socketpair.h socks.h \
- speedcheck.h splay.h strcase.h strdup.h strerror.h strtok.h \
- strtoofft.h system_win32.h telnet.h tftp.h timediff.h \
- timeval.h transfer.h url.h urlapi-int.h urldata.h \
- version_win32.h warnless.h ws.h vauth/digest.h vauth/ntlm.h \
- vauth/vauth.h vtls/bearssl.h vtls/cipher_suite.h vtls/gtls.h \
+ request.h rtsp.h select.h sendf.h setopt.h setup-os400.h \
+ setup-vms.h setup-win32.h share.h sigpipe.h slist.h smb.h \
+ smtp.h sockaddr.h socketpair.h socks.h speedcheck.h splay.h \
+ strcase.h strdup.h strerror.h system_win32.h telnet.h tftp.h \
+ transfer.h uint-bset.h uint-hash.h uint-spbset.h uint-table.h \
+ url.h urlapi-int.h urldata.h ws.h vauth/digest.h vauth/vauth.h \
+ vtls/bearssl.h vtls/cipher_suite.h vtls/gtls.h \
vtls/hostcheck.h vtls/keylog.h vtls/mbedtls.h \
vtls/mbedtls_threadlock.h vtls/openssl.h vtls/rustls.h \
vtls/schannel.h vtls/schannel_int.h vtls/sectransp.h \
- vtls/vtls.h vtls/vtls_int.h vtls/wolfssl.h vtls/x509asn1.h \
+ vtls/vtls.h vtls/vtls_int.h vtls/vtls_scache.h \
+ vtls/vtls_spack.h vtls/wolfssl.h vtls/x509asn1.h \
vquic/curl_msh3.h vquic/curl_ngtcp2.h vquic/curl_osslq.h \
vquic/curl_quiche.h vquic/vquic.h vquic/vquic_int.h \
- vquic/vquic-tls.h vssh/curl_path.h vssh/ssh.h libcurl.rc
+ vquic/vquic-tls.h vssh/curl_path.h vssh/ssh.h curlx/base64.h \
+ curlx/curlx.h curlx/dynbuf.h curlx/inet_pton.h \
+ curlx/multibyte.h curlx/nonblock.h curlx/strparse.h \
+ curlx/timediff.h curlx/timeval.h curlx/version_win32.h \
+ curlx/warnless.h curlx/winapi.h libcurl.rc
am__objects_1 = libcurl_la-altsvc.lo libcurl_la-amigaos.lo \
- libcurl_la-asyn-ares.lo libcurl_la-asyn-thread.lo \
- libcurl_la-base64.lo libcurl_la-bufq.lo libcurl_la-bufref.lo \
- libcurl_la-c-hyper.lo libcurl_la-cf-h1-proxy.lo \
+ libcurl_la-asyn-ares.lo libcurl_la-asyn-base.lo \
+ libcurl_la-asyn-thrdd.lo libcurl_la-bufq.lo \
+ libcurl_la-bufref.lo libcurl_la-cf-h1-proxy.lo \
libcurl_la-cf-h2-proxy.lo libcurl_la-cf-haproxy.lo \
libcurl_la-cf-https-connect.lo libcurl_la-cf-socket.lo \
libcurl_la-cfilters.lo libcurl_la-conncache.lo \
libcurl_la-connect.lo libcurl_la-content_encoding.lo \
- libcurl_la-cookie.lo libcurl_la-curl_addrinfo.lo \
- libcurl_la-curl_des.lo libcurl_la-curl_endian.lo \
- libcurl_la-curl_fnmatch.lo libcurl_la-curl_get_line.lo \
- libcurl_la-curl_gethostname.lo libcurl_la-curl_gssapi.lo \
- libcurl_la-curl_memrchr.lo libcurl_la-curl_multibyte.lo \
+ libcurl_la-cookie.lo libcurl_la-cshutdn.lo \
+ libcurl_la-curl_addrinfo.lo libcurl_la-curl_des.lo \
+ libcurl_la-curl_endian.lo libcurl_la-curl_fnmatch.lo \
+ libcurl_la-curl_get_line.lo libcurl_la-curl_gethostname.lo \
+ libcurl_la-curl_gssapi.lo libcurl_la-curl_memrchr.lo \
libcurl_la-curl_ntlm_core.lo libcurl_la-curl_range.lo \
libcurl_la-curl_rtmp.lo libcurl_la-curl_sasl.lo \
libcurl_la-curl_sha512_256.lo libcurl_la-curl_sspi.lo \
libcurl_la-curl_threads.lo libcurl_la-curl_trc.lo \
- libcurl_la-cw-out.lo libcurl_la-dict.lo libcurl_la-dllmain.lo \
- libcurl_la-doh.lo libcurl_la-dynbuf.lo libcurl_la-dynhds.lo \
- libcurl_la-easy.lo libcurl_la-easygetopt.lo \
- libcurl_la-easyoptions.lo libcurl_la-escape.lo \
+ libcurl_la-cw-out.lo libcurl_la-cw-pause.lo libcurl_la-dict.lo \
+ libcurl_la-doh.lo libcurl_la-dynhds.lo libcurl_la-easy.lo \
+ libcurl_la-easygetopt.lo libcurl_la-easyoptions.lo \
+ libcurl_la-escape.lo libcurl_la-fake_addrinfo.lo \
libcurl_la-file.lo libcurl_la-fileinfo.lo libcurl_la-fopen.lo \
libcurl_la-formdata.lo libcurl_la-ftp.lo \
libcurl_la-ftplistparser.lo libcurl_la-getenv.lo \
libcurl_la-getinfo.lo libcurl_la-gopher.lo libcurl_la-hash.lo \
- libcurl_la-headers.lo libcurl_la-hmac.lo \
- libcurl_la-hostasyn.lo libcurl_la-hostip.lo \
- libcurl_la-hostip4.lo libcurl_la-hostip6.lo \
- libcurl_la-hostsyn.lo libcurl_la-hsts.lo libcurl_la-http.lo \
- libcurl_la-http1.lo libcurl_la-http2.lo \
+ libcurl_la-headers.lo libcurl_la-hmac.lo libcurl_la-hostip.lo \
+ libcurl_la-hostip4.lo libcurl_la-hostip6.lo libcurl_la-hsts.lo \
+ libcurl_la-http.lo libcurl_la-http1.lo libcurl_la-http2.lo \
libcurl_la-http_aws_sigv4.lo libcurl_la-http_chunks.lo \
libcurl_la-http_digest.lo libcurl_la-http_negotiate.lo \
libcurl_la-http_ntlm.lo libcurl_la-http_proxy.lo \
- libcurl_la-idn.lo libcurl_la-if2ip.lo libcurl_la-imap.lo \
- libcurl_la-inet_ntop.lo libcurl_la-inet_pton.lo \
- libcurl_la-krb5.lo libcurl_la-ldap.lo libcurl_la-llist.lo \
- libcurl_la-macos.lo libcurl_la-md4.lo libcurl_la-md5.lo \
- libcurl_la-memdebug.lo libcurl_la-mime.lo \
- libcurl_la-mprintf.lo libcurl_la-mqtt.lo libcurl_la-multi.lo \
- libcurl_la-netrc.lo libcurl_la-nonblock.lo \
+ libcurl_la-httpsrr.lo libcurl_la-idn.lo libcurl_la-if2ip.lo \
+ libcurl_la-imap.lo libcurl_la-inet_ntop.lo libcurl_la-krb5.lo \
+ libcurl_la-ldap.lo libcurl_la-llist.lo libcurl_la-macos.lo \
+ libcurl_la-md4.lo libcurl_la-md5.lo libcurl_la-memdebug.lo \
+ libcurl_la-mime.lo libcurl_la-mprintf.lo libcurl_la-mqtt.lo \
+ libcurl_la-multi.lo libcurl_la-multi_ev.lo libcurl_la-netrc.lo \
libcurl_la-noproxy.lo libcurl_la-openldap.lo \
libcurl_la-parsedate.lo libcurl_la-pingpong.lo \
libcurl_la-pop3.lo libcurl_la-progress.lo libcurl_la-psl.lo \
@@ -333,12 +346,12 @@ am__objects_1 = libcurl_la-altsvc.lo libcurl_la-amigaos.lo \
libcurl_la-socks_gssapi.lo libcurl_la-socks_sspi.lo \
libcurl_la-speedcheck.lo libcurl_la-splay.lo \
libcurl_la-strcase.lo libcurl_la-strdup.lo \
- libcurl_la-strerror.lo libcurl_la-strtok.lo \
- libcurl_la-strtoofft.lo libcurl_la-system_win32.lo \
- libcurl_la-telnet.lo libcurl_la-tftp.lo libcurl_la-timediff.lo \
- libcurl_la-timeval.lo libcurl_la-transfer.lo libcurl_la-url.lo \
- libcurl_la-urlapi.lo libcurl_la-version.lo \
- libcurl_la-version_win32.lo libcurl_la-warnless.lo \
+ libcurl_la-strequal.lo libcurl_la-strerror.lo \
+ libcurl_la-system_win32.lo libcurl_la-telnet.lo \
+ libcurl_la-tftp.lo libcurl_la-transfer.lo \
+ libcurl_la-uint-bset.lo libcurl_la-uint-hash.lo \
+ libcurl_la-uint-spbset.lo libcurl_la-uint-table.lo \
+ libcurl_la-url.lo libcurl_la-urlapi.lo libcurl_la-version.lo \
libcurl_la-ws.lo
am__dirstamp = $(am__leading_dot)dirstamp
am__objects_2 = vauth/libcurl_la-cleartext.lo vauth/libcurl_la-cram.lo \
@@ -356,6 +369,7 @@ am__objects_3 = vtls/libcurl_la-bearssl.lo \
vtls/libcurl_la-openssl.lo vtls/libcurl_la-rustls.lo \
vtls/libcurl_la-schannel.lo vtls/libcurl_la-schannel_verify.lo \
vtls/libcurl_la-sectransp.lo vtls/libcurl_la-vtls.lo \
+ vtls/libcurl_la-vtls_scache.lo vtls/libcurl_la-vtls_spack.lo \
vtls/libcurl_la-wolfssl.lo vtls/libcurl_la-x509asn1.lo
am__objects_4 = vquic/libcurl_la-curl_msh3.lo \
vquic/libcurl_la-curl_ngtcp2.lo vquic/libcurl_la-curl_osslq.lo \
@@ -363,23 +377,30 @@ am__objects_4 = vquic/libcurl_la-curl_msh3.lo \
vquic/libcurl_la-vquic-tls.lo
am__objects_5 = vssh/libcurl_la-libssh.lo vssh/libcurl_la-libssh2.lo \
vssh/libcurl_la-curl_path.lo vssh/libcurl_la-wolfssh.lo
-am__objects_6 = $(am__objects_1) $(am__objects_2) $(am__objects_3) \
- $(am__objects_4) $(am__objects_5)
-am__objects_7 =
-am__objects_8 = $(am__objects_7) $(am__objects_7) $(am__objects_7) \
- $(am__objects_7) $(am__objects_7)
-am__objects_9 = libcurl.lo
-@HAVE_WINDRES_TRUE@@USE_CPPFLAG_CURL_STATICLIB_FALSE@am__objects_10 = $(am__objects_9)
-@DEBUGBUILD_TRUE@@USE_UNITY_TRUE@am__objects_11 = \
-@DEBUGBUILD_TRUE@@USE_UNITY_TRUE@ libcurl_la-memdebug.lo \
-@DEBUGBUILD_TRUE@@USE_UNITY_TRUE@ libcurl_la-curl_multibyte.lo
-@USE_UNITY_TRUE@am__objects_12 = libcurl_la-curl_threads.lo \
-@USE_UNITY_TRUE@ libcurl_la-timediff.lo libcurl_la-warnless.lo \
-@USE_UNITY_TRUE@ $(am__objects_11)
-@USE_UNITY_FALSE@am_libcurl_la_OBJECTS = $(am__objects_6) \
-@USE_UNITY_FALSE@ $(am__objects_8) $(am__objects_10)
-@USE_UNITY_TRUE@am_libcurl_la_OBJECTS = $(am__objects_12) \
-@USE_UNITY_TRUE@ $(am__objects_10)
+am__objects_6 = curlx/libcurl_la-base64.lo curlx/libcurl_la-dynbuf.lo \
+ curlx/libcurl_la-inet_pton.lo curlx/libcurl_la-multibyte.lo \
+ curlx/libcurl_la-nonblock.lo curlx/libcurl_la-strparse.lo \
+ curlx/libcurl_la-timediff.lo curlx/libcurl_la-timeval.lo \
+ curlx/libcurl_la-version_win32.lo curlx/libcurl_la-warnless.lo \
+ curlx/libcurl_la-winapi.lo
+@DOING_NATIVE_WINDOWS_TRUE@am__objects_7 = libcurl_la-dllmain.lo
+am__objects_8 = $(am__objects_1) $(am__objects_2) $(am__objects_3) \
+ $(am__objects_4) $(am__objects_5) $(am__objects_6) \
+ $(am__objects_7)
+am__objects_9 =
+am__objects_10 = $(am__objects_9) $(am__objects_9) $(am__objects_9) \
+ $(am__objects_9) $(am__objects_9) $(am__objects_9)
+am__objects_11 = libcurl.lo
+@HAVE_WINDRES_TRUE@@USE_CPPFLAG_CURL_STATICLIB_FALSE@am__objects_12 = $(am__objects_11)
+@CURLDEBUG_TRUE@@USE_UNITY_TRUE@am__objects_13 = \
+@CURLDEBUG_TRUE@@USE_UNITY_TRUE@ libcurl_la-memdebug.lo
+@USE_UNITY_TRUE@am__objects_14 = libcurl_la-curl_threads.lo \
+@USE_UNITY_TRUE@ curlx/libcurl_la-timediff.lo \
+@USE_UNITY_TRUE@ curlx/libcurl_la-warnless.lo $(am__objects_13)
+@USE_UNITY_FALSE@am_libcurl_la_OBJECTS = $(am__objects_8) \
+@USE_UNITY_FALSE@ $(am__objects_10) $(am__objects_12)
+@USE_UNITY_TRUE@am_libcurl_la_OBJECTS = $(am__objects_14) \
+@USE_UNITY_TRUE@ $(am__objects_12)
@USE_UNITY_TRUE@nodist_libcurl_la_OBJECTS = \
@USE_UNITY_TRUE@ libcurl_la-libcurl_unity.lo
libcurl_la_OBJECTS = $(am_libcurl_la_OBJECTS) \
@@ -393,30 +414,30 @@ libcurl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(CFLAGS) $(libcurl_la_LDFLAGS) $(LDFLAGS) -o $@
libcurlu_la_LIBADD =
am__libcurlu_la_SOURCES_DIST = altsvc.c amigaos.c asyn-ares.c \
- asyn-thread.c base64.c bufq.c bufref.c c-hyper.c cf-h1-proxy.c \
+ asyn-base.c asyn-thrdd.c bufq.c bufref.c cf-h1-proxy.c \
cf-h2-proxy.c cf-haproxy.c cf-https-connect.c cf-socket.c \
cfilters.c conncache.c connect.c content_encoding.c cookie.c \
- curl_addrinfo.c curl_des.c curl_endian.c curl_fnmatch.c \
- curl_get_line.c curl_gethostname.c curl_gssapi.c \
- curl_memrchr.c curl_multibyte.c curl_ntlm_core.c curl_range.c \
+ cshutdn.c curl_addrinfo.c curl_des.c curl_endian.c \
+ curl_fnmatch.c curl_get_line.c curl_gethostname.c \
+ curl_gssapi.c curl_memrchr.c curl_ntlm_core.c curl_range.c \
curl_rtmp.c curl_sasl.c curl_sha512_256.c curl_sspi.c \
- curl_threads.c curl_trc.c cw-out.c dict.c dllmain.c doh.c \
- dynbuf.c dynhds.c easy.c easygetopt.c easyoptions.c escape.c \
- file.c fileinfo.c fopen.c formdata.c ftp.c ftplistparser.c \
- getenv.c getinfo.c gopher.c hash.c headers.c hmac.c hostasyn.c \
- hostip.c hostip4.c hostip6.c hostsyn.c hsts.c http.c http1.c \
+ curl_threads.c curl_trc.c cw-out.c cw-pause.c dict.c doh.c \
+ dynhds.c easy.c easygetopt.c easyoptions.c escape.c \
+ fake_addrinfo.c file.c fileinfo.c fopen.c formdata.c ftp.c \
+ ftplistparser.c getenv.c getinfo.c gopher.c hash.c headers.c \
+ hmac.c hostip.c hostip4.c hostip6.c hsts.c http.c http1.c \
http2.c http_aws_sigv4.c http_chunks.c http_digest.c \
- http_negotiate.c http_ntlm.c http_proxy.c idn.c if2ip.c imap.c \
- inet_ntop.c inet_pton.c krb5.c ldap.c llist.c macos.c md4.c \
- md5.c memdebug.c mime.c mprintf.c mqtt.c multi.c netrc.c \
- nonblock.c noproxy.c openldap.c parsedate.c pingpong.c pop3.c \
+ http_negotiate.c http_ntlm.c http_proxy.c httpsrr.c idn.c \
+ if2ip.c imap.c inet_ntop.c krb5.c ldap.c llist.c macos.c md4.c \
+ md5.c memdebug.c mime.c mprintf.c mqtt.c multi.c multi_ev.c \
+ netrc.c noproxy.c openldap.c parsedate.c pingpong.c pop3.c \
progress.c psl.c rand.c rename.c request.c rtsp.c select.c \
sendf.c setopt.c sha256.c share.c slist.c smb.c smtp.c \
socketpair.c socks.c socks_gssapi.c socks_sspi.c speedcheck.c \
- splay.c strcase.c strdup.c strerror.c strtok.c strtoofft.c \
- system_win32.c telnet.c tftp.c timediff.c timeval.c transfer.c \
- url.c urlapi.c version.c version_win32.c warnless.c ws.c \
- vauth/cleartext.c vauth/cram.c vauth/digest.c \
+ splay.c strcase.c strdup.c strequal.c strerror.c \
+ system_win32.c telnet.c tftp.c transfer.c uint-bset.c \
+ uint-hash.c uint-spbset.c uint-table.c url.c urlapi.c \
+ version.c ws.c vauth/cleartext.c vauth/cram.c vauth/digest.c \
vauth/digest_sspi.c vauth/gsasl.c vauth/krb5_gssapi.c \
vauth/krb5_sspi.c vauth/ntlm.c vauth/ntlm_sspi.c \
vauth/oauth2.c vauth/spnego_gssapi.c vauth/spnego_sspi.c \
@@ -424,86 +445,94 @@ am__libcurlu_la_SOURCES_DIST = altsvc.c amigaos.c asyn-ares.c \
vtls/hostcheck.c vtls/keylog.c vtls/mbedtls.c \
vtls/mbedtls_threadlock.c vtls/openssl.c vtls/rustls.c \
vtls/schannel.c vtls/schannel_verify.c vtls/sectransp.c \
- vtls/vtls.c vtls/wolfssl.c vtls/x509asn1.c vquic/curl_msh3.c \
+ vtls/vtls.c vtls/vtls_scache.c vtls/vtls_spack.c \
+ vtls/wolfssl.c vtls/x509asn1.c vquic/curl_msh3.c \
vquic/curl_ngtcp2.c vquic/curl_osslq.c vquic/curl_quiche.c \
vquic/vquic.c vquic/vquic-tls.c vssh/libssh.c vssh/libssh2.c \
- vssh/curl_path.c vssh/wolfssh.c altsvc.h amigaos.h \
- arpa_telnet.h asyn.h bufq.h bufref.h c-hyper.h cf-h1-proxy.h \
- cf-h2-proxy.h cf-haproxy.h cf-https-connect.h cf-socket.h \
- cfilters.h conncache.h connect.h content_encoding.h cookie.h \
- curl_addrinfo.h curl_base64.h curl_ctype.h curl_des.h \
- curl_endian.h curl_fnmatch.h curl_get_line.h \
- curl_gethostname.h curl_gssapi.h curl_hmac.h curl_krb5.h \
- curl_ldap.h curl_md4.h curl_md5.h curl_memory.h curl_memrchr.h \
- curl_multibyte.h curl_ntlm_core.h curl_printf.h curl_range.h \
- curl_rtmp.h curl_sasl.h curl_setup.h curl_setup_once.h \
- curl_sha256.h curl_sha512_256.h curl_sspi.h curl_threads.h \
- curl_trc.h curlx.h cw-out.h dict.h doh.h dynbuf.h dynhds.h \
- easy_lock.h easyif.h easyoptions.h escape.h file.h fileinfo.h \
- fopen.h formdata.h ftp.h ftplistparser.h functypes.h getinfo.h \
- gopher.h hash.h headers.h hostip.h hsts.h http.h http1.h \
- http2.h http_aws_sigv4.h http_chunks.h http_digest.h \
- http_negotiate.h http_ntlm.h http_proxy.h idn.h if2ip.h imap.h \
- inet_ntop.h inet_pton.h llist.h macos.h memdebug.h mime.h \
- mqtt.h multihandle.h multiif.h netrc.h nonblock.h noproxy.h \
+ vssh/curl_path.c vssh/wolfssh.c curlx/base64.c curlx/dynbuf.c \
+ curlx/inet_pton.c curlx/multibyte.c curlx/nonblock.c \
+ curlx/strparse.c curlx/timediff.c curlx/timeval.c \
+ curlx/version_win32.c curlx/warnless.c curlx/winapi.c \
+ dllmain.c altsvc.h amigaos.h arpa_telnet.h asyn.h bufq.h \
+ bufref.h cf-h1-proxy.h cf-h2-proxy.h cf-haproxy.h \
+ cf-https-connect.h cf-socket.h cfilters.h conncache.h \
+ cshutdn.h connect.h content_encoding.h cookie.h \
+ curl_addrinfo.h curl_ctype.h curl_des.h curl_endian.h \
+ curl_fnmatch.h curl_get_line.h curl_gethostname.h \
+ curl_gssapi.h curl_hmac.h curl_krb5.h curl_ldap.h curl_md4.h \
+ curl_md5.h curl_memory.h curl_memrchr.h curl_ntlm_core.h \
+ curl_printf.h curl_range.h curl_rtmp.h curl_sasl.h \
+ curl_setup.h curl_setup_once.h curl_sha256.h curl_sha512_256.h \
+ curl_sspi.h curl_threads.h curl_trc.h cw-out.h cw-pause.h \
+ dict.h doh.h dynhds.h easy_lock.h easyif.h easyoptions.h \
+ escape.h fake_addrinfo.h file.h fileinfo.h fopen.h formdata.h \
+ ftp.h ftplistparser.h functypes.h getinfo.h gopher.h hash.h \
+ headers.h hostip.h hsts.h http.h http1.h http2.h \
+ http_aws_sigv4.h http_chunks.h http_digest.h http_negotiate.h \
+ http_ntlm.h http_proxy.h httpsrr.h idn.h if2ip.h imap.h \
+ inet_ntop.h llist.h macos.h memdebug.h mime.h mqtt.h \
+ multihandle.h multi_ev.h multiif.h netrc.h noproxy.h \
parsedate.h pingpong.h pop3.h progress.h psl.h rand.h rename.h \
- request.h rtsp.h select.h sendf.h setopt.h setup-vms.h share.h \
- sigpipe.h slist.h smb.h smtp.h sockaddr.h socketpair.h socks.h \
- speedcheck.h splay.h strcase.h strdup.h strerror.h strtok.h \
- strtoofft.h system_win32.h telnet.h tftp.h timediff.h \
- timeval.h transfer.h url.h urlapi-int.h urldata.h \
- version_win32.h warnless.h ws.h vauth/digest.h vauth/ntlm.h \
- vauth/vauth.h vtls/bearssl.h vtls/cipher_suite.h vtls/gtls.h \
+ request.h rtsp.h select.h sendf.h setopt.h setup-os400.h \
+ setup-vms.h setup-win32.h share.h sigpipe.h slist.h smb.h \
+ smtp.h sockaddr.h socketpair.h socks.h speedcheck.h splay.h \
+ strcase.h strdup.h strerror.h system_win32.h telnet.h tftp.h \
+ transfer.h uint-bset.h uint-hash.h uint-spbset.h uint-table.h \
+ url.h urlapi-int.h urldata.h ws.h vauth/digest.h vauth/vauth.h \
+ vtls/bearssl.h vtls/cipher_suite.h vtls/gtls.h \
vtls/hostcheck.h vtls/keylog.h vtls/mbedtls.h \
vtls/mbedtls_threadlock.h vtls/openssl.h vtls/rustls.h \
vtls/schannel.h vtls/schannel_int.h vtls/sectransp.h \
- vtls/vtls.h vtls/vtls_int.h vtls/wolfssl.h vtls/x509asn1.h \
+ vtls/vtls.h vtls/vtls_int.h vtls/vtls_scache.h \
+ vtls/vtls_spack.h vtls/wolfssl.h vtls/x509asn1.h \
vquic/curl_msh3.h vquic/curl_ngtcp2.h vquic/curl_osslq.h \
vquic/curl_quiche.h vquic/vquic.h vquic/vquic_int.h \
- vquic/vquic-tls.h vssh/curl_path.h vssh/ssh.h
-am__objects_13 = libcurlu_la-altsvc.lo libcurlu_la-amigaos.lo \
- libcurlu_la-asyn-ares.lo libcurlu_la-asyn-thread.lo \
- libcurlu_la-base64.lo libcurlu_la-bufq.lo \
- libcurlu_la-bufref.lo libcurlu_la-c-hyper.lo \
- libcurlu_la-cf-h1-proxy.lo libcurlu_la-cf-h2-proxy.lo \
- libcurlu_la-cf-haproxy.lo libcurlu_la-cf-https-connect.lo \
- libcurlu_la-cf-socket.lo libcurlu_la-cfilters.lo \
- libcurlu_la-conncache.lo libcurlu_la-connect.lo \
- libcurlu_la-content_encoding.lo libcurlu_la-cookie.lo \
+ vquic/vquic-tls.h vssh/curl_path.h vssh/ssh.h curlx/base64.h \
+ curlx/curlx.h curlx/dynbuf.h curlx/inet_pton.h \
+ curlx/multibyte.h curlx/nonblock.h curlx/strparse.h \
+ curlx/timediff.h curlx/timeval.h curlx/version_win32.h \
+ curlx/warnless.h curlx/winapi.h
+am__objects_15 = libcurlu_la-altsvc.lo libcurlu_la-amigaos.lo \
+ libcurlu_la-asyn-ares.lo libcurlu_la-asyn-base.lo \
+ libcurlu_la-asyn-thrdd.lo libcurlu_la-bufq.lo \
+ libcurlu_la-bufref.lo libcurlu_la-cf-h1-proxy.lo \
+ libcurlu_la-cf-h2-proxy.lo libcurlu_la-cf-haproxy.lo \
+ libcurlu_la-cf-https-connect.lo libcurlu_la-cf-socket.lo \
+ libcurlu_la-cfilters.lo libcurlu_la-conncache.lo \
+ libcurlu_la-connect.lo libcurlu_la-content_encoding.lo \
+ libcurlu_la-cookie.lo libcurlu_la-cshutdn.lo \
libcurlu_la-curl_addrinfo.lo libcurlu_la-curl_des.lo \
libcurlu_la-curl_endian.lo libcurlu_la-curl_fnmatch.lo \
libcurlu_la-curl_get_line.lo libcurlu_la-curl_gethostname.lo \
libcurlu_la-curl_gssapi.lo libcurlu_la-curl_memrchr.lo \
- libcurlu_la-curl_multibyte.lo libcurlu_la-curl_ntlm_core.lo \
- libcurlu_la-curl_range.lo libcurlu_la-curl_rtmp.lo \
- libcurlu_la-curl_sasl.lo libcurlu_la-curl_sha512_256.lo \
- libcurlu_la-curl_sspi.lo libcurlu_la-curl_threads.lo \
- libcurlu_la-curl_trc.lo libcurlu_la-cw-out.lo \
- libcurlu_la-dict.lo libcurlu_la-dllmain.lo libcurlu_la-doh.lo \
- libcurlu_la-dynbuf.lo libcurlu_la-dynhds.lo \
+ libcurlu_la-curl_ntlm_core.lo libcurlu_la-curl_range.lo \
+ libcurlu_la-curl_rtmp.lo libcurlu_la-curl_sasl.lo \
+ libcurlu_la-curl_sha512_256.lo libcurlu_la-curl_sspi.lo \
+ libcurlu_la-curl_threads.lo libcurlu_la-curl_trc.lo \
+ libcurlu_la-cw-out.lo libcurlu_la-cw-pause.lo \
+ libcurlu_la-dict.lo libcurlu_la-doh.lo libcurlu_la-dynhds.lo \
libcurlu_la-easy.lo libcurlu_la-easygetopt.lo \
libcurlu_la-easyoptions.lo libcurlu_la-escape.lo \
- libcurlu_la-file.lo libcurlu_la-fileinfo.lo \
- libcurlu_la-fopen.lo libcurlu_la-formdata.lo \
- libcurlu_la-ftp.lo libcurlu_la-ftplistparser.lo \
- libcurlu_la-getenv.lo libcurlu_la-getinfo.lo \
- libcurlu_la-gopher.lo libcurlu_la-hash.lo \
- libcurlu_la-headers.lo libcurlu_la-hmac.lo \
- libcurlu_la-hostasyn.lo libcurlu_la-hostip.lo \
- libcurlu_la-hostip4.lo libcurlu_la-hostip6.lo \
- libcurlu_la-hostsyn.lo libcurlu_la-hsts.lo libcurlu_la-http.lo \
+ libcurlu_la-fake_addrinfo.lo libcurlu_la-file.lo \
+ libcurlu_la-fileinfo.lo libcurlu_la-fopen.lo \
+ libcurlu_la-formdata.lo libcurlu_la-ftp.lo \
+ libcurlu_la-ftplistparser.lo libcurlu_la-getenv.lo \
+ libcurlu_la-getinfo.lo libcurlu_la-gopher.lo \
+ libcurlu_la-hash.lo libcurlu_la-headers.lo libcurlu_la-hmac.lo \
+ libcurlu_la-hostip.lo libcurlu_la-hostip4.lo \
+ libcurlu_la-hostip6.lo libcurlu_la-hsts.lo libcurlu_la-http.lo \
libcurlu_la-http1.lo libcurlu_la-http2.lo \
libcurlu_la-http_aws_sigv4.lo libcurlu_la-http_chunks.lo \
libcurlu_la-http_digest.lo libcurlu_la-http_negotiate.lo \
libcurlu_la-http_ntlm.lo libcurlu_la-http_proxy.lo \
- libcurlu_la-idn.lo libcurlu_la-if2ip.lo libcurlu_la-imap.lo \
- libcurlu_la-inet_ntop.lo libcurlu_la-inet_pton.lo \
+ libcurlu_la-httpsrr.lo libcurlu_la-idn.lo libcurlu_la-if2ip.lo \
+ libcurlu_la-imap.lo libcurlu_la-inet_ntop.lo \
libcurlu_la-krb5.lo libcurlu_la-ldap.lo libcurlu_la-llist.lo \
libcurlu_la-macos.lo libcurlu_la-md4.lo libcurlu_la-md5.lo \
libcurlu_la-memdebug.lo libcurlu_la-mime.lo \
libcurlu_la-mprintf.lo libcurlu_la-mqtt.lo \
- libcurlu_la-multi.lo libcurlu_la-netrc.lo \
- libcurlu_la-nonblock.lo libcurlu_la-noproxy.lo \
+ libcurlu_la-multi.lo libcurlu_la-multi_ev.lo \
+ libcurlu_la-netrc.lo libcurlu_la-noproxy.lo \
libcurlu_la-openldap.lo libcurlu_la-parsedate.lo \
libcurlu_la-pingpong.lo libcurlu_la-pop3.lo \
libcurlu_la-progress.lo libcurlu_la-psl.lo libcurlu_la-rand.lo \
@@ -515,15 +544,14 @@ am__objects_13 = libcurlu_la-altsvc.lo libcurlu_la-amigaos.lo \
libcurlu_la-socks.lo libcurlu_la-socks_gssapi.lo \
libcurlu_la-socks_sspi.lo libcurlu_la-speedcheck.lo \
libcurlu_la-splay.lo libcurlu_la-strcase.lo \
- libcurlu_la-strdup.lo libcurlu_la-strerror.lo \
- libcurlu_la-strtok.lo libcurlu_la-strtoofft.lo \
- libcurlu_la-system_win32.lo libcurlu_la-telnet.lo \
- libcurlu_la-tftp.lo libcurlu_la-timediff.lo \
- libcurlu_la-timeval.lo libcurlu_la-transfer.lo \
- libcurlu_la-url.lo libcurlu_la-urlapi.lo \
- libcurlu_la-version.lo libcurlu_la-version_win32.lo \
- libcurlu_la-warnless.lo libcurlu_la-ws.lo
-am__objects_14 = vauth/libcurlu_la-cleartext.lo \
+ libcurlu_la-strdup.lo libcurlu_la-strequal.lo \
+ libcurlu_la-strerror.lo libcurlu_la-system_win32.lo \
+ libcurlu_la-telnet.lo libcurlu_la-tftp.lo \
+ libcurlu_la-transfer.lo libcurlu_la-uint-bset.lo \
+ libcurlu_la-uint-hash.lo libcurlu_la-uint-spbset.lo \
+ libcurlu_la-uint-table.lo libcurlu_la-url.lo \
+ libcurlu_la-urlapi.lo libcurlu_la-version.lo libcurlu_la-ws.lo
+am__objects_16 = vauth/libcurlu_la-cleartext.lo \
vauth/libcurlu_la-cram.lo vauth/libcurlu_la-digest.lo \
vauth/libcurlu_la-digest_sspi.lo vauth/libcurlu_la-gsasl.lo \
vauth/libcurlu_la-krb5_gssapi.lo \
@@ -531,7 +559,7 @@ am__objects_14 = vauth/libcurlu_la-cleartext.lo \
vauth/libcurlu_la-ntlm_sspi.lo vauth/libcurlu_la-oauth2.lo \
vauth/libcurlu_la-spnego_gssapi.lo \
vauth/libcurlu_la-spnego_sspi.lo vauth/libcurlu_la-vauth.lo
-am__objects_15 = vtls/libcurlu_la-bearssl.lo \
+am__objects_17 = vtls/libcurlu_la-bearssl.lo \
vtls/libcurlu_la-cipher_suite.lo vtls/libcurlu_la-gtls.lo \
vtls/libcurlu_la-hostcheck.lo vtls/libcurlu_la-keylog.lo \
vtls/libcurlu_la-mbedtls.lo \
@@ -540,26 +568,36 @@ am__objects_15 = vtls/libcurlu_la-bearssl.lo \
vtls/libcurlu_la-schannel.lo \
vtls/libcurlu_la-schannel_verify.lo \
vtls/libcurlu_la-sectransp.lo vtls/libcurlu_la-vtls.lo \
+ vtls/libcurlu_la-vtls_scache.lo vtls/libcurlu_la-vtls_spack.lo \
vtls/libcurlu_la-wolfssl.lo vtls/libcurlu_la-x509asn1.lo
-am__objects_16 = vquic/libcurlu_la-curl_msh3.lo \
+am__objects_18 = vquic/libcurlu_la-curl_msh3.lo \
vquic/libcurlu_la-curl_ngtcp2.lo \
vquic/libcurlu_la-curl_osslq.lo \
vquic/libcurlu_la-curl_quiche.lo vquic/libcurlu_la-vquic.lo \
vquic/libcurlu_la-vquic-tls.lo
-am__objects_17 = vssh/libcurlu_la-libssh.lo \
+am__objects_19 = vssh/libcurlu_la-libssh.lo \
vssh/libcurlu_la-libssh2.lo vssh/libcurlu_la-curl_path.lo \
vssh/libcurlu_la-wolfssh.lo
-am__objects_18 = $(am__objects_13) $(am__objects_14) $(am__objects_15) \
- $(am__objects_16) $(am__objects_17)
-@DEBUGBUILD_TRUE@@USE_UNITY_TRUE@am__objects_19 = \
-@DEBUGBUILD_TRUE@@USE_UNITY_TRUE@ libcurlu_la-memdebug.lo \
-@DEBUGBUILD_TRUE@@USE_UNITY_TRUE@ libcurlu_la-curl_multibyte.lo
-@USE_UNITY_TRUE@am__objects_20 = libcurlu_la-curl_threads.lo \
-@USE_UNITY_TRUE@ libcurlu_la-timediff.lo \
-@USE_UNITY_TRUE@ libcurlu_la-warnless.lo $(am__objects_19)
-@USE_UNITY_FALSE@am_libcurlu_la_OBJECTS = $(am__objects_18) \
-@USE_UNITY_FALSE@ $(am__objects_8)
-@USE_UNITY_TRUE@am_libcurlu_la_OBJECTS = $(am__objects_20)
+am__objects_20 = curlx/libcurlu_la-base64.lo \
+ curlx/libcurlu_la-dynbuf.lo curlx/libcurlu_la-inet_pton.lo \
+ curlx/libcurlu_la-multibyte.lo curlx/libcurlu_la-nonblock.lo \
+ curlx/libcurlu_la-strparse.lo curlx/libcurlu_la-timediff.lo \
+ curlx/libcurlu_la-timeval.lo \
+ curlx/libcurlu_la-version_win32.lo \
+ curlx/libcurlu_la-warnless.lo curlx/libcurlu_la-winapi.lo
+@DOING_NATIVE_WINDOWS_TRUE@am__objects_21 = libcurlu_la-dllmain.lo
+am__objects_22 = $(am__objects_15) $(am__objects_16) $(am__objects_17) \
+ $(am__objects_18) $(am__objects_19) $(am__objects_20) \
+ $(am__objects_21)
+@CURLDEBUG_TRUE@@USE_UNITY_TRUE@am__objects_23 = \
+@CURLDEBUG_TRUE@@USE_UNITY_TRUE@ libcurlu_la-memdebug.lo
+@USE_UNITY_TRUE@am__objects_24 = libcurlu_la-curl_threads.lo \
+@USE_UNITY_TRUE@ curlx/libcurlu_la-timediff.lo \
+@USE_UNITY_TRUE@ curlx/libcurlu_la-warnless.lo \
+@USE_UNITY_TRUE@ $(am__objects_23)
+@USE_UNITY_FALSE@am_libcurlu_la_OBJECTS = $(am__objects_22) \
+@USE_UNITY_FALSE@ $(am__objects_10)
+@USE_UNITY_TRUE@am_libcurlu_la_OBJECTS = $(am__objects_24)
@USE_UNITY_TRUE@nodist_libcurlu_la_OBJECTS = \
@USE_UNITY_TRUE@ libcurlu_la-libcurl_unity.lo
libcurlu_la_OBJECTS = $(am_libcurlu_la_OBJECTS) \
@@ -586,11 +624,10 @@ am__maybe_remake_depfiles = depfiles
am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
./$(DEPDIR)/libcurl_la-amigaos.Plo \
./$(DEPDIR)/libcurl_la-asyn-ares.Plo \
- ./$(DEPDIR)/libcurl_la-asyn-thread.Plo \
- ./$(DEPDIR)/libcurl_la-base64.Plo \
+ ./$(DEPDIR)/libcurl_la-asyn-base.Plo \
+ ./$(DEPDIR)/libcurl_la-asyn-thrdd.Plo \
./$(DEPDIR)/libcurl_la-bufq.Plo \
./$(DEPDIR)/libcurl_la-bufref.Plo \
- ./$(DEPDIR)/libcurl_la-c-hyper.Plo \
./$(DEPDIR)/libcurl_la-cf-h1-proxy.Plo \
./$(DEPDIR)/libcurl_la-cf-h2-proxy.Plo \
./$(DEPDIR)/libcurl_la-cf-haproxy.Plo \
@@ -601,6 +638,7 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
./$(DEPDIR)/libcurl_la-connect.Plo \
./$(DEPDIR)/libcurl_la-content_encoding.Plo \
./$(DEPDIR)/libcurl_la-cookie.Plo \
+ ./$(DEPDIR)/libcurl_la-cshutdn.Plo \
./$(DEPDIR)/libcurl_la-curl_addrinfo.Plo \
./$(DEPDIR)/libcurl_la-curl_des.Plo \
./$(DEPDIR)/libcurl_la-curl_endian.Plo \
@@ -609,7 +647,6 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
./$(DEPDIR)/libcurl_la-curl_gethostname.Plo \
./$(DEPDIR)/libcurl_la-curl_gssapi.Plo \
./$(DEPDIR)/libcurl_la-curl_memrchr.Plo \
- ./$(DEPDIR)/libcurl_la-curl_multibyte.Plo \
./$(DEPDIR)/libcurl_la-curl_ntlm_core.Plo \
./$(DEPDIR)/libcurl_la-curl_range.Plo \
./$(DEPDIR)/libcurl_la-curl_rtmp.Plo \
@@ -619,15 +656,16 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
./$(DEPDIR)/libcurl_la-curl_threads.Plo \
./$(DEPDIR)/libcurl_la-curl_trc.Plo \
./$(DEPDIR)/libcurl_la-cw-out.Plo \
+ ./$(DEPDIR)/libcurl_la-cw-pause.Plo \
./$(DEPDIR)/libcurl_la-dict.Plo \
./$(DEPDIR)/libcurl_la-dllmain.Plo \
./$(DEPDIR)/libcurl_la-doh.Plo \
- ./$(DEPDIR)/libcurl_la-dynbuf.Plo \
./$(DEPDIR)/libcurl_la-dynhds.Plo \
./$(DEPDIR)/libcurl_la-easy.Plo \
./$(DEPDIR)/libcurl_la-easygetopt.Plo \
./$(DEPDIR)/libcurl_la-easyoptions.Plo \
./$(DEPDIR)/libcurl_la-escape.Plo \
+ ./$(DEPDIR)/libcurl_la-fake_addrinfo.Plo \
./$(DEPDIR)/libcurl_la-file.Plo \
./$(DEPDIR)/libcurl_la-fileinfo.Plo \
./$(DEPDIR)/libcurl_la-fopen.Plo \
@@ -640,11 +678,9 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
./$(DEPDIR)/libcurl_la-hash.Plo \
./$(DEPDIR)/libcurl_la-headers.Plo \
./$(DEPDIR)/libcurl_la-hmac.Plo \
- ./$(DEPDIR)/libcurl_la-hostasyn.Plo \
./$(DEPDIR)/libcurl_la-hostip.Plo \
./$(DEPDIR)/libcurl_la-hostip4.Plo \
./$(DEPDIR)/libcurl_la-hostip6.Plo \
- ./$(DEPDIR)/libcurl_la-hostsyn.Plo \
./$(DEPDIR)/libcurl_la-hsts.Plo \
./$(DEPDIR)/libcurl_la-http.Plo \
./$(DEPDIR)/libcurl_la-http1.Plo \
@@ -655,11 +691,11 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
./$(DEPDIR)/libcurl_la-http_negotiate.Plo \
./$(DEPDIR)/libcurl_la-http_ntlm.Plo \
./$(DEPDIR)/libcurl_la-http_proxy.Plo \
+ ./$(DEPDIR)/libcurl_la-httpsrr.Plo \
./$(DEPDIR)/libcurl_la-idn.Plo \
./$(DEPDIR)/libcurl_la-if2ip.Plo \
./$(DEPDIR)/libcurl_la-imap.Plo \
./$(DEPDIR)/libcurl_la-inet_ntop.Plo \
- ./$(DEPDIR)/libcurl_la-inet_pton.Plo \
./$(DEPDIR)/libcurl_la-krb5.Plo \
./$(DEPDIR)/libcurl_la-ldap.Plo \
./$(DEPDIR)/libcurl_la-libcurl_unity.Plo \
@@ -671,8 +707,8 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
./$(DEPDIR)/libcurl_la-mprintf.Plo \
./$(DEPDIR)/libcurl_la-mqtt.Plo \
./$(DEPDIR)/libcurl_la-multi.Plo \
+ ./$(DEPDIR)/libcurl_la-multi_ev.Plo \
./$(DEPDIR)/libcurl_la-netrc.Plo \
- ./$(DEPDIR)/libcurl_la-nonblock.Plo \
./$(DEPDIR)/libcurl_la-noproxy.Plo \
./$(DEPDIR)/libcurl_la-openldap.Plo \
./$(DEPDIR)/libcurl_la-parsedate.Plo \
@@ -698,29 +734,27 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
./$(DEPDIR)/libcurl_la-splay.Plo \
./$(DEPDIR)/libcurl_la-strcase.Plo \
./$(DEPDIR)/libcurl_la-strdup.Plo \
+ ./$(DEPDIR)/libcurl_la-strequal.Plo \
./$(DEPDIR)/libcurl_la-strerror.Plo \
- ./$(DEPDIR)/libcurl_la-strtok.Plo \
- ./$(DEPDIR)/libcurl_la-strtoofft.Plo \
./$(DEPDIR)/libcurl_la-system_win32.Plo \
./$(DEPDIR)/libcurl_la-telnet.Plo \
./$(DEPDIR)/libcurl_la-tftp.Plo \
- ./$(DEPDIR)/libcurl_la-timediff.Plo \
- ./$(DEPDIR)/libcurl_la-timeval.Plo \
./$(DEPDIR)/libcurl_la-transfer.Plo \
+ ./$(DEPDIR)/libcurl_la-uint-bset.Plo \
+ ./$(DEPDIR)/libcurl_la-uint-hash.Plo \
+ ./$(DEPDIR)/libcurl_la-uint-spbset.Plo \
+ ./$(DEPDIR)/libcurl_la-uint-table.Plo \
./$(DEPDIR)/libcurl_la-url.Plo \
./$(DEPDIR)/libcurl_la-urlapi.Plo \
./$(DEPDIR)/libcurl_la-version.Plo \
- ./$(DEPDIR)/libcurl_la-version_win32.Plo \
- ./$(DEPDIR)/libcurl_la-warnless.Plo \
./$(DEPDIR)/libcurl_la-ws.Plo \
./$(DEPDIR)/libcurlu_la-altsvc.Plo \
./$(DEPDIR)/libcurlu_la-amigaos.Plo \
./$(DEPDIR)/libcurlu_la-asyn-ares.Plo \
- ./$(DEPDIR)/libcurlu_la-asyn-thread.Plo \
- ./$(DEPDIR)/libcurlu_la-base64.Plo \
+ ./$(DEPDIR)/libcurlu_la-asyn-base.Plo \
+ ./$(DEPDIR)/libcurlu_la-asyn-thrdd.Plo \
./$(DEPDIR)/libcurlu_la-bufq.Plo \
./$(DEPDIR)/libcurlu_la-bufref.Plo \
- ./$(DEPDIR)/libcurlu_la-c-hyper.Plo \
./$(DEPDIR)/libcurlu_la-cf-h1-proxy.Plo \
./$(DEPDIR)/libcurlu_la-cf-h2-proxy.Plo \
./$(DEPDIR)/libcurlu_la-cf-haproxy.Plo \
@@ -731,6 +765,7 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
./$(DEPDIR)/libcurlu_la-connect.Plo \
./$(DEPDIR)/libcurlu_la-content_encoding.Plo \
./$(DEPDIR)/libcurlu_la-cookie.Plo \
+ ./$(DEPDIR)/libcurlu_la-cshutdn.Plo \
./$(DEPDIR)/libcurlu_la-curl_addrinfo.Plo \
./$(DEPDIR)/libcurlu_la-curl_des.Plo \
./$(DEPDIR)/libcurlu_la-curl_endian.Plo \
@@ -739,7 +774,6 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
./$(DEPDIR)/libcurlu_la-curl_gethostname.Plo \
./$(DEPDIR)/libcurlu_la-curl_gssapi.Plo \
./$(DEPDIR)/libcurlu_la-curl_memrchr.Plo \
- ./$(DEPDIR)/libcurlu_la-curl_multibyte.Plo \
./$(DEPDIR)/libcurlu_la-curl_ntlm_core.Plo \
./$(DEPDIR)/libcurlu_la-curl_range.Plo \
./$(DEPDIR)/libcurlu_la-curl_rtmp.Plo \
@@ -749,15 +783,16 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
./$(DEPDIR)/libcurlu_la-curl_threads.Plo \
./$(DEPDIR)/libcurlu_la-curl_trc.Plo \
./$(DEPDIR)/libcurlu_la-cw-out.Plo \
+ ./$(DEPDIR)/libcurlu_la-cw-pause.Plo \
./$(DEPDIR)/libcurlu_la-dict.Plo \
./$(DEPDIR)/libcurlu_la-dllmain.Plo \
./$(DEPDIR)/libcurlu_la-doh.Plo \
- ./$(DEPDIR)/libcurlu_la-dynbuf.Plo \
./$(DEPDIR)/libcurlu_la-dynhds.Plo \
./$(DEPDIR)/libcurlu_la-easy.Plo \
./$(DEPDIR)/libcurlu_la-easygetopt.Plo \
./$(DEPDIR)/libcurlu_la-easyoptions.Plo \
./$(DEPDIR)/libcurlu_la-escape.Plo \
+ ./$(DEPDIR)/libcurlu_la-fake_addrinfo.Plo \
./$(DEPDIR)/libcurlu_la-file.Plo \
./$(DEPDIR)/libcurlu_la-fileinfo.Plo \
./$(DEPDIR)/libcurlu_la-fopen.Plo \
@@ -770,11 +805,9 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
./$(DEPDIR)/libcurlu_la-hash.Plo \
./$(DEPDIR)/libcurlu_la-headers.Plo \
./$(DEPDIR)/libcurlu_la-hmac.Plo \
- ./$(DEPDIR)/libcurlu_la-hostasyn.Plo \
./$(DEPDIR)/libcurlu_la-hostip.Plo \
./$(DEPDIR)/libcurlu_la-hostip4.Plo \
./$(DEPDIR)/libcurlu_la-hostip6.Plo \
- ./$(DEPDIR)/libcurlu_la-hostsyn.Plo \
./$(DEPDIR)/libcurlu_la-hsts.Plo \
./$(DEPDIR)/libcurlu_la-http.Plo \
./$(DEPDIR)/libcurlu_la-http1.Plo \
@@ -785,11 +818,11 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
./$(DEPDIR)/libcurlu_la-http_negotiate.Plo \
./$(DEPDIR)/libcurlu_la-http_ntlm.Plo \
./$(DEPDIR)/libcurlu_la-http_proxy.Plo \
+ ./$(DEPDIR)/libcurlu_la-httpsrr.Plo \
./$(DEPDIR)/libcurlu_la-idn.Plo \
./$(DEPDIR)/libcurlu_la-if2ip.Plo \
./$(DEPDIR)/libcurlu_la-imap.Plo \
./$(DEPDIR)/libcurlu_la-inet_ntop.Plo \
- ./$(DEPDIR)/libcurlu_la-inet_pton.Plo \
./$(DEPDIR)/libcurlu_la-krb5.Plo \
./$(DEPDIR)/libcurlu_la-ldap.Plo \
./$(DEPDIR)/libcurlu_la-libcurl_unity.Plo \
@@ -802,8 +835,8 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
./$(DEPDIR)/libcurlu_la-mprintf.Plo \
./$(DEPDIR)/libcurlu_la-mqtt.Plo \
./$(DEPDIR)/libcurlu_la-multi.Plo \
+ ./$(DEPDIR)/libcurlu_la-multi_ev.Plo \
./$(DEPDIR)/libcurlu_la-netrc.Plo \
- ./$(DEPDIR)/libcurlu_la-nonblock.Plo \
./$(DEPDIR)/libcurlu_la-noproxy.Plo \
./$(DEPDIR)/libcurlu_la-openldap.Plo \
./$(DEPDIR)/libcurlu_la-parsedate.Plo \
@@ -831,21 +864,42 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
./$(DEPDIR)/libcurlu_la-splay.Plo \
./$(DEPDIR)/libcurlu_la-strcase.Plo \
./$(DEPDIR)/libcurlu_la-strdup.Plo \
+ ./$(DEPDIR)/libcurlu_la-strequal.Plo \
./$(DEPDIR)/libcurlu_la-strerror.Plo \
- ./$(DEPDIR)/libcurlu_la-strtok.Plo \
- ./$(DEPDIR)/libcurlu_la-strtoofft.Plo \
./$(DEPDIR)/libcurlu_la-system_win32.Plo \
./$(DEPDIR)/libcurlu_la-telnet.Plo \
./$(DEPDIR)/libcurlu_la-tftp.Plo \
- ./$(DEPDIR)/libcurlu_la-timediff.Plo \
- ./$(DEPDIR)/libcurlu_la-timeval.Plo \
./$(DEPDIR)/libcurlu_la-transfer.Plo \
+ ./$(DEPDIR)/libcurlu_la-uint-bset.Plo \
+ ./$(DEPDIR)/libcurlu_la-uint-hash.Plo \
+ ./$(DEPDIR)/libcurlu_la-uint-spbset.Plo \
+ ./$(DEPDIR)/libcurlu_la-uint-table.Plo \
./$(DEPDIR)/libcurlu_la-url.Plo \
./$(DEPDIR)/libcurlu_la-urlapi.Plo \
./$(DEPDIR)/libcurlu_la-version.Plo \
- ./$(DEPDIR)/libcurlu_la-version_win32.Plo \
- ./$(DEPDIR)/libcurlu_la-warnless.Plo \
./$(DEPDIR)/libcurlu_la-ws.Plo \
+ curlx/$(DEPDIR)/libcurl_la-base64.Plo \
+ curlx/$(DEPDIR)/libcurl_la-dynbuf.Plo \
+ curlx/$(DEPDIR)/libcurl_la-inet_pton.Plo \
+ curlx/$(DEPDIR)/libcurl_la-multibyte.Plo \
+ curlx/$(DEPDIR)/libcurl_la-nonblock.Plo \
+ curlx/$(DEPDIR)/libcurl_la-strparse.Plo \
+ curlx/$(DEPDIR)/libcurl_la-timediff.Plo \
+ curlx/$(DEPDIR)/libcurl_la-timeval.Plo \
+ curlx/$(DEPDIR)/libcurl_la-version_win32.Plo \
+ curlx/$(DEPDIR)/libcurl_la-warnless.Plo \
+ curlx/$(DEPDIR)/libcurl_la-winapi.Plo \
+ curlx/$(DEPDIR)/libcurlu_la-base64.Plo \
+ curlx/$(DEPDIR)/libcurlu_la-dynbuf.Plo \
+ curlx/$(DEPDIR)/libcurlu_la-inet_pton.Plo \
+ curlx/$(DEPDIR)/libcurlu_la-multibyte.Plo \
+ curlx/$(DEPDIR)/libcurlu_la-nonblock.Plo \
+ curlx/$(DEPDIR)/libcurlu_la-strparse.Plo \
+ curlx/$(DEPDIR)/libcurlu_la-timediff.Plo \
+ curlx/$(DEPDIR)/libcurlu_la-timeval.Plo \
+ curlx/$(DEPDIR)/libcurlu_la-version_win32.Plo \
+ curlx/$(DEPDIR)/libcurlu_la-warnless.Plo \
+ curlx/$(DEPDIR)/libcurlu_la-winapi.Plo \
vauth/$(DEPDIR)/libcurl_la-cleartext.Plo \
vauth/$(DEPDIR)/libcurl_la-cram.Plo \
vauth/$(DEPDIR)/libcurl_la-digest.Plo \
@@ -905,6 +959,8 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
vtls/$(DEPDIR)/libcurl_la-schannel_verify.Plo \
vtls/$(DEPDIR)/libcurl_la-sectransp.Plo \
vtls/$(DEPDIR)/libcurl_la-vtls.Plo \
+ vtls/$(DEPDIR)/libcurl_la-vtls_scache.Plo \
+ vtls/$(DEPDIR)/libcurl_la-vtls_spack.Plo \
vtls/$(DEPDIR)/libcurl_la-wolfssl.Plo \
vtls/$(DEPDIR)/libcurl_la-x509asn1.Plo \
vtls/$(DEPDIR)/libcurlu_la-bearssl.Plo \
@@ -920,6 +976,8 @@ am__depfiles_remade = ./$(DEPDIR)/libcurl_la-altsvc.Plo \
vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Plo \
vtls/$(DEPDIR)/libcurlu_la-sectransp.Plo \
vtls/$(DEPDIR)/libcurlu_la-vtls.Plo \
+ vtls/$(DEPDIR)/libcurlu_la-vtls_scache.Plo \
+ vtls/$(DEPDIR)/libcurlu_la-vtls_spack.Plo \
vtls/$(DEPDIR)/libcurlu_la-wolfssl.Plo \
vtls/$(DEPDIR)/libcurlu_la-x509asn1.Plo
am__mv = mv -f
@@ -975,7 +1033,6 @@ DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
-APACHECTL = @APACHECTL@
APXS = @APXS@
AR = @AR@
AR_FLAGS = @AR_FLAGS@
@@ -1002,31 +1059,10 @@ CURL_CA_BUNDLE = @CURL_CA_BUNDLE@
CURL_CA_EMBED = @CURL_CA_EMBED@
CURL_CFLAG_EXTRAS = @CURL_CFLAG_EXTRAS@
CURL_CPP = @CURL_CPP@
-CURL_DISABLE_DICT = @CURL_DISABLE_DICT@
-CURL_DISABLE_FILE = @CURL_DISABLE_FILE@
-CURL_DISABLE_FTP = @CURL_DISABLE_FTP@
-CURL_DISABLE_GOPHER = @CURL_DISABLE_GOPHER@
-CURL_DISABLE_HTTP = @CURL_DISABLE_HTTP@
-CURL_DISABLE_IMAP = @CURL_DISABLE_IMAP@
-CURL_DISABLE_IPFS = @CURL_DISABLE_IPFS@
-CURL_DISABLE_LDAP = @CURL_DISABLE_LDAP@
-CURL_DISABLE_LDAPS = @CURL_DISABLE_LDAPS@
-CURL_DISABLE_MQTT = @CURL_DISABLE_MQTT@
-CURL_DISABLE_POP3 = @CURL_DISABLE_POP3@
-CURL_DISABLE_PROXY = @CURL_DISABLE_PROXY@
-CURL_DISABLE_RTSP = @CURL_DISABLE_RTSP@
-CURL_DISABLE_SMB = @CURL_DISABLE_SMB@
-CURL_DISABLE_SMTP = @CURL_DISABLE_SMTP@
-CURL_DISABLE_TELNET = @CURL_DISABLE_TELNET@
-CURL_DISABLE_TFTP = @CURL_DISABLE_TFTP@
-CURL_DISABLE_WEBSOCKETS = @CURL_DISABLE_WEBSOCKETS@
CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX = @CURL_LIBCURL_VERSIONED_SYMBOLS_PREFIX@
CURL_LIBCURL_VERSIONED_SYMBOLS_SONAME = @CURL_LIBCURL_VERSIONED_SYMBOLS_SONAME@
CURL_NETWORK_AND_TIME_LIBS = @CURL_NETWORK_AND_TIME_LIBS@
-CURL_NETWORK_LIBS = @CURL_NETWORK_LIBS@
-CURL_WITH_MULTI_SSL = @CURL_WITH_MULTI_SSL@
CYGPATH_W = @CYGPATH_W@
-DEFAULT_SSL_BACKEND = @DEFAULT_SSL_BACKEND@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DLLTOOL = @DLLTOOL@
@@ -1045,27 +1081,20 @@ FILECMD = @FILECMD@
FISH_FUNCTIONS_DIR = @FISH_FUNCTIONS_DIR@
GCOV = @GCOV@
GREP = @GREP@
-HAVE_BROTLI = @HAVE_BROTLI@
-HAVE_GNUTLS_SRP = @HAVE_GNUTLS_SRP@
-HAVE_LDAP_SSL = @HAVE_LDAP_SSL@
HAVE_LIBZ = @HAVE_LIBZ@
-HAVE_OPENSSL_SRP = @HAVE_OPENSSL_SRP@
-HAVE_PROTO_BSDSOCKET_H = @HAVE_PROTO_BSDSOCKET_H@
-HAVE_ZSTD = @HAVE_ZSTD@
HTTPD = @HTTPD@
HTTPD_NGHTTPX = @HTTPD_NGHTTPX@
-IDN_ENABLED = @IDN_ENABLED@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
-IPV6_ENABLED = @IPV6_ENABLED@
LCOV = @LCOV@
LD = @LD@
LDFLAGS = @LDFLAGS@
LIBCURL_PC_CFLAGS = @LIBCURL_PC_CFLAGS@
LIBCURL_PC_CFLAGS_PRIVATE = @LIBCURL_PC_CFLAGS_PRIVATE@
+LIBCURL_PC_LDFLAGS_PRIVATE = @LIBCURL_PC_LDFLAGS_PRIVATE@
LIBCURL_PC_LIBS = @LIBCURL_PC_LIBS@
LIBCURL_PC_LIBS_PRIVATE = @LIBCURL_PC_LIBS_PRIVATE@
LIBCURL_PC_REQUIRES = @LIBCURL_PC_REQUIRES@
@@ -1098,9 +1127,6 @@ PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
PERL = @PERL@
-PKGADD_NAME = @PKGADD_NAME@
-PKGADD_PKG = @PKGADD_PKG@
-PKGADD_VENDOR = @PKGADD_VENDOR@
PKGCONFIG = @PKGCONFIG@
RANLIB = @RANLIB@
RC = @RC@
@@ -1108,46 +1134,10 @@ SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
SSL_BACKENDS = @SSL_BACKENDS@
-SSL_ENABLED = @SSL_ENABLED@
-SSL_LIBS = @SSL_LIBS@
STRIP = @STRIP@
SUPPORT_FEATURES = @SUPPORT_FEATURES@
SUPPORT_PROTOCOLS = @SUPPORT_PROTOCOLS@
TEST_NGHTTPX = @TEST_NGHTTPX@
-USE_APPLE_IDN = @USE_APPLE_IDN@
-USE_ARES = @USE_ARES@
-USE_BEARSSL = @USE_BEARSSL@
-USE_GNUTLS = @USE_GNUTLS@
-USE_HYPER = @USE_HYPER@
-USE_LIBPSL = @USE_LIBPSL@
-USE_LIBRTMP = @USE_LIBRTMP@
-USE_LIBSSH = @USE_LIBSSH@
-USE_LIBSSH2 = @USE_LIBSSH2@
-USE_LIBUV = @USE_LIBUV@
-USE_MBEDTLS = @USE_MBEDTLS@
-USE_MSH3 = @USE_MSH3@
-USE_NGHTTP2 = @USE_NGHTTP2@
-USE_NGHTTP3 = @USE_NGHTTP3@
-USE_NGTCP2 = @USE_NGTCP2@
-USE_NGTCP2_CRYPTO_BORINGSSL = @USE_NGTCP2_CRYPTO_BORINGSSL@
-USE_NGTCP2_CRYPTO_GNUTLS = @USE_NGTCP2_CRYPTO_GNUTLS@
-USE_NGTCP2_CRYPTO_QUICTLS = @USE_NGTCP2_CRYPTO_QUICTLS@
-USE_NGTCP2_CRYPTO_WOLFSSL = @USE_NGTCP2_CRYPTO_WOLFSSL@
-USE_NGTCP2_H3 = @USE_NGTCP2_H3@
-USE_OPENLDAP = @USE_OPENLDAP@
-USE_OPENSSL_H3 = @USE_OPENSSL_H3@
-USE_OPENSSL_QUIC = @USE_OPENSSL_QUIC@
-USE_QUICHE = @USE_QUICHE@
-USE_RUSTLS = @USE_RUSTLS@
-USE_SCHANNEL = @USE_SCHANNEL@
-USE_SECTRANSP = @USE_SECTRANSP@
-USE_UNIX_SOCKETS = @USE_UNIX_SOCKETS@
-USE_WIN32_CRYPTO = @USE_WIN32_CRYPTO@
-USE_WIN32_LARGE_FILES = @USE_WIN32_LARGE_FILES@
-USE_WIN32_SMALL_FILES = @USE_WIN32_SMALL_FILES@
-USE_WINDOWS_SSPI = @USE_WINDOWS_SSPI@
-USE_WOLFSSH = @USE_WOLFSSH@
-USE_WOLFSSL = @USE_WOLFSSL@
VERSION = @VERSION@
VERSIONNUM = @VERSIONNUM@
VSFTPD = @VSFTPD@
@@ -1236,11 +1226,9 @@ CMAKE_DIST = CMakeLists.txt curl_config.h.cmake
CHECKSRC_DIST = .checksrc vauth/.checksrc vquic/.checksrc vssh/.checksrc \
vtls/.checksrc
-EXTRA_DIST = Makefile.mk config-win32.h config-win32ce.h config-plan9.h \
- config-riscos.h config-mac.h curl_config.h.in config-dos.h libcurl.rc \
- config-amigaos.h config-win32ce.h config-os400.h setup-os400.h \
- $(CMAKE_DIST) setup-win32.h Makefile.soname optiontable.pl libcurl.def \
- $(CHECKSRC_DIST)
+EXTRA_DIST = config-mac.h config-os400.h config-plan9.h config-riscos.h \
+ config-win32.h curl_config.h.in libcurl.rc libcurl.def \
+ $(CMAKE_DIST) Makefile.soname optiontable.pl $(CHECKSRC_DIST)
lib_LTLIBRARIES = libcurl.la
@BUILD_UNITTESTS_FALSE@noinst_LTLIBRARIES =
@@ -1253,9 +1241,8 @@ lib_LTLIBRARIES = libcurl.la
#
# $(top_srcdir)/include is for libcurl's external include files
# $(top_builddir)/lib is for libcurl's generated lib/curl_config.h file
-# $(top_srcdir)/lib for libcurl's lib/curl_setup.h and other "private" files
AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/lib \
- -I$(top_srcdir)/lib -DBUILDING_LIBCURL
+ -DBUILDING_LIBCURL $(am__append_1) $(am__append_2)
# Keep in sync with CURL_LIBCURL_VERSIONED_SYMBOLS_SONAME in configure.ac
VERSIONCHANGE = 12
@@ -1266,6 +1253,33 @@ VERSIONDEL = 8
VERSIONINFO = -version-info $(VERSIONCHANGE):$(VERSIONADD):$(VERSIONDEL)
AM_LDFLAGS =
AM_CFLAGS =
+LIB_CURLX_CFILES = \
+ curlx/base64.c \
+ curlx/dynbuf.c \
+ curlx/inet_pton.c \
+ curlx/multibyte.c \
+ curlx/nonblock.c \
+ curlx/strparse.c \
+ curlx/timediff.c \
+ curlx/timeval.c \
+ curlx/version_win32.c \
+ curlx/warnless.c \
+ curlx/winapi.c
+
+LIB_CURLX_HFILES = \
+ curlx/base64.h \
+ curlx/curlx.h \
+ curlx/dynbuf.h \
+ curlx/inet_pton.h \
+ curlx/multibyte.h \
+ curlx/nonblock.h \
+ curlx/strparse.h \
+ curlx/timediff.h \
+ curlx/timeval.h \
+ curlx/version_win32.h \
+ curlx/warnless.h \
+ curlx/winapi.h
+
LIB_VAUTH_CFILES = \
vauth/cleartext.c \
vauth/cram.c \
@@ -1283,7 +1297,6 @@ LIB_VAUTH_CFILES = \
LIB_VAUTH_HFILES = \
vauth/digest.h \
- vauth/ntlm.h \
vauth/vauth.h
LIB_VTLS_CFILES = \
@@ -1300,6 +1313,8 @@ LIB_VTLS_CFILES = \
vtls/schannel_verify.c \
vtls/sectransp.c \
vtls/vtls.c \
+ vtls/vtls_scache.c \
+ vtls/vtls_spack.c \
vtls/wolfssl.c \
vtls/x509asn1.c
@@ -1318,6 +1333,8 @@ LIB_VTLS_HFILES = \
vtls/sectransp.h \
vtls/vtls.h \
vtls/vtls_int.h \
+ vtls/vtls_scache.h \
+ vtls/vtls_spack.h \
vtls/wolfssl.h \
vtls/x509asn1.h
@@ -1352,11 +1369,10 @@ LIB_CFILES = \
altsvc.c \
amigaos.c \
asyn-ares.c \
- asyn-thread.c \
- base64.c \
+ asyn-base.c \
+ asyn-thrdd.c \
bufq.c \
bufref.c \
- c-hyper.c \
cf-h1-proxy.c \
cf-h2-proxy.c \
cf-haproxy.c \
@@ -1367,6 +1383,7 @@ LIB_CFILES = \
connect.c \
content_encoding.c \
cookie.c \
+ cshutdn.c \
curl_addrinfo.c \
curl_des.c \
curl_endian.c \
@@ -1375,7 +1392,6 @@ LIB_CFILES = \
curl_gethostname.c \
curl_gssapi.c \
curl_memrchr.c \
- curl_multibyte.c \
curl_ntlm_core.c \
curl_range.c \
curl_rtmp.c \
@@ -1385,15 +1401,15 @@ LIB_CFILES = \
curl_threads.c \
curl_trc.c \
cw-out.c \
+ cw-pause.c \
dict.c \
- dllmain.c \
doh.c \
- dynbuf.c \
dynhds.c \
easy.c \
easygetopt.c \
easyoptions.c \
escape.c \
+ fake_addrinfo.c \
file.c \
fileinfo.c \
fopen.c \
@@ -1406,11 +1422,9 @@ LIB_CFILES = \
hash.c \
headers.c \
hmac.c \
- hostasyn.c \
hostip.c \
hostip4.c \
hostip6.c \
- hostsyn.c \
hsts.c \
http.c \
http1.c \
@@ -1421,11 +1435,11 @@ LIB_CFILES = \
http_negotiate.c \
http_ntlm.c \
http_proxy.c \
+ httpsrr.c \
idn.c \
if2ip.c \
imap.c \
inet_ntop.c \
- inet_pton.c \
krb5.c \
ldap.c \
llist.c \
@@ -1437,8 +1451,8 @@ LIB_CFILES = \
mprintf.c \
mqtt.c \
multi.c \
+ multi_ev.c \
netrc.c \
- nonblock.c \
noproxy.c \
openldap.c \
parsedate.c \
@@ -1466,20 +1480,19 @@ LIB_CFILES = \
splay.c \
strcase.c \
strdup.c \
+ strequal.c \
strerror.c \
- strtok.c \
- strtoofft.c \
system_win32.c \
telnet.c \
tftp.c \
- timediff.c \
- timeval.c \
transfer.c \
+ uint-bset.c \
+ uint-hash.c \
+ uint-spbset.c \
+ uint-table.c \
url.c \
urlapi.c \
version.c \
- version_win32.c \
- warnless.c \
ws.c
LIB_HFILES = \
@@ -1489,7 +1502,6 @@ LIB_HFILES = \
asyn.h \
bufq.h \
bufref.h \
- c-hyper.h \
cf-h1-proxy.h \
cf-h2-proxy.h \
cf-haproxy.h \
@@ -1497,11 +1509,11 @@ LIB_HFILES = \
cf-socket.h \
cfilters.h \
conncache.h \
+ cshutdn.h \
connect.h \
content_encoding.h \
cookie.h \
curl_addrinfo.h \
- curl_base64.h \
curl_ctype.h \
curl_des.h \
curl_endian.h \
@@ -1516,7 +1528,6 @@ LIB_HFILES = \
curl_md5.h \
curl_memory.h \
curl_memrchr.h \
- curl_multibyte.h \
curl_ntlm_core.h \
curl_printf.h \
curl_range.h \
@@ -1529,16 +1540,16 @@ LIB_HFILES = \
curl_sspi.h \
curl_threads.h \
curl_trc.h \
- curlx.h \
cw-out.h \
+ cw-pause.h \
dict.h \
doh.h \
- dynbuf.h \
dynhds.h \
easy_lock.h \
easyif.h \
easyoptions.h \
escape.h \
+ fake_addrinfo.h \
file.h \
fileinfo.h \
fopen.h \
@@ -1561,20 +1572,20 @@ LIB_HFILES = \
http_negotiate.h \
http_ntlm.h \
http_proxy.h \
+ httpsrr.h \
idn.h \
if2ip.h \
imap.h \
inet_ntop.h \
- inet_pton.h \
llist.h \
macos.h \
memdebug.h \
mime.h \
mqtt.h \
multihandle.h \
+ multi_ev.h \
multiif.h \
netrc.h \
- nonblock.h \
noproxy.h \
parsedate.h \
pingpong.h \
@@ -1588,7 +1599,9 @@ LIB_HFILES = \
select.h \
sendf.h \
setopt.h \
+ setup-os400.h \
setup-vms.h \
+ setup-win32.h \
share.h \
sigpipe.h \
slist.h \
@@ -1602,47 +1615,43 @@ LIB_HFILES = \
strcase.h \
strdup.h \
strerror.h \
- strtok.h \
- strtoofft.h \
system_win32.h \
telnet.h \
tftp.h \
- timediff.h \
- timeval.h \
transfer.h \
+ uint-bset.h \
+ uint-hash.h \
+ uint-spbset.h \
+ uint-table.h \
url.h \
urlapi-int.h \
urldata.h \
- version_win32.h \
- warnless.h \
ws.h
LIB_RCFILES = libcurl.rc
CSOURCES = $(LIB_CFILES) $(LIB_VAUTH_CFILES) $(LIB_VTLS_CFILES) \
- $(LIB_VQUIC_CFILES) $(LIB_VSSH_CFILES)
-
+ $(LIB_VQUIC_CFILES) $(LIB_VSSH_CFILES) $(LIB_CURLX_CFILES) \
+ $(am__append_3)
HHEADERS = $(LIB_HFILES) $(LIB_VAUTH_HFILES) $(LIB_VTLS_HFILES) \
- $(LIB_VQUIC_HFILES) $(LIB_VSSH_HFILES)
-
+ $(LIB_VQUIC_HFILES) $(LIB_VSSH_HFILES) $(LIB_CURLX_HFILES)
-# Makefile.inc provides the CSOURCES and HHEADERS defines
# Keep these separate to avoid duplicate definitions when linking libtests
# in static mode.
-@USE_UNITY_TRUE@curl_EXCLUDE = curl_threads.c timediff.c warnless.c \
-@USE_UNITY_TRUE@ $(am__append_1)
+@USE_UNITY_TRUE@curl_EXCLUDE = curl_threads.c curlx/timediff.c \
+@USE_UNITY_TRUE@ curlx/warnless.c $(am__append_4)
@USE_UNITY_TRUE@nodist_libcurl_la_SOURCES = libcurl_unity.c
@USE_UNITY_FALSE@libcurl_la_SOURCES = $(CSOURCES) $(HHEADERS) \
-@USE_UNITY_FALSE@ $(am__append_8)
-@USE_UNITY_TRUE@libcurl_la_SOURCES = $(curl_EXCLUDE) $(am__append_8)
+@USE_UNITY_FALSE@ $(am__append_11)
+@USE_UNITY_TRUE@libcurl_la_SOURCES = $(curl_EXCLUDE) $(am__append_11)
@USE_UNITY_TRUE@nodist_libcurlu_la_SOURCES = libcurl_unity.c
@USE_UNITY_FALSE@libcurlu_la_SOURCES = $(CSOURCES) $(HHEADERS)
@USE_UNITY_TRUE@libcurlu_la_SOURCES = $(curl_EXCLUDE)
@USE_UNITY_TRUE@CLEANFILES = libcurl_unity.c
-libcurl_la_CPPFLAGS_EXTRA = $(am__append_7) $(am__append_9)
-libcurl_la_LDFLAGS_EXTRA = $(am__append_2) $(am__append_3) \
- $(am__append_4) $(am__append_5) $(am__append_6)
-libcurl_la_CFLAGS_EXTRA = $(am__append_10)
+libcurl_la_CPPFLAGS_EXTRA = $(am__append_10) $(am__append_12)
+libcurl_la_LDFLAGS_EXTRA = $(am__append_5) $(am__append_6) \
+ $(am__append_7) $(am__append_8) $(am__append_9)
+libcurl_la_CFLAGS_EXTRA = $(am__append_13)
libcurl_la_CPPFLAGS = $(AM_CPPFLAGS) $(libcurl_la_CPPFLAGS_EXTRA)
libcurl_la_LDFLAGS = $(AM_LDFLAGS) $(libcurl_la_LDFLAGS_EXTRA) $(CURL_LDFLAGS_LIB) $(LIBCURL_PC_LIBS_PRIVATE)
libcurl_la_CFLAGS = $(AM_CFLAGS) $(libcurl_la_CFLAGS_EXTRA)
@@ -1655,7 +1664,8 @@ CS_1 =
CS_ = $(CS_0)
# disable the tests that are mostly causing false positives
-TIDYFLAGS = -checks=-clang-analyzer-security.insecureAPI.strcpy,-clang-analyzer-optin.performance.Padding,-clang-analyzer-valist.Uninitialized,-clang-analyzer-core.NonNullParamChecker,-clang-analyzer-core.NullDereference -quiet
+TIDYFLAGS := -checks=-clang-analyzer-security.insecureAPI.strcpy,-clang-analyzer-optin.performance.Padding,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling \
+ -quiet $(am__append_14)
TIDY := clang-tidy
all: curl_config.h
$(MAKE) $(AM_MAKEFLAGS) all-am
@@ -1819,6 +1829,10 @@ vtls/libcurl_la-sectransp.lo: vtls/$(am__dirstamp) \
vtls/$(DEPDIR)/$(am__dirstamp)
vtls/libcurl_la-vtls.lo: vtls/$(am__dirstamp) \
vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurl_la-vtls_scache.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurl_la-vtls_spack.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
vtls/libcurl_la-wolfssl.lo: vtls/$(am__dirstamp) \
vtls/$(DEPDIR)/$(am__dirstamp)
vtls/libcurl_la-x509asn1.lo: vtls/$(am__dirstamp) \
@@ -1855,6 +1869,34 @@ vssh/libcurl_la-curl_path.lo: vssh/$(am__dirstamp) \
vssh/$(DEPDIR)/$(am__dirstamp)
vssh/libcurl_la-wolfssh.lo: vssh/$(am__dirstamp) \
vssh/$(DEPDIR)/$(am__dirstamp)
+curlx/$(am__dirstamp):
+ @$(MKDIR_P) curlx
+ @: > curlx/$(am__dirstamp)
+curlx/$(DEPDIR)/$(am__dirstamp):
+ @$(MKDIR_P) curlx/$(DEPDIR)
+ @: > curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurl_la-base64.lo: curlx/$(am__dirstamp) \
+ curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurl_la-dynbuf.lo: curlx/$(am__dirstamp) \
+ curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurl_la-inet_pton.lo: curlx/$(am__dirstamp) \
+ curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurl_la-multibyte.lo: curlx/$(am__dirstamp) \
+ curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurl_la-nonblock.lo: curlx/$(am__dirstamp) \
+ curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurl_la-strparse.lo: curlx/$(am__dirstamp) \
+ curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurl_la-timediff.lo: curlx/$(am__dirstamp) \
+ curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurl_la-timeval.lo: curlx/$(am__dirstamp) \
+ curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurl_la-version_win32.lo: curlx/$(am__dirstamp) \
+ curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurl_la-warnless.lo: curlx/$(am__dirstamp) \
+ curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurl_la-winapi.lo: curlx/$(am__dirstamp) \
+ curlx/$(DEPDIR)/$(am__dirstamp)
libcurl.la: $(libcurl_la_OBJECTS) $(libcurl_la_DEPENDENCIES) $(EXTRA_libcurl_la_DEPENDENCIES)
$(AM_V_CCLD)$(libcurl_la_LINK) -rpath $(libdir) $(libcurl_la_OBJECTS) $(libcurl_la_LIBADD) $(LIBS)
@@ -1910,6 +1952,10 @@ vtls/libcurlu_la-sectransp.lo: vtls/$(am__dirstamp) \
vtls/$(DEPDIR)/$(am__dirstamp)
vtls/libcurlu_la-vtls.lo: vtls/$(am__dirstamp) \
vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurlu_la-vtls_scache.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
+vtls/libcurlu_la-vtls_spack.lo: vtls/$(am__dirstamp) \
+ vtls/$(DEPDIR)/$(am__dirstamp)
vtls/libcurlu_la-wolfssl.lo: vtls/$(am__dirstamp) \
vtls/$(DEPDIR)/$(am__dirstamp)
vtls/libcurlu_la-x509asn1.lo: vtls/$(am__dirstamp) \
@@ -1934,12 +1980,36 @@ vssh/libcurlu_la-curl_path.lo: vssh/$(am__dirstamp) \
vssh/$(DEPDIR)/$(am__dirstamp)
vssh/libcurlu_la-wolfssh.lo: vssh/$(am__dirstamp) \
vssh/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurlu_la-base64.lo: curlx/$(am__dirstamp) \
+ curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurlu_la-dynbuf.lo: curlx/$(am__dirstamp) \
+ curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurlu_la-inet_pton.lo: curlx/$(am__dirstamp) \
+ curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurlu_la-multibyte.lo: curlx/$(am__dirstamp) \
+ curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurlu_la-nonblock.lo: curlx/$(am__dirstamp) \
+ curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurlu_la-strparse.lo: curlx/$(am__dirstamp) \
+ curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurlu_la-timediff.lo: curlx/$(am__dirstamp) \
+ curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurlu_la-timeval.lo: curlx/$(am__dirstamp) \
+ curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurlu_la-version_win32.lo: curlx/$(am__dirstamp) \
+ curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurlu_la-warnless.lo: curlx/$(am__dirstamp) \
+ curlx/$(DEPDIR)/$(am__dirstamp)
+curlx/libcurlu_la-winapi.lo: curlx/$(am__dirstamp) \
+ curlx/$(DEPDIR)/$(am__dirstamp)
libcurlu.la: $(libcurlu_la_OBJECTS) $(libcurlu_la_DEPENDENCIES) $(EXTRA_libcurlu_la_DEPENDENCIES)
$(AM_V_CCLD)$(libcurlu_la_LINK) $(am_libcurlu_la_rpath) $(libcurlu_la_OBJECTS) $(libcurlu_la_LIBADD) $(LIBS)
mostlyclean-compile:
-rm -f *.$(OBJEXT)
+ -rm -f curlx/*.$(OBJEXT)
+ -rm -f curlx/*.lo
-rm -f vauth/*.$(OBJEXT)
-rm -f vauth/*.lo
-rm -f vquic/*.$(OBJEXT)
@@ -1955,11 +2025,10 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-altsvc.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-amigaos.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-asyn-ares.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-asyn-thread.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-base64.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-asyn-base.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-asyn-thrdd.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-bufq.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-bufref.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-c-hyper.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cf-h1-proxy.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cf-h2-proxy.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cf-haproxy.Plo@am__quote@ # am--include-marker
@@ -1970,6 +2039,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-connect.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-content_encoding.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cookie.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cshutdn.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_addrinfo.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_des.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_endian.Plo@am__quote@ # am--include-marker
@@ -1978,7 +2048,6 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_gethostname.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_gssapi.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_memrchr.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_multibyte.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_ntlm_core.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_range.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_rtmp.Plo@am__quote@ # am--include-marker
@@ -1988,15 +2057,16 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_threads.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-curl_trc.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cw-out.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-cw-pause.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-dict.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-dllmain.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-doh.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-dynbuf.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-dynhds.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-easy.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-easygetopt.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-easyoptions.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-escape.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-fake_addrinfo.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-file.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-fileinfo.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-fopen.Plo@am__quote@ # am--include-marker
@@ -2009,11 +2079,9 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hash.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-headers.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hmac.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hostasyn.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hostip.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hostip4.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hostip6.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hostsyn.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-hsts.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http1.Plo@am__quote@ # am--include-marker
@@ -2024,11 +2092,11 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http_negotiate.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http_ntlm.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-http_proxy.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-httpsrr.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-idn.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-if2ip.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-imap.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-inet_ntop.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-inet_pton.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-krb5.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-ldap.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-libcurl_unity.Plo@am__quote@ # am--include-marker
@@ -2041,8 +2109,8 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-mprintf.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-mqtt.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-multi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-multi_ev.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-netrc.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-nonblock.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-noproxy.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-openldap.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-parsedate.Plo@am__quote@ # am--include-marker
@@ -2070,29 +2138,27 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-splay.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strcase.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strdup.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strequal.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strerror.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strtok.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-strtoofft.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-system_win32.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-telnet.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-tftp.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-timediff.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-timeval.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-transfer.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-uint-bset.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-uint-hash.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-uint-spbset.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-uint-table.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-url.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-urlapi.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-version.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-version_win32.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-warnless.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurl_la-ws.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-altsvc.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-amigaos.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-asyn-ares.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-asyn-thread.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-base64.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-asyn-base.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-asyn-thrdd.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-bufq.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-bufref.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-c-hyper.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cf-h1-proxy.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cf-h2-proxy.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cf-haproxy.Plo@am__quote@ # am--include-marker
@@ -2103,6 +2169,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-connect.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-content_encoding.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cookie.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cshutdn.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_addrinfo.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_des.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_endian.Plo@am__quote@ # am--include-marker
@@ -2111,7 +2178,6 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_gethostname.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_gssapi.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_memrchr.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_multibyte.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_ntlm_core.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_range.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_rtmp.Plo@am__quote@ # am--include-marker
@@ -2121,15 +2187,16 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_threads.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-curl_trc.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cw-out.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-cw-pause.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-dict.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-dllmain.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-doh.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-dynbuf.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-dynhds.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-easy.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-easygetopt.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-easyoptions.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-escape.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-fake_addrinfo.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-file.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-fileinfo.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-fopen.Plo@am__quote@ # am--include-marker
@@ -2142,11 +2209,9 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hash.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-headers.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hmac.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hostasyn.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hostip.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hostip4.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hostip6.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hostsyn.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-hsts.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http1.Plo@am__quote@ # am--include-marker
@@ -2157,11 +2222,11 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http_negotiate.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http_ntlm.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-http_proxy.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-httpsrr.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-idn.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-if2ip.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-imap.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-inet_ntop.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-inet_pton.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-krb5.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-ldap.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-libcurl_unity.Plo@am__quote@ # am--include-marker
@@ -2174,8 +2239,8 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-mprintf.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-mqtt.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-multi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-multi_ev.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-netrc.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-nonblock.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-noproxy.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-openldap.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-parsedate.Plo@am__quote@ # am--include-marker
@@ -2203,21 +2268,42 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-splay.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strcase.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strdup.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strequal.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strerror.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strtok.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-strtoofft.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-system_win32.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-telnet.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-tftp.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-timediff.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-timeval.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-transfer.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-uint-bset.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-uint-hash.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-uint-spbset.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-uint-table.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-url.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-urlapi.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-version.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-version_win32.Plo@am__quote@ # am--include-marker
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-warnless.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libcurlu_la-ws.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurl_la-base64.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurl_la-dynbuf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurl_la-inet_pton.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurl_la-multibyte.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurl_la-nonblock.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurl_la-strparse.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurl_la-timediff.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurl_la-timeval.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurl_la-version_win32.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurl_la-warnless.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurl_la-winapi.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurlu_la-base64.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurlu_la-dynbuf.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurlu_la-inet_pton.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurlu_la-multibyte.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurlu_la-nonblock.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurlu_la-strparse.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurlu_la-timediff.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurlu_la-timeval.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurlu_la-version_win32.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurlu_la-warnless.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@curlx/$(DEPDIR)/libcurlu_la-winapi.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-cleartext.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-cram.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@vauth/$(DEPDIR)/libcurl_la-digest.Plo@am__quote@ # am--include-marker
@@ -2277,6 +2363,8 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-schannel_verify.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-sectransp.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-vtls.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-vtls_scache.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-vtls_spack.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-wolfssl.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurl_la-x509asn1.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-bearssl.Plo@am__quote@ # am--include-marker
@@ -2292,6 +2380,8 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-sectransp.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-vtls.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-vtls_scache.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-vtls_spack.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-wolfssl.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@vtls/$(DEPDIR)/libcurlu_la-x509asn1.Plo@am__quote@ # am--include-marker
@@ -2346,19 +2436,19 @@ libcurl_la-asyn-ares.lo: asyn-ares.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-asyn-ares.lo `test -f 'asyn-ares.c' || echo '$(srcdir)/'`asyn-ares.c
-libcurl_la-asyn-thread.lo: asyn-thread.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-asyn-thread.lo -MD -MP -MF $(DEPDIR)/libcurl_la-asyn-thread.Tpo -c -o libcurl_la-asyn-thread.lo `test -f 'asyn-thread.c' || echo '$(srcdir)/'`asyn-thread.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-asyn-thread.Tpo $(DEPDIR)/libcurl_la-asyn-thread.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asyn-thread.c' object='libcurl_la-asyn-thread.lo' libtool=yes @AMDEPBACKSLASH@
+libcurl_la-asyn-base.lo: asyn-base.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-asyn-base.lo -MD -MP -MF $(DEPDIR)/libcurl_la-asyn-base.Tpo -c -o libcurl_la-asyn-base.lo `test -f 'asyn-base.c' || echo '$(srcdir)/'`asyn-base.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-asyn-base.Tpo $(DEPDIR)/libcurl_la-asyn-base.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asyn-base.c' object='libcurl_la-asyn-base.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-asyn-thread.lo `test -f 'asyn-thread.c' || echo '$(srcdir)/'`asyn-thread.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-asyn-base.lo `test -f 'asyn-base.c' || echo '$(srcdir)/'`asyn-base.c
-libcurl_la-base64.lo: base64.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-base64.lo -MD -MP -MF $(DEPDIR)/libcurl_la-base64.Tpo -c -o libcurl_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-base64.Tpo $(DEPDIR)/libcurl_la-base64.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='base64.c' object='libcurl_la-base64.lo' libtool=yes @AMDEPBACKSLASH@
+libcurl_la-asyn-thrdd.lo: asyn-thrdd.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-asyn-thrdd.lo -MD -MP -MF $(DEPDIR)/libcurl_la-asyn-thrdd.Tpo -c -o libcurl_la-asyn-thrdd.lo `test -f 'asyn-thrdd.c' || echo '$(srcdir)/'`asyn-thrdd.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-asyn-thrdd.Tpo $(DEPDIR)/libcurl_la-asyn-thrdd.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asyn-thrdd.c' object='libcurl_la-asyn-thrdd.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-asyn-thrdd.lo `test -f 'asyn-thrdd.c' || echo '$(srcdir)/'`asyn-thrdd.c
libcurl_la-bufq.lo: bufq.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-bufq.lo -MD -MP -MF $(DEPDIR)/libcurl_la-bufq.Tpo -c -o libcurl_la-bufq.lo `test -f 'bufq.c' || echo '$(srcdir)/'`bufq.c
@@ -2374,13 +2464,6 @@ libcurl_la-bufref.lo: bufref.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-bufref.lo `test -f 'bufref.c' || echo '$(srcdir)/'`bufref.c
-libcurl_la-c-hyper.lo: c-hyper.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-c-hyper.lo -MD -MP -MF $(DEPDIR)/libcurl_la-c-hyper.Tpo -c -o libcurl_la-c-hyper.lo `test -f 'c-hyper.c' || echo '$(srcdir)/'`c-hyper.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-c-hyper.Tpo $(DEPDIR)/libcurl_la-c-hyper.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='c-hyper.c' object='libcurl_la-c-hyper.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-c-hyper.lo `test -f 'c-hyper.c' || echo '$(srcdir)/'`c-hyper.c
-
libcurl_la-cf-h1-proxy.lo: cf-h1-proxy.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-cf-h1-proxy.lo -MD -MP -MF $(DEPDIR)/libcurl_la-cf-h1-proxy.Tpo -c -o libcurl_la-cf-h1-proxy.lo `test -f 'cf-h1-proxy.c' || echo '$(srcdir)/'`cf-h1-proxy.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-cf-h1-proxy.Tpo $(DEPDIR)/libcurl_la-cf-h1-proxy.Plo
@@ -2451,6 +2534,13 @@ libcurl_la-cookie.lo: cookie.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-cookie.lo `test -f 'cookie.c' || echo '$(srcdir)/'`cookie.c
+libcurl_la-cshutdn.lo: cshutdn.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-cshutdn.lo -MD -MP -MF $(DEPDIR)/libcurl_la-cshutdn.Tpo -c -o libcurl_la-cshutdn.lo `test -f 'cshutdn.c' || echo '$(srcdir)/'`cshutdn.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-cshutdn.Tpo $(DEPDIR)/libcurl_la-cshutdn.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cshutdn.c' object='libcurl_la-cshutdn.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@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-cshutdn.lo `test -f 'cshutdn.c' || echo '$(srcdir)/'`cshutdn.c
+
libcurl_la-curl_addrinfo.lo: curl_addrinfo.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_addrinfo.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_addrinfo.Tpo -c -o libcurl_la-curl_addrinfo.lo `test -f 'curl_addrinfo.c' || echo '$(srcdir)/'`curl_addrinfo.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_addrinfo.Tpo $(DEPDIR)/libcurl_la-curl_addrinfo.Plo
@@ -2507,13 +2597,6 @@ libcurl_la-curl_memrchr.lo: curl_memrchr.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-curl_memrchr.lo `test -f 'curl_memrchr.c' || echo '$(srcdir)/'`curl_memrchr.c
-libcurl_la-curl_multibyte.lo: curl_multibyte.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_multibyte.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_multibyte.Tpo -c -o libcurl_la-curl_multibyte.lo `test -f 'curl_multibyte.c' || echo '$(srcdir)/'`curl_multibyte.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_multibyte.Tpo $(DEPDIR)/libcurl_la-curl_multibyte.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_multibyte.c' object='libcurl_la-curl_multibyte.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-curl_multibyte.lo `test -f 'curl_multibyte.c' || echo '$(srcdir)/'`curl_multibyte.c
-
libcurl_la-curl_ntlm_core.lo: curl_ntlm_core.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-curl_ntlm_core.lo -MD -MP -MF $(DEPDIR)/libcurl_la-curl_ntlm_core.Tpo -c -o libcurl_la-curl_ntlm_core.lo `test -f 'curl_ntlm_core.c' || echo '$(srcdir)/'`curl_ntlm_core.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-curl_ntlm_core.Tpo $(DEPDIR)/libcurl_la-curl_ntlm_core.Plo
@@ -2577,6 +2660,13 @@ libcurl_la-cw-out.lo: cw-out.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-cw-out.lo `test -f 'cw-out.c' || echo '$(srcdir)/'`cw-out.c
+libcurl_la-cw-pause.lo: cw-pause.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-cw-pause.lo -MD -MP -MF $(DEPDIR)/libcurl_la-cw-pause.Tpo -c -o libcurl_la-cw-pause.lo `test -f 'cw-pause.c' || echo '$(srcdir)/'`cw-pause.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-cw-pause.Tpo $(DEPDIR)/libcurl_la-cw-pause.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cw-pause.c' object='libcurl_la-cw-pause.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-cw-pause.lo `test -f 'cw-pause.c' || echo '$(srcdir)/'`cw-pause.c
+
libcurl_la-dict.lo: dict.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-dict.lo -MD -MP -MF $(DEPDIR)/libcurl_la-dict.Tpo -c -o libcurl_la-dict.lo `test -f 'dict.c' || echo '$(srcdir)/'`dict.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-dict.Tpo $(DEPDIR)/libcurl_la-dict.Plo
@@ -2584,13 +2674,6 @@ libcurl_la-dict.lo: dict.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-dict.lo `test -f 'dict.c' || echo '$(srcdir)/'`dict.c
-libcurl_la-dllmain.lo: dllmain.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-dllmain.lo -MD -MP -MF $(DEPDIR)/libcurl_la-dllmain.Tpo -c -o libcurl_la-dllmain.lo `test -f 'dllmain.c' || echo '$(srcdir)/'`dllmain.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-dllmain.Tpo $(DEPDIR)/libcurl_la-dllmain.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dllmain.c' object='libcurl_la-dllmain.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-dllmain.lo `test -f 'dllmain.c' || echo '$(srcdir)/'`dllmain.c
-
libcurl_la-doh.lo: doh.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-doh.lo -MD -MP -MF $(DEPDIR)/libcurl_la-doh.Tpo -c -o libcurl_la-doh.lo `test -f 'doh.c' || echo '$(srcdir)/'`doh.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-doh.Tpo $(DEPDIR)/libcurl_la-doh.Plo
@@ -2598,13 +2681,6 @@ libcurl_la-doh.lo: doh.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-doh.lo `test -f 'doh.c' || echo '$(srcdir)/'`doh.c
-libcurl_la-dynbuf.lo: dynbuf.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-dynbuf.lo -MD -MP -MF $(DEPDIR)/libcurl_la-dynbuf.Tpo -c -o libcurl_la-dynbuf.lo `test -f 'dynbuf.c' || echo '$(srcdir)/'`dynbuf.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-dynbuf.Tpo $(DEPDIR)/libcurl_la-dynbuf.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dynbuf.c' object='libcurl_la-dynbuf.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-dynbuf.lo `test -f 'dynbuf.c' || echo '$(srcdir)/'`dynbuf.c
-
libcurl_la-dynhds.lo: dynhds.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-dynhds.lo -MD -MP -MF $(DEPDIR)/libcurl_la-dynhds.Tpo -c -o libcurl_la-dynhds.lo `test -f 'dynhds.c' || echo '$(srcdir)/'`dynhds.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-dynhds.Tpo $(DEPDIR)/libcurl_la-dynhds.Plo
@@ -2640,6 +2716,13 @@ libcurl_la-escape.lo: escape.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-escape.lo `test -f 'escape.c' || echo '$(srcdir)/'`escape.c
+libcurl_la-fake_addrinfo.lo: fake_addrinfo.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-fake_addrinfo.lo -MD -MP -MF $(DEPDIR)/libcurl_la-fake_addrinfo.Tpo -c -o libcurl_la-fake_addrinfo.lo `test -f 'fake_addrinfo.c' || echo '$(srcdir)/'`fake_addrinfo.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-fake_addrinfo.Tpo $(DEPDIR)/libcurl_la-fake_addrinfo.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fake_addrinfo.c' object='libcurl_la-fake_addrinfo.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-fake_addrinfo.lo `test -f 'fake_addrinfo.c' || echo '$(srcdir)/'`fake_addrinfo.c
+
libcurl_la-file.lo: file.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-file.lo -MD -MP -MF $(DEPDIR)/libcurl_la-file.Tpo -c -o libcurl_la-file.lo `test -f 'file.c' || echo '$(srcdir)/'`file.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-file.Tpo $(DEPDIR)/libcurl_la-file.Plo
@@ -2724,13 +2807,6 @@ libcurl_la-hmac.lo: hmac.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-hmac.lo `test -f 'hmac.c' || echo '$(srcdir)/'`hmac.c
-libcurl_la-hostasyn.lo: hostasyn.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hostasyn.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hostasyn.Tpo -c -o libcurl_la-hostasyn.lo `test -f 'hostasyn.c' || echo '$(srcdir)/'`hostasyn.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hostasyn.Tpo $(DEPDIR)/libcurl_la-hostasyn.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostasyn.c' object='libcurl_la-hostasyn.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hostasyn.lo `test -f 'hostasyn.c' || echo '$(srcdir)/'`hostasyn.c
-
libcurl_la-hostip.lo: hostip.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hostip.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hostip.Tpo -c -o libcurl_la-hostip.lo `test -f 'hostip.c' || echo '$(srcdir)/'`hostip.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hostip.Tpo $(DEPDIR)/libcurl_la-hostip.Plo
@@ -2752,13 +2828,6 @@ libcurl_la-hostip6.lo: hostip6.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-hostip6.lo `test -f 'hostip6.c' || echo '$(srcdir)/'`hostip6.c
-libcurl_la-hostsyn.lo: hostsyn.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hostsyn.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hostsyn.Tpo -c -o libcurl_la-hostsyn.lo `test -f 'hostsyn.c' || echo '$(srcdir)/'`hostsyn.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hostsyn.Tpo $(DEPDIR)/libcurl_la-hostsyn.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostsyn.c' object='libcurl_la-hostsyn.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-hostsyn.lo `test -f 'hostsyn.c' || echo '$(srcdir)/'`hostsyn.c
-
libcurl_la-hsts.lo: hsts.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-hsts.lo -MD -MP -MF $(DEPDIR)/libcurl_la-hsts.Tpo -c -o libcurl_la-hsts.lo `test -f 'hsts.c' || echo '$(srcdir)/'`hsts.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-hsts.Tpo $(DEPDIR)/libcurl_la-hsts.Plo
@@ -2829,6 +2898,13 @@ libcurl_la-http_proxy.lo: http_proxy.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-http_proxy.lo `test -f 'http_proxy.c' || echo '$(srcdir)/'`http_proxy.c
+libcurl_la-httpsrr.lo: httpsrr.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-httpsrr.lo -MD -MP -MF $(DEPDIR)/libcurl_la-httpsrr.Tpo -c -o libcurl_la-httpsrr.lo `test -f 'httpsrr.c' || echo '$(srcdir)/'`httpsrr.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-httpsrr.Tpo $(DEPDIR)/libcurl_la-httpsrr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='httpsrr.c' object='libcurl_la-httpsrr.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@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-httpsrr.lo `test -f 'httpsrr.c' || echo '$(srcdir)/'`httpsrr.c
+
libcurl_la-idn.lo: idn.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-idn.lo -MD -MP -MF $(DEPDIR)/libcurl_la-idn.Tpo -c -o libcurl_la-idn.lo `test -f 'idn.c' || echo '$(srcdir)/'`idn.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-idn.Tpo $(DEPDIR)/libcurl_la-idn.Plo
@@ -2857,13 +2933,6 @@ libcurl_la-inet_ntop.lo: inet_ntop.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-inet_ntop.lo `test -f 'inet_ntop.c' || echo '$(srcdir)/'`inet_ntop.c
-libcurl_la-inet_pton.lo: inet_pton.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-inet_pton.lo -MD -MP -MF $(DEPDIR)/libcurl_la-inet_pton.Tpo -c -o libcurl_la-inet_pton.lo `test -f 'inet_pton.c' || echo '$(srcdir)/'`inet_pton.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-inet_pton.Tpo $(DEPDIR)/libcurl_la-inet_pton.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='inet_pton.c' object='libcurl_la-inet_pton.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-inet_pton.lo `test -f 'inet_pton.c' || echo '$(srcdir)/'`inet_pton.c
-
libcurl_la-krb5.lo: krb5.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-krb5.lo -MD -MP -MF $(DEPDIR)/libcurl_la-krb5.Tpo -c -o libcurl_la-krb5.lo `test -f 'krb5.c' || echo '$(srcdir)/'`krb5.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-krb5.Tpo $(DEPDIR)/libcurl_la-krb5.Plo
@@ -2941,6 +3010,13 @@ libcurl_la-multi.lo: multi.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-multi.lo `test -f 'multi.c' || echo '$(srcdir)/'`multi.c
+libcurl_la-multi_ev.lo: multi_ev.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-multi_ev.lo -MD -MP -MF $(DEPDIR)/libcurl_la-multi_ev.Tpo -c -o libcurl_la-multi_ev.lo `test -f 'multi_ev.c' || echo '$(srcdir)/'`multi_ev.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-multi_ev.Tpo $(DEPDIR)/libcurl_la-multi_ev.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='multi_ev.c' object='libcurl_la-multi_ev.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-multi_ev.lo `test -f 'multi_ev.c' || echo '$(srcdir)/'`multi_ev.c
+
libcurl_la-netrc.lo: netrc.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-netrc.lo -MD -MP -MF $(DEPDIR)/libcurl_la-netrc.Tpo -c -o libcurl_la-netrc.lo `test -f 'netrc.c' || echo '$(srcdir)/'`netrc.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-netrc.Tpo $(DEPDIR)/libcurl_la-netrc.Plo
@@ -2948,13 +3024,6 @@ libcurl_la-netrc.lo: netrc.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-netrc.lo `test -f 'netrc.c' || echo '$(srcdir)/'`netrc.c
-libcurl_la-nonblock.lo: nonblock.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-nonblock.lo -MD -MP -MF $(DEPDIR)/libcurl_la-nonblock.Tpo -c -o libcurl_la-nonblock.lo `test -f 'nonblock.c' || echo '$(srcdir)/'`nonblock.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-nonblock.Tpo $(DEPDIR)/libcurl_la-nonblock.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nonblock.c' object='libcurl_la-nonblock.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-nonblock.lo `test -f 'nonblock.c' || echo '$(srcdir)/'`nonblock.c
-
libcurl_la-noproxy.lo: noproxy.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-noproxy.lo -MD -MP -MF $(DEPDIR)/libcurl_la-noproxy.Tpo -c -o libcurl_la-noproxy.lo `test -f 'noproxy.c' || echo '$(srcdir)/'`noproxy.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-noproxy.Tpo $(DEPDIR)/libcurl_la-noproxy.Plo
@@ -3144,6 +3213,13 @@ libcurl_la-strdup.lo: strdup.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-strdup.lo `test -f 'strdup.c' || echo '$(srcdir)/'`strdup.c
+libcurl_la-strequal.lo: strequal.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-strequal.lo -MD -MP -MF $(DEPDIR)/libcurl_la-strequal.Tpo -c -o libcurl_la-strequal.lo `test -f 'strequal.c' || echo '$(srcdir)/'`strequal.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-strequal.Tpo $(DEPDIR)/libcurl_la-strequal.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strequal.c' object='libcurl_la-strequal.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@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-strequal.lo `test -f 'strequal.c' || echo '$(srcdir)/'`strequal.c
+
libcurl_la-strerror.lo: strerror.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-strerror.lo -MD -MP -MF $(DEPDIR)/libcurl_la-strerror.Tpo -c -o libcurl_la-strerror.lo `test -f 'strerror.c' || echo '$(srcdir)/'`strerror.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-strerror.Tpo $(DEPDIR)/libcurl_la-strerror.Plo
@@ -3151,20 +3227,6 @@ libcurl_la-strerror.lo: strerror.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-strerror.lo `test -f 'strerror.c' || echo '$(srcdir)/'`strerror.c
-libcurl_la-strtok.lo: strtok.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-strtok.lo -MD -MP -MF $(DEPDIR)/libcurl_la-strtok.Tpo -c -o libcurl_la-strtok.lo `test -f 'strtok.c' || echo '$(srcdir)/'`strtok.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-strtok.Tpo $(DEPDIR)/libcurl_la-strtok.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strtok.c' object='libcurl_la-strtok.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-strtok.lo `test -f 'strtok.c' || echo '$(srcdir)/'`strtok.c
-
-libcurl_la-strtoofft.lo: strtoofft.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-strtoofft.lo -MD -MP -MF $(DEPDIR)/libcurl_la-strtoofft.Tpo -c -o libcurl_la-strtoofft.lo `test -f 'strtoofft.c' || echo '$(srcdir)/'`strtoofft.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-strtoofft.Tpo $(DEPDIR)/libcurl_la-strtoofft.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strtoofft.c' object='libcurl_la-strtoofft.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-strtoofft.lo `test -f 'strtoofft.c' || echo '$(srcdir)/'`strtoofft.c
-
libcurl_la-system_win32.lo: system_win32.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-system_win32.lo -MD -MP -MF $(DEPDIR)/libcurl_la-system_win32.Tpo -c -o libcurl_la-system_win32.lo `test -f 'system_win32.c' || echo '$(srcdir)/'`system_win32.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-system_win32.Tpo $(DEPDIR)/libcurl_la-system_win32.Plo
@@ -3186,20 +3248,6 @@ libcurl_la-tftp.lo: tftp.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-tftp.lo `test -f 'tftp.c' || echo '$(srcdir)/'`tftp.c
-libcurl_la-timediff.lo: timediff.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-timediff.lo -MD -MP -MF $(DEPDIR)/libcurl_la-timediff.Tpo -c -o libcurl_la-timediff.lo `test -f 'timediff.c' || echo '$(srcdir)/'`timediff.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-timediff.Tpo $(DEPDIR)/libcurl_la-timediff.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='timediff.c' object='libcurl_la-timediff.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-timediff.lo `test -f 'timediff.c' || echo '$(srcdir)/'`timediff.c
-
-libcurl_la-timeval.lo: timeval.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-timeval.lo -MD -MP -MF $(DEPDIR)/libcurl_la-timeval.Tpo -c -o libcurl_la-timeval.lo `test -f 'timeval.c' || echo '$(srcdir)/'`timeval.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-timeval.Tpo $(DEPDIR)/libcurl_la-timeval.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='timeval.c' object='libcurl_la-timeval.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-timeval.lo `test -f 'timeval.c' || echo '$(srcdir)/'`timeval.c
-
libcurl_la-transfer.lo: transfer.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-transfer.lo -MD -MP -MF $(DEPDIR)/libcurl_la-transfer.Tpo -c -o libcurl_la-transfer.lo `test -f 'transfer.c' || echo '$(srcdir)/'`transfer.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-transfer.Tpo $(DEPDIR)/libcurl_la-transfer.Plo
@@ -3207,6 +3255,34 @@ libcurl_la-transfer.lo: transfer.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-transfer.lo `test -f 'transfer.c' || echo '$(srcdir)/'`transfer.c
+libcurl_la-uint-bset.lo: uint-bset.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-uint-bset.lo -MD -MP -MF $(DEPDIR)/libcurl_la-uint-bset.Tpo -c -o libcurl_la-uint-bset.lo `test -f 'uint-bset.c' || echo '$(srcdir)/'`uint-bset.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-uint-bset.Tpo $(DEPDIR)/libcurl_la-uint-bset.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='uint-bset.c' object='libcurl_la-uint-bset.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@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-uint-bset.lo `test -f 'uint-bset.c' || echo '$(srcdir)/'`uint-bset.c
+
+libcurl_la-uint-hash.lo: uint-hash.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-uint-hash.lo -MD -MP -MF $(DEPDIR)/libcurl_la-uint-hash.Tpo -c -o libcurl_la-uint-hash.lo `test -f 'uint-hash.c' || echo '$(srcdir)/'`uint-hash.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-uint-hash.Tpo $(DEPDIR)/libcurl_la-uint-hash.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='uint-hash.c' object='libcurl_la-uint-hash.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-uint-hash.lo `test -f 'uint-hash.c' || echo '$(srcdir)/'`uint-hash.c
+
+libcurl_la-uint-spbset.lo: uint-spbset.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-uint-spbset.lo -MD -MP -MF $(DEPDIR)/libcurl_la-uint-spbset.Tpo -c -o libcurl_la-uint-spbset.lo `test -f 'uint-spbset.c' || echo '$(srcdir)/'`uint-spbset.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-uint-spbset.Tpo $(DEPDIR)/libcurl_la-uint-spbset.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='uint-spbset.c' object='libcurl_la-uint-spbset.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@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-uint-spbset.lo `test -f 'uint-spbset.c' || echo '$(srcdir)/'`uint-spbset.c
+
+libcurl_la-uint-table.lo: uint-table.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-uint-table.lo -MD -MP -MF $(DEPDIR)/libcurl_la-uint-table.Tpo -c -o libcurl_la-uint-table.lo `test -f 'uint-table.c' || echo '$(srcdir)/'`uint-table.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-uint-table.Tpo $(DEPDIR)/libcurl_la-uint-table.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='uint-table.c' object='libcurl_la-uint-table.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@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-uint-table.lo `test -f 'uint-table.c' || echo '$(srcdir)/'`uint-table.c
+
libcurl_la-url.lo: url.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-url.lo -MD -MP -MF $(DEPDIR)/libcurl_la-url.Tpo -c -o libcurl_la-url.lo `test -f 'url.c' || echo '$(srcdir)/'`url.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-url.Tpo $(DEPDIR)/libcurl_la-url.Plo
@@ -3228,20 +3304,6 @@ libcurl_la-version.lo: version.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-version.lo `test -f 'version.c' || echo '$(srcdir)/'`version.c
-libcurl_la-version_win32.lo: version_win32.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-version_win32.lo -MD -MP -MF $(DEPDIR)/libcurl_la-version_win32.Tpo -c -o libcurl_la-version_win32.lo `test -f 'version_win32.c' || echo '$(srcdir)/'`version_win32.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-version_win32.Tpo $(DEPDIR)/libcurl_la-version_win32.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='version_win32.c' object='libcurl_la-version_win32.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-version_win32.lo `test -f 'version_win32.c' || echo '$(srcdir)/'`version_win32.c
-
-libcurl_la-warnless.lo: warnless.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-warnless.lo -MD -MP -MF $(DEPDIR)/libcurl_la-warnless.Tpo -c -o libcurl_la-warnless.lo `test -f 'warnless.c' || echo '$(srcdir)/'`warnless.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-warnless.Tpo $(DEPDIR)/libcurl_la-warnless.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='warnless.c' object='libcurl_la-warnless.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-warnless.lo `test -f 'warnless.c' || echo '$(srcdir)/'`warnless.c
-
libcurl_la-ws.lo: ws.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-ws.lo -MD -MP -MF $(DEPDIR)/libcurl_la-ws.Tpo -c -o libcurl_la-ws.lo `test -f 'ws.c' || echo '$(srcdir)/'`ws.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-ws.Tpo $(DEPDIR)/libcurl_la-ws.Plo
@@ -3431,6 +3493,20 @@ vtls/libcurl_la-vtls.lo: vtls/vtls.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 vtls/libcurl_la-vtls.lo `test -f 'vtls/vtls.c' || echo '$(srcdir)/'`vtls/vtls.c
+vtls/libcurl_la-vtls_scache.lo: vtls/vtls_scache.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-vtls_scache.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-vtls_scache.Tpo -c -o vtls/libcurl_la-vtls_scache.lo `test -f 'vtls/vtls_scache.c' || echo '$(srcdir)/'`vtls/vtls_scache.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-vtls_scache.Tpo vtls/$(DEPDIR)/libcurl_la-vtls_scache.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/vtls_scache.c' object='vtls/libcurl_la-vtls_scache.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-vtls_scache.lo `test -f 'vtls/vtls_scache.c' || echo '$(srcdir)/'`vtls/vtls_scache.c
+
+vtls/libcurl_la-vtls_spack.lo: vtls/vtls_spack.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-vtls_spack.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-vtls_spack.Tpo -c -o vtls/libcurl_la-vtls_spack.lo `test -f 'vtls/vtls_spack.c' || echo '$(srcdir)/'`vtls/vtls_spack.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-vtls_spack.Tpo vtls/$(DEPDIR)/libcurl_la-vtls_spack.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/vtls_spack.c' object='vtls/libcurl_la-vtls_spack.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurl_la-vtls_spack.lo `test -f 'vtls/vtls_spack.c' || echo '$(srcdir)/'`vtls/vtls_spack.c
+
vtls/libcurl_la-wolfssl.lo: vtls/wolfssl.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT vtls/libcurl_la-wolfssl.lo -MD -MP -MF vtls/$(DEPDIR)/libcurl_la-wolfssl.Tpo -c -o vtls/libcurl_la-wolfssl.lo `test -f 'vtls/wolfssl.c' || echo '$(srcdir)/'`vtls/wolfssl.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurl_la-wolfssl.Tpo vtls/$(DEPDIR)/libcurl_la-wolfssl.Plo
@@ -3515,6 +3591,90 @@ vssh/libcurl_la-wolfssh.lo: vssh/wolfssh.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 vssh/libcurl_la-wolfssh.lo `test -f 'vssh/wolfssh.c' || echo '$(srcdir)/'`vssh/wolfssh.c
+curlx/libcurl_la-base64.lo: curlx/base64.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT curlx/libcurl_la-base64.lo -MD -MP -MF curlx/$(DEPDIR)/libcurl_la-base64.Tpo -c -o curlx/libcurl_la-base64.lo `test -f 'curlx/base64.c' || echo '$(srcdir)/'`curlx/base64.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurl_la-base64.Tpo curlx/$(DEPDIR)/libcurl_la-base64.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curlx/base64.c' object='curlx/libcurl_la-base64.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurl_la-base64.lo `test -f 'curlx/base64.c' || echo '$(srcdir)/'`curlx/base64.c
+
+curlx/libcurl_la-dynbuf.lo: curlx/dynbuf.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT curlx/libcurl_la-dynbuf.lo -MD -MP -MF curlx/$(DEPDIR)/libcurl_la-dynbuf.Tpo -c -o curlx/libcurl_la-dynbuf.lo `test -f 'curlx/dynbuf.c' || echo '$(srcdir)/'`curlx/dynbuf.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurl_la-dynbuf.Tpo curlx/$(DEPDIR)/libcurl_la-dynbuf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curlx/dynbuf.c' object='curlx/libcurl_la-dynbuf.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurl_la-dynbuf.lo `test -f 'curlx/dynbuf.c' || echo '$(srcdir)/'`curlx/dynbuf.c
+
+curlx/libcurl_la-inet_pton.lo: curlx/inet_pton.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT curlx/libcurl_la-inet_pton.lo -MD -MP -MF curlx/$(DEPDIR)/libcurl_la-inet_pton.Tpo -c -o curlx/libcurl_la-inet_pton.lo `test -f 'curlx/inet_pton.c' || echo '$(srcdir)/'`curlx/inet_pton.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurl_la-inet_pton.Tpo curlx/$(DEPDIR)/libcurl_la-inet_pton.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curlx/inet_pton.c' object='curlx/libcurl_la-inet_pton.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurl_la-inet_pton.lo `test -f 'curlx/inet_pton.c' || echo '$(srcdir)/'`curlx/inet_pton.c
+
+curlx/libcurl_la-multibyte.lo: curlx/multibyte.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT curlx/libcurl_la-multibyte.lo -MD -MP -MF curlx/$(DEPDIR)/libcurl_la-multibyte.Tpo -c -o curlx/libcurl_la-multibyte.lo `test -f 'curlx/multibyte.c' || echo '$(srcdir)/'`curlx/multibyte.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurl_la-multibyte.Tpo curlx/$(DEPDIR)/libcurl_la-multibyte.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curlx/multibyte.c' object='curlx/libcurl_la-multibyte.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurl_la-multibyte.lo `test -f 'curlx/multibyte.c' || echo '$(srcdir)/'`curlx/multibyte.c
+
+curlx/libcurl_la-nonblock.lo: curlx/nonblock.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT curlx/libcurl_la-nonblock.lo -MD -MP -MF curlx/$(DEPDIR)/libcurl_la-nonblock.Tpo -c -o curlx/libcurl_la-nonblock.lo `test -f 'curlx/nonblock.c' || echo '$(srcdir)/'`curlx/nonblock.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurl_la-nonblock.Tpo curlx/$(DEPDIR)/libcurl_la-nonblock.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curlx/nonblock.c' object='curlx/libcurl_la-nonblock.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurl_la-nonblock.lo `test -f 'curlx/nonblock.c' || echo '$(srcdir)/'`curlx/nonblock.c
+
+curlx/libcurl_la-strparse.lo: curlx/strparse.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 curlx/libcurl_la-strparse.lo -MD -MP -MF curlx/$(DEPDIR)/libcurl_la-strparse.Tpo -c -o curlx/libcurl_la-strparse.lo `test -f 'curlx/strparse.c' || echo '$(srcdir)/'`curlx/strparse.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurl_la-strparse.Tpo curlx/$(DEPDIR)/libcurl_la-strparse.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curlx/strparse.c' object='curlx/libcurl_la-strparse.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@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 curlx/libcurl_la-strparse.lo `test -f 'curlx/strparse.c' || echo '$(srcdir)/'`curlx/strparse.c
+
+curlx/libcurl_la-timediff.lo: curlx/timediff.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT curlx/libcurl_la-timediff.lo -MD -MP -MF curlx/$(DEPDIR)/libcurl_la-timediff.Tpo -c -o curlx/libcurl_la-timediff.lo `test -f 'curlx/timediff.c' || echo '$(srcdir)/'`curlx/timediff.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurl_la-timediff.Tpo curlx/$(DEPDIR)/libcurl_la-timediff.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curlx/timediff.c' object='curlx/libcurl_la-timediff.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurl_la-timediff.lo `test -f 'curlx/timediff.c' || echo '$(srcdir)/'`curlx/timediff.c
+
+curlx/libcurl_la-timeval.lo: curlx/timeval.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT curlx/libcurl_la-timeval.lo -MD -MP -MF curlx/$(DEPDIR)/libcurl_la-timeval.Tpo -c -o curlx/libcurl_la-timeval.lo `test -f 'curlx/timeval.c' || echo '$(srcdir)/'`curlx/timeval.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurl_la-timeval.Tpo curlx/$(DEPDIR)/libcurl_la-timeval.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curlx/timeval.c' object='curlx/libcurl_la-timeval.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurl_la-timeval.lo `test -f 'curlx/timeval.c' || echo '$(srcdir)/'`curlx/timeval.c
+
+curlx/libcurl_la-version_win32.lo: curlx/version_win32.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT curlx/libcurl_la-version_win32.lo -MD -MP -MF curlx/$(DEPDIR)/libcurl_la-version_win32.Tpo -c -o curlx/libcurl_la-version_win32.lo `test -f 'curlx/version_win32.c' || echo '$(srcdir)/'`curlx/version_win32.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurl_la-version_win32.Tpo curlx/$(DEPDIR)/libcurl_la-version_win32.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curlx/version_win32.c' object='curlx/libcurl_la-version_win32.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurl_la-version_win32.lo `test -f 'curlx/version_win32.c' || echo '$(srcdir)/'`curlx/version_win32.c
+
+curlx/libcurl_la-warnless.lo: curlx/warnless.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT curlx/libcurl_la-warnless.lo -MD -MP -MF curlx/$(DEPDIR)/libcurl_la-warnless.Tpo -c -o curlx/libcurl_la-warnless.lo `test -f 'curlx/warnless.c' || echo '$(srcdir)/'`curlx/warnless.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurl_la-warnless.Tpo curlx/$(DEPDIR)/libcurl_la-warnless.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curlx/warnless.c' object='curlx/libcurl_la-warnless.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurl_la-warnless.lo `test -f 'curlx/warnless.c' || echo '$(srcdir)/'`curlx/warnless.c
+
+curlx/libcurl_la-winapi.lo: curlx/winapi.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 curlx/libcurl_la-winapi.lo -MD -MP -MF curlx/$(DEPDIR)/libcurl_la-winapi.Tpo -c -o curlx/libcurl_la-winapi.lo `test -f 'curlx/winapi.c' || echo '$(srcdir)/'`curlx/winapi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurl_la-winapi.Tpo curlx/$(DEPDIR)/libcurl_la-winapi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curlx/winapi.c' object='curlx/libcurl_la-winapi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@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 curlx/libcurl_la-winapi.lo `test -f 'curlx/winapi.c' || echo '$(srcdir)/'`curlx/winapi.c
+
+libcurl_la-dllmain.lo: dllmain.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -MT libcurl_la-dllmain.lo -MD -MP -MF $(DEPDIR)/libcurl_la-dllmain.Tpo -c -o libcurl_la-dllmain.lo `test -f 'dllmain.c' || echo '$(srcdir)/'`dllmain.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-dllmain.Tpo $(DEPDIR)/libcurl_la-dllmain.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dllmain.c' object='libcurl_la-dllmain.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurl_la_CPPFLAGS) $(CPPFLAGS) $(libcurl_la_CFLAGS) $(CFLAGS) -c -o libcurl_la-dllmain.lo `test -f 'dllmain.c' || echo '$(srcdir)/'`dllmain.c
+
libcurl_la-libcurl_unity.lo: libcurl_unity.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-libcurl_unity.lo -MD -MP -MF $(DEPDIR)/libcurl_la-libcurl_unity.Tpo -c -o libcurl_la-libcurl_unity.lo `test -f 'libcurl_unity.c' || echo '$(srcdir)/'`libcurl_unity.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurl_la-libcurl_unity.Tpo $(DEPDIR)/libcurl_la-libcurl_unity.Plo
@@ -3543,19 +3703,19 @@ libcurlu_la-asyn-ares.lo: asyn-ares.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-asyn-ares.lo `test -f 'asyn-ares.c' || echo '$(srcdir)/'`asyn-ares.c
-libcurlu_la-asyn-thread.lo: asyn-thread.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-asyn-thread.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-asyn-thread.Tpo -c -o libcurlu_la-asyn-thread.lo `test -f 'asyn-thread.c' || echo '$(srcdir)/'`asyn-thread.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-asyn-thread.Tpo $(DEPDIR)/libcurlu_la-asyn-thread.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asyn-thread.c' object='libcurlu_la-asyn-thread.lo' libtool=yes @AMDEPBACKSLASH@
+libcurlu_la-asyn-base.lo: asyn-base.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-asyn-base.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-asyn-base.Tpo -c -o libcurlu_la-asyn-base.lo `test -f 'asyn-base.c' || echo '$(srcdir)/'`asyn-base.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-asyn-base.Tpo $(DEPDIR)/libcurlu_la-asyn-base.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asyn-base.c' object='libcurlu_la-asyn-base.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-asyn-thread.lo `test -f 'asyn-thread.c' || echo '$(srcdir)/'`asyn-thread.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-asyn-base.lo `test -f 'asyn-base.c' || echo '$(srcdir)/'`asyn-base.c
-libcurlu_la-base64.lo: base64.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-base64.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-base64.Tpo -c -o libcurlu_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-base64.Tpo $(DEPDIR)/libcurlu_la-base64.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='base64.c' object='libcurlu_la-base64.lo' libtool=yes @AMDEPBACKSLASH@
+libcurlu_la-asyn-thrdd.lo: asyn-thrdd.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-asyn-thrdd.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-asyn-thrdd.Tpo -c -o libcurlu_la-asyn-thrdd.lo `test -f 'asyn-thrdd.c' || echo '$(srcdir)/'`asyn-thrdd.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-asyn-thrdd.Tpo $(DEPDIR)/libcurlu_la-asyn-thrdd.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='asyn-thrdd.c' object='libcurlu_la-asyn-thrdd.lo' libtool=yes @AMDEPBACKSLASH@
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-base64.lo `test -f 'base64.c' || echo '$(srcdir)/'`base64.c
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-asyn-thrdd.lo `test -f 'asyn-thrdd.c' || echo '$(srcdir)/'`asyn-thrdd.c
libcurlu_la-bufq.lo: bufq.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-bufq.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-bufq.Tpo -c -o libcurlu_la-bufq.lo `test -f 'bufq.c' || echo '$(srcdir)/'`bufq.c
@@ -3571,13 +3731,6 @@ libcurlu_la-bufref.lo: bufref.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-bufref.lo `test -f 'bufref.c' || echo '$(srcdir)/'`bufref.c
-libcurlu_la-c-hyper.lo: c-hyper.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-c-hyper.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-c-hyper.Tpo -c -o libcurlu_la-c-hyper.lo `test -f 'c-hyper.c' || echo '$(srcdir)/'`c-hyper.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-c-hyper.Tpo $(DEPDIR)/libcurlu_la-c-hyper.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='c-hyper.c' object='libcurlu_la-c-hyper.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-c-hyper.lo `test -f 'c-hyper.c' || echo '$(srcdir)/'`c-hyper.c
-
libcurlu_la-cf-h1-proxy.lo: cf-h1-proxy.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-cf-h1-proxy.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-cf-h1-proxy.Tpo -c -o libcurlu_la-cf-h1-proxy.lo `test -f 'cf-h1-proxy.c' || echo '$(srcdir)/'`cf-h1-proxy.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-cf-h1-proxy.Tpo $(DEPDIR)/libcurlu_la-cf-h1-proxy.Plo
@@ -3648,6 +3801,13 @@ libcurlu_la-cookie.lo: cookie.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-cookie.lo `test -f 'cookie.c' || echo '$(srcdir)/'`cookie.c
+libcurlu_la-cshutdn.lo: cshutdn.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-cshutdn.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-cshutdn.Tpo -c -o libcurlu_la-cshutdn.lo `test -f 'cshutdn.c' || echo '$(srcdir)/'`cshutdn.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-cshutdn.Tpo $(DEPDIR)/libcurlu_la-cshutdn.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cshutdn.c' object='libcurlu_la-cshutdn.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@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-cshutdn.lo `test -f 'cshutdn.c' || echo '$(srcdir)/'`cshutdn.c
+
libcurlu_la-curl_addrinfo.lo: curl_addrinfo.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_addrinfo.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_addrinfo.Tpo -c -o libcurlu_la-curl_addrinfo.lo `test -f 'curl_addrinfo.c' || echo '$(srcdir)/'`curl_addrinfo.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_addrinfo.Tpo $(DEPDIR)/libcurlu_la-curl_addrinfo.Plo
@@ -3704,13 +3864,6 @@ libcurlu_la-curl_memrchr.lo: curl_memrchr.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-curl_memrchr.lo `test -f 'curl_memrchr.c' || echo '$(srcdir)/'`curl_memrchr.c
-libcurlu_la-curl_multibyte.lo: curl_multibyte.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_multibyte.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_multibyte.Tpo -c -o libcurlu_la-curl_multibyte.lo `test -f 'curl_multibyte.c' || echo '$(srcdir)/'`curl_multibyte.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_multibyte.Tpo $(DEPDIR)/libcurlu_la-curl_multibyte.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curl_multibyte.c' object='libcurlu_la-curl_multibyte.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-curl_multibyte.lo `test -f 'curl_multibyte.c' || echo '$(srcdir)/'`curl_multibyte.c
-
libcurlu_la-curl_ntlm_core.lo: curl_ntlm_core.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-curl_ntlm_core.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-curl_ntlm_core.Tpo -c -o libcurlu_la-curl_ntlm_core.lo `test -f 'curl_ntlm_core.c' || echo '$(srcdir)/'`curl_ntlm_core.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-curl_ntlm_core.Tpo $(DEPDIR)/libcurlu_la-curl_ntlm_core.Plo
@@ -3774,6 +3927,13 @@ libcurlu_la-cw-out.lo: cw-out.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-cw-out.lo `test -f 'cw-out.c' || echo '$(srcdir)/'`cw-out.c
+libcurlu_la-cw-pause.lo: cw-pause.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-cw-pause.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-cw-pause.Tpo -c -o libcurlu_la-cw-pause.lo `test -f 'cw-pause.c' || echo '$(srcdir)/'`cw-pause.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-cw-pause.Tpo $(DEPDIR)/libcurlu_la-cw-pause.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cw-pause.c' object='libcurlu_la-cw-pause.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-cw-pause.lo `test -f 'cw-pause.c' || echo '$(srcdir)/'`cw-pause.c
+
libcurlu_la-dict.lo: dict.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-dict.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-dict.Tpo -c -o libcurlu_la-dict.lo `test -f 'dict.c' || echo '$(srcdir)/'`dict.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-dict.Tpo $(DEPDIR)/libcurlu_la-dict.Plo
@@ -3781,13 +3941,6 @@ libcurlu_la-dict.lo: dict.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-dict.lo `test -f 'dict.c' || echo '$(srcdir)/'`dict.c
-libcurlu_la-dllmain.lo: dllmain.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-dllmain.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-dllmain.Tpo -c -o libcurlu_la-dllmain.lo `test -f 'dllmain.c' || echo '$(srcdir)/'`dllmain.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-dllmain.Tpo $(DEPDIR)/libcurlu_la-dllmain.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dllmain.c' object='libcurlu_la-dllmain.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-dllmain.lo `test -f 'dllmain.c' || echo '$(srcdir)/'`dllmain.c
-
libcurlu_la-doh.lo: doh.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-doh.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-doh.Tpo -c -o libcurlu_la-doh.lo `test -f 'doh.c' || echo '$(srcdir)/'`doh.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-doh.Tpo $(DEPDIR)/libcurlu_la-doh.Plo
@@ -3795,13 +3948,6 @@ libcurlu_la-doh.lo: doh.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-doh.lo `test -f 'doh.c' || echo '$(srcdir)/'`doh.c
-libcurlu_la-dynbuf.lo: dynbuf.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-dynbuf.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-dynbuf.Tpo -c -o libcurlu_la-dynbuf.lo `test -f 'dynbuf.c' || echo '$(srcdir)/'`dynbuf.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-dynbuf.Tpo $(DEPDIR)/libcurlu_la-dynbuf.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dynbuf.c' object='libcurlu_la-dynbuf.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-dynbuf.lo `test -f 'dynbuf.c' || echo '$(srcdir)/'`dynbuf.c
-
libcurlu_la-dynhds.lo: dynhds.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-dynhds.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-dynhds.Tpo -c -o libcurlu_la-dynhds.lo `test -f 'dynhds.c' || echo '$(srcdir)/'`dynhds.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-dynhds.Tpo $(DEPDIR)/libcurlu_la-dynhds.Plo
@@ -3837,6 +3983,13 @@ libcurlu_la-escape.lo: escape.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-escape.lo `test -f 'escape.c' || echo '$(srcdir)/'`escape.c
+libcurlu_la-fake_addrinfo.lo: fake_addrinfo.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-fake_addrinfo.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-fake_addrinfo.Tpo -c -o libcurlu_la-fake_addrinfo.lo `test -f 'fake_addrinfo.c' || echo '$(srcdir)/'`fake_addrinfo.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-fake_addrinfo.Tpo $(DEPDIR)/libcurlu_la-fake_addrinfo.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fake_addrinfo.c' object='libcurlu_la-fake_addrinfo.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-fake_addrinfo.lo `test -f 'fake_addrinfo.c' || echo '$(srcdir)/'`fake_addrinfo.c
+
libcurlu_la-file.lo: file.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-file.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-file.Tpo -c -o libcurlu_la-file.lo `test -f 'file.c' || echo '$(srcdir)/'`file.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-file.Tpo $(DEPDIR)/libcurlu_la-file.Plo
@@ -3921,13 +4074,6 @@ libcurlu_la-hmac.lo: hmac.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-hmac.lo `test -f 'hmac.c' || echo '$(srcdir)/'`hmac.c
-libcurlu_la-hostasyn.lo: hostasyn.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hostasyn.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hostasyn.Tpo -c -o libcurlu_la-hostasyn.lo `test -f 'hostasyn.c' || echo '$(srcdir)/'`hostasyn.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hostasyn.Tpo $(DEPDIR)/libcurlu_la-hostasyn.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostasyn.c' object='libcurlu_la-hostasyn.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hostasyn.lo `test -f 'hostasyn.c' || echo '$(srcdir)/'`hostasyn.c
-
libcurlu_la-hostip.lo: hostip.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hostip.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hostip.Tpo -c -o libcurlu_la-hostip.lo `test -f 'hostip.c' || echo '$(srcdir)/'`hostip.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hostip.Tpo $(DEPDIR)/libcurlu_la-hostip.Plo
@@ -3949,13 +4095,6 @@ libcurlu_la-hostip6.lo: hostip6.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-hostip6.lo `test -f 'hostip6.c' || echo '$(srcdir)/'`hostip6.c
-libcurlu_la-hostsyn.lo: hostsyn.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hostsyn.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hostsyn.Tpo -c -o libcurlu_la-hostsyn.lo `test -f 'hostsyn.c' || echo '$(srcdir)/'`hostsyn.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hostsyn.Tpo $(DEPDIR)/libcurlu_la-hostsyn.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hostsyn.c' object='libcurlu_la-hostsyn.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-hostsyn.lo `test -f 'hostsyn.c' || echo '$(srcdir)/'`hostsyn.c
-
libcurlu_la-hsts.lo: hsts.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-hsts.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-hsts.Tpo -c -o libcurlu_la-hsts.lo `test -f 'hsts.c' || echo '$(srcdir)/'`hsts.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-hsts.Tpo $(DEPDIR)/libcurlu_la-hsts.Plo
@@ -4026,6 +4165,13 @@ libcurlu_la-http_proxy.lo: http_proxy.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-http_proxy.lo `test -f 'http_proxy.c' || echo '$(srcdir)/'`http_proxy.c
+libcurlu_la-httpsrr.lo: httpsrr.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-httpsrr.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-httpsrr.Tpo -c -o libcurlu_la-httpsrr.lo `test -f 'httpsrr.c' || echo '$(srcdir)/'`httpsrr.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-httpsrr.Tpo $(DEPDIR)/libcurlu_la-httpsrr.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='httpsrr.c' object='libcurlu_la-httpsrr.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@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-httpsrr.lo `test -f 'httpsrr.c' || echo '$(srcdir)/'`httpsrr.c
+
libcurlu_la-idn.lo: idn.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-idn.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-idn.Tpo -c -o libcurlu_la-idn.lo `test -f 'idn.c' || echo '$(srcdir)/'`idn.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-idn.Tpo $(DEPDIR)/libcurlu_la-idn.Plo
@@ -4054,13 +4200,6 @@ libcurlu_la-inet_ntop.lo: inet_ntop.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-inet_ntop.lo `test -f 'inet_ntop.c' || echo '$(srcdir)/'`inet_ntop.c
-libcurlu_la-inet_pton.lo: inet_pton.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-inet_pton.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-inet_pton.Tpo -c -o libcurlu_la-inet_pton.lo `test -f 'inet_pton.c' || echo '$(srcdir)/'`inet_pton.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-inet_pton.Tpo $(DEPDIR)/libcurlu_la-inet_pton.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='inet_pton.c' object='libcurlu_la-inet_pton.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-inet_pton.lo `test -f 'inet_pton.c' || echo '$(srcdir)/'`inet_pton.c
-
libcurlu_la-krb5.lo: krb5.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-krb5.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-krb5.Tpo -c -o libcurlu_la-krb5.lo `test -f 'krb5.c' || echo '$(srcdir)/'`krb5.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-krb5.Tpo $(DEPDIR)/libcurlu_la-krb5.Plo
@@ -4138,6 +4277,13 @@ libcurlu_la-multi.lo: multi.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-multi.lo `test -f 'multi.c' || echo '$(srcdir)/'`multi.c
+libcurlu_la-multi_ev.lo: multi_ev.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-multi_ev.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-multi_ev.Tpo -c -o libcurlu_la-multi_ev.lo `test -f 'multi_ev.c' || echo '$(srcdir)/'`multi_ev.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-multi_ev.Tpo $(DEPDIR)/libcurlu_la-multi_ev.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='multi_ev.c' object='libcurlu_la-multi_ev.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-multi_ev.lo `test -f 'multi_ev.c' || echo '$(srcdir)/'`multi_ev.c
+
libcurlu_la-netrc.lo: netrc.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-netrc.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-netrc.Tpo -c -o libcurlu_la-netrc.lo `test -f 'netrc.c' || echo '$(srcdir)/'`netrc.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-netrc.Tpo $(DEPDIR)/libcurlu_la-netrc.Plo
@@ -4145,13 +4291,6 @@ libcurlu_la-netrc.lo: netrc.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-netrc.lo `test -f 'netrc.c' || echo '$(srcdir)/'`netrc.c
-libcurlu_la-nonblock.lo: nonblock.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-nonblock.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-nonblock.Tpo -c -o libcurlu_la-nonblock.lo `test -f 'nonblock.c' || echo '$(srcdir)/'`nonblock.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-nonblock.Tpo $(DEPDIR)/libcurlu_la-nonblock.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nonblock.c' object='libcurlu_la-nonblock.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-nonblock.lo `test -f 'nonblock.c' || echo '$(srcdir)/'`nonblock.c
-
libcurlu_la-noproxy.lo: noproxy.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-noproxy.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-noproxy.Tpo -c -o libcurlu_la-noproxy.lo `test -f 'noproxy.c' || echo '$(srcdir)/'`noproxy.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-noproxy.Tpo $(DEPDIR)/libcurlu_la-noproxy.Plo
@@ -4341,6 +4480,13 @@ libcurlu_la-strdup.lo: strdup.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-strdup.lo `test -f 'strdup.c' || echo '$(srcdir)/'`strdup.c
+libcurlu_la-strequal.lo: strequal.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-strequal.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-strequal.Tpo -c -o libcurlu_la-strequal.lo `test -f 'strequal.c' || echo '$(srcdir)/'`strequal.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-strequal.Tpo $(DEPDIR)/libcurlu_la-strequal.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strequal.c' object='libcurlu_la-strequal.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@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-strequal.lo `test -f 'strequal.c' || echo '$(srcdir)/'`strequal.c
+
libcurlu_la-strerror.lo: strerror.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-strerror.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-strerror.Tpo -c -o libcurlu_la-strerror.lo `test -f 'strerror.c' || echo '$(srcdir)/'`strerror.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-strerror.Tpo $(DEPDIR)/libcurlu_la-strerror.Plo
@@ -4348,20 +4494,6 @@ libcurlu_la-strerror.lo: strerror.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-strerror.lo `test -f 'strerror.c' || echo '$(srcdir)/'`strerror.c
-libcurlu_la-strtok.lo: strtok.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-strtok.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-strtok.Tpo -c -o libcurlu_la-strtok.lo `test -f 'strtok.c' || echo '$(srcdir)/'`strtok.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-strtok.Tpo $(DEPDIR)/libcurlu_la-strtok.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strtok.c' object='libcurlu_la-strtok.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-strtok.lo `test -f 'strtok.c' || echo '$(srcdir)/'`strtok.c
-
-libcurlu_la-strtoofft.lo: strtoofft.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-strtoofft.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-strtoofft.Tpo -c -o libcurlu_la-strtoofft.lo `test -f 'strtoofft.c' || echo '$(srcdir)/'`strtoofft.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-strtoofft.Tpo $(DEPDIR)/libcurlu_la-strtoofft.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='strtoofft.c' object='libcurlu_la-strtoofft.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-strtoofft.lo `test -f 'strtoofft.c' || echo '$(srcdir)/'`strtoofft.c
-
libcurlu_la-system_win32.lo: system_win32.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-system_win32.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-system_win32.Tpo -c -o libcurlu_la-system_win32.lo `test -f 'system_win32.c' || echo '$(srcdir)/'`system_win32.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-system_win32.Tpo $(DEPDIR)/libcurlu_la-system_win32.Plo
@@ -4383,20 +4515,6 @@ libcurlu_la-tftp.lo: tftp.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-tftp.lo `test -f 'tftp.c' || echo '$(srcdir)/'`tftp.c
-libcurlu_la-timediff.lo: timediff.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-timediff.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-timediff.Tpo -c -o libcurlu_la-timediff.lo `test -f 'timediff.c' || echo '$(srcdir)/'`timediff.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-timediff.Tpo $(DEPDIR)/libcurlu_la-timediff.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='timediff.c' object='libcurlu_la-timediff.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-timediff.lo `test -f 'timediff.c' || echo '$(srcdir)/'`timediff.c
-
-libcurlu_la-timeval.lo: timeval.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-timeval.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-timeval.Tpo -c -o libcurlu_la-timeval.lo `test -f 'timeval.c' || echo '$(srcdir)/'`timeval.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-timeval.Tpo $(DEPDIR)/libcurlu_la-timeval.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='timeval.c' object='libcurlu_la-timeval.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-timeval.lo `test -f 'timeval.c' || echo '$(srcdir)/'`timeval.c
-
libcurlu_la-transfer.lo: transfer.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-transfer.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-transfer.Tpo -c -o libcurlu_la-transfer.lo `test -f 'transfer.c' || echo '$(srcdir)/'`transfer.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-transfer.Tpo $(DEPDIR)/libcurlu_la-transfer.Plo
@@ -4404,6 +4522,34 @@ libcurlu_la-transfer.lo: transfer.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-transfer.lo `test -f 'transfer.c' || echo '$(srcdir)/'`transfer.c
+libcurlu_la-uint-bset.lo: uint-bset.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-uint-bset.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-uint-bset.Tpo -c -o libcurlu_la-uint-bset.lo `test -f 'uint-bset.c' || echo '$(srcdir)/'`uint-bset.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-uint-bset.Tpo $(DEPDIR)/libcurlu_la-uint-bset.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='uint-bset.c' object='libcurlu_la-uint-bset.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@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-uint-bset.lo `test -f 'uint-bset.c' || echo '$(srcdir)/'`uint-bset.c
+
+libcurlu_la-uint-hash.lo: uint-hash.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-uint-hash.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-uint-hash.Tpo -c -o libcurlu_la-uint-hash.lo `test -f 'uint-hash.c' || echo '$(srcdir)/'`uint-hash.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-uint-hash.Tpo $(DEPDIR)/libcurlu_la-uint-hash.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='uint-hash.c' object='libcurlu_la-uint-hash.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-uint-hash.lo `test -f 'uint-hash.c' || echo '$(srcdir)/'`uint-hash.c
+
+libcurlu_la-uint-spbset.lo: uint-spbset.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-uint-spbset.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-uint-spbset.Tpo -c -o libcurlu_la-uint-spbset.lo `test -f 'uint-spbset.c' || echo '$(srcdir)/'`uint-spbset.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-uint-spbset.Tpo $(DEPDIR)/libcurlu_la-uint-spbset.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='uint-spbset.c' object='libcurlu_la-uint-spbset.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@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-uint-spbset.lo `test -f 'uint-spbset.c' || echo '$(srcdir)/'`uint-spbset.c
+
+libcurlu_la-uint-table.lo: uint-table.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-uint-table.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-uint-table.Tpo -c -o libcurlu_la-uint-table.lo `test -f 'uint-table.c' || echo '$(srcdir)/'`uint-table.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-uint-table.Tpo $(DEPDIR)/libcurlu_la-uint-table.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='uint-table.c' object='libcurlu_la-uint-table.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@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-uint-table.lo `test -f 'uint-table.c' || echo '$(srcdir)/'`uint-table.c
+
libcurlu_la-url.lo: url.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-url.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-url.Tpo -c -o libcurlu_la-url.lo `test -f 'url.c' || echo '$(srcdir)/'`url.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-url.Tpo $(DEPDIR)/libcurlu_la-url.Plo
@@ -4425,20 +4571,6 @@ libcurlu_la-version.lo: version.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-version.lo `test -f 'version.c' || echo '$(srcdir)/'`version.c
-libcurlu_la-version_win32.lo: version_win32.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-version_win32.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-version_win32.Tpo -c -o libcurlu_la-version_win32.lo `test -f 'version_win32.c' || echo '$(srcdir)/'`version_win32.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-version_win32.Tpo $(DEPDIR)/libcurlu_la-version_win32.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='version_win32.c' object='libcurlu_la-version_win32.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-version_win32.lo `test -f 'version_win32.c' || echo '$(srcdir)/'`version_win32.c
-
-libcurlu_la-warnless.lo: warnless.c
-@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-warnless.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-warnless.Tpo -c -o libcurlu_la-warnless.lo `test -f 'warnless.c' || echo '$(srcdir)/'`warnless.c
-@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-warnless.Tpo $(DEPDIR)/libcurlu_la-warnless.Plo
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='warnless.c' object='libcurlu_la-warnless.lo' libtool=yes @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-warnless.lo `test -f 'warnless.c' || echo '$(srcdir)/'`warnless.c
-
libcurlu_la-ws.lo: ws.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-ws.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-ws.Tpo -c -o libcurlu_la-ws.lo `test -f 'ws.c' || echo '$(srcdir)/'`ws.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-ws.Tpo $(DEPDIR)/libcurlu_la-ws.Plo
@@ -4628,6 +4760,20 @@ vtls/libcurlu_la-vtls.lo: vtls/vtls.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 vtls/libcurlu_la-vtls.lo `test -f 'vtls/vtls.c' || echo '$(srcdir)/'`vtls/vtls.c
+vtls/libcurlu_la-vtls_scache.lo: vtls/vtls_scache.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-vtls_scache.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-vtls_scache.Tpo -c -o vtls/libcurlu_la-vtls_scache.lo `test -f 'vtls/vtls_scache.c' || echo '$(srcdir)/'`vtls/vtls_scache.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-vtls_scache.Tpo vtls/$(DEPDIR)/libcurlu_la-vtls_scache.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/vtls_scache.c' object='vtls/libcurlu_la-vtls_scache.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-vtls_scache.lo `test -f 'vtls/vtls_scache.c' || echo '$(srcdir)/'`vtls/vtls_scache.c
+
+vtls/libcurlu_la-vtls_spack.lo: vtls/vtls_spack.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-vtls_spack.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-vtls_spack.Tpo -c -o vtls/libcurlu_la-vtls_spack.lo `test -f 'vtls/vtls_spack.c' || echo '$(srcdir)/'`vtls/vtls_spack.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-vtls_spack.Tpo vtls/$(DEPDIR)/libcurlu_la-vtls_spack.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='vtls/vtls_spack.c' object='vtls/libcurlu_la-vtls_spack.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o vtls/libcurlu_la-vtls_spack.lo `test -f 'vtls/vtls_spack.c' || echo '$(srcdir)/'`vtls/vtls_spack.c
+
vtls/libcurlu_la-wolfssl.lo: vtls/wolfssl.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT vtls/libcurlu_la-wolfssl.lo -MD -MP -MF vtls/$(DEPDIR)/libcurlu_la-wolfssl.Tpo -c -o vtls/libcurlu_la-wolfssl.lo `test -f 'vtls/wolfssl.c' || echo '$(srcdir)/'`vtls/wolfssl.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) vtls/$(DEPDIR)/libcurlu_la-wolfssl.Tpo vtls/$(DEPDIR)/libcurlu_la-wolfssl.Plo
@@ -4712,6 +4858,90 @@ vssh/libcurlu_la-wolfssh.lo: vssh/wolfssh.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 vssh/libcurlu_la-wolfssh.lo `test -f 'vssh/wolfssh.c' || echo '$(srcdir)/'`vssh/wolfssh.c
+curlx/libcurlu_la-base64.lo: curlx/base64.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT curlx/libcurlu_la-base64.lo -MD -MP -MF curlx/$(DEPDIR)/libcurlu_la-base64.Tpo -c -o curlx/libcurlu_la-base64.lo `test -f 'curlx/base64.c' || echo '$(srcdir)/'`curlx/base64.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurlu_la-base64.Tpo curlx/$(DEPDIR)/libcurlu_la-base64.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curlx/base64.c' object='curlx/libcurlu_la-base64.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurlu_la-base64.lo `test -f 'curlx/base64.c' || echo '$(srcdir)/'`curlx/base64.c
+
+curlx/libcurlu_la-dynbuf.lo: curlx/dynbuf.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT curlx/libcurlu_la-dynbuf.lo -MD -MP -MF curlx/$(DEPDIR)/libcurlu_la-dynbuf.Tpo -c -o curlx/libcurlu_la-dynbuf.lo `test -f 'curlx/dynbuf.c' || echo '$(srcdir)/'`curlx/dynbuf.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurlu_la-dynbuf.Tpo curlx/$(DEPDIR)/libcurlu_la-dynbuf.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curlx/dynbuf.c' object='curlx/libcurlu_la-dynbuf.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurlu_la-dynbuf.lo `test -f 'curlx/dynbuf.c' || echo '$(srcdir)/'`curlx/dynbuf.c
+
+curlx/libcurlu_la-inet_pton.lo: curlx/inet_pton.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT curlx/libcurlu_la-inet_pton.lo -MD -MP -MF curlx/$(DEPDIR)/libcurlu_la-inet_pton.Tpo -c -o curlx/libcurlu_la-inet_pton.lo `test -f 'curlx/inet_pton.c' || echo '$(srcdir)/'`curlx/inet_pton.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurlu_la-inet_pton.Tpo curlx/$(DEPDIR)/libcurlu_la-inet_pton.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curlx/inet_pton.c' object='curlx/libcurlu_la-inet_pton.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurlu_la-inet_pton.lo `test -f 'curlx/inet_pton.c' || echo '$(srcdir)/'`curlx/inet_pton.c
+
+curlx/libcurlu_la-multibyte.lo: curlx/multibyte.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT curlx/libcurlu_la-multibyte.lo -MD -MP -MF curlx/$(DEPDIR)/libcurlu_la-multibyte.Tpo -c -o curlx/libcurlu_la-multibyte.lo `test -f 'curlx/multibyte.c' || echo '$(srcdir)/'`curlx/multibyte.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurlu_la-multibyte.Tpo curlx/$(DEPDIR)/libcurlu_la-multibyte.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curlx/multibyte.c' object='curlx/libcurlu_la-multibyte.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurlu_la-multibyte.lo `test -f 'curlx/multibyte.c' || echo '$(srcdir)/'`curlx/multibyte.c
+
+curlx/libcurlu_la-nonblock.lo: curlx/nonblock.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT curlx/libcurlu_la-nonblock.lo -MD -MP -MF curlx/$(DEPDIR)/libcurlu_la-nonblock.Tpo -c -o curlx/libcurlu_la-nonblock.lo `test -f 'curlx/nonblock.c' || echo '$(srcdir)/'`curlx/nonblock.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurlu_la-nonblock.Tpo curlx/$(DEPDIR)/libcurlu_la-nonblock.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curlx/nonblock.c' object='curlx/libcurlu_la-nonblock.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurlu_la-nonblock.lo `test -f 'curlx/nonblock.c' || echo '$(srcdir)/'`curlx/nonblock.c
+
+curlx/libcurlu_la-strparse.lo: curlx/strparse.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 curlx/libcurlu_la-strparse.lo -MD -MP -MF curlx/$(DEPDIR)/libcurlu_la-strparse.Tpo -c -o curlx/libcurlu_la-strparse.lo `test -f 'curlx/strparse.c' || echo '$(srcdir)/'`curlx/strparse.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurlu_la-strparse.Tpo curlx/$(DEPDIR)/libcurlu_la-strparse.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curlx/strparse.c' object='curlx/libcurlu_la-strparse.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@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 curlx/libcurlu_la-strparse.lo `test -f 'curlx/strparse.c' || echo '$(srcdir)/'`curlx/strparse.c
+
+curlx/libcurlu_la-timediff.lo: curlx/timediff.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT curlx/libcurlu_la-timediff.lo -MD -MP -MF curlx/$(DEPDIR)/libcurlu_la-timediff.Tpo -c -o curlx/libcurlu_la-timediff.lo `test -f 'curlx/timediff.c' || echo '$(srcdir)/'`curlx/timediff.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurlu_la-timediff.Tpo curlx/$(DEPDIR)/libcurlu_la-timediff.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curlx/timediff.c' object='curlx/libcurlu_la-timediff.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurlu_la-timediff.lo `test -f 'curlx/timediff.c' || echo '$(srcdir)/'`curlx/timediff.c
+
+curlx/libcurlu_la-timeval.lo: curlx/timeval.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT curlx/libcurlu_la-timeval.lo -MD -MP -MF curlx/$(DEPDIR)/libcurlu_la-timeval.Tpo -c -o curlx/libcurlu_la-timeval.lo `test -f 'curlx/timeval.c' || echo '$(srcdir)/'`curlx/timeval.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurlu_la-timeval.Tpo curlx/$(DEPDIR)/libcurlu_la-timeval.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curlx/timeval.c' object='curlx/libcurlu_la-timeval.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurlu_la-timeval.lo `test -f 'curlx/timeval.c' || echo '$(srcdir)/'`curlx/timeval.c
+
+curlx/libcurlu_la-version_win32.lo: curlx/version_win32.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT curlx/libcurlu_la-version_win32.lo -MD -MP -MF curlx/$(DEPDIR)/libcurlu_la-version_win32.Tpo -c -o curlx/libcurlu_la-version_win32.lo `test -f 'curlx/version_win32.c' || echo '$(srcdir)/'`curlx/version_win32.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurlu_la-version_win32.Tpo curlx/$(DEPDIR)/libcurlu_la-version_win32.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curlx/version_win32.c' object='curlx/libcurlu_la-version_win32.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurlu_la-version_win32.lo `test -f 'curlx/version_win32.c' || echo '$(srcdir)/'`curlx/version_win32.c
+
+curlx/libcurlu_la-warnless.lo: curlx/warnless.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT curlx/libcurlu_la-warnless.lo -MD -MP -MF curlx/$(DEPDIR)/libcurlu_la-warnless.Tpo -c -o curlx/libcurlu_la-warnless.lo `test -f 'curlx/warnless.c' || echo '$(srcdir)/'`curlx/warnless.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurlu_la-warnless.Tpo curlx/$(DEPDIR)/libcurlu_la-warnless.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curlx/warnless.c' object='curlx/libcurlu_la-warnless.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o curlx/libcurlu_la-warnless.lo `test -f 'curlx/warnless.c' || echo '$(srcdir)/'`curlx/warnless.c
+
+curlx/libcurlu_la-winapi.lo: curlx/winapi.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 curlx/libcurlu_la-winapi.lo -MD -MP -MF curlx/$(DEPDIR)/libcurlu_la-winapi.Tpo -c -o curlx/libcurlu_la-winapi.lo `test -f 'curlx/winapi.c' || echo '$(srcdir)/'`curlx/winapi.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) curlx/$(DEPDIR)/libcurlu_la-winapi.Tpo curlx/$(DEPDIR)/libcurlu_la-winapi.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='curlx/winapi.c' object='curlx/libcurlu_la-winapi.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@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 curlx/libcurlu_la-winapi.lo `test -f 'curlx/winapi.c' || echo '$(srcdir)/'`curlx/winapi.c
+
+libcurlu_la-dllmain.lo: dllmain.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -MT libcurlu_la-dllmain.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-dllmain.Tpo -c -o libcurlu_la-dllmain.lo `test -f 'dllmain.c' || echo '$(srcdir)/'`dllmain.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-dllmain.Tpo $(DEPDIR)/libcurlu_la-dllmain.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dllmain.c' object='libcurlu_la-dllmain.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libcurlu_la_CPPFLAGS) $(CPPFLAGS) $(libcurlu_la_CFLAGS) $(CFLAGS) -c -o libcurlu_la-dllmain.lo `test -f 'dllmain.c' || echo '$(srcdir)/'`dllmain.c
+
libcurlu_la-libcurl_unity.lo: libcurl_unity.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-libcurl_unity.lo -MD -MP -MF $(DEPDIR)/libcurlu_la-libcurl_unity.Tpo -c -o libcurlu_la-libcurl_unity.lo `test -f 'libcurl_unity.c' || echo '$(srcdir)/'`libcurl_unity.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libcurlu_la-libcurl_unity.Tpo $(DEPDIR)/libcurlu_la-libcurl_unity.Plo
@@ -4724,6 +4954,7 @@ mostlyclean-libtool:
clean-libtool:
-rm -rf .libs _libs
+ -rm -rf curlx/.libs curlx/_libs
-rm -rf vauth/.libs vauth/_libs
-rm -rf vquic/.libs vquic/_libs
-rm -rf vssh/.libs vssh/_libs
@@ -4816,6 +5047,7 @@ distdir-am: $(DISTFILES)
check-am: all-am
check: check-am
@DEBUGBUILD_FALSE@all-local:
+@NOT_CURL_CI_FALSE@all-local:
all-am: Makefile $(LTLIBRARIES) curl_config.h all-local
installdirs:
for dir in "$(DESTDIR)$(libdir)"; do \
@@ -4848,6 +5080,8 @@ clean-generic:
distclean-generic:
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -rm -f curlx/$(DEPDIR)/$(am__dirstamp)
+ -rm -f curlx/$(am__dirstamp)
-rm -f vauth/$(DEPDIR)/$(am__dirstamp)
-rm -f vauth/$(am__dirstamp)
-rm -f vquic/$(DEPDIR)/$(am__dirstamp)
@@ -4869,11 +5103,10 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/libcurl_la-altsvc.Plo
-rm -f ./$(DEPDIR)/libcurl_la-amigaos.Plo
-rm -f ./$(DEPDIR)/libcurl_la-asyn-ares.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-asyn-thread.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-base64.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-asyn-base.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-asyn-thrdd.Plo
-rm -f ./$(DEPDIR)/libcurl_la-bufq.Plo
-rm -f ./$(DEPDIR)/libcurl_la-bufref.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-c-hyper.Plo
-rm -f ./$(DEPDIR)/libcurl_la-cf-h1-proxy.Plo
-rm -f ./$(DEPDIR)/libcurl_la-cf-h2-proxy.Plo
-rm -f ./$(DEPDIR)/libcurl_la-cf-haproxy.Plo
@@ -4884,6 +5117,7 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/libcurl_la-connect.Plo
-rm -f ./$(DEPDIR)/libcurl_la-content_encoding.Plo
-rm -f ./$(DEPDIR)/libcurl_la-cookie.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-cshutdn.Plo
-rm -f ./$(DEPDIR)/libcurl_la-curl_addrinfo.Plo
-rm -f ./$(DEPDIR)/libcurl_la-curl_des.Plo
-rm -f ./$(DEPDIR)/libcurl_la-curl_endian.Plo
@@ -4892,7 +5126,6 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/libcurl_la-curl_gethostname.Plo
-rm -f ./$(DEPDIR)/libcurl_la-curl_gssapi.Plo
-rm -f ./$(DEPDIR)/libcurl_la-curl_memrchr.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-curl_multibyte.Plo
-rm -f ./$(DEPDIR)/libcurl_la-curl_ntlm_core.Plo
-rm -f ./$(DEPDIR)/libcurl_la-curl_range.Plo
-rm -f ./$(DEPDIR)/libcurl_la-curl_rtmp.Plo
@@ -4902,15 +5135,16 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/libcurl_la-curl_threads.Plo
-rm -f ./$(DEPDIR)/libcurl_la-curl_trc.Plo
-rm -f ./$(DEPDIR)/libcurl_la-cw-out.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-cw-pause.Plo
-rm -f ./$(DEPDIR)/libcurl_la-dict.Plo
-rm -f ./$(DEPDIR)/libcurl_la-dllmain.Plo
-rm -f ./$(DEPDIR)/libcurl_la-doh.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-dynbuf.Plo
-rm -f ./$(DEPDIR)/libcurl_la-dynhds.Plo
-rm -f ./$(DEPDIR)/libcurl_la-easy.Plo
-rm -f ./$(DEPDIR)/libcurl_la-easygetopt.Plo
-rm -f ./$(DEPDIR)/libcurl_la-easyoptions.Plo
-rm -f ./$(DEPDIR)/libcurl_la-escape.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-fake_addrinfo.Plo
-rm -f ./$(DEPDIR)/libcurl_la-file.Plo
-rm -f ./$(DEPDIR)/libcurl_la-fileinfo.Plo
-rm -f ./$(DEPDIR)/libcurl_la-fopen.Plo
@@ -4923,11 +5157,9 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/libcurl_la-hash.Plo
-rm -f ./$(DEPDIR)/libcurl_la-headers.Plo
-rm -f ./$(DEPDIR)/libcurl_la-hmac.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-hostasyn.Plo
-rm -f ./$(DEPDIR)/libcurl_la-hostip.Plo
-rm -f ./$(DEPDIR)/libcurl_la-hostip4.Plo
-rm -f ./$(DEPDIR)/libcurl_la-hostip6.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-hostsyn.Plo
-rm -f ./$(DEPDIR)/libcurl_la-hsts.Plo
-rm -f ./$(DEPDIR)/libcurl_la-http.Plo
-rm -f ./$(DEPDIR)/libcurl_la-http1.Plo
@@ -4938,11 +5170,11 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/libcurl_la-http_negotiate.Plo
-rm -f ./$(DEPDIR)/libcurl_la-http_ntlm.Plo
-rm -f ./$(DEPDIR)/libcurl_la-http_proxy.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-httpsrr.Plo
-rm -f ./$(DEPDIR)/libcurl_la-idn.Plo
-rm -f ./$(DEPDIR)/libcurl_la-if2ip.Plo
-rm -f ./$(DEPDIR)/libcurl_la-imap.Plo
-rm -f ./$(DEPDIR)/libcurl_la-inet_ntop.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-inet_pton.Plo
-rm -f ./$(DEPDIR)/libcurl_la-krb5.Plo
-rm -f ./$(DEPDIR)/libcurl_la-ldap.Plo
-rm -f ./$(DEPDIR)/libcurl_la-libcurl_unity.Plo
@@ -4955,8 +5187,8 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/libcurl_la-mprintf.Plo
-rm -f ./$(DEPDIR)/libcurl_la-mqtt.Plo
-rm -f ./$(DEPDIR)/libcurl_la-multi.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-multi_ev.Plo
-rm -f ./$(DEPDIR)/libcurl_la-netrc.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-nonblock.Plo
-rm -f ./$(DEPDIR)/libcurl_la-noproxy.Plo
-rm -f ./$(DEPDIR)/libcurl_la-openldap.Plo
-rm -f ./$(DEPDIR)/libcurl_la-parsedate.Plo
@@ -4984,29 +5216,27 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/libcurl_la-splay.Plo
-rm -f ./$(DEPDIR)/libcurl_la-strcase.Plo
-rm -f ./$(DEPDIR)/libcurl_la-strdup.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-strequal.Plo
-rm -f ./$(DEPDIR)/libcurl_la-strerror.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-strtok.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-strtoofft.Plo
-rm -f ./$(DEPDIR)/libcurl_la-system_win32.Plo
-rm -f ./$(DEPDIR)/libcurl_la-telnet.Plo
-rm -f ./$(DEPDIR)/libcurl_la-tftp.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-timediff.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-timeval.Plo
-rm -f ./$(DEPDIR)/libcurl_la-transfer.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-uint-bset.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-uint-hash.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-uint-spbset.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-uint-table.Plo
-rm -f ./$(DEPDIR)/libcurl_la-url.Plo
-rm -f ./$(DEPDIR)/libcurl_la-urlapi.Plo
-rm -f ./$(DEPDIR)/libcurl_la-version.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-version_win32.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-warnless.Plo
-rm -f ./$(DEPDIR)/libcurl_la-ws.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-altsvc.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-amigaos.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-asyn-ares.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-asyn-thread.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-base64.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-asyn-base.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-asyn-thrdd.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-bufq.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-bufref.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-c-hyper.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-cf-h1-proxy.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-cf-h2-proxy.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-cf-haproxy.Plo
@@ -5017,6 +5247,7 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/libcurlu_la-connect.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-content_encoding.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-cookie.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-cshutdn.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-curl_addrinfo.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-curl_des.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-curl_endian.Plo
@@ -5025,7 +5256,6 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/libcurlu_la-curl_gethostname.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-curl_gssapi.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-curl_memrchr.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-curl_multibyte.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-curl_ntlm_core.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-curl_range.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-curl_rtmp.Plo
@@ -5035,15 +5265,16 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/libcurlu_la-curl_threads.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-curl_trc.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-cw-out.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-cw-pause.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-dict.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-dllmain.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-doh.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-dynbuf.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-dynhds.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-easy.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-easygetopt.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-easyoptions.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-escape.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-fake_addrinfo.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-file.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-fileinfo.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-fopen.Plo
@@ -5056,11 +5287,9 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/libcurlu_la-hash.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-headers.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-hmac.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-hostasyn.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-hostip.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-hostip4.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-hostip6.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-hostsyn.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-hsts.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-http.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-http1.Plo
@@ -5071,11 +5300,11 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/libcurlu_la-http_negotiate.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-http_ntlm.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-http_proxy.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-httpsrr.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-idn.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-if2ip.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-imap.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-inet_ntop.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-inet_pton.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-krb5.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-ldap.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-libcurl_unity.Plo
@@ -5088,8 +5317,8 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/libcurlu_la-mprintf.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-mqtt.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-multi.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-multi_ev.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-netrc.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-nonblock.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-noproxy.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-openldap.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-parsedate.Plo
@@ -5117,21 +5346,42 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/libcurlu_la-splay.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-strcase.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-strdup.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-strequal.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-strerror.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-strtok.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-strtoofft.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-system_win32.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-telnet.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-tftp.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-timediff.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-timeval.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-transfer.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-uint-bset.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-uint-hash.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-uint-spbset.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-uint-table.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-url.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-urlapi.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-version.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-version_win32.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-warnless.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-ws.Plo
+ -rm -f curlx/$(DEPDIR)/libcurl_la-base64.Plo
+ -rm -f curlx/$(DEPDIR)/libcurl_la-dynbuf.Plo
+ -rm -f curlx/$(DEPDIR)/libcurl_la-inet_pton.Plo
+ -rm -f curlx/$(DEPDIR)/libcurl_la-multibyte.Plo
+ -rm -f curlx/$(DEPDIR)/libcurl_la-nonblock.Plo
+ -rm -f curlx/$(DEPDIR)/libcurl_la-strparse.Plo
+ -rm -f curlx/$(DEPDIR)/libcurl_la-timediff.Plo
+ -rm -f curlx/$(DEPDIR)/libcurl_la-timeval.Plo
+ -rm -f curlx/$(DEPDIR)/libcurl_la-version_win32.Plo
+ -rm -f curlx/$(DEPDIR)/libcurl_la-warnless.Plo
+ -rm -f curlx/$(DEPDIR)/libcurl_la-winapi.Plo
+ -rm -f curlx/$(DEPDIR)/libcurlu_la-base64.Plo
+ -rm -f curlx/$(DEPDIR)/libcurlu_la-dynbuf.Plo
+ -rm -f curlx/$(DEPDIR)/libcurlu_la-inet_pton.Plo
+ -rm -f curlx/$(DEPDIR)/libcurlu_la-multibyte.Plo
+ -rm -f curlx/$(DEPDIR)/libcurlu_la-nonblock.Plo
+ -rm -f curlx/$(DEPDIR)/libcurlu_la-strparse.Plo
+ -rm -f curlx/$(DEPDIR)/libcurlu_la-timediff.Plo
+ -rm -f curlx/$(DEPDIR)/libcurlu_la-timeval.Plo
+ -rm -f curlx/$(DEPDIR)/libcurlu_la-version_win32.Plo
+ -rm -f curlx/$(DEPDIR)/libcurlu_la-warnless.Plo
+ -rm -f curlx/$(DEPDIR)/libcurlu_la-winapi.Plo
-rm -f vauth/$(DEPDIR)/libcurl_la-cleartext.Plo
-rm -f vauth/$(DEPDIR)/libcurl_la-cram.Plo
-rm -f vauth/$(DEPDIR)/libcurl_la-digest.Plo
@@ -5191,6 +5441,8 @@ distclean: distclean-am
-rm -f vtls/$(DEPDIR)/libcurl_la-schannel_verify.Plo
-rm -f vtls/$(DEPDIR)/libcurl_la-sectransp.Plo
-rm -f vtls/$(DEPDIR)/libcurl_la-vtls.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-vtls_scache.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-vtls_spack.Plo
-rm -f vtls/$(DEPDIR)/libcurl_la-wolfssl.Plo
-rm -f vtls/$(DEPDIR)/libcurl_la-x509asn1.Plo
-rm -f vtls/$(DEPDIR)/libcurlu_la-bearssl.Plo
@@ -5206,6 +5458,8 @@ distclean: distclean-am
-rm -f vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Plo
-rm -f vtls/$(DEPDIR)/libcurlu_la-sectransp.Plo
-rm -f vtls/$(DEPDIR)/libcurlu_la-vtls.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-vtls_scache.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-vtls_spack.Plo
-rm -f vtls/$(DEPDIR)/libcurlu_la-wolfssl.Plo
-rm -f vtls/$(DEPDIR)/libcurlu_la-x509asn1.Plo
-rm -f Makefile
@@ -5256,11 +5510,10 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/libcurl_la-altsvc.Plo
-rm -f ./$(DEPDIR)/libcurl_la-amigaos.Plo
-rm -f ./$(DEPDIR)/libcurl_la-asyn-ares.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-asyn-thread.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-base64.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-asyn-base.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-asyn-thrdd.Plo
-rm -f ./$(DEPDIR)/libcurl_la-bufq.Plo
-rm -f ./$(DEPDIR)/libcurl_la-bufref.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-c-hyper.Plo
-rm -f ./$(DEPDIR)/libcurl_la-cf-h1-proxy.Plo
-rm -f ./$(DEPDIR)/libcurl_la-cf-h2-proxy.Plo
-rm -f ./$(DEPDIR)/libcurl_la-cf-haproxy.Plo
@@ -5271,6 +5524,7 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/libcurl_la-connect.Plo
-rm -f ./$(DEPDIR)/libcurl_la-content_encoding.Plo
-rm -f ./$(DEPDIR)/libcurl_la-cookie.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-cshutdn.Plo
-rm -f ./$(DEPDIR)/libcurl_la-curl_addrinfo.Plo
-rm -f ./$(DEPDIR)/libcurl_la-curl_des.Plo
-rm -f ./$(DEPDIR)/libcurl_la-curl_endian.Plo
@@ -5279,7 +5533,6 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/libcurl_la-curl_gethostname.Plo
-rm -f ./$(DEPDIR)/libcurl_la-curl_gssapi.Plo
-rm -f ./$(DEPDIR)/libcurl_la-curl_memrchr.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-curl_multibyte.Plo
-rm -f ./$(DEPDIR)/libcurl_la-curl_ntlm_core.Plo
-rm -f ./$(DEPDIR)/libcurl_la-curl_range.Plo
-rm -f ./$(DEPDIR)/libcurl_la-curl_rtmp.Plo
@@ -5289,15 +5542,16 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/libcurl_la-curl_threads.Plo
-rm -f ./$(DEPDIR)/libcurl_la-curl_trc.Plo
-rm -f ./$(DEPDIR)/libcurl_la-cw-out.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-cw-pause.Plo
-rm -f ./$(DEPDIR)/libcurl_la-dict.Plo
-rm -f ./$(DEPDIR)/libcurl_la-dllmain.Plo
-rm -f ./$(DEPDIR)/libcurl_la-doh.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-dynbuf.Plo
-rm -f ./$(DEPDIR)/libcurl_la-dynhds.Plo
-rm -f ./$(DEPDIR)/libcurl_la-easy.Plo
-rm -f ./$(DEPDIR)/libcurl_la-easygetopt.Plo
-rm -f ./$(DEPDIR)/libcurl_la-easyoptions.Plo
-rm -f ./$(DEPDIR)/libcurl_la-escape.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-fake_addrinfo.Plo
-rm -f ./$(DEPDIR)/libcurl_la-file.Plo
-rm -f ./$(DEPDIR)/libcurl_la-fileinfo.Plo
-rm -f ./$(DEPDIR)/libcurl_la-fopen.Plo
@@ -5310,11 +5564,9 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/libcurl_la-hash.Plo
-rm -f ./$(DEPDIR)/libcurl_la-headers.Plo
-rm -f ./$(DEPDIR)/libcurl_la-hmac.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-hostasyn.Plo
-rm -f ./$(DEPDIR)/libcurl_la-hostip.Plo
-rm -f ./$(DEPDIR)/libcurl_la-hostip4.Plo
-rm -f ./$(DEPDIR)/libcurl_la-hostip6.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-hostsyn.Plo
-rm -f ./$(DEPDIR)/libcurl_la-hsts.Plo
-rm -f ./$(DEPDIR)/libcurl_la-http.Plo
-rm -f ./$(DEPDIR)/libcurl_la-http1.Plo
@@ -5325,11 +5577,11 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/libcurl_la-http_negotiate.Plo
-rm -f ./$(DEPDIR)/libcurl_la-http_ntlm.Plo
-rm -f ./$(DEPDIR)/libcurl_la-http_proxy.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-httpsrr.Plo
-rm -f ./$(DEPDIR)/libcurl_la-idn.Plo
-rm -f ./$(DEPDIR)/libcurl_la-if2ip.Plo
-rm -f ./$(DEPDIR)/libcurl_la-imap.Plo
-rm -f ./$(DEPDIR)/libcurl_la-inet_ntop.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-inet_pton.Plo
-rm -f ./$(DEPDIR)/libcurl_la-krb5.Plo
-rm -f ./$(DEPDIR)/libcurl_la-ldap.Plo
-rm -f ./$(DEPDIR)/libcurl_la-libcurl_unity.Plo
@@ -5342,8 +5594,8 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/libcurl_la-mprintf.Plo
-rm -f ./$(DEPDIR)/libcurl_la-mqtt.Plo
-rm -f ./$(DEPDIR)/libcurl_la-multi.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-multi_ev.Plo
-rm -f ./$(DEPDIR)/libcurl_la-netrc.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-nonblock.Plo
-rm -f ./$(DEPDIR)/libcurl_la-noproxy.Plo
-rm -f ./$(DEPDIR)/libcurl_la-openldap.Plo
-rm -f ./$(DEPDIR)/libcurl_la-parsedate.Plo
@@ -5371,29 +5623,27 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/libcurl_la-splay.Plo
-rm -f ./$(DEPDIR)/libcurl_la-strcase.Plo
-rm -f ./$(DEPDIR)/libcurl_la-strdup.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-strequal.Plo
-rm -f ./$(DEPDIR)/libcurl_la-strerror.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-strtok.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-strtoofft.Plo
-rm -f ./$(DEPDIR)/libcurl_la-system_win32.Plo
-rm -f ./$(DEPDIR)/libcurl_la-telnet.Plo
-rm -f ./$(DEPDIR)/libcurl_la-tftp.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-timediff.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-timeval.Plo
-rm -f ./$(DEPDIR)/libcurl_la-transfer.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-uint-bset.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-uint-hash.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-uint-spbset.Plo
+ -rm -f ./$(DEPDIR)/libcurl_la-uint-table.Plo
-rm -f ./$(DEPDIR)/libcurl_la-url.Plo
-rm -f ./$(DEPDIR)/libcurl_la-urlapi.Plo
-rm -f ./$(DEPDIR)/libcurl_la-version.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-version_win32.Plo
- -rm -f ./$(DEPDIR)/libcurl_la-warnless.Plo
-rm -f ./$(DEPDIR)/libcurl_la-ws.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-altsvc.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-amigaos.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-asyn-ares.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-asyn-thread.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-base64.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-asyn-base.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-asyn-thrdd.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-bufq.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-bufref.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-c-hyper.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-cf-h1-proxy.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-cf-h2-proxy.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-cf-haproxy.Plo
@@ -5404,6 +5654,7 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/libcurlu_la-connect.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-content_encoding.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-cookie.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-cshutdn.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-curl_addrinfo.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-curl_des.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-curl_endian.Plo
@@ -5412,7 +5663,6 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/libcurlu_la-curl_gethostname.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-curl_gssapi.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-curl_memrchr.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-curl_multibyte.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-curl_ntlm_core.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-curl_range.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-curl_rtmp.Plo
@@ -5422,15 +5672,16 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/libcurlu_la-curl_threads.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-curl_trc.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-cw-out.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-cw-pause.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-dict.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-dllmain.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-doh.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-dynbuf.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-dynhds.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-easy.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-easygetopt.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-easyoptions.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-escape.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-fake_addrinfo.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-file.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-fileinfo.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-fopen.Plo
@@ -5443,11 +5694,9 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/libcurlu_la-hash.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-headers.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-hmac.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-hostasyn.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-hostip.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-hostip4.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-hostip6.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-hostsyn.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-hsts.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-http.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-http1.Plo
@@ -5458,11 +5707,11 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/libcurlu_la-http_negotiate.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-http_ntlm.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-http_proxy.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-httpsrr.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-idn.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-if2ip.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-imap.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-inet_ntop.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-inet_pton.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-krb5.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-ldap.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-libcurl_unity.Plo
@@ -5475,8 +5724,8 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/libcurlu_la-mprintf.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-mqtt.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-multi.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-multi_ev.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-netrc.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-nonblock.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-noproxy.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-openldap.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-parsedate.Plo
@@ -5504,21 +5753,42 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/libcurlu_la-splay.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-strcase.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-strdup.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-strequal.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-strerror.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-strtok.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-strtoofft.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-system_win32.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-telnet.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-tftp.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-timediff.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-timeval.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-transfer.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-uint-bset.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-uint-hash.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-uint-spbset.Plo
+ -rm -f ./$(DEPDIR)/libcurlu_la-uint-table.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-url.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-urlapi.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-version.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-version_win32.Plo
- -rm -f ./$(DEPDIR)/libcurlu_la-warnless.Plo
-rm -f ./$(DEPDIR)/libcurlu_la-ws.Plo
+ -rm -f curlx/$(DEPDIR)/libcurl_la-base64.Plo
+ -rm -f curlx/$(DEPDIR)/libcurl_la-dynbuf.Plo
+ -rm -f curlx/$(DEPDIR)/libcurl_la-inet_pton.Plo
+ -rm -f curlx/$(DEPDIR)/libcurl_la-multibyte.Plo
+ -rm -f curlx/$(DEPDIR)/libcurl_la-nonblock.Plo
+ -rm -f curlx/$(DEPDIR)/libcurl_la-strparse.Plo
+ -rm -f curlx/$(DEPDIR)/libcurl_la-timediff.Plo
+ -rm -f curlx/$(DEPDIR)/libcurl_la-timeval.Plo
+ -rm -f curlx/$(DEPDIR)/libcurl_la-version_win32.Plo
+ -rm -f curlx/$(DEPDIR)/libcurl_la-warnless.Plo
+ -rm -f curlx/$(DEPDIR)/libcurl_la-winapi.Plo
+ -rm -f curlx/$(DEPDIR)/libcurlu_la-base64.Plo
+ -rm -f curlx/$(DEPDIR)/libcurlu_la-dynbuf.Plo
+ -rm -f curlx/$(DEPDIR)/libcurlu_la-inet_pton.Plo
+ -rm -f curlx/$(DEPDIR)/libcurlu_la-multibyte.Plo
+ -rm -f curlx/$(DEPDIR)/libcurlu_la-nonblock.Plo
+ -rm -f curlx/$(DEPDIR)/libcurlu_la-strparse.Plo
+ -rm -f curlx/$(DEPDIR)/libcurlu_la-timediff.Plo
+ -rm -f curlx/$(DEPDIR)/libcurlu_la-timeval.Plo
+ -rm -f curlx/$(DEPDIR)/libcurlu_la-version_win32.Plo
+ -rm -f curlx/$(DEPDIR)/libcurlu_la-warnless.Plo
+ -rm -f curlx/$(DEPDIR)/libcurlu_la-winapi.Plo
-rm -f vauth/$(DEPDIR)/libcurl_la-cleartext.Plo
-rm -f vauth/$(DEPDIR)/libcurl_la-cram.Plo
-rm -f vauth/$(DEPDIR)/libcurl_la-digest.Plo
@@ -5578,6 +5848,8 @@ maintainer-clean: maintainer-clean-am
-rm -f vtls/$(DEPDIR)/libcurl_la-schannel_verify.Plo
-rm -f vtls/$(DEPDIR)/libcurl_la-sectransp.Plo
-rm -f vtls/$(DEPDIR)/libcurl_la-vtls.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-vtls_scache.Plo
+ -rm -f vtls/$(DEPDIR)/libcurl_la-vtls_spack.Plo
-rm -f vtls/$(DEPDIR)/libcurl_la-wolfssl.Plo
-rm -f vtls/$(DEPDIR)/libcurl_la-x509asn1.Plo
-rm -f vtls/$(DEPDIR)/libcurlu_la-bearssl.Plo
@@ -5593,6 +5865,8 @@ maintainer-clean: maintainer-clean-am
-rm -f vtls/$(DEPDIR)/libcurlu_la-schannel_verify.Plo
-rm -f vtls/$(DEPDIR)/libcurlu_la-sectransp.Plo
-rm -f vtls/$(DEPDIR)/libcurlu_la-vtls.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-vtls_scache.Plo
+ -rm -f vtls/$(DEPDIR)/libcurlu_la-vtls_spack.Plo
-rm -f vtls/$(DEPDIR)/libcurlu_la-wolfssl.Plo
-rm -f vtls/$(DEPDIR)/libcurlu_la-x509asn1.Plo
-rm -f Makefile
@@ -5650,18 +5924,18 @@ uninstall-am: uninstall-libLTLIBRARIES
@HAVE_WINDRES_TRUE@@USE_CPPFLAG_CURL_STATICLIB_FALSE@$(LIB_RCFILES): $(top_srcdir)/include/curl/curlver.h
checksrc:
- $(CHECKSRC)(@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(srcdir) \
- -W$(srcdir)/curl_config.h $(srcdir)/*.[ch] $(srcdir)/vauth/*.[ch] \
- $(srcdir)/vtls/*.[ch] $(srcdir)/vquic/*.[ch] $(srcdir)/vssh/*.[ch])
+ $(CHECKSRC)(@PERL@ $(top_srcdir)/scripts/checksrc.pl -D$(srcdir) \
+ $(CSOURCES) $(HHEADERS))
# for debug builds, we scan the sources on all regular make invokes
-@DEBUGBUILD_TRUE@all-local: checksrc
+@DEBUGBUILD_TRUE@@NOT_CURL_CI_TRUE@all-local: checksrc
tidy:
- $(TIDY) $(CSOURCES) $(TIDYFLAGS) -- $(AM_CPPFLAGS) $(CPPFLAGS) -DHAVE_CONFIG_H
+ (_csources=`echo ' $(CSOURCES)' | sed -E -e 's/ +$$//' -e 's/ +/ /g' -e 's| | $(srcdir)/|g'`; \
+ $(TIDY) $$_csources $(TIDYFLAGS) $(CURL_CLANG_TIDYFLAGS) -- $(AM_CPPFLAGS) $(CPPFLAGS) -DHAVE_CONFIG_H)
optiontable:
- perl optiontable.pl < $(top_srcdir)/include/curl/curl.h > easyoptions.c
+ @PERL@ $(srcdir)/optiontable.pl < $(top_srcdir)/include/curl/curl.h > $(srcdir)/easyoptions.c
@HAVE_WINDRES_TRUE@.rc.lo:
@HAVE_WINDRES_TRUE@ $(LIBTOOL) --tag=RC --mode=compile $(RC) -I$(top_srcdir)/include $(RCFLAGS) -i $< -o $@
diff --git a/libs/libcurl/src/Makefile.inc b/libs/libcurl/src/Makefile.inc
index 592b36e5a9..1b8684a8ba 100644
--- a/libs/libcurl/src/Makefile.inc
+++ b/libs/libcurl/src/Makefile.inc
@@ -22,6 +22,33 @@
#
###########################################################################
+LIB_CURLX_CFILES = \
+ curlx/base64.c \
+ curlx/dynbuf.c \
+ curlx/inet_pton.c \
+ curlx/multibyte.c \
+ curlx/nonblock.c \
+ curlx/strparse.c \
+ curlx/timediff.c \
+ curlx/timeval.c \
+ curlx/version_win32.c \
+ curlx/warnless.c \
+ curlx/winapi.c
+
+LIB_CURLX_HFILES = \
+ curlx/base64.h \
+ curlx/curlx.h \
+ curlx/dynbuf.h \
+ curlx/inet_pton.h \
+ curlx/multibyte.h \
+ curlx/nonblock.h \
+ curlx/strparse.h \
+ curlx/timediff.h \
+ curlx/timeval.h \
+ curlx/version_win32.h \
+ curlx/warnless.h \
+ curlx/winapi.h
+
LIB_VAUTH_CFILES = \
vauth/cleartext.c \
vauth/cram.c \
@@ -39,7 +66,6 @@ LIB_VAUTH_CFILES = \
LIB_VAUTH_HFILES = \
vauth/digest.h \
- vauth/ntlm.h \
vauth/vauth.h
LIB_VTLS_CFILES = \
@@ -56,6 +82,8 @@ LIB_VTLS_CFILES = \
vtls/schannel_verify.c \
vtls/sectransp.c \
vtls/vtls.c \
+ vtls/vtls_scache.c \
+ vtls/vtls_spack.c \
vtls/wolfssl.c \
vtls/x509asn1.c
@@ -74,6 +102,8 @@ LIB_VTLS_HFILES = \
vtls/sectransp.h \
vtls/vtls.h \
vtls/vtls_int.h \
+ vtls/vtls_scache.h \
+ vtls/vtls_spack.h \
vtls/wolfssl.h \
vtls/x509asn1.h
@@ -108,11 +138,10 @@ LIB_CFILES = \
altsvc.c \
amigaos.c \
asyn-ares.c \
- asyn-thread.c \
- base64.c \
+ asyn-base.c \
+ asyn-thrdd.c \
bufq.c \
bufref.c \
- c-hyper.c \
cf-h1-proxy.c \
cf-h2-proxy.c \
cf-haproxy.c \
@@ -123,6 +152,7 @@ LIB_CFILES = \
connect.c \
content_encoding.c \
cookie.c \
+ cshutdn.c \
curl_addrinfo.c \
curl_des.c \
curl_endian.c \
@@ -131,7 +161,6 @@ LIB_CFILES = \
curl_gethostname.c \
curl_gssapi.c \
curl_memrchr.c \
- curl_multibyte.c \
curl_ntlm_core.c \
curl_range.c \
curl_rtmp.c \
@@ -141,15 +170,15 @@ LIB_CFILES = \
curl_threads.c \
curl_trc.c \
cw-out.c \
+ cw-pause.c \
dict.c \
- dllmain.c \
doh.c \
- dynbuf.c \
dynhds.c \
easy.c \
easygetopt.c \
easyoptions.c \
escape.c \
+ fake_addrinfo.c \
file.c \
fileinfo.c \
fopen.c \
@@ -162,11 +191,9 @@ LIB_CFILES = \
hash.c \
headers.c \
hmac.c \
- hostasyn.c \
hostip.c \
hostip4.c \
hostip6.c \
- hostsyn.c \
hsts.c \
http.c \
http1.c \
@@ -177,11 +204,11 @@ LIB_CFILES = \
http_negotiate.c \
http_ntlm.c \
http_proxy.c \
+ httpsrr.c \
idn.c \
if2ip.c \
imap.c \
inet_ntop.c \
- inet_pton.c \
krb5.c \
ldap.c \
llist.c \
@@ -193,8 +220,8 @@ LIB_CFILES = \
mprintf.c \
mqtt.c \
multi.c \
+ multi_ev.c \
netrc.c \
- nonblock.c \
noproxy.c \
openldap.c \
parsedate.c \
@@ -222,20 +249,19 @@ LIB_CFILES = \
splay.c \
strcase.c \
strdup.c \
+ strequal.c \
strerror.c \
- strtok.c \
- strtoofft.c \
system_win32.c \
telnet.c \
tftp.c \
- timediff.c \
- timeval.c \
transfer.c \
+ uint-bset.c \
+ uint-hash.c \
+ uint-spbset.c \
+ uint-table.c \
url.c \
urlapi.c \
version.c \
- version_win32.c \
- warnless.c \
ws.c
LIB_HFILES = \
@@ -245,7 +271,6 @@ LIB_HFILES = \
asyn.h \
bufq.h \
bufref.h \
- c-hyper.h \
cf-h1-proxy.h \
cf-h2-proxy.h \
cf-haproxy.h \
@@ -253,11 +278,11 @@ LIB_HFILES = \
cf-socket.h \
cfilters.h \
conncache.h \
+ cshutdn.h \
connect.h \
content_encoding.h \
cookie.h \
curl_addrinfo.h \
- curl_base64.h \
curl_ctype.h \
curl_des.h \
curl_endian.h \
@@ -272,7 +297,6 @@ LIB_HFILES = \
curl_md5.h \
curl_memory.h \
curl_memrchr.h \
- curl_multibyte.h \
curl_ntlm_core.h \
curl_printf.h \
curl_range.h \
@@ -285,16 +309,16 @@ LIB_HFILES = \
curl_sspi.h \
curl_threads.h \
curl_trc.h \
- curlx.h \
cw-out.h \
+ cw-pause.h \
dict.h \
doh.h \
- dynbuf.h \
dynhds.h \
easy_lock.h \
easyif.h \
easyoptions.h \
escape.h \
+ fake_addrinfo.h \
file.h \
fileinfo.h \
fopen.h \
@@ -317,20 +341,20 @@ LIB_HFILES = \
http_negotiate.h \
http_ntlm.h \
http_proxy.h \
+ httpsrr.h \
idn.h \
if2ip.h \
imap.h \
inet_ntop.h \
- inet_pton.h \
llist.h \
macos.h \
memdebug.h \
mime.h \
mqtt.h \
multihandle.h \
+ multi_ev.h \
multiif.h \
netrc.h \
- nonblock.h \
noproxy.h \
parsedate.h \
pingpong.h \
@@ -344,7 +368,9 @@ LIB_HFILES = \
select.h \
sendf.h \
setopt.h \
+ setup-os400.h \
setup-vms.h \
+ setup-win32.h \
share.h \
sigpipe.h \
slist.h \
@@ -358,24 +384,22 @@ LIB_HFILES = \
strcase.h \
strdup.h \
strerror.h \
- strtok.h \
- strtoofft.h \
system_win32.h \
telnet.h \
tftp.h \
- timediff.h \
- timeval.h \
transfer.h \
+ uint-bset.h \
+ uint-hash.h \
+ uint-spbset.h \
+ uint-table.h \
url.h \
urlapi-int.h \
urldata.h \
- version_win32.h \
- warnless.h \
ws.h
LIB_RCFILES = libcurl.rc
CSOURCES = $(LIB_CFILES) $(LIB_VAUTH_CFILES) $(LIB_VTLS_CFILES) \
- $(LIB_VQUIC_CFILES) $(LIB_VSSH_CFILES)
+ $(LIB_VQUIC_CFILES) $(LIB_VSSH_CFILES) $(LIB_CURLX_CFILES)
HHEADERS = $(LIB_HFILES) $(LIB_VAUTH_HFILES) $(LIB_VTLS_HFILES) \
- $(LIB_VQUIC_HFILES) $(LIB_VSSH_HFILES)
+ $(LIB_VQUIC_HFILES) $(LIB_VSSH_HFILES) $(LIB_CURLX_HFILES)
diff --git a/libs/libcurl/src/altsvc.c b/libs/libcurl/src/altsvc.c
index aac25f62b1..599fb5a5e5 100644
--- a/libs/libcurl/src/altsvc.c
+++ b/libs/libcurl/src/altsvc.c
@@ -35,11 +35,13 @@
#include "strcase.h"
#include "parsedate.h"
#include "sendf.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
#include "fopen.h"
#include "rename.h"
#include "strdup.h"
-#include "inet_pton.h"
+#include "curlx/inet_pton.h"
+#include "curlx/strparse.h"
+#include "connect.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -47,28 +49,12 @@
#include "memdebug.h"
#define MAX_ALTSVC_LINE 4095
-#define MAX_ALTSVC_DATELENSTR "64"
-#define MAX_ALTSVC_DATELEN 64
-#define MAX_ALTSVC_HOSTLENSTR "512"
-#define MAX_ALTSVC_HOSTLEN 512
-#define MAX_ALTSVC_ALPNLENSTR "10"
+#define MAX_ALTSVC_DATELEN 256
+#define MAX_ALTSVC_HOSTLEN 2048
#define MAX_ALTSVC_ALPNLEN 10
#define H3VERSION "h3"
-static enum alpnid alpn2alpnid(char *name)
-{
- if(strcasecompare(name, "h1"))
- return ALPN_h1;
- if(strcasecompare(name, "h2"))
- return ALPN_h2;
- if(strcasecompare(name, H3VERSION))
- return ALPN_h3;
- if(strcasecompare(name, "http/1.1"))
- return ALPN_h1;
- return ALPN_none; /* unknown, probably rubbish input */
-}
-
/* Given the ALPN ID, return the name */
const char *Curl_alpnid2str(enum alpnid id)
{
@@ -93,33 +79,33 @@ static void altsvc_free(struct altsvc *as)
}
static struct altsvc *altsvc_createid(const char *srchost,
+ size_t hlen,
const char *dsthost,
size_t dlen, /* dsthost length */
enum alpnid srcalpnid,
enum alpnid dstalpnid,
- unsigned int srcport,
- unsigned int dstport)
+ size_t srcport,
+ size_t dstport)
{
struct altsvc *as = calloc(1, sizeof(struct altsvc));
- size_t hlen;
if(!as)
return NULL;
- hlen = strlen(srchost);
DEBUGASSERT(hlen);
DEBUGASSERT(dlen);
- if(!hlen || !dlen) {
+ if(!hlen || !dlen)
/* bad input */
- free(as);
- return NULL;
- }
+ goto error;
if((hlen > 2) && srchost[0] == '[') {
/* IPv6 address, strip off brackets */
srchost++;
hlen -= 2;
}
- else if(srchost[hlen - 1] == '.')
+ else if(srchost[hlen - 1] == '.') {
/* strip off trailing dot */
hlen--;
+ if(!hlen)
+ goto error;
+ }
if((dlen > 2) && dsthost[0] == '[') {
/* IPv6 address, strip off brackets */
dsthost++;
@@ -136,8 +122,8 @@ static struct altsvc *altsvc_createid(const char *srchost,
as->src.alpnid = srcalpnid;
as->dst.alpnid = dstalpnid;
- as->src.port = curlx_ultous(srcport);
- as->dst.port = curlx_ultous(dstport);
+ as->src.port = (unsigned short)srcport;
+ as->dst.port = (unsigned short)dstport;
return as;
error:
@@ -145,53 +131,75 @@ error:
return NULL;
}
-static struct altsvc *altsvc_create(char *srchost,
- char *dsthost,
- char *srcalpn,
- char *dstalpn,
- unsigned int srcport,
- unsigned int dstport)
+static struct altsvc *altsvc_create(struct Curl_str *srchost,
+ struct Curl_str *dsthost,
+ struct Curl_str *srcalpn,
+ struct Curl_str *dstalpn,
+ size_t srcport,
+ size_t dstport)
{
- enum alpnid dstalpnid = alpn2alpnid(dstalpn);
- enum alpnid srcalpnid = alpn2alpnid(srcalpn);
+ enum alpnid dstalpnid =
+ Curl_alpn2alpnid(curlx_str(dstalpn), curlx_strlen(dstalpn));
+ enum alpnid srcalpnid =
+ Curl_alpn2alpnid(curlx_str(srcalpn), curlx_strlen(srcalpn));
if(!srcalpnid || !dstalpnid)
return NULL;
- return altsvc_createid(srchost, dsthost, strlen(dsthost),
+ return altsvc_createid(curlx_str(srchost), curlx_strlen(srchost),
+ curlx_str(dsthost), curlx_strlen(dsthost),
srcalpnid, dstalpnid,
srcport, dstport);
}
/* only returns SERIOUS errors */
-static CURLcode altsvc_add(struct altsvcinfo *asi, char *line)
+static CURLcode altsvc_add(struct altsvcinfo *asi, const char *line)
{
/* Example line:
h2 example.com 443 h3 shiny.example.com 8443 "20191231 10:00:00" 1
*/
- char srchost[MAX_ALTSVC_HOSTLEN + 1];
- char dsthost[MAX_ALTSVC_HOSTLEN + 1];
- char srcalpn[MAX_ALTSVC_ALPNLEN + 1];
- char dstalpn[MAX_ALTSVC_ALPNLEN + 1];
- char date[MAX_ALTSVC_DATELEN + 1];
- unsigned int srcport;
- unsigned int dstport;
- unsigned int prio;
- unsigned int persist;
- int rc;
-
- rc = sscanf(line,
- "%" MAX_ALTSVC_ALPNLENSTR "s %" MAX_ALTSVC_HOSTLENSTR "s %u "
- "%" MAX_ALTSVC_ALPNLENSTR "s %" MAX_ALTSVC_HOSTLENSTR "s %u "
- "\"%" MAX_ALTSVC_DATELENSTR "[^\"]\" %u %u",
- srcalpn, srchost, &srcport,
- dstalpn, dsthost, &dstport,
- date, &persist, &prio);
- if(9 == rc) {
+ struct Curl_str srchost;
+ struct Curl_str dsthost;
+ struct Curl_str srcalpn;
+ struct Curl_str dstalpn;
+ struct Curl_str date;
+ curl_off_t srcport;
+ curl_off_t dstport;
+ curl_off_t persist;
+ curl_off_t prio;
+
+ if(curlx_str_word(&line, &srcalpn, MAX_ALTSVC_ALPNLEN) ||
+ curlx_str_singlespace(&line) ||
+ curlx_str_word(&line, &srchost, MAX_ALTSVC_HOSTLEN) ||
+ curlx_str_singlespace(&line) ||
+ curlx_str_number(&line, &srcport, 65535) ||
+ curlx_str_singlespace(&line) ||
+ curlx_str_word(&line, &dstalpn, MAX_ALTSVC_ALPNLEN) ||
+ curlx_str_singlespace(&line) ||
+ curlx_str_word(&line, &dsthost, MAX_ALTSVC_HOSTLEN) ||
+ curlx_str_singlespace(&line) ||
+ curlx_str_number(&line, &dstport, 65535) ||
+ curlx_str_singlespace(&line) ||
+ curlx_str_quotedword(&line, &date, MAX_ALTSVC_DATELEN) ||
+ curlx_str_singlespace(&line) ||
+ curlx_str_number(&line, &persist, 1) ||
+ curlx_str_singlespace(&line) ||
+ curlx_str_number(&line, &prio, 0) ||
+ curlx_str_newline(&line))
+ ;
+ else {
struct altsvc *as;
- time_t expires = Curl_getdate_capped(date);
- as = altsvc_create(srchost, dsthost, srcalpn, dstalpn, srcport, dstport);
+ char dbuf[MAX_ALTSVC_DATELEN + 1];
+ time_t expires;
+
+ /* The date parser works on a null-terminated string. The maximum length
+ is upheld by curlx_str_quotedword(). */
+ memcpy(dbuf, curlx_str(&date), curlx_strlen(&date));
+ dbuf[curlx_strlen(&date)] = 0;
+ expires = Curl_getdate_capped(dbuf);
+ as = altsvc_create(&srchost, &dsthost, &srcalpn, &dstalpn,
+ (size_t)srcport, (size_t)dstport);
if(as) {
as->expires = expires;
- as->prio = prio;
+ as->prio = 0; /* not supported to just set zero */
as->persist = persist ? 1 : 0;
Curl_llist_append(&asi->list, as, &as->node);
}
@@ -223,18 +231,14 @@ static CURLcode altsvc_load(struct altsvcinfo *asi, const char *file)
fp = fopen(file, FOPEN_READTEXT);
if(fp) {
struct dynbuf buf;
- Curl_dyn_init(&buf, MAX_ALTSVC_LINE);
+ curlx_dyn_init(&buf, MAX_ALTSVC_LINE);
while(Curl_get_line(&buf, fp)) {
- char *lineptr = Curl_dyn_ptr(&buf);
- while(*lineptr && ISBLANK(*lineptr))
- lineptr++;
- if(*lineptr == '#')
- /* skip commented lines */
- continue;
-
- altsvc_add(asi, lineptr);
+ const char *lineptr = curlx_dyn_ptr(&buf);
+ curlx_str_passblanks(&lineptr);
+ if(curlx_str_single(&lineptr, '#'))
+ altsvc_add(asi, lineptr);
}
- Curl_dyn_free(&buf); /* free the line buffer */
+ curlx_dyn_free(&buf); /* free the line buffer */
fclose(fp);
}
return result;
@@ -257,11 +261,11 @@ static CURLcode altsvc_out(struct altsvc *as, FILE *fp)
#ifdef USE_IPV6
else {
char ipv6_unused[16];
- if(1 == Curl_inet_pton(AF_INET6, as->dst.host, ipv6_unused)) {
+ if(1 == curlx_inet_pton(AF_INET6, as->dst.host, ipv6_unused)) {
dst6_pre = "[";
dst6_post = "]";
}
- if(1 == Curl_inet_pton(AF_INET6, as->src.host, ipv6_unused)) {
+ if(1 == curlx_inet_pton(AF_INET6, as->src.host, ipv6_unused)) {
src6_pre = "[";
src6_post = "]";
}
@@ -399,26 +403,6 @@ CURLcode Curl_altsvc_save(struct Curl_easy *data,
return result;
}
-static CURLcode getalnum(const char **ptr, char *alpnbuf, size_t buflen)
-{
- size_t len;
- const char *protop;
- const char *p = *ptr;
- while(*p && ISBLANK(*p))
- p++;
- protop = p;
- while(*p && !ISBLANK(*p) && (*p != ';') && (*p != '='))
- p++;
- len = p - protop;
- *ptr = p;
-
- if(!len || (len >= buflen))
- return CURLE_BAD_FUNCTION_ARGUMENT;
- memcpy(alpnbuf, protop, len);
- alpnbuf[len] = 0;
- return CURLE_OK;
-}
-
/* hostcompare() returns true if 'host' matches 'check'. The first host
* argument may have a trailing dot present that will be ignored.
*/
@@ -454,15 +438,16 @@ static void altsvc_flush(struct altsvcinfo *asi, enum alpnid srcalpnid,
}
}
-#ifdef DEBUGBUILD
+#if defined(DEBUGBUILD) || defined(UNITTESTS)
/* to play well with debug builds, we can *set* a fixed time this will
return */
static time_t altsvc_debugtime(void *unused)
{
- char *timestr = getenv("CURL_TIME");
+ const char *timestr = getenv("CURL_TIME");
(void)unused;
if(timestr) {
- long val = strtol(timestr, NULL, 10);
+ curl_off_t val;
+ curlx_str_number(&timestr, &val, TIME_T_MAX);
return (time_t)val;
}
return time(NULL);
@@ -471,8 +456,6 @@ static time_t altsvc_debugtime(void *unused)
#define time(x) altsvc_debugtime(x)
#endif
-#define ISNEWLINE(x) (((x) == '\n') || (x) == '\r')
-
/*
* Curl_altsvc_parse() takes an incoming alt-svc response header and stores
* the data correctly in the cache.
@@ -490,178 +473,158 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data,
unsigned short srcport)
{
const char *p = value;
- char alpnbuf[MAX_ALTSVC_ALPNLEN] = "";
struct altsvc *as;
unsigned short dstport = srcport; /* the same by default */
- CURLcode result = getalnum(&p, alpnbuf, sizeof(alpnbuf));
size_t entries = 0;
+ struct Curl_str alpn;
+ const char *sp;
+ time_t maxage = 24 * 3600; /* default is 24 hours */
+ bool persist = FALSE;
#ifdef CURL_DISABLE_VERBOSE_STRINGS
(void)data;
#endif
- if(result) {
- infof(data, "Excessive alt-svc header, ignoring.");
- return CURLE_OK;
- }
DEBUGASSERT(asi);
- /* "clear" is a magic keyword */
- if(strcasecompare(alpnbuf, "clear")) {
- /* Flush cached alternatives for this source origin */
- altsvc_flush(asi, srcalpnid, srchost, srcport);
- return CURLE_OK;
+ /* initial check for "clear" */
+ if(!curlx_str_until(&p, &alpn, MAX_ALTSVC_LINE, ';') &&
+ !curlx_str_single(&p, ';')) {
+ curlx_str_trimblanks(&alpn);
+ /* "clear" is a magic keyword */
+ if(curlx_str_casecompare(&alpn, "clear")) {
+ /* Flush cached alternatives for this source origin */
+ altsvc_flush(asi, srcalpnid, srchost, srcport);
+ return CURLE_OK;
+ }
+ }
+
+ p = value;
+
+ if(curlx_str_until(&p, &alpn, MAX_ALTSVC_LINE, '='))
+ return CURLE_OK; /* strange line */
+
+ curlx_str_trimblanks(&alpn);
+
+ /* Handle the optional 'ma' and 'persist' flags once first, as they need to
+ be known for each alternative service. Unknown flags are skipped. */
+ sp = strchr(p, ';');
+ if(sp) {
+ sp++; /* pass the semicolon */
+ for(;;) {
+ struct Curl_str name;
+ struct Curl_str val;
+ const char *vp;
+ curl_off_t num;
+ bool quoted;
+ /* allow some extra whitespaces around name and value */
+ if(curlx_str_until(&sp, &name, 20, '=') ||
+ curlx_str_single(&sp, '=') ||
+ curlx_str_until(&sp, &val, 80, ';'))
+ break;
+ curlx_str_trimblanks(&name);
+ curlx_str_trimblanks(&val);
+ /* the value might be quoted */
+ vp = curlx_str(&val);
+ quoted = (*vp == '\"');
+ if(quoted)
+ vp++;
+ if(!curlx_str_number(&vp, &num, TIME_T_MAX)) {
+ if(curlx_str_casecompare(&name, "ma"))
+ maxage = (time_t)num;
+ else if(curlx_str_casecompare(&name, "persist") && (num == 1))
+ persist = TRUE;
+ }
+ if(quoted && curlx_str_single(&sp, '\"'))
+ break;
+ if(curlx_str_single(&sp, ';'))
+ break;
+ }
}
do {
- if(*p == '=') {
- /* [protocol]="[host][:port]" */
- enum alpnid dstalpnid = alpn2alpnid(alpnbuf); /* the same by default */
- p++;
- if(*p == '\"') {
- const char *dsthost = "";
- size_t dstlen = 0; /* destination hostname length */
- const char *value_ptr;
- char option[32];
- unsigned long num;
- char *end_ptr;
- bool quoted = FALSE;
- time_t maxage = 24 * 3600; /* default is 24 hours */
- bool persist = FALSE;
- bool valid = TRUE;
- p++;
- if(*p != ':') {
+ if(!curlx_str_single(&p, '=')) {
+ /* [protocol]="[host][:port], [protocol]="[host][:port]" */
+ enum alpnid dstalpnid =
+ Curl_alpn2alpnid(curlx_str(&alpn), curlx_strlen(&alpn));
+ if(!curlx_str_single(&p, '\"')) {
+ struct Curl_str dsthost;
+ curl_off_t port = 0;
+ if(curlx_str_single(&p, ':')) {
/* hostname starts here */
- const char *hostp = p;
- if(*p == '[') {
- /* pass all valid IPv6 letters - does not handle zone id */
- dstlen = strspn(++p, "0123456789abcdefABCDEF:.");
- if(p[dstlen] != ']')
- /* invalid host syntax, bail out */
+ if(curlx_str_single(&p, '[')) {
+ if(curlx_str_until(&p, &dsthost, MAX_ALTSVC_HOSTLEN, ':')) {
+ infof(data, "Bad alt-svc hostname, ignoring.");
break;
- /* we store the IPv6 numerical address *with* brackets */
- dstlen += 2;
- p = &p[dstlen-1];
- }
- else {
- while(*p && (ISALNUM(*p) || (*p == '.') || (*p == '-')))
- p++;
- dstlen = p - hostp;
- }
- if(!dstlen || (dstlen >= MAX_ALTSVC_HOSTLEN)) {
- infof(data, "Excessive alt-svc hostname, ignoring.");
- valid = FALSE;
+ }
}
else {
- dsthost = hostp;
+ /* IPv6 host name */
+ if(curlx_str_until(&p, &dsthost, MAX_IPADR_LEN, ']') ||
+ curlx_str_single(&p, ']')) {
+ infof(data, "Bad alt-svc IPv6 hostname, ignoring.");
+ break;
+ }
}
+ if(curlx_str_single(&p, ':'))
+ break;
}
- else {
+ else
/* no destination name, use source host */
- dsthost = srchost;
- dstlen = strlen(srchost);
- }
- if(*p == ':') {
- unsigned long port = 0;
- p++;
- if(ISDIGIT(*p))
- /* a port number */
- port = strtoul(p, &end_ptr, 10);
- else
- end_ptr = (char *)p; /* not left uninitialized */
- if(!port || port > USHRT_MAX || end_ptr == p || *end_ptr != '\"') {
- infof(data, "Unknown alt-svc port number, ignoring.");
- valid = FALSE;
- }
- else {
- dstport = curlx_ultous(port);
- p = end_ptr;
- }
- }
- if(*p++ != '\"')
+ curlx_str_assign(&dsthost, srchost, strlen(srchost));
+
+ if(curlx_str_number(&p, &port, 0xffff)) {
+ infof(data, "Unknown alt-svc port number, ignoring.");
break;
- /* Handle the optional 'ma' and 'persist' flags. Unknown flags
- are skipped. */
- for(;;) {
- while(ISBLANK(*p))
- p++;
- if(*p != ';')
- break;
- p++; /* pass the semicolon */
- if(!*p || ISNEWLINE(*p))
- break;
- result = getalnum(&p, option, sizeof(option));
- if(result) {
- /* skip option if name is too long */
- option[0] = '\0';
- }
- while(*p && ISBLANK(*p))
- p++;
- if(*p != '=')
- return CURLE_OK;
- p++;
- while(*p && ISBLANK(*p))
- p++;
- if(!*p)
- return CURLE_OK;
- if(*p == '\"') {
- /* quoted value */
- p++;
- quoted = TRUE;
- }
- value_ptr = p;
- if(quoted) {
- while(*p && *p != '\"')
- p++;
- if(!*p++)
- return CURLE_OK;
- }
- else {
- while(*p && !ISBLANK(*p) && *p!= ';' && *p != ',')
- p++;
- }
- num = strtoul(value_ptr, &end_ptr, 10);
- if((end_ptr != value_ptr) && (num < ULONG_MAX)) {
- if(strcasecompare("ma", option))
- maxage = (time_t)num;
- else if(strcasecompare("persist", option) && (num == 1))
- persist = TRUE;
- }
}
- if(dstalpnid && valid) {
+
+ dstport = (unsigned short)port;
+
+ if(curlx_str_single(&p, '\"'))
+ break;
+
+ if(dstalpnid) {
if(!entries++)
/* Flush cached alternatives for this source origin, if any - when
this is the first entry of the line. */
altsvc_flush(asi, srcalpnid, srchost, srcport);
- as = altsvc_createid(srchost, dsthost, dstlen,
+ as = altsvc_createid(srchost, strlen(srchost),
+ curlx_str(&dsthost),
+ curlx_strlen(&dsthost),
srcalpnid, dstalpnid,
srcport, dstport);
if(as) {
- /* The expires time also needs to take the Age: value (if any) into
- account. [See RFC 7838 section 3.1] */
- as->expires = maxage + time(NULL);
+ time_t secs = time(NULL);
+ /* The expires time also needs to take the Age: value (if any)
+ into account. [See RFC 7838 section 3.1] */
+ if(maxage > (TIME_T_MAX - secs))
+ as->expires = TIME_T_MAX;
+ else
+ as->expires = maxage + secs;
as->persist = persist;
Curl_llist_append(&asi->list, as, &as->node);
- infof(data, "Added alt-svc: %s:%d over %s", dsthost, dstport,
- Curl_alpnid2str(dstalpnid));
+ infof(data, "Added alt-svc: %.*s:%d over %s",
+ (int)curlx_strlen(&dsthost), curlx_str(&dsthost),
+ dstport, Curl_alpnid2str(dstalpnid));
}
}
}
else
break;
+
/* after the double quote there can be a comma if there is another
string or a semicolon if no more */
- if(*p == ',') {
- /* comma means another alternative is presented */
- p++;
- result = getalnum(&p, alpnbuf, sizeof(alpnbuf));
- if(result)
- break;
- }
+ if(curlx_str_single(&p, ','))
+ break;
+
+ /* comma means another alternative is present */
+ if(curlx_str_until(&p, &alpn, MAX_ALTSVC_LINE, '='))
+ break;
+ curlx_str_trimblanks(&alpn);
}
else
break;
- } while(*p && (*p != ';') && (*p != '\n') && (*p != '\r'));
+ } while(1);
return CURLE_OK;
}
diff --git a/libs/libcurl/src/altsvc.h b/libs/libcurl/src/altsvc.h
index bf427bb84f..8b2293a813 100644
--- a/libs/libcurl/src/altsvc.h
+++ b/libs/libcurl/src/altsvc.h
@@ -29,13 +29,6 @@
#include <curl/curl.h>
#include "llist.h"
-enum alpnid {
- ALPN_none = 0,
- ALPN_h1 = CURLALTSVC_H1,
- ALPN_h2 = CURLALTSVC_H2,
- ALPN_h3 = CURLALTSVC_H3
-};
-
struct althost {
char *host;
unsigned short port;
@@ -46,9 +39,9 @@ struct altsvc {
struct althost src;
struct althost dst;
time_t expires;
- bool persist;
- unsigned int prio;
struct Curl_llist_node node;
+ unsigned int prio;
+ BIT(persist);
};
struct altsvcinfo {
diff --git a/libs/libcurl/src/amigaos.c b/libs/libcurl/src/amigaos.c
index 9951d07312..fe61764a6e 100644
--- a/libs/libcurl/src/amigaos.c
+++ b/libs/libcurl/src/amigaos.c
@@ -184,7 +184,7 @@ int Curl_amiga_select(int nfds, fd_set *readfds, fd_set *writefds,
{
int r = WaitSelect(nfds, readfds, writefds, errorfds, timeout, 0);
/* Ensure Ctrl-C signal is actioned */
- if((r == -1) && (SOCKERRNO == EINTR))
+ if((r == -1) && (SOCKERRNO == SOCKEINTR))
raise(SIGINT);
return r;
}
@@ -196,12 +196,11 @@ int Curl_amiga_select(int nfds, fd_set *readfds, fd_set *writefds,
*/
struct Library *SocketBase = NULL;
-extern int errno, h_errno;
#ifdef __libnix__
void __request(const char *msg);
#else
-# define __request(msg) Printf(msg "\n\a")
+# define __request(msg) Printf((const unsigned char *)(msg "\n\a"), 0)
#endif
void Curl_amiga_cleanup(void)
@@ -215,7 +214,7 @@ void Curl_amiga_cleanup(void)
CURLcode Curl_amiga_init(void)
{
if(!SocketBase)
- SocketBase = OpenLibrary("bsdsocket.library", 4);
+ SocketBase = OpenLibrary((const unsigned char *)"bsdsocket.library", 4);
if(!SocketBase) {
__request("No TCP/IP Stack running!");
diff --git a/libs/libcurl/src/asyn-ares.c b/libs/libcurl/src/asyn-ares.c
index 1ce5d78a4f..e97810c1b9 100644
--- a/libs/libcurl/src/asyn-ares.c
+++ b/libs/libcurl/src/asyn-ares.c
@@ -24,14 +24,14 @@
#include "curl_setup.h"
+#ifdef CURLRES_ARES
+
/***********************************************************************
* Only for ares-enabled builds
* And only for functions that fulfill the asynch resolver backend API
* as defined in asyn.h, nothing else belongs in this file!
**********************************************************************/
-#ifdef CURLRES_ARES
-
#include <limits.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
@@ -54,25 +54,18 @@
#include "share.h"
#include "url.h"
#include "multiif.h"
-#include "inet_pton.h"
+#include "curlx/inet_pton.h"
#include "connect.h"
#include "select.h"
#include "progress.h"
-#include "timediff.h"
+#include "curlx/timediff.h"
+#include "httpsrr.h"
+#include "strdup.h"
-#if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \
- defined(_WIN32)
-# define CARES_STATICLIB
-#endif
#include <ares.h>
#include <ares_version.h> /* really old c-ares did not include this by
itself */
-#if ARES_VERSION >= 0x010500
-/* c-ares 1.5.0 or later, the callback proto is modified */
-#define HAVE_CARES_CALLBACK_TIMEOUTS 1
-#endif
-
#if ARES_VERSION >= 0x010601
/* IPv6 supported since 1.6.1 */
#define HAVE_CARES_IPV6 1
@@ -93,22 +86,18 @@
#define HAVE_CARES_GETADDRINFO 1
#endif
+#ifdef USE_HTTPSRR
+#if ARES_VERSION < 0x011c00
+#error "requires c-ares 1.28.0 or newer for HTTPSRR"
+#endif
+#define HTTPSRR_WORKS
+#endif
+
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
#include "memdebug.h"
-struct thread_data {
- int num_pending; /* number of outstanding c-ares requests */
- struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares
- parts */
- int last_status;
-#ifndef HAVE_CARES_GETADDRINFO
- struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */
-#endif
- char hostname[1];
-};
-
/* How long we are willing to wait for additional parallel responses after
obtaining a "definitive" one. For old c-ares without getaddrinfo.
@@ -124,12 +113,15 @@ struct thread_data {
static int ares_ver = 0;
+static CURLcode async_ares_set_dns_servers(struct Curl_easy *data,
+ bool reset_on_null);
+
/*
- * Curl_resolver_global_init() - the generic low-level asynchronous name
+ * Curl_async_global_init() - the generic low-level asynchronous name
* resolve API. Called from curl_global_init() to initialize global resolver
* environment. Initializes ares library.
*/
-int Curl_resolver_global_init(void)
+int Curl_async_global_init(void)
{
#ifdef CARES_HAVE_ARES_LIBRARY_INIT
if(ares_library_init(ARES_LIB_INIT_ALL)) {
@@ -141,12 +133,12 @@ int Curl_resolver_global_init(void)
}
/*
- * Curl_resolver_global_cleanup()
+ * Curl_async_global_cleanup()
*
* Called from curl_global_cleanup() to destroy global resolver environment.
* Deinitializes ares library.
*/
-void Curl_resolver_global_cleanup(void)
+void Curl_async_global_cleanup(void)
{
#ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
ares_library_cleanup();
@@ -160,25 +152,22 @@ static void sock_state_cb(void *data, ares_socket_t socket_fd,
struct Curl_easy *easy = data;
if(!readable && !writable) {
DEBUGASSERT(easy);
- Curl_multi_closed(easy, socket_fd);
+ Curl_multi_will_close(easy, socket_fd);
}
}
-/*
- * Curl_resolver_init()
- *
- * Called from curl_easy_init() -> Curl_open() to initialize resolver
- * URL-state specific environment ('resolver' member of the UrlState
- * structure). Fills the passed pointer by the initialized ares_channel.
- */
-CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
+static CURLcode async_ares_init(struct Curl_easy *data)
{
+ struct async_ares_ctx *ares = &data->state.async.ares;
int status;
struct ares_options options;
int optmask = ARES_OPT_SOCK_STATE_CB;
+ CURLcode rc = CURLE_OK;
+
options.sock_state_cb = sock_state_cb;
- options.sock_state_cb_data = easy;
+ options.sock_state_cb_data = data;
+ DEBUGASSERT(!ares->channel);
/*
if c ares < 1.20.0: curl set timeout to CARES_TIMEOUT_PER_ATTEMPT (2s)
@@ -194,240 +183,193 @@ CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
optmask |= ARES_OPT_TIMEOUTMS;
}
- status = ares_init_options((ares_channel*)resolver, &options, optmask);
+ status = ares_init_options(&ares->channel, &options, optmask);
if(status != ARES_SUCCESS) {
- if(status == ARES_ENOMEM)
- return CURLE_OUT_OF_MEMORY;
- else
- return CURLE_FAILED_INIT;
+ ares->channel = NULL;
+ rc = (status == ARES_ENOMEM) ?
+ CURLE_OUT_OF_MEMORY : CURLE_FAILED_INIT;
+ goto out;
}
- return CURLE_OK;
- /* make sure that all other returns from this function should destroy the
- ares channel before returning error! */
+
+ rc = async_ares_set_dns_servers(data, FALSE);
+ if(rc && rc != CURLE_NOT_BUILT_IN)
+ goto out;
+
+ rc = Curl_async_ares_set_dns_interface(data);
+ if(rc && rc != CURLE_NOT_BUILT_IN)
+ goto out;
+
+ rc = Curl_async_ares_set_dns_local_ip4(data);
+ if(rc && rc != CURLE_NOT_BUILT_IN)
+ goto out;
+
+ rc = Curl_async_ares_set_dns_local_ip6(data);
+ if(rc && rc != CURLE_NOT_BUILT_IN)
+ goto out;
+
+ rc = CURLE_OK;
+
+out:
+ if(rc && ares->channel) {
+ ares_destroy(ares->channel);
+ ares->channel = NULL;
+ }
+ return rc;
}
-/*
- * Curl_resolver_cleanup()
- *
- * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
- * URL-state specific environment ('resolver' member of the UrlState
- * structure). Destroys the ares channel.
- */
-void Curl_resolver_cleanup(void *resolver)
+static CURLcode async_ares_init_lazy(struct Curl_easy *data)
{
- ares_destroy((ares_channel)resolver);
+ struct async_ares_ctx *ares = &data->state.async.ares;
+ if(!ares->channel)
+ return async_ares_init(data);
+ return CURLE_OK;
}
-/*
- * Curl_resolver_duphandle()
- *
- * Called from curl_easy_duphandle() to duplicate resolver URL-state specific
- * environment ('resolver' member of the UrlState structure). Duplicates the
- * 'from' ares channel and passes the resulting channel to the 'to' pointer.
- */
-CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from)
+CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl)
{
- (void)from;
- /*
- * it would be better to call ares_dup instead, but right now
- * it is not possible to set 'sock_state_cb_data' outside of
- * ares_init_options
- */
- return Curl_resolver_init(easy, to);
+ struct async_ares_ctx *ares = &data->state.async.ares;
+ CURLcode result = CURLE_OK;
+ if(!ares->channel) {
+ result = async_ares_init(data);
+ }
+ *impl = ares->channel;
+ return result;
}
-static void destroy_async_data(struct Curl_async *async);
+static void async_ares_cleanup(struct Curl_easy *data);
-/*
- * Cancel all possibly still on-going resolves for this connection.
- */
-void Curl_resolver_cancel(struct Curl_easy *data)
+void Curl_async_ares_shutdown(struct Curl_easy *data)
{
- DEBUGASSERT(data);
- if(data->state.async.resolver)
- ares_cancel((ares_channel)data->state.async.resolver);
- destroy_async_data(&data->state.async);
+ struct async_ares_ctx *ares = &data->state.async.ares;
+ if(ares->channel)
+ ares_cancel(ares->channel);
+ async_ares_cleanup(data);
}
-/*
- * We are equivalent to Curl_resolver_cancel() for the c-ares resolver. We
- * never block.
- */
-void Curl_resolver_kill(struct Curl_easy *data)
+void Curl_async_ares_destroy(struct Curl_easy *data)
{
- /* We do not need to check the resolver state because we can be called safely
- at any time and we always do the same thing. */
- Curl_resolver_cancel(data);
+ struct async_ares_ctx *ares = &data->state.async.ares;
+ Curl_async_ares_shutdown(data);
+ if(ares->channel) {
+ ares_destroy(ares->channel);
+ ares->channel = NULL;
+ }
}
/*
- * destroy_async_data() cleans up async resolver data.
+ * async_ares_cleanup() cleans up async resolver data.
*/
-static void destroy_async_data(struct Curl_async *async)
+static void async_ares_cleanup(struct Curl_easy *data)
{
- if(async->tdata) {
- struct thread_data *res = async->tdata;
- if(res) {
- if(res->temp_ai) {
- Curl_freeaddrinfo(res->temp_ai);
- res->temp_ai = NULL;
- }
- free(res);
- }
- async->tdata = NULL;
+ struct async_ares_ctx *ares = &data->state.async.ares;
+ if(ares->temp_ai) {
+ Curl_freeaddrinfo(ares->temp_ai);
+ ares->temp_ai = NULL;
}
+#ifdef USE_HTTPSRR
+ Curl_httpsrr_cleanup(&ares->hinfo);
+#endif
}
/*
- * Curl_resolver_getsock() is called when someone from the outside world
- * (using curl_multi_fdset()) wants to get our fd_set setup and we are talking
- * with ares. The caller must make sure that this function is only called when
- * we have a working ares channel.
- *
- * Returns: sockets-in-use-bitmap
+ * Curl_async_getsock() is called when someone from the outside world
+ * (using curl_multi_fdset()) wants to get our fd_set setup.
*/
-int Curl_resolver_getsock(struct Curl_easy *data,
- curl_socket_t *socks)
+int Curl_async_getsock(struct Curl_easy *data, curl_socket_t *socks)
{
- struct timeval maxtime = { CURL_TIMEOUT_RESOLVE, 0 };
- struct timeval timebuf;
- int max = ares_getsock((ares_channel)data->state.async.resolver,
- (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE);
- struct timeval *timeout =
- ares_timeout((ares_channel)data->state.async.resolver, &maxtime, &timebuf);
- timediff_t milli = curlx_tvtoms(timeout);
- Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
- return max;
+ struct async_ares_ctx *ares = &data->state.async.ares;
+ DEBUGASSERT(ares->channel);
+ return Curl_ares_getsock(data, ares->channel, socks);
}
/*
- * waitperform()
- *
- * 1) Ask ares what sockets it currently plays with, then
- * 2) wait for the timeout period to check for action on ares' sockets.
- * 3) tell ares to act on all the sockets marked as "with action"
- *
- * return number of sockets it worked on, or -1 on error
- */
-
-static int waitperform(struct Curl_easy *data, timediff_t timeout_ms)
-{
- int nfds;
- int bitmask;
- ares_socket_t socks[ARES_GETSOCK_MAXNUM];
- struct pollfd pfd[ARES_GETSOCK_MAXNUM];
- int i;
- int num = 0;
-
- bitmask = ares_getsock((ares_channel)data->state.async.resolver, socks,
- ARES_GETSOCK_MAXNUM);
-
- for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
- pfd[i].events = 0;
- pfd[i].revents = 0;
- if(ARES_GETSOCK_READABLE(bitmask, i)) {
- pfd[i].fd = socks[i];
- pfd[i].events |= POLLRDNORM|POLLIN;
- }
- if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
- pfd[i].fd = socks[i];
- pfd[i].events |= POLLWRNORM|POLLOUT;
- }
- if(pfd[i].events)
- num++;
- else
- break;
- }
-
- if(num) {
- nfds = Curl_poll(pfd, (unsigned int)num, timeout_ms);
- if(nfds < 0)
- return -1;
- }
- else
- nfds = 0;
-
- if(!nfds)
- /* Call ares_process() unconditionally here, even if we simply timed out
- above, as otherwise the ares name resolve will not timeout! */
- ares_process_fd((ares_channel)data->state.async.resolver, ARES_SOCKET_BAD,
- ARES_SOCKET_BAD);
- else {
- /* move through the descriptors and ask for processing on them */
- for(i = 0; i < num; i++)
- ares_process_fd((ares_channel)data->state.async.resolver,
- (pfd[i].revents & (POLLRDNORM|POLLIN)) ?
- pfd[i].fd : ARES_SOCKET_BAD,
- (pfd[i].revents & (POLLWRNORM|POLLOUT)) ?
- pfd[i].fd : ARES_SOCKET_BAD);
- }
- return nfds;
-}
-
-/*
- * Curl_resolver_is_resolved() is called repeatedly to check if a previous
+ * Curl_async_is_resolved() is called repeatedly to check if a previous
* name resolve request has completed. It should also make sure to time-out if
* the operation seems to take too long.
*
* Returns normal CURLcode errors.
*/
-CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
- struct Curl_dns_entry **dns)
+CURLcode Curl_async_is_resolved(struct Curl_easy *data,
+ struct Curl_dns_entry **dns)
{
- struct thread_data *res = data->state.async.tdata;
+ struct async_ares_ctx *ares = &data->state.async.ares;
CURLcode result = CURLE_OK;
DEBUGASSERT(dns);
*dns = NULL;
- if(waitperform(data, 0) < 0)
+ if(data->state.async.done) {
+ *dns = data->state.async.dns;
+ return CURLE_OK;
+ }
+
+ if(Curl_ares_perform(ares->channel, 0) < 0)
return CURLE_UNRECOVERABLE_POLL;
#ifndef HAVE_CARES_GETADDRINFO
/* Now that we have checked for any last minute results above, see if there
are any responses still pending when the EXPIRE_HAPPY_EYEBALLS_DNS timer
expires. */
- if(res
- && res->num_pending
+ if(ares->num_pending
/* This is only set to non-zero if the timer was started. */
- && (res->happy_eyeballs_dns_time.tv_sec
- || res->happy_eyeballs_dns_time.tv_usec)
- && (Curl_timediff(Curl_now(), res->happy_eyeballs_dns_time)
+ && (ares->happy_eyeballs_dns_time.tv_sec
+ || ares->happy_eyeballs_dns_time.tv_usec)
+ && (curlx_timediff(curlx_now(), ares->happy_eyeballs_dns_time)
>= HAPPY_EYEBALLS_DNS_TIMEOUT)) {
/* Remember that the EXPIRE_HAPPY_EYEBALLS_DNS timer is no longer
running. */
- memset(
- &res->happy_eyeballs_dns_time, 0, sizeof(res->happy_eyeballs_dns_time));
+ memset(&ares->happy_eyeballs_dns_time, 0,
+ sizeof(ares->happy_eyeballs_dns_time));
/* Cancel the raw c-ares request, which will fire query_completed_cb() with
ARES_ECANCELLED synchronously for all pending responses. This will
leave us with res->num_pending == 0, which is perfect for the next
block. */
- ares_cancel((ares_channel)data->state.async.resolver);
- DEBUGASSERT(res->num_pending == 0);
+ ares_cancel(ares->channel);
+ DEBUGASSERT(ares->num_pending == 0);
}
#endif
- if(res && !res->num_pending) {
- (void)Curl_addrinfo_callback(data, res->last_status, res->temp_ai);
- /* temp_ai ownership is moved to the connection, so we need not free-up
- them */
- res->temp_ai = NULL;
-
- if(!data->state.async.dns)
+ if(!ares->num_pending) {
+ /* all c-ares operations done, what is the result to report? */
+ Curl_resolv_unlink(data, &data->state.async.dns);
+ data->state.async.done = TRUE;
+ result = ares->result;
+ if(ares->last_status == CURL_ASYNC_SUCCESS && !result) {
+ data->state.async.dns =
+ Curl_dnscache_mk_entry(data, ares->temp_ai,
+ data->state.async.hostname, 0,
+ data->state.async.port, FALSE);
+ ares->temp_ai = NULL; /* temp_ai now owned by entry */
+#ifdef HTTPSRR_WORKS
+ if(data->state.async.dns) {
+ struct Curl_https_rrinfo *lhrr = Curl_httpsrr_dup_move(&ares->hinfo);
+ if(!lhrr)
+ result = CURLE_OUT_OF_MEMORY;
+ else
+ data->state.async.dns->hinfo = lhrr;
+ }
+#endif
+ if(!result && data->state.async.dns)
+ result = Curl_dnscache_add(data, data->state.async.dns);
+ }
+ /* if we have not found anything, report the proper
+ * CURLE_COULDNT_RESOLVE_* code */
+ if(!result && !data->state.async.dns)
result = Curl_resolver_error(data);
- else
- *dns = data->state.async.dns;
-
- destroy_async_data(&data->state.async);
+ if(result)
+ Curl_resolv_unlink(data, &data->state.async.dns);
+ *dns = data->state.async.dns;
+ CURL_TRC_DNS(data, "is_resolved() result=%d, dns=%sfound",
+ result, *dns ? "" : "not ");
+ async_ares_cleanup(data);
}
-
return result;
}
/*
- * Curl_resolver_wait_resolv()
+ * Curl_async_await()
*
* Waits for a resolve to finish. This function should be avoided since using
* this risk getting the multi interface to "hang".
@@ -437,12 +379,13 @@ CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
* Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
* CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
*/
-CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
- struct Curl_dns_entry **entry)
+CURLcode Curl_async_await(struct Curl_easy *data,
+ struct Curl_dns_entry **entry)
{
+ struct async_ares_ctx *ares = &data->state.async.ares;
CURLcode result = CURLE_OK;
timediff_t timeout;
- struct curltime now = Curl_now();
+ struct curltime now = curlx_now();
DEBUGASSERT(entry);
*entry = NULL; /* clear on entry */
@@ -471,7 +414,7 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
store.tv_sec = itimeout/1000;
store.tv_usec = (itimeout%1000)*1000;
- tvp = ares_timeout((ares_channel)data->state.async.resolver, &store, &tv);
+ tvp = ares_timeout(ares->channel, &store, &tv);
/* use the timeout period ares returned to us above if less than one
second is left, otherwise just use 1000ms to make sure the progress
@@ -481,18 +424,18 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
else
timeout_ms = 1000;
- if(waitperform(data, timeout_ms) < 0)
+ if(Curl_ares_perform(ares->channel, timeout_ms) < 0)
return CURLE_UNRECOVERABLE_POLL;
- result = Curl_resolver_is_resolved(data, entry);
+ result = Curl_async_is_resolved(data, entry);
if(result || data->state.async.done)
break;
if(Curl_pgrsUpdate(data))
result = CURLE_ABORTED_BY_CALLBACK;
else {
- struct curltime now2 = Curl_now();
- timediff_t timediff = Curl_timediff(now2, now); /* spent time */
+ struct curltime now2 = curlx_now();
+ timediff_t timediff = curlx_timediff(now2, now); /* spent time */
if(timediff <= 0)
timeout -= 1; /* always deduct at least 1 */
else if(timediff > timeout)
@@ -504,163 +447,155 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
if(timeout < 0)
result = CURLE_OPERATION_TIMEDOUT;
}
- if(result)
- /* failure, so we cancel the ares operation */
- ares_cancel((ares_channel)data->state.async.resolver);
/* Operation complete, if the lookup was successful we now have the entry
in the cache. */
+ data->state.async.done = TRUE;
if(entry)
*entry = data->state.async.dns;
if(result)
- /* close the connection, since we cannot return failure here without
- cleaning up this connection properly. */
- connclose(data->conn, "c-ares resolve failed");
-
+ ares_cancel(ares->channel);
return result;
}
#ifndef HAVE_CARES_GETADDRINFO
/* Connects results to the list */
-static void compound_results(struct thread_data *res,
- struct Curl_addrinfo *ai)
+static void async_addr_concat(struct Curl_addrinfo **pbase,
+ struct Curl_addrinfo *ai)
{
if(!ai)
return;
+ /* When adding `ai` to an existing address list, we prefer ipv6
+ * to be in front. */
#ifdef USE_IPV6 /* CURLRES_IPV6 */
- if(res->temp_ai && res->temp_ai->ai_family == PF_INET6) {
- /* We have results already, put the new IPv6 entries at the head of the
- list. */
- struct Curl_addrinfo *temp_ai_tail = res->temp_ai;
-
- while(temp_ai_tail->ai_next)
- temp_ai_tail = temp_ai_tail->ai_next;
-
- temp_ai_tail->ai_next = ai;
+ if(*pbase && (*pbase)->ai_family == PF_INET6) {
+ /* ipv6 already in front, append `ai` */
+ struct Curl_addrinfo *tail = *pbase;
+ while(tail->ai_next)
+ tail = tail->ai_next;
+ tail->ai_next = ai;
}
else
#endif /* CURLRES_IPV6 */
{
- /* Add the new results to the list of old results. */
- struct Curl_addrinfo *ai_tail = ai;
- while(ai_tail->ai_next)
- ai_tail = ai_tail->ai_next;
-
- ai_tail->ai_next = res->temp_ai;
- res->temp_ai = ai;
+ /* prepend to the (possibly) existing list. */
+ struct Curl_addrinfo *tail = ai;
+ while(tail->ai_next)
+ tail = tail->ai_next;
+ tail->ai_next = *pbase;
+ *pbase = ai;
}
}
/*
* ares_query_completed_cb() is the callback that ares will call when
- * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(),
- * when using ares, is completed either successfully or with failure.
+ * the host query initiated by ares_gethostbyname() from
+ * Curl_async_getaddrinfo(), when using ares, is completed either
+ * successfully or with failure.
*/
-static void query_completed_cb(void *arg, /* (struct connectdata *) */
- int status,
-#ifdef HAVE_CARES_CALLBACK_TIMEOUTS
- int timeouts,
-#endif
- struct hostent *hostent)
+static void async_ares_hostbyname_cb(void *user_data,
+ int status,
+ int timeouts,
+ struct hostent *hostent)
{
- struct Curl_easy *data = (struct Curl_easy *)arg;
- struct thread_data *res;
+ struct Curl_easy *data = (struct Curl_easy *)user_data;
+ struct async_ares_ctx *ares = &data->state.async.ares;
-#ifdef HAVE_CARES_CALLBACK_TIMEOUTS
(void)timeouts; /* ignored */
-#endif
if(ARES_EDESTRUCTION == status)
/* when this ares handle is getting destroyed, the 'arg' pointer may not
be valid so only defer it when we know the 'status' says its fine! */
return;
- res = data->state.async.tdata;
- if(res) {
- res->num_pending--;
+ if(CURL_ASYNC_SUCCESS == status) {
+ ares->last_status = status; /* one success overrules any error */
+ async_addr_concat(&ares->temp_ai,
+ Curl_he2ai(hostent, data->state.async.port));
+ }
+ else if(ares->last_status != ARES_SUCCESS) {
+ /* no success so far, remember error */
+ ares->last_status = status;
+ }
- if(CURL_ASYNC_SUCCESS == status) {
- struct Curl_addrinfo *ai = Curl_he2ai(hostent, data->state.async.port);
- if(ai) {
- compound_results(res, ai);
- }
- }
- /* A successful result overwrites any previous error */
- if(res->last_status != ARES_SUCCESS)
- res->last_status = status;
-
- /* If there are responses still pending, we presume they must be the
- complementary IPv4 or IPv6 lookups that we started in parallel in
- Curl_resolver_getaddrinfo() (for Happy Eyeballs). If we have got a
- "definitive" response from one of a set of parallel queries, we need to
- think about how long we are willing to wait for more responses. */
- if(res->num_pending
- /* Only these c-ares status values count as "definitive" for these
- purposes. For example, ARES_ENODATA is what we expect when there is
- no IPv6 entry for a domain name, and that is not a reason to get more
- aggressive in our timeouts for the other response. Other errors are
- either a result of bad input (which should affect all parallel
- requests), local or network conditions, non-definitive server
- responses, or us cancelling the request. */
- && (status == ARES_SUCCESS || status == ARES_ENOTFOUND)) {
- /* Right now, there can only be up to two parallel queries, so do not
- bother handling any other cases. */
- DEBUGASSERT(res->num_pending == 1);
-
- /* it is possible that one of these parallel queries could succeed
- quickly, but the other could always fail or timeout (when we are
- talking to a pool of DNS servers that can only successfully resolve
- IPv4 address, for example).
-
- it is also possible that the other request could always just take
- longer because it needs more time or only the second DNS server can
- fulfill it successfully. But, to align with the philosophy of Happy
- Eyeballs, we do not want to wait _too_ long or users will think
- requests are slow when IPv6 lookups do not actually work (but IPv4
- ones do).
-
- So, now that we have a usable answer (some IPv4 addresses, some IPv6
- addresses, or "no such domain"), we start a timeout for the remaining
- pending responses. Even though it is typical that this resolved
- request came back quickly, that needn't be the case. It might be that
- this completing request did not get a result from the first DNS
- server or even the first round of the whole DNS server pool. So it
- could already be quite some time after we issued the DNS queries in
- the first place. Without modifying c-ares, we cannot know exactly
- where in its retry cycle we are. We could guess based on how much
- time has gone by, but it does not really matter. Happy Eyeballs tells
- us that, given usable information in hand, we simply do not want to
- wait "too much longer" after we get a result.
-
- We simply wait an additional amount of time equal to the default
- c-ares query timeout. That is enough time for a typical parallel
- response to arrive without being "too long". Even on a network
- where one of the two types of queries is failing or timing out
- constantly, this will usually mean we wait a total of the default
- c-ares timeout (5 seconds) plus the round trip time for the successful
- request, which seems bearable. The downside is that c-ares might race
- with us to issue one more retry just before we give up, but it seems
- better to "waste" that request instead of trying to guess the perfect
- timeout to prevent it. After all, we do not even know where in the
- c-ares retry cycle each request is.
- */
- res->happy_eyeballs_dns_time = Curl_now();
- Curl_expire(data, HAPPY_EYEBALLS_DNS_TIMEOUT,
- EXPIRE_HAPPY_EYEBALLS_DNS);
- }
+ ares->num_pending--;
+
+ CURL_TRC_DNS(data, "ares: hostbyname done, status=%d, pending=%d, "
+ "addr=%sfound",
+ status, ares->num_pending, ares->temp_ai ? "" : "not ");
+ /* If there are responses still pending, we presume they must be the
+ complementary IPv4 or IPv6 lookups that we started in parallel in
+ Curl_async_getaddrinfo() (for Happy Eyeballs). If we have got a
+ "definitive" response from one of a set of parallel queries, we need to
+ think about how long we are willing to wait for more responses. */
+ if(ares->num_pending
+ /* Only these c-ares status values count as "definitive" for these
+ purposes. For example, ARES_ENODATA is what we expect when there is
+ no IPv6 entry for a domain name, and that is not a reason to get more
+ aggressive in our timeouts for the other response. Other errors are
+ either a result of bad input (which should affect all parallel
+ requests), local or network conditions, non-definitive server
+ responses, or us cancelling the request. */
+ && (status == ARES_SUCCESS || status == ARES_ENOTFOUND)) {
+ /* Right now, there can only be up to two parallel queries, so do not
+ bother handling any other cases. */
+ DEBUGASSERT(ares->num_pending == 1);
+
+ /* it is possible that one of these parallel queries could succeed
+ quickly, but the other could always fail or timeout (when we are
+ talking to a pool of DNS servers that can only successfully resolve
+ IPv4 address, for example).
+
+ it is also possible that the other request could always just take
+ longer because it needs more time or only the second DNS server can
+ fulfill it successfully. But, to align with the philosophy of Happy
+ Eyeballs, we do not want to wait _too_ long or users will think
+ requests are slow when IPv6 lookups do not actually work (but IPv4
+ ones do).
+
+ So, now that we have a usable answer (some IPv4 addresses, some IPv6
+ addresses, or "no such domain"), we start a timeout for the remaining
+ pending responses. Even though it is typical that this resolved
+ request came back quickly, that needn't be the case. It might be that
+ this completing request did not get a result from the first DNS
+ server or even the first round of the whole DNS server pool. So it
+ could already be quite some time after we issued the DNS queries in
+ the first place. Without modifying c-ares, we cannot know exactly
+ where in its retry cycle we are. We could guess based on how much
+ time has gone by, but it does not really matter. Happy Eyeballs tells
+ us that, given usable information in hand, we simply do not want to
+ wait "too much longer" after we get a result.
+
+ We simply wait an additional amount of time equal to the default
+ c-ares query timeout. That is enough time for a typical parallel
+ response to arrive without being "too long". Even on a network
+ where one of the two types of queries is failing or timing out
+ constantly, this will usually mean we wait a total of the default
+ c-ares timeout (5 seconds) plus the round trip time for the successful
+ request, which seems bearable. The downside is that c-ares might race
+ with us to issue one more retry just before we give up, but it seems
+ better to "waste" that request instead of trying to guess the perfect
+ timeout to prevent it. After all, we do not even know where in the
+ c-ares retry cycle each request is.
+ */
+ ares->happy_eyeballs_dns_time = curlx_now();
+ Curl_expire(data, HAPPY_EYEBALLS_DNS_TIMEOUT,
+ EXPIRE_HAPPY_EYEBALLS_DNS);
}
}
+
#else
/* c-ares 1.16.0 or later */
/*
- * ares2addr() converts an address list provided by c-ares to an internal
- * libcurl compatible list
+ * async_ares_node2addr() converts an address list provided by c-ares
+ * to an internal libcurl compatible list.
*/
-static struct Curl_addrinfo *ares2addr(struct ares_addrinfo_node *node)
+static struct Curl_addrinfo *
+async_ares_node2addr(struct ares_addrinfo_node *node)
{
/* traverse the ares_addrinfo_node list */
struct ares_addrinfo_node *ai;
@@ -730,129 +665,183 @@ static struct Curl_addrinfo *ares2addr(struct ares_addrinfo_node *node)
return cafirst;
}
-static void addrinfo_cb(void *arg, int status, int timeouts,
- struct ares_addrinfo *result)
+static void async_ares_addrinfo_cb(void *user_data, int status, int timeouts,
+ struct ares_addrinfo *result)
{
- struct Curl_easy *data = (struct Curl_easy *)arg;
- struct thread_data *res = data->state.async.tdata;
+ struct Curl_easy *data = (struct Curl_easy *)user_data;
+ struct async_ares_ctx *ares = &data->state.async.ares;
(void)timeouts;
+ CURL_TRC_DNS(data, "asyn-ares: addrinfo callback, status=%d", status);
if(ARES_SUCCESS == status) {
- res->temp_ai = ares2addr(result->nodes);
- res->last_status = CURL_ASYNC_SUCCESS;
+ ares->temp_ai = async_ares_node2addr(result->nodes);
+ ares->last_status = CURL_ASYNC_SUCCESS;
ares_freeaddrinfo(result);
}
- res->num_pending--;
+ ares->num_pending--;
+ CURL_TRC_DNS(data, "ares: addrinfo done, status=%d, pending=%d, "
+ "addr=%sfound",
+ status, ares->num_pending, ares->temp_ai ? "" : "not ");
}
#endif
+
+#ifdef USE_HTTPSRR
+static void async_ares_rr_done(void *user_data, ares_status_t status,
+ size_t timeouts,
+ const ares_dns_record_t *dnsrec)
+{
+ struct Curl_easy *data = user_data;
+ struct async_ares_ctx *ares = &data->state.async.ares;
+
+ (void)timeouts;
+ --ares->num_pending;
+ CURL_TRC_DNS(data, "ares: httpsrr done, status=%d, pending=%d, "
+ "dnsres=%sfound",
+ status, ares->num_pending,
+ (dnsrec &&
+ ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER)) ?
+ "" : "not ");
+ if((ARES_SUCCESS != status) || !dnsrec)
+ return;
+ ares->result = Curl_httpsrr_from_ares(data, dnsrec, &ares->hinfo);
+}
+#endif /* USE_HTTPSRR */
+
/*
- * Curl_resolver_getaddrinfo() - when using ares
+ * Curl_async_getaddrinfo() - when using ares
*
* Returns name information about the given hostname and port number. If
* successful, the 'hostent' is returned and the fourth argument will point to
* memory we need to free after use. That memory *MUST* be freed with
* Curl_freeaddrinfo(), nothing else.
*/
-struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
- const char *hostname,
- int port,
- int *waitp)
+struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
+ const char *hostname,
+ int port,
+ int ip_version,
+ int *waitp)
{
- struct thread_data *res = NULL;
- size_t namelen = strlen(hostname);
+ struct async_ares_ctx *ares = &data->state.async.ares;
*waitp = 0; /* default to synchronous response */
- res = calloc(1, sizeof(struct thread_data) + namelen);
- if(res) {
- strcpy(res->hostname, hostname);
- data->state.async.hostname = res->hostname;
- data->state.async.port = port;
- data->state.async.done = FALSE; /* not done */
- data->state.async.status = 0; /* clear */
- data->state.async.dns = NULL; /* clear */
- data->state.async.tdata = res;
+ if(async_ares_init_lazy(data))
+ return NULL;
- /* initial status - failed */
- res->last_status = ARES_ENOTFOUND;
+ data->state.async.done = FALSE; /* not done */
+ data->state.async.dns = NULL; /* clear */
+ data->state.async.port = port;
+ data->state.async.ip_version = ip_version;
+ data->state.async.hostname = strdup(hostname);
+ if(!data->state.async.hostname)
+ return NULL;
+
+ /* initial status - failed */
+ ares->last_status = ARES_ENOTFOUND;
#ifdef HAVE_CARES_GETADDRINFO
- {
- struct ares_addrinfo_hints hints;
- char service[12];
- int pf = PF_INET;
- memset(&hints, 0, sizeof(hints));
+ {
+ struct ares_addrinfo_hints hints;
+ char service[12];
+ int pf = PF_INET;
+ memset(&hints, 0, sizeof(hints));
#ifdef CURLRES_IPV6
- if((data->conn->ip_version != CURL_IPRESOLVE_V4) &&
- Curl_ipv6works(data)) {
- /* The stack seems to be IPv6-enabled */
- if(data->conn->ip_version == CURL_IPRESOLVE_V6)
- pf = PF_INET6;
- else
- pf = PF_UNSPEC;
- }
-#endif /* CURLRES_IPV6 */
- hints.ai_family = pf;
- hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP) ?
- SOCK_STREAM : SOCK_DGRAM;
- /* Since the service is a numerical one, set the hint flags
- * accordingly to save a call to getservbyname in inside C-Ares
- */
- hints.ai_flags = ARES_AI_NUMERICSERV;
- msnprintf(service, sizeof(service), "%d", port);
- res->num_pending = 1;
- ares_getaddrinfo((ares_channel)data->state.async.resolver, hostname,
- service, &hints, addrinfo_cb, data);
+ if((ip_version != CURL_IPRESOLVE_V4) &&
+ Curl_ipv6works(data)) {
+ /* The stack seems to be IPv6-enabled */
+ if(ip_version == CURL_IPRESOLVE_V6)
+ pf = PF_INET6;
+ else
+ pf = PF_UNSPEC;
}
+#endif /* CURLRES_IPV6 */
+ CURL_TRC_DNS(data, "asyn-ares: fire off getaddrinfo for %s",
+ (pf == PF_UNSPEC) ? "A+AAAA" :
+ ((pf == PF_INET) ? "A" : "AAAA"));
+ hints.ai_family = pf;
+ hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP) ?
+ SOCK_STREAM : SOCK_DGRAM;
+ /* Since the service is a numerical one, set the hint flags
+ * accordingly to save a call to getservbyname in inside C-Ares
+ */
+ hints.ai_flags = ARES_AI_NUMERICSERV;
+ msnprintf(service, sizeof(service), "%d", port);
+ ares->num_pending = 1;
+ ares_getaddrinfo(ares->channel, data->state.async.hostname,
+ service, &hints, async_ares_addrinfo_cb, data);
+ }
#else
#ifdef HAVE_CARES_IPV6
- if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
- /* The stack seems to be IPv6-enabled */
- res->num_pending = 2;
-
- /* areschannel is already setup in the Curl_open() function */
- ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
- PF_INET, query_completed_cb, data);
- ares_gethostbyname((ares_channel)data->state.async.resolver, hostname,
- PF_INET6, query_completed_cb, data);
- }
- else
+ if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
+ /* The stack seems to be IPv6-enabled */
+ /* areschannel is already setup in the Curl_open() function */
+ CURL_TRC_DNS(data, "asyn-ares: fire off query for A");
+ ares_gethostbyname(ares->channel, hostname, PF_INET,
+ async_ares_hostbyname_cb, data);
+ CURL_TRC_DNS(data, "asyn-ares: fire off query for AAAA");
+ ares->num_pending = 2;
+ ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET6,
+ async_ares_hostbyname_cb, data);
+ }
+ else
#endif
- {
- res->num_pending = 1;
-
- /* areschannel is already setup in the Curl_open() function */
- ares_gethostbyname((ares_channel)data->state.async.resolver,
- hostname, PF_INET,
- query_completed_cb, data);
- }
+ {
+ /* areschannel is already setup in the Curl_open() function */
+ CURL_TRC_DNS(data, "asyn-ares: fire off query for A");
+ ares->num_pending = 1;
+ ares_gethostbyname(ares->channel, data->state.async.hostname, PF_INET,
+ async_ares_hostbyname_cb, data);
+ }
#endif
- *waitp = 1; /* expect asynchronous response */
+#ifdef USE_HTTPSRR
+ {
+ CURL_TRC_DNS(data, "asyn-ares: fire off query for HTTPSRR");
+ memset(&ares->hinfo, 0, sizeof(ares->hinfo));
+ ares->hinfo.port = -1;
+ ares->num_pending++; /* one more */
+ ares_query_dnsrec(ares->channel, data->state.async.hostname,
+ ARES_CLASS_IN, ARES_REC_TYPE_HTTPS,
+ async_ares_rr_done, data, NULL);
}
+#endif
+ *waitp = 1; /* expect asynchronous response */
+
return NULL; /* no struct yet */
}
-CURLcode Curl_set_dns_servers(struct Curl_easy *data,
- char *servers)
+/* Set what DNS server are is to use. This is called in 2 situations:
+ * 1. when the application does 'CURLOPT_DNS_SERVERS' and passing NULL
+ * means any previous set value should be unset. Which means
+ * we need to destroy and create the are channel anew, if there is one.
+ * 2. When we lazy init the ares channel and NULL means that there
+ * are no preferences and we do not reset any existing channel. */
+static CURLcode async_ares_set_dns_servers(struct Curl_easy *data,
+ bool reset_on_null)
{
+ struct async_ares_ctx *ares = &data->state.async.ares;
CURLcode result = CURLE_NOT_BUILT_IN;
- int ares_result;
-
- /* If server is NULL or empty, this would purge all DNS servers
- * from ares library, which will cause any and all queries to fail.
- * So, just return OK if none are configured and do not actually make
- * any changes to c-ares. This lets c-ares use its defaults, which
- * it gets from the OS (for instance from /etc/resolv.conf on Linux).
- */
- if(!(servers && servers[0]))
+ const char *servers = data->set.str[STRING_DNS_SERVERS];
+ int ares_result = ARES_SUCCESS;
+
+#if defined(CURLDEBUG) && defined(HAVE_CARES_SERVERS_CSV)
+ if(getenv("CURL_DNS_SERVER"))
+ servers = getenv("CURL_DNS_SERVER");
+#endif
+
+ if(!servers) {
+ if(reset_on_null) {
+ Curl_async_destroy(data);
+ }
return CURLE_OK;
+ }
#ifdef HAVE_CARES_SERVERS_CSV
+ /* if channel is not there, this is just a parameter check */
+ if(ares->channel)
#ifdef HAVE_CARES_PORTS_CSV
- ares_result = ares_set_servers_ports_csv(data->state.async.resolver,
- servers);
+ ares_result = ares_set_servers_ports_csv(ares->channel, servers);
#else
- ares_result = ares_set_servers_csv(data->state.async.resolver, servers);
+ ares_result = ares_set_servers_csv(ares->channel, servers);
#endif
switch(ares_result) {
case ARES_SUCCESS:
@@ -876,14 +865,23 @@ CURLcode Curl_set_dns_servers(struct Curl_easy *data,
return result;
}
-CURLcode Curl_set_dns_interface(struct Curl_easy *data,
- const char *interf)
+CURLcode Curl_async_ares_set_dns_servers(struct Curl_easy *data)
+{
+ return async_ares_set_dns_servers(data, TRUE);
+}
+
+CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data)
{
#ifdef HAVE_CARES_LOCAL_DEV
+ struct async_ares_ctx *ares = &data->state.async.ares;
+ const char *interf = data->set.str[STRING_DNS_INTERFACE];
+
if(!interf)
interf = "";
- ares_set_local_dev((ares_channel)data->state.async.resolver, interf);
+ /* if channel is not there, this is just a parameter check */
+ if(ares->channel)
+ ares_set_local_dev(ares->channel, interf);
return CURLE_OK;
#else /* c-ares version too old! */
@@ -893,24 +891,26 @@ CURLcode Curl_set_dns_interface(struct Curl_easy *data,
#endif
}
-CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
- const char *local_ip4)
+CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data)
{
#ifdef HAVE_CARES_SET_LOCAL
+ struct async_ares_ctx *ares = &data->state.async.ares;
struct in_addr a4;
+ const char *local_ip4 = data->set.str[STRING_DNS_LOCAL_IP4];
if((!local_ip4) || (local_ip4[0] == 0)) {
a4.s_addr = 0; /* disabled: do not bind to a specific address */
}
else {
- if(Curl_inet_pton(AF_INET, local_ip4, &a4) != 1) {
+ if(curlx_inet_pton(AF_INET, local_ip4, &a4) != 1) {
DEBUGF(infof(data, "bad DNS IPv4 address"));
return CURLE_BAD_FUNCTION_ARGUMENT;
}
}
- ares_set_local_ip4((ares_channel)data->state.async.resolver,
- ntohl(a4.s_addr));
+ /* if channel is not there yet, this is just a parameter check */
+ if(ares->channel)
+ ares_set_local_ip4(ares->channel, ntohl(a4.s_addr));
return CURLE_OK;
#else /* c-ares version too old! */
@@ -920,30 +920,33 @@ CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
#endif
}
-CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
- const char *local_ip6)
+CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data)
{
#if defined(HAVE_CARES_SET_LOCAL) && defined(USE_IPV6)
+ struct async_ares_ctx *ares = &data->state.async.ares;
unsigned char a6[INET6_ADDRSTRLEN];
+ const char *local_ip6 = data->set.str[STRING_DNS_LOCAL_IP6];
if((!local_ip6) || (local_ip6[0] == 0)) {
/* disabled: do not bind to a specific address */
memset(a6, 0, sizeof(a6));
}
else {
- if(Curl_inet_pton(AF_INET6, local_ip6, a6) != 1) {
+ if(curlx_inet_pton(AF_INET6, local_ip6, a6) != 1) {
DEBUGF(infof(data, "bad DNS IPv6 address"));
return CURLE_BAD_FUNCTION_ARGUMENT;
}
}
- ares_set_local_ip6((ares_channel)data->state.async.resolver, a6);
+ /* if channel is not there, this is just a parameter check */
+ if(ares->channel)
+ ares_set_local_ip6(ares->channel, a6);
return CURLE_OK;
#else /* c-ares version too old! */
(void)data;
- (void)local_ip6;
return CURLE_NOT_BUILT_IN;
#endif
}
+
#endif /* CURLRES_ARES */
diff --git a/libs/libcurl/src/asyn-base.c b/libs/libcurl/src/asyn-base.c
new file mode 100644
index 0000000000..4d2996d237
--- /dev/null
+++ b/libs/libcurl/src/asyn-base.c
@@ -0,0 +1,196 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#ifdef USE_ARES
+#include <ares.h>
+#include <ares_version.h> /* really old c-ares did not include this by
+ itself */
+#endif
+
+#include "urldata.h"
+#include "asyn.h"
+#include "sendf.h"
+#include "hostip.h"
+#include "hash.h"
+#include "multiif.h"
+#include "select.h"
+#include "share.h"
+#include "url.h"
+#include "curl_memory.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/***********************************************************************
+ * Only for builds using asynchronous name resolves
+ **********************************************************************/
+#ifdef CURLRES_ASYNCH
+
+
+#ifdef USE_ARES
+
+#if ARES_VERSION < 0x010600
+#error "requires c-ares 1.6.0 or newer"
+#endif
+
+/*
+ * Curl_ares_getsock() is called when the outside world (using
+ * curl_multi_fdset()) wants to get our fd_set setup and we are talking with
+ * ares. The caller must make sure that this function is only called when we
+ * have a working ares channel.
+ *
+ * Returns: sockets-in-use-bitmap
+ */
+
+int Curl_ares_getsock(struct Curl_easy *data,
+ ares_channel channel,
+ curl_socket_t *socks)
+{
+ struct timeval maxtime = { CURL_TIMEOUT_RESOLVE, 0 };
+ struct timeval timebuf;
+ int max = ares_getsock(channel,
+ (ares_socket_t *)socks, MAX_SOCKSPEREASYHANDLE);
+ struct timeval *timeout = ares_timeout(channel, &maxtime, &timebuf);
+ timediff_t milli = curlx_tvtoms(timeout);
+ Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
+ return max;
+}
+
+/*
+ * Curl_ares_perform()
+ *
+ * 1) Ask ares what sockets it currently plays with, then
+ * 2) wait for the timeout period to check for action on ares' sockets.
+ * 3) tell ares to act on all the sockets marked as "with action"
+ *
+ * return number of sockets it worked on, or -1 on error
+ */
+int Curl_ares_perform(ares_channel channel,
+ timediff_t timeout_ms)
+{
+ int nfds;
+ int bitmask;
+ ares_socket_t socks[ARES_GETSOCK_MAXNUM];
+ struct pollfd pfd[ARES_GETSOCK_MAXNUM];
+ int i;
+ int num = 0;
+
+ if(!channel)
+ return 0;
+
+ bitmask = ares_getsock(channel, socks, ARES_GETSOCK_MAXNUM);
+
+ for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
+ pfd[i].events = 0;
+ pfd[i].revents = 0;
+ if(ARES_GETSOCK_READABLE(bitmask, i)) {
+ pfd[i].fd = socks[i];
+ pfd[i].events |= POLLRDNORM|POLLIN;
+ }
+ if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
+ pfd[i].fd = socks[i];
+ pfd[i].events |= POLLWRNORM|POLLOUT;
+ }
+ if(pfd[i].events)
+ num++;
+ else
+ break;
+ }
+
+ if(num) {
+ nfds = Curl_poll(pfd, (unsigned int)num, timeout_ms);
+ if(nfds < 0)
+ return -1;
+ }
+ else
+ nfds = 0;
+
+ if(!nfds)
+ /* Call ares_process() unconditionally here, even if we simply timed out
+ above, as otherwise the ares name resolve will not timeout! */
+ ares_process_fd(channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
+ else {
+ /* move through the descriptors and ask for processing on them */
+ for(i = 0; i < num; i++)
+ ares_process_fd(channel,
+ (pfd[i].revents & (POLLRDNORM|POLLIN)) ?
+ pfd[i].fd : ARES_SOCKET_BAD,
+ (pfd[i].revents & (POLLWRNORM|POLLOUT)) ?
+ pfd[i].fd : ARES_SOCKET_BAD);
+ }
+ return nfds;
+}
+
+#endif
+
+#endif /* CURLRES_ASYNCH */
+
+#ifdef USE_CURL_ASYNC
+
+#include "doh.h"
+
+void Curl_async_shutdown(struct Curl_easy *data)
+{
+#ifdef CURLRES_ARES
+ Curl_async_ares_shutdown(data);
+#endif
+#ifdef CURLRES_THREADED
+ Curl_async_thrdd_shutdown(data);
+#endif
+#ifndef CURL_DISABLE_DOH
+ Curl_doh_cleanup(data);
+#endif
+ Curl_safefree(data->state.async.hostname);
+}
+
+void Curl_async_destroy(struct Curl_easy *data)
+{
+#ifdef CURLRES_ARES
+ Curl_async_ares_destroy(data);
+#endif
+#ifdef CURLRES_THREADED
+ Curl_async_thrdd_destroy(data);
+#endif
+#ifndef CURL_DISABLE_DOH
+ Curl_doh_cleanup(data);
+#endif
+ Curl_safefree(data->state.async.hostname);
+}
+
+#endif /* USE_CURL_ASYNC */
diff --git a/libs/libcurl/src/asyn-thrdd.c b/libs/libcurl/src/asyn-thrdd.c
new file mode 100644
index 0000000000..3911ea0e01
--- /dev/null
+++ b/libs/libcurl/src/asyn-thrdd.c
@@ -0,0 +1,760 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+#include "socketpair.h"
+
+/***********************************************************************
+ * Only for threaded name resolves builds
+ **********************************************************************/
+#ifdef CURLRES_THREADED
+
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef __VMS
+#include <in.h>
+#include <inet.h>
+#endif
+
+#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
+# include <pthread.h>
+#endif
+
+#ifdef HAVE_GETADDRINFO
+# define RESOLVER_ENOMEM EAI_MEMORY /* = WSA_NOT_ENOUGH_MEMORY on Windows */
+#else
+# define RESOLVER_ENOMEM SOCKENOMEM
+#endif
+
+#include "urldata.h"
+#include "sendf.h"
+#include "hostip.h"
+#include "hash.h"
+#include "share.h"
+#include "url.h"
+#include "multiif.h"
+#include "inet_ntop.h"
+#include "curl_threads.h"
+#include "strdup.h"
+
+#ifdef USE_ARES
+#include <ares.h>
+#ifdef USE_HTTPSRR
+#define USE_HTTPSRR_ARES /* the combo */
+#endif
+#endif
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+
+/*
+ * Curl_async_global_init()
+ * Called from curl_global_init() to initialize global resolver environment.
+ * Does nothing here.
+ */
+int Curl_async_global_init(void)
+{
+#if defined(USE_ARES) && defined(CARES_HAVE_ARES_LIBRARY_INIT)
+ if(ares_library_init(ARES_LIB_INIT_ALL)) {
+ return CURLE_FAILED_INIT;
+ }
+#endif
+ return CURLE_OK;
+}
+
+/*
+ * Curl_async_global_cleanup()
+ * Called from curl_global_cleanup() to destroy global resolver environment.
+ * Does nothing here.
+ */
+void Curl_async_global_cleanup(void)
+{
+#if defined(USE_ARES) && defined(CARES_HAVE_ARES_LIBRARY_INIT)
+ ares_library_cleanup();
+#endif
+}
+
+static void async_thrdd_destroy(struct Curl_easy *);
+
+CURLcode Curl_async_get_impl(struct Curl_easy *data, void **impl)
+{
+ (void)data;
+ *impl = NULL;
+ return CURLE_OK;
+}
+
+/* Destroy context of threaded resolver */
+static void addr_ctx_destroy(struct async_thrdd_addr_ctx *addr_ctx)
+{
+ if(addr_ctx) {
+ DEBUGASSERT(!addr_ctx->ref_count);
+ Curl_mutex_destroy(&addr_ctx->mutx);
+ free(addr_ctx->hostname);
+ if(addr_ctx->res)
+ Curl_freeaddrinfo(addr_ctx->res);
+#ifndef CURL_DISABLE_SOCKETPAIR
+ /*
+ * close one end of the socket pair (may be done in resolver thread);
+ * the other end (for reading) is always closed in the parent thread.
+ */
+#ifndef USE_EVENTFD
+ if(addr_ctx->sock_pair[1] != CURL_SOCKET_BAD) {
+ wakeup_close(addr_ctx->sock_pair[1]);
+ }
+#endif
+#endif
+ free(addr_ctx);
+ }
+}
+
+/* Initialize context for threaded resolver */
+static struct async_thrdd_addr_ctx *
+addr_ctx_create(const char *hostname, int port,
+ const struct addrinfo *hints)
+{
+ struct async_thrdd_addr_ctx *addr_ctx = calloc(1, sizeof(*addr_ctx));
+ if(!addr_ctx)
+ return NULL;
+
+ addr_ctx->thread_hnd = curl_thread_t_null;
+ addr_ctx->port = port;
+#ifndef CURL_DISABLE_SOCKETPAIR
+ addr_ctx->sock_pair[0] = CURL_SOCKET_BAD;
+ addr_ctx->sock_pair[1] = CURL_SOCKET_BAD;
+#endif
+ addr_ctx->ref_count = 0;
+
+#ifdef HAVE_GETADDRINFO
+ DEBUGASSERT(hints);
+ addr_ctx->hints = *hints;
+#else
+ (void) hints;
+#endif
+
+ Curl_mutex_init(&addr_ctx->mutx);
+
+#ifndef CURL_DISABLE_SOCKETPAIR
+ /* create socket pair or pipe */
+ if(wakeup_create(addr_ctx->sock_pair, FALSE) < 0) {
+ addr_ctx->sock_pair[0] = CURL_SOCKET_BAD;
+ addr_ctx->sock_pair[1] = CURL_SOCKET_BAD;
+ goto err_exit;
+ }
+#endif
+ addr_ctx->sock_error = CURL_ASYNC_SUCCESS;
+
+ /* Copying hostname string because original can be destroyed by parent
+ * thread during gethostbyname execution.
+ */
+ addr_ctx->hostname = strdup(hostname);
+ if(!addr_ctx->hostname)
+ goto err_exit;
+
+ addr_ctx->ref_count = 1;
+ return addr_ctx;
+
+err_exit:
+#ifndef CURL_DISABLE_SOCKETPAIR
+ if(addr_ctx->sock_pair[0] != CURL_SOCKET_BAD) {
+ wakeup_close(addr_ctx->sock_pair[0]);
+ addr_ctx->sock_pair[0] = CURL_SOCKET_BAD;
+ }
+#endif
+ addr_ctx_destroy(addr_ctx);
+ return NULL;
+}
+
+#ifdef HAVE_GETADDRINFO
+
+/*
+ * getaddrinfo_thread() resolves a name and then exits.
+ *
+ * For builds without ARES, but with USE_IPV6, create a resolver thread
+ * and wait on it.
+ */
+static
+#if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE)
+DWORD
+#else
+unsigned int
+#endif
+CURL_STDCALL getaddrinfo_thread(void *arg)
+{
+ struct async_thrdd_addr_ctx *addr_ctx = arg;
+ char service[12];
+ int rc;
+ bool all_gone;
+
+ msnprintf(service, sizeof(service), "%d", addr_ctx->port);
+
+ rc = Curl_getaddrinfo_ex(addr_ctx->hostname, service,
+ &addr_ctx->hints, &addr_ctx->res);
+
+ if(rc) {
+ addr_ctx->sock_error = SOCKERRNO ? SOCKERRNO : rc;
+ if(addr_ctx->sock_error == 0)
+ addr_ctx->sock_error = RESOLVER_ENOMEM;
+ }
+ else {
+ Curl_addrinfo_set_port(addr_ctx->res, addr_ctx->port);
+ }
+
+ Curl_mutex_acquire(&addr_ctx->mutx);
+ if(addr_ctx->ref_count > 1) {
+ /* Someone still waiting on our results. */
+#ifndef CURL_DISABLE_SOCKETPAIR
+ if(addr_ctx->sock_pair[1] != CURL_SOCKET_BAD) {
+#ifdef USE_EVENTFD
+ const uint64_t buf[1] = { 1 };
+#else
+ const char buf[1] = { 1 };
+#endif
+ /* DNS has been resolved, signal client task */
+ if(wakeup_write(addr_ctx->sock_pair[1], buf, sizeof(buf)) < 0) {
+ /* update sock_erro to errno */
+ addr_ctx->sock_error = SOCKERRNO;
+ }
+ }
+#endif
+ }
+ /* thread gives up its reference to the shared data now. */
+ --addr_ctx->ref_count;
+ all_gone = !addr_ctx->ref_count;
+ Curl_mutex_release(&addr_ctx->mutx);
+ if(all_gone)
+ addr_ctx_destroy(addr_ctx);
+
+ return 0;
+}
+
+#else /* HAVE_GETADDRINFO */
+
+/*
+ * gethostbyname_thread() resolves a name and then exits.
+ */
+static
+#if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE)
+DWORD
+#else
+unsigned int
+#endif
+CURL_STDCALL gethostbyname_thread(void *arg)
+{
+ struct async_thrdd_addr_ctx *addr_ctx = arg;
+ bool all_gone;
+
+ addr_ctx->res = Curl_ipv4_resolve_r(addr_ctx->hostname, addr_ctx->port);
+
+ if(!addr_ctx->res) {
+ addr_ctx->sock_error = SOCKERRNO;
+ if(addr_ctx->sock_error == 0)
+ addr_ctx->sock_error = RESOLVER_ENOMEM;
+ }
+
+ Curl_mutex_acquire(&addr_ctx->mutx);
+ /* thread gives up its reference to the shared data now. */
+ --addr_ctx->ref_count;
+ all_gone = !addr_ctx->ref_count;;
+ Curl_mutex_release(&addr_ctx->mutx);
+ if(all_gone)
+ addr_ctx_destroy(addr_ctx);
+
+ return 0;
+}
+
+#endif /* HAVE_GETADDRINFO */
+
+/*
+ * async_thrdd_destroy() cleans up async resolver data and thread handle.
+ */
+static void async_thrdd_destroy(struct Curl_easy *data)
+{
+ struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
+ struct async_thrdd_addr_ctx *addr = thrdd->addr;
+#ifdef USE_HTTPSRR_ARES
+ if(thrdd->rr.channel) {
+ ares_destroy(thrdd->rr.channel);
+ thrdd->rr.channel = NULL;
+ }
+ Curl_httpsrr_cleanup(&thrdd->rr.hinfo);
+#endif
+
+ if(addr) {
+#ifndef CURL_DISABLE_SOCKETPAIR
+ curl_socket_t sock_rd = addr->sock_pair[0];
+#endif
+ bool done;
+
+ /* Release our reference to the data shared with the thread. */
+ Curl_mutex_acquire(&addr->mutx);
+ --addr->ref_count;
+ CURL_TRC_DNS(data, "resolve, destroy async data, shared ref=%d",
+ addr->ref_count);
+ done = !addr->ref_count;
+ /* we give up our reference to `addr`, so NULL our pointer.
+ * coverity analyses this as being a potential unsynched write,
+ * assuming two calls to this function could be invoked concurrently.
+ * Which they never are, as the transfer's side runs single-threaded. */
+ thrdd->addr = NULL;
+ if(!done) {
+ /* thread is still running. Detach the thread while mutexed, it will
+ * trigger the cleanup when it releases its reference. */
+ Curl_thread_destroy(&addr->thread_hnd);
+ }
+ Curl_mutex_release(&addr->mutx);
+
+ if(done) {
+ /* thread has released its reference, join it and
+ * release the memory we shared with it. */
+ if(addr->thread_hnd != curl_thread_t_null)
+ Curl_thread_join(&addr->thread_hnd);
+ addr_ctx_destroy(addr);
+ }
+#ifndef CURL_DISABLE_SOCKETPAIR
+ /*
+ * ensure CURLMOPT_SOCKETFUNCTION fires CURL_POLL_REMOVE
+ * before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL
+ */
+ Curl_multi_will_close(data, sock_rd);
+ wakeup_close(sock_rd);
+#endif
+ }
+}
+
+#ifdef USE_HTTPSRR_ARES
+
+static void async_thrdd_rr_done(void *user_data, ares_status_t status,
+ size_t timeouts,
+ const ares_dns_record_t *dnsrec)
+{
+ struct Curl_easy *data = user_data;
+ struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
+
+ (void)timeouts;
+ thrdd->rr.done = TRUE;
+ if((ARES_SUCCESS != status) || !dnsrec)
+ return;
+ thrdd->rr.result = Curl_httpsrr_from_ares(data, dnsrec, &thrdd->rr.hinfo);
+}
+
+static CURLcode async_rr_start(struct Curl_easy *data)
+{
+ struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
+ int status;
+
+ DEBUGASSERT(!thrdd->rr.channel);
+ status = ares_init_options(&thrdd->rr.channel, NULL, 0);
+ if(status != ARES_SUCCESS) {
+ thrdd->rr.channel = NULL;
+ return CURLE_FAILED_INIT;
+ }
+
+ memset(&thrdd->rr.hinfo, 0, sizeof(thrdd->rr.hinfo));
+ thrdd->rr.hinfo.port = -1;
+ ares_query_dnsrec(thrdd->rr.channel,
+ data->conn->host.name, ARES_CLASS_IN,
+ ARES_REC_TYPE_HTTPS,
+ async_thrdd_rr_done, data, NULL);
+ return CURLE_OK;
+}
+#endif
+
+/*
+ * async_thrdd_init() starts a new thread that performs the actual
+ * resolve. This function returns before the resolve is done.
+ *
+ * Returns FALSE in case of failure, otherwise TRUE.
+ */
+static bool async_thrdd_init(struct Curl_easy *data,
+ const char *hostname, int port, int ip_version,
+ const struct addrinfo *hints)
+{
+ struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
+ struct async_thrdd_addr_ctx *addr_ctx;
+
+ /* !checksrc! disable ERRNOVAR 1 */
+ int err = ENOMEM;
+
+ if(thrdd->addr
+#ifdef USE_HTTPSRR_ARES
+ || thrdd->rr.channel
+#endif
+ ) {
+ CURL_TRC_DNS(data, "starting new resolve, with previous not cleaned up");
+ async_thrdd_destroy(data);
+ DEBUGASSERT(!thrdd->addr);
+#ifdef USE_HTTPSRR_ARES
+ DEBUGASSERT(!thrdd->rr.channel);
+#endif
+ }
+
+ data->state.async.dns = NULL;
+ data->state.async.done = FALSE;
+ data->state.async.port = port;
+ data->state.async.ip_version = ip_version;
+ data->state.async.hostname = strdup(hostname);
+ if(!data->state.async.hostname)
+ goto err_exit;
+
+ addr_ctx = addr_ctx_create(hostname, port, hints);
+ if(!addr_ctx)
+ goto err_exit;
+ thrdd->addr = addr_ctx;
+
+ Curl_mutex_acquire(&addr_ctx->mutx);
+ DEBUGASSERT(addr_ctx->ref_count == 1);
+ /* passing addr_ctx to the thread adds a reference */
+ addr_ctx->start = curlx_now();
+ ++addr_ctx->ref_count;
+#ifdef HAVE_GETADDRINFO
+ addr_ctx->thread_hnd = Curl_thread_create(getaddrinfo_thread, addr_ctx);
+#else
+ addr_ctx->thread_hnd = Curl_thread_create(gethostbyname_thread, addr_ctx);
+#endif
+ if(addr_ctx->thread_hnd == curl_thread_t_null) {
+ /* The thread never started, remove its reference that never happened. */
+ --addr_ctx->ref_count;
+ err = errno;
+ Curl_mutex_release(&addr_ctx->mutx);
+ goto err_exit;
+ }
+ Curl_mutex_release(&addr_ctx->mutx);
+
+#ifdef USE_HTTPSRR_ARES
+ if(async_rr_start(data))
+ infof(data, "Failed HTTPS RR operation");
+#endif
+ CURL_TRC_DNS(data, "resolve thread started for of %s:%d", hostname, port);
+ return TRUE;
+
+err_exit:
+ CURL_TRC_DNS(data, "resolve thread failed init: %d", err);
+ async_thrdd_destroy(data);
+ CURL_SETERRNO(err);
+ return FALSE;
+}
+
+/*
+ * 'entry' may be NULL and then no data is returned
+ */
+static CURLcode asyn_thrdd_await(struct Curl_easy *data,
+ struct async_thrdd_addr_ctx *addr_ctx,
+ struct Curl_dns_entry **entry)
+{
+ CURLcode result = CURLE_OK;
+
+ DEBUGASSERT(addr_ctx->thread_hnd != curl_thread_t_null);
+
+ CURL_TRC_DNS(data, "resolve, wait for thread to finish");
+ /* wait for the thread to resolve the name */
+ if(Curl_thread_join(&addr_ctx->thread_hnd)) {
+ if(entry)
+ result = Curl_async_is_resolved(data, entry);
+ }
+ else
+ DEBUGASSERT(0);
+
+ data->state.async.done = TRUE;
+ if(entry)
+ *entry = data->state.async.dns;
+
+ async_thrdd_destroy(data);
+ return result;
+}
+
+
+/*
+ * Until we gain a way to signal the resolver threads to stop early, we must
+ * simply wait for them and ignore their results.
+ */
+void Curl_async_thrdd_shutdown(struct Curl_easy *data)
+{
+ struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
+
+ /* If we are still resolving, we must wait for the threads to fully clean up,
+ unfortunately. Otherwise, we can simply cancel to clean up any resolver
+ data. */
+ if(thrdd->addr && (thrdd->addr->thread_hnd != curl_thread_t_null) &&
+ !data->set.quick_exit)
+ (void)asyn_thrdd_await(data, thrdd->addr, NULL);
+ else
+ async_thrdd_destroy(data);
+}
+
+void Curl_async_thrdd_destroy(struct Curl_easy *data)
+{
+ Curl_async_thrdd_shutdown(data);
+}
+
+/*
+ * Curl_async_await()
+ *
+ * Waits for a resolve to finish. This function should be avoided since using
+ * this risk getting the multi interface to "hang".
+ *
+ * If 'entry' is non-NULL, make it point to the resolved dns entry
+ *
+ * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
+ * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
+ *
+ * This is the version for resolves-in-a-thread.
+ */
+CURLcode Curl_async_await(struct Curl_easy *data,
+ struct Curl_dns_entry **entry)
+{
+ struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
+ if(thrdd->addr)
+ return asyn_thrdd_await(data, thrdd->addr, entry);
+ return CURLE_FAILED_INIT;
+}
+
+/*
+ * Curl_async_is_resolved() is called repeatedly to check if a previous
+ * name resolve request has completed. It should also make sure to time-out if
+ * the operation seems to take too long.
+ */
+CURLcode Curl_async_is_resolved(struct Curl_easy *data,
+ struct Curl_dns_entry **dns)
+{
+ struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
+ bool done = FALSE;
+
+ DEBUGASSERT(dns);
+ *dns = NULL;
+
+ if(data->state.async.done) {
+ *dns = data->state.async.dns;
+ CURL_TRC_DNS(data, "threaded: is_resolved(), already done, dns=%sfound",
+ *dns ? "" : "not ");
+ return CURLE_OK;
+ }
+
+#ifdef USE_HTTPSRR_ARES
+ /* best effort, ignore errors */
+ if(thrdd->rr.channel)
+ (void)Curl_ares_perform(thrdd->rr.channel, 0);
+#endif
+
+ DEBUGASSERT(thrdd->addr);
+ if(!thrdd->addr)
+ return CURLE_FAILED_INIT;
+
+ Curl_mutex_acquire(&thrdd->addr->mutx);
+ done = (thrdd->addr->ref_count == 1);
+ Curl_mutex_release(&thrdd->addr->mutx);
+
+ if(done) {
+ CURLcode result = CURLE_OK;
+
+ data->state.async.done = TRUE;
+ Curl_resolv_unlink(data, &data->state.async.dns);
+
+ if(thrdd->addr->res) {
+ data->state.async.dns =
+ Curl_dnscache_mk_entry(data, thrdd->addr->res,
+ data->state.async.hostname, 0,
+ data->state.async.port, FALSE);
+ thrdd->addr->res = NULL;
+ if(!data->state.async.dns)
+ result = CURLE_OUT_OF_MEMORY;
+
+#ifdef USE_HTTPSRR_ARES
+ if(thrdd->rr.channel) {
+ result = thrdd->rr.result;
+ if(!result) {
+ struct Curl_https_rrinfo *lhrr;
+ lhrr = Curl_httpsrr_dup_move(&thrdd->rr.hinfo);
+ if(!lhrr)
+ result = CURLE_OUT_OF_MEMORY;
+ else
+ data->state.async.dns->hinfo = lhrr;
+ }
+ }
+#endif
+ if(!result && data->state.async.dns)
+ result = Curl_dnscache_add(data, data->state.async.dns);
+ }
+
+ if(!result && !data->state.async.dns)
+ result = Curl_resolver_error(data);
+ if(result)
+ Curl_resolv_unlink(data, &data->state.async.dns);
+ *dns = data->state.async.dns;
+ CURL_TRC_DNS(data, "is_resolved() result=%d, dns=%sfound",
+ result, *dns ? "" : "not ");
+ async_thrdd_destroy(data);
+ return result;
+ }
+ else {
+ /* poll for name lookup done with exponential backoff up to 250ms */
+ /* should be fine even if this converts to 32-bit */
+ timediff_t elapsed = curlx_timediff(curlx_now(),
+ data->progress.t_startsingle);
+ if(elapsed < 0)
+ elapsed = 0;
+
+ if(thrdd->addr->poll_interval == 0)
+ /* Start at 1ms poll interval */
+ thrdd->addr->poll_interval = 1;
+ else if(elapsed >= thrdd->addr->interval_end)
+ /* Back-off exponentially if last interval expired */
+ thrdd->addr->poll_interval *= 2;
+
+ if(thrdd->addr->poll_interval > 250)
+ thrdd->addr->poll_interval = 250;
+
+ thrdd->addr->interval_end = elapsed + thrdd->addr->poll_interval;
+ Curl_expire(data, thrdd->addr->poll_interval, EXPIRE_ASYNC_NAME);
+ return CURLE_OK;
+ }
+}
+
+int Curl_async_getsock(struct Curl_easy *data, curl_socket_t *socks)
+{
+ struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
+ int ret_val = 0;
+#if !defined(CURL_DISABLE_SOCKETPAIR) || defined(USE_HTTPSRR_ARES)
+ int socketi = 0;
+#else
+ (void)socks;
+#endif
+
+#ifdef USE_HTTPSRR_ARES
+ if(thrdd->rr.channel) {
+ ret_val = Curl_ares_getsock(data, thrdd->rr.channel, socks);
+ for(socketi = 0; socketi < (MAX_SOCKSPEREASYHANDLE - 1); socketi++)
+ if(!ARES_GETSOCK_READABLE(ret_val, socketi) &&
+ !ARES_GETSOCK_WRITABLE(ret_val, socketi))
+ break;
+ }
+#endif
+ if(!thrdd->addr)
+ return ret_val;
+
+#ifndef CURL_DISABLE_SOCKETPAIR
+ if(thrdd->addr) {
+ /* return read fd to client for polling the DNS resolution status */
+ socks[socketi] = thrdd->addr->sock_pair[0];
+ ret_val |= GETSOCK_READSOCK(socketi);
+ }
+ else
+#endif
+ {
+ timediff_t milli;
+ timediff_t ms = curlx_timediff(curlx_now(), thrdd->addr->start);
+ if(ms < 3)
+ milli = 0;
+ else if(ms <= 50)
+ milli = ms/3;
+ else if(ms <= 250)
+ milli = 50;
+ else
+ milli = 200;
+ Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
+ }
+
+ return ret_val;
+}
+
+#ifndef HAVE_GETADDRINFO
+/*
+ * Curl_async_getaddrinfo() - for platforms without getaddrinfo
+ */
+struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
+ const char *hostname,
+ int port,
+ int ip_version,
+ int *waitp)
+{
+ (void)ip_version;
+ *waitp = 0; /* default to synchronous response */
+
+ /* fire up a new resolver thread! */
+ if(async_thrdd_init(data, hostname, port, ip_version, NULL)) {
+ *waitp = 1; /* expect asynchronous response */
+ return NULL;
+ }
+
+ failf(data, "getaddrinfo() thread failed");
+
+ return NULL;
+}
+
+#else /* !HAVE_GETADDRINFO */
+
+/*
+ * Curl_async_getaddrinfo() - for getaddrinfo
+ */
+struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
+ const char *hostname,
+ int port,
+ int ip_version,
+ int *waitp)
+{
+ struct addrinfo hints;
+ int pf = PF_INET;
+ *waitp = 0; /* default to synchronous response */
+
+ CURL_TRC_DNS(data, "init threaded resolve of %s:%d", hostname, port);
+#ifdef CURLRES_IPV6
+ if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
+ /* The stack seems to be IPv6-enabled */
+ if(ip_version == CURL_IPRESOLVE_V6)
+ pf = PF_INET6;
+ else
+ pf = PF_UNSPEC;
+ }
+#else
+ (void)ip_version;
+#endif /* CURLRES_IPV6 */
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = pf;
+ hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP) ?
+ SOCK_STREAM : SOCK_DGRAM;
+
+ /* fire up a new resolver thread! */
+ if(async_thrdd_init(data, hostname, port, ip_version, &hints)) {
+ *waitp = 1; /* expect asynchronous response */
+ return NULL;
+ }
+
+ failf(data, "getaddrinfo() thread failed to start");
+ return NULL;
+
+}
+
+#endif /* !HAVE_GETADDRINFO */
+
+#endif /* CURLRES_THREADED */
diff --git a/libs/libcurl/src/asyn-thread.c b/libs/libcurl/src/asyn-thread.c
deleted file mode 100644
index 11b317b283..0000000000
--- a/libs/libcurl/src/asyn-thread.c
+++ /dev/null
@@ -1,781 +0,0 @@
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-#include "socketpair.h"
-
-/***********************************************************************
- * Only for threaded name resolves builds
- **********************************************************************/
-#ifdef CURLRES_THREADED
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef __VMS
-#include <in.h>
-#include <inet.h>
-#endif
-
-#if defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)
-# include <pthread.h>
-#endif
-
-#ifdef HAVE_GETADDRINFO
-# define RESOLVER_ENOMEM EAI_MEMORY
-#else
-# define RESOLVER_ENOMEM ENOMEM
-#endif
-
-#include "urldata.h"
-#include "sendf.h"
-#include "hostip.h"
-#include "hash.h"
-#include "share.h"
-#include "url.h"
-#include "multiif.h"
-#include "inet_ntop.h"
-#include "curl_threads.h"
-#include "connect.h"
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
-
-struct resdata {
- struct curltime start;
-};
-
-/*
- * Curl_resolver_global_init()
- * Called from curl_global_init() to initialize global resolver environment.
- * Does nothing here.
- */
-int Curl_resolver_global_init(void)
-{
- return CURLE_OK;
-}
-
-/*
- * Curl_resolver_global_cleanup()
- * Called from curl_global_cleanup() to destroy global resolver environment.
- * Does nothing here.
- */
-void Curl_resolver_global_cleanup(void)
-{
-}
-
-/*
- * Curl_resolver_init()
- * Called from curl_easy_init() -> Curl_open() to initialize resolver
- * URL-state specific environment ('resolver' member of the UrlState
- * structure).
- */
-CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
-{
- (void)easy;
- *resolver = calloc(1, sizeof(struct resdata));
- if(!*resolver)
- return CURLE_OUT_OF_MEMORY;
- return CURLE_OK;
-}
-
-/*
- * Curl_resolver_cleanup()
- * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
- * URL-state specific environment ('resolver' member of the UrlState
- * structure).
- */
-void Curl_resolver_cleanup(void *resolver)
-{
- free(resolver);
-}
-
-/*
- * Curl_resolver_duphandle()
- * Called from curl_easy_duphandle() to duplicate resolver URL state-specific
- * environment ('resolver' member of the UrlState structure).
- */
-CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from)
-{
- (void)from;
- return Curl_resolver_init(easy, to);
-}
-
-static void destroy_async_data(struct Curl_async *);
-
-/*
- * Cancel all possibly still on-going resolves for this connection.
- */
-void Curl_resolver_cancel(struct Curl_easy *data)
-{
- destroy_async_data(&data->state.async);
-}
-
-/* This function is used to init a threaded resolve */
-static bool init_resolve_thread(struct Curl_easy *data,
- const char *hostname, int port,
- const struct addrinfo *hints);
-
-
-/* Data for synchronization between resolver thread and its parent */
-struct thread_sync_data {
- curl_mutex_t *mtx;
- int done;
- int port;
- char *hostname; /* hostname to resolve, Curl_async.hostname
- duplicate */
-#ifndef CURL_DISABLE_SOCKETPAIR
- struct Curl_easy *data;
- curl_socket_t sock_pair[2]; /* eventfd/pipes/socket pair */
-#endif
- int sock_error;
- struct Curl_addrinfo *res;
-#ifdef HAVE_GETADDRINFO
- struct addrinfo hints;
-#endif
- struct thread_data *td; /* for thread-self cleanup */
-};
-
-struct thread_data {
- curl_thread_t thread_hnd;
- unsigned int poll_interval;
- timediff_t interval_end;
- struct thread_sync_data tsd;
-};
-
-static struct thread_sync_data *conn_thread_sync_data(struct Curl_easy *data)
-{
- return &(data->state.async.tdata->tsd);
-}
-
-/* Destroy resolver thread synchronization data */
-static
-void destroy_thread_sync_data(struct thread_sync_data *tsd)
-{
- if(tsd->mtx) {
- Curl_mutex_destroy(tsd->mtx);
- free(tsd->mtx);
- }
-
- free(tsd->hostname);
-
- if(tsd->res)
- Curl_freeaddrinfo(tsd->res);
-
-#ifndef CURL_DISABLE_SOCKETPAIR
- /*
- * close one end of the socket pair (may be done in resolver thread);
- * the other end (for reading) is always closed in the parent thread.
- */
- if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
- wakeup_close(tsd->sock_pair[1]);
- }
-#endif
- memset(tsd, 0, sizeof(*tsd));
-}
-
-/* Initialize resolver thread synchronization data */
-static
-int init_thread_sync_data(struct thread_data *td,
- const char *hostname,
- int port,
- const struct addrinfo *hints)
-{
- struct thread_sync_data *tsd = &td->tsd;
-
- memset(tsd, 0, sizeof(*tsd));
-
- tsd->td = td;
- tsd->port = port;
- /* Treat the request as done until the thread actually starts so any early
- * cleanup gets done properly.
- */
- tsd->done = 1;
-#ifdef HAVE_GETADDRINFO
- DEBUGASSERT(hints);
- tsd->hints = *hints;
-#else
- (void) hints;
-#endif
-
- tsd->mtx = malloc(sizeof(curl_mutex_t));
- if(!tsd->mtx)
- goto err_exit;
-
- Curl_mutex_init(tsd->mtx);
-
-#ifndef CURL_DISABLE_SOCKETPAIR
- /* create socket pair or pipe */
- if(wakeup_create(tsd->sock_pair, FALSE) < 0) {
- tsd->sock_pair[0] = CURL_SOCKET_BAD;
- tsd->sock_pair[1] = CURL_SOCKET_BAD;
- goto err_exit;
- }
-#endif
- tsd->sock_error = CURL_ASYNC_SUCCESS;
-
- /* Copying hostname string because original can be destroyed by parent
- * thread during gethostbyname execution.
- */
- tsd->hostname = strdup(hostname);
- if(!tsd->hostname)
- goto err_exit;
-
- return 1;
-
-err_exit:
-#ifndef CURL_DISABLE_SOCKETPAIR
- if(tsd->sock_pair[0] != CURL_SOCKET_BAD) {
- wakeup_close(tsd->sock_pair[0]);
- tsd->sock_pair[0] = CURL_SOCKET_BAD;
- }
-#endif
- destroy_thread_sync_data(tsd);
- return 0;
-}
-
-static CURLcode getaddrinfo_complete(struct Curl_easy *data)
-{
- struct thread_sync_data *tsd = conn_thread_sync_data(data);
- CURLcode result;
-
- result = Curl_addrinfo_callback(data, tsd->sock_error, tsd->res);
- /* The tsd->res structure has been copied to async.dns and perhaps the DNS
- cache. Set our copy to NULL so destroy_thread_sync_data does not free it.
- */
- tsd->res = NULL;
-
- return result;
-}
-
-
-#ifdef HAVE_GETADDRINFO
-
-/*
- * getaddrinfo_thread() resolves a name and then exits.
- *
- * For builds without ARES, but with USE_IPV6, create a resolver thread
- * and wait on it.
- */
-static
-#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_UWP)
-DWORD
-#else
-unsigned int
-#endif
-CURL_STDCALL getaddrinfo_thread(void *arg)
-{
- struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
- struct thread_data *td = tsd->td;
- char service[12];
- int rc;
-#ifndef CURL_DISABLE_SOCKETPAIR
-#ifdef USE_EVENTFD
- const void *buf;
- const uint64_t val = 1;
-#else
- char buf[1];
-#endif
-#endif
-
- msnprintf(service, sizeof(service), "%d", tsd->port);
-
- rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res);
-
- if(rc) {
- tsd->sock_error = SOCKERRNO ? SOCKERRNO : rc;
- if(tsd->sock_error == 0)
- tsd->sock_error = RESOLVER_ENOMEM;
- }
- else {
- Curl_addrinfo_set_port(tsd->res, tsd->port);
- }
-
- Curl_mutex_acquire(tsd->mtx);
- if(tsd->done) {
- /* too late, gotta clean up the mess */
- Curl_mutex_release(tsd->mtx);
- destroy_thread_sync_data(tsd);
- free(td);
- }
- else {
-#ifndef CURL_DISABLE_SOCKETPAIR
- if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
-#ifdef USE_EVENTFD
- buf = &val;
-#else
- buf[0] = 1;
-#endif
- /* DNS has been resolved, signal client task */
- if(wakeup_write(tsd->sock_pair[1], buf, sizeof(buf)) < 0) {
- /* update sock_erro to errno */
- tsd->sock_error = SOCKERRNO;
- }
- }
-#endif
- tsd->done = 1;
- Curl_mutex_release(tsd->mtx);
- }
-
- return 0;
-}
-
-#else /* HAVE_GETADDRINFO */
-
-/*
- * gethostbyname_thread() resolves a name and then exits.
- */
-static
-#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_UWP)
-DWORD
-#else
-unsigned int
-#endif
-CURL_STDCALL gethostbyname_thread(void *arg)
-{
- struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
- struct thread_data *td = tsd->td;
-
- tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port);
-
- if(!tsd->res) {
- tsd->sock_error = SOCKERRNO;
- if(tsd->sock_error == 0)
- tsd->sock_error = RESOLVER_ENOMEM;
- }
-
- Curl_mutex_acquire(tsd->mtx);
- if(tsd->done) {
- /* too late, gotta clean up the mess */
- Curl_mutex_release(tsd->mtx);
- destroy_thread_sync_data(tsd);
- free(td);
- }
- else {
- tsd->done = 1;
- Curl_mutex_release(tsd->mtx);
- }
-
- return 0;
-}
-
-#endif /* HAVE_GETADDRINFO */
-
-/*
- * destroy_async_data() cleans up async resolver data and thread handle.
- */
-static void destroy_async_data(struct Curl_async *async)
-{
- if(async->tdata) {
- struct thread_data *td = async->tdata;
- int done;
-#ifndef CURL_DISABLE_SOCKETPAIR
- curl_socket_t sock_rd = td->tsd.sock_pair[0];
- struct Curl_easy *data = td->tsd.data;
-#endif
-
- /*
- * if the thread is still blocking in the resolve syscall, detach it and
- * let the thread do the cleanup...
- */
- Curl_mutex_acquire(td->tsd.mtx);
- done = td->tsd.done;
- td->tsd.done = 1;
- Curl_mutex_release(td->tsd.mtx);
-
- if(!done) {
- Curl_thread_destroy(td->thread_hnd);
- }
- else {
- if(td->thread_hnd != curl_thread_t_null)
- Curl_thread_join(&td->thread_hnd);
-
- destroy_thread_sync_data(&td->tsd);
-
- free(async->tdata);
- }
-#ifndef CURL_DISABLE_SOCKETPAIR
- /*
- * ensure CURLMOPT_SOCKETFUNCTION fires CURL_POLL_REMOVE
- * before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL
- */
- Curl_multi_closed(data, sock_rd);
- wakeup_close(sock_rd);
-#endif
- }
- async->tdata = NULL;
-
- free(async->hostname);
- async->hostname = NULL;
-}
-
-/*
- * init_resolve_thread() starts a new thread that performs the actual
- * resolve. This function returns before the resolve is done.
- *
- * Returns FALSE in case of failure, otherwise TRUE.
- */
-static bool init_resolve_thread(struct Curl_easy *data,
- const char *hostname, int port,
- const struct addrinfo *hints)
-{
- struct thread_data *td = calloc(1, sizeof(struct thread_data));
- int err = ENOMEM;
- struct Curl_async *asp = &data->state.async;
-
- data->state.async.tdata = td;
- if(!td)
- goto errno_exit;
-
- asp->port = port;
- asp->done = FALSE;
- asp->status = 0;
- asp->dns = NULL;
- td->thread_hnd = curl_thread_t_null;
-
- if(!init_thread_sync_data(td, hostname, port, hints)) {
- asp->tdata = NULL;
- free(td);
- goto errno_exit;
- }
-
- free(asp->hostname);
- asp->hostname = strdup(hostname);
- if(!asp->hostname)
- goto err_exit;
-
- /* The thread will set this to 1 when complete. */
- td->tsd.done = 0;
-
-#ifdef HAVE_GETADDRINFO
- td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
-#else
- td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd);
-#endif
-
- if(td->thread_hnd == curl_thread_t_null) {
- /* The thread never started, so mark it as done here for proper cleanup. */
- td->tsd.done = 1;
- err = errno;
- goto err_exit;
- }
-
- return TRUE;
-
-err_exit:
- destroy_async_data(asp);
-
-errno_exit:
- errno = err;
- return FALSE;
-}
-
-/*
- * 'entry' may be NULL and then no data is returned
- */
-static CURLcode thread_wait_resolv(struct Curl_easy *data,
- struct Curl_dns_entry **entry,
- bool report)
-{
- struct thread_data *td;
- CURLcode result = CURLE_OK;
-
- DEBUGASSERT(data);
- td = data->state.async.tdata;
- DEBUGASSERT(td);
- DEBUGASSERT(td->thread_hnd != curl_thread_t_null);
-
- /* wait for the thread to resolve the name */
- if(Curl_thread_join(&td->thread_hnd)) {
- if(entry)
- result = getaddrinfo_complete(data);
- }
- else
- DEBUGASSERT(0);
-
- data->state.async.done = TRUE;
-
- if(entry)
- *entry = data->state.async.dns;
-
- if(!data->state.async.dns && report)
- /* a name was not resolved, report error */
- result = Curl_resolver_error(data);
-
- destroy_async_data(&data->state.async);
-
- if(!data->state.async.dns && report)
- connclose(data->conn, "asynch resolve failed");
-
- return result;
-}
-
-
-/*
- * Until we gain a way to signal the resolver threads to stop early, we must
- * simply wait for them and ignore their results.
- */
-void Curl_resolver_kill(struct Curl_easy *data)
-{
- struct thread_data *td = data->state.async.tdata;
-
- /* If we are still resolving, we must wait for the threads to fully clean up,
- unfortunately. Otherwise, we can simply cancel to clean up any resolver
- data. */
- if(td && td->thread_hnd != curl_thread_t_null
- && (data->set.quick_exit != 1L))
- (void)thread_wait_resolv(data, NULL, FALSE);
- else
- Curl_resolver_cancel(data);
-}
-
-/*
- * Curl_resolver_wait_resolv()
- *
- * Waits for a resolve to finish. This function should be avoided since using
- * this risk getting the multi interface to "hang".
- *
- * If 'entry' is non-NULL, make it point to the resolved dns entry
- *
- * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
- * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
- *
- * This is the version for resolves-in-a-thread.
- */
-CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
- struct Curl_dns_entry **entry)
-{
- return thread_wait_resolv(data, entry, TRUE);
-}
-
-/*
- * Curl_resolver_is_resolved() is called repeatedly to check if a previous
- * name resolve request has completed. It should also make sure to time-out if
- * the operation seems to take too long.
- */
-CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
- struct Curl_dns_entry **entry)
-{
- struct thread_data *td = data->state.async.tdata;
- int done = 0;
-
- DEBUGASSERT(entry);
- *entry = NULL;
-
- if(!td) {
- DEBUGASSERT(td);
- return CURLE_COULDNT_RESOLVE_HOST;
- }
-
- Curl_mutex_acquire(td->tsd.mtx);
- done = td->tsd.done;
- Curl_mutex_release(td->tsd.mtx);
-
- if(done) {
- getaddrinfo_complete(data);
-
- if(!data->state.async.dns) {
- CURLcode result = Curl_resolver_error(data);
- destroy_async_data(&data->state.async);
- return result;
- }
- destroy_async_data(&data->state.async);
- *entry = data->state.async.dns;
- }
- else {
- /* poll for name lookup done with exponential backoff up to 250ms */
- /* should be fine even if this converts to 32-bit */
- timediff_t elapsed = Curl_timediff(Curl_now(),
- data->progress.t_startsingle);
- if(elapsed < 0)
- elapsed = 0;
-
- if(td->poll_interval == 0)
- /* Start at 1ms poll interval */
- td->poll_interval = 1;
- else if(elapsed >= td->interval_end)
- /* Back-off exponentially if last interval expired */
- td->poll_interval *= 2;
-
- if(td->poll_interval > 250)
- td->poll_interval = 250;
-
- td->interval_end = elapsed + td->poll_interval;
- Curl_expire(data, td->poll_interval, EXPIRE_ASYNC_NAME);
- }
-
- return CURLE_OK;
-}
-
-int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *socks)
-{
- int ret_val = 0;
- timediff_t milli;
- timediff_t ms;
- struct resdata *reslv = (struct resdata *)data->state.async.resolver;
-#ifndef CURL_DISABLE_SOCKETPAIR
- struct thread_data *td = data->state.async.tdata;
-#else
- (void)socks;
-#endif
-
-#ifndef CURL_DISABLE_SOCKETPAIR
- if(td) {
- /* return read fd to client for polling the DNS resolution status */
- socks[0] = td->tsd.sock_pair[0];
- td->tsd.data = data;
- ret_val = GETSOCK_READSOCK(0);
- }
- else {
-#endif
- ms = Curl_timediff(Curl_now(), reslv->start);
- if(ms < 3)
- milli = 0;
- else if(ms <= 50)
- milli = ms/3;
- else if(ms <= 250)
- milli = 50;
- else
- milli = 200;
- Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
-#ifndef CURL_DISABLE_SOCKETPAIR
- }
-#endif
-
-
- return ret_val;
-}
-
-#ifndef HAVE_GETADDRINFO
-/*
- * Curl_getaddrinfo() - for platforms without getaddrinfo
- */
-struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
- const char *hostname,
- int port,
- int *waitp)
-{
- struct resdata *reslv = (struct resdata *)data->state.async.resolver;
-
- *waitp = 0; /* default to synchronous response */
-
- reslv->start = Curl_now();
-
- /* fire up a new resolver thread! */
- if(init_resolve_thread(data, hostname, port, NULL)) {
- *waitp = 1; /* expect asynchronous response */
- return NULL;
- }
-
- failf(data, "getaddrinfo() thread failed");
-
- return NULL;
-}
-
-#else /* !HAVE_GETADDRINFO */
-
-/*
- * Curl_resolver_getaddrinfo() - for getaddrinfo
- */
-struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
- const char *hostname,
- int port,
- int *waitp)
-{
- struct addrinfo hints;
- int pf = PF_INET;
- struct resdata *reslv = (struct resdata *)data->state.async.resolver;
-
- *waitp = 0; /* default to synchronous response */
-
-#ifdef CURLRES_IPV6
- if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
- /* The stack seems to be IPv6-enabled */
- if(data->conn->ip_version == CURL_IPRESOLVE_V6)
- pf = PF_INET6;
- else
- pf = PF_UNSPEC;
- }
-#endif /* CURLRES_IPV6 */
-
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = pf;
- hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP) ?
- SOCK_STREAM : SOCK_DGRAM;
-
- reslv->start = Curl_now();
- /* fire up a new resolver thread! */
- if(init_resolve_thread(data, hostname, port, &hints)) {
- *waitp = 1; /* expect asynchronous response */
- return NULL;
- }
-
- failf(data, "getaddrinfo() thread failed to start");
- return NULL;
-
-}
-
-#endif /* !HAVE_GETADDRINFO */
-
-CURLcode Curl_set_dns_servers(struct Curl_easy *data,
- char *servers)
-{
- (void)data;
- (void)servers;
- return CURLE_NOT_BUILT_IN;
-
-}
-
-CURLcode Curl_set_dns_interface(struct Curl_easy *data,
- const char *interf)
-{
- (void)data;
- (void)interf;
- return CURLE_NOT_BUILT_IN;
-}
-
-CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
- const char *local_ip4)
-{
- (void)data;
- (void)local_ip4;
- return CURLE_NOT_BUILT_IN;
-}
-
-CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
- const char *local_ip6)
-{
- (void)data;
- (void)local_ip6;
- return CURLE_NOT_BUILT_IN;
-}
-
-#endif /* CURLRES_THREADED */
diff --git a/libs/libcurl/src/asyn.h b/libs/libcurl/src/asyn.h
index fd6792edb6..16626d8a8f 100644
--- a/libs/libcurl/src/asyn.h
+++ b/libs/libcurl/src/asyn.h
@@ -25,13 +25,22 @@
***************************************************************************/
#include "curl_setup.h"
+
+struct Curl_easy;
+struct Curl_dns_entry;
+
+#ifdef CURLRES_ASYNCH
+
#include "curl_addrinfo.h"
+#include "httpsrr.h"
struct addrinfo;
struct hostent;
-struct Curl_easy;
struct connectdata;
-struct Curl_dns_entry;
+
+#if defined(CURLRES_ARES) && defined(CURLRES_THREADED)
+#error cannot have both CURLRES_ARES and CURLRES_THREADED defined
+#endif
/*
* This header defines all functions in the internal asynch resolver interface.
@@ -41,85 +50,38 @@ struct Curl_dns_entry;
*/
/*
- * Curl_resolver_global_init()
+ * Curl_async_global_init()
*
* Called from curl_global_init() to initialize global resolver environment.
* Returning anything else than CURLE_OK fails curl_global_init().
*/
-int Curl_resolver_global_init(void);
+int Curl_async_global_init(void);
/*
- * Curl_resolver_global_cleanup()
+ * Curl_async_global_cleanup()
* Called from curl_global_cleanup() to destroy global resolver environment.
*/
-void Curl_resolver_global_cleanup(void);
-
-/*
- * Curl_resolver_init()
- * Called from curl_easy_init() -> Curl_open() to initialize resolver
- * URL-state specific environment ('resolver' member of the UrlState
- * structure). Should fill the passed pointer by the initialized handler.
- * Returning anything else than CURLE_OK fails curl_easy_init() with the
- * correspondent code.
- */
-CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver);
-
-/*
- * Curl_resolver_cleanup()
- * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
- * URL-state specific environment ('resolver' member of the UrlState
- * structure). Should destroy the handler and free all resources connected to
- * it.
- */
-void Curl_resolver_cleanup(void *resolver);
+void Curl_async_global_cleanup(void);
/*
- * Curl_resolver_duphandle()
- * Called from curl_easy_duphandle() to duplicate resolver URL-state specific
- * environment ('resolver' member of the UrlState structure). Should
- * duplicate the 'from' handle and pass the resulting handle to the 'to'
- * pointer. Returning anything else than CURLE_OK causes failed
- * curl_easy_duphandle() call.
+ * Curl_async_get_impl()
+ * Get the resolver implementation instance (c-ares channel) or NULL
+ * for passing to application callback.
*/
-CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to,
- void *from);
+CURLcode Curl_async_get_impl(struct Curl_easy *easy, void **impl);
-/*
- * Curl_resolver_cancel().
- *
- * It is called from inside other functions to cancel currently performing
- * resolver request. Should also free any temporary resources allocated to
- * perform a request. This never waits for resolver threads to complete.
- *
- * It is safe to call this when conn is in any state.
- */
-void Curl_resolver_cancel(struct Curl_easy *data);
-
-/*
- * Curl_resolver_kill().
+/* Curl_async_getsock()
*
- * This acts like Curl_resolver_cancel() except it will block until any threads
- * associated with the resolver are complete. This never blocks for resolvers
- * that do not use threads. This is intended to be the "last chance" function
- * that cleans up an in-progress resolver completely (before its owner is about
- * to die).
- *
- * It is safe to call this when conn is in any state.
- */
-void Curl_resolver_kill(struct Curl_easy *data);
-
-/* Curl_resolver_getsock()
- *
- * This function is called from the multi_getsock() function. 'sock' is a
+ * This function is called from the Curl_multi_getsock() function. 'sock' is a
* pointer to an array to hold the file descriptors, with 'numsock' being the
* size of that array (in number of entries). This function is supposed to
* return bitmask indicating what file descriptors (referring to array indexes
* in the 'sock' array) to wait for, read/write.
*/
-int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *sock);
+int Curl_async_getsock(struct Curl_easy *data, curl_socket_t *sock);
/*
- * Curl_resolver_is_resolved()
+ * Curl_async_is_resolved()
*
* Called repeatedly to check if a previous name resolve request has
* completed. It should also make sure to time-out if the operation seems to
@@ -127,25 +89,25 @@ int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *sock);
*
* Returns normal CURLcode errors.
*/
-CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
- struct Curl_dns_entry **dns);
+CURLcode Curl_async_is_resolved(struct Curl_easy *data,
+ struct Curl_dns_entry **dns);
/*
- * Curl_resolver_wait_resolv()
+ * Curl_async_await()
*
* Waits for a resolve to finish. This function should be avoided since using
* this risk getting the multi interface to "hang".
*
- * If 'entry' is non-NULL, make it point to the resolved dns entry
+ * On return 'entry' is assigned the resolved dns (CURLE_OK or NULL otherwise.
*
* Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
* CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
*/
-CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
- struct Curl_dns_entry **dnsentry);
+CURLcode Curl_async_await(struct Curl_easy *data,
+ struct Curl_dns_entry **dnsentry);
/*
- * Curl_resolver_getaddrinfo() - when using this resolver
+ * Curl_async_getaddrinfo() - when using this resolver
*
* Returns name information about the given hostname and port number. If
* successful, the 'hostent' is returned and the fourth argument will point to
@@ -155,29 +117,157 @@ CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
* Each resolver backend must of course make sure to return data in the
* correct format to comply with this.
*/
-struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
- const char *hostname,
- int port,
- int *waitp);
+struct Curl_addrinfo *Curl_async_getaddrinfo(struct Curl_easy *data,
+ const char *hostname,
+ int port,
+ int ip_version,
+ int *waitp);
+
+#ifdef USE_ARES
+/* common functions for c-ares and threaded resolver with HTTPSRR */
+#include <ares.h>
+
+int Curl_ares_getsock(struct Curl_easy *data,
+ ares_channel channel,
+ curl_socket_t *socks);
+int Curl_ares_perform(ares_channel channel,
+ timediff_t timeout_ms);
+#endif
+
+#ifdef CURLRES_ARES
+/* async resolving implementation using c-ares alone */
+struct async_ares_ctx {
+ ares_channel channel;
+ int num_pending; /* number of outstanding c-ares requests */
+ struct Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares
+ parts */
+ int last_status;
+ CURLcode result; /* CURLE_OK or error handling response */
+#ifndef HAVE_CARES_GETADDRINFO
+ struct curltime happy_eyeballs_dns_time; /* when this timer started, or 0 */
+#endif
+#ifdef USE_HTTPSRR
+ struct Curl_https_rrinfo hinfo;
+#endif
+};
+
+void Curl_async_ares_shutdown(struct Curl_easy *data);
+void Curl_async_ares_destroy(struct Curl_easy *data);
+
+/* Set the DNS server to use by ares, from `data` settings. */
+CURLcode Curl_async_ares_set_dns_servers(struct Curl_easy *data);
+
+/* Set the DNS interfacer to use by ares, from `data` settings. */
+CURLcode Curl_async_ares_set_dns_interface(struct Curl_easy *data);
+
+/* Set the local ipv4 address to use by ares, from `data` settings. */
+CURLcode Curl_async_ares_set_dns_local_ip4(struct Curl_easy *data);
+
+/* Set the local ipv6 address to use by ares, from `data` settings. */
+CURLcode Curl_async_ares_set_dns_local_ip6(struct Curl_easy *data);
+
+#endif /* CURLRES_ARES */
+
+#ifdef CURLRES_THREADED
+/* async resolving implementation using POSIX threads */
+#include "curl_threads.h"
+
+/* Context for threaded address resolver */
+struct async_thrdd_addr_ctx {
+ curl_thread_t thread_hnd;
+ char *hostname; /* hostname to resolve, Curl_async.hostname
+ duplicate */
+ curl_mutex_t mutx;
+#ifndef CURL_DISABLE_SOCKETPAIR
+ curl_socket_t sock_pair[2]; /* eventfd/pipes/socket pair */
+#endif
+ struct Curl_addrinfo *res;
+#ifdef HAVE_GETADDRINFO
+ struct addrinfo hints;
+#endif
+ struct curltime start;
+ timediff_t interval_end;
+ unsigned int poll_interval;
+ int port;
+ int sock_error;
+ int ref_count;
+};
+
+/* Context for threaded resolver */
+struct async_thrdd_ctx {
+ /* `addr` is a pointer since this memory is shared with a started
+ * thread. Since threads cannot be killed, we use reference counting
+ * so that we can "release" our pointer to this memory while the
+ * thread is still running. */
+ struct async_thrdd_addr_ctx *addr;
+#if defined(USE_HTTPSRR) && defined(USE_ARES)
+ struct {
+ ares_channel channel;
+ struct Curl_https_rrinfo hinfo;
+ CURLcode result;
+ BIT(done);
+ } rr;
+#endif
+};
+
+void Curl_async_thrdd_shutdown(struct Curl_easy *data);
+void Curl_async_thrdd_destroy(struct Curl_easy *data);
+
+#endif /* CURLRES_THREADED */
+
+#ifndef CURL_DISABLE_DOH
+struct doh_probes;
+#endif
+
+#else /* CURLRES_ASYNCH */
-#ifndef CURLRES_ASYNCH
/* convert these functions if an asynch resolver is not used */
-#define Curl_resolver_cancel(x) Curl_nop_stmt
-#define Curl_resolver_kill(x) Curl_nop_stmt
-#define Curl_resolver_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST
-#define Curl_resolver_wait_resolv(x,y) CURLE_COULDNT_RESOLVE_HOST
-#define Curl_resolver_duphandle(x,y,z) CURLE_OK
-#define Curl_resolver_init(x,y) CURLE_OK
-#define Curl_resolver_global_init() CURLE_OK
-#define Curl_resolver_global_cleanup() Curl_nop_stmt
-#define Curl_resolver_cleanup(x) Curl_nop_stmt
+#define Curl_async_get_impl(x,y) (*(y) = NULL, CURLE_OK)
+#define Curl_async_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST
+#define Curl_async_await(x,y) CURLE_COULDNT_RESOLVE_HOST
+#define Curl_async_global_init() CURLE_OK
+#define Curl_async_global_cleanup() Curl_nop_stmt
+
+#endif /* !CURLRES_ASYNCH */
+
+#if defined(CURLRES_ASYNCH) || !defined(CURL_DISABLE_DOH)
+#define USE_CURL_ASYNC
#endif
-#ifdef CURLRES_ASYNCH
-#define Curl_resolver_asynch() 1
-#else
-#define Curl_resolver_asynch() 0
+#ifdef USE_CURL_ASYNC
+struct Curl_async {
+#ifdef CURLRES_ARES /* */
+ struct async_ares_ctx ares;
+#elif defined(CURLRES_THREADED)
+ struct async_thrdd_ctx thrdd;
#endif
+#ifndef CURL_DISABLE_DOH
+ struct doh_probes *doh; /* DoH specific data for this request */
+#endif
+ struct Curl_dns_entry *dns; /* result of resolving on success */
+ char *hostname; /* copy of the params resolv started with */
+ int port;
+ int ip_version;
+ BIT(done);
+};
+
+/*
+ * Curl_async_shutdown().
+ *
+ * This shuts down all ongoing operations.
+ */
+void Curl_async_shutdown(struct Curl_easy *data);
+
+/*
+ * Curl_async_destroy().
+ *
+ * This frees the resources of any async resolve.
+ */
+void Curl_async_destroy(struct Curl_easy *data);
+#else /* !USE_CURL_ASYNC */
+#define Curl_async_shutdown(x) Curl_nop_stmt
+#define Curl_async_destroy(x) Curl_nop_stmt
+#endif /* USE_CURL_ASYNC */
/********** end of generic resolver interface functions *****************/
diff --git a/libs/libcurl/src/bufq.c b/libs/libcurl/src/bufq.c
index 93c56aacec..a52bbad2c4 100644
--- a/libs/libcurl/src/bufq.c
+++ b/libs/libcurl/src/bufq.c
@@ -45,11 +45,6 @@ static size_t chunk_len(const struct buf_chunk *chunk)
return chunk->w_offset - chunk->r_offset;
}
-static size_t chunk_space(const struct buf_chunk *chunk)
-{
- return chunk->dlen - chunk->w_offset;
-}
-
static void chunk_reset(struct buf_chunk *chunk)
{
chunk->next = NULL;
@@ -287,24 +282,6 @@ size_t Curl_bufq_len(const struct bufq *q)
return len;
}
-size_t Curl_bufq_space(const struct bufq *q)
-{
- size_t space = 0;
- if(q->tail)
- space += chunk_space(q->tail);
- if(q->spare) {
- struct buf_chunk *chunk = q->spare;
- while(chunk) {
- space += chunk->dlen;
- chunk = chunk->next;
- }
- }
- if(q->chunk_count < q->max_chunks) {
- space += (q->max_chunks - q->chunk_count) * q->chunk_size;
- }
- return space;
-}
-
bool Curl_bufq_is_empty(const struct bufq *q)
{
return !q->head || chunk_is_empty(q->head);
diff --git a/libs/libcurl/src/bufq.h b/libs/libcurl/src/bufq.h
index 71e2de8d82..f9e5818f31 100644
--- a/libs/libcurl/src/bufq.h
+++ b/libs/libcurl/src/bufq.h
@@ -151,14 +151,6 @@ void Curl_bufq_free(struct bufq *q);
size_t Curl_bufq_len(const struct bufq *q);
/**
- * Return the total amount of free space in the queue.
- * The returned length is the number of bytes that can
- * be expected to be written successfully to the bufq,
- * providing no memory allocations fail.
- */
-size_t Curl_bufq_space(const struct bufq *q);
-
-/**
* Returns TRUE iff there is no data in the buffer queue.
*/
bool Curl_bufq_is_empty(const struct bufq *q);
diff --git a/libs/libcurl/src/bufref.c b/libs/libcurl/src/bufref.c
index e94dc910b2..45409d02e2 100644
--- a/libs/libcurl/src/bufref.c
+++ b/libs/libcurl/src/bufref.c
@@ -30,7 +30,9 @@
#include "curl_memory.h"
#include "memdebug.h"
+#ifdef DEBUGBUILD
#define SIGNATURE 0x5c48e9b2 /* Random pattern. */
+#endif
/*
* Init a bufref struct.
@@ -59,7 +61,7 @@ void Curl_bufref_free(struct bufref *br)
DEBUGASSERT(br->ptr || !br->len);
if(br->ptr && br->dtor)
- br->dtor((void *) br->ptr);
+ br->dtor(CURL_UNCONST(br->ptr));
br->dtor = NULL;
br->ptr = NULL;
diff --git a/libs/libcurl/src/c-hyper.c b/libs/libcurl/src/c-hyper.c
deleted file mode 100644
index bf70002c04..0000000000
--- a/libs/libcurl/src/c-hyper.c
+++ /dev/null
@@ -1,1254 +0,0 @@
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-/* Curl's integration with Hyper. This replaces certain functions in http.c,
- * based on configuration #defines. This implementation supports HTTP/1.1 but
- * not HTTP/2.
- */
-#include "curl_setup.h"
-
-#if !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER)
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef HAVE_NET_IF_H
-#include <net/if.h>
-#endif
-#ifdef HAVE_SYS_IOCTL_H
-#include <sys/ioctl.h>
-#endif
-
-#ifdef HAVE_SYS_PARAM_H
-#include <sys/param.h>
-#endif
-
-#include <hyper.h>
-#include "urldata.h"
-#include "cfilters.h"
-#include "sendf.h"
-#include "headers.h"
-#include "transfer.h"
-#include "multiif.h"
-#include "progress.h"
-#include "content_encoding.h"
-#include "ws.h"
-
-/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
-
-
-static CURLcode cr_hyper_add(struct Curl_easy *data);
-
-typedef enum {
- USERDATA_NOT_SET = 0, /* for tasks with no userdata set; must be zero */
- USERDATA_RESP_BODY
-} userdata_t;
-
-size_t Curl_hyper_recv(void *userp, hyper_context *ctx,
- uint8_t *buf, size_t buflen)
-{
- struct hyp_io_ctx *io_ctx = userp;
- struct Curl_easy *data = io_ctx->data;
- struct connectdata *conn = data->conn;
- CURLcode result;
- ssize_t nread;
- DEBUGASSERT(conn);
- (void)ctx;
-
- DEBUGF(infof(data, "Curl_hyper_recv(%zu)", buflen));
- result = Curl_conn_recv(data, io_ctx->sockindex,
- (char *)buf, buflen, &nread);
- if(result == CURLE_AGAIN) {
- /* would block, register interest */
- DEBUGF(infof(data, "Curl_hyper_recv(%zu) -> EAGAIN", buflen));
- if(data->hyp.read_waker)
- hyper_waker_free(data->hyp.read_waker);
- data->hyp.read_waker = hyper_context_waker(ctx);
- if(!data->hyp.read_waker) {
- failf(data, "Couldn't make the read hyper_context_waker");
- return HYPER_IO_ERROR;
- }
- return HYPER_IO_PENDING;
- }
- else if(result) {
- failf(data, "Curl_read failed");
- return HYPER_IO_ERROR;
- }
- DEBUGF(infof(data, "Curl_hyper_recv(%zu) -> %zd", buflen, nread));
- return (size_t)nread;
-}
-
-size_t Curl_hyper_send(void *userp, hyper_context *ctx,
- const uint8_t *buf, size_t buflen)
-{
- struct hyp_io_ctx *io_ctx = userp;
- struct Curl_easy *data = io_ctx->data;
- CURLcode result;
- size_t nwrote;
-
- DEBUGF(infof(data, "Curl_hyper_send(%zu)", buflen));
- result = Curl_conn_send(data, io_ctx->sockindex,
- (void *)buf, buflen, FALSE, &nwrote);
- if(result == CURLE_AGAIN) {
- DEBUGF(infof(data, "Curl_hyper_send(%zu) -> EAGAIN", buflen));
- /* would block, register interest */
- if(data->hyp.write_waker)
- hyper_waker_free(data->hyp.write_waker);
- data->hyp.write_waker = hyper_context_waker(ctx);
- if(!data->hyp.write_waker) {
- failf(data, "Couldn't make the write hyper_context_waker");
- return HYPER_IO_ERROR;
- }
- return HYPER_IO_PENDING;
- }
- else if(result) {
- failf(data, "Curl_write failed");
- return HYPER_IO_ERROR;
- }
- DEBUGF(infof(data, "Curl_hyper_send(%zu) -> %zd", buflen, nwrote));
- return (size_t)nwrote;
-}
-
-static int hyper_each_header(void *userdata,
- const uint8_t *name,
- size_t name_len,
- const uint8_t *value,
- size_t value_len)
-{
- struct Curl_easy *data = (struct Curl_easy *)userdata;
- size_t len;
- char *headp;
- CURLcode result;
- int writetype;
-
- if(name_len + value_len + 2 > CURL_MAX_HTTP_HEADER) {
- failf(data, "Too long response header");
- data->state.hresult = CURLE_TOO_LARGE;
- return HYPER_ITER_BREAK;
- }
-
- Curl_dyn_reset(&data->state.headerb);
- if(name_len) {
- if(Curl_dyn_addf(&data->state.headerb, "%.*s: %.*s\r\n",
- (int) name_len, name, (int) value_len, value))
- return HYPER_ITER_BREAK;
- }
- else {
- if(Curl_dyn_addn(&data->state.headerb, STRCONST("\r\n")))
- return HYPER_ITER_BREAK;
- }
- len = Curl_dyn_len(&data->state.headerb);
- headp = Curl_dyn_ptr(&data->state.headerb);
-
- result = Curl_http_header(data, headp, len);
- if(result) {
- data->state.hresult = result;
- return HYPER_ITER_BREAK;
- }
-
- Curl_debug(data, CURLINFO_HEADER_IN, headp, len);
-
- writetype = CLIENTWRITE_HEADER;
- if(data->state.hconnect)
- writetype |= CLIENTWRITE_CONNECT;
- if(data->req.httpcode/100 == 1)
- writetype |= CLIENTWRITE_1XX;
- result = Curl_client_write(data, writetype, headp, len);
- if(result) {
- data->state.hresult = CURLE_ABORTED_BY_CALLBACK;
- return HYPER_ITER_BREAK;
- }
-
- result = Curl_bump_headersize(data, len, FALSE);
- if(result) {
- data->state.hresult = result;
- return HYPER_ITER_BREAK;
- }
- return HYPER_ITER_CONTINUE;
-}
-
-static int hyper_body_chunk(void *userdata, const hyper_buf *chunk)
-{
- char *buf = (char *)hyper_buf_bytes(chunk);
- size_t len = hyper_buf_len(chunk);
- struct Curl_easy *data = (struct Curl_easy *)userdata;
- struct SingleRequest *k = &data->req;
- CURLcode result = CURLE_OK;
-
- if(!k->bodywritten) {
-#if defined(USE_NTLM)
- struct connectdata *conn = data->conn;
- if(conn->bits.close &&
- (((data->req.httpcode == 401) &&
- (conn->http_ntlm_state == NTLMSTATE_TYPE2)) ||
- ((data->req.httpcode == 407) &&
- (conn->proxy_ntlm_state == NTLMSTATE_TYPE2)))) {
- infof(data, "Connection closed while negotiating NTLM");
- data->state.authproblem = TRUE;
- Curl_safefree(data->req.newurl);
- }
-#endif
- if(Curl_http_exp100_is_selected(data)) {
- if(data->req.httpcode < 400) {
- Curl_http_exp100_got100(data);
- if(data->hyp.send_body_waker) {
- hyper_waker_wake(data->hyp.send_body_waker);
- data->hyp.send_body_waker = NULL;
- }
- }
- else { /* >= 4xx */
- Curl_req_abort_sending(data);
- }
- }
- if(data->state.hconnect && (data->req.httpcode/100 != 2) &&
- data->state.authproxy.done) {
- data->req.done = TRUE;
- result = CURLE_OK;
- }
- else
- result = Curl_http_firstwrite(data);
- if(result || data->req.done) {
- infof(data, "Return early from hyper_body_chunk");
- data->state.hresult = result;
- return HYPER_ITER_BREAK;
- }
- }
- result = Curl_client_write(data, CLIENTWRITE_BODY, buf, len);
-
- if(result) {
- data->state.hresult = result;
- return HYPER_ITER_BREAK;
- }
-
- return HYPER_ITER_CONTINUE;
-}
-
-/*
- * Hyper does not consider the status line, the first line in an HTTP/1
- * response, to be a header. The libcurl API does. This function sends the
- * status line in the header callback. */
-static CURLcode status_line(struct Curl_easy *data,
- struct connectdata *conn,
- uint16_t http_status,
- int http_version,
- const uint8_t *reason, size_t rlen)
-{
- CURLcode result;
- size_t len;
- const char *vstr;
- int writetype;
- vstr = http_version == HYPER_HTTP_VERSION_1_1 ? "1.1" :
- (http_version == HYPER_HTTP_VERSION_2 ? "2" : "1.0");
-
- /* We need to set 'httpcodeq' for functions that check the response code in
- a single place. */
- data->req.httpcode = http_status;
- data->req.httpversion = http_version == HYPER_HTTP_VERSION_1_1 ? 11 :
- (http_version == HYPER_HTTP_VERSION_2 ? 20 : 10);
- if(data->state.hconnect)
- /* CONNECT */
- data->info.httpproxycode = http_status;
- else {
- conn->httpversion = (unsigned char)data->req.httpversion;
- if(http_version == HYPER_HTTP_VERSION_1_0)
- data->state.httpwant = CURL_HTTP_VERSION_1_0;
-
- result = Curl_http_statusline(data, conn);
- if(result)
- return result;
- }
-
- Curl_dyn_reset(&data->state.headerb);
-
- result = Curl_dyn_addf(&data->state.headerb, "HTTP/%s %03d %.*s\r\n",
- vstr,
- (int)http_status,
- (int)rlen, reason);
- if(result)
- return result;
- len = Curl_dyn_len(&data->state.headerb);
- Curl_debug(data, CURLINFO_HEADER_IN, Curl_dyn_ptr(&data->state.headerb),
- len);
-
- writetype = CLIENTWRITE_HEADER|CLIENTWRITE_STATUS;
- if(data->state.hconnect)
- writetype |= CLIENTWRITE_CONNECT;
- result = Curl_client_write(data, writetype,
- Curl_dyn_ptr(&data->state.headerb), len);
- if(result)
- return result;
-
- result = Curl_bump_headersize(data, len, FALSE);
- return result;
-}
-
-/*
- * Hyper does not pass on the last empty response header. The libcurl API
- * does. This function sends an empty header in the header callback.
- */
-static CURLcode empty_header(struct Curl_easy *data)
-{
- CURLcode result = Curl_http_size(data);
- if(!result) {
- result = hyper_each_header(data, NULL, 0, NULL, 0) ?
- CURLE_WRITE_ERROR : CURLE_OK;
- if(result)
- failf(data, "hyperstream: could not pass blank header");
- /* Hyper does chunked decoding itself. If it was added during
- * response header processing, remove it again. */
- Curl_cwriter_remove_by_name(data, "chunked");
- }
- return result;
-}
-
-CURLcode Curl_hyper_stream(struct Curl_easy *data,
- struct connectdata *conn,
- int *didwhat,
- int select_res)
-{
- hyper_response *resp = NULL;
- uint16_t http_status;
- int http_version;
- hyper_headers *headers = NULL;
- hyper_body *resp_body = NULL;
- struct hyptransfer *h = &data->hyp;
- hyper_task *task;
- hyper_task *foreach;
- const uint8_t *reasonp;
- size_t reason_len;
- CURLcode result = CURLE_OK;
- struct SingleRequest *k = &data->req;
- (void)conn;
-
- if(data->hyp.send_body_waker) {
- /* If there is still something to upload, wake it to give it
- * another try. */
- hyper_waker_wake(data->hyp.send_body_waker);
- data->hyp.send_body_waker = NULL;
- }
-
- if(select_res & CURL_CSELECT_IN) {
- if(h->read_waker)
- hyper_waker_wake(h->read_waker);
- h->read_waker = NULL;
- }
- if(select_res & CURL_CSELECT_OUT) {
- if(h->write_waker)
- hyper_waker_wake(h->write_waker);
- h->write_waker = NULL;
- }
-
- while(1) {
- hyper_task_return_type t;
- task = hyper_executor_poll(h->exec);
- if(!task) {
- *didwhat = KEEP_RECV;
- break;
- }
- t = hyper_task_type(task);
- if(t == HYPER_TASK_ERROR) {
- hyper_error *hypererr = hyper_task_value(task);
- hyper_task_free(task);
- if(data->state.hresult) {
- /* override Hyper's view, might not even be an error */
- result = data->state.hresult;
- infof(data, "hyperstream is done (by early callback)");
- }
- else {
- uint8_t errbuf[256];
- size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf));
- hyper_code code = hyper_error_code(hypererr);
- failf(data, "Hyper: [%d] %.*s", (int)code, (int)errlen, errbuf);
- switch(code) {
- case HYPERE_ABORTED_BY_CALLBACK:
- result = CURLE_OK;
- goto out;
- case HYPERE_UNEXPECTED_EOF:
- if(!data->req.bytecount)
- result = CURLE_GOT_NOTHING;
- else
- result = CURLE_RECV_ERROR;
- goto out;
- case HYPERE_INVALID_PEER_MESSAGE:
- /* bump headerbytecount to avoid the count remaining at zero and
- appearing to not having read anything from the peer at all */
- data->req.headerbytecount++;
- result = CURLE_UNSUPPORTED_PROTOCOL; /* maybe */
- goto out;
- default:
- result = CURLE_RECV_ERROR;
- goto out;
- }
- }
- data->req.done = TRUE;
- hyper_error_free(hypererr);
- break;
- }
- else if(t == HYPER_TASK_EMPTY) {
- void *userdata = hyper_task_userdata(task);
- hyper_task_free(task);
- if(userdata == (void *)USERDATA_RESP_BODY) {
- /* end of transfer */
- data->req.done = TRUE;
- infof(data, "hyperstream is done");
- if(!k->bodywritten) {
- /* hyper does not always call the body write callback */
- result = Curl_http_firstwrite(data);
- }
- break;
- }
- else {
- /* A background task for hyper; ignore */
- DEBUGF(infof(data, "hyper: some background task done"));
- continue;
- }
- }
- else if(t == HYPER_TASK_RESPONSE) {
- resp = hyper_task_value(task);
- hyper_task_free(task);
-
- *didwhat = KEEP_RECV;
- if(!resp) {
- failf(data, "hyperstream: could not get response");
- result = CURLE_RECV_ERROR;
- goto out;
- }
-
- http_status = hyper_response_status(resp);
- http_version = hyper_response_version(resp);
- reasonp = hyper_response_reason_phrase(resp);
- reason_len = hyper_response_reason_phrase_len(resp);
-
- if(http_status == 417 && Curl_http_exp100_is_selected(data)) {
- infof(data, "Got 417 while waiting for a 100");
- data->state.disableexpect = TRUE;
- data->req.newurl = strdup(data->state.url);
- Curl_req_abort_sending(data);
- }
-
- result = status_line(data, conn,
- http_status, http_version, reasonp, reason_len);
- if(result)
- goto out;
-
- headers = hyper_response_headers(resp);
- if(!headers) {
- failf(data, "hyperstream: could not get response headers");
- result = CURLE_RECV_ERROR;
- goto out;
- }
-
- /* the headers are already received */
- hyper_headers_foreach(headers, hyper_each_header, data);
- if(data->state.hresult) {
- result = data->state.hresult;
- goto out;
- }
-
- result = empty_header(data);
- if(result)
- goto out;
-
- k->deductheadercount =
- (100 <= http_status && 199 >= http_status) ? k->headerbytecount : 0;
-#ifndef CURL_DISABLE_WEBSOCKETS
- if(k->upgr101 == UPGR101_WS) {
- if(http_status == 101) {
- /* verify the response */
- result = Curl_ws_accept(data, NULL, 0);
- if(result)
- goto out;
- }
- else {
- failf(data, "Expected 101, got %u", k->httpcode);
- result = CURLE_HTTP_RETURNED_ERROR;
- goto out;
- }
- }
-#endif
-
- /* Curl_http_auth_act() checks what authentication methods that are
- * available and decides which one (if any) to use. It will set 'newurl'
- * if an auth method was picked. */
- result = Curl_http_auth_act(data);
- if(result)
- goto out;
-
- resp_body = hyper_response_body(resp);
- if(!resp_body) {
- failf(data, "hyperstream: could not get response body");
- result = CURLE_RECV_ERROR;
- goto out;
- }
- foreach = hyper_body_foreach(resp_body, hyper_body_chunk, data);
- if(!foreach) {
- failf(data, "hyperstream: body foreach failed");
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
- hyper_task_set_userdata(foreach, (void *)USERDATA_RESP_BODY);
- if(HYPERE_OK != hyper_executor_push(h->exec, foreach)) {
- failf(data, "Couldn't hyper_executor_push the body-foreach");
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
-
- hyper_response_free(resp);
- resp = NULL;
- }
- else {
- DEBUGF(infof(data, "hyper: unhandled tasktype %x", t));
- }
- } /* while(1) */
-
- if(!result && Curl_xfer_needs_flush(data)) {
- DEBUGF(infof(data, "Curl_hyper_stream(), connection needs flush"));
- result = Curl_xfer_flush(data);
- }
-
-out:
- DEBUGF(infof(data, "Curl_hyper_stream() -> %d", result));
- if(resp)
- hyper_response_free(resp);
- return result;
-}
-
-static CURLcode debug_request(struct Curl_easy *data,
- const char *method,
- const char *path)
-{
- char *req = aprintf("%s %s HTTP/1.1\r\n", method, path);
- if(!req)
- return CURLE_OUT_OF_MEMORY;
- Curl_debug(data, CURLINFO_HEADER_OUT, req, strlen(req));
- free(req);
- return CURLE_OK;
-}
-
-/*
- * Given a full header line "name: value" (optional CRLF in the input, should
- * be in the output), add to Hyper and send to the debug callback.
- *
- * Supports multiple headers.
- */
-
-CURLcode Curl_hyper_header(struct Curl_easy *data, hyper_headers *headers,
- const char *line)
-{
- const char *p;
- const char *n;
- size_t nlen;
- const char *v;
- size_t vlen;
- bool newline = TRUE;
- int numh = 0;
-
- if(!line)
- return CURLE_OK;
- n = line;
- do {
- size_t linelen = 0;
-
- p = strchr(n, ':');
- if(!p)
- /* this is fine if we already added at least one header */
- return numh ? CURLE_OK : CURLE_BAD_FUNCTION_ARGUMENT;
- nlen = p - n;
- p++; /* move past the colon */
- while(*p == ' ')
- p++;
- v = p;
- p = strchr(v, '\r');
- if(!p) {
- p = strchr(v, '\n');
- if(p)
- linelen = 1; /* LF only */
- else {
- p = strchr(v, '\0');
- newline = FALSE; /* no newline */
- }
- }
- else
- linelen = 2; /* CRLF ending */
- linelen += (p - n);
- vlen = p - v;
-
- if(HYPERE_OK != hyper_headers_add(headers, (uint8_t *)n, nlen,
- (uint8_t *)v, vlen)) {
- failf(data, "hyper refused to add header '%s'", line);
- return CURLE_OUT_OF_MEMORY;
- }
- if(data->set.verbose) {
- char *ptr = NULL;
- if(!newline) {
- ptr = aprintf("%.*s\r\n", (int)linelen, line);
- if(!ptr)
- return CURLE_OUT_OF_MEMORY;
- Curl_debug(data, CURLINFO_HEADER_OUT, ptr, linelen + 2);
- free(ptr);
- }
- else
- Curl_debug(data, CURLINFO_HEADER_OUT, (char *)n, linelen);
- }
- numh++;
- n += linelen;
- } while(newline);
- return CURLE_OK;
-}
-
-static CURLcode request_target(struct Curl_easy *data,
- struct connectdata *conn,
- const char *method,
- hyper_request *req)
-{
- CURLcode result;
- struct dynbuf r;
-
- Curl_dyn_init(&r, DYN_HTTP_REQUEST);
-
- result = Curl_http_target(data, conn, &r);
- if(result)
- return result;
-
- if(hyper_request_set_uri(req, (uint8_t *)Curl_dyn_uptr(&r),
- Curl_dyn_len(&r))) {
- failf(data, "error setting uri to hyper");
- result = CURLE_OUT_OF_MEMORY;
- }
- else
- result = debug_request(data, method, Curl_dyn_ptr(&r));
-
- Curl_dyn_free(&r);
-
- return result;
-}
-
-static int uploadstreamed(void *userdata, hyper_context *ctx,
- hyper_buf **chunk)
-{
- size_t fillcount;
- struct Curl_easy *data = (struct Curl_easy *)userdata;
- CURLcode result;
- char *xfer_ulbuf;
- size_t xfer_ulblen;
- bool eos;
- int rc = HYPER_POLL_ERROR;
- (void)ctx;
-
- result = Curl_multi_xfer_ulbuf_borrow(data, &xfer_ulbuf, &xfer_ulblen);
- if(result)
- goto out;
-
- result = Curl_client_read(data, xfer_ulbuf, xfer_ulblen, &fillcount, &eos);
- if(result)
- goto out;
-
- if(fillcount) {
- hyper_buf *copy = hyper_buf_copy((uint8_t *)xfer_ulbuf, fillcount);
- if(copy)
- *chunk = copy;
- else {
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
- /* increasing the writebytecount here is a little premature but we
- do not know exactly when the body is sent */
- data->req.writebytecount += fillcount;
- if(eos)
- data->req.eos_read = TRUE;
- Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
- rc = HYPER_POLL_READY;
- }
- else if(eos) {
- data->req.eos_read = TRUE;
- *chunk = NULL;
- rc = HYPER_POLL_READY;
- }
- else {
- /* paused, save a waker */
- if(data->hyp.send_body_waker)
- hyper_waker_free(data->hyp.send_body_waker);
- data->hyp.send_body_waker = hyper_context_waker(ctx);
- rc = HYPER_POLL_PENDING;
- }
-
- if(!data->req.upload_done && data->req.eos_read) {
- DEBUGF(infof(data, "hyper: uploadstreamed(), upload is done"));
- result = Curl_req_set_upload_done(data);
- }
-
-out:
- Curl_multi_xfer_ulbuf_release(data, xfer_ulbuf);
- data->state.hresult = result;
- DEBUGF(infof(data, "hyper: uploadstreamed() -> %d", result));
- return rc;
-}
-
-/*
- * finalize_request() sets up last headers and optional body settings
- */
-static CURLcode finalize_request(struct Curl_easy *data,
- hyper_headers *headers,
- hyper_request *hyperreq,
- Curl_HttpReq httpreq)
-{
- CURLcode result = CURLE_OK;
- struct dynbuf req;
- if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) {
- Curl_pgrsSetUploadSize(data, 0); /* no request body */
- }
- else {
- hyper_body *body;
- Curl_dyn_init(&req, DYN_HTTP_REQUEST);
- result = Curl_http_req_complete(data, &req, httpreq);
- if(result)
- return result;
-
- /* if the "complete" above did produce more than the closing line,
- parse the added headers */
- if(Curl_dyn_len(&req) != 2 || strcmp(Curl_dyn_ptr(&req), "\r\n")) {
- result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&req));
- if(result)
- return result;
- }
-
- Curl_dyn_free(&req);
-
- body = hyper_body_new();
- hyper_body_set_userdata(body, data);
- hyper_body_set_data_func(body, uploadstreamed);
-
- if(HYPERE_OK != hyper_request_set_body(hyperreq, body)) {
- /* fail */
- result = CURLE_OUT_OF_MEMORY;
- }
- }
-
- return cr_hyper_add(data);
-}
-
-static CURLcode cookies(struct Curl_easy *data,
- struct connectdata *conn,
- hyper_headers *headers)
-{
- struct dynbuf req;
- CURLcode result;
- Curl_dyn_init(&req, DYN_HTTP_REQUEST);
-
- result = Curl_http_cookies(data, conn, &req);
- if(!result)
- result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&req));
- Curl_dyn_free(&req);
- return result;
-}
-
-/* called on 1xx responses */
-static void http1xx_cb(void *arg, struct hyper_response *resp)
-{
- struct Curl_easy *data = (struct Curl_easy *)arg;
- hyper_headers *headers = NULL;
- CURLcode result = CURLE_OK;
- uint16_t http_status;
- int http_version;
- const uint8_t *reasonp;
- size_t reason_len;
-
- infof(data, "Got HTTP 1xx informational");
-
- http_status = hyper_response_status(resp);
- http_version = hyper_response_version(resp);
- reasonp = hyper_response_reason_phrase(resp);
- reason_len = hyper_response_reason_phrase_len(resp);
-
- result = status_line(data, data->conn,
- http_status, http_version, reasonp, reason_len);
- if(!result) {
- headers = hyper_response_headers(resp);
- if(!headers) {
- failf(data, "hyperstream: could not get 1xx response headers");
- result = CURLE_RECV_ERROR;
- }
- }
- data->state.hresult = result;
-
- if(!result) {
- /* the headers are already received */
- hyper_headers_foreach(headers, hyper_each_header, data);
- /* this callback also sets data->state.hresult on error */
-
- if(empty_header(data))
- result = CURLE_OUT_OF_MEMORY;
- }
-
- if(data->state.hresult)
- infof(data, "ERROR in 1xx, bail out");
-}
-
-/*
- * Curl_http() gets called from the generic multi_do() function when an HTTP
- * request is to be performed. This creates and sends a properly constructed
- * HTTP request.
- */
-CURLcode Curl_http(struct Curl_easy *data, bool *done)
-{
- struct connectdata *conn = data->conn;
- struct hyptransfer *h = &data->hyp;
- hyper_io *io = NULL;
- hyper_clientconn_options *options = NULL;
- hyper_task *task = NULL; /* for the handshake */
- hyper_task *sendtask = NULL; /* for the send */
- hyper_clientconn *client = NULL;
- hyper_request *req = NULL;
- hyper_headers *headers = NULL;
- hyper_task *handshake = NULL;
- CURLcode result;
- const char *p_accept; /* Accept: string */
- const char *method;
- Curl_HttpReq httpreq;
- const char *te = NULL; /* transfer-encoding */
- hyper_code rc;
-
- /* Always consider the DO phase done after this function call, even if there
- may be parts of the request that is not yet sent, since we can deal with
- the rest of the request in the PERFORM phase. */
- *done = TRUE;
- result = Curl_client_start(data);
- if(result)
- goto out;
-
- /* Add collecting of headers written to client. For a new connection,
- * we might have done that already, but reuse
- * or multiplex needs it here as well. */
- result = Curl_headers_init(data);
- if(result)
- goto out;
-
- infof(data, "Time for the Hyper dance");
- memset(h, 0, sizeof(struct hyptransfer));
-
- result = Curl_http_host(data, conn);
- if(result)
- goto out;
-
- Curl_http_method(data, conn, &method, &httpreq);
-
- DEBUGASSERT(data->req.bytecount == 0);
-
- /* setup the authentication headers */
- {
- char *pq = NULL;
- if(data->state.up.query) {
- pq = aprintf("%s?%s", data->state.up.path, data->state.up.query);
- if(!pq) {
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
- }
- result = Curl_http_output_auth(data, conn, method, httpreq,
- (pq ? pq : data->state.up.path), FALSE);
- free(pq);
- if(result)
- goto out;
- }
-
- result = Curl_http_req_set_reader(data, httpreq, &te);
- if(result)
- goto out;
-
- result = Curl_http_range(data, httpreq);
- if(result)
- goto out;
-
- result = Curl_http_useragent(data);
- if(result)
- goto out;
-
- io = hyper_io_new();
- if(!io) {
- failf(data, "Couldn't create hyper IO");
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
- /* tell Hyper how to read/write network data */
- h->io_ctx.data = data;
- h->io_ctx.sockindex = FIRSTSOCKET;
- hyper_io_set_userdata(io, &h->io_ctx);
- hyper_io_set_read(io, Curl_hyper_recv);
- hyper_io_set_write(io, Curl_hyper_send);
-
- /* create an executor to poll futures */
- if(!h->exec) {
- h->exec = hyper_executor_new();
- if(!h->exec) {
- failf(data, "Couldn't create hyper executor");
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
- }
-
- options = hyper_clientconn_options_new();
- if(!options) {
- failf(data, "Couldn't create hyper client options");
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
- if(conn->alpn == CURL_HTTP_VERSION_2) {
- failf(data, "ALPN protocol h2 not supported with Hyper");
- result = CURLE_UNSUPPORTED_PROTOCOL;
- goto out;
- }
- hyper_clientconn_options_set_preserve_header_case(options, 1);
- hyper_clientconn_options_set_preserve_header_order(options, 1);
- hyper_clientconn_options_http1_allow_multiline_headers(options, 1);
-
- hyper_clientconn_options_exec(options, h->exec);
-
- /* "Both the `io` and the `options` are consumed in this function call" */
- handshake = hyper_clientconn_handshake(io, options);
- if(!handshake) {
- failf(data, "Couldn't create hyper client handshake");
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
- io = NULL;
- options = NULL;
-
- if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) {
- failf(data, "Couldn't hyper_executor_push the handshake");
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
- handshake = NULL; /* ownership passed on */
-
- task = hyper_executor_poll(h->exec);
- if(!task) {
- failf(data, "Couldn't hyper_executor_poll the handshake");
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
-
- client = hyper_task_value(task);
- hyper_task_free(task);
-
- req = hyper_request_new();
- if(!req) {
- failf(data, "Couldn't hyper_request_new");
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
-
- if(!Curl_use_http_1_1plus(data, conn)) {
- if(HYPERE_OK != hyper_request_set_version(req,
- HYPER_HTTP_VERSION_1_0)) {
- failf(data, "error setting HTTP version");
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
- }
-
- if(hyper_request_set_method(req, (uint8_t *)method, strlen(method))) {
- failf(data, "error setting method");
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
-
- result = request_target(data, conn, method, req);
- if(result)
- goto out;
-
- headers = hyper_request_headers(req);
- if(!headers) {
- failf(data, "hyper_request_headers");
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
-
- rc = hyper_request_on_informational(req, http1xx_cb, data);
- if(rc) {
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
-
- if(data->state.aptr.host) {
- result = Curl_hyper_header(data, headers, data->state.aptr.host);
- if(result)
- goto out;
- }
-
-#ifndef CURL_DISABLE_PROXY
- if(data->state.aptr.proxyuserpwd) {
- result = Curl_hyper_header(data, headers, data->state.aptr.proxyuserpwd);
- if(result)
- goto out;
- }
-#endif
-
- if(data->state.aptr.userpwd) {
- result = Curl_hyper_header(data, headers, data->state.aptr.userpwd);
- if(result)
- goto out;
- }
-
- if((data->state.use_range && data->state.aptr.rangeline)) {
- result = Curl_hyper_header(data, headers, data->state.aptr.rangeline);
- if(result)
- goto out;
- }
-
- if(data->set.str[STRING_USERAGENT] &&
- *data->set.str[STRING_USERAGENT] &&
- data->state.aptr.uagent) {
- result = Curl_hyper_header(data, headers, data->state.aptr.uagent);
- if(result)
- goto out;
- }
-
- p_accept = Curl_checkheaders(data,
- STRCONST("Accept")) ? NULL : "Accept: */*\r\n";
- if(p_accept) {
- result = Curl_hyper_header(data, headers, p_accept);
- if(result)
- goto out;
- }
- if(te) {
- result = Curl_hyper_header(data, headers, te);
- if(result)
- goto out;
- }
-
-#ifndef CURL_DISABLE_ALTSVC
- if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used"))) {
- char *altused = aprintf("Alt-Used: %s:%d\r\n",
- conn->conn_to_host.name, conn->conn_to_port);
- if(!altused) {
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
- result = Curl_hyper_header(data, headers, altused);
- if(result)
- goto out;
- free(altused);
- }
-#endif
-
-#ifndef CURL_DISABLE_PROXY
- if(conn->bits.httpproxy && !conn->bits.tunnel_proxy &&
- !Curl_checkheaders(data, STRCONST("Proxy-Connection")) &&
- !Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) {
- result = Curl_hyper_header(data, headers, "Proxy-Connection: Keep-Alive");
- if(result)
- goto out;
- }
-#endif
-
- Curl_safefree(data->state.aptr.ref);
- if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer"))) {
- data->state.aptr.ref = aprintf("Referer: %s\r\n", data->state.referer);
- if(!data->state.aptr.ref)
- result = CURLE_OUT_OF_MEMORY;
- else
- result = Curl_hyper_header(data, headers, data->state.aptr.ref);
- if(result)
- goto out;
- }
-
-#ifdef HAVE_LIBZ
- /* we only consider transfer-encoding magic if libz support is built-in */
- result = Curl_transferencode(data);
- if(result)
- goto out;
- result = Curl_hyper_header(data, headers, data->state.aptr.te);
- if(result)
- goto out;
-#endif
-
- if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) &&
- data->set.str[STRING_ENCODING]) {
- Curl_safefree(data->state.aptr.accept_encoding);
- data->state.aptr.accept_encoding =
- aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
- if(!data->state.aptr.accept_encoding)
- result = CURLE_OUT_OF_MEMORY;
- else
- result = Curl_hyper_header(data, headers,
- data->state.aptr.accept_encoding);
- if(result)
- goto out;
- }
- else
- Curl_safefree(data->state.aptr.accept_encoding);
-
- result = cookies(data, conn, headers);
- if(result)
- goto out;
-
- if(!result && conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS))
- result = Curl_ws_request(data, headers);
-
- result = Curl_add_timecondition(data, headers);
- if(result)
- goto out;
-
- result = Curl_add_custom_headers(data, FALSE, headers);
- if(result)
- goto out;
-
- result = finalize_request(data, headers, req, httpreq);
- if(result)
- goto out;
-
- Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"\r\n", 2);
-
- if(data->req.upload_chunky && data->req.authneg) {
- data->req.upload_chunky = TRUE;
- }
- else {
- data->req.upload_chunky = FALSE;
- }
- sendtask = hyper_clientconn_send(client, req);
- if(!sendtask) {
- failf(data, "hyper_clientconn_send");
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
- req = NULL;
-
- if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) {
- failf(data, "Couldn't hyper_executor_push the send");
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
- sendtask = NULL; /* ownership passed on */
-
- hyper_clientconn_free(client);
- client = NULL;
-
- if((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) {
- /* HTTP GET/HEAD download */
- Curl_pgrsSetUploadSize(data, 0); /* nothing */
- result = Curl_req_set_upload_done(data);
- if(result)
- goto out;
- }
-
- Curl_xfer_setup1(data, CURL_XFER_SENDRECV, -1, TRUE);
- conn->datastream = Curl_hyper_stream;
-
- /* clear userpwd and proxyuserpwd to avoid reusing old credentials
- * from reused connections */
- Curl_safefree(data->state.aptr.userpwd);
-#ifndef CURL_DISABLE_PROXY
- Curl_safefree(data->state.aptr.proxyuserpwd);
-#endif
-
-out:
- if(result) {
- if(io)
- hyper_io_free(io);
- if(options)
- hyper_clientconn_options_free(options);
- if(handshake)
- hyper_task_free(handshake);
- if(client)
- hyper_clientconn_free(client);
- if(req)
- hyper_request_free(req);
- }
- return result;
-}
-
-void Curl_hyper_done(struct Curl_easy *data)
-{
- struct hyptransfer *h = &data->hyp;
- if(h->exec) {
- hyper_executor_free(h->exec);
- h->exec = NULL;
- }
- if(h->read_waker) {
- hyper_waker_free(h->read_waker);
- h->read_waker = NULL;
- }
- if(h->write_waker) {
- hyper_waker_free(h->write_waker);
- h->write_waker = NULL;
- }
- if(h->send_body_waker) {
- hyper_waker_free(h->send_body_waker);
- h->send_body_waker = NULL;
- }
-}
-
-static CURLcode cr_hyper_unpause(struct Curl_easy *data,
- struct Curl_creader *reader)
-{
- (void)reader;
- if(data->hyp.send_body_waker) {
- hyper_waker_wake(data->hyp.send_body_waker);
- data->hyp.send_body_waker = NULL;
- }
- return CURLE_OK;
-}
-
-/* Hyper client reader, handling unpausing */
-static const struct Curl_crtype cr_hyper_protocol = {
- "cr-hyper",
- Curl_creader_def_init,
- Curl_creader_def_read,
- Curl_creader_def_close,
- Curl_creader_def_needs_rewind,
- Curl_creader_def_total_length,
- Curl_creader_def_resume_from,
- Curl_creader_def_rewind,
- cr_hyper_unpause,
- Curl_creader_def_is_paused,
- Curl_creader_def_done,
- sizeof(struct Curl_creader)
-};
-
-static CURLcode cr_hyper_add(struct Curl_easy *data)
-{
- struct Curl_creader *reader = NULL;
- CURLcode result;
-
- result = Curl_creader_create(&reader, data, &cr_hyper_protocol,
- CURL_CR_PROTOCOL);
- if(!result)
- result = Curl_creader_add(data, reader);
-
- if(result && reader)
- Curl_creader_free(data, reader);
- return result;
-}
-
-#endif /* !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) */
diff --git a/libs/libcurl/src/c-hyper.h b/libs/libcurl/src/c-hyper.h
deleted file mode 100644
index 422e6e4935..0000000000
--- a/libs/libcurl/src/c-hyper.h
+++ /dev/null
@@ -1,63 +0,0 @@
-#ifndef HEADER_CURL_HYPER_H
-#define HEADER_CURL_HYPER_H
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.haxx.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-#include "curl_setup.h"
-
-#if !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER)
-
-#include <hyper.h>
-
-struct hyp_io_ctx {
- struct Curl_easy *data;
- int sockindex;
-};
-
-/* per-transfer data for the Hyper backend */
-struct hyptransfer {
- hyper_waker *write_waker;
- hyper_waker *read_waker;
- const hyper_executor *exec;
- hyper_waker *send_body_waker;
- struct hyp_io_ctx io_ctx;
-};
-
-size_t Curl_hyper_recv(void *userp, hyper_context *ctx,
- uint8_t *buf, size_t buflen);
-size_t Curl_hyper_send(void *userp, hyper_context *ctx,
- const uint8_t *buf, size_t buflen);
-CURLcode Curl_hyper_stream(struct Curl_easy *data,
- struct connectdata *conn,
- int *didwhat,
- int select_res);
-
-CURLcode Curl_hyper_header(struct Curl_easy *data, hyper_headers *headers,
- const char *line);
-void Curl_hyper_done(struct Curl_easy *);
-
-#else
-#define Curl_hyper_done(x)
-
-#endif /* !defined(CURL_DISABLE_HTTP) && defined(USE_HYPER) */
-#endif /* HEADER_CURL_HYPER_H */
diff --git a/libs/libcurl/src/cf-h1-proxy.c b/libs/libcurl/src/cf-h1-proxy.c
index 7dfe499e1e..9a24245c6f 100644
--- a/libs/libcurl/src/cf-h1-proxy.c
+++ b/libs/libcurl/src/cf-h1-proxy.c
@@ -27,11 +27,8 @@
#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
#include <curl/curl.h>
-#ifdef USE_HYPER
-#include <hyper.h>
-#endif
#include "urldata.h"
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
#include "sendf.h"
#include "http.h"
#include "http1.h"
@@ -43,10 +40,11 @@
#include "cf-h1-proxy.h"
#include "connect.h"
#include "curl_trc.h"
-#include "curlx.h"
+#include "strcase.h"
#include "vtls/vtls.h"
#include "transfer.h"
#include "multiif.h"
+#include "curlx/strparse.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -99,8 +97,8 @@ static CURLcode tunnel_reinit(struct Curl_cfilter *cf,
(void)data;
(void)cf;
DEBUGASSERT(ts);
- Curl_dyn_reset(&ts->rcvbuf);
- Curl_dyn_reset(&ts->request_data);
+ curlx_dyn_reset(&ts->rcvbuf);
+ curlx_dyn_reset(&ts->request_data);
ts->tunnel_state = H1_TUNNEL_INIT;
ts->keepon = KEEPON_CONNECT;
ts->cl = 0;
@@ -125,8 +123,8 @@ static CURLcode tunnel_init(struct Curl_cfilter *cf,
infof(data, "allocate connect buffer");
- Curl_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS);
- Curl_dyn_init(&ts->request_data, DYN_HTTP_REQUEST);
+ curlx_dyn_init(&ts->rcvbuf, DYN_PROXY_CONNECT_HEADERS);
+ curlx_dyn_init(&ts->request_data, DYN_HTTP_REQUEST);
Curl_httpchunk_init(data, &ts->ch, TRUE);
*pts = ts;
@@ -152,7 +150,7 @@ static void h1_tunnel_go_state(struct Curl_cfilter *cf,
CURL_TRC_CF(data, cf, "new tunnel state 'connect'");
ts->tunnel_state = H1_TUNNEL_CONNECT;
ts->keepon = KEEPON_CONNECT;
- Curl_dyn_reset(&ts->rcvbuf);
+ curlx_dyn_reset(&ts->rcvbuf);
break;
case H1_TUNNEL_RECEIVE:
@@ -175,8 +173,8 @@ static void h1_tunnel_go_state(struct Curl_cfilter *cf,
if(new_state == H1_TUNNEL_FAILED)
CURL_TRC_CF(data, cf, "new tunnel state 'failed'");
ts->tunnel_state = new_state;
- Curl_dyn_reset(&ts->rcvbuf);
- Curl_dyn_reset(&ts->request_data);
+ curlx_dyn_reset(&ts->rcvbuf);
+ curlx_dyn_reset(&ts->request_data);
/* restore the protocol pointer */
data->info.httpcode = 0; /* clear it as it might've been used for the
proxy */
@@ -184,9 +182,6 @@ static void h1_tunnel_go_state(struct Curl_cfilter *cf,
make sure that it is not accidentally used for the document request
after we have connected. So let's free and clear it here. */
Curl_safefree(data->state.aptr.proxyuserpwd);
-#ifdef USE_HYPER
- data->state.hconnect = FALSE;
-#endif
break;
}
}
@@ -198,8 +193,8 @@ static void tunnel_free(struct Curl_cfilter *cf,
struct h1_tunnel_state *ts = cf->ctx;
if(ts) {
h1_tunnel_go_state(cf, ts, H1_TUNNEL_FAILED, data);
- Curl_dyn_free(&ts->rcvbuf);
- Curl_dyn_free(&ts->request_data);
+ curlx_dyn_free(&ts->rcvbuf);
+ curlx_dyn_free(&ts->request_data);
Curl_httpchunk_free(data, &ts->ch);
free(ts);
cf->ctx = NULL;
@@ -209,10 +204,9 @@ static void tunnel_free(struct Curl_cfilter *cf,
static bool tunnel_want_send(struct h1_tunnel_state *ts)
{
- return (ts->tunnel_state == H1_TUNNEL_CONNECT);
+ return ts->tunnel_state == H1_TUNNEL_CONNECT;
}
-#ifndef USE_HYPER
static CURLcode start_CONNECT(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct h1_tunnel_state *ts)
@@ -232,7 +226,7 @@ static CURLcode start_CONNECT(struct Curl_cfilter *cf,
infof(data, "Establish HTTP proxy tunnel to %s", req->authority);
- Curl_dyn_reset(&ts->request_data);
+ curlx_dyn_reset(&ts->request_data);
ts->nsent = 0;
ts->headerlines = 0;
http_minor = (cf->conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ? 0 : 1;
@@ -254,8 +248,8 @@ static CURLcode send_CONNECT(struct Curl_cfilter *cf,
struct h1_tunnel_state *ts,
bool *done)
{
- char *buf = Curl_dyn_ptr(&ts->request_data);
- size_t request_len = Curl_dyn_len(&ts->request_data);
+ char *buf = curlx_dyn_ptr(&ts->request_data);
+ size_t request_len = curlx_dyn_len(&ts->request_data);
size_t blen = request_len;
CURLcode result = CURLE_OK;
ssize_t nwritten;
@@ -321,8 +315,11 @@ static CURLcode on_resp_header(struct Curl_cfilter *cf,
k->httpcode);
}
else {
- (void)curlx_strtoofft(header + strlen("Content-Length:"),
- NULL, 10, &ts->cl);
+ const char *p = header + strlen("Content-Length:");
+ if(curlx_str_numblanks(&p, &ts->cl)) {
+ failf(data, "Unsupported Content-Length value");
+ return CURLE_WEIRD_SERVER_REPLY;
+ }
}
}
else if(Curl_compareheader(header,
@@ -447,7 +444,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
continue;
}
- if(Curl_dyn_addn(&ts->rcvbuf, &byte, 1)) {
+ if(curlx_dyn_addn(&ts->rcvbuf, &byte, 1)) {
failf(data, "CONNECT response too large");
return CURLE_RECV_ERROR;
}
@@ -457,8 +454,8 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
continue;
ts->headerlines++;
- linep = Curl_dyn_ptr(&ts->rcvbuf);
- line_len = Curl_dyn_len(&ts->rcvbuf); /* amount of bytes in this line */
+ linep = curlx_dyn_ptr(&ts->rcvbuf);
+ line_len = curlx_dyn_len(&ts->rcvbuf); /* amount of bytes in this line */
/* output debug if that is requested */
Curl_debug(data, CURLINFO_HEADER_IN, linep, line_len);
@@ -515,7 +512,7 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
if(result)
return result;
- Curl_dyn_reset(&ts->rcvbuf);
+ curlx_dyn_reset(&ts->rcvbuf);
} /* while there is buffer left and loop is requested */
if(error)
@@ -529,337 +526,6 @@ static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
return result;
}
-#else /* USE_HYPER */
-
-static CURLcode CONNECT_host(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- char **pauthority,
- char **phost_header)
-{
- const char *hostname;
- int port;
- bool ipv6_ip;
- CURLcode result;
- char *authority; /* for CONNECT, the destination host + port */
- char *host_header = NULL; /* Host: authority */
-
- result = Curl_http_proxy_get_destination(cf, &hostname, &port, &ipv6_ip);
- if(result)
- return result;
-
- authority = aprintf("%s%s%s:%d", ipv6_ip ? "[":"", hostname,
- ipv6_ip ? "]" : "", port);
- if(!authority)
- return CURLE_OUT_OF_MEMORY;
-
- /* If user is not overriding the Host header later */
- if(!Curl_checkProxyheaders(data, cf->conn, STRCONST("Host"))) {
- host_header = aprintf("Host: %s\r\n", authority);
- if(!host_header) {
- free(authority);
- return CURLE_OUT_OF_MEMORY;
- }
- }
- *pauthority = authority;
- *phost_header = host_header;
- return CURLE_OK;
-}
-
-/* The Hyper version of CONNECT */
-static CURLcode start_CONNECT(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct h1_tunnel_state *ts)
-{
- struct connectdata *conn = cf->conn;
- struct hyptransfer *h = &data->hyp;
- curl_socket_t tunnelsocket = Curl_conn_cf_get_socket(cf, data);
- hyper_io *io = NULL;
- hyper_request *req = NULL;
- hyper_headers *headers = NULL;
- hyper_clientconn_options *options = NULL;
- hyper_task *handshake = NULL;
- hyper_task *task = NULL; /* for the handshake */
- hyper_clientconn *client = NULL;
- hyper_task *sendtask = NULL; /* for the send */
- char *authority = NULL; /* for CONNECT */
- char *host_header = NULL; /* Host: */
- CURLcode result = CURLE_OUT_OF_MEMORY;
- (void)ts;
-
- io = hyper_io_new();
- if(!io) {
- failf(data, "Couldn't create hyper IO");
- result = CURLE_OUT_OF_MEMORY;
- goto error;
- }
- /* tell Hyper how to read/write network data */
- h->io_ctx.data = data;
- h->io_ctx.sockindex = cf->sockindex;
- hyper_io_set_userdata(io, &h->io_ctx);
- hyper_io_set_read(io, Curl_hyper_recv);
- hyper_io_set_write(io, Curl_hyper_send);
- conn->sockfd = tunnelsocket;
-
- data->state.hconnect = TRUE;
-
- /* create an executor to poll futures */
- if(!h->exec) {
- h->exec = hyper_executor_new();
- if(!h->exec) {
- failf(data, "Couldn't create hyper executor");
- result = CURLE_OUT_OF_MEMORY;
- goto error;
- }
- }
-
- options = hyper_clientconn_options_new();
- if(!options) {
- failf(data, "Couldn't create hyper client options");
- result = CURLE_OUT_OF_MEMORY;
- goto error;
- }
- hyper_clientconn_options_set_preserve_header_case(options, 1);
- hyper_clientconn_options_set_preserve_header_order(options, 1);
-
- hyper_clientconn_options_exec(options, h->exec);
-
- /* "Both the `io` and the `options` are consumed in this function
- call" */
- handshake = hyper_clientconn_handshake(io, options);
- if(!handshake) {
- failf(data, "Couldn't create hyper client handshake");
- result = CURLE_OUT_OF_MEMORY;
- goto error;
- }
- io = NULL;
- options = NULL;
-
- if(HYPERE_OK != hyper_executor_push(h->exec, handshake)) {
- failf(data, "Couldn't hyper_executor_push the handshake");
- result = CURLE_OUT_OF_MEMORY;
- goto error;
- }
- handshake = NULL; /* ownership passed on */
-
- task = hyper_executor_poll(h->exec);
- if(!task) {
- failf(data, "Couldn't hyper_executor_poll the handshake");
- result = CURLE_OUT_OF_MEMORY;
- goto error;
- }
-
- client = hyper_task_value(task);
- hyper_task_free(task);
-
- req = hyper_request_new();
- if(!req) {
- failf(data, "Couldn't hyper_request_new");
- result = CURLE_OUT_OF_MEMORY;
- goto error;
- }
- if(hyper_request_set_method(req, (uint8_t *)"CONNECT",
- strlen("CONNECT"))) {
- failf(data, "error setting method");
- result = CURLE_OUT_OF_MEMORY;
- goto error;
- }
-
- /* This only happens if we have looped here due to authentication
- reasons, and we do not really use the newly cloned URL here
- then. Just free() it. */
- Curl_safefree(data->req.newurl);
-
- result = CONNECT_host(cf, data, &authority, &host_header);
- if(result)
- goto error;
-
- infof(data, "Establish HTTP proxy tunnel to %s", authority);
-
- if(hyper_request_set_uri(req, (uint8_t *)authority,
- strlen(authority))) {
- failf(data, "error setting path");
- result = CURLE_OUT_OF_MEMORY;
- goto error;
- }
- if(data->set.verbose) {
- char *se = aprintf("CONNECT %s HTTP/1.1\r\n", authority);
- if(!se) {
- result = CURLE_OUT_OF_MEMORY;
- goto error;
- }
- Curl_debug(data, CURLINFO_HEADER_OUT, se, strlen(se));
- free(se);
- }
- /* Setup the proxy-authorization header, if any */
- result = Curl_http_output_auth(data, conn, "CONNECT", HTTPREQ_GET,
- authority, TRUE);
- if(result)
- goto error;
- Curl_safefree(authority);
-
- /* default is 1.1 */
- if((conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) &&
- (HYPERE_OK != hyper_request_set_version(req,
- HYPER_HTTP_VERSION_1_0))) {
- failf(data, "error setting HTTP version");
- result = CURLE_OUT_OF_MEMORY;
- goto error;
- }
-
- headers = hyper_request_headers(req);
- if(!headers) {
- failf(data, "hyper_request_headers");
- result = CURLE_OUT_OF_MEMORY;
- goto error;
- }
- if(host_header) {
- result = Curl_hyper_header(data, headers, host_header);
- if(result)
- goto error;
- Curl_safefree(host_header);
- }
-
- if(data->state.aptr.proxyuserpwd) {
- result = Curl_hyper_header(data, headers,
- data->state.aptr.proxyuserpwd);
- if(result)
- goto error;
- }
-
- if(!Curl_checkProxyheaders(data, conn, STRCONST("User-Agent")) &&
- data->set.str[STRING_USERAGENT] && *data->set.str[STRING_USERAGENT]) {
- struct dynbuf ua;
- Curl_dyn_init(&ua, DYN_HTTP_REQUEST);
- result = Curl_dyn_addf(&ua, "User-Agent: %s\r\n",
- data->set.str[STRING_USERAGENT]);
- if(result)
- goto error;
- result = Curl_hyper_header(data, headers, Curl_dyn_ptr(&ua));
- if(result)
- goto error;
- Curl_dyn_free(&ua);
- }
-
- if(!Curl_checkProxyheaders(data, conn, STRCONST("Proxy-Connection"))) {
- result = Curl_hyper_header(data, headers,
- "Proxy-Connection: Keep-Alive");
- if(result)
- goto error;
- }
-
- result = Curl_add_custom_headers(data, TRUE, headers);
- if(result)
- goto error;
-
- result = Curl_creader_set_null(data);
- if(result)
- goto error;
-
- sendtask = hyper_clientconn_send(client, req);
- if(!sendtask) {
- failf(data, "hyper_clientconn_send");
- result = CURLE_OUT_OF_MEMORY;
- goto error;
- }
- req = NULL;
-
- if(HYPERE_OK != hyper_executor_push(h->exec, sendtask)) {
- failf(data, "Couldn't hyper_executor_push the send");
- result = CURLE_OUT_OF_MEMORY;
- goto error;
- }
- sendtask = NULL; /* ownership passed on */
-
- hyper_clientconn_free(client);
- client = NULL;
-
-error:
- free(host_header);
- free(authority);
- if(io)
- hyper_io_free(io);
- if(options)
- hyper_clientconn_options_free(options);
- if(handshake)
- hyper_task_free(handshake);
- if(client)
- hyper_clientconn_free(client);
- if(req)
- hyper_request_free(req);
-
- return result;
-}
-
-static CURLcode send_CONNECT(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct h1_tunnel_state *ts,
- bool *done)
-{
- struct hyptransfer *h = &data->hyp;
- struct connectdata *conn = cf->conn;
- hyper_task *task = NULL;
- hyper_error *hypererr = NULL;
- CURLcode result = CURLE_OK;
-
- (void)ts;
- (void)conn;
- do {
- task = hyper_executor_poll(h->exec);
- if(task) {
- bool error = hyper_task_type(task) == HYPER_TASK_ERROR;
- if(error)
- hypererr = hyper_task_value(task);
- hyper_task_free(task);
- if(error) {
- /* this could probably use a better error code? */
- result = CURLE_OUT_OF_MEMORY;
- goto error;
- }
- }
- } while(task);
-error:
- *done = (result == CURLE_OK);
- if(hypererr) {
- uint8_t errbuf[256];
- size_t errlen = hyper_error_print(hypererr, errbuf, sizeof(errbuf));
- failf(data, "Hyper: %.*s", (int)errlen, errbuf);
- hyper_error_free(hypererr);
- }
- return result;
-}
-
-static CURLcode recv_CONNECT_resp(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct h1_tunnel_state *ts,
- bool *done)
-{
- struct hyptransfer *h = &data->hyp;
- CURLcode result;
- int didwhat;
-
- (void)ts;
- result = Curl_hyper_stream(data, cf->conn, &didwhat,
- CURL_CSELECT_IN | CURL_CSELECT_OUT);
- *done = data->req.done;
- if(result || !*done)
- return result;
- if(h->exec) {
- hyper_executor_free(h->exec);
- h->exec = NULL;
- }
- if(h->read_waker) {
- hyper_waker_free(h->read_waker);
- h->read_waker = NULL;
- }
- if(h->write_waker) {
- hyper_waker_free(h->write_waker);
- h->write_waker = NULL;
- }
- return result;
-}
-
-#endif /* USE_HYPER */
-
static CURLcode H1_CONNECT(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct h1_tunnel_state *ts)
@@ -935,7 +601,7 @@ static CURLcode H1_CONNECT(struct Curl_cfilter *cf,
infof(data, "Connect me again please");
Curl_conn_cf_close(cf, data);
connkeep(conn, "HTTP proxy CONNECT");
- result = Curl_conn_cf_connect(cf->next, data, FALSE, &done);
+ result = Curl_conn_cf_connect(cf->next, data, &done);
goto out;
}
else {
@@ -975,7 +641,7 @@ out:
static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool blocking, bool *done)
+ bool *done)
{
CURLcode result;
struct h1_tunnel_state *ts = cf->ctx;
@@ -986,7 +652,7 @@ static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf,
}
CURL_TRC_CF(data, cf, "connect");
- result = cf->next->cft->do_connect(cf->next, data, blocking, done);
+ result = cf->next->cft->do_connect(cf->next, data, done);
if(result || !*done)
return result;
@@ -998,7 +664,6 @@ static CURLcode cf_h1_proxy_connect(struct Curl_cfilter *cf,
cf->ctx = ts;
}
- /* TODO: can we do blocking? */
/* We want "seamless" operations through HTTP proxy tunnel */
result = H1_CONNECT(cf, data, ts);
diff --git a/libs/libcurl/src/cf-h2-proxy.c b/libs/libcurl/src/cf-h2-proxy.c
index e978b83804..fe257c6457 100644
--- a/libs/libcurl/src/cf-h2-proxy.c
+++ b/libs/libcurl/src/cf-h2-proxy.c
@@ -32,7 +32,7 @@
#include "connect.h"
#include "curl_trc.h"
#include "bufq.h"
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
#include "dynhds.h"
#include "http1.h"
#include "http2.h"
@@ -277,6 +277,8 @@ static int proxy_h2_client_new(struct Curl_cfilter *cf,
{
struct cf_h2_proxy_ctx *ctx = cf->ctx;
nghttp2_option *o;
+ nghttp2_mem mem = {NULL, Curl_nghttp2_malloc, Curl_nghttp2_free,
+ Curl_nghttp2_calloc, Curl_nghttp2_realloc};
int rc = nghttp2_option_new(&o);
if(rc)
@@ -289,7 +291,7 @@ static int proxy_h2_client_new(struct Curl_cfilter *cf,
HTTP field value. */
nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1);
#endif
- rc = nghttp2_session_client_new2(&ctx->h2, cbs, cf, o);
+ rc = nghttp2_session_client_new3(&ctx->h2, cbs, cf, o, &mem);
nghttp2_option_del(o);
return rc;
}
@@ -617,7 +619,7 @@ static int proxy_h2_fr_print(const nghttp2_frame *frame,
frame->hd.flags & NGHTTP2_FLAG_ACK);
case NGHTTP2_GOAWAY: {
char scratch[128];
- size_t s_len = sizeof(scratch)/sizeof(scratch[0]);
+ size_t s_len = CURL_ARRAYSIZE(scratch);
size_t len = (frame->goaway.opaque_data_len < s_len) ?
frame->goaway.opaque_data_len : s_len-1;
if(len)
@@ -863,7 +865,9 @@ static int tunnel_recv_callback(nghttp2_session *session, uint8_t flags,
if(nwritten < 0) {
if(result != CURLE_AGAIN)
return NGHTTP2_ERR_CALLBACK_FAILURE;
+#ifdef DEBUGBUILD
nwritten = 0;
+#endif
}
DEBUGASSERT((size_t)nwritten == len);
return 0;
@@ -1086,7 +1090,7 @@ out:
static CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool blocking, bool *done)
+ bool *done)
{
struct cf_h2_proxy_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
@@ -1101,7 +1105,7 @@ static CURLcode cf_h2_proxy_connect(struct Curl_cfilter *cf,
/* Connect the lower filters first */
if(!cf->next->connected) {
- result = Curl_conn_cf_connect(cf->next, data, blocking, done);
+ result = Curl_conn_cf_connect(cf->next, data, done);
if(result || !*done)
return result;
}
@@ -1404,7 +1408,7 @@ static ssize_t cf_h2_proxy_send(struct Curl_cfilter *cf,
ssize_t nwritten;
CURLcode result;
- (void)eos; /* TODO, maybe useful for blocks? */
+ (void)eos;
if(ctx->tunnel.state != H2_TUNNEL_ESTABLISHED) {
*err = CURLE_SEND_ERROR;
return -1;
diff --git a/libs/libcurl/src/cf-haproxy.c b/libs/libcurl/src/cf-haproxy.c
index 375105e297..05855a828e 100644
--- a/libs/libcurl/src/cf-haproxy.c
+++ b/libs/libcurl/src/cf-haproxy.c
@@ -54,13 +54,13 @@ static void cf_haproxy_ctx_reset(struct cf_haproxy_ctx *ctx)
{
DEBUGASSERT(ctx);
ctx->state = HAPROXY_INIT;
- Curl_dyn_reset(&ctx->data_out);
+ curlx_dyn_reset(&ctx->data_out);
}
static void cf_haproxy_ctx_free(struct cf_haproxy_ctx *ctx)
{
if(ctx) {
- Curl_dyn_free(&ctx->data_out);
+ curlx_dyn_free(&ctx->data_out);
free(ctx);
}
}
@@ -79,7 +79,7 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf,
#ifdef USE_UNIX_SOCKETS
if(cf->conn->unix_domain_socket)
/* the buffer is large enough to hold this! */
- result = Curl_dyn_addn(&ctx->data_out, STRCONST("PROXY UNKNOWN\r\n"));
+ result = curlx_dyn_addn(&ctx->data_out, STRCONST("PROXY UNKNOWN\r\n"));
else {
#endif /* USE_UNIX_SOCKETS */
result = Curl_conn_cf_get_ip_info(cf->next, data, &is_ipv6, &ipquad);
@@ -92,10 +92,10 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf,
else
client_ip = ipquad.local_ip;
- result = Curl_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n",
- is_ipv6 ? "TCP6" : "TCP4",
- client_ip, ipquad.remote_ip,
- ipquad.local_port, ipquad.remote_port);
+ result = curlx_dyn_addf(&ctx->data_out, "PROXY %s %s %s %i %i\r\n",
+ is_ipv6 ? "TCP6" : "TCP4",
+ client_ip, ipquad.remote_ip,
+ ipquad.local_port, ipquad.remote_port);
#ifdef USE_UNIX_SOCKETS
}
@@ -105,7 +105,7 @@ static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf,
static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool blocking, bool *done)
+ bool *done)
{
struct cf_haproxy_ctx *ctx = cf->ctx;
CURLcode result;
@@ -117,7 +117,7 @@ static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
return CURLE_OK;
}
- result = cf->next->cft->do_connect(cf->next, data, blocking, done);
+ result = cf->next->cft->do_connect(cf->next, data, done);
if(result || !*done)
return result;
@@ -129,11 +129,11 @@ static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
ctx->state = HAPROXY_SEND;
FALLTHROUGH();
case HAPROXY_SEND:
- len = Curl_dyn_len(&ctx->data_out);
+ len = curlx_dyn_len(&ctx->data_out);
if(len > 0) {
ssize_t nwritten;
nwritten = Curl_conn_cf_send(cf->next, data,
- Curl_dyn_ptr(&ctx->data_out), len, FALSE,
+ curlx_dyn_ptr(&ctx->data_out), len, FALSE,
&result);
if(nwritten < 0) {
if(result != CURLE_AGAIN)
@@ -141,8 +141,8 @@ static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
result = CURLE_OK;
nwritten = 0;
}
- Curl_dyn_tail(&ctx->data_out, len - (size_t)nwritten);
- if(Curl_dyn_len(&ctx->data_out) > 0) {
+ curlx_dyn_tail(&ctx->data_out, len - (size_t)nwritten);
+ if(curlx_dyn_len(&ctx->data_out) > 0) {
result = CURLE_OK;
goto out;
}
@@ -150,7 +150,7 @@ static CURLcode cf_haproxy_connect(struct Curl_cfilter *cf,
ctx->state = HAPROXY_DONE;
FALLTHROUGH();
default:
- Curl_dyn_free(&ctx->data_out);
+ curlx_dyn_free(&ctx->data_out);
break;
}
@@ -222,7 +222,7 @@ static CURLcode cf_haproxy_create(struct Curl_cfilter **pcf,
goto out;
}
ctx->state = HAPROXY_INIT;
- Curl_dyn_init(&ctx->data_out, DYN_HAXPROXY);
+ curlx_dyn_init(&ctx->data_out, DYN_HAXPROXY);
result = Curl_cf_create(&cf, &Curl_cft_haproxy, ctx);
if(result)
diff --git a/libs/libcurl/src/cf-https-connect.c b/libs/libcurl/src/cf-https-connect.c
index 3d1cdca111..49b3332cac 100644
--- a/libs/libcurl/src/cf-https-connect.c
+++ b/libs/libcurl/src/cf-https-connect.c
@@ -24,13 +24,14 @@
#include "curl_setup.h"
-#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER)
+#if !defined(CURL_DISABLE_HTTP)
#include "urldata.h"
#include <curl/curl.h>
#include "curl_trc.h"
#include "cfilters.h"
#include "connect.h"
+#include "hostip.h"
#include "multiif.h"
#include "cf-https-connect.h"
#include "http2.h"
@@ -41,7 +42,6 @@
#include "curl_memory.h"
#include "memdebug.h"
-
typedef enum {
CF_HC_INIT,
CF_HC_CONNECT,
@@ -55,7 +55,7 @@ struct cf_hc_baller {
CURLcode result;
struct curltime started;
int reply_ms;
- BIT(enabled);
+ enum alpnid alpn_id;
BIT(shutdown);
};
@@ -73,7 +73,7 @@ static void cf_hc_baller_reset(struct cf_hc_baller *b,
static bool cf_hc_baller_is_active(struct cf_hc_baller *b)
{
- return b->enabled && b->cf && !b->result;
+ return b->cf && !b->result;
}
static bool cf_hc_baller_has_started(struct cf_hc_baller *b)
@@ -84,7 +84,7 @@ static bool cf_hc_baller_has_started(struct cf_hc_baller *b)
static int cf_hc_baller_reply_ms(struct cf_hc_baller *b,
struct Curl_easy *data)
{
- if(b->reply_ms < 0)
+ if(b->cf && (b->reply_ms < 0))
b->cf->cft->query(b->cf, data, CF_QUERY_CONNECT_REPLY_MS,
&b->reply_ms, NULL);
return b->reply_ms;
@@ -113,29 +113,54 @@ static CURLcode cf_hc_baller_cntrl(struct cf_hc_baller *b,
struct cf_hc_ctx {
cf_hc_state state;
- const struct Curl_dns_entry *remotehost;
struct curltime started; /* when connect started */
CURLcode result; /* overall result */
- struct cf_hc_baller h3_baller;
- struct cf_hc_baller h21_baller;
+ struct cf_hc_baller ballers[2];
+ size_t baller_count;
unsigned int soft_eyeballs_timeout_ms;
unsigned int hard_eyeballs_timeout_ms;
};
+static void cf_hc_baller_assign(struct cf_hc_baller *b,
+ enum alpnid alpn_id)
+{
+ b->alpn_id = alpn_id;
+ switch(b->alpn_id) {
+ case ALPN_h3:
+ b->name = "h3";
+ break;
+ case ALPN_h2:
+ b->name = "h2";
+ break;
+ case ALPN_h1:
+ b->name = "h1";
+ break;
+ default:
+ b->result = CURLE_FAILED_INIT;
+ break;
+ }
+}
+
static void cf_hc_baller_init(struct cf_hc_baller *b,
struct Curl_cfilter *cf,
struct Curl_easy *data,
- const char *name,
int transport)
{
- struct cf_hc_ctx *ctx = cf->ctx;
struct Curl_cfilter *save = cf->next;
- b->name = name;
cf->next = NULL;
- b->started = Curl_now();
- b->result = Curl_cf_setup_insert_after(cf, data, ctx->remotehost,
- transport, CURL_CF_SSL_ENABLE);
+ b->started = curlx_now();
+ switch(b->alpn_id) {
+ case ALPN_h3:
+ transport = TRNSPRT_QUIC;
+ break;
+ default:
+ break;
+ }
+
+ if(!b->result)
+ b->result = Curl_cf_setup_insert_after(cf, data, transport,
+ CURL_CF_SSL_ENABLE);
b->cf = cf->next;
cf->next = save;
}
@@ -148,7 +173,7 @@ static CURLcode cf_hc_baller_connect(struct cf_hc_baller *b,
struct Curl_cfilter *save = cf->next;
cf->next = b->cf;
- b->result = Curl_conn_cf_connect(cf->next, data, FALSE, done);
+ b->result = Curl_conn_cf_connect(cf->next, data, done);
b->cf = cf->next; /* it might mutate */
cf->next = save;
return b->result;
@@ -157,14 +182,15 @@ static CURLcode cf_hc_baller_connect(struct cf_hc_baller *b,
static void cf_hc_reset(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct cf_hc_ctx *ctx = cf->ctx;
+ size_t i;
if(ctx) {
- cf_hc_baller_reset(&ctx->h3_baller, data);
- cf_hc_baller_reset(&ctx->h21_baller, data);
+ for(i = 0; i < ctx->baller_count; ++i)
+ cf_hc_baller_reset(&ctx->ballers[i], data);
ctx->state = CF_HC_INIT;
ctx->result = CURLE_OK;
ctx->hard_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout;
- ctx->soft_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout / 2;
+ ctx->soft_eyeballs_timeout_ms = data->set.happy_eyeballs_timeout / 4;
}
}
@@ -175,21 +201,22 @@ static CURLcode baller_connected(struct Curl_cfilter *cf,
struct cf_hc_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
int reply_ms;
+ size_t i;
DEBUGASSERT(winner->cf);
- if(winner != &ctx->h3_baller)
- cf_hc_baller_reset(&ctx->h3_baller, data);
- if(winner != &ctx->h21_baller)
- cf_hc_baller_reset(&ctx->h21_baller, data);
+ for(i = 0; i < ctx->baller_count; ++i)
+ if(winner != &ctx->ballers[i])
+ cf_hc_baller_reset(&ctx->ballers[i], data);
reply_ms = cf_hc_baller_reply_ms(winner, data);
if(reply_ms >= 0)
CURL_TRC_CF(data, cf, "connect+handshake %s: %dms, 1st data: %dms",
- winner->name, (int)Curl_timediff(Curl_now(), winner->started),
- reply_ms);
+ winner->name, (int)curlx_timediff(curlx_now(),
+ winner->started), reply_ms);
else
CURL_TRC_CF(data, cf, "deferred handshake %s: %dms",
- winner->name, (int)Curl_timediff(Curl_now(), winner->started));
+ winner->name, (int)curlx_timediff(curlx_now(),
+ winner->started));
cf->next = winner->cf;
winner->cf = NULL;
@@ -218,31 +245,40 @@ static CURLcode baller_connected(struct Curl_cfilter *cf,
}
-static bool time_to_start_h21(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct curltime now)
+static bool time_to_start_next(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ size_t idx, struct curltime now)
{
struct cf_hc_ctx *ctx = cf->ctx;
timediff_t elapsed_ms;
+ size_t i;
- if(!ctx->h21_baller.enabled || cf_hc_baller_has_started(&ctx->h21_baller))
+ if(idx >= ctx->baller_count)
return FALSE;
-
- if(!ctx->h3_baller.enabled || !cf_hc_baller_is_active(&ctx->h3_baller))
+ if(cf_hc_baller_has_started(&ctx->ballers[idx]))
+ return FALSE;
+ for(i = 0; i < idx; i++) {
+ if(!ctx->ballers[i].result)
+ break;
+ }
+ if(i == idx) {
+ CURL_TRC_CF(data, cf, "all previous attempts failed, starting %s",
+ ctx->ballers[idx].name);
return TRUE;
-
- elapsed_ms = Curl_timediff(now, ctx->started);
+ }
+ elapsed_ms = curlx_timediff(now, ctx->started);
if(elapsed_ms >= ctx->hard_eyeballs_timeout_ms) {
- CURL_TRC_CF(data, cf, "hard timeout of %dms reached, starting h21",
- ctx->hard_eyeballs_timeout_ms);
+ CURL_TRC_CF(data, cf, "hard timeout of %dms reached, starting %s",
+ ctx->hard_eyeballs_timeout_ms, ctx->ballers[idx].name);
return TRUE;
}
- if(elapsed_ms >= ctx->soft_eyeballs_timeout_ms) {
- if(cf_hc_baller_reply_ms(&ctx->h3_baller, data) < 0) {
- CURL_TRC_CF(data, cf, "soft timeout of %dms reached, h3 has not "
- "seen any data, starting h21",
- ctx->soft_eyeballs_timeout_ms);
+ if((idx > 0) && (elapsed_ms >= ctx->soft_eyeballs_timeout_ms)) {
+ if(cf_hc_baller_reply_ms(&ctx->ballers[idx - 1], data) < 0) {
+ CURL_TRC_CF(data, cf, "soft timeout of %dms reached, %s has not "
+ "seen any data, starting %s",
+ ctx->soft_eyeballs_timeout_ms,
+ ctx->ballers[idx - 1].name, ctx->ballers[idx].name);
return TRUE;
}
/* set the effective hard timeout again */
@@ -254,67 +290,73 @@ static bool time_to_start_h21(struct Curl_cfilter *cf,
static CURLcode cf_hc_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool blocking, bool *done)
+ bool *done)
{
struct cf_hc_ctx *ctx = cf->ctx;
struct curltime now;
CURLcode result = CURLE_OK;
+ size_t i, failed_ballers;
- (void)blocking;
if(cf->connected) {
*done = TRUE;
return CURLE_OK;
}
*done = FALSE;
- now = Curl_now();
+ now = curlx_now();
switch(ctx->state) {
case CF_HC_INIT:
- DEBUGASSERT(!ctx->h3_baller.cf);
- DEBUGASSERT(!ctx->h21_baller.cf);
DEBUGASSERT(!cf->next);
+ for(i = 0; i < ctx->baller_count; i++)
+ DEBUGASSERT(!ctx->ballers[i].cf);
CURL_TRC_CF(data, cf, "connect, init");
ctx->started = now;
- if(ctx->h3_baller.enabled) {
- cf_hc_baller_init(&ctx->h3_baller, cf, data, "h3", TRNSPRT_QUIC);
- if(ctx->h21_baller.enabled)
- Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS);
+ cf_hc_baller_init(&ctx->ballers[0], cf, data, cf->conn->transport);
+ if(ctx->baller_count > 1) {
+ Curl_expire(data, ctx->soft_eyeballs_timeout_ms, EXPIRE_ALPN_EYEBALLS);
+ CURL_TRC_CF(data, cf, "set next attempt to start in %ums",
+ ctx->soft_eyeballs_timeout_ms);
}
- else if(ctx->h21_baller.enabled)
- cf_hc_baller_init(&ctx->h21_baller, cf, data, "h21",
- cf->conn->transport);
ctx->state = CF_HC_CONNECT;
FALLTHROUGH();
case CF_HC_CONNECT:
- if(cf_hc_baller_is_active(&ctx->h3_baller)) {
- result = cf_hc_baller_connect(&ctx->h3_baller, cf, data, done);
+ if(cf_hc_baller_is_active(&ctx->ballers[0])) {
+ result = cf_hc_baller_connect(&ctx->ballers[0], cf, data, done);
if(!result && *done) {
- result = baller_connected(cf, data, &ctx->h3_baller);
+ result = baller_connected(cf, data, &ctx->ballers[0]);
goto out;
}
}
- if(time_to_start_h21(cf, data, now)) {
- cf_hc_baller_init(&ctx->h21_baller, cf, data, "h21",
- cf->conn->transport);
+ if(time_to_start_next(cf, data, 1, now)) {
+ cf_hc_baller_init(&ctx->ballers[1], cf, data, cf->conn->transport);
}
- if(cf_hc_baller_is_active(&ctx->h21_baller)) {
- CURL_TRC_CF(data, cf, "connect, check h21");
- result = cf_hc_baller_connect(&ctx->h21_baller, cf, data, done);
+ if((ctx->baller_count > 1) && cf_hc_baller_is_active(&ctx->ballers[1])) {
+ CURL_TRC_CF(data, cf, "connect, check %s", ctx->ballers[1].name);
+ result = cf_hc_baller_connect(&ctx->ballers[1], cf, data, done);
if(!result && *done) {
- result = baller_connected(cf, data, &ctx->h21_baller);
+ result = baller_connected(cf, data, &ctx->ballers[1]);
goto out;
}
}
- if((!ctx->h3_baller.enabled || ctx->h3_baller.result) &&
- (!ctx->h21_baller.enabled || ctx->h21_baller.result)) {
- /* both failed or disabled. we give up */
- CURL_TRC_CF(data, cf, "connect, all failed");
- result = ctx->result = ctx->h3_baller.enabled ?
- ctx->h3_baller.result : ctx->h21_baller.result;
+ failed_ballers = 0;
+ for(i = 0; i < ctx->baller_count; i++) {
+ if(ctx->ballers[i].result)
+ ++failed_ballers;
+ }
+
+ if(failed_ballers == ctx->baller_count) {
+ /* all have failed. we give up */
+ CURL_TRC_CF(data, cf, "connect, all attempts failed");
+ for(i = 0; i < ctx->baller_count; i++) {
+ if(ctx->ballers[i].result) {
+ result = ctx->ballers[i].result;
+ break;
+ }
+ }
ctx->state = CF_HC_FAILURE;
goto out;
}
@@ -344,7 +386,6 @@ static CURLcode cf_hc_shutdown(struct Curl_cfilter *cf,
struct Curl_easy *data, bool *done)
{
struct cf_hc_ctx *ctx = cf->ctx;
- struct cf_hc_baller *ballers[2];
size_t i;
CURLcode result = CURLE_OK;
@@ -356,10 +397,8 @@ static CURLcode cf_hc_shutdown(struct Curl_cfilter *cf,
/* shutdown all ballers that have not done so already. If one fails,
* continue shutting down others until all are shutdown. */
- ballers[0] = &ctx->h3_baller;
- ballers[1] = &ctx->h21_baller;
- for(i = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) {
- struct cf_hc_baller *b = ballers[i];
+ for(i = 0; i < ctx->baller_count; i++) {
+ struct cf_hc_baller *b = &ctx->ballers[i];
bool bdone = FALSE;
if(!cf_hc_baller_is_active(b) || b->shutdown)
continue;
@@ -369,14 +408,14 @@ static CURLcode cf_hc_shutdown(struct Curl_cfilter *cf,
}
*done = TRUE;
- for(i = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) {
- if(ballers[i] && !ballers[i]->shutdown)
+ for(i = 0; i < ctx->baller_count; i++) {
+ if(!ctx->ballers[i].shutdown)
*done = FALSE;
}
if(*done) {
- for(i = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) {
- if(ballers[i] && ballers[i]->result)
- result = ballers[i]->result;
+ for(i = 0; i < ctx->baller_count; i++) {
+ if(ctx->ballers[i].result)
+ result = ctx->ballers[i].result;
}
}
CURL_TRC_CF(data, cf, "shutdown -> %d, done=%d", result, *done);
@@ -389,13 +428,10 @@ static void cf_hc_adjust_pollset(struct Curl_cfilter *cf,
{
if(!cf->connected) {
struct cf_hc_ctx *ctx = cf->ctx;
- struct cf_hc_baller *ballers[2];
size_t i;
- ballers[0] = &ctx->h3_baller;
- ballers[1] = &ctx->h21_baller;
- for(i = 0; i < sizeof(ballers)/sizeof(ballers[0]); i++) {
- struct cf_hc_baller *b = ballers[i];
+ for(i = 0; i < ctx->baller_count; i++) {
+ struct cf_hc_baller *b = &ctx->ballers[i];
if(!cf_hc_baller_is_active(b))
continue;
Curl_conn_cf_adjust_pollset(b->cf, data, ps);
@@ -408,13 +444,15 @@ static bool cf_hc_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data)
{
struct cf_hc_ctx *ctx = cf->ctx;
+ size_t i;
if(cf->connected)
return cf->next->cft->has_data_pending(cf->next, data);
- CURL_TRC_CF((struct Curl_easy *)data, cf, "data_pending");
- return cf_hc_baller_data_pending(&ctx->h3_baller, data)
- || cf_hc_baller_data_pending(&ctx->h21_baller, data);
+ for(i = 0; i < ctx->baller_count; i++)
+ if(cf_hc_baller_data_pending(&ctx->ballers[i], data))
+ return TRUE;
+ return FALSE;
}
static struct curltime cf_get_max_baller_time(struct Curl_cfilter *cf,
@@ -422,21 +460,17 @@ static struct curltime cf_get_max_baller_time(struct Curl_cfilter *cf,
int query)
{
struct cf_hc_ctx *ctx = cf->ctx;
- struct Curl_cfilter *cfb;
struct curltime t, tmax;
+ size_t i;
memset(&tmax, 0, sizeof(tmax));
- memset(&t, 0, sizeof(t));
- cfb = ctx->h21_baller.enabled ? ctx->h21_baller.cf : NULL;
- if(cfb && !cfb->cft->query(cfb, data, query, NULL, &t)) {
- if((t.tv_sec || t.tv_usec) && Curl_timediff_us(t, tmax) > 0)
- tmax = t;
- }
- memset(&t, 0, sizeof(t));
- cfb = ctx->h3_baller.enabled ? ctx->h3_baller.cf : NULL;
- if(cfb && !cfb->cft->query(cfb, data, query, NULL, &t)) {
- if((t.tv_sec || t.tv_usec) && Curl_timediff_us(t, tmax) > 0)
- tmax = t;
+ for(i = 0; i < ctx->baller_count; i++) {
+ struct Curl_cfilter *cfb = ctx->ballers[i].cf;
+ memset(&t, 0, sizeof(t));
+ if(cfb && !cfb->cft->query(cfb, data, query, NULL, &t)) {
+ if((t.tv_sec || t.tv_usec) && curlx_timediff_us(t, tmax) > 0)
+ tmax = t;
+ }
}
return tmax;
}
@@ -446,6 +480,7 @@ static CURLcode cf_hc_query(struct Curl_cfilter *cf,
int query, int *pres1, void *pres2)
{
struct cf_hc_ctx *ctx = cf->ctx;
+ size_t i;
if(!cf->connected) {
switch(query) {
@@ -460,11 +495,11 @@ static CURLcode cf_hc_query(struct Curl_cfilter *cf,
return CURLE_OK;
}
case CF_QUERY_NEED_FLUSH: {
- if(cf_hc_baller_needs_flush(&ctx->h3_baller, data)
- || cf_hc_baller_needs_flush(&ctx->h21_baller, data)) {
- *pres1 = TRUE;
- return CURLE_OK;
- }
+ for(i = 0; i < ctx->baller_count; i++)
+ if(cf_hc_baller_needs_flush(&ctx->ballers[i], data)) {
+ *pres1 = TRUE;
+ return CURLE_OK;
+ }
break;
}
default:
@@ -482,14 +517,17 @@ static CURLcode cf_hc_cntrl(struct Curl_cfilter *cf,
{
struct cf_hc_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
+ size_t i;
if(!cf->connected) {
- result = cf_hc_baller_cntrl(&ctx->h3_baller, data, event, arg1, arg2);
- if(!result || (result == CURLE_AGAIN))
- result = cf_hc_baller_cntrl(&ctx->h21_baller, data, event, arg1, arg2);
- if(result == CURLE_AGAIN)
- result = CURLE_OK;
+ for(i = 0; i < ctx->baller_count; i++) {
+ result = cf_hc_baller_cntrl(&ctx->ballers[i], data, event, arg1, arg2);
+ if(result && (result != CURLE_AGAIN))
+ goto out;
+ }
+ result = CURLE_OK;
}
+out:
return result;
}
@@ -536,22 +574,32 @@ struct Curl_cftype Curl_cft_http_connect = {
static CURLcode cf_hc_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
- const struct Curl_dns_entry *remotehost,
- bool try_h3, bool try_h21)
+ enum alpnid *alpnids, size_t alpn_count)
{
struct Curl_cfilter *cf = NULL;
struct cf_hc_ctx *ctx;
CURLcode result = CURLE_OK;
+ size_t i;
+
+ DEBUGASSERT(alpnids);
+ DEBUGASSERT(alpn_count);
+ DEBUGASSERT(alpn_count <= CURL_ARRAYSIZE(ctx->ballers));
+ if(!alpn_count || (alpn_count > CURL_ARRAYSIZE(ctx->ballers))) {
+ failf(data, "https-connect filter create with unsupported %zu ALPN ids",
+ alpn_count);
+ return CURLE_FAILED_INIT;
+ }
- (void)data;
ctx = calloc(1, sizeof(*ctx));
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
- ctx->remotehost = remotehost;
- ctx->h3_baller.enabled = try_h3;
- ctx->h21_baller.enabled = try_h21;
+ for(i = 0; i < alpn_count; ++i)
+ cf_hc_baller_assign(&ctx->ballers[i], alpnids[i]);
+ for(; i < CURL_ARRAYSIZE(ctx->ballers); ++i)
+ ctx->ballers[i].alpn_id = ALPN_none;
+ ctx->baller_count = alpn_count;
result = Curl_cf_create(&cf, &Curl_cft_http_connect, ctx);
if(result)
@@ -568,14 +616,13 @@ out:
static CURLcode cf_http_connect_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
- const struct Curl_dns_entry *remotehost,
- bool try_h3, bool try_h21)
+ enum alpnid *alpn_ids, size_t alpn_count)
{
struct Curl_cfilter *cf;
CURLcode result = CURLE_OK;
DEBUGASSERT(data);
- result = cf_hc_create(&cf, data, remotehost, try_h3, try_h21);
+ result = cf_hc_create(&cf, data, alpn_ids, alpn_count);
if(result)
goto out;
Curl_conn_cf_add(data, conn, sockindex, cf);
@@ -583,38 +630,114 @@ out:
return result;
}
+static bool cf_https_alpns_contain(enum alpnid id,
+ enum alpnid *list, size_t len)
+{
+ size_t i;
+ for(i = 0; i < len; ++i) {
+ if(id == list[i])
+ return TRUE;
+ }
+ return FALSE;
+}
+
CURLcode Curl_cf_https_setup(struct Curl_easy *data,
struct connectdata *conn,
- int sockindex,
- const struct Curl_dns_entry *remotehost)
+ int sockindex)
{
- bool try_h3 = FALSE, try_h21 = TRUE; /* defaults, for now */
+ enum alpnid alpn_ids[2];
+ size_t alpn_count = 0;
CURLcode result = CURLE_OK;
+ struct Curl_cfilter cf_fake, *cf = NULL;
(void)sockindex;
- (void)remotehost;
-
- if(!conn->bits.tls_enable_alpn)
- goto out;
+ /* we want to log for the filter before we create it, fake it. */
+ memset(&cf_fake, 0, sizeof(cf_fake));
+ cf_fake.cft = &Curl_cft_http_connect;
+ cf = &cf_fake;
+
+ if(conn->bits.tls_enable_alpn) {
+#ifdef USE_HTTPSRR
+ /* Is there an HTTPSRR use its ALPNs here.
+ * We are here after having selected a connection to a host+port and
+ * can no longer change that. Any HTTPSRR advice for other hosts and ports
+ * we need to ignore. */
+ struct Curl_dns_entry *dns = data->state.dns[sockindex];
+ struct Curl_https_rrinfo *rr = dns ? dns->hinfo : NULL;
+ if(rr && !rr->no_def_alpn && /* ALPNs are defaults */
+ (!rr->target || /* for same host */
+ !rr->target[0] ||
+ (rr->target[0] == '.' &&
+ !rr->target[1])) &&
+ (rr->port < 0 || /* for same port */
+ rr->port == conn->remote_port)) {
+ size_t i;
+ for(i = 0; i < CURL_ARRAYSIZE(rr->alpns) &&
+ alpn_count < CURL_ARRAYSIZE(alpn_ids); ++i) {
+ enum alpnid alpn = rr->alpns[i];
+ if(cf_https_alpns_contain(alpn, alpn_ids, alpn_count))
+ continue;
+ switch(alpn) {
+ case ALPN_h3:
+ if(Curl_conn_may_http3(data, conn))
+ break; /* not possible */
+ if(data->state.http_neg.allowed & CURL_HTTP_V3x) {
+ CURL_TRC_CF(data, cf, "adding h3 via HTTPS-RR");
+ alpn_ids[alpn_count++] = alpn;
+ }
+ break;
+ case ALPN_h2:
+ if(data->state.http_neg.allowed & CURL_HTTP_V2x) {
+ CURL_TRC_CF(data, cf, "adding h2 via HTTPS-RR");
+ alpn_ids[alpn_count++] = alpn;
+ }
+ break;
+ case ALPN_h1:
+ if(data->state.http_neg.allowed & CURL_HTTP_V1x) {
+ CURL_TRC_CF(data, cf, "adding h1 via HTTPS-RR");
+ alpn_ids[alpn_count++] = alpn;
+ }
+ break;
+ default: /* ignore */
+ break;
+ }
+ }
+ }
+#endif
- if(data->state.httpwant == CURL_HTTP_VERSION_3ONLY) {
- result = Curl_conn_may_http3(data, conn);
- if(result) /* cannot do it */
- goto out;
- try_h3 = TRUE;
- try_h21 = FALSE;
+ if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) &&
+ (data->state.http_neg.wanted & CURL_HTTP_V3x) &&
+ !cf_https_alpns_contain(ALPN_h3, alpn_ids, alpn_count)) {
+ result = Curl_conn_may_http3(data, conn);
+ if(!result) {
+ CURL_TRC_CF(data, cf, "adding wanted h3");
+ alpn_ids[alpn_count++] = ALPN_h3;
+ }
+ else if(data->state.http_neg.wanted == CURL_HTTP_V3x)
+ goto out; /* only h3 allowed, not possible, error out */
+ }
+ if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) &&
+ (data->state.http_neg.wanted & CURL_HTTP_V2x) &&
+ !cf_https_alpns_contain(ALPN_h2, alpn_ids, alpn_count)) {
+ CURL_TRC_CF(data, cf, "adding wanted h2");
+ alpn_ids[alpn_count++] = ALPN_h2;
+ }
+ else if((alpn_count < CURL_ARRAYSIZE(alpn_ids)) &&
+ (data->state.http_neg.wanted & CURL_HTTP_V1x) &&
+ !cf_https_alpns_contain(ALPN_h1, alpn_ids, alpn_count)) {
+ CURL_TRC_CF(data, cf, "adding wanted h1");
+ alpn_ids[alpn_count++] = ALPN_h1;
+ }
}
- else if(data->state.httpwant >= CURL_HTTP_VERSION_3) {
- /* We assume that silently not even trying H3 is ok here */
- /* TODO: should we fail instead? */
- try_h3 = (Curl_conn_may_http3(data, conn) == CURLE_OK);
- try_h21 = TRUE;
+
+ /* If we identified ALPNs to use, install our filter. Otherwise,
+ * install nothing, so our call will use a default connect setup. */
+ if(alpn_count) {
+ result = cf_http_connect_add(data, conn, sockindex, alpn_ids, alpn_count);
}
- result = cf_http_connect_add(data, conn, sockindex, remotehost,
- try_h3, try_h21);
out:
return result;
}
-#endif /* !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) */
+#endif /* !defined(CURL_DISABLE_HTTP) */
diff --git a/libs/libcurl/src/cf-https-connect.h b/libs/libcurl/src/cf-https-connect.h
index 3ca1468ea9..8d956c04bf 100644
--- a/libs/libcurl/src/cf-https-connect.h
+++ b/libs/libcurl/src/cf-https-connect.h
@@ -25,7 +25,7 @@
***************************************************************************/
#include "curl_setup.h"
-#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER)
+#if !defined(CURL_DISABLE_HTTP)
struct Curl_cfilter;
struct Curl_easy;
@@ -38,21 +38,18 @@ extern struct Curl_cftype Curl_cft_http_connect;
CURLcode Curl_cf_http_connect_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
- const struct Curl_dns_entry *remotehost,
bool try_h3, bool try_h21);
CURLcode
Curl_cf_http_connect_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
- const struct Curl_dns_entry *remotehost,
bool try_h3, bool try_h21);
CURLcode Curl_cf_https_setup(struct Curl_easy *data,
struct connectdata *conn,
- int sockindex,
- const struct Curl_dns_entry *remotehost);
+ int sockindex);
-#endif /* !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) */
+#endif /* !defined(CURL_DISABLE_HTTP) */
#endif /* HEADER_CURL_CF_HTTP_H */
diff --git a/libs/libcurl/src/cf-socket.c b/libs/libcurl/src/cf-socket.c
index 89f19cb317..9e85ee211a 100644
--- a/libs/libcurl/src/cf-socket.c
+++ b/libs/libcurl/src/cf-socket.c
@@ -74,15 +74,17 @@
#include "multiif.h"
#include "sockaddr.h" /* required for Curl_sockaddr_storage */
#include "inet_ntop.h"
-#include "inet_pton.h"
+#include "curlx/inet_pton.h"
#include "progress.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
#include "conncache.h"
#include "multihandle.h"
#include "rand.h"
#include "share.h"
#include "strdup.h"
-#include "version_win32.h"
+#include "system_win32.h"
+#include "curlx/version_win32.h"
+#include "curlx/strparse.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -113,8 +115,8 @@ static void tcpnodelay(struct Curl_easy *data, curl_socket_t sockfd)
int level = IPPROTO_TCP;
char buffer[STRERROR_LEN];
- if(setsockopt(sockfd, level, TCP_NODELAY, (void *)&onoff,
- sizeof(onoff)) < 0)
+ if(setsockopt(sockfd, level, TCP_NODELAY,
+ (void *)&onoff, sizeof(onoff)) < 0)
infof(data, "Could not set TCP_NODELAY: %s",
Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
#else
@@ -133,8 +135,8 @@ static void nosigpipe(struct Curl_easy *data,
{
int onoff = 1;
(void)data;
- if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&onoff,
- sizeof(onoff)) < 0) {
+ if(setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE,
+ (void *)&onoff, sizeof(onoff)) < 0) {
#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
char buffer[STRERROR_LEN];
infof(data, "Could not set SO_NOSIGPIPE: %s",
@@ -181,7 +183,7 @@ tcpkeepalive(struct Curl_easy *data,
/* only set IDLE and INTVL if setting KEEPALIVE is successful */
if(setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE,
- (void *)&optval, sizeof(optval)) < 0) {
+ (void *)&optval, sizeof(optval)) < 0) {
infof(data, "Failed to set SO_KEEPALIVE on fd "
"%" FMT_SOCKET_T ": errno %d",
sockfd, SOCKERRNO);
@@ -234,7 +236,7 @@ tcpkeepalive(struct Curl_easy *data,
optval = curlx_sltosi(data->set.tcp_keepidle);
KEEPALIVE_FACTOR(optval);
if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE,
- (void *)&optval, sizeof(optval)) < 0) {
+ (void *)&optval, sizeof(optval)) < 0) {
infof(data, "Failed to set TCP_KEEPIDLE on fd "
"%" FMT_SOCKET_T ": errno %d",
sockfd, SOCKERRNO);
@@ -244,7 +246,7 @@ tcpkeepalive(struct Curl_easy *data,
optval = curlx_sltosi(data->set.tcp_keepidle);
KEEPALIVE_FACTOR(optval);
if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE,
- (void *)&optval, sizeof(optval)) < 0) {
+ (void *)&optval, sizeof(optval)) < 0) {
infof(data, "Failed to set TCP_KEEPALIVE on fd "
"%" FMT_SOCKET_T ": errno %d",
sockfd, SOCKERRNO);
@@ -254,7 +256,7 @@ tcpkeepalive(struct Curl_easy *data,
optval = curlx_sltosi(data->set.tcp_keepidle);
KEEPALIVE_FACTOR(optval);
if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE_THRESHOLD,
- (void *)&optval, sizeof(optval)) < 0) {
+ (void *)&optval, sizeof(optval)) < 0) {
infof(data, "Failed to set TCP_KEEPALIVE_THRESHOLD on fd "
"%" FMT_SOCKET_T ": errno %d",
sockfd, SOCKERRNO);
@@ -264,7 +266,7 @@ tcpkeepalive(struct Curl_easy *data,
optval = curlx_sltosi(data->set.tcp_keepintvl);
KEEPALIVE_FACTOR(optval);
if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPINTVL,
- (void *)&optval, sizeof(optval)) < 0) {
+ (void *)&optval, sizeof(optval)) < 0) {
infof(data, "Failed to set TCP_KEEPINTVL on fd "
"%" FMT_SOCKET_T ": errno %d",
sockfd, SOCKERRNO);
@@ -285,7 +287,7 @@ tcpkeepalive(struct Curl_easy *data,
curlx_sltosi(data->set.tcp_keepintvl);
KEEPALIVE_FACTOR(optval);
if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPALIVE_ABORT_THRESHOLD,
- (void *)&optval, sizeof(optval)) < 0) {
+ (void *)&optval, sizeof(optval)) < 0) {
infof(data, "Failed to set TCP_KEEPALIVE_ABORT_THRESHOLD on fd "
"%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
}
@@ -293,7 +295,7 @@ tcpkeepalive(struct Curl_easy *data,
#ifdef TCP_KEEPCNT
optval = curlx_sltosi(data->set.tcp_keepcnt);
if(setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPCNT,
- (void *)&optval, sizeof(optval)) < 0) {
+ (void *)&optval, sizeof(optval)) < 0) {
infof(data, "Failed to set TCP_KEEPCNT on fd "
"%" FMT_SOCKET_T ": errno %d", sockfd, SOCKERRNO);
}
@@ -306,9 +308,9 @@ tcpkeepalive(struct Curl_easy *data,
* Assign the address `ai` to the Curl_sockaddr_ex `dest` and
* set the transport used.
*/
-void Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest,
- const struct Curl_addrinfo *ai,
- int transport)
+CURLcode Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest,
+ const struct Curl_addrinfo *ai,
+ int transport)
{
/*
* The Curl_sockaddr_ex structure is basically libcurl's external API
@@ -334,9 +336,13 @@ void Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest,
}
dest->addrlen = (unsigned int)ai->ai_addrlen;
- if(dest->addrlen > sizeof(struct Curl_sockaddr_storage))
- dest->addrlen = sizeof(struct Curl_sockaddr_storage);
+ if(dest->addrlen > sizeof(struct Curl_sockaddr_storage)) {
+ DEBUGASSERT(0);
+ return CURLE_TOO_LARGE;
+ }
+
memcpy(&dest->curl_sa_addr, ai->ai_addr, dest->addrlen);
+ return CURLE_OK;
}
static CURLcode socket_open(struct Curl_easy *data,
@@ -395,12 +401,16 @@ CURLcode Curl_socket_open(struct Curl_easy *data,
curl_socket_t *sockfd)
{
struct Curl_sockaddr_ex dummy;
+ CURLcode result;
if(!addr)
/* if the caller does not want info back, use a local temp copy */
addr = &dummy;
- Curl_sock_assign_addr(addr, ai, transport);
+ result = Curl_sock_assign_addr(addr, ai, transport);
+ if(result)
+ return result;
+
return socket_open(data, addr, sockfd);
}
@@ -412,7 +422,7 @@ static int socket_close(struct Curl_easy *data, struct connectdata *conn,
if(use_callback && conn && conn->fclosesocket) {
int rc;
- Curl_multi_closed(data, sock);
+ Curl_multi_will_close(data, sock);
Curl_set_in_callback(data, TRUE);
rc = conn->fclosesocket(conn->closesocket_client, sock);
Curl_set_in_callback(data, FALSE);
@@ -421,7 +431,7 @@ static int socket_close(struct Curl_easy *data, struct connectdata *conn,
if(conn)
/* tell the multi-socket code about this */
- Curl_multi_closed(data, sock);
+ Curl_multi_will_close(data, sock);
sclose(sock);
@@ -452,9 +462,6 @@ int Curl_socket_close(struct Curl_easy *data, struct connectdata *conn,
Windows. Following function trying to detect OS version and skips
SO_SNDBUF adjustment for Windows Vista and above.
*/
-#define DETECT_OS_NONE 0
-#define DETECT_OS_PREVISTA 1
-#define DETECT_OS_VISTA_OR_LATER 2
void Curl_sndbuf_init(curl_socket_t sockfd)
{
@@ -462,17 +469,7 @@ void Curl_sndbuf_init(curl_socket_t sockfd)
int curval = 0;
int curlen = sizeof(curval);
- static int detectOsState = DETECT_OS_NONE;
-
- if(detectOsState == DETECT_OS_NONE) {
- if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
- VERSION_GREATER_THAN_EQUAL))
- detectOsState = DETECT_OS_VISTA_OR_LATER;
- else
- detectOsState = DETECT_OS_PREVISTA;
- }
-
- if(detectOsState == DETECT_OS_VISTA_OR_LATER)
+ if(Curl_isVistaOrGreater)
return;
if(getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&curval, &curlen) == 0)
@@ -600,36 +597,39 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
if(!iface && !host && !port)
/* no local kind of binding was requested */
return CURLE_OK;
+ else if(iface && (strlen(iface) >= 255) )
+ return CURLE_BAD_FUNCTION_ARGUMENT;
memset(&sa, 0, sizeof(struct Curl_sockaddr_storage));
- if(iface && (strlen(iface) < 255) ) {
+ if(iface || host) {
char myhost[256] = "";
int done = 0; /* -1 for error, 1 for address found */
if2ip_result_t if2ip_result = IF2IP_NOT_FOUND;
- /* interface */
#ifdef SO_BINDTODEVICE
- /*
- * This binds the local socket to a particular interface. This will
- * force even requests to other local interfaces to go out the external
- * interface. Only bind to the interface when specified as interface,
- * not just as a hostname or ip address.
- *
- * The interface might be a VRF, eg: vrf-blue, which means it cannot be
- * converted to an IP address and would fail Curl_if2ip. Simply try to
- * use it straight away.
- */
- if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
- iface, (curl_socklen_t)strlen(iface) + 1) == 0) {
- /* This is often "errno 1, error: Operation not permitted" if you are
- * not running as root or another suitable privileged user. If it
- * succeeds it means the parameter was a valid interface and not an IP
- * address. Return immediately.
- */
- if(!host_input) {
- infof(data, "socket successfully bound to interface '%s'", iface);
- return CURLE_OK;
+ if(iface) {
+ /*
+ * This binds the local socket to a particular interface. This will
+ * force even requests to other local interfaces to go out the external
+ * interface. Only bind to the interface when specified as interface,
+ * not just as a hostname or ip address.
+ *
+ * The interface might be a VRF, eg: vrf-blue, which means it cannot be
+ * converted to an IP address and would fail Curl_if2ip. Simply try to
+ * use it straight away.
+ */
+ if(setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
+ iface, (curl_socklen_t)strlen(iface) + 1) == 0) {
+ /* This is often "errno 1, error: Operation not permitted" if you are
+ * not running as root or another suitable privileged user. If it
+ * succeeds it means the parameter was a valid interface and not an IP
+ * address. Return immediately.
+ */
+ if(!host_input) {
+ infof(data, "socket successfully bound to interface '%s'", iface);
+ return CURLE_OK;
+ }
}
}
#endif
@@ -674,21 +674,14 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
* of the connection. The resolve functions should really be changed
* to take a type parameter instead.
*/
- unsigned char ipver = conn->ip_version;
- int rc;
-
- if(af == AF_INET)
- conn->ip_version = CURL_IPRESOLVE_V4;
+ int ip_version = (af == AF_INET) ?
+ CURL_IPRESOLVE_V4 : CURL_IPRESOLVE_WHATEVER;
#ifdef USE_IPV6
- else if(af == AF_INET6)
- conn->ip_version = CURL_IPRESOLVE_V6;
+ if(af == AF_INET6)
+ ip_version = CURL_IPRESOLVE_V6;
#endif
- rc = Curl_resolv(data, host, 80, FALSE, &h);
- if(rc == CURLRESOLV_PENDING)
- (void)Curl_resolver_wait_resolv(data, &h);
- conn->ip_version = ipver;
-
+ (void)Curl_resolv_blocking(data, host, 80, ip_version, &h);
if(h) {
int h_af = h->addr->ai_family;
/* convert the resolved address, sizeof myhost >= INET_ADDRSTRLEN */
@@ -721,7 +714,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
if(scope_ptr)
*(scope_ptr++) = '\0';
#endif
- if(Curl_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) {
+ if(curlx_inet_pton(AF_INET6, myhost, &si6->sin6_addr) > 0) {
si6->sin6_family = AF_INET6;
si6->sin6_port = htons(port);
#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
@@ -730,10 +723,10 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
Curl_printable_address. The latter returns only numeric scope
IDs and the former returns none at all. So the scope ID, if
present, is known to be numeric */
- unsigned long scope_id = strtoul(scope_ptr, NULL, 10);
- if(scope_id > UINT_MAX)
+ curl_off_t scope_id;
+ if(curlx_str_number((const char **)CURL_UNCONST(&scope_ptr),
+ &scope_id, UINT_MAX))
return CURLE_UNSUPPORTED_PROTOCOL;
-
si6->sin6_scope_id = (unsigned int)scope_id;
}
#endif
@@ -744,7 +737,7 @@ static CURLcode bindlocal(struct Curl_easy *data, struct connectdata *conn,
#endif
/* IPv4 address */
if((af == AF_INET) &&
- (Curl_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) {
+ (curlx_inet_pton(AF_INET, myhost, &si4->sin_addr) > 0)) {
si4->sin_family = AF_INET;
si4->sin_port = htons(port);
sizeof_sa = sizeof(struct sockaddr_in);
@@ -844,7 +837,7 @@ static bool verifyconnect(curl_socket_t sockfd, int *error)
* Someone got to verify this on Win-NT 4.0, 2000."
*/
-#ifdef _WIN32_WCE
+#ifdef UNDER_CE
Sleep(0);
#else
SleepEx(0, FALSE);
@@ -854,7 +847,7 @@ static bool verifyconnect(curl_socket_t sockfd, int *error)
if(0 != getsockopt(sockfd, SOL_SOCKET, SO_ERROR, (void *)&err, &errSize))
err = SOCKERRNO;
-#ifdef _WIN32_WCE
+#ifdef UNDER_CE
/* Old Windows CE versions do not support SO_ERROR */
if(WSAENOPROTOOPT == err) {
SET_SOCKERRNO(0);
@@ -868,7 +861,7 @@ static bool verifyconnect(curl_socket_t sockfd, int *error)
err = 0;
}
#endif
- if((0 == err) || (EISCONN == err))
+ if((0 == err) || (SOCKEISCONN == err))
/* we are connected, awesome! */
rc = TRUE;
else
@@ -891,10 +884,10 @@ static CURLcode socket_connect_result(struct Curl_easy *data,
const char *ipaddress, int error)
{
switch(error) {
- case EINPROGRESS:
- case EWOULDBLOCK:
+ case SOCKEINPROGRESS:
+ case SOCKEWOULDBLOCK:
#if defined(EAGAIN)
-#if (EAGAIN) != (EWOULDBLOCK)
+#if (EAGAIN) != (SOCKEWOULDBLOCK)
/* On some platforms EAGAIN and EWOULDBLOCK are the
* same value, and on others they are different, hence
* the odd #if
@@ -921,15 +914,6 @@ static CURLcode socket_connect_result(struct Curl_easy *data,
}
}
-/* We have a recv buffer to enhance reads with len < NW_SMALL_READS.
- * This happens often on TLS connections where the TLS implementation
- * tries to read the head of a TLS record, determine the length of the
- * full record and then make a subsequent read for that.
- * On large reads, we will not fill the buffer to avoid the double copy. */
-#define NW_RECV_CHUNK_SIZE (64 * 1024)
-#define NW_RECV_CHUNKS 1
-#define NW_SMALL_READS (1024)
-
struct cf_socket_ctx {
int transport;
struct Curl_sockaddr_ex addr; /* address to connect to */
@@ -956,42 +940,50 @@ struct cf_socket_ctx {
BIT(active);
};
-static void cf_socket_ctx_init(struct cf_socket_ctx *ctx,
- const struct Curl_addrinfo *ai,
- int transport)
+static CURLcode cf_socket_ctx_init(struct cf_socket_ctx *ctx,
+ const struct Curl_addrinfo *ai,
+ int transport)
{
+ CURLcode result;
+
memset(ctx, 0, sizeof(*ctx));
ctx->sock = CURL_SOCKET_BAD;
ctx->transport = transport;
- Curl_sock_assign_addr(&ctx->addr, ai, transport);
+
+ result = Curl_sock_assign_addr(&ctx->addr, ai, transport);
+ if(result)
+ return result;
+
#ifdef DEBUGBUILD
{
- char *p = getenv("CURL_DBG_SOCK_WBLOCK");
+ const char *p = getenv("CURL_DBG_SOCK_WBLOCK");
if(p) {
- long l = strtol(p, NULL, 10);
- if(l >= 0 && l <= 100)
+ curl_off_t l;
+ if(!curlx_str_number(&p, &l, 100))
ctx->wblock_percent = (int)l;
}
p = getenv("CURL_DBG_SOCK_WPARTIAL");
if(p) {
- long l = strtol(p, NULL, 10);
- if(l >= 0 && l <= 100)
+ curl_off_t l;
+ if(!curlx_str_number(&p, &l, 100))
ctx->wpartial_percent = (int)l;
}
p = getenv("CURL_DBG_SOCK_RBLOCK");
if(p) {
- long l = strtol(p, NULL, 10);
- if(l >= 0 && l <= 100)
+ curl_off_t l;
+ if(!curlx_str_number(&p, &l, 100))
ctx->rblock_percent = (int)l;
}
p = getenv("CURL_DBG_SOCK_RMAX");
if(p) {
- long l = strtol(p, NULL, 10);
- if(l >= 0)
+ curl_off_t l;
+ if(!curlx_str_number(&p, &l, CURL_OFF_T_MAX))
ctx->recv_max = (size_t)l;
}
}
#endif
+
+ return result;
}
static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data)
@@ -999,7 +991,7 @@ static void cf_socket_close(struct Curl_cfilter *cf, struct Curl_easy *data)
struct cf_socket_ctx *ctx = cf->ctx;
if(ctx && CURL_SOCKET_BAD != ctx->sock) {
- CURL_TRC_CF(data, cf, "cf_socket_close(%" FMT_SOCKET_T ")", ctx->sock);
+ CURL_TRC_CF(data, cf, "cf_socket_close, fd=%" FMT_SOCKET_T, ctx->sock);
if(ctx->sock == cf->conn->sock[cf->sockindex])
cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
socket_close(data, cf->conn, !ctx->accepted, ctx->sock);
@@ -1021,7 +1013,7 @@ static CURLcode cf_socket_shutdown(struct Curl_cfilter *cf,
if(cf->connected) {
struct cf_socket_ctx *ctx = cf->ctx;
- CURL_TRC_CF(data, cf, "cf_socket_shutdown(%" FMT_SOCKET_T ")", ctx->sock);
+ CURL_TRC_CF(data, cf, "cf_socket_shutdown, fd=%" FMT_SOCKET_T, ctx->sock);
/* On TCP, and when the socket looks well and non-blocking mode
* can be enabled, receive dangling bytes before close to avoid
* entering RST states unnecessarily. */
@@ -1113,7 +1105,7 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf,
(void)data;
DEBUGASSERT(ctx->sock == CURL_SOCKET_BAD);
- ctx->started_at = Curl_now();
+ ctx->started_at = curlx_now();
#ifdef SOCK_NONBLOCK
/* Do not tuck SOCK_NONBLOCK into socktype when opensocket callback is set
* because we would not know how socketype is about to be used in the
@@ -1229,7 +1221,7 @@ out:
}
else if(isconnected) {
set_local_ip(cf, data);
- ctx->connected_at = Curl_now();
+ ctx->connected_at = curlx_now();
cf->connected = TRUE;
}
CURL_TRC_CF(data, cf, "cf_socket_open() -> %d, fd=%" FMT_SOCKET_T,
@@ -1279,7 +1271,7 @@ static int do_connect(struct Curl_cfilter *cf, struct Curl_easy *data,
rc = connect(ctx->sock, &ctx->addr.curl_sa_addr, ctx->addr.addrlen);
#elif defined(MSG_FASTOPEN) /* old Linux */
- if(cf->conn->given->flags & PROTOPT_SSL)
+ if(Curl_conn_is_ssl(cf->conn, cf->sockindex))
rc = connect(ctx->sock, &ctx->addr.curl_sa_addr, ctx->addr.addrlen);
else
rc = 0; /* Do nothing */
@@ -1294,7 +1286,7 @@ static int do_connect(struct Curl_cfilter *cf, struct Curl_easy *data,
static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool blocking, bool *done)
+ bool *done)
{
struct cf_socket_ctx *ctx = cf->ctx;
CURLcode result = CURLE_COULDNT_CONNECT;
@@ -1306,10 +1298,6 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
return CURLE_OK;
}
- /* TODO: need to support blocking connect? */
- if(blocking)
- return CURLE_UNSUPPORTED_PROTOCOL;
-
*done = FALSE; /* a negative world view is best */
if(ctx->sock == CURL_SOCKET_BAD) {
int error;
@@ -1352,7 +1340,7 @@ static CURLcode cf_tcp_connect(struct Curl_cfilter *cf,
else if(rc == CURL_CSELECT_OUT || cf->conn->bits.tcp_fastopen) {
if(verifyconnect(ctx->sock, &ctx->error)) {
/* we are connected with TCP, awesome! */
- ctx->connected_at = Curl_now();
+ ctx->connected_at = curlx_now();
set_local_ip(cf, data);
*done = TRUE;
cf->connected = TRUE;
@@ -1413,7 +1401,7 @@ static void cf_socket_adjust_pollset(struct Curl_cfilter *cf,
/* A listening socket filter needs to be connected before the accept
* for some weird FTP interaction. This should be rewritten, so that
* FTP no longer does the socket checks and accept calls and delegates
- * all that to the filter. TODO. */
+ * all that to the filter. */
if(ctx->listening) {
Curl_pollset_set_in_only(data, ps, ctx->sock);
CURL_TRC_CF(data, cf, "adjust_pollset, listening, POLLIN fd=%"
@@ -1440,7 +1428,7 @@ static bool cf_socket_data_pending(struct Curl_cfilter *cf,
(void)data;
readable = SOCKET_READABLE(ctx->sock, 0);
- return (readable > 0 && (readable & CURL_CSELECT_IN));
+ return readable > 0 && (readable & CURL_CSELECT_IN);
}
#ifdef USE_WINSOCK
@@ -1453,9 +1441,9 @@ static void win_update_sndbuf_size(struct cf_socket_ctx *ctx)
{
ULONG ideal;
DWORD ideallen;
- struct curltime n = Curl_now();
+ struct curltime n = curlx_now();
- if(Curl_timediff(n, ctx->last_sndbuf_query_at) > 1000) {
+ if(curlx_timediff(n, ctx->last_sndbuf_query_at) > 1000) {
if(!WSAIoctl(ctx->sock, SIO_IDEAL_SEND_BACKLOG_QUERY, 0, 0,
&ideal, sizeof(ideal), &ideallen, 0, 0) &&
ideal != ctx->sndbuf_size &&
@@ -1520,15 +1508,16 @@ static ssize_t cf_socket_send(struct Curl_cfilter *cf, struct Curl_easy *data,
int sockerr = SOCKERRNO;
if(
-#ifdef WSAEWOULDBLOCK
+#ifdef USE_WINSOCK
/* This is how Windows does it */
- (WSAEWOULDBLOCK == sockerr)
+ (SOCKEWOULDBLOCK == sockerr)
#else
/* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
due to its inability to send off data without blocking. We therefore
treat both error codes the same here */
- (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr) ||
- (EINPROGRESS == sockerr)
+ (SOCKEWOULDBLOCK == sockerr) ||
+ (EAGAIN == sockerr) || (SOCKEINTR == sockerr) ||
+ (SOCKEINPROGRESS == sockerr)
#endif
) {
/* this is just a case of EWOULDBLOCK */
@@ -1588,14 +1577,15 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
int sockerr = SOCKERRNO;
if(
-#ifdef WSAEWOULDBLOCK
+#ifdef USE_WINSOCK
/* This is how Windows does it */
- (WSAEWOULDBLOCK == sockerr)
+ (SOCKEWOULDBLOCK == sockerr)
#else
/* errno may be EWOULDBLOCK or on some systems EAGAIN when it returned
due to its inability to send off data without blocking. We therefore
treat both error codes the same here */
- (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) || (EINTR == sockerr)
+ (SOCKEWOULDBLOCK == sockerr) ||
+ (EAGAIN == sockerr) || (SOCKEINTR == sockerr)
#endif
) {
/* this is just a case of EWOULDBLOCK */
@@ -1614,7 +1604,7 @@ static ssize_t cf_socket_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
CURL_TRC_CF(data, cf, "recv(len=%zu) -> %d, err=%d", len, (int)nread,
*err);
if(nread > 0 && !ctx->got_first_byte) {
- ctx->first_byte_at = Curl_now();
+ ctx->first_byte_at = curlx_now();
ctx->got_first_byte = TRUE;
}
return nread;
@@ -1725,7 +1715,7 @@ static CURLcode cf_socket_query(struct Curl_cfilter *cf,
return CURLE_OK;
case CF_QUERY_CONNECT_REPLY_MS:
if(ctx->got_first_byte) {
- timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at);
+ timediff_t ms = curlx_timediff(ctx->first_byte_at, ctx->started_at);
*pres1 = (ms < INT_MAX) ? (int)ms : INT_MAX;
}
else
@@ -1802,7 +1792,10 @@ CURLcode Curl_cf_tcp_create(struct Curl_cfilter **pcf,
result = CURLE_OUT_OF_MEMORY;
goto out;
}
- cf_socket_ctx_init(ctx, ai, transport);
+
+ result = cf_socket_ctx_init(ctx, ai, transport);
+ if(result)
+ goto out;
result = Curl_cf_create(&cf, &Curl_cft_tcp, ctx);
@@ -1828,6 +1821,8 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
/* QUIC needs a connected socket, nonblocking */
DEBUGASSERT(ctx->sock != CURL_SOCKET_BAD);
+ /* error: The 1st argument to 'connect' is -1 but should be >= 0
+ NOLINTNEXTLINE(clang-analyzer-unix.StdCLibraryFunctions) */
rc = connect(ctx->sock, &ctx->addr.curl_sa_addr,
(curl_socklen_t)ctx->addr.addrlen);
if(-1 == rc) {
@@ -1846,8 +1841,9 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
* non-blocking socket created by cf_socket_open() to it. Thus, we
* do not need to call curlx_nonblock() in cf_udp_setup_quic() anymore.
*/
+#ifdef __linux__
switch(ctx->addr.family) {
-#if defined(__linux__) && defined(IP_MTU_DISCOVER)
+#ifdef IP_MTU_DISCOVER
case AF_INET: {
int val = IP_PMTUDISC_DO;
(void)setsockopt(ctx->sock, IPPROTO_IP, IP_MTU_DISCOVER, &val,
@@ -1855,7 +1851,7 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
break;
}
#endif
-#if defined(__linux__) && defined(IPV6_MTU_DISCOVER)
+#ifdef IPV6_MTU_DISCOVER
case AF_INET6: {
int val = IPV6_PMTUDISC_DO;
(void)setsockopt(ctx->sock, IPPROTO_IPV6, IPV6_MTU_DISCOVER, &val,
@@ -1865,24 +1861,24 @@ static CURLcode cf_udp_setup_quic(struct Curl_cfilter *cf,
#endif
}
-#if defined(__linux__) && defined(UDP_GRO) && \
+#if defined(UDP_GRO) && \
(defined(HAVE_SENDMMSG) || defined(HAVE_SENDMSG)) && \
((defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || defined(USE_QUICHE))
(void)setsockopt(ctx->sock, IPPROTO_UDP, UDP_GRO, &one,
(socklen_t)sizeof(one));
#endif
+#endif
return CURLE_OK;
}
static CURLcode cf_udp_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool blocking, bool *done)
+ bool *done)
{
struct cf_socket_ctx *ctx = cf->ctx;
CURLcode result = CURLE_COULDNT_CONNECT;
- (void)blocking;
if(cf->connected) {
*done = TRUE;
return CURLE_OK;
@@ -1951,7 +1947,10 @@ CURLcode Curl_cf_udp_create(struct Curl_cfilter **pcf,
result = CURLE_OUT_OF_MEMORY;
goto out;
}
- cf_socket_ctx_init(ctx, ai, transport);
+
+ result = cf_socket_ctx_init(ctx, ai, transport);
+ if(result)
+ goto out;
result = Curl_cf_create(&cf, &Curl_cft_udp, ctx);
@@ -2003,7 +2002,10 @@ CURLcode Curl_cf_unix_create(struct Curl_cfilter **pcf,
result = CURLE_OUT_OF_MEMORY;
goto out;
}
- cf_socket_ctx_init(ctx, ai, transport);
+
+ result = cf_socket_ctx_init(ctx, ai, transport);
+ if(result)
+ goto out;
result = Curl_cf_create(&cf, &Curl_cft_unix, ctx);
@@ -2030,7 +2032,7 @@ static timediff_t cf_tcp_accept_timeleft(struct Curl_cfilter *cf,
timeout_ms = data->set.accepttimeout;
#endif
- now = Curl_now();
+ now = curlx_now();
/* check if the generic timeout possibly is set shorter */
other = Curl_timeleft(data, &now, FALSE);
if(other && (other < timeout_ms))
@@ -2039,7 +2041,7 @@ static timediff_t cf_tcp_accept_timeleft(struct Curl_cfilter *cf,
timeout_ms = other;
else {
/* subtract elapsed time */
- timeout_ms -= Curl_timediff(now, ctx->started_at);
+ timeout_ms -= curlx_timediff(now, ctx->started_at);
if(!timeout_ms)
/* avoid returning 0 as that means no timeout! */
timeout_ms = -1;
@@ -2081,7 +2083,7 @@ static void cf_tcp_set_accepted_remote_ip(struct Curl_cfilter *cf,
static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool blocking, bool *done)
+ bool *done)
{
struct cf_socket_ctx *ctx = cf->ctx;
#ifdef USE_IPV6
@@ -2097,7 +2099,6 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
/* we start accepted, if we ever close, we cannot go on */
(void)data;
- (void)blocking;
if(cf->connected) {
*done = TRUE;
return CURLE_OK;
@@ -2136,7 +2137,12 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
if(0 == getsockname(ctx->sock, (struct sockaddr *) &add, &size)) {
size = sizeof(add);
+#ifdef HAVE_ACCEPT4
+ s_accepted = accept4(ctx->sock, (struct sockaddr *) &add, &size,
+ SOCK_NONBLOCK | SOCK_CLOEXEC);
+#else
s_accepted = accept(ctx->sock, (struct sockaddr *) &add, &size);
+#endif
}
if(CURL_SOCKET_BAD == s_accepted) {
@@ -2145,7 +2151,9 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
}
infof(data, "Connection accepted from server");
+#ifndef HAVE_ACCEPT4
(void)curlx_nonblock(s_accepted, TRUE); /* enable non-blocking */
+#endif
/* Replace any filter on SECONDARY with one listening on this socket */
ctx->listening = FALSE;
ctx->accepted = TRUE;
@@ -2156,7 +2164,7 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
cf_tcp_set_accepted_remote_ip(cf, data);
set_local_ip(cf, data);
ctx->active = TRUE;
- ctx->connected_at = Curl_now();
+ ctx->connected_at = curlx_now();
cf->connected = TRUE;
CURL_TRC_CF(data, cf, "accepted_set(sock=%" FMT_SOCKET_T
", remote=%s port=%d)",
@@ -2174,6 +2182,7 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf,
if(error)
return CURLE_ABORTED_BY_CALLBACK;
}
+ *done = TRUE;
return CURLE_OK;
}
@@ -2185,7 +2194,7 @@ struct Curl_cftype Curl_cft_tcp_accept = {
cf_tcp_accept_connect,
cf_socket_close,
cf_socket_shutdown,
- cf_socket_get_host, /* TODO: not accurate */
+ cf_socket_get_host,
cf_socket_adjust_pollset,
cf_socket_data_pending,
cf_socket_send,
@@ -2222,7 +2231,7 @@ CURLcode Curl_conn_tcp_listen_set(struct Curl_easy *data,
goto out;
Curl_conn_cf_add(data, conn, sockindex, cf);
- ctx->started_at = Curl_now();
+ ctx->started_at = curlx_now();
conn->sock[sockindex] = ctx->sock;
set_local_ip(cf, data);
CURL_TRC_CF(data, cf, "set filter for listen socket fd=%" FMT_SOCKET_T
diff --git a/libs/libcurl/src/cf-socket.h b/libs/libcurl/src/cf-socket.h
index e62c064cff..520e764c1c 100644
--- a/libs/libcurl/src/cf-socket.h
+++ b/libs/libcurl/src/cf-socket.h
@@ -25,7 +25,7 @@
***************************************************************************/
#include "curl_setup.h"
-#include "nonblock.h" /* for curlx_nonblock(), formerly Curl_nonblock() */
+#include "curlx/nonblock.h" /* for curlx_nonblock() */
#include "sockaddr.h"
struct Curl_addrinfo;
@@ -95,9 +95,9 @@ void Curl_sndbuf_init(curl_socket_t sockfd);
* Assign the address `ai` to the Curl_sockaddr_ex `dest` and
* set the transport used.
*/
-void Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest,
- const struct Curl_addrinfo *ai,
- int transport);
+CURLcode Curl_sock_assign_addr(struct Curl_sockaddr_ex *dest,
+ const struct Curl_addrinfo *ai,
+ int transport);
/**
* Creates a cfilter that opens a TCP socket to the given address
diff --git a/libs/libcurl/src/cfilters.c b/libs/libcurl/src/cfilters.c
index f2aaa1e864..43a5e004eb 100644
--- a/libs/libcurl/src/cfilters.c
+++ b/libs/libcurl/src/cfilters.c
@@ -28,23 +28,20 @@
#include "strerror.h"
#include "cfilters.h"
#include "connect.h"
-#include "url.h" /* for Curl_safefree() */
+#include "url.h"
#include "sendf.h"
#include "sockaddr.h" /* required for Curl_sockaddr_storage */
#include "multiif.h"
#include "progress.h"
#include "select.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
+#include "curlx/strparse.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
#include "memdebug.h"
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
-#endif
-
static void cf_cntrl_update_info(struct Curl_easy *data,
struct connectdata *conn);
@@ -201,11 +198,11 @@ CURLcode Curl_conn_shutdown(struct Curl_easy *data, int sockindex, bool *done)
}
*done = FALSE;
- now = Curl_now();
+ now = curlx_now();
if(!Curl_shutdown_started(data, sockindex)) {
- DEBUGF(infof(data, "shutdown start on%s connection",
- sockindex ? " secondary" : ""));
- Curl_shutdown_start(data, sockindex, &now);
+ CURL_TRC_M(data, "shutdown start on%s connection",
+ sockindex ? " secondary" : "");
+ Curl_shutdown_start(data, sockindex, 0, &now);
}
else {
timeout_ms = Curl_shutdown_timeleft(data->conn, sockindex, &now);
@@ -371,10 +368,10 @@ bool Curl_conn_cf_discard_sub(struct Curl_cfilter *cf,
CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool blocking, bool *done)
+ bool *done)
{
if(cf)
- return cf->cft->do_connect(cf, data, blocking, done);
+ return cf->cft->do_connect(cf, data, done);
return CURLE_FAILED_INIT;
}
@@ -408,6 +405,9 @@ CURLcode Curl_conn_connect(struct Curl_easy *data,
bool blocking,
bool *done)
{
+#define CF_CONN_NUM_POLLS_ON_STACK 5
+ struct pollfd a_few_on_stack[CF_CONN_NUM_POLLS_ON_STACK];
+ struct curl_pollfds cpfds;
struct Curl_cfilter *cf;
CURLcode result = CURLE_OK;
@@ -415,14 +415,17 @@ CURLcode Curl_conn_connect(struct Curl_easy *data,
DEBUGASSERT(data->conn);
cf = data->conn->cfilter[sockindex];
- DEBUGASSERT(cf);
if(!cf) {
*done = FALSE;
return CURLE_FAILED_INIT;
}
*done = cf->connected;
- if(!*done) {
+ if(*done)
+ return CURLE_OK;
+
+ Curl_pollfds_init(&cpfds, a_few_on_stack, CF_CONN_NUM_POLLS_ON_STACK);
+ while(!*done) {
if(Curl_conn_needs_flush(data, sockindex)) {
DEBUGF(infof(data, "Curl_conn_connect(index=%d), flush", sockindex));
result = Curl_conn_flush(data, sockindex);
@@ -430,24 +433,75 @@ CURLcode Curl_conn_connect(struct Curl_easy *data,
return result;
}
- result = cf->cft->do_connect(cf, data, blocking, done);
+ result = cf->cft->do_connect(cf, data, done);
+ CURL_TRC_CF(data, cf, "Curl_conn_connect(block=%d) -> %d, done=%d",
+ blocking, result, *done);
if(!result && *done) {
/* Now that the complete filter chain is connected, let all filters
* persist information at the connection. E.g. cf-socket sets the
* socket and ip related information. */
cf_cntrl_update_info(data, data->conn);
conn_report_connect_stats(data, data->conn);
- data->conn->keepalive = Curl_now();
+ data->conn->keepalive = curlx_now();
Curl_verboseconnect(data, data->conn, sockindex);
+ goto out;
}
else if(result) {
+ CURL_TRC_CF(data, cf, "Curl_conn_connect(), filter returned %d",
+ result);
conn_report_connect_stats(data, data->conn);
+ goto out;
+ }
+
+ if(!blocking)
+ goto out;
+ else {
+ /* check allowed time left */
+ const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
+ curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
+ struct easy_pollset ps;
+ int rc;
+
+ if(timeout_ms < 0) {
+ /* no need to continue if time already is up */
+ failf(data, "connect timeout");
+ result = CURLE_OPERATION_TIMEDOUT;
+ goto out;
+ }
+
+ CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), do poll");
+ Curl_pollfds_reset(&cpfds);
+ memset(&ps, 0, sizeof(ps));
+ /* In general, we want to send after connect, wait on that. */
+ if(sockfd != CURL_SOCKET_BAD)
+ Curl_pollset_set_out_only(data, &ps, sockfd);
+ Curl_conn_adjust_pollset(data, data->conn, &ps);
+ result = Curl_pollfds_add_ps(&cpfds, &ps);
+ if(result)
+ goto out;
+
+ rc = Curl_poll(cpfds.pfds, cpfds.n,
+ CURLMIN(timeout_ms, (cpfds.n ? 1000 : 10)));
+ CURL_TRC_CF(data, cf, "Curl_conn_connect(block=1), Curl_poll() -> %d",
+ rc);
+ if(rc < 0) {
+ result = CURLE_COULDNT_CONNECT;
+ goto out;
+ }
+ /* continue iterating */
}
}
+out:
+ Curl_pollfds_cleanup(&cpfds);
return result;
}
+bool Curl_conn_is_setup(struct connectdata *conn, int sockindex)
+{
+ return (conn->cfilter[sockindex] != NULL);
+}
+
bool Curl_conn_is_connected(struct connectdata *conn, int sockindex)
{
struct Curl_cfilter *cf;
@@ -494,13 +548,36 @@ bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex)
for(; cf; cf = cf->next) {
if(cf->cft->flags & CF_TYPE_MULTIPLEX)
return TRUE;
- if(cf->cft->flags & CF_TYPE_IP_CONNECT
- || cf->cft->flags & CF_TYPE_SSL)
+ if(cf->cft->flags & (CF_TYPE_IP_CONNECT|CF_TYPE_SSL))
return FALSE;
}
return FALSE;
}
+unsigned char Curl_conn_http_version(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ struct Curl_cfilter *cf;
+ CURLcode result = CURLE_UNKNOWN_OPTION;
+ unsigned char v = 0;
+
+ cf = conn->cfilter[FIRSTSOCKET];
+ for(; cf; cf = cf->next) {
+ if(cf->cft->flags & CF_TYPE_HTTP) {
+ int value = 0;
+ result = cf->cft->query(cf, data, CF_QUERY_HTTP_VERSION, &value, NULL);
+ if(!result && ((value < 0) || (value > 255)))
+ result = CURLE_FAILED_INIT;
+ else
+ v = (unsigned char)value;
+ break;
+ }
+ if(cf->cft->flags & (CF_TYPE_IP_CONNECT|CF_TYPE_SSL))
+ break;
+ }
+ return (unsigned char)(result ? 0 : v);
+}
+
bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex)
{
struct Curl_cfilter *cf;
@@ -553,14 +630,15 @@ void Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf,
}
void Curl_conn_adjust_pollset(struct Curl_easy *data,
- struct easy_pollset *ps)
+ struct connectdata *conn,
+ struct easy_pollset *ps)
{
int i;
DEBUGASSERT(data);
- DEBUGASSERT(data->conn);
+ DEBUGASSERT(conn);
for(i = 0; i < 2; ++i) {
- Curl_conn_cf_adjust_pollset(data->conn->cfilter[i], data, ps);
+ Curl_conn_cf_adjust_pollset(conn->cfilter[i], data, ps);
}
}
@@ -702,7 +780,7 @@ static CURLcode cf_cntrl_all(struct connectdata *conn,
CURLcode result = CURLE_OK;
size_t i;
- for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) {
+ for(i = 0; i < CURL_ARRAYSIZE(conn->cfilter); ++i) {
result = Curl_conn_cf_cntrl(conn->cfilter[i], data, ignore_result,
event, arg1, arg2);
if(!ignore_result && result)
@@ -711,18 +789,6 @@ static CURLcode cf_cntrl_all(struct connectdata *conn,
return result;
}
-void Curl_conn_ev_data_attach(struct connectdata *conn,
- struct Curl_easy *data)
-{
- cf_cntrl_all(conn, data, TRUE, CF_CTRL_DATA_ATTACH, 0, NULL);
-}
-
-void Curl_conn_ev_data_detach(struct connectdata *conn,
- struct Curl_easy *data)
-{
- cf_cntrl_all(conn, data, TRUE, CF_CTRL_DATA_DETACH, 0, NULL);
-}
-
CURLcode Curl_conn_ev_data_setup(struct Curl_easy *data)
{
return cf_cntrl_all(data->conn, data, FALSE,
@@ -874,14 +940,14 @@ CURLcode Curl_conn_send(struct Curl_easy *data, int sockindex,
DEBUGASSERT(data->conn);
conn = data->conn;
#ifdef DEBUGBUILD
- {
+ if(write_len) {
/* Allow debug builds to override this logic to force short sends
*/
- char *p = getenv("CURL_SMALLSENDS");
+ const char *p = getenv("CURL_SMALLSENDS");
if(p) {
- size_t altsize = (size_t)strtoul(p, NULL, 10);
- if(altsize)
- write_len = CURLMIN(write_len, altsize);
+ curl_off_t altsize;
+ if(!curlx_str_number(&p, &altsize, write_len))
+ write_len = (size_t)altsize;
}
}
#endif
diff --git a/libs/libcurl/src/cfilters.h b/libs/libcurl/src/cfilters.h
index 9d835fdbf3..9da69327aa 100644
--- a/libs/libcurl/src/cfilters.h
+++ b/libs/libcurl/src/cfilters.h
@@ -24,7 +24,7 @@
*
***************************************************************************/
-#include "timediff.h"
+#include "curlx/timediff.h"
struct Curl_cfilter;
struct Curl_easy;
@@ -51,7 +51,7 @@ typedef CURLcode Curl_cft_shutdown(struct Curl_cfilter *cf,
typedef CURLcode Curl_cft_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool blocking, bool *done);
+ bool *done);
/* Return the hostname and port the connection goes to.
* This may change with the connection state of filters when tunneling
@@ -65,10 +65,10 @@ typedef CURLcode Curl_cft_connect(struct Curl_cfilter *cf,
* @param pport on return, contains the port number
*/
typedef void Curl_cft_get_host(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const char **phost,
- const char **pdisplay_host,
- int *pport);
+ struct Curl_easy *data,
+ const char **phost,
+ const char **pdisplay_host,
+ int *pport);
struct easy_pollset;
@@ -96,8 +96,8 @@ struct easy_pollset;
* @param ps the pollset (inout) for the easy handle
*/
typedef void Curl_cft_adjust_pollset(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct easy_pollset *ps);
+ struct Curl_easy *data,
+ struct easy_pollset *ps);
typedef bool Curl_cft_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data);
@@ -132,8 +132,6 @@ typedef CURLcode Curl_cft_conn_keep_alive(struct Curl_cfilter *cf,
* to all filters in the chain. Overall result is always CURLE_OK.
*/
/* data event arg1 arg2 return */
-#define CF_CTRL_DATA_ATTACH 1 /* 0 NULL ignored */
-#define CF_CTRL_DATA_DETACH 2 /* 0 NULL ignored */
#define CF_CTRL_DATA_SETUP 4 /* 0 NULL first fail */
#define CF_CTRL_DATA_IDLE 5 /* 0 NULL first fail */
#define CF_CTRL_DATA_PAUSE 6 /* on/off NULL first fail */
@@ -178,6 +176,7 @@ typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf,
#define CF_QUERY_STREAM_ERROR 6 /* error code - */
#define CF_QUERY_NEED_FLUSH 7 /* TRUE/FALSE - */
#define CF_QUERY_IP_INFO 8 /* TRUE/FALSE struct ip_quadruple */
+#define CF_QUERY_HTTP_VERSION 9 /* number (10/11/20/30) - */
/**
* Query the cfilter for properties. Filters ignorant of a query will
@@ -197,11 +196,13 @@ typedef CURLcode Curl_cft_query(struct Curl_cfilter *cf,
* CF_TYPE_SSL: provide SSL/TLS
* CF_TYPE_MULTIPLEX: provides multiplexing of easy handles
* CF_TYPE_PROXY provides proxying
+ * CF_TYPE_HTTP implement a version of the HTTP protocol
*/
#define CF_TYPE_IP_CONNECT (1 << 0)
#define CF_TYPE_SSL (1 << 1)
#define CF_TYPE_MULTIPLEX (1 << 2)
#define CF_TYPE_PROXY (1 << 3)
+#define CF_TYPE_HTTP (1 << 4)
/* A connection filter type, e.g. specific implementation. */
struct Curl_cftype {
@@ -244,8 +245,8 @@ void Curl_cf_def_get_host(struct Curl_cfilter *cf, struct Curl_easy *data,
const char **phost, const char **pdisplay_host,
int *pport);
void Curl_cf_def_adjust_pollset(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct easy_pollset *ps);
+ struct Curl_easy *data,
+ struct easy_pollset *ps);
bool Curl_cf_def_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data);
ssize_t Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data,
@@ -254,8 +255,8 @@ ssize_t Curl_cf_def_send(struct Curl_cfilter *cf, struct Curl_easy *data,
ssize_t Curl_cf_def_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
char *buf, size_t len, CURLcode *err);
CURLcode Curl_cf_def_cntrl(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- int event, int arg1, void *arg2);
+ struct Curl_easy *data,
+ int event, int arg1, void *arg2);
bool Curl_cf_def_conn_is_alive(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *input_pending);
@@ -323,7 +324,7 @@ void Curl_conn_cf_discard_all(struct Curl_easy *data,
CURLcode Curl_conn_cf_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool blocking, bool *done);
+ bool *done);
void Curl_conn_cf_close(struct Curl_cfilter *cf, struct Curl_easy *data);
ssize_t Curl_conn_cf_send(struct Curl_cfilter *cf, struct Curl_easy *data,
const void *buf, size_t len, bool eos,
@@ -370,6 +371,11 @@ CURLcode Curl_conn_connect(struct Curl_easy *data, int sockindex,
bool blocking, bool *done);
/**
+ * Check if a filter chain at `sockindex` for connection `conn` exists.
+ */
+bool Curl_conn_is_setup(struct connectdata *conn, int sockindex);
+
+/**
* Check if the filter chain at `sockindex` for connection `conn` is
* completely connected.
*/
@@ -395,6 +401,13 @@ bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex);
bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex);
/**
+ * Return the HTTP version used on the FIRSTSOCKET connection filters
+ * or 0 if unknown. Value otherwise is 09, 10, 11, etc.
+ */
+unsigned char Curl_conn_http_version(struct Curl_easy *data,
+ struct connectdata *conn);
+
+/**
* Close the filter chain at `sockindex` for connection `data->conn`.
* Filters remain in place and may be connected again afterwards.
*/
@@ -447,7 +460,8 @@ void Curl_conn_cf_adjust_pollset(struct Curl_cfilter *cf,
* Adjust pollset from filters installed at transfer's connection.
*/
void Curl_conn_adjust_pollset(struct Curl_easy *data,
- struct easy_pollset *ps);
+ struct connectdata *conn,
+ struct easy_pollset *ps);
/**
* Curl_poll() the filter chain at `cf` with timeout `timeout_ms`.
@@ -477,24 +491,6 @@ ssize_t Curl_cf_send(struct Curl_easy *data, int sockindex,
const void *buf, size_t len, bool eos, CURLcode *code);
/**
- * The easy handle `data` is being attached to `conn`. This does
- * not mean that data will actually do a transfer. Attachment is
- * also used for temporary actions on the connection.
- */
-void Curl_conn_ev_data_attach(struct connectdata *conn,
- struct Curl_easy *data);
-
-/**
- * The easy handle `data` is being detached (no longer served)
- * by connection `conn`. All filters are informed to release any resources
- * related to `data`.
- * Note: there may be several `data` attached to a connection at the same
- * time.
- */
-void Curl_conn_ev_data_detach(struct connectdata *conn,
- struct Curl_easy *data);
-
-/**
* Notify connection filters that they need to setup data for
* a transfer.
*/
@@ -665,7 +661,7 @@ struct cf_call_data {
(save) = CF_CTX_CALL_DATA(cf); \
DEBUGASSERT((save).data == NULL || (save).depth > 0); \
CF_CTX_CALL_DATA(cf).depth++; \
- CF_CTX_CALL_DATA(cf).data = (struct Curl_easy *)data; \
+ CF_CTX_CALL_DATA(cf).data = (struct Curl_easy *)CURL_UNCONST(data); \
} while(0)
#define CF_DATA_RESTORE(cf, save) \
@@ -680,7 +676,7 @@ struct cf_call_data {
#define CF_DATA_SAVE(save, cf, data) \
do { \
(save) = CF_CTX_CALL_DATA(cf); \
- CF_CTX_CALL_DATA(cf).data = (struct Curl_easy *)data; \
+ CF_CTX_CALL_DATA(cf).data = (struct Curl_easy *)CURL_UNCONST(data); \
} while(0)
#define CF_DATA_RESTORE(cf, save) \
diff --git a/libs/libcurl/src/config-amigaos.h b/libs/libcurl/src/config-amigaos.h
deleted file mode 100644
index 82765a0f72..0000000000
--- a/libs/libcurl/src/config-amigaos.h
+++ /dev/null
@@ -1,129 +0,0 @@
-#ifndef HEADER_CURL_CONFIG_AMIGAOS_H
-#define HEADER_CURL_CONFIG_AMIGAOS_H
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-/* ================================================================ */
-/* Hand crafted config file for AmigaOS */
-/* ================================================================ */
-
-#ifdef __AMIGA__ /* Any AmigaOS flavour */
-
-#define HAVE_ARPA_INET_H 1
-#define HAVE_CLOSESOCKET_CAMEL 1
-#define HAVE_IOCTLSOCKET_CAMEL 1
-#define HAVE_IOCTLSOCKET_CAMEL_FIONBIO 1
-#define HAVE_LONGLONG 1
-#define HAVE_NETDB_H 1
-#define HAVE_NETINET_IN_H 1
-#define HAVE_NET_IF_H 1
-#define HAVE_PWD_H 1
-#define HAVE_SELECT 1
-#define HAVE_SIGNAL 1
-#define HAVE_SOCKET 1
-#define HAVE_STRCASECMP 1
-#define HAVE_STRDUP 1
-#define HAVE_STRICMP 1
-#define HAVE_STRINGS_H 1
-#define HAVE_STRUCT_TIMEVAL 1
-#define HAVE_SYS_PARAM_H 1
-#define HAVE_SYS_SOCKET_H 1
-#define HAVE_SYS_SOCKIO_H 1
-#define HAVE_SYS_STAT_H 1
-#define HAVE_SYS_TIME_H 1
-#define HAVE_SYS_TYPES_H 1
-#define HAVE_UNISTD_H 1
-#define HAVE_UTIME 1
-#define HAVE_UTIME_H 1
-#define HAVE_WRITABLE_ARGV 1
-#define HAVE_SYS_IOCTL_H 1
-
-#define NEED_MALLOC_H 1
-
-#define SIZEOF_INT 4
-#define SIZEOF_SIZE_T 4
-
-#ifndef SIZEOF_CURL_OFF_T
-#define SIZEOF_CURL_OFF_T 8
-#endif
-
-#define USE_MANUAL 1
-#define CURL_DISABLE_LDAP 1
-
-#ifndef CURL_OS
-#define CURL_OS "AmigaOS"
-#endif
-
-#define PACKAGE "curl"
-#define PACKAGE_BUGREPORT "a suitable mailing list: https://curl.se/mail/"
-#define PACKAGE_NAME "curl"
-#define PACKAGE_STRING "curl -"
-#define PACKAGE_TARNAME "curl"
-#define PACKAGE_VERSION "-"
-
-#if defined(USE_AMISSL)
-#define CURL_CA_PATH "AmiSSL:Certs"
-#elif defined(__MORPHOS__)
-#define CURL_CA_BUNDLE "MOSSYS:Data/SSL/curl-ca-bundle.crt"
-#else
-#define CURL_CA_BUNDLE "s:curl-ca-bundle.crt"
-#endif
-
-#define STDC_HEADERS 1
-
-#define in_addr_t int
-
-#ifndef F_OK
-# define F_OK 0
-#endif
-
-#ifndef O_RDONLY
-# define O_RDONLY 0x0000
-#endif
-
-#ifndef LONG_MAX
-# define LONG_MAX 0x7fffffffL
-#endif
-
-#ifndef LONG_MIN
-# define LONG_MIN (-0x7fffffffL-1)
-#endif
-
-#define HAVE_RECV 1
-#define RECV_TYPE_ARG1 long
-#define RECV_TYPE_ARG2 char *
-#define RECV_TYPE_ARG3 long
-#define RECV_TYPE_ARG4 long
-#define RECV_TYPE_RETV long
-
-#define HAVE_SEND 1
-#define SEND_TYPE_ARG1 int
-#define SEND_QUAL_ARG2 const
-#define SEND_TYPE_ARG2 char *
-#define SEND_TYPE_ARG3 int
-#define SEND_TYPE_ARG4 int
-#define SEND_TYPE_RETV int
-
-#endif /* __AMIGA__ */
-#endif /* HEADER_CURL_CONFIG_AMIGAOS_H */
diff --git a/libs/libcurl/src/config-dos.h b/libs/libcurl/src/config-dos.h
deleted file mode 100644
index 2c326f2049..0000000000
--- a/libs/libcurl/src/config-dos.h
+++ /dev/null
@@ -1,138 +0,0 @@
-#ifndef HEADER_CURL_CONFIG_DOS_H
-#define HEADER_CURL_CONFIG_DOS_H
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-
-/* ================================================================ */
-/* lib/config-dos.h - Hand crafted config file for DOS */
-/* ================================================================ */
-
-#ifndef CURL_OS
-#if defined(DJGPP)
- #define CURL_OS "MSDOS/djgpp"
-#elif defined(__HIGHC__)
- #define CURL_OS "MSDOS/HighC"
-#else
- #define CURL_OS "MSDOS/?"
-#endif
-#endif
-
-#define PACKAGE "curl"
-
-#define USE_MANUAL 1
-
-#define HAVE_ARPA_INET_H 1
-#define HAVE_FCNTL_H 1
-#define HAVE_FREEADDRINFO 1
-#define HAVE_GETADDRINFO 1
-#define HAVE_GETTIMEOFDAY 1
-#define HAVE_IO_H 1
-#define HAVE_IOCTL_FIONBIO 1
-#define HAVE_IOCTLSOCKET 1
-#define HAVE_IOCTLSOCKET_FIONBIO 1
-#define HAVE_LOCALE_H 1
-#define HAVE_LONGLONG 1
-#define HAVE_NETDB_H 1
-#define HAVE_NETINET_IN_H 1
-#define HAVE_NETINET_TCP_H 1
-#define HAVE_NET_IF_H 1
-#define HAVE_RECV 1
-#define HAVE_SELECT 1
-#define HAVE_SEND 1
-#define HAVE_SETLOCALE 1
-#define HAVE_SETMODE 1
-#define HAVE_SIGNAL 1
-#define HAVE_SOCKET 1
-#define HAVE_STRDUP 1
-#define HAVE_STRICMP 1
-#define HAVE_STRTOLL 1
-#define HAVE_STRUCT_TIMEVAL 1
-#define HAVE_SYS_IOCTL_H 1
-#define HAVE_SYS_SOCKET_H 1
-#define HAVE_SYS_STAT_H 1
-#define HAVE_SYS_TYPES_H 1
-#define HAVE_UNISTD_H 1
-
-#define NEED_MALLOC_H 1
-
-#define SIZEOF_INT 4
-#define SIZEOF_LONG 4
-#define SIZEOF_SIZE_T 4
-#define SIZEOF_CURL_OFF_T 8
-#define STDC_HEADERS 1
-
-/* Qualifiers for send() and recv() */
-
-#define SEND_TYPE_ARG1 int
-#define SEND_QUAL_ARG2 const
-#define SEND_TYPE_ARG2 void *
-#define SEND_TYPE_ARG3 int
-#define SEND_TYPE_ARG4 int
-#define SEND_TYPE_RETV int
-
-#define RECV_TYPE_ARG1 int
-#define RECV_TYPE_ARG2 void *
-#define RECV_TYPE_ARG3 int
-#define RECV_TYPE_ARG4 int
-#define RECV_TYPE_RETV int
-
-#define BSD
-
-/* CURLDEBUG definition enables memory tracking */
-/* #define CURLDEBUG */
-
-/* to disable LDAP */
-#define CURL_DISABLE_LDAP 1
-
-#define in_addr_t u_long
-
-#if defined(__HIGHC__) || \
- (defined(__GNUC__) && (__GNUC__ < 4))
- #define ssize_t int
-#endif
-
-/* Target HAVE_x section */
-
-#if defined(DJGPP)
- #define HAVE_BASENAME 1
- #define HAVE_STRCASECMP 1
- #define HAVE_SIGACTION 1
- #define HAVE_SIGSETJMP 1
- #define HAVE_SYS_TIME_H 1
- #define HAVE_TERMIOS_H 1
-
-#elif defined(__HIGHC__)
- #define HAVE_SYS_TIME_H 1
- #define strerror(e) strerror_s_((e))
-#endif
-
-#ifdef MSDOS /* Watt-32 */
- #define HAVE_CLOSE_S 1
-#endif
-
-#undef word
-#undef byte
-
-#endif /* HEADER_CURL_CONFIG_DOS_H */
diff --git a/libs/libcurl/src/config-mac.h b/libs/libcurl/src/config-mac.h
index f167831ccb..cb67d3321d 100644
--- a/libs/libcurl/src/config-mac.h
+++ b/libs/libcurl/src/config-mac.h
@@ -65,10 +65,6 @@
#define HAVE_SIGACTION 1
-#ifdef MACOS_SSL_SUPPORT
-# define USE_OPENSSL 1
-#endif
-
#define CURL_DISABLE_LDAP 1
#define HAVE_IOCTL_FIONBIO 1
@@ -97,7 +93,4 @@
#define SEND_TYPE_ARG4 int
#define SEND_TYPE_RETV ssize_t
-#define HAVE_EXTRA_STRICMP_H 1
-#define HAVE_EXTRA_STRDUP_H 1
-
#endif /* HEADER_CURL_CONFIG_MAC_H */
diff --git a/libs/libcurl/src/config-os400.h b/libs/libcurl/src/config-os400.h
index aed0f1490d..ca60ad334c 100644
--- a/libs/libcurl/src/config-os400.h
+++ b/libs/libcurl/src/config-os400.h
@@ -30,11 +30,6 @@
#pragma enum(int)
-#undef PACKAGE
-
-/* Version number of this archive. */
-#undef VERSION
-
/* Define cpu-machine-OS */
#ifndef CURL_OS
#define CURL_OS "OS/400"
@@ -110,9 +105,6 @@
/* Define if you have the GNU gssapi libraries */
#undef HAVE_GSSGNU
-/* Define if you need the malloc.h header file even with stdlib.h */
-/* #define NEED_MALLOC_H 1 */
-
/* Define if you have the <netdb.h> header file. */
#define HAVE_NETDB_H
@@ -160,12 +152,6 @@
/* Define if you have the <stropts.h> header file. */
#undef HAVE_STROPTS_H
-/* Define if you have the `strtok_r' function. */
-#define HAVE_STRTOK_R
-
-/* Define if you have the `strtoll' function. */
-#undef HAVE_STRTOLL /* Allows ASCII compile on V5R1. */
-
/* Define if you have the <sys/param.h> header file. */
#define HAVE_SYS_PARAM_H
@@ -202,9 +188,6 @@
/* Define if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H
-/* Name of package */
-#undef PACKAGE
-
/* The size of `int', as computed by sizeof. */
#define SIZEOF_INT 4
@@ -245,9 +228,6 @@
/* Define to empty if `const' does not conform to ANSI C. */
#undef const
-/* type to use in place of in_addr_t if not defined */
-#define in_addr_t unsigned long
-
/* Define to `unsigned' if <sys/types.h> does not define. */
#undef size_t
diff --git a/libs/libcurl/src/config-plan9.h b/libs/libcurl/src/config-plan9.h
index 50bb1081dd..191c7ce6e0 100644
--- a/libs/libcurl/src/config-plan9.h
+++ b/libs/libcurl/src/config-plan9.h
@@ -35,13 +35,6 @@
#ifndef CURL_OS
#define CURL_OS "plan9"
#endif
-#define PACKAGE "curl"
-#define PACKAGE_NAME "curl"
-#define PACKAGE_BUGREPORT "a suitable mailing list: https://curl.se/mail/"
-#define PACKAGE_STRING "curl -"
-#define PACKAGE_TARNAME "curl"
-#define PACKAGE_VERSION "-"
-#define VERSION "0.0.0" /* TODO */
#define STDC_HEADERS 1
@@ -113,12 +106,9 @@
#define HAVE_SIGSETJMP 1
#define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1
#define HAVE_SOCKET 1
-#define HAVE_SSL_GET_SHUTDOWN 1
#define HAVE_STDBOOL_H 1
#define HAVE_STRCASECMP 1
#define HAVE_STRDUP 1
-#define HAVE_STRTOK_R 1
-#define HAVE_STRTOLL 1
#define HAVE_STRUCT_TIMEVAL 1
#define HAVE_SYS_IOCTL_H 1
#define HAVE_SYS_PARAM_H 1
diff --git a/libs/libcurl/src/config-riscos.h b/libs/libcurl/src/config-riscos.h
index 8d6fbb6ea5..cb24c34f6d 100644
--- a/libs/libcurl/src/config-riscos.h
+++ b/libs/libcurl/src/config-riscos.h
@@ -28,12 +28,6 @@
/* Hand crafted config file for RISC OS */
/* ================================================================ */
-/* Name of this package! */
-#undef PACKAGE
-
-/* Version number of this archive. */
-#undef VERSION
-
/* Define cpu-machine-OS */
#ifndef CURL_OS
#define CURL_OS "ARM-RISC OS"
@@ -105,9 +99,6 @@
/* Define if you have the <io.h> header file. */
#undef HAVE_IO_H
-/* Define if you need the malloc.h header file even with stdlib.h */
-/* #define NEED_MALLOC_H 1 */
-
/* Define if you have the <netdb.h> header file. */
#define HAVE_NETDB_H
@@ -147,12 +138,6 @@
/* Define if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
-/* Define if you have the `strtok_r' function. */
-#undef HAVE_STRTOK_R
-
-/* Define if you have the `strtoll' function. */
-#undef HAVE_STRTOLL
-
/* Define if you have the <sys/param.h> header file. */
#undef HAVE_SYS_PARAM_H
@@ -183,9 +168,6 @@
/* Define if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H
-/* Name of package */
-#undef PACKAGE
-
/* The size of `int', as computed by sizeof. */
#define SIZEOF_INT 4
diff --git a/libs/libcurl/src/config-win32.h b/libs/libcurl/src/config-win32.h
index 7b4c9d2748..f79cf315cf 100644
--- a/libs/libcurl/src/config-win32.h
+++ b/libs/libcurl/src/config-win32.h
@@ -35,17 +35,18 @@
/* Define if you have the <arpa/inet.h> header file. */
/* #define HAVE_ARPA_INET_H 1 */
+#ifndef UNDER_CE
+
/* Define if you have the <fcntl.h> header file. */
-#define HAVE_FCNTL_H 1
+#define HAVE_FCNTL_H 1 /* exists on __MINGW32CE__ */
/* Define if you have the <io.h> header file. */
-#define HAVE_IO_H 1
+#define HAVE_IO_H 1 /* exists on __MINGW32CE__ */
/* Define if you have the <locale.h> header file. */
#define HAVE_LOCALE_H 1
-/* Define if you need <malloc.h> header even with <stdlib.h> header file. */
-#define NEED_MALLOC_H 1
+#endif
/* Define if you have the <netdb.h> header file. */
/* #define HAVE_NETDB_H 1 */
@@ -54,12 +55,19 @@
/* #define HAVE_NETINET_IN_H 1 */
/* Define to 1 if you have the <stdbool.h> header file. */
+#ifndef UNDER_CE
#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) || defined(__MINGW32__)
-#define HAVE_STDBOOL_H 1
+#define HAVE_STDBOOL_H 1 /* exists on __MINGW32CE__ */
+#endif
+#endif
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#if (defined(_MSC_VER) && (_MSC_VER >= 1600)) || defined(__MINGW32__)
+#define HAVE_STDINT_H 1
#endif
/* Define if you have the <sys/param.h> header file. */
-#if defined(__MINGW32__)
+#ifdef __MINGW32__
#define HAVE_SYS_PARAM_H 1
#endif
@@ -76,7 +84,7 @@
#define HAVE_SYS_STAT_H 1
/* Define if you have the <sys/time.h> header file. */
-#if defined(__MINGW32__)
+#ifdef __MINGW32__
#define HAVE_SYS_TIME_H 1
#endif
@@ -93,12 +101,12 @@
/* #define HAVE_TERMIOS_H 1 */
/* Define if you have the <unistd.h> header file. */
-#if defined(__MINGW32__)
+#ifdef __MINGW32__
#define HAVE_UNISTD_H 1
#endif
/* Define to 1 if you have the <libgen.h> header file. */
-#if defined(__MINGW32__)
+#ifdef __MINGW32__
#define HAVE_LIBGEN_H 1
#endif
@@ -110,8 +118,10 @@
#define STDC_HEADERS 1
/* Define to 1 if bool is an available type. */
+#ifndef UNDER_CE
#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) || defined(__MINGW32__)
-#define HAVE_BOOL_T 1
+#define HAVE_BOOL_T 1 /* exists on __MINGW32CE__ */
+#endif
#endif
/* ---------------------------------------------------------------- */
@@ -122,7 +132,7 @@
#define HAVE_CLOSESOCKET 1
/* Define if you have the ftruncate function. */
-#if defined(__MINGW32__)
+#ifdef __MINGW32__
#define HAVE_FTRUNCATE 1
#endif
@@ -136,7 +146,7 @@
#define HAVE_GETHOSTNAME 1
/* Define if you have the gettimeofday function. */
-#if defined(__MINGW32__)
+#ifdef __MINGW32__
#define HAVE_GETTIMEOFDAY 1
#endif
@@ -153,6 +163,7 @@
#define USE_LIBSSH2 1
#define HAVE_LIBSSH2_H 1
+#ifndef UNDER_CE
/* Define if you have the setlocale function. */
#define HAVE_SETLOCALE 1
@@ -161,26 +172,14 @@
/* Define if you have the _setmode function. */
#define HAVE__SETMODE 1
+#endif
/* Define if you have the socket function. */
#define HAVE_SOCKET 1
-/* Define if you have the strcasecmp function. */
-#if defined(__MINGW32__)
-#define HAVE_STRCASECMP 1
-#endif
-
/* Define if you have the strdup function. */
#define HAVE_STRDUP 1
-/* Define if you have the stricmp function. */
-#define HAVE_STRICMP 1
-
-/* Define if you have the strtoll function. */
-#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) || defined(__MINGW32__)
-#define HAVE_STRTOLL 1
-#endif
-
/* Define if you have the utime function. */
#define HAVE_UTIME 1
@@ -228,36 +227,31 @@
#define HAVE_SNPRINTF 1
#endif
-#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600 /* Vista */
-/* Define to 1 if you have a IPv6 capable working inet_ntop function. */
+/* Vista */
+#if (defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600) && !defined(UNDER_CE)
+/* Define to 1 if you have an IPv6 capable working inet_ntop function. */
#define HAVE_INET_NTOP 1
-/* Define to 1 if you have a IPv6 capable working inet_pton function. */
+/* Define to 1 if you have an IPv6 capable working inet_pton function. */
#define HAVE_INET_PTON 1
#endif
/* Define to 1 if you have the `basename' function. */
-#if defined(__MINGW32__)
+#ifdef __MINGW32__
#define HAVE_BASENAME 1
#endif
-/* Define to 1 if you have the strtok_r function. */
-#if defined(__MINGW32__)
-#define HAVE_STRTOK_R 1
-#endif
-
/* Define to 1 if you have the signal function. */
+#ifndef UNDER_CE
#define HAVE_SIGNAL 1
+#endif
/* ---------------------------------------------------------------- */
/* TYPEDEF REPLACEMENTS */
/* ---------------------------------------------------------------- */
-/* Define if in_addr_t is not an available 'typedefed' type. */
-#define in_addr_t unsigned long
-
/* Define if ssize_t is not an available 'typedefed' type. */
#ifndef _SSIZE_T_DEFINED
-# if defined(__MINGW32__)
+# ifdef __MINGW32__
# elif defined(_WIN64)
# define _SSIZE_T_DEFINED
# define ssize_t __int64
@@ -281,7 +275,7 @@
#define SIZEOF_LONG 4
/* Define to the size of `size_t', as computed by sizeof. */
-#if defined(_WIN64)
+#ifdef _WIN64
# define SIZEOF_SIZE_T 8
#else
# define SIZEOF_SIZE_T 4
@@ -304,19 +298,12 @@
/* #undef HAVE_GMTIME_R */
/* Define if the compiler supports the 'long long' data type. */
-#if (defined(_MSC_VER) && (_MSC_VER >= 1310)) || defined(__MINGW32__)
+#if defined(_MSC_VER) || defined(__MINGW32__)
#define HAVE_LONGLONG 1
#endif
-/* Define to avoid VS2005 complaining about portable C functions. */
-#if defined(_MSC_VER) && (_MSC_VER >= 1400)
-#define _CRT_SECURE_NO_DEPRECATE 1
-#define _CRT_NONSTDC_NO_DEPRECATE 1
-#endif
-
-/* mingw-w64 and visual studio >= 2005 (MSVCR80)
- all default to 64-bit time_t unless _USE_32BIT_TIME_T is defined */
-#if (defined(_MSC_VER) && (_MSC_VER >= 1400)) || defined(__MINGW32__)
+/* Default to 64-bit time_t unless _USE_32BIT_TIME_T is defined */
+#if defined(_MSC_VER) || defined(__MINGW32__)
# ifndef _USE_32BIT_TIME_T
# define SIZEOF_TIME_T 8
# else
@@ -324,8 +311,10 @@
# endif
#endif
+#ifndef UNDER_CE
+
/* Define some minimum and default build targets for Visual Studio */
-#if defined(_MSC_VER)
+#ifdef _MSC_VER
/* Officially, Microsoft's Windows SDK versions 6.X does not support Windows
2000 as a supported build target. VS2008 default installations provides
an embedded Windows SDK v6.0A along with the claim that Windows 2000 is a
@@ -336,7 +325,7 @@
/* The minimum build target for VS2012 is Vista unless Update 1 is installed
and the v110_xp toolset is chosen. */
-# if defined(_USING_V110_SDK71_)
+# ifdef _USING_V110_SDK71_
# define VS2012_MIN_TARGET 0x0501
# else
# define VS2012_MIN_TARGET 0x0600
@@ -348,7 +337,7 @@
/* VS2012 default build target is Windows Vista unless Update 1 is installed
and the v110_xp toolset is chosen. */
-# if defined(_USING_V110_SDK71_)
+# ifdef _USING_V110_SDK71_
# define VS2012_DEF_TARGET 0x0501
# else
# define VS2012_DEF_TARGET 0x0600
@@ -356,12 +345,12 @@
#endif
/* VS2008 default target settings and minimum build target check. */
-#if defined(_MSC_VER) && (_MSC_VER >= 1500) && (_MSC_VER <= 1600)
+#if defined(_MSC_VER) && (_MSC_VER <= 1600)
# ifndef _WIN32_WINNT
-# define _WIN32_WINNT VS2008_DEF_TARGET
+# define _WIN32_WINNT VS2008_DEF_TARGET
# endif
# ifndef WINVER
-# define WINVER VS2008_DEF_TARGET
+# define WINVER VS2008_DEF_TARGET
# endif
# if (_WIN32_WINNT < VS2008_MIN_TARGET) || (WINVER < VS2008_MIN_TARGET)
# error VS2008 does not support Windows build targets prior to Windows 2000
@@ -371,13 +360,13 @@
/* VS2012 default target settings and minimum build target check. */
#if defined(_MSC_VER) && (_MSC_VER >= 1700)
# ifndef _WIN32_WINNT
-# define _WIN32_WINNT VS2012_DEF_TARGET
+# define _WIN32_WINNT VS2012_DEF_TARGET
# endif
# ifndef WINVER
-# define WINVER VS2012_DEF_TARGET
+# define WINVER VS2012_DEF_TARGET
# endif
# if (_WIN32_WINNT < VS2012_MIN_TARGET) || (WINVER < VS2012_MIN_TARGET)
-# if defined(_USING_V110_SDK71_)
+# ifdef _USING_V110_SDK71_
# error VS2012 does not support Windows build targets prior to Windows XP
# else
# error VS2012 does not support Windows build targets prior to Windows \
@@ -386,10 +375,14 @@ Vista
# endif
#endif
+#endif /* UNDER_CE */
+
/* Windows XP is required for freeaddrinfo, getaddrinfo */
+#ifndef UNDER_CE
#define HAVE_FREEADDRINFO 1
#define HAVE_GETADDRINFO 1
#define HAVE_GETADDRINFO_THREADSAFE 1
+#endif
/* ---------------------------------------------------------------- */
/* STRUCT RELATED */
@@ -408,33 +401,18 @@ Vista
/* LARGE FILE SUPPORT */
/* ---------------------------------------------------------------- */
-#if defined(_MSC_VER) && !defined(_WIN32_WCE)
-# if (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64)
-# define USE_WIN32_LARGE_FILES
-# else
-# define USE_WIN32_SMALL_FILES
-# endif
-#endif
+#ifndef UNDER_CE
-#if defined(__MINGW32__) && !defined(USE_WIN32_LARGE_FILES)
+#if defined(_MSC_VER) || defined(__MINGW32__)
# define USE_WIN32_LARGE_FILES
-#endif
-
-#if !defined(USE_WIN32_LARGE_FILES) && !defined(USE_WIN32_SMALL_FILES)
-# define USE_WIN32_SMALL_FILES
-#endif
-
/* Number of bits in a file offset, on hosts where this is settable. */
-#if defined(USE_WIN32_LARGE_FILES) && defined(__MINGW32__)
-# ifndef _FILE_OFFSET_BITS
-# define _FILE_OFFSET_BITS 64
+# ifdef __MINGW32__
+# ifndef _FILE_OFFSET_BITS
+# define _FILE_OFFSET_BITS 64
+# endif
# endif
#endif
-#ifdef USE_WIN32_LARGE_FILES
-#define HAVE__FSEEKI64
-#endif
-
/* Define to the size of `off_t', as computed by sizeof. */
#if defined(__MINGW32__) && \
defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64)
@@ -443,6 +421,8 @@ Vista
# define SIZEOF_OFF_T 4
#endif
+#endif /* UNDER_CE */
+
/* ---------------------------------------------------------------- */
/* DNS RESOLVER SPECIALTY */
/* ---------------------------------------------------------------- */
@@ -468,14 +448,10 @@ Vista
/* LDAP SUPPORT */
/* ---------------------------------------------------------------- */
-#if defined(CURL_HAS_NOVELL_LDAPSDK)
-#undef USE_WIN32_LDAP
-#define HAVE_LDAP_SSL_H 1
-#define HAVE_LDAP_URL_PARSE 1
-#elif defined(CURL_HAS_OPENLDAP_LDAPSDK)
+#ifdef CURL_HAS_OPENLDAP_LDAPSDK
#undef USE_WIN32_LDAP
#define HAVE_LDAP_URL_PARSE 1
-#else
+#elif !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE)
#undef HAVE_LDAP_URL_PARSE
#define HAVE_LDAP_SSL 1
#define USE_WIN32_LDAP 1
@@ -485,12 +461,14 @@ Vista
#define USE_OPENSSL 1
/* Define to use the Windows crypto library. */
-#if !defined(CURL_WINDOWS_UWP)
+#ifndef CURL_WINDOWS_UWP
#define USE_WIN32_CRYPTO
#endif
/* Define to use Unix sockets. */
+#ifndef UNDER_CE
#define USE_UNIX_SOCKETS
+#endif
/* ---------------------------------------------------------------- */
/* ADDITIONAL DEFINITIONS */
@@ -498,25 +476,51 @@ Vista
/* Define cpu-machine-OS */
#ifndef CURL_OS
-#if defined(_M_IX86) || defined(__i386__) /* x86 (MSVC or gcc) */
-#define CURL_OS "i386-pc-win32"
-#elif defined(_M_X64) || defined(__x86_64__) /* x86_64 (MSVC >=2005 or gcc) */
-#define CURL_OS "x86_64-pc-win32"
-#elif defined(_M_IA64) || defined(__ia64__) /* Itanium */
-#define CURL_OS "ia64-pc-win32"
-#elif defined(_M_ARM_NT) || defined(__arm__) /* ARMv7-Thumb2 (Windows RT) */
-#define CURL_OS "thumbv7a-pc-win32"
-#elif defined(_M_ARM64) || defined(__aarch64__) /* ARM64 (Windows 10) */
-#define CURL_OS "aarch64-pc-win32"
-#else
-#define CURL_OS "unknown-pc-win32"
+# ifdef UNDER_CE
+# ifdef _M_ARM
+# define CURL_OS "arm-pc-win32ce"
+# else
+# define CURL_OS "i386-pc-win32ce"
+# endif
+# else /* !UNDER_CE */
+# if defined(_M_IX86) || defined(__i386__) /* x86 (MSVC or gcc) */
+# define CURL_OS "i386-pc-win32"
+# elif defined(_M_X64) || defined(__x86_64__) /* x86_64 (VS2005+ or gcc) */
+# define CURL_OS "x86_64-pc-win32"
+# elif defined(_M_IA64) || defined(__ia64__) /* Itanium */
+# define CURL_OS "ia64-pc-win32"
+# elif defined(_M_ARM_NT) || defined(__arm__) /* ARMv7-Thumb2 */
+# define CURL_OS "thumbv7a-pc-win32"
+# elif defined(_M_ARM64) || defined(__aarch64__) /* ARM64 (Windows 10) */
+# define CURL_OS "aarch64-pc-win32"
+# else
+# define CURL_OS "unknown-pc-win32"
+# endif
+# endif /* UNDER_CE */
+#endif /* !CURL_OS */
+
+/* ---------------------------------------------------------------- */
+/* Windows CE */
+/* ---------------------------------------------------------------- */
+
+#ifdef UNDER_CE
+
+#ifndef UNICODE
+#define UNICODE
#endif
+
+#ifndef _UNICODE
+#define _UNICODE
#endif
-/* Name of package */
-#define PACKAGE "curl"
+#define CURL_DISABLE_FILE 1
+#define CURL_DISABLE_TELNET 1
+#define CURL_DISABLE_LDAP 1
+
+#ifndef _MSC_VER
+extern int stat(const char *path, struct stat *buffer);
+#endif
-/* If you want to build curl with the built-in manual */
-#define USE_MANUAL 1
+#endif /* UNDER_CE */
#endif /* HEADER_CURL_CONFIG_WIN32_H */
diff --git a/libs/libcurl/src/config-win32ce.h b/libs/libcurl/src/config-win32ce.h
deleted file mode 100644
index 2ea8dceded..0000000000
--- a/libs/libcurl/src/config-win32ce.h
+++ /dev/null
@@ -1,303 +0,0 @@
-#ifndef HEADER_CURL_CONFIG_WIN32CE_H
-#define HEADER_CURL_CONFIG_WIN32CE_H
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-/* ================================================================ */
-/* lib/config-win32ce.h - Hand crafted config file for Windows CE */
-/* ================================================================ */
-
-/* ---------------------------------------------------------------- */
-/* HEADER FILES */
-/* ---------------------------------------------------------------- */
-
-/* Define if you have the <arpa/inet.h> header file. */
-/* #define HAVE_ARPA_INET_H 1 */
-
-/* Define if you have the <fcntl.h> header file. */
-#define HAVE_FCNTL_H 1
-
-/* Define if you have the <io.h> header file. */
-#define HAVE_IO_H 1
-
-/* Define if you need the malloc.h header file even with stdlib.h */
-#define NEED_MALLOC_H 1
-
-/* Define if you have the <netdb.h> header file. */
-/* #define HAVE_NETDB_H 1 */
-
-/* Define if you have the <netinet/in.h> header file. */
-/* #define HAVE_NETINET_IN_H 1 */
-
-/* Define if you have the <sys/param.h> header file. */
-/* #define HAVE_SYS_PARAM_H 1 */
-
-/* Define if you have the <sys/select.h> header file. */
-/* #define HAVE_SYS_SELECT_H 1 */
-
-/* Define if you have the <sys/socket.h> header file. */
-/* #define HAVE_SYS_SOCKET_H 1 */
-
-/* Define if you have the <sys/sockio.h> header file. */
-/* #define HAVE_SYS_SOCKIO_H 1 */
-
-/* Define if you have the <sys/stat.h> header file. */
-#define HAVE_SYS_STAT_H 1
-
-/* Define if you have the <sys/time.h> header file */
-/* #define HAVE_SYS_TIME_H 1 */
-
-/* Define if you have the <sys/types.h> header file. */
-/* #define HAVE_SYS_TYPES_H 1 */
-
-/* Define if you have the <sys/utime.h> header file */
-#define HAVE_SYS_UTIME_H 1
-
-/* Define if you have the <termio.h> header file. */
-/* #define HAVE_TERMIO_H 1 */
-
-/* Define if you have the <termios.h> header file. */
-/* #define HAVE_TERMIOS_H 1 */
-
-/* Define if you have the <unistd.h> header file. */
-#if defined(__MINGW32__)
-#define HAVE_UNISTD_H 1
-#endif
-
-/* ---------------------------------------------------------------- */
-/* OTHER HEADER INFO */
-/* ---------------------------------------------------------------- */
-
-/* Define if you have the ANSI C header files. */
-#define STDC_HEADERS 1
-
-/* ---------------------------------------------------------------- */
-/* FUNCTIONS */
-/* ---------------------------------------------------------------- */
-
-/* Define if you have the closesocket function. */
-#define HAVE_CLOSESOCKET 1
-
-/* Define if you have the gethostname function. */
-#define HAVE_GETHOSTNAME 1
-
-/* Define if you have the gettimeofday function. */
-/* #define HAVE_GETTIMEOFDAY 1 */
-
-/* Define if you have the ioctlsocket function. */
-#define HAVE_IOCTLSOCKET 1
-
-/* Define if you have a working ioctlsocket FIONBIO function. */
-#define HAVE_IOCTLSOCKET_FIONBIO 1
-
-/* Define if you have the select function. */
-#define HAVE_SELECT 1
-
-/* Define if you have the socket function. */
-#define HAVE_SOCKET 1
-
-/* Define if you have the strcasecmp function. */
-/* #define HAVE_STRCASECMP 1 */
-
-/* Define if you have the strdup function. */
-/* #define HAVE_STRDUP 1 */
-
-/* Define if you have the stricmp function. */
-/* #define HAVE_STRICMP 1 */
-
-/* Define if you have the strtoll function. */
-#if defined(__MINGW32__)
-#define HAVE_STRTOLL 1
-#endif
-
-/* Define if you have the utime function */
-#define HAVE_UTIME 1
-
-/* Define if you have the recv function. */
-#define HAVE_RECV 1
-
-/* Define to the type of arg 1 for recv. */
-#define RECV_TYPE_ARG1 SOCKET
-
-/* Define to the type of arg 2 for recv. */
-#define RECV_TYPE_ARG2 char *
-
-/* Define to the type of arg 3 for recv. */
-#define RECV_TYPE_ARG3 int
-
-/* Define to the type of arg 4 for recv. */
-#define RECV_TYPE_ARG4 int
-
-/* Define to the function return type for recv. */
-#define RECV_TYPE_RETV int
-
-/* Define if you have the send function. */
-#define HAVE_SEND 1
-
-/* Define to the type of arg 1 for send. */
-#define SEND_TYPE_ARG1 SOCKET
-
-/* Define to the type qualifier of arg 2 for send. */
-#define SEND_QUAL_ARG2 const
-
-/* Define to the type of arg 2 for send. */
-#define SEND_TYPE_ARG2 char *
-
-/* Define to the type of arg 3 for send. */
-#define SEND_TYPE_ARG3 int
-
-/* Define to the type of arg 4 for send. */
-#define SEND_TYPE_ARG4 int
-
-/* Define to the function return type for send. */
-#define SEND_TYPE_RETV int
-
-/* ---------------------------------------------------------------- */
-/* TYPEDEF REPLACEMENTS */
-/* ---------------------------------------------------------------- */
-
-/* Define this if in_addr_t is not an available 'typedefed' type */
-#define in_addr_t unsigned long
-
-/* Define ssize_t if it is not an available 'typedefed' type */
-#if defined(_WIN64)
-#define ssize_t __int64
-#else
-#define ssize_t int
-#endif
-
-/* ---------------------------------------------------------------- */
-/* TYPE SIZES */
-/* ---------------------------------------------------------------- */
-
-/* The size of `int', as computed by sizeof. */
-#define SIZEOF_INT 4
-
-/* The size of `long long', as computed by sizeof. */
-/* #define SIZEOF_LONG_LONG 8 */
-
-/* Define to the size of `long', as computed by sizeof. */
-#define SIZEOF_LONG 4
-
-/* The size of `size_t', as computed by sizeof. */
-#if defined(_WIN64)
-# define SIZEOF_SIZE_T 8
-#else
-# define SIZEOF_SIZE_T 4
-#endif
-
-/* ---------------------------------------------------------------- */
-/* STRUCT RELATED */
-/* ---------------------------------------------------------------- */
-
-/* Define this if you have struct sockaddr_storage */
-/* #define HAVE_STRUCT_SOCKADDR_STORAGE 1 */
-
-/* Define this if you have struct timeval */
-#define HAVE_STRUCT_TIMEVAL 1
-
-/* Define this if struct sockaddr_in6 has the sin6_scope_id member */
-#define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1
-
-/* ---------------------------------------------------------------- */
-/* COMPILER SPECIFIC */
-/* ---------------------------------------------------------------- */
-
-/* Undef keyword 'const' if it does not work. */
-/* #undef const */
-
-/* Define to avoid VS2005 complaining about portable C functions */
-#if defined(_MSC_VER) && (_MSC_VER >= 1400)
-#define _CRT_SECURE_NO_DEPRECATE 1
-#define _CRT_NONSTDC_NO_DEPRECATE 1
-#endif
-
-/* VS2005 and later default size for time_t is 64-bit, unless */
-/* _USE_32BIT_TIME_T has been defined to get a 32-bit time_t. */
-#if defined(_MSC_VER) && (_MSC_VER >= 1400)
-# ifndef _USE_32BIT_TIME_T
-# define SIZEOF_TIME_T 8
-# else
-# define SIZEOF_TIME_T 4
-# endif
-#endif
-
-/* ---------------------------------------------------------------- */
-/* LARGE FILE SUPPORT */
-/* ---------------------------------------------------------------- */
-
-#if defined(_MSC_VER) && !defined(_WIN32_WCE)
-# if (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64)
-# define USE_WIN32_LARGE_FILES
-# else
-# define USE_WIN32_SMALL_FILES
-# endif
-#endif
-
-#if !defined(USE_WIN32_LARGE_FILES) && !defined(USE_WIN32_SMALL_FILES)
-# define USE_WIN32_SMALL_FILES
-#endif
-
-/* ---------------------------------------------------------------- */
-/* LDAP SUPPORT */
-/* ---------------------------------------------------------------- */
-
-#define USE_WIN32_LDAP 1
-#undef HAVE_LDAP_URL_PARSE
-
-/* ---------------------------------------------------------------- */
-/* ADDITIONAL DEFINITIONS */
-/* ---------------------------------------------------------------- */
-
-/* Define cpu-machine-OS */
-#ifndef CURL_OS
-#define CURL_OS "i386-pc-win32ce"
-#endif
-
-/* Name of package */
-#define PACKAGE "curl"
-
-/* ---------------------------------------------------------------- */
-/* Windows CE */
-/* ---------------------------------------------------------------- */
-
-#ifndef UNICODE
-# define UNICODE
-#endif
-
-#ifndef _UNICODE
-# define _UNICODE
-#endif
-
-#define CURL_DISABLE_FILE 1
-#define CURL_DISABLE_TELNET 1
-#define CURL_DISABLE_LDAP 1
-
-#define ENOSPC 1
-#define ENOMEM 2
-#define EAGAIN 3
-
-extern int stat(const char *path, struct stat *buffer);
-
-#endif /* HEADER_CURL_CONFIG_WIN32CE_H */
diff --git a/libs/libcurl/src/conncache.c b/libs/libcurl/src/conncache.c
index 17431a181c..1de4a86b7d 100644
--- a/libs/libcurl/src/conncache.c
+++ b/libs/libcurl/src/conncache.c
@@ -32,7 +32,9 @@
#include "cfilters.h"
#include "progress.h"
#include "multiif.h"
+#include "multi_ev.h"
#include "sendf.h"
+#include "cshutdn.h"
#include "conncache.h"
#include "http_negotiate.h"
#include "http_ntlm.h"
@@ -41,6 +43,8 @@
#include "connect.h"
#include "select.h"
#include "strcase.h"
+#include "curlx/strparse.h"
+#include "uint-table.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -50,24 +54,24 @@
#define CPOOL_IS_LOCKED(c) ((c) && (c)->locked)
-#define CPOOL_LOCK(c) \
+#define CPOOL_LOCK(c,d) \
do { \
if((c)) { \
if(CURL_SHARE_KEEP_CONNECT((c)->share)) \
- Curl_share_lock(((c)->idata), CURL_LOCK_DATA_CONNECT, \
+ Curl_share_lock((d), CURL_LOCK_DATA_CONNECT, \
CURL_LOCK_ACCESS_SINGLE); \
DEBUGASSERT(!(c)->locked); \
(c)->locked = TRUE; \
} \
} while(0)
-#define CPOOL_UNLOCK(c) \
+#define CPOOL_UNLOCK(c,d) \
do { \
if((c)) { \
DEBUGASSERT((c)->locked); \
(c)->locked = FALSE; \
if(CURL_SHARE_KEEP_CONNECT((c)->share)) \
- Curl_share_unlock((c)->idata, CURL_LOCK_DATA_CONNECT); \
+ Curl_share_unlock((d), CURL_LOCK_DATA_CONNECT); \
} \
} while(0)
@@ -84,33 +88,18 @@ static void cpool_discard_conn(struct cpool *cpool,
struct Curl_easy *data,
struct connectdata *conn,
bool aborted);
-static void cpool_close_and_destroy(struct cpool *cpool,
- struct connectdata *conn,
- struct Curl_easy *data,
- bool do_shutdown);
-static void cpool_run_conn_shutdown(struct Curl_easy *data,
- struct connectdata *conn,
- bool *done);
-static void cpool_run_conn_shutdown_handler(struct Curl_easy *data,
- struct connectdata *conn);
-static CURLMcode cpool_update_shutdown_ev(struct Curl_multi *multi,
- struct Curl_easy *data,
- struct connectdata *conn);
-static void cpool_shutdown_all(struct cpool *cpool,
- struct Curl_easy *data, int timeout_ms);
-static void cpool_close_and_destroy_all(struct cpool *cpool);
-static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool);
-
-static struct cpool_bundle *cpool_bundle_create(const char *dest,
- size_t dest_len)
+
+static struct cpool_bundle *cpool_bundle_create(const char *dest)
{
struct cpool_bundle *bundle;
+ size_t dest_len = strlen(dest);
+
bundle = calloc(1, sizeof(*bundle) + dest_len);
if(!bundle)
return NULL;
Curl_llist_init(&bundle->conns, NULL);
- bundle->dest_len = dest_len;
- memcpy(bundle->dest, dest, dest_len);
+ bundle->dest_len = dest_len + 1;
+ memcpy(bundle->dest, dest, bundle->dest_len);
return bundle;
}
@@ -144,57 +133,102 @@ static void cpool_bundle_free_entry(void *freethis)
cpool_bundle_destroy((struct cpool_bundle *)freethis);
}
-int Curl_cpool_init(struct cpool *cpool,
- Curl_cpool_disconnect_cb *disconnect_cb,
- struct Curl_multi *multi,
- struct Curl_share *share,
- size_t size)
+void Curl_cpool_init(struct cpool *cpool,
+ struct Curl_easy *idata,
+ struct Curl_share *share,
+ size_t size)
{
- DEBUGASSERT(!!multi != !!share); /* either one */
Curl_hash_init(&cpool->dest2bundle, size, Curl_hash_str,
- Curl_str_key_compare, cpool_bundle_free_entry);
- Curl_llist_init(&cpool->shutdowns, NULL);
+ curlx_str_key_compare, cpool_bundle_free_entry);
- DEBUGASSERT(disconnect_cb);
- if(!disconnect_cb)
- return 1;
+ DEBUGASSERT(idata);
- /* allocate a new easy handle to use when closing cached connections */
- cpool->idata = curl_easy_init();
- if(!cpool->idata)
- return 1; /* bad */
- cpool->idata->state.internal = TRUE;
- /* TODO: this is quirky. We need an internal handle for certain
- * operations, but we do not add it to the multi (if there is one).
- * But we give it the multi so that socket event operations can work.
- * Probably better to have an internal handle owned by the multi that
- * can be used for cpool operations. */
- cpool->idata->multi = multi;
-#ifdef DEBUGBUILD
- if(getenv("CURL_DEBUG"))
- cpool->idata->set.verbose = TRUE;
-#endif
+ cpool->idata = idata;
+ cpool->share = share;
+ cpool->initialised = TRUE;
+}
+
+/* Return the "first" connection in the pool or NULL. */
+static struct connectdata *cpool_get_first(struct cpool *cpool)
+{
+ struct Curl_hash_iterator iter;
+ struct Curl_hash_element *he;
+ struct cpool_bundle *bundle;
+ struct Curl_llist_node *conn_node;
+
+ Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
+ for(he = Curl_hash_next_element(&iter); he;
+ he = Curl_hash_next_element(&iter)) {
+ bundle = he->ptr;
+ conn_node = Curl_llist_head(&bundle->conns);
+ if(conn_node)
+ return Curl_node_elem(conn_node);
+ }
+ return NULL;
+}
+
+
+static struct cpool_bundle *cpool_find_bundle(struct cpool *cpool,
+ struct connectdata *conn)
+{
+ return Curl_hash_pick(&cpool->dest2bundle,
+ conn->destination, strlen(conn->destination) + 1);
+}
- cpool->disconnect_cb = disconnect_cb;
- cpool->idata->multi = cpool->multi = multi;
- cpool->idata->share = cpool->share = share;
- return 0; /* good */
+static void cpool_remove_bundle(struct cpool *cpool,
+ struct cpool_bundle *bundle)
+{
+ if(!cpool)
+ return;
+ Curl_hash_delete(&cpool->dest2bundle, bundle->dest, bundle->dest_len);
+}
+
+
+static void cpool_remove_conn(struct cpool *cpool,
+ struct connectdata *conn)
+{
+ struct Curl_llist *list = Curl_node_llist(&conn->cpool_node);
+ DEBUGASSERT(cpool);
+ if(list) {
+ /* The connection is certainly in the pool, but where? */
+ struct cpool_bundle *bundle = cpool_find_bundle(cpool, conn);
+ if(bundle && (list == &bundle->conns)) {
+ cpool_bundle_remove(bundle, conn);
+ if(!Curl_llist_count(&bundle->conns))
+ cpool_remove_bundle(cpool, bundle);
+ conn->bits.in_cpool = FALSE;
+ cpool->num_conn--;
+ }
+ else {
+ /* Should have been in the bundle list */
+ DEBUGASSERT(NULL);
+ }
+ }
}
void Curl_cpool_destroy(struct cpool *cpool)
{
- if(cpool) {
- if(cpool->idata) {
- cpool_close_and_destroy_all(cpool);
- /* The internal closure handle is special and we need to
- * disconnect it from multi/share before closing it down. */
- cpool->idata->multi = NULL;
- cpool->idata->share = NULL;
- Curl_close(&cpool->idata);
+ if(cpool && cpool->initialised && cpool->idata) {
+ struct connectdata *conn;
+ SIGPIPE_VARIABLE(pipe_st);
+
+ CURL_TRC_M(cpool->idata, "%s[CPOOL] destroy, %zu connections",
+ cpool->share ? "[SHARE] " : "", cpool->num_conn);
+ /* Move all connections to the shutdown list */
+ sigpipe_init(&pipe_st);
+ CPOOL_LOCK(cpool, cpool->idata);
+ conn = cpool_get_first(cpool);
+ while(conn) {
+ cpool_remove_conn(cpool, conn);
+ sigpipe_apply(cpool->idata, &pipe_st);
+ connclose(conn, "kill all");
+ cpool_discard_conn(cpool, cpool->idata, conn, FALSE);
+ conn = cpool_get_first(cpool);
}
+ CPOOL_UNLOCK(cpool, cpool->idata);
+ sigpipe_restore(&pipe_st);
Curl_hash_destroy(&cpool->dest2bundle);
- cpool->multi = NULL;
}
}
@@ -217,23 +251,14 @@ void Curl_cpool_xfer_init(struct Curl_easy *data)
DEBUGASSERT(cpool);
if(cpool) {
- CPOOL_LOCK(cpool);
+ CPOOL_LOCK(cpool, data);
/* the identifier inside the connection cache */
data->id = cpool->next_easy_id++;
if(cpool->next_easy_id <= 0)
cpool->next_easy_id = 0;
data->state.lastconnect_id = -1;
- /* The closure handle only ever has default timeouts set. To improve the
- state somewhat we clone the timeouts from each added handle so that the
- closure handle always has the same timeouts as the most recently added
- easy handle. */
- cpool->idata->set.timeout = data->set.timeout;
- cpool->idata->set.server_response_timeout =
- data->set.server_response_timeout;
- cpool->idata->set.no_signal = data->set.no_signal;
-
- CPOOL_UNLOCK(cpool);
+ CPOOL_UNLOCK(cpool, data);
}
else {
/* We should not get here, but in a non-debug build, do something */
@@ -242,19 +267,12 @@ void Curl_cpool_xfer_init(struct Curl_easy *data)
}
}
-static struct cpool_bundle *cpool_find_bundle(struct cpool *cpool,
- struct connectdata *conn)
-{
- return Curl_hash_pick(&cpool->dest2bundle,
- conn->destination, conn->destination_len);
-}
-
static struct cpool_bundle *
cpool_add_bundle(struct cpool *cpool, struct connectdata *conn)
{
struct cpool_bundle *bundle;
- bundle = cpool_bundle_create(conn->destination, conn->destination_len);
+ bundle = cpool_bundle_create(conn->destination);
if(!bundle)
return NULL;
@@ -266,17 +284,70 @@ cpool_add_bundle(struct cpool *cpool, struct connectdata *conn)
return bundle;
}
-static void cpool_remove_bundle(struct cpool *cpool,
- struct cpool_bundle *bundle)
+static struct connectdata *
+cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle)
{
- if(!cpool)
- return;
+ struct Curl_llist_node *curr;
+ timediff_t highscore = -1;
+ timediff_t score;
+ struct curltime now;
+ struct connectdata *oldest_idle = NULL;
+ struct connectdata *conn;
- Curl_hash_delete(&cpool->dest2bundle, bundle->dest, bundle->dest_len);
+ now = curlx_now();
+ curr = Curl_llist_head(&bundle->conns);
+ while(curr) {
+ conn = Curl_node_elem(curr);
+
+ if(!CONN_INUSE(conn)) {
+ /* Set higher score for the age passed since the connection was used */
+ score = curlx_timediff(now, conn->lastused);
+
+ if(score > highscore) {
+ highscore = score;
+ oldest_idle = conn;
+ }
+ }
+ curr = Curl_node_next(curr);
+ }
+ return oldest_idle;
+}
+
+static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool)
+{
+ struct Curl_hash_iterator iter;
+ struct Curl_llist_node *curr;
+ struct Curl_hash_element *he;
+ struct connectdata *oldest_idle = NULL;
+ struct cpool_bundle *bundle;
+ struct curltime now;
+ timediff_t highscore =- 1;
+ timediff_t score;
+
+ now = curlx_now();
+ Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
+
+ for(he = Curl_hash_next_element(&iter); he;
+ he = Curl_hash_next_element(&iter)) {
+ struct connectdata *conn;
+ bundle = he->ptr;
+
+ for(curr = Curl_llist_head(&bundle->conns); curr;
+ curr = Curl_node_next(curr)) {
+ conn = Curl_node_elem(curr);
+ if(CONN_INUSE(conn) || conn->bits.close || conn->connect_only)
+ continue;
+ /* Set higher score for the age passed since the connection was used */
+ score = curlx_timediff(now, conn->lastused);
+ if(score > highscore) {
+ highscore = score;
+ oldest_idle = conn;
+ }
+ }
+ }
+ return oldest_idle;
}
-static struct connectdata *
-cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle);
int Curl_cpool_check_limits(struct Curl_easy *data,
struct connectdata *conn)
@@ -285,70 +356,95 @@ int Curl_cpool_check_limits(struct Curl_easy *data,
struct cpool_bundle *bundle;
size_t dest_limit = 0;
size_t total_limit = 0;
+ size_t shutdowns;
int result = CPOOL_LIMIT_OK;
if(!cpool)
return CPOOL_LIMIT_OK;
- if(data && data->multi) {
- dest_limit = data->multi->max_host_connections;
- total_limit = data->multi->max_total_connections;
+ if(cpool->idata->multi) {
+ dest_limit = cpool->idata->multi->max_host_connections;
+ total_limit = cpool->idata->multi->max_total_connections;
}
if(!dest_limit && !total_limit)
return CPOOL_LIMIT_OK;
- CPOOL_LOCK(cpool);
+ CPOOL_LOCK(cpool, cpool->idata);
if(dest_limit) {
+ size_t live;
+
bundle = cpool_find_bundle(cpool, conn);
- while(bundle && (Curl_llist_count(&bundle->conns) >= dest_limit)) {
- struct connectdata *oldest_idle = NULL;
- /* The bundle is full. Extract the oldest connection that may
- * be removed now, if there is one. */
- oldest_idle = cpool_bundle_get_oldest_idle(bundle);
- if(!oldest_idle)
+ live = bundle ? Curl_llist_count(&bundle->conns) : 0;
+ shutdowns = Curl_cshutdn_dest_count(data, conn->destination);
+ while((live + shutdowns) >= dest_limit) {
+ if(shutdowns) {
+ /* close one connection in shutdown right away, if we can */
+ if(!Curl_cshutdn_close_oldest(data, conn->destination))
+ break;
+ }
+ else if(!bundle)
break;
- /* disconnect the old conn and continue */
- DEBUGF(infof(data, "Discarding connection #%"
- FMT_OFF_T " from %zu to reach destination "
- "limit of %zu", oldest_idle->connection_id,
- Curl_llist_count(&bundle->conns), dest_limit));
- Curl_cpool_disconnect(data, oldest_idle, FALSE);
-
- /* in case the bundle was destroyed in disconnect, look it up again */
- bundle = cpool_find_bundle(cpool, conn);
+ else {
+ struct connectdata *oldest_idle = NULL;
+ /* The bundle is full. Extract the oldest connection that may
+ * be removed now, if there is one. */
+ oldest_idle = cpool_bundle_get_oldest_idle(bundle);
+ if(!oldest_idle)
+ break;
+ /* disconnect the old conn and continue */
+ CURL_TRC_M(data, "Discarding connection #%"
+ FMT_OFF_T " from %zu to reach destination "
+ "limit of %zu", oldest_idle->connection_id,
+ Curl_llist_count(&bundle->conns), dest_limit);
+ Curl_conn_terminate(cpool->idata, oldest_idle, FALSE);
+
+ /* in case the bundle was destroyed in disconnect, look it up again */
+ bundle = cpool_find_bundle(cpool, conn);
+ live = bundle ? Curl_llist_count(&bundle->conns) : 0;
+ }
+ shutdowns = Curl_cshutdn_dest_count(cpool->idata, conn->destination);
}
- if(bundle && (Curl_llist_count(&bundle->conns) >= dest_limit)) {
+ if((live + shutdowns) >= dest_limit) {
result = CPOOL_LIMIT_DEST;
goto out;
}
}
if(total_limit) {
- while(cpool->num_conn >= total_limit) {
- struct connectdata *oldest_idle = cpool_get_oldest_idle(cpool);
- if(!oldest_idle)
- break;
- /* disconnect the old conn and continue */
- DEBUGF(infof(data, "Discarding connection #%"
+ shutdowns = Curl_cshutdn_count(cpool->idata);
+ while((cpool->num_conn + shutdowns) >= total_limit) {
+ if(shutdowns) {
+ /* close one connection in shutdown right away, if we can */
+ if(!Curl_cshutdn_close_oldest(data, NULL))
+ break;
+ }
+ else {
+ struct connectdata *oldest_idle = cpool_get_oldest_idle(cpool);
+ if(!oldest_idle)
+ break;
+ /* disconnect the old conn and continue */
+ CURL_TRC_M(data, "Discarding connection #%"
FMT_OFF_T " from %zu to reach total "
"limit of %zu",
- oldest_idle->connection_id, cpool->num_conn, total_limit));
- Curl_cpool_disconnect(data, oldest_idle, FALSE);
+ oldest_idle->connection_id, cpool->num_conn, total_limit);
+ Curl_conn_terminate(cpool->idata, oldest_idle, FALSE);
+ }
+ shutdowns = Curl_cshutdn_count(cpool->idata);
}
- if(cpool->num_conn >= total_limit) {
+ if((cpool->num_conn + shutdowns) >= total_limit) {
result = CPOOL_LIMIT_TOTAL;
goto out;
}
}
out:
- CPOOL_UNLOCK(cpool);
+ CPOOL_UNLOCK(cpool, cpool->idata);
return result;
}
-CURLcode Curl_cpool_add_conn(struct Curl_easy *data,
- struct connectdata *conn)
+CURLcode Curl_cpool_add(struct Curl_easy *data,
+ struct connectdata *conn)
{
CURLcode result = CURLE_OK;
struct cpool_bundle *bundle = NULL;
@@ -359,7 +455,7 @@ CURLcode Curl_cpool_add_conn(struct Curl_easy *data,
if(!cpool)
return CURLE_FAILED_INIT;
- CPOOL_LOCK(cpool);
+ CPOOL_LOCK(cpool, data);
bundle = cpool_find_bundle(cpool, conn);
if(!bundle) {
bundle = cpool_add_bundle(cpool, conn);
@@ -372,37 +468,15 @@ CURLcode Curl_cpool_add_conn(struct Curl_easy *data,
cpool_bundle_add(bundle, conn);
conn->connection_id = cpool->next_connection_id++;
cpool->num_conn++;
- DEBUGF(infof(data, "Added connection %" FMT_OFF_T ". "
- "The cache now contains %zu members",
- conn->connection_id, cpool->num_conn));
+ CURL_TRC_M(data, "[CPOOL] added connection %" FMT_OFF_T ". "
+ "The cache now contains %zu members",
+ conn->connection_id, cpool->num_conn);
out:
- CPOOL_UNLOCK(cpool);
+ CPOOL_UNLOCK(cpool, data);
return result;
}
-static void cpool_remove_conn(struct cpool *cpool,
- struct connectdata *conn)
-{
- struct Curl_llist *list = Curl_node_llist(&conn->cpool_node);
- DEBUGASSERT(cpool);
- if(list) {
- /* The connection is certainly in the pool, but where? */
- struct cpool_bundle *bundle = cpool_find_bundle(cpool, conn);
- if(bundle && (list == &bundle->conns)) {
- cpool_bundle_remove(bundle, conn);
- if(!Curl_llist_count(&bundle->conns))
- cpool_remove_bundle(cpool, bundle);
- conn->bits.in_cpool = FALSE;
- cpool->num_conn--;
- }
- else {
- /* Not in a bundle, already in the shutdown list? */
- DEBUGASSERT(list == &cpool->shutdowns);
- }
- }
-}
-
/* This function iterates the entire connection pool and calls the function
func() with the connection pointer as the first argument and the supplied
'param' argument as the other.
@@ -450,25 +524,6 @@ static bool cpool_foreach(struct Curl_easy *data,
return FALSE;
}
-/* Return a live connection in the pool or NULL. */
-static struct connectdata *cpool_get_live_conn(struct cpool *cpool)
-{
- struct Curl_hash_iterator iter;
- struct Curl_hash_element *he;
- struct cpool_bundle *bundle;
- struct Curl_llist_node *conn_node;
-
- Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
- for(he = Curl_hash_next_element(&iter); he;
- he = Curl_hash_next_element(&iter)) {
- bundle = he->ptr;
- conn_node = Curl_llist_head(&bundle->conns);
- if(conn_node)
- return Curl_node_elem(conn_node);
- }
- return NULL;
-}
-
/*
* A connection (already in the pool) has become idle. Do any
* cleanups in regard to the pool's limits.
@@ -479,103 +534,36 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
struct connectdata *conn)
{
unsigned int maxconnects = !data->multi->maxconnects ?
- data->multi->num_easy * 4 : data->multi->maxconnects;
+ (Curl_multi_xfers_running(data->multi) * 4) : data->multi->maxconnects;
struct connectdata *oldest_idle = NULL;
struct cpool *cpool = cpool_get_instance(data);
bool kept = TRUE;
- conn->lastused = Curl_now(); /* it was used up until now */
+ conn->lastused = curlx_now(); /* it was used up until now */
if(cpool && maxconnects) {
/* may be called form a callback already under lock */
bool do_lock = !CPOOL_IS_LOCKED(cpool);
if(do_lock)
- CPOOL_LOCK(cpool);
+ CPOOL_LOCK(cpool, data);
if(cpool->num_conn > maxconnects) {
- infof(data, "Connection pool is full, closing the oldest one");
+ infof(data, "Connection pool is full, closing the oldest of %zu/%u",
+ cpool->num_conn, maxconnects);
oldest_idle = cpool_get_oldest_idle(cpool);
kept = (oldest_idle != conn);
if(oldest_idle) {
- Curl_cpool_disconnect(cpool->idata, oldest_idle, FALSE);
+ Curl_conn_terminate(data, oldest_idle, FALSE);
}
}
if(do_lock)
- CPOOL_UNLOCK(cpool);
+ CPOOL_UNLOCK(cpool, data);
}
return kept;
}
-/*
- * This function finds the connection in the connection bundle that has been
- * unused for the longest time.
- */
-static struct connectdata *
-cpool_bundle_get_oldest_idle(struct cpool_bundle *bundle)
-{
- struct Curl_llist_node *curr;
- timediff_t highscore = -1;
- timediff_t score;
- struct curltime now;
- struct connectdata *oldest_idle = NULL;
- struct connectdata *conn;
-
- now = Curl_now();
- curr = Curl_llist_head(&bundle->conns);
- while(curr) {
- conn = Curl_node_elem(curr);
-
- if(!CONN_INUSE(conn)) {
- /* Set higher score for the age passed since the connection was used */
- score = Curl_timediff(now, conn->lastused);
-
- if(score > highscore) {
- highscore = score;
- oldest_idle = conn;
- }
- }
- curr = Curl_node_next(curr);
- }
- return oldest_idle;
-}
-
-static struct connectdata *cpool_get_oldest_idle(struct cpool *cpool)
-{
- struct Curl_hash_iterator iter;
- struct Curl_llist_node *curr;
- struct Curl_hash_element *he;
- struct connectdata *oldest_idle = NULL;
- struct cpool_bundle *bundle;
- struct curltime now;
- timediff_t highscore =- 1;
- timediff_t score;
-
- now = Curl_now();
- Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
-
- for(he = Curl_hash_next_element(&iter); he;
- he = Curl_hash_next_element(&iter)) {
- struct connectdata *conn;
- bundle = he->ptr;
-
- for(curr = Curl_llist_head(&bundle->conns); curr;
- curr = Curl_node_next(curr)) {
- conn = Curl_node_elem(curr);
- if(CONN_INUSE(conn) || conn->bits.close || conn->connect_only)
- continue;
- /* Set higher score for the age passed since the connection was used */
- score = Curl_timediff(now, conn->lastused);
- if(score > highscore) {
- highscore = score;
- oldest_idle = conn;
- }
- }
- }
- return oldest_idle;
-}
-
bool Curl_cpool_find(struct Curl_easy *data,
- const char *destination, size_t dest_len,
+ const char *destination,
Curl_cpool_conn_match_cb *conn_cb,
Curl_cpool_done_match_cb *done_cb,
void *userdata)
@@ -589,8 +577,10 @@ bool Curl_cpool_find(struct Curl_easy *data,
if(!cpool)
return FALSE;
- CPOOL_LOCK(cpool);
- bundle = Curl_hash_pick(&cpool->dest2bundle, (void *)destination, dest_len);
+ CPOOL_LOCK(cpool, data);
+ bundle = Curl_hash_pick(&cpool->dest2bundle,
+ CURL_UNCONST(destination),
+ strlen(destination) + 1);
if(bundle) {
struct Curl_llist_node *curr = Curl_llist_head(&bundle->conns);
while(curr) {
@@ -608,89 +598,10 @@ bool Curl_cpool_find(struct Curl_easy *data,
if(done_cb) {
result = done_cb(result, userdata);
}
- CPOOL_UNLOCK(cpool);
+ CPOOL_UNLOCK(cpool, data);
return result;
}
-static void cpool_shutdown_discard_all(struct cpool *cpool)
-{
- struct Curl_llist_node *e = Curl_llist_head(&cpool->shutdowns);
- struct connectdata *conn;
-
- if(!e)
- return;
-
- DEBUGF(infof(cpool->idata, "cpool_shutdown_discard_all"));
- while(e) {
- conn = Curl_node_elem(e);
- Curl_node_remove(e);
- DEBUGF(infof(cpool->idata, "discard connection #%" FMT_OFF_T,
- conn->connection_id));
- cpool_close_and_destroy(cpool, conn, NULL, FALSE);
- e = Curl_llist_head(&cpool->shutdowns);
- }
-}
-
-static void cpool_close_and_destroy_all(struct cpool *cpool)
-{
- struct connectdata *conn;
- int timeout_ms = 0;
- SIGPIPE_VARIABLE(pipe_st);
-
- DEBUGASSERT(cpool);
- /* Move all connections to the shutdown list */
- sigpipe_init(&pipe_st);
- CPOOL_LOCK(cpool);
- conn = cpool_get_live_conn(cpool);
- while(conn) {
- cpool_remove_conn(cpool, conn);
- sigpipe_apply(cpool->idata, &pipe_st);
- connclose(conn, "kill all");
- cpool_discard_conn(cpool, cpool->idata, conn, FALSE);
-
- conn = cpool_get_live_conn(cpool);
- }
- CPOOL_UNLOCK(cpool);
-
- /* Just for testing, run graceful shutdown */
-#ifdef DEBUGBUILD
- {
- char *p = getenv("CURL_GRACEFUL_SHUTDOWN");
- if(p) {
- long l = strtol(p, NULL, 10);
- if(l > 0 && l < INT_MAX)
- timeout_ms = (int)l;
- }
- }
-#endif
- sigpipe_apply(cpool->idata, &pipe_st);
- cpool_shutdown_all(cpool, cpool->idata, timeout_ms);
-
- /* discard all connections in the shutdown list */
- cpool_shutdown_discard_all(cpool);
-
- Curl_hostcache_clean(cpool->idata, cpool->idata->dns.hostcache);
- sigpipe_restore(&pipe_st);
-}
-
-
-static void cpool_shutdown_destroy_oldest(struct cpool *cpool)
-{
- struct Curl_llist_node *e;
- struct connectdata *conn;
-
- e = Curl_llist_head(&cpool->shutdowns);
- if(e) {
- SIGPIPE_VARIABLE(pipe_st);
- conn = Curl_node_elem(e);
- Curl_node_remove(e);
- sigpipe_init(&pipe_st);
- sigpipe_apply(cpool->idata, &pipe_st);
- cpool_close_and_destroy(cpool, conn, NULL, FALSE);
- sigpipe_restore(&pipe_st);
- }
-}
-
static void cpool_discard_conn(struct cpool *cpool,
struct Curl_easy *data,
struct connectdata *conn,
@@ -699,6 +610,7 @@ static void cpool_discard_conn(struct cpool *cpool,
bool done = FALSE;
DEBUGASSERT(data);
+ DEBUGASSERT(!data->conn);
DEBUGASSERT(cpool);
DEBUGASSERT(!conn->bits.in_cpool);
@@ -707,9 +619,9 @@ static void cpool_discard_conn(struct cpool *cpool,
* are other users of it
*/
if(CONN_INUSE(conn) && !aborted) {
- DEBUGF(infof(data, "[CCACHE] not discarding #%" FMT_OFF_T
- " still in use by %zu transfers", conn->connection_id,
- CONN_INUSE(conn)));
+ CURL_TRC_M(data, "[CPOOL] not discarding #%" FMT_OFF_T
+ " still in use by %u transfers", conn->connection_id,
+ CONN_ATTACHED(conn));
return;
}
@@ -728,52 +640,18 @@ static void cpool_discard_conn(struct cpool *cpool,
done = TRUE;
if(!done) {
/* Attempt to shutdown the connection right away. */
- Curl_attach_connection(data, conn);
- cpool_run_conn_shutdown(data, conn, &done);
- DEBUGF(infof(data, "[CCACHE] shutdown #%" FMT_OFF_T ", done=%d",
- conn->connection_id, done));
- Curl_detach_connection(data);
- }
-
- if(done) {
- cpool_close_and_destroy(cpool, conn, data, FALSE);
- return;
+ Curl_cshutdn_run_once(cpool->idata, conn, &done);
}
- /* Add the connection to our shutdown list for non-blocking shutdown
- * during multi processing. */
- if(data->multi && data->multi->max_shutdown_connections > 0 &&
- (data->multi->max_shutdown_connections >=
- (long)Curl_llist_count(&cpool->shutdowns))) {
- DEBUGF(infof(data, "[CCACHE] discarding oldest shutdown connection "
- "due to limit of %ld",
- data->multi->max_shutdown_connections));
- cpool_shutdown_destroy_oldest(cpool);
- }
-
- if(data->multi && data->multi->socket_cb) {
- DEBUGASSERT(cpool == &data->multi->cpool);
- /* Start with an empty shutdown pollset, so out internal closure handle
- * is added to the sockets. */
- memset(&conn->shutdown_poll, 0, sizeof(conn->shutdown_poll));
- if(cpool_update_shutdown_ev(data->multi, cpool->idata, conn)) {
- DEBUGF(infof(data, "[CCACHE] update events for shutdown failed, "
- "discarding #%" FMT_OFF_T,
- conn->connection_id));
- cpool_close_and_destroy(cpool, conn, data, FALSE);
- return;
- }
- }
-
- Curl_llist_append(&cpool->shutdowns, conn, &conn->cpool_node);
- DEBUGF(infof(data, "[CCACHE] added #%" FMT_OFF_T
- " to shutdown list of length %zu", conn->connection_id,
- Curl_llist_count(&cpool->shutdowns)));
+ if(done || !data->multi)
+ Curl_cshutdn_terminate(cpool->idata, conn, FALSE);
+ else
+ Curl_cshutdn_add(&data->multi->cshutdn, conn, cpool->num_conn);
}
-void Curl_cpool_disconnect(struct Curl_easy *data,
- struct connectdata *conn,
- bool aborted)
+void Curl_conn_terminate(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool aborted)
{
struct cpool *cpool = cpool_get_instance(data);
bool do_lock;
@@ -787,7 +665,7 @@ void Curl_cpool_disconnect(struct Curl_easy *data,
* are other users of it */
if(CONN_INUSE(conn) && !aborted) {
DEBUGASSERT(0); /* does this ever happen? */
- DEBUGF(infof(data, "Curl_disconnect when inuse: %zu", CONN_INUSE(conn)));
+ DEBUGF(infof(data, "Curl_disconnect when inuse: %u", CONN_ATTACHED(conn)));
return;
}
@@ -795,15 +673,17 @@ void Curl_cpool_disconnect(struct Curl_easy *data,
* user callback in find. */
do_lock = !CPOOL_IS_LOCKED(cpool);
if(do_lock)
- CPOOL_LOCK(cpool);
+ CPOOL_LOCK(cpool, data);
if(conn->bits.in_cpool) {
cpool_remove_conn(cpool, conn);
DEBUGASSERT(!conn->bits.in_cpool);
}
- /* Run the callback to let it clean up anything it wants to. */
- aborted = cpool->disconnect_cb(data, conn, aborted);
+ /* treat the connection as aborted in CONNECT_ONLY situations,
+ * so no graceful shutdown is attempted. */
+ if(conn->connect_only)
+ aborted = TRUE;
if(data->multi) {
/* Add it to the multi's cpool for shutdown handling */
@@ -812,376 +692,15 @@ void Curl_cpool_disconnect(struct Curl_easy *data,
cpool_discard_conn(&data->multi->cpool, data, conn, aborted);
}
else {
- /* No multi available. Make a best-effort shutdown + close */
+ /* No multi available, terminate */
infof(data, "closing connection #%" FMT_OFF_T, conn->connection_id);
- cpool_close_and_destroy(NULL, conn, data, !aborted);
+ Curl_cshutdn_terminate(cpool->idata, conn, !aborted);
}
if(do_lock)
- CPOOL_UNLOCK(cpool);
-}
-
-static void cpool_run_conn_shutdown_handler(struct Curl_easy *data,
- struct connectdata *conn)
-{
- if(!conn->bits.shutdown_handler) {
- if(conn->dns_entry)
- Curl_resolv_unlink(data, &conn->dns_entry);
-
- /* Cleanup NTLM connection-related data */
- Curl_http_auth_cleanup_ntlm(conn);
-
- /* Cleanup NEGOTIATE connection-related data */
- Curl_http_auth_cleanup_negotiate(conn);
-
- if(conn->handler && conn->handler->disconnect) {
- /* This is set if protocol-specific cleanups should be made */
- DEBUGF(infof(data, "connection #%" FMT_OFF_T
- ", shutdown protocol handler (aborted=%d)",
- conn->connection_id, conn->bits.aborted));
-
- conn->handler->disconnect(data, conn, conn->bits.aborted);
- }
-
- /* possible left-overs from the async name resolvers */
- Curl_resolver_cancel(data);
-
- conn->bits.shutdown_handler = TRUE;
- }
-}
-
-static void cpool_run_conn_shutdown(struct Curl_easy *data,
- struct connectdata *conn,
- bool *done)
-{
- CURLcode r1, r2;
- bool done1, done2;
-
- /* We expect to be attached when called */
- DEBUGASSERT(data->conn == conn);
-
- cpool_run_conn_shutdown_handler(data, conn);
-
- if(conn->bits.shutdown_filters) {
- *done = TRUE;
- return;
- }
-
- if(!conn->connect_only && Curl_conn_is_connected(conn, FIRSTSOCKET))
- r1 = Curl_conn_shutdown(data, FIRSTSOCKET, &done1);
- else {
- r1 = CURLE_OK;
- done1 = TRUE;
- }
-
- if(!conn->connect_only && Curl_conn_is_connected(conn, SECONDARYSOCKET))
- r2 = Curl_conn_shutdown(data, SECONDARYSOCKET, &done2);
- else {
- r2 = CURLE_OK;
- done2 = TRUE;
- }
-
- /* we are done when any failed or both report success */
- *done = (r1 || r2 || (done1 && done2));
- if(*done)
- conn->bits.shutdown_filters = TRUE;
+ CPOOL_UNLOCK(cpool, data);
}
-static CURLcode cpool_add_pollfds(struct cpool *cpool,
- struct curl_pollfds *cpfds)
-{
- CURLcode result = CURLE_OK;
-
- if(Curl_llist_head(&cpool->shutdowns)) {
- struct Curl_llist_node *e;
- struct easy_pollset ps;
- struct connectdata *conn;
-
- for(e = Curl_llist_head(&cpool->shutdowns); e;
- e = Curl_node_next(e)) {
- conn = Curl_node_elem(e);
- memset(&ps, 0, sizeof(ps));
- Curl_attach_connection(cpool->idata, conn);
- Curl_conn_adjust_pollset(cpool->idata, &ps);
- Curl_detach_connection(cpool->idata);
-
- result = Curl_pollfds_add_ps(cpfds, &ps);
- if(result) {
- Curl_pollfds_cleanup(cpfds);
- goto out;
- }
- }
- }
-out:
- return result;
-}
-
-CURLcode Curl_cpool_add_pollfds(struct cpool *cpool,
- struct curl_pollfds *cpfds)
-{
- CURLcode result;
- CPOOL_LOCK(cpool);
- result = cpool_add_pollfds(cpool, cpfds);
- CPOOL_UNLOCK(cpool);
- return result;
-}
-
-CURLcode Curl_cpool_add_waitfds(struct cpool *cpool,
- struct curl_waitfds *cwfds)
-{
- CURLcode result = CURLE_OK;
-
- CPOOL_LOCK(cpool);
- if(Curl_llist_head(&cpool->shutdowns)) {
- struct Curl_llist_node *e;
- struct easy_pollset ps;
- struct connectdata *conn;
-
- for(e = Curl_llist_head(&cpool->shutdowns); e;
- e = Curl_node_next(e)) {
- conn = Curl_node_elem(e);
- memset(&ps, 0, sizeof(ps));
- Curl_attach_connection(cpool->idata, conn);
- Curl_conn_adjust_pollset(cpool->idata, &ps);
- Curl_detach_connection(cpool->idata);
-
- result = Curl_waitfds_add_ps(cwfds, &ps);
- if(result)
- goto out;
- }
- }
-out:
- CPOOL_UNLOCK(cpool);
- return result;
-}
-
-static void cpool_perform(struct cpool *cpool)
-{
- struct Curl_easy *data = cpool->idata;
- struct Curl_llist_node *e = Curl_llist_head(&cpool->shutdowns);
- struct Curl_llist_node *enext;
- struct connectdata *conn;
- struct curltime *nowp = NULL;
- struct curltime now;
- timediff_t next_from_now_ms = 0, ms;
- bool done;
-
- if(!e)
- return;
-
- DEBUGASSERT(data);
- DEBUGF(infof(data, "[CCACHE] perform, %zu connections being shutdown",
- Curl_llist_count(&cpool->shutdowns)));
- while(e) {
- enext = Curl_node_next(e);
- conn = Curl_node_elem(e);
- Curl_attach_connection(data, conn);
- cpool_run_conn_shutdown(data, conn, &done);
- DEBUGF(infof(data, "[CCACHE] shutdown #%" FMT_OFF_T ", done=%d",
- conn->connection_id, done));
- Curl_detach_connection(data);
- if(done) {
- Curl_node_remove(e);
- cpool_close_and_destroy(cpool, conn, NULL, FALSE);
- }
- else {
- /* Not done, when does this connection time out? */
- if(!nowp) {
- now = Curl_now();
- nowp = &now;
- }
- ms = Curl_conn_shutdown_timeleft(conn, nowp);
- if(ms && ms < next_from_now_ms)
- next_from_now_ms = ms;
- }
- e = enext;
- }
-
- if(next_from_now_ms)
- Curl_expire(data, next_from_now_ms, EXPIRE_RUN_NOW);
-}
-
-void Curl_cpool_multi_perform(struct Curl_multi *multi)
-{
- CPOOL_LOCK(&multi->cpool);
- cpool_perform(&multi->cpool);
- CPOOL_UNLOCK(&multi->cpool);
-}
-
-
-/*
- * Close and destroy the connection. Run the shutdown sequence once,
- * of so requested.
- */
-static void cpool_close_and_destroy(struct cpool *cpool,
- struct connectdata *conn,
- struct Curl_easy *data,
- bool do_shutdown)
-{
- bool done;
-
- /* there must be a connection to close */
- DEBUGASSERT(conn);
- /* it must be removed from the connection pool */
- DEBUGASSERT(!conn->bits.in_cpool);
- /* there must be an associated transfer */
- DEBUGASSERT(data || cpool);
- if(!data)
- data = cpool->idata;
-
- /* the transfer must be detached from the connection */
- DEBUGASSERT(data && !data->conn);
-
- Curl_attach_connection(data, conn);
-
- cpool_run_conn_shutdown_handler(data, conn);
- if(do_shutdown) {
- /* Make a last attempt to shutdown handlers and filters, if
- * not done so already. */
- cpool_run_conn_shutdown(data, conn, &done);
- }
-
- if(cpool)
- DEBUGF(infof(data, "[CCACHE] closing #%" FMT_OFF_T,
- conn->connection_id));
- else
- DEBUGF(infof(data, "closing connection #%" FMT_OFF_T,
- conn->connection_id));
- Curl_conn_close(data, SECONDARYSOCKET);
- Curl_conn_close(data, FIRSTSOCKET);
- Curl_detach_connection(data);
-
- Curl_conn_free(data, conn);
-}
-
-
-static CURLMcode cpool_update_shutdown_ev(struct Curl_multi *multi,
- struct Curl_easy *data,
- struct connectdata *conn)
-{
- struct easy_pollset ps;
- CURLMcode mresult;
-
- DEBUGASSERT(data);
- DEBUGASSERT(multi);
- DEBUGASSERT(multi->socket_cb);
-
- memset(&ps, 0, sizeof(ps));
- Curl_attach_connection(data, conn);
- Curl_conn_adjust_pollset(data, &ps);
- Curl_detach_connection(data);
-
- mresult = Curl_multi_pollset_ev(multi, data, &ps, &conn->shutdown_poll);
-
- if(!mresult) /* Remember for next time */
- memcpy(&conn->shutdown_poll, &ps, sizeof(ps));
- return mresult;
-}
-
-void Curl_cpool_multi_socket(struct Curl_multi *multi,
- curl_socket_t s, int ev_bitmask)
-{
- struct cpool *cpool = &multi->cpool;
- struct Curl_easy *data = cpool->idata;
- struct Curl_llist_node *e;
- struct connectdata *conn;
- bool done;
-
- (void)ev_bitmask;
- DEBUGASSERT(multi->socket_cb);
- CPOOL_LOCK(cpool);
- e = Curl_llist_head(&cpool->shutdowns);
- while(e) {
- conn = Curl_node_elem(e);
- if(s == conn->sock[FIRSTSOCKET] || s == conn->sock[SECONDARYSOCKET]) {
- Curl_attach_connection(data, conn);
- cpool_run_conn_shutdown(data, conn, &done);
- DEBUGF(infof(data, "[CCACHE] shutdown #%" FMT_OFF_T ", done=%d",
- conn->connection_id, done));
- Curl_detach_connection(data);
- if(done || cpool_update_shutdown_ev(multi, data, conn)) {
- Curl_node_remove(e);
- cpool_close_and_destroy(cpool, conn, NULL, FALSE);
- }
- break;
- }
- e = Curl_node_next(e);
- }
- CPOOL_UNLOCK(cpool);
-}
-
-#define NUM_POLLS_ON_STACK 10
-
-static CURLcode cpool_shutdown_wait(struct cpool *cpool, int timeout_ms)
-{
- struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
- struct curl_pollfds cpfds;
- CURLcode result;
-
- Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK);
-
- result = cpool_add_pollfds(cpool, &cpfds);
- if(result)
- goto out;
-
- Curl_poll(cpfds.pfds, cpfds.n, CURLMIN(timeout_ms, 1000));
-
-out:
- Curl_pollfds_cleanup(&cpfds);
- return result;
-}
-
-static void cpool_shutdown_all(struct cpool *cpool,
- struct Curl_easy *data, int timeout_ms)
-{
- struct connectdata *conn;
- struct curltime started = Curl_now();
-
- if(!data)
- return;
- (void)data;
-
- DEBUGF(infof(data, "cpool shutdown all"));
-
- /* Move all connections into the shutdown queue */
- for(conn = cpool_get_live_conn(cpool); conn;
- conn = cpool_get_live_conn(cpool)) {
- /* Move conn from live set to shutdown or destroy right away */
- DEBUGF(infof(data, "moving connection #%" FMT_OFF_T
- " to shutdown queue", conn->connection_id));
- cpool_remove_conn(cpool, conn);
- cpool_discard_conn(cpool, data, conn, FALSE);
- }
-
- while(Curl_llist_head(&cpool->shutdowns)) {
- timediff_t timespent;
- int remain_ms;
-
- cpool_perform(cpool);
-
- if(!Curl_llist_head(&cpool->shutdowns)) {
- DEBUGF(infof(data, "cpool shutdown ok"));
- break;
- }
-
- /* wait for activity, timeout or "nothing" */
- timespent = Curl_timediff(Curl_now(), started);
- if(timespent >= (timediff_t)timeout_ms) {
- DEBUGF(infof(data, "cpool shutdown %s",
- (timeout_ms > 0) ? "timeout" : "best effort done"));
- break;
- }
-
- remain_ms = timeout_ms - (int)timespent;
- if(cpool_shutdown_wait(cpool, remain_ms)) {
- DEBUGF(infof(data, "cpool shutdown all, abort"));
- break;
- }
- }
-
- /* Due to errors/timeout, we might come here without being done. */
- cpool_shutdown_discard_all(cpool);
-}
struct cpool_reaper_ctx {
struct curltime now;
@@ -1193,7 +712,7 @@ static int cpool_reap_dead_cb(struct Curl_easy *data,
struct cpool_reaper_ctx *rctx = param;
if(Curl_conn_seems_dead(conn, data, &rctx->now)) {
/* stop the iteration here, pass back the connection that was pruned */
- Curl_cpool_disconnect(data, conn, FALSE);
+ Curl_conn_terminate(data, conn, FALSE);
return 1;
}
return 0; /* continue iteration */
@@ -1215,16 +734,16 @@ void Curl_cpool_prune_dead(struct Curl_easy *data)
if(!cpool)
return;
- rctx.now = Curl_now();
- CPOOL_LOCK(cpool);
- elapsed = Curl_timediff(rctx.now, cpool->last_cleanup);
+ rctx.now = curlx_now();
+ CPOOL_LOCK(cpool, data);
+ elapsed = curlx_timediff(rctx.now, cpool->last_cleanup);
if(elapsed >= 1000L) {
while(cpool_foreach(data, cpool, &rctx, cpool_reap_dead_cb))
;
cpool->last_cleanup = rctx.now;
}
- CPOOL_UNLOCK(cpool);
+ CPOOL_UNLOCK(cpool, data);
}
static int conn_upkeep(struct Curl_easy *data,
@@ -1232,7 +751,6 @@ static int conn_upkeep(struct Curl_easy *data,
void *param)
{
struct curltime *now = param;
- /* TODO, shall we reap connections that return an error here? */
Curl_conn_upkeep(data, conn, now);
return 0; /* continue iteration */
}
@@ -1240,14 +758,14 @@ static int conn_upkeep(struct Curl_easy *data,
CURLcode Curl_cpool_upkeep(void *data)
{
struct cpool *cpool = cpool_get_instance(data);
- struct curltime now = Curl_now();
+ struct curltime now = curlx_now();
if(!cpool)
return CURLE_OK;
- CPOOL_LOCK(cpool);
+ CPOOL_LOCK(cpool, data);
cpool_foreach(data, cpool, &now, conn_upkeep);
- CPOOL_UNLOCK(cpool);
+ CPOOL_UNLOCK(cpool, data);
return CURLE_OK;
}
@@ -1278,9 +796,9 @@ struct connectdata *Curl_cpool_get_conn(struct Curl_easy *data,
return NULL;
fctx.id = conn_id;
fctx.conn = NULL;
- CPOOL_LOCK(cpool);
- cpool_foreach(cpool->idata, cpool, &fctx, cpool_find_conn);
- CPOOL_UNLOCK(cpool);
+ CPOOL_LOCK(cpool, data);
+ cpool_foreach(data, cpool, &fctx, cpool_find_conn);
+ CPOOL_UNLOCK(cpool, data);
return fctx.conn;
}
@@ -1313,9 +831,9 @@ void Curl_cpool_do_by_id(struct Curl_easy *data, curl_off_t conn_id,
dctx.id = conn_id;
dctx.cb = cb;
dctx.cbdata = cbdata;
- CPOOL_LOCK(cpool);
+ CPOOL_LOCK(cpool, data);
cpool_foreach(data, cpool, &dctx, cpool_do_conn);
- CPOOL_UNLOCK(cpool);
+ CPOOL_UNLOCK(cpool, data);
}
void Curl_cpool_do_locked(struct Curl_easy *data,
@@ -1324,9 +842,9 @@ void Curl_cpool_do_locked(struct Curl_easy *data,
{
struct cpool *cpool = cpool_get_instance(data);
if(cpool) {
- CPOOL_LOCK(cpool);
+ CPOOL_LOCK(cpool, data);
cb(conn, data, cbdata);
- CPOOL_UNLOCK(cpool);
+ CPOOL_UNLOCK(cpool, data);
}
else
cb(conn, data, cbdata);
diff --git a/libs/libcurl/src/conncache.h b/libs/libcurl/src/conncache.h
index df1f1353aa..b97252fa4e 100644
--- a/libs/libcurl/src/conncache.h
+++ b/libs/libcurl/src/conncache.h
@@ -26,26 +26,27 @@
***************************************************************************/
#include <curl/curl.h>
-#include "timeval.h"
+#include "curlx/timeval.h"
struct connectdata;
struct Curl_easy;
struct curl_pollfds;
-struct curl_waitfds;
+struct Curl_waitfds;
struct Curl_multi;
struct Curl_share;
/**
- * Callback invoked when disconnecting connections.
- * @param data transfer last handling the connection, not attached
- * @param conn the connection to discard
- * @param aborted if the connection is being aborted
- * @return if the connection is being aborted, e.g. should NOT perform
- * a shutdown and just close.
- **/
-typedef bool Curl_cpool_disconnect_cb(struct Curl_easy *data,
- struct connectdata *conn,
- bool aborted);
+ * Terminate the connection, e.g. close and destroy.
+ * If the connection is in a cpool, remove it.
+ * If a `cshutdn` is available (e.g. data has a multi handle),
+ * pass the connection to that for controlled shutdown.
+ * Otherwise terminate it right away.
+ * Takes ownership of `conn`.
+ * `data` should not be attached to a connection.
+ */
+void Curl_conn_terminate(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool aborted);
struct cpool {
/* the pooled connections, bundled per destination */
@@ -54,22 +55,19 @@ struct cpool {
curl_off_t next_connection_id;
curl_off_t next_easy_id;
struct curltime last_cleanup;
- struct Curl_llist shutdowns; /* The connections being shut down */
- struct Curl_easy *idata; /* internal handle used for discard */
- struct Curl_multi *multi; /* != NULL iff pool belongs to multi */
- struct Curl_share *share; /* != NULL iff pool belongs to share */
- Curl_cpool_disconnect_cb *disconnect_cb;
+ struct Curl_easy *idata; /* internal handle for maintenance */
+ struct Curl_share *share; /* != NULL if pool belongs to share */
BIT(locked);
+ BIT(initialised);
};
/* Init the pool, pass multi only if pool is owned by it.
- * returns 1 on error, 0 is fine.
+ * Cannot fail.
*/
-int Curl_cpool_init(struct cpool *cpool,
- Curl_cpool_disconnect_cb *disconnect_cb,
- struct Curl_multi *multi,
- struct Curl_share *share,
- size_t size);
+void Curl_cpool_init(struct cpool *cpool,
+ struct Curl_easy *idata,
+ struct Curl_share *share,
+ size_t size);
/* Destroy all connections and free all members */
void Curl_cpool_destroy(struct cpool *connc);
@@ -78,14 +76,13 @@ void Curl_cpool_destroy(struct cpool *connc);
* Assigns `data->id`. */
void Curl_cpool_xfer_init(struct Curl_easy *data);
-/**
- * Get the connection with the given id from the transfer's pool.
- */
+/* Get the connection with the given id from `data`'s conn pool. */
struct connectdata *Curl_cpool_get_conn(struct Curl_easy *data,
curl_off_t conn_id);
-CURLcode Curl_cpool_add_conn(struct Curl_easy *data,
- struct connectdata *conn) WARN_UNUSED_RESULT;
+/* Add the connection to the pool. */
+CURLcode Curl_cpool_add(struct Curl_easy *data,
+ struct connectdata *conn) WARN_UNUSED_RESULT;
/**
* Return if the pool has reached its configured limits for adding
@@ -110,16 +107,13 @@ typedef bool Curl_cpool_done_match_cb(bool result, void *userdata);
* All callbacks are invoked while the pool's lock is held.
* @param data current transfer
* @param destination match agaonst `conn->destination` in pool
- * @param dest_len destination length, including terminating NUL
* @param conn_cb must be present, called for each connection in the
* bundle until it returns TRUE
- * @param result_cb if not NULL, is called at the end with the result
- * of the `conn_cb` or FALSE if never called.
* @return combined result of last conn_db and result_cb or FALSE if no
connections were present.
*/
bool Curl_cpool_find(struct Curl_easy *data,
- const char *destination, size_t dest_len,
+ const char *destination,
Curl_cpool_conn_match_cb *conn_cb,
Curl_cpool_done_match_cb *done_cb,
void *userdata);
@@ -134,17 +128,6 @@ bool Curl_cpool_conn_now_idle(struct Curl_easy *data,
struct connectdata *conn);
/**
- * Remove the connection from the pool and tear it down.
- * If `aborted` is FALSE, the connection will be shut down first
- * before closing and destroying it.
- * If the shutdown is not immediately complete, the connection
- * will be placed into the pool's shutdown queue.
- */
-void Curl_cpool_disconnect(struct Curl_easy *data,
- struct connectdata *conn,
- bool aborted);
-
-/**
* This function scans the data's connection pool for half-open/dead
* connections, closes and removes them.
* The cleanup is done at most once per second.
@@ -180,22 +163,4 @@ void Curl_cpool_do_locked(struct Curl_easy *data,
struct connectdata *conn,
Curl_cpool_conn_do_cb *cb, void *cbdata);
-/**
- * Add sockets and POLLIN/OUT flags for connections handled by the pool.
- */
-CURLcode Curl_cpool_add_pollfds(struct cpool *connc,
- struct curl_pollfds *cpfds);
-CURLcode Curl_cpool_add_waitfds(struct cpool *connc,
- struct curl_waitfds *cwfds);
-
-/**
- * Perform maintenance on connections in the pool. Specifically,
- * progress the shutdown of connections in the queue.
- */
-void Curl_cpool_multi_perform(struct Curl_multi *multi);
-
-void Curl_cpool_multi_socket(struct Curl_multi *multi,
- curl_socket_t s, int ev_bitmask);
-
-
#endif /* HEADER_CURL_CONNCACHE_H */
diff --git a/libs/libcurl/src/connect.c b/libs/libcurl/src/connect.c
index 6fcdd3eac7..cbc31a2bc3 100644
--- a/libs/libcurl/src/connect.c
+++ b/libs/libcurl/src/connect.c
@@ -67,25 +67,43 @@
#include "multiif.h"
#include "sockaddr.h" /* required for Curl_sockaddr_storage */
#include "inet_ntop.h"
-#include "inet_pton.h"
+#include "curlx/inet_pton.h"
#include "vtls/vtls.h" /* for vtsl cfilters */
#include "progress.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
#include "conncache.h"
#include "multihandle.h"
#include "share.h"
-#include "version_win32.h"
+#include "curlx/version_win32.h"
#include "vquic/vquic.h" /* for quic cfilters */
#include "http_proxy.h"
#include "socks.h"
+#include "strcase.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
#include "memdebug.h"
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+#if !defined(CURL_DISABLE_ALTSVC) || defined(USE_HTTPSRR)
+
+enum alpnid Curl_alpn2alpnid(const char *name, size_t len)
+{
+ if(len == 2) {
+ if(strncasecompare(name, "h1", 2))
+ return ALPN_h1;
+ if(strncasecompare(name, "h2", 2))
+ return ALPN_h2;
+ if(strncasecompare(name, "h3", 2))
+ return ALPN_h3;
+ }
+ else if(len == 8) {
+ if(strncasecompare(name, "http/1.1", 8))
+ return ALPN_h1;
+ }
+ return ALPN_none; /* unknown, probably rubbish input */
+}
+
#endif
/*
@@ -94,7 +112,7 @@
* infinite time left). If the value is negative, the timeout time has already
* elapsed.
* @param data the transfer to check on
- * @param nowp timestamp to use for calculation, NULL to use Curl_now()
+ * @param nowp timestamp to use for calculation, NULL to use curlx_now()
* @param duringconnect TRUE iff connect timeout is also taken into account.
* @unittest: 1303
*/
@@ -115,13 +133,13 @@ timediff_t Curl_timeleft(struct Curl_easy *data,
return 0; /* no timeout in place or checked, return "no limit" */
if(!nowp) {
- now = Curl_now();
+ now = curlx_now();
nowp = &now;
}
if(data->set.timeout > 0) {
timeleft_ms = data->set.timeout -
- Curl_timediff(*nowp, data->progress.t_startop);
+ curlx_timediff(*nowp, data->progress.t_startop);
if(!timeleft_ms)
timeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
if(!duringconnect)
@@ -132,7 +150,7 @@ timediff_t Curl_timeleft(struct Curl_easy *data,
timediff_t ctimeout_ms = (data->set.connecttimeout > 0) ?
data->set.connecttimeout : DEFAULT_CONNECT_TIMEOUT;
ctimeleft_ms = ctimeout_ms -
- Curl_timediff(*nowp, data->progress.t_startsingle);
+ curlx_timediff(*nowp, data->progress.t_startsingle);
if(!ctimeleft_ms)
ctimeleft_ms = -1; /* 0 is "no limit", fake 1 ms expiry */
if(!timeleft_ms)
@@ -143,18 +161,24 @@ timediff_t Curl_timeleft(struct Curl_easy *data,
}
void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
- struct curltime *nowp)
+ int timeout_ms, struct curltime *nowp)
{
struct curltime now;
DEBUGASSERT(data->conn);
if(!nowp) {
- now = Curl_now();
+ now = curlx_now();
nowp = &now;
}
data->conn->shutdown.start[sockindex] = *nowp;
- data->conn->shutdown.timeout_ms = (data->set.shutdowntimeout > 0) ?
- data->set.shutdowntimeout : DEFAULT_SHUTDOWN_TIMEOUT_MS;
+ data->conn->shutdown.timeout_ms = (timeout_ms > 0) ?
+ (unsigned int)timeout_ms :
+ ((data->set.shutdowntimeout > 0) ?
+ data->set.shutdowntimeout : DEFAULT_SHUTDOWN_TIMEOUT_MS);
+ /* Set a timer, unless we operate on the admin handle */
+ if(data->mid && data->conn->shutdown.timeout_ms)
+ Curl_expire_ex(data, nowp, data->conn->shutdown.timeout_ms,
+ EXPIRE_SHUTDOWN);
}
timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex,
@@ -167,11 +191,11 @@ timediff_t Curl_shutdown_timeleft(struct connectdata *conn, int sockindex,
return 0; /* not started or no limits */
if(!nowp) {
- now = Curl_now();
+ now = curlx_now();
nowp = &now;
}
left_ms = conn->shutdown.timeout_ms -
- Curl_timediff(*nowp, conn->shutdown.start[sockindex]);
+ curlx_timediff(*nowp, conn->shutdown.start[sockindex]);
return left_ms ? left_ms : -1;
}
@@ -186,7 +210,7 @@ timediff_t Curl_conn_shutdown_timeleft(struct connectdata *conn,
if(!conn->shutdown.start[i].tv_sec)
continue;
if(!nowp) {
- now = Curl_now();
+ now = curlx_now();
nowp = &now;
}
ms = Curl_shutdown_timeleft(conn, i, nowp);
@@ -248,8 +272,7 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
switch(sa->sa_family) {
case AF_INET:
si = (struct sockaddr_in *)(void *) sa;
- if(Curl_inet_ntop(sa->sa_family, &si->sin_addr,
- addr, MAX_IPADR_LEN)) {
+ if(Curl_inet_ntop(sa->sa_family, &si->sin_addr, addr, MAX_IPADR_LEN)) {
unsigned short us_port = ntohs(si->sin_port);
*port = us_port;
return TRUE;
@@ -258,8 +281,7 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
#ifdef USE_IPV6
case AF_INET6:
si6 = (struct sockaddr_in6 *)(void *) sa;
- if(Curl_inet_ntop(sa->sa_family, &si6->sin6_addr,
- addr, MAX_IPADR_LEN)) {
+ if(Curl_inet_ntop(sa->sa_family, &si6->sin6_addr, addr, MAX_IPADR_LEN)) {
unsigned short us_port = ntohs(si6->sin6_port);
*port = us_port;
return TRUE;
@@ -283,7 +305,7 @@ bool Curl_addr2string(struct sockaddr *sa, curl_socklen_t salen,
addr[0] = '\0';
*port = 0;
- errno = EAFNOSUPPORT;
+ CURL_SETERRNO(SOCKEAFNOSUPPORT);
return FALSE;
}
@@ -386,7 +408,6 @@ typedef enum {
struct cf_he_ctx {
int transport;
cf_ip_connect_create *cf_create;
- const struct Curl_dns_entry *remotehost;
cf_connect_state state;
struct eyeballer *baller[2];
struct eyeballer *winner;
@@ -523,7 +544,7 @@ static CURLcode baller_start(struct Curl_cfilter *cf,
baller->has_started = TRUE;
while(baller->addr) {
- baller->started = Curl_now();
+ baller->started = curlx_now();
baller->timeoutms = addr_next_match(baller->addr, baller->ai_family) ?
USETIME(timeoutms) : timeoutms;
baller_initiate(cf, data, baller);
@@ -575,18 +596,18 @@ static CURLcode baller_connect(struct Curl_cfilter *cf,
*connected = baller->connected;
if(!baller->result && !*connected) {
/* evaluate again */
- baller->result = Curl_conn_cf_connect(baller->cf, data, 0, connected);
+ baller->result = Curl_conn_cf_connect(baller->cf, data, connected);
if(!baller->result) {
if(*connected) {
baller->connected = TRUE;
baller->is_done = TRUE;
}
- else if(Curl_timediff(*now, baller->started) >= baller->timeoutms) {
+ else if(curlx_timediff(*now, baller->started) >= baller->timeoutms) {
infof(data, "%s connect timeout after %" FMT_TIMEDIFF_T
"ms, move on!", baller->name, baller->timeoutms);
-#if defined(ETIMEDOUT)
- baller->error = ETIMEDOUT;
+#ifdef SOCKETIMEDOUT
+ baller->error = SOCKETIMEDOUT;
#endif
baller->result = CURLE_OPERATION_TIMEDOUT;
}
@@ -620,9 +641,9 @@ static CURLcode is_connected(struct Curl_cfilter *cf,
* cot ballers in a QUIC appropriate way. */
evaluate:
*connected = FALSE; /* a negative world view is best */
- now = Curl_now();
+ now = curlx_now();
ongoing = not_started = 0;
- for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
+ for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
struct eyeballer *baller = ctx->baller[i];
if(!baller || baller->is_done)
@@ -675,7 +696,7 @@ evaluate:
* start new ballers or return ok. */
if((ongoing || not_started) && Curl_timeleft(data, &now, TRUE) < 0) {
failf(data, "Connection timeout after %" FMT_OFF_T " ms",
- Curl_timediff(now, data->progress.t_startsingle));
+ curlx_timediff(now, data->progress.t_startsingle));
return CURLE_OPERATION_TIMEDOUT;
}
@@ -683,7 +704,7 @@ evaluate:
if(not_started > 0) {
int added = 0;
- for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
+ for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
struct eyeballer *baller = ctx->baller[i];
if(!baller || baller->has_started)
@@ -691,7 +712,7 @@ evaluate:
/* We start its primary baller has failed to connect or if
* its start delay_ms have expired */
if((baller->primary && baller->primary->is_done) ||
- Curl_timediff(now, ctx->started) >= baller->delay_ms) {
+ curlx_timediff(now, ctx->started) >= baller->delay_ms) {
baller_start(cf, data, baller, Curl_timeleft(data, &now, TRUE));
if(baller->is_done) {
CURL_TRC_CF(data, cf, "%s done", baller->name);
@@ -717,7 +738,7 @@ evaluate:
/* all ballers have failed to connect. */
CURL_TRC_CF(data, cf, "all eyeballers failed");
result = CURLE_COULDNT_CONNECT;
- for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
+ for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
struct eyeballer *baller = ctx->baller[i];
if(!baller)
continue;
@@ -744,14 +765,11 @@ evaluate:
failf(data, "Failed to connect to %s port %u after "
"%" FMT_TIMEDIFF_T " ms: %s",
hostname, conn->primary.remote_port,
- Curl_timediff(now, data->progress.t_startsingle),
+ curlx_timediff(now, data->progress.t_startsingle),
curl_easy_strerror(result));
-#ifdef WSAETIMEDOUT
- if(WSAETIMEDOUT == data->state.os_errno)
- result = CURLE_OPERATION_TIMEDOUT;
-#elif defined(ETIMEDOUT)
- if(ETIMEDOUT == data->state.os_errno)
+#ifdef SOCKETIMEDOUT
+ if(SOCKETIMEDOUT == data->state.os_errno)
result = CURLE_OPERATION_TIMEDOUT;
#endif
@@ -763,8 +781,7 @@ evaluate:
* There might be more than one IP address to try out.
*/
static CURLcode start_connect(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const struct Curl_dns_entry *remotehost)
+ struct Curl_easy *data)
{
struct cf_he_ctx *ctx = cf->ctx;
struct connectdata *conn = cf->conn;
@@ -772,6 +789,10 @@ static CURLcode start_connect(struct Curl_cfilter *cf,
int ai_family0 = 0, ai_family1 = 0;
timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
const struct Curl_addrinfo *addr0 = NULL, *addr1 = NULL;
+ struct Curl_dns_entry *dns = data->state.dns[cf->sockindex];
+
+ if(!dns)
+ return CURLE_FAILED_INIT;
if(timeout_ms < 0) {
/* a precaution, no need to continue if time already is up */
@@ -779,9 +800,9 @@ static CURLcode start_connect(struct Curl_cfilter *cf,
return CURLE_OPERATION_TIMEDOUT;
}
- ctx->started = Curl_now();
+ ctx->started = curlx_now();
- /* remotehost->addr is the list of addresses from the resolver, each
+ /* dns->addr is the list of addresses from the resolver, each
* with an address family. The list has at least one entry, possibly
* many more.
* We try at most 2 at a time, until we either get a connection or
@@ -793,27 +814,27 @@ static CURLcode start_connect(struct Curl_cfilter *cf,
if(conn->ip_version == CURL_IPRESOLVE_V6) {
#ifdef USE_IPV6
ai_family0 = AF_INET6;
- addr0 = addr_first_match(remotehost->addr, ai_family0);
+ addr0 = addr_first_match(dns->addr, ai_family0);
#endif
}
else if(conn->ip_version == CURL_IPRESOLVE_V4) {
ai_family0 = AF_INET;
- addr0 = addr_first_match(remotehost->addr, ai_family0);
+ addr0 = addr_first_match(dns->addr, ai_family0);
}
else {
/* no user preference, we try ipv6 always first when available */
#ifdef USE_IPV6
ai_family0 = AF_INET6;
- addr0 = addr_first_match(remotehost->addr, ai_family0);
+ addr0 = addr_first_match(dns->addr, ai_family0);
#endif
/* next candidate is ipv4 */
ai_family1 = AF_INET;
- addr1 = addr_first_match(remotehost->addr, ai_family1);
+ addr1 = addr_first_match(dns->addr, ai_family1);
/* no ip address families, probably AF_UNIX or something, use the
* address family given to us */
- if(!addr1 && !addr0 && remotehost->addr) {
- ai_family0 = remotehost->addr->ai_family;
- addr0 = addr_first_match(remotehost->addr, ai_family0);
+ if(!addr1 && !addr0 && dns->addr) {
+ ai_family0 = dns->addr->ai_family;
+ addr0 = addr_first_match(dns->addr, ai_family0);
}
}
@@ -862,7 +883,7 @@ static void cf_he_ctx_clear(struct Curl_cfilter *cf, struct Curl_easy *data)
DEBUGASSERT(ctx);
DEBUGASSERT(data);
- for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
+ for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
baller_free(ctx->baller[i], data);
ctx->baller[i] = NULL;
}
@@ -885,7 +906,7 @@ static CURLcode cf_he_shutdown(struct Curl_cfilter *cf,
/* shutdown all ballers that have not done so already. If one fails,
* continue shutting down others until all are shutdown. */
- for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
+ for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
struct eyeballer *baller = ctx->baller[i];
bool bdone = FALSE;
if(!baller || !baller->cf || baller->shutdown)
@@ -896,12 +917,12 @@ static CURLcode cf_he_shutdown(struct Curl_cfilter *cf,
}
*done = TRUE;
- for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
+ for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
if(ctx->baller[i] && !ctx->baller[i]->shutdown)
*done = FALSE;
}
if(*done) {
- for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
+ for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
if(ctx->baller[i] && ctx->baller[i]->result)
result = ctx->baller[i]->result;
}
@@ -918,7 +939,7 @@ static void cf_he_adjust_pollset(struct Curl_cfilter *cf,
size_t i;
if(!cf->connected) {
- for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
+ for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
struct eyeballer *baller = ctx->baller[i];
if(!baller || !baller->cf)
continue;
@@ -930,7 +951,7 @@ static void cf_he_adjust_pollset(struct Curl_cfilter *cf,
static CURLcode cf_he_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool blocking, bool *done)
+ bool *done)
{
struct cf_he_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
@@ -940,7 +961,6 @@ static CURLcode cf_he_connect(struct Curl_cfilter *cf,
return CURLE_OK;
}
- (void)blocking; /* TODO: do we want to support this? */
DEBUGASSERT(ctx);
*done = FALSE;
@@ -948,7 +968,7 @@ static CURLcode cf_he_connect(struct Curl_cfilter *cf,
case SCFST_INIT:
DEBUGASSERT(CURL_SOCKET_BAD == Curl_conn_cf_get_socket(cf, data));
DEBUGASSERT(!cf->connected);
- result = start_connect(cf, data, ctx->remotehost);
+ result = start_connect(cf, data);
if(result)
return result;
ctx->state = SCFST_WAITING;
@@ -1015,7 +1035,7 @@ static bool cf_he_data_pending(struct Curl_cfilter *cf,
if(cf->connected)
return cf->next->cft->has_data_pending(cf->next, data);
- for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
+ for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
struct eyeballer *baller = ctx->baller[i];
if(!baller || !baller->cf)
continue;
@@ -1034,13 +1054,13 @@ static struct curltime get_max_baller_time(struct Curl_cfilter *cf,
size_t i;
memset(&tmax, 0, sizeof(tmax));
- for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
+ for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
struct eyeballer *baller = ctx->baller[i];
memset(&t, 0, sizeof(t));
if(baller && baller->cf &&
!baller->cf->cft->query(baller->cf, data, query, NULL, &t)) {
- if((t.tv_sec || t.tv_usec) && Curl_timediff_us(t, tmax) > 0)
+ if((t.tv_sec || t.tv_usec) && curlx_timediff_us(t, tmax) > 0)
tmax = t;
}
}
@@ -1059,7 +1079,7 @@ static CURLcode cf_he_query(struct Curl_cfilter *cf,
int reply_ms = -1;
size_t i;
- for(i = 0; i < ARRAYSIZE(ctx->baller); i++) {
+ for(i = 0; i < CURL_ARRAYSIZE(ctx->baller); i++) {
struct eyeballer *baller = ctx->baller[i];
int breply_ms;
@@ -1140,7 +1160,6 @@ cf_happy_eyeballs_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
struct connectdata *conn,
cf_ip_connect_create *cf_create,
- const struct Curl_dns_entry *remotehost,
int transport)
{
struct cf_he_ctx *ctx = NULL;
@@ -1156,14 +1175,13 @@ cf_happy_eyeballs_create(struct Curl_cfilter **pcf,
}
ctx->transport = transport;
ctx->cf_create = cf_create;
- ctx->remotehost = remotehost;
result = Curl_cf_create(pcf, &Curl_cft_happy_eyeballs, ctx);
out:
if(result) {
Curl_safefree(*pcf);
- Curl_safefree(ctx);
+ free(ctx);
}
return result;
}
@@ -1193,7 +1211,7 @@ struct transport_provider transport_providers[] = {
static cf_ip_connect_create *get_cf_create(int transport)
{
size_t i;
- for(i = 0; i < ARRAYSIZE(transport_providers); ++i) {
+ for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) {
if(transport == transport_providers[i].transport)
return transport_providers[i].cf_create;
}
@@ -1202,7 +1220,6 @@ static cf_ip_connect_create *get_cf_create(int transport)
static CURLcode cf_he_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
- const struct Curl_dns_entry *remotehost,
int transport)
{
cf_ip_connect_create *cf_create;
@@ -1217,8 +1234,7 @@ static CURLcode cf_he_insert_after(struct Curl_cfilter *cf_at,
return CURLE_UNSUPPORTED_PROTOCOL;
}
result = cf_happy_eyeballs_create(&cf, data, cf_at->conn,
- cf_create, remotehost,
- transport);
+ cf_create, transport);
if(result)
return result;
@@ -1238,17 +1254,17 @@ typedef enum {
struct cf_setup_ctx {
cf_setup_state state;
- const struct Curl_dns_entry *remotehost;
int ssl_mode;
int transport;
};
static CURLcode cf_setup_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool blocking, bool *done)
+ bool *done)
{
struct cf_setup_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
+ struct Curl_dns_entry *dns = data->state.dns[cf->sockindex];
if(cf->connected) {
*done = TRUE;
@@ -1257,14 +1273,17 @@ static CURLcode cf_setup_connect(struct Curl_cfilter *cf,
/* connect current sub-chain */
connect_sub_chain:
+ if(!dns)
+ return CURLE_FAILED_INIT;
+
if(cf->next && !cf->next->connected) {
- result = Curl_conn_cf_connect(cf->next, data, blocking, done);
+ result = Curl_conn_cf_connect(cf->next, data, done);
if(result || !*done)
return result;
}
if(ctx->state < CF_SETUP_CNNCT_EYEBALLS) {
- result = cf_he_insert_after(cf, data, ctx->remotehost, ctx->transport);
+ result = cf_he_insert_after(cf, data, ctx->transport);
if(result)
return result;
ctx->state = CF_SETUP_CNNCT_EYEBALLS;
@@ -1392,7 +1411,6 @@ struct Curl_cftype Curl_cft_setup = {
static CURLcode cf_setup_create(struct Curl_cfilter **pcf,
struct Curl_easy *data,
- const struct Curl_dns_entry *remotehost,
int transport,
int ssl_mode)
{
@@ -1407,7 +1425,6 @@ static CURLcode cf_setup_create(struct Curl_cfilter **pcf,
goto out;
}
ctx->state = CF_SETUP_INIT;
- ctx->remotehost = remotehost;
ctx->ssl_mode = ssl_mode;
ctx->transport = transport;
@@ -1418,14 +1435,15 @@ static CURLcode cf_setup_create(struct Curl_cfilter **pcf,
out:
*pcf = result ? NULL : cf;
- free(ctx);
+ if(ctx) {
+ free(ctx);
+ }
return result;
}
static CURLcode cf_setup_add(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
- const struct Curl_dns_entry *remotehost,
int transport,
int ssl_mode)
{
@@ -1433,7 +1451,7 @@ static CURLcode cf_setup_add(struct Curl_easy *data,
CURLcode result = CURLE_OK;
DEBUGASSERT(data);
- result = cf_setup_create(&cf, data, remotehost, transport, ssl_mode);
+ result = cf_setup_create(&cf, data, transport, ssl_mode);
if(result)
goto out;
Curl_conn_cf_add(data, conn, sockindex, cf);
@@ -1447,7 +1465,7 @@ void Curl_debug_set_transport_provider(int transport,
cf_ip_connect_create *cf_create)
{
size_t i;
- for(i = 0; i < ARRAYSIZE(transport_providers); ++i) {
+ for(i = 0; i < CURL_ARRAYSIZE(transport_providers); ++i) {
if(transport == transport_providers[i].transport) {
transport_providers[i].cf_create = cf_create;
return;
@@ -1458,7 +1476,6 @@ void Curl_debug_set_transport_provider(int transport,
CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
- const struct Curl_dns_entry *remotehost,
int transport,
int ssl_mode)
{
@@ -1466,7 +1483,7 @@ CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
CURLcode result;
DEBUGASSERT(data);
- result = cf_setup_create(&cf, data, remotehost, transport, ssl_mode);
+ result = cf_setup_create(&cf, data, transport, ssl_mode);
if(result)
goto out;
Curl_conn_cf_insert_after(cf_at, cf);
@@ -1477,33 +1494,38 @@ out:
CURLcode Curl_conn_setup(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
- const struct Curl_dns_entry *remotehost,
+ struct Curl_dns_entry *dns,
int ssl_mode)
{
CURLcode result = CURLE_OK;
DEBUGASSERT(data);
DEBUGASSERT(conn->handler);
+ DEBUGASSERT(dns);
-#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER)
+ Curl_resolv_unlink(data, &data->state.dns[sockindex]);
+ data->state.dns[sockindex] = dns;
+
+#if !defined(CURL_DISABLE_HTTP)
if(!conn->cfilter[sockindex] &&
conn->handler->protocol == CURLPROTO_HTTPS) {
DEBUGASSERT(ssl_mode != CURL_CF_SSL_DISABLE);
- result = Curl_cf_https_setup(data, conn, sockindex, remotehost);
+ result = Curl_cf_https_setup(data, conn, sockindex);
if(result)
goto out;
}
-#endif /* !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER) */
+#endif /* !defined(CURL_DISABLE_HTTP) */
/* Still no cfilter set, apply default. */
if(!conn->cfilter[sockindex]) {
- result = cf_setup_add(data, conn, sockindex, remotehost,
- conn->transport, ssl_mode);
+ result = cf_setup_add(data, conn, sockindex, conn->transport, ssl_mode);
if(result)
goto out;
}
DEBUGASSERT(conn->cfilter[sockindex]);
out:
+ if(result)
+ Curl_resolv_unlink(data, &data->state.dns[sockindex]);
return result;
}
diff --git a/libs/libcurl/src/connect.h b/libs/libcurl/src/connect.h
index c9a12188c4..d44fb6b4f6 100644
--- a/libs/libcurl/src/connect.h
+++ b/libs/libcurl/src/connect.h
@@ -25,13 +25,15 @@
***************************************************************************/
#include "curl_setup.h"
-#include "nonblock.h" /* for curlx_nonblock(), formerly Curl_nonblock() */
+#include "curlx/nonblock.h" /* for curlx_nonblock() */
#include "sockaddr.h"
-#include "timeval.h"
+#include "curlx/timeval.h"
struct Curl_dns_entry;
struct ip_quadruple;
+enum alpnid Curl_alpn2alpnid(const char *name, size_t len);
+
/* generic function that returns how much time there is left to run, according
to the timeouts set */
timediff_t Curl_timeleft(struct Curl_easy *data,
@@ -43,7 +45,7 @@ timediff_t Curl_timeleft(struct Curl_easy *data,
#define DEFAULT_SHUTDOWN_TIMEOUT_MS (2 * 1000)
void Curl_shutdown_start(struct Curl_easy *data, int sockindex,
- struct curltime *nowp);
+ int timeout_ms, struct curltime *nowp);
/* return how much time there is left to shutdown the connection at
* sockindex. Returns 0 if there is no limit or shutdown has not started. */
@@ -124,7 +126,6 @@ typedef CURLcode cf_ip_connect_create(struct Curl_cfilter **pcf,
CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
struct Curl_easy *data,
- const struct Curl_dns_entry *remotehost,
int transport,
int ssl_mode);
@@ -136,7 +137,7 @@ CURLcode Curl_cf_setup_insert_after(struct Curl_cfilter *cf_at,
CURLcode Curl_conn_setup(struct Curl_easy *data,
struct connectdata *conn,
int sockindex,
- const struct Curl_dns_entry *remotehost,
+ struct Curl_dns_entry *dns,
int ssl_mode);
extern struct Curl_cftype Curl_cft_happy_eyeballs;
diff --git a/libs/libcurl/src/content_encoding.c b/libs/libcurl/src/content_encoding.c
index cfd3a51c35..f553f3c0fb 100644
--- a/libs/libcurl/src/content_encoding.c
+++ b/libs/libcurl/src/content_encoding.c
@@ -66,33 +66,21 @@
/* allow no more than 5 "chained" compression steps */
#define MAX_ENCODE_STACK 5
-#define DSIZ CURL_MAX_WRITE_SIZE /* buffer size for decompressed data */
-
+#if defined(HAVE_LIBZ) || defined(HAVE_BROTLI) || defined(HAVE_ZSTD)
+#define DECOMPRESS_BUFFER_SIZE 16384 /* buffer size for decompressed data */
+#endif
#ifdef HAVE_LIBZ
-/* Comment this out if zlib is always going to be at least ver. 1.2.0.4
- (doing so will reduce code size slightly). */
-#define OLD_ZLIB_SUPPORT 1
-
-#define GZIP_MAGIC_0 0x1f
-#define GZIP_MAGIC_1 0x8b
-
-/* gzip flag byte */
-#define ASCII_FLAG 0x01 /* bit 0 set: file probably ASCII text */
-#define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
-#define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
-#define ORIG_NAME 0x08 /* bit 3 set: original filename present */
-#define COMMENT 0x10 /* bit 4 set: file comment present */
-#define RESERVED 0xE0 /* bits 5..7: reserved */
+#if !defined(ZLIB_VERNUM) || (ZLIB_VERNUM < 0x1252)
+#error "requires zlib 1.2.5.2 or newer"
+#endif
typedef enum {
ZLIB_UNINIT, /* uninitialized */
ZLIB_INIT, /* initialized */
ZLIB_INFLATING, /* inflating started. */
ZLIB_EXTERNAL_TRAILER, /* reading external trailer */
- ZLIB_GZIP_HEADER, /* reading gzip header */
- ZLIB_GZIP_INFLATING, /* inflating gzip stream */
ZLIB_INIT_GZIP /* initialized in transparent gzip mode */
} zlibInitState;
@@ -100,6 +88,7 @@ typedef enum {
struct zlib_writer {
struct Curl_cwriter super;
zlibInitState zlib_init; /* zlib init state */
+ char buffer[DECOMPRESS_BUFFER_SIZE]; /* Put the decompressed data here. */
uInt trailerlen; /* Remaining trailer byte count. */
z_stream z; /* State structure for zlib. */
};
@@ -137,9 +126,6 @@ static CURLcode
exit_zlib(struct Curl_easy *data,
z_stream *z, zlibInitState *zlib_init, CURLcode result)
{
- if(*zlib_init == ZLIB_GZIP_HEADER)
- Curl_safefree(z->next_in);
-
if(*zlib_init != ZLIB_UNINIT) {
if(inflateEnd(z) != Z_OK && result == CURLE_OK)
result = process_zlib_error(data, z);
@@ -180,24 +166,16 @@ static CURLcode inflate_stream(struct Curl_easy *data,
struct zlib_writer *zp = (struct zlib_writer *) writer;
z_stream *z = &zp->z; /* zlib state structure */
uInt nread = z->avail_in;
- Bytef *orig_in = z->next_in;
+ z_const Bytef *orig_in = z->next_in;
bool done = FALSE;
CURLcode result = CURLE_OK; /* Curl_client_write status */
- char *decomp; /* Put the decompressed data here. */
/* Check state. */
if(zp->zlib_init != ZLIB_INIT &&
zp->zlib_init != ZLIB_INFLATING &&
- zp->zlib_init != ZLIB_INIT_GZIP &&
- zp->zlib_init != ZLIB_GZIP_INFLATING)
+ zp->zlib_init != ZLIB_INIT_GZIP)
return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR);
- /* Dynamically allocate a buffer for decompression because it is uncommonly
- large to hold on the stack */
- decomp = malloc(DSIZ);
- if(!decomp)
- return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY);
-
/* because the buffer size is fixed, iteratively decompress and transfer to
the client via next_write function. */
while(!done) {
@@ -205,23 +183,17 @@ static CURLcode inflate_stream(struct Curl_easy *data,
done = TRUE;
/* (re)set buffer for decompressed output for every iteration */
- z->next_out = (Bytef *) decomp;
- z->avail_out = DSIZ;
+ z->next_out = (Bytef *) zp->buffer;
+ z->avail_out = DECOMPRESS_BUFFER_SIZE;
-#ifdef Z_BLOCK
- /* Z_BLOCK is only available in zlib ver. >= 1.2.0.5 */
status = inflate(z, Z_BLOCK);
-#else
- /* fallback for zlib ver. < 1.2.0.5 */
- status = inflate(z, Z_SYNC_FLUSH);
-#endif
/* Flush output data if some. */
- if(z->avail_out != DSIZ) {
+ if(z->avail_out != DECOMPRESS_BUFFER_SIZE) {
if(status == Z_OK || status == Z_STREAM_END) {
zp->zlib_init = started; /* Data started. */
- result = Curl_cwriter_write(data, writer->next, type, decomp,
- DSIZ - z->avail_out);
+ result = Curl_cwriter_write(data, writer->next, type, zp->buffer,
+ DECOMPRESS_BUFFER_SIZE - z->avail_out);
if(result) {
exit_zlib(data, z, &zp->zlib_init, result);
break;
@@ -245,9 +217,7 @@ static CURLcode inflate_stream(struct Curl_easy *data,
/* some servers seem to not generate zlib headers, so this is an attempt
to fix and continue anyway */
if(zp->zlib_init == ZLIB_INIT) {
- /* Do not use inflateReset2(): only available since zlib 1.2.3.4. */
- (void) inflateEnd(z); /* do not care about the return code */
- if(inflateInit2(z, -MAX_WBITS) == Z_OK) {
+ if(inflateReset2(z, -MAX_WBITS) == Z_OK) {
z->next_in = orig_in;
z->avail_in = nread;
zp->zlib_init = ZLIB_INFLATING;
@@ -264,7 +234,6 @@ static CURLcode inflate_stream(struct Curl_easy *data,
break;
}
}
- free(decomp);
/* We are about to leave this call so the `nread' data bytes will not be seen
again. If we are in a state that would wrongly allow restart in raw mode
@@ -278,7 +247,7 @@ static CURLcode inflate_stream(struct Curl_easy *data,
/* Deflate handler. */
static CURLcode deflate_do_init(struct Curl_easy *data,
- struct Curl_cwriter *writer)
+ struct Curl_cwriter *writer)
{
struct zlib_writer *zp = (struct zlib_writer *) writer;
z_stream *z = &zp->z; /* zlib state structure */
@@ -294,8 +263,8 @@ static CURLcode deflate_do_init(struct Curl_easy *data,
}
static CURLcode deflate_do_write(struct Curl_easy *data,
- struct Curl_cwriter *writer, int type,
- const char *buf, size_t nbytes)
+ struct Curl_cwriter *writer, int type,
+ const char *buf, size_t nbytes)
{
struct zlib_writer *zp = (struct zlib_writer *) writer;
z_stream *z = &zp->z; /* zlib state structure */
@@ -304,8 +273,8 @@ static CURLcode deflate_do_write(struct Curl_easy *data,
return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
/* Set the compressed input when this function is called */
- z->next_in = (Bytef *) buf;
- z->avail_in = (uInt) nbytes;
+ z->next_in = (z_const Bytef *)buf;
+ z->avail_in = (uInt)nbytes;
if(zp->zlib_init == ZLIB_EXTERNAL_TRAILER)
return process_trailer(data, zp);
@@ -315,7 +284,7 @@ static CURLcode deflate_do_write(struct Curl_easy *data,
}
static void deflate_do_close(struct Curl_easy *data,
- struct Curl_cwriter *writer)
+ struct Curl_cwriter *writer)
{
struct zlib_writer *zp = (struct zlib_writer *) writer;
z_stream *z = &zp->z; /* zlib state structure */
@@ -335,7 +304,7 @@ static const struct Curl_cwtype deflate_encoding = {
/* Gzip handler. */
static CURLcode gzip_do_init(struct Curl_easy *data,
- struct Curl_cwriter *writer)
+ struct Curl_cwriter *writer)
{
struct zlib_writer *zp = (struct zlib_writer *) writer;
z_stream *z = &zp->z; /* zlib state structure */
@@ -344,115 +313,16 @@ static CURLcode gzip_do_init(struct Curl_easy *data,
z->zalloc = (alloc_func) zalloc_cb;
z->zfree = (free_func) zfree_cb;
- if(strcmp(zlibVersion(), "1.2.0.4") >= 0) {
- /* zlib ver. >= 1.2.0.4 supports transparent gzip decompressing */
- if(inflateInit2(z, MAX_WBITS + 32) != Z_OK) {
- return process_zlib_error(data, z);
- }
- zp->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */
- }
- else {
- /* we must parse the gzip header and trailer ourselves */
- if(inflateInit2(z, -MAX_WBITS) != Z_OK) {
- return process_zlib_error(data, z);
- }
- zp->trailerlen = 8; /* A CRC-32 and a 32-bit input size (RFC 1952, 2.2) */
- zp->zlib_init = ZLIB_INIT; /* Initial call state */
- }
+ if(inflateInit2(z, MAX_WBITS + 32) != Z_OK)
+ return process_zlib_error(data, z);
+ zp->zlib_init = ZLIB_INIT_GZIP; /* Transparent gzip decompress state */
return CURLE_OK;
}
-#ifdef OLD_ZLIB_SUPPORT
-/* Skip over the gzip header */
-typedef enum {
- GZIP_OK,
- GZIP_BAD,
- GZIP_UNDERFLOW
-} gzip_status;
-
-static gzip_status check_gzip_header(unsigned char const *data, ssize_t len,
- ssize_t *headerlen)
-{
- int method, flags;
- const ssize_t totallen = len;
-
- /* The shortest header is 10 bytes */
- if(len < 10)
- return GZIP_UNDERFLOW;
-
- if((data[0] != GZIP_MAGIC_0) || (data[1] != GZIP_MAGIC_1))
- return GZIP_BAD;
-
- method = data[2];
- flags = data[3];
-
- if(method != Z_DEFLATED || (flags & RESERVED) != 0) {
- /* cannot handle this compression method or unknown flag */
- return GZIP_BAD;
- }
-
- /* Skip over time, xflags, OS code and all previous bytes */
- len -= 10;
- data += 10;
-
- if(flags & EXTRA_FIELD) {
- ssize_t extra_len;
-
- if(len < 2)
- return GZIP_UNDERFLOW;
-
- extra_len = (data[1] << 8) | data[0];
-
- if(len < (extra_len + 2))
- return GZIP_UNDERFLOW;
-
- len -= (extra_len + 2);
- data += (extra_len + 2);
- }
-
- if(flags & ORIG_NAME) {
- /* Skip over NUL-terminated filename */
- while(len && *data) {
- --len;
- ++data;
- }
- if(!len || *data)
- return GZIP_UNDERFLOW;
-
- /* Skip over the NUL */
- --len;
- ++data;
- }
-
- if(flags & COMMENT) {
- /* Skip over NUL-terminated comment */
- while(len && *data) {
- --len;
- ++data;
- }
- if(!len || *data)
- return GZIP_UNDERFLOW;
-
- /* Skip over the NUL */
- --len;
- }
-
- if(flags & HEAD_CRC) {
- if(len < 2)
- return GZIP_UNDERFLOW;
-
- len -= 2;
- }
-
- *headerlen = totallen - len;
- return GZIP_OK;
-}
-#endif
-
static CURLcode gzip_do_write(struct Curl_easy *data,
- struct Curl_cwriter *writer, int type,
- const char *buf, size_t nbytes)
+ struct Curl_cwriter *writer, int type,
+ const char *buf, size_t nbytes)
{
struct zlib_writer *zp = (struct zlib_writer *) writer;
z_stream *z = &zp->z; /* zlib state structure */
@@ -462,123 +332,14 @@ static CURLcode gzip_do_write(struct Curl_easy *data,
if(zp->zlib_init == ZLIB_INIT_GZIP) {
/* Let zlib handle the gzip decompression entirely */
- z->next_in = (Bytef *) buf;
- z->avail_in = (uInt) nbytes;
+ z->next_in = (z_const Bytef *)buf;
+ z->avail_in = (uInt)nbytes;
/* Now uncompress the data */
return inflate_stream(data, writer, type, ZLIB_INIT_GZIP);
}
-#ifndef OLD_ZLIB_SUPPORT
- /* Support for old zlib versions is compiled away and we are running with
- an old version, so return an error. */
+ /* We are running with an old version: return error. */
return exit_zlib(data, z, &zp->zlib_init, CURLE_WRITE_ERROR);
-
-#else
- /* This next mess is to get around the potential case where there is not
- * enough data passed in to skip over the gzip header. If that happens, we
- * malloc a block and copy what we have then wait for the next call. If
- * there still is not enough (this is definitely a worst-case scenario), we
- * make the block bigger, copy the next part in and keep waiting.
- *
- * This is only required with zlib versions < 1.2.0.4 as newer versions
- * can handle the gzip header themselves.
- */
-
- switch(zp->zlib_init) {
- /* Skip over gzip header? */
- case ZLIB_INIT:
- {
- /* Initial call state */
- ssize_t hlen;
-
- switch(check_gzip_header((unsigned char *) buf, nbytes, &hlen)) {
- case GZIP_OK:
- z->next_in = (Bytef *) buf + hlen;
- z->avail_in = (uInt) (nbytes - hlen);
- zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */
- break;
-
- case GZIP_UNDERFLOW:
- /* We need more data so we can find the end of the gzip header. it is
- * possible that the memory block we malloc here will never be freed if
- * the transfer abruptly aborts after this point. Since it is unlikely
- * that circumstances will be right for this code path to be followed in
- * the first place, and it is even more unlikely for a transfer to fail
- * immediately afterwards, it should seldom be a problem.
- */
- z->avail_in = (uInt) nbytes;
- z->next_in = malloc(z->avail_in);
- if(!z->next_in) {
- return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY);
- }
- memcpy(z->next_in, buf, z->avail_in);
- zp->zlib_init = ZLIB_GZIP_HEADER; /* Need more gzip header data state */
- /* We do not have any data to inflate yet */
- return CURLE_OK;
-
- case GZIP_BAD:
- default:
- return exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z));
- }
-
- }
- break;
-
- case ZLIB_GZIP_HEADER:
- {
- /* Need more gzip header data state */
- ssize_t hlen;
- z->avail_in += (uInt) nbytes;
- z->next_in = Curl_saferealloc(z->next_in, z->avail_in);
- if(!z->next_in) {
- return exit_zlib(data, z, &zp->zlib_init, CURLE_OUT_OF_MEMORY);
- }
- /* Append the new block of data to the previous one */
- memcpy(z->next_in + z->avail_in - nbytes, buf, nbytes);
-
- switch(check_gzip_header(z->next_in, (ssize_t)z->avail_in, &hlen)) {
- case GZIP_OK:
- /* This is the zlib stream data */
- free(z->next_in);
- /* Do not point into the malloced block since we just freed it */
- z->next_in = (Bytef *) buf + hlen + nbytes - z->avail_in;
- z->avail_in = z->avail_in - (uInt)hlen;
- zp->zlib_init = ZLIB_GZIP_INFLATING; /* Inflating stream state */
- break;
-
- case GZIP_UNDERFLOW:
- /* We still do not have any data to inflate! */
- return CURLE_OK;
-
- case GZIP_BAD:
- default:
- return exit_zlib(data, z, &zp->zlib_init, process_zlib_error(data, z));
- }
-
- }
- break;
-
- case ZLIB_EXTERNAL_TRAILER:
- z->next_in = (Bytef *) buf;
- z->avail_in = (uInt) nbytes;
- return process_trailer(data, zp);
-
- case ZLIB_GZIP_INFLATING:
- default:
- /* Inflating stream state */
- z->next_in = (Bytef *) buf;
- z->avail_in = (uInt) nbytes;
- break;
- }
-
- if(z->avail_in == 0) {
- /* We do not have any data to inflate; wait until next time */
- return CURLE_OK;
- }
-
- /* We have parsed the header, now uncompress the data */
- return inflate_stream(data, writer, type, ZLIB_GZIP_INFLATING);
-#endif
}
static void gzip_do_close(struct Curl_easy *data,
@@ -601,11 +362,11 @@ static const struct Curl_cwtype gzip_encoding = {
#endif /* HAVE_LIBZ */
-
#ifdef HAVE_BROTLI
/* Brotli writer. */
struct brotli_writer {
struct Curl_cwriter super;
+ char buffer[DECOMPRESS_BUFFER_SIZE];
BrotliDecoderState *br; /* State structure for brotli. */
};
@@ -648,7 +409,7 @@ static CURLcode brotli_map_error(BrotliDecoderErrorCode be)
}
static CURLcode brotli_do_init(struct Curl_easy *data,
- struct Curl_cwriter *writer)
+ struct Curl_cwriter *writer)
{
struct brotli_writer *bp = (struct brotli_writer *) writer;
(void) data;
@@ -658,12 +419,11 @@ static CURLcode brotli_do_init(struct Curl_easy *data,
}
static CURLcode brotli_do_write(struct Curl_easy *data,
- struct Curl_cwriter *writer, int type,
- const char *buf, size_t nbytes)
+ struct Curl_cwriter *writer, int type,
+ const char *buf, size_t nbytes)
{
struct brotli_writer *bp = (struct brotli_writer *) writer;
const uint8_t *src = (const uint8_t *) buf;
- char *decomp;
uint8_t *dst;
size_t dstleft;
CURLcode result = CURLE_OK;
@@ -675,18 +435,14 @@ static CURLcode brotli_do_write(struct Curl_easy *data,
if(!bp->br)
return CURLE_WRITE_ERROR; /* Stream already ended. */
- decomp = malloc(DSIZ);
- if(!decomp)
- return CURLE_OUT_OF_MEMORY;
-
while((nbytes || r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) &&
result == CURLE_OK) {
- dst = (uint8_t *) decomp;
- dstleft = DSIZ;
+ dst = (uint8_t *) bp->buffer;
+ dstleft = DECOMPRESS_BUFFER_SIZE;
r = BrotliDecoderDecompressStream(bp->br,
&nbytes, &src, &dstleft, &dst, NULL);
result = Curl_cwriter_write(data, writer->next, type,
- decomp, DSIZ - dstleft);
+ bp->buffer, DECOMPRESS_BUFFER_SIZE - dstleft);
if(result)
break;
switch(r) {
@@ -704,7 +460,6 @@ static CURLcode brotli_do_write(struct Curl_easy *data,
break;
}
}
- free(decomp);
return result;
}
@@ -712,7 +467,6 @@ static void brotli_do_close(struct Curl_easy *data,
struct Curl_cwriter *writer)
{
struct brotli_writer *bp = (struct brotli_writer *) writer;
-
(void) data;
if(bp->br) {
@@ -731,30 +485,51 @@ static const struct Curl_cwtype brotli_encoding = {
};
#endif
-
#ifdef HAVE_ZSTD
/* Zstd writer. */
struct zstd_writer {
struct Curl_cwriter super;
ZSTD_DStream *zds; /* State structure for zstd. */
- void *decomp;
+ char buffer[DECOMPRESS_BUFFER_SIZE];
};
+#ifdef ZSTD_STATIC_LINKING_ONLY
+static void *Curl_zstd_alloc(void *opaque, size_t size)
+{
+ (void)opaque;
+ return Curl_cmalloc(size);
+}
+
+static void Curl_zstd_free(void *opaque, void *address)
+{
+ (void)opaque;
+ Curl_cfree(address);
+}
+#endif
+
static CURLcode zstd_do_init(struct Curl_easy *data,
- struct Curl_cwriter *writer)
+ struct Curl_cwriter *writer)
{
struct zstd_writer *zp = (struct zstd_writer *) writer;
(void)data;
+#ifdef ZSTD_STATIC_LINKING_ONLY
+ zp->zds = ZSTD_createDStream_advanced((ZSTD_customMem) {
+ .customAlloc = Curl_zstd_alloc,
+ .customFree = Curl_zstd_free,
+ .opaque = NULL
+ });
+#else
zp->zds = ZSTD_createDStream();
- zp->decomp = NULL;
+#endif
+
return zp->zds ? CURLE_OK : CURLE_OUT_OF_MEMORY;
}
static CURLcode zstd_do_write(struct Curl_easy *data,
- struct Curl_cwriter *writer, int type,
- const char *buf, size_t nbytes)
+ struct Curl_cwriter *writer, int type,
+ const char *buf, size_t nbytes)
{
CURLcode result = CURLE_OK;
struct zstd_writer *zp = (struct zstd_writer *) writer;
@@ -765,19 +540,14 @@ static CURLcode zstd_do_write(struct Curl_easy *data,
if(!(type & CLIENTWRITE_BODY) || !nbytes)
return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
- if(!zp->decomp) {
- zp->decomp = malloc(DSIZ);
- if(!zp->decomp)
- return CURLE_OUT_OF_MEMORY;
- }
in.pos = 0;
in.src = buf;
in.size = nbytes;
for(;;) {
out.pos = 0;
- out.dst = zp->decomp;
- out.size = DSIZ;
+ out.dst = zp->buffer;
+ out.size = DECOMPRESS_BUFFER_SIZE;
errorCode = ZSTD_decompressStream(zp->zds, &out, &in);
if(ZSTD_isError(errorCode)) {
@@ -785,7 +555,7 @@ static CURLcode zstd_do_write(struct Curl_easy *data,
}
if(out.pos > 0) {
result = Curl_cwriter_write(data, writer->next, type,
- zp->decomp, out.pos);
+ zp->buffer, out.pos);
if(result)
break;
}
@@ -800,13 +570,8 @@ static void zstd_do_close(struct Curl_easy *data,
struct Curl_cwriter *writer)
{
struct zstd_writer *zp = (struct zstd_writer *) writer;
-
(void)data;
- if(zp->decomp) {
- free(zp->decomp);
- zp->decomp = NULL;
- }
if(zp->zds) {
ZSTD_freeDStream(zp->zds);
zp->zds = NULL;
@@ -823,7 +588,6 @@ static const struct Curl_cwtype zstd_encoding = {
};
#endif
-
/* Identity handler. */
static const struct Curl_cwtype identity_encoding = {
"identity",
@@ -834,7 +598,6 @@ static const struct Curl_cwtype identity_encoding = {
sizeof(struct Curl_cwriter)
};
-
/* supported general content decoders. */
static const struct Curl_cwtype * const general_unencoders[] = {
&identity_encoding,
@@ -898,7 +661,7 @@ void Curl_all_content_encodings(char *buf, size_t blen)
/* Deferred error dummy writer. */
static CURLcode error_do_init(struct Curl_easy *data,
- struct Curl_cwriter *writer)
+ struct Curl_cwriter *writer)
{
(void)data;
(void)writer;
@@ -906,8 +669,8 @@ static CURLcode error_do_init(struct Curl_easy *data,
}
static CURLcode error_do_write(struct Curl_easy *data,
- struct Curl_cwriter *writer, int type,
- const char *buf, size_t nbytes)
+ struct Curl_cwriter *writer, int type,
+ const char *buf, size_t nbytes)
{
(void) writer;
(void) buf;
@@ -974,6 +737,7 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
Curl_cwriter_phase phase = is_transfer ?
CURL_CW_TRANSFER_DECODE : CURL_CW_CONTENT_DECODE;
CURLcode result;
+ bool has_chunked = FALSE;
do {
const char *name;
@@ -987,7 +751,7 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
name = enclist;
for(namelen = 0; *enclist && *enclist != ','; enclist++)
- if(!ISSPACE(*enclist))
+ if(*enclist > ' ')
namelen = enclist - name + 1;
if(namelen) {
@@ -1002,9 +766,21 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
* Exception is "chunked" transfer-encoding which always must happen */
if((is_transfer && !data->set.http_transfer_encoding && !is_chunked) ||
(!is_transfer && data->set.http_ce_skip)) {
+ bool is_identity = strncasecompare(name, "identity", 8);
/* not requested, ignore */
CURL_TRC_WRITE(data, "decoder not requested, ignored: %.*s",
(int)namelen, name);
+ if(is_transfer && !data->set.http_te_skip) {
+ if(has_chunked)
+ failf(data, "A Transfer-Encoding (%.*s) was listed after chunked",
+ (int)namelen, name);
+ else if(is_identity)
+ continue;
+ else
+ failf(data, "Unsolicited Transfer-Encoding (%.*s) found",
+ (int)namelen, name);
+ return CURLE_BAD_CONTENT_ENCODING;
+ }
return CURLE_OK;
}
@@ -1055,6 +831,8 @@ CURLcode Curl_build_unencoding_stack(struct Curl_easy *data,
Curl_cwriter_free(data, writer);
return result;
}
+ if(is_chunked)
+ has_chunked = TRUE;
}
} while(*enclist);
@@ -1082,5 +860,4 @@ void Curl_all_content_encodings(char *buf, size_t blen)
strcpy(buf, CONTENT_ENCODING_DEFAULT);
}
-
#endif /* CURL_DISABLE_HTTP */
diff --git a/libs/libcurl/src/cookie.c b/libs/libcurl/src/cookie.c
index b190bb3767..0b80ad8f0c 100644
--- a/libs/libcurl/src/cookie.c
+++ b/libs/libcurl/src/cookie.c
@@ -76,11 +76,9 @@ Example set of cookies:
#include "urldata.h"
#include "cookie.h"
#include "psl.h"
-#include "strtok.h"
#include "sendf.h"
#include "slist.h"
#include "share.h"
-#include "strtoofft.h"
#include "strcase.h"
#include "curl_get_line.h"
#include "curl_memrchr.h"
@@ -89,6 +87,7 @@ Example set of cookies:
#include "fopen.h"
#include "strdup.h"
#include "llist.h"
+#include "curlx/strparse.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -97,6 +96,26 @@ Example set of cookies:
static void strstore(char **str, const char *newstr, size_t len);
+/* number of seconds in 400 days */
+#define COOKIES_MAXAGE (400*24*3600)
+
+/* Make sure cookies never expire further away in time than 400 days into the
+ future. (from RFC6265bis draft-19)
+
+ For the sake of easier testing, align the capped time to an even 60 second
+ boundary.
+*/
+static void cap_expires(time_t now, struct Cookie *co)
+{
+ if((TIME_T_MAX - COOKIES_MAXAGE - 30) > now) {
+ timediff_t cap = now + COOKIES_MAXAGE;
+ if(co->expires > cap) {
+ cap += 30;
+ co->expires = (cap/60)*60;
+ }
+ }
+}
+
static void freecookie(struct Cookie *co)
{
free(co->domain);
@@ -140,12 +159,10 @@ static bool cookie_tailmatch(const char *cookie_domain,
* matching cookie path and URL path
* RFC6265 5.1.4 Paths and Path-Match
*/
-static bool pathmatch(const char *cookie_path, const char *request_uri)
+static bool pathmatch(const char *cookie_path, const char *uri_path)
{
size_t cookie_path_len;
size_t uri_path_len;
- char *uri_path = NULL;
- char *pos;
bool ret = FALSE;
/* cookie_path must not have last '/' separator. ex: /sample */
@@ -155,19 +172,9 @@ static bool pathmatch(const char *cookie_path, const char *request_uri)
return TRUE;
}
- uri_path = strdup(request_uri);
- if(!uri_path)
- return FALSE;
- pos = strchr(uri_path, '?');
- if(pos)
- *pos = 0x0;
-
/* #-fragments are already cut off! */
- if(0 == strlen(uri_path) || uri_path[0] != '/') {
- strstore(&uri_path, "/", 1);
- if(!uri_path)
- return FALSE;
- }
+ if(0 == strlen(uri_path) || uri_path[0] != '/')
+ uri_path = "/";
/*
* here, RFC6265 5.1.4 says
@@ -181,16 +188,12 @@ static bool pathmatch(const char *cookie_path, const char *request_uri)
uri_path_len = strlen(uri_path);
- if(uri_path_len < cookie_path_len) {
- ret = FALSE;
+ if(uri_path_len < cookie_path_len)
goto pathmatched;
- }
/* not using checkprefix() because matching should be case-sensitive */
- if(strncmp(cookie_path, uri_path, cookie_path_len)) {
- ret = FALSE;
+ if(strncmp(cookie_path, uri_path, cookie_path_len))
goto pathmatched;
- }
/* The cookie-path and the uri-path are identical. */
if(cookie_path_len == uri_path_len) {
@@ -204,10 +207,7 @@ static bool pathmatch(const char *cookie_path, const char *request_uri)
goto pathmatched;
}
- ret = FALSE;
-
pathmatched:
- free(uri_path);
return ret;
}
@@ -281,34 +281,27 @@ static size_t cookiehash(const char * const domain)
*/
static char *sanitize_cookie_path(const char *cookie_path)
{
- size_t len;
- char *new_path = strdup(cookie_path);
- if(!new_path)
- return NULL;
+ size_t len = strlen(cookie_path);
- /* some stupid site sends path attribute with '"'. */
- len = strlen(new_path);
- if(new_path[0] == '\"') {
- memmove(new_path, new_path + 1, len);
+ /* some sites send path attribute within '"'. */
+ if(cookie_path[0] == '\"') {
+ cookie_path++;
len--;
}
- if(len && (new_path[len - 1] == '\"')) {
- new_path[--len] = 0x0;
- }
+ if(len && (cookie_path[len - 1] == '\"'))
+ len--;
/* RFC6265 5.2.4 The Path Attribute */
- if(new_path[0] != '/') {
+ if(cookie_path[0] != '/')
/* Let cookie-path be the default-path. */
- strstore(&new_path, "/", 1);
- return new_path;
- }
+ return strdup("/");
+ /* remove trailing slash */
/* convert /hoge/ to /hoge */
- if(len && new_path[len - 1] == '/') {
- new_path[len - 1] = 0x0;
- }
+ if(len && cookie_path[len - 1] == '/')
+ len--;
- return new_path;
+ return Curl_memdup0(cookie_path, len);
}
/*
@@ -350,9 +343,12 @@ void Curl_cookie_loadfiles(struct Curl_easy *data)
*/
static void strstore(char **str, const char *newstr, size_t len)
{
- DEBUGASSERT(newstr);
DEBUGASSERT(str);
free(*str);
+ if(!len) {
+ len++;
+ newstr = "";
+ }
*str = Curl_memdup0(newstr, len);
}
@@ -438,18 +434,16 @@ static bool bad_domain(const char *domain, size_t len)
fine. The prime reason for filtering out control bytes is that some HTTP
servers return 400 for requests that contain such.
*/
-static int invalid_octets(const char *p)
+static bool invalid_octets(const char *ptr)
{
+ const unsigned char *p = (const unsigned char *)ptr;
/* Reject all bytes \x01 - \x1f (*except* \x09, TAB) + \x7f */
- static const char badoctets[] = {
- "\x01\x02\x03\x04\x05\x06\x07\x08\x0a"
- "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14"
- "\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x7f"
- };
- size_t len;
- /* scan for all the octets that are *not* in cookie-octet */
- len = strcspn(p, badoctets);
- return (p[len] != '\0');
+ while(*p) {
+ if(((*p != 9) && (*p < 0x20)) || (*p == 0x7f))
+ return TRUE;
+ p++;
+ }
+ return FALSE;
}
#define CERR_OK 0
@@ -466,9 +460,18 @@ static int invalid_octets(const char *p)
#define CERR_COMMENT 11 /* a commented line */
#define CERR_RANGE 12 /* expire range problem */
#define CERR_FIELDS 13 /* incomplete netscape line */
+#ifdef USE_LIBPSL
#define CERR_PSL 14 /* a public suffix */
+#endif
#define CERR_LIVE_WINS 15
+/* The maximum length we accept a date string for the 'expire' keyword. The
+ standard date formats are within the 30 bytes range. This adds an extra
+ margin just to make sure it realistically works with what is used out
+ there.
+*/
+#define MAX_DATE_LENGTH 80
+
static int
parse_cookie_header(struct Curl_easy *data,
struct Cookie *co,
@@ -490,51 +493,29 @@ parse_cookie_header(struct Curl_easy *data,
now = time(NULL);
do {
- size_t vlen;
- size_t nlen;
-
- while(*ptr && ISBLANK(*ptr))
- ptr++;
+ struct Curl_str name;
+ struct Curl_str val;
/* we have a <name>=<value> pair or a stand-alone word here */
- nlen = strcspn(ptr, ";\t\r\n=");
- if(nlen) {
+ if(!curlx_str_cspn(&ptr, &name, ";\t\r\n=")) {
bool done = FALSE;
bool sep = FALSE;
- const char *namep = ptr;
- const char *valuep;
-
- ptr += nlen;
-
- /* trim trailing spaces and tabs after name */
- while(nlen && ISBLANK(namep[nlen - 1]))
- nlen--;
-
- if(*ptr == '=') {
- vlen = strcspn(++ptr, ";\r\n");
- valuep = ptr;
- sep = TRUE;
- ptr = &valuep[vlen];
+ curlx_str_trimblanks(&name);
- /* Strip off trailing whitespace from the value */
- while(vlen && ISBLANK(valuep[vlen-1]))
- vlen--;
+ if(!curlx_str_single(&ptr, '=')) {
+ sep = TRUE; /* a '=' was used */
+ if(!curlx_str_cspn(&ptr, &val, ";\r\n")) {
+ curlx_str_trimblanks(&val);
- /* Skip leading whitespace from the value */
- while(vlen && ISBLANK(*valuep)) {
- valuep++;
- vlen--;
- }
-
- /* Reject cookies with a TAB inside the value */
- if(memchr(valuep, '\t', vlen)) {
- infof(data, "cookie contains TAB, dropping");
- return CERR_TAB;
+ /* Reject cookies with a TAB inside the value */
+ if(memchr(curlx_str(&val), '\t', curlx_strlen(&val))) {
+ infof(data, "cookie contains TAB, dropping");
+ return CERR_TAB;
+ }
}
}
else {
- valuep = NULL;
- vlen = 0;
+ curlx_str_init(&val);
}
/*
@@ -542,10 +523,11 @@ parse_cookie_header(struct Curl_easy *data,
* combination of name + contents. Chrome and Firefox support 4095 or
* 4096 bytes combo
*/
- if(nlen >= (MAX_NAME-1) || vlen >= (MAX_NAME-1) ||
- ((nlen + vlen) > MAX_NAME)) {
+ if(curlx_strlen(&name) >= (MAX_NAME-1) ||
+ curlx_strlen(&val) >= (MAX_NAME-1) ||
+ ((curlx_strlen(&name) + curlx_strlen(&val)) > MAX_NAME)) {
infof(data, "oversized cookie dropped, name/val %zu + %zu bytes",
- nlen, vlen);
+ curlx_strlen(&name), curlx_strlen(&val));
return CERR_TOO_BIG;
}
@@ -555,12 +537,10 @@ parse_cookie_header(struct Curl_easy *data,
* "the rest". Prefixes must start with '__' and end with a '-', so
* only test for names where that can possibly be true.
*/
- if(nlen >= 7 && namep[0] == '_' && namep[1] == '_') {
- if(strncasecompare("__Secure-", namep, 9))
- co->prefix_secure = TRUE;
- else if(strncasecompare("__Host-", namep, 7))
- co->prefix_host = TRUE;
- }
+ if(!strncmp("__Secure-", curlx_str(&name), 9))
+ co->prefix_secure = TRUE;
+ else if(!strncmp("__Host-", curlx_str(&name), 7))
+ co->prefix_host = TRUE;
/*
* Use strstore() below to properly deal with received cookie
@@ -574,8 +554,8 @@ parse_cookie_header(struct Curl_easy *data,
/* Bad name/value pair. */
return CERR_NO_SEP;
- strstore(&co->name, namep, nlen);
- strstore(&co->value, valuep, vlen);
+ strstore(&co->name, curlx_str(&name), curlx_strlen(&name));
+ strstore(&co->value, curlx_str(&val), curlx_strlen(&val));
done = TRUE;
if(!co->name || !co->value)
return CERR_NO_NAME_VALUE;
@@ -585,7 +565,7 @@ parse_cookie_header(struct Curl_easy *data,
return CERR_INVALID_OCTET;
}
}
- else if(!vlen) {
+ else if(!curlx_strlen(&val)) {
/*
* this was a "<name>=" with no content, and we must allow
* 'secure' and 'httponly' specified this weirdly
@@ -596,7 +576,7 @@ parse_cookie_header(struct Curl_easy *data,
* using a secure protocol, or when the cookie is being set by
* reading from file
*/
- if((nlen == 6) && strncasecompare("secure", namep, 6)) {
+ if(curlx_str_casecompare(&name, "secure")) {
if(secure || !ci->running) {
co->secure = TRUE;
}
@@ -604,7 +584,7 @@ parse_cookie_header(struct Curl_easy *data,
return CERR_BAD_SECURE;
}
}
- else if((nlen == 8) && strncasecompare("httponly", namep, 8))
+ else if(curlx_str_casecompare(&name, "httponly"))
co->httponly = TRUE;
else if(sep)
/* there was a '=' so we are not done parsing this field */
@@ -612,8 +592,8 @@ parse_cookie_header(struct Curl_easy *data,
}
if(done)
;
- else if((nlen == 4) && strncasecompare("path", namep, 4)) {
- strstore(&co->path, valuep, vlen);
+ else if(curlx_str_casecompare(&name, "path")) {
+ strstore(&co->path, curlx_str(&val), curlx_strlen(&val));
if(!co->path)
return CERR_OUT_OF_MEMORY;
free(co->spath); /* if this is set again */
@@ -621,19 +601,16 @@ parse_cookie_header(struct Curl_easy *data,
if(!co->spath)
return CERR_OUT_OF_MEMORY;
}
- else if((nlen == 6) &&
- strncasecompare("domain", namep, 6) && vlen) {
+ else if(curlx_str_casecompare(&name, "domain") && curlx_strlen(&val)) {
bool is_ip;
-
+ const char *v = curlx_str(&val);
/*
* Now, we make sure that our host is within the given domain, or
* the given domain is not valid and thus cannot be set.
*/
- if('.' == valuep[0]) {
- valuep++; /* ignore preceding dot */
- vlen--;
- }
+ if('.' == *v)
+ curlx_str_nudge(&val, 1);
#ifndef USE_LIBPSL
/*
@@ -641,17 +618,19 @@ parse_cookie_header(struct Curl_easy *data,
* TLD or otherwise "protected" suffix. To reduce risk, we require a
* dot OR the exact hostname being "localhost".
*/
- if(bad_domain(valuep, vlen))
+ if(bad_domain(curlx_str(&val), curlx_strlen(&val)))
domain = ":";
#endif
- is_ip = Curl_host_is_ipnum(domain ? domain : valuep);
+ is_ip = Curl_host_is_ipnum(domain ? domain : curlx_str(&val));
if(!domain
- || (is_ip && !strncmp(valuep, domain, vlen) &&
- (vlen == strlen(domain)))
- || (!is_ip && cookie_tailmatch(valuep, vlen, domain))) {
- strstore(&co->domain, valuep, vlen);
+ || (is_ip && !strncmp(curlx_str(&val), domain,
+ curlx_strlen(&val)) &&
+ (curlx_strlen(&val) == strlen(domain)))
+ || (!is_ip && cookie_tailmatch(curlx_str(&val),
+ curlx_strlen(&val), domain))) {
+ strstore(&co->domain, curlx_str(&val), curlx_strlen(&val));
if(!co->domain)
return CERR_OUT_OF_MEMORY;
@@ -665,14 +644,14 @@ parse_cookie_header(struct Curl_easy *data,
* not a domain to which the current host belongs. Mark as bad.
*/
infof(data, "skipped cookie with bad tailmatch domain: %s",
- valuep);
+ curlx_str(&val));
return CERR_NO_TAILMATCH;
}
}
- else if((nlen == 7) && strncasecompare("version", namep, 7)) {
+ else if(curlx_str_casecompare(&name, "version")) {
/* just ignore */
}
- else if((nlen == 7) && strncasecompare("max-age", namep, 7)) {
+ else if(curlx_str_casecompare(&name, "max-age") && curlx_strlen(&val)) {
/*
* Defined in RFC2109:
*
@@ -682,21 +661,22 @@ parse_cookie_header(struct Curl_easy *data,
* client should discard the cookie. A value of zero means the
* cookie should be discarded immediately.
*/
- CURLofft offt;
- const char *maxage = valuep;
- offt = curlx_strtoofft((*maxage == '\"') ?
- &maxage[1] : &maxage[0], NULL, 10,
- &co->expires);
- switch(offt) {
- case CURL_OFFT_FLOW:
+ int rc;
+ const char *maxage = curlx_str(&val);
+ if(*maxage == '\"')
+ maxage++;
+ rc = curlx_str_number(&maxage, &co->expires, CURL_OFF_T_MAX);
+
+ switch(rc) {
+ case STRE_OVERFLOW:
/* overflow, used max value */
co->expires = CURL_OFF_T_MAX;
break;
- case CURL_OFFT_INVAL:
+ default:
/* negative or otherwise bad, expire */
co->expires = 1;
break;
- case CURL_OFFT_OK:
+ case STRE_OK:
if(!co->expires)
/* already expired */
co->expires = 1;
@@ -707,16 +687,20 @@ parse_cookie_header(struct Curl_easy *data,
co->expires += now;
break;
}
+ cap_expires(now, co);
}
- else if((nlen == 7) && strncasecompare("expires", namep, 7)) {
- if(!co->expires) {
+ else if(curlx_str_casecompare(&name, "expires") && curlx_strlen(&val)) {
+ if(!co->expires && (curlx_strlen(&val) < MAX_DATE_LENGTH)) {
/*
* Let max-age have priority.
*
* If the date cannot get parsed for whatever reason, the cookie
* will be treated as a session cookie
*/
- co->expires = Curl_getdate_capped(valuep);
+ char dbuf[MAX_DATE_LENGTH + 1];
+ memcpy(dbuf, curlx_str(&val), curlx_strlen(&val));
+ dbuf[curlx_strlen(&val)] = 0;
+ co->expires = Curl_getdate_capped(dbuf);
/*
* Session cookies have expires set to 0 so if we get that back
@@ -727,6 +711,7 @@ parse_cookie_header(struct Curl_easy *data,
co->expires = 1;
else if(co->expires < 0)
co->expires = 0;
+ cap_expires(now, co);
}
}
@@ -734,15 +719,8 @@ parse_cookie_header(struct Curl_easy *data,
* Else, this is the second (or more) name we do not know about!
*/
}
- else {
- /* this is an "illegal" <what>=<this> pair */
- }
- while(*ptr && ISBLANK(*ptr))
- ptr++;
- if(*ptr == ';')
- ptr++;
- else
+ if(curlx_str_single(&ptr, ';'))
break;
} while(1);
@@ -755,23 +733,11 @@ parse_cookie_header(struct Curl_easy *data,
if(!co->path && path) {
/*
- * No path was given in the header line, set the default. Note that the
- * passed-in path to this function MAY have a '?' and following part that
- * MUST NOT be stored as part of the path.
- */
- char *queryp = strchr(path, '?');
-
- /*
- * queryp is where the interesting part of the path ends, so now we
- * want to the find the last
+ * No path was given in the header line, set the default.
*/
- char *endslash;
- if(!queryp)
- endslash = strrchr(path, '/');
- else
- endslash = memrchr(path, '/', (queryp - path));
+ const char *endslash = strrchr(path, '/');
if(endslash) {
- size_t pathlen = (endslash-path + 1); /* include end slash */
+ size_t pathlen = (endslash - path + 1); /* include end slash */
co->path = Curl_memdup0(path, pathlen);
if(co->path) {
co->spath = sanitize_cookie_path(co->path);
@@ -805,10 +771,9 @@ parse_netscape(struct Cookie *co,
* This line is NOT an HTTP header style line, we do offer support for
* reading the odd netscape cookies-file format here
*/
- char *ptr;
- char *firstptr;
- char *tok_buf = NULL;
+ const char *ptr, *next;
int fields;
+ size_t len;
/*
* In 2008, Internet Explorer introduced HTTP-only cookies to prevent XSS
@@ -825,27 +790,22 @@ parse_netscape(struct Cookie *co,
/* do not even try the comments */
return CERR_COMMENT;
- /* strip off the possible end-of-line characters */
- ptr = strchr(lineptr, '\r');
- if(ptr)
- *ptr = 0; /* clear it */
- ptr = strchr(lineptr, '\n');
- if(ptr)
- *ptr = 0; /* clear it */
-
- firstptr = strtok_r((char *)lineptr, "\t", &tok_buf); /* tokenize on TAB */
-
/*
* Now loop through the fields and init the struct we already have
* allocated
*/
fields = 0;
- for(ptr = firstptr; ptr; ptr = strtok_r(NULL, "\t", &tok_buf), fields++) {
+ for(next = lineptr; next; fields++) {
+ ptr = next;
+ len = strcspn(ptr, "\t\r\n");
+ next = (ptr[len] == '\t' ? &ptr[len + 1] : NULL);
switch(fields) {
case 0:
- if(ptr[0]=='.') /* skip preceding dots */
+ if(ptr[0]=='.') { /* skip preceding dots */
ptr++;
- co->domain = strdup(ptr);
+ len--;
+ }
+ co->domain = Curl_memdup0(ptr, len);
if(!co->domain)
return CERR_OUT_OF_MEMORY;
break;
@@ -853,15 +813,15 @@ parse_netscape(struct Cookie *co,
/*
* flag: A TRUE/FALSE value indicating if all machines within a given
* domain can access the variable. Set TRUE when the cookie says
- * .domain.com and to false when the domain is complete www.domain.com
+ * .example.com and to false when the domain is complete www.example.com
*/
- co->tailmatch = !!strcasecompare(ptr, "TRUE");
+ co->tailmatch = !!strncasecompare(ptr, "TRUE", len);
break;
case 2:
/* The file format allows the path field to remain not filled in */
- if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) {
+ if(strncmp("TRUE", ptr, len) && strncmp("FALSE", ptr, len)) {
/* only if the path does not look like a boolean option! */
- co->path = strdup(ptr);
+ co->path = Curl_memdup0(ptr, len);
if(!co->path)
return CERR_OUT_OF_MEMORY;
else {
@@ -882,7 +842,7 @@ parse_netscape(struct Cookie *co,
FALLTHROUGH();
case 3:
co->secure = FALSE;
- if(strcasecompare(ptr, "TRUE")) {
+ if(strncasecompare(ptr, "TRUE", len)) {
if(secure || ci->running)
co->secure = TRUE;
else
@@ -890,11 +850,11 @@ parse_netscape(struct Cookie *co,
}
break;
case 4:
- if(curlx_strtoofft(ptr, NULL, 10, &co->expires))
+ if(curlx_str_number(&ptr, &co->expires, CURL_OFF_T_MAX))
return CERR_RANGE;
break;
case 5:
- co->name = strdup(ptr);
+ co->name = Curl_memdup0(ptr, len);
if(!co->name)
return CERR_OUT_OF_MEMORY;
else {
@@ -906,7 +866,7 @@ parse_netscape(struct Cookie *co,
}
break;
case 6:
- co->value = strdup(ptr);
+ co->value = Curl_memdup0(ptr, len);
if(!co->value)
return CERR_OUT_OF_MEMORY;
break;
@@ -989,7 +949,7 @@ replace_existing(struct Curl_easy *data,
size_t myhash = cookiehash(co->domain);
for(n = Curl_llist_head(&ci->cookielist[myhash]); n; n = Curl_node_next(n)) {
struct Cookie *clist = Curl_node_elem(n);
- if(strcasecompare(clist->name, co->name)) {
+ if(!strcmp(clist->name, co->name)) {
/* the names are identical */
bool matching_domains = FALSE;
@@ -1029,7 +989,7 @@ replace_existing(struct Curl_easy *data,
}
}
- if(!replace_n && strcasecompare(clist->name, co->name)) {
+ if(!replace_n && !strcmp(clist->name, co->name)) {
/* the names are identical */
if(clist->domain && co->domain) {
@@ -1250,21 +1210,20 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
ci->running = FALSE; /* this is not running, this is init */
if(fp) {
struct dynbuf buf;
- Curl_dyn_init(&buf, MAX_COOKIE_LINE);
+ curlx_dyn_init(&buf, MAX_COOKIE_LINE);
while(Curl_get_line(&buf, fp)) {
- char *lineptr = Curl_dyn_ptr(&buf);
+ const char *lineptr = curlx_dyn_ptr(&buf);
bool headerline = FALSE;
if(checkprefix("Set-Cookie:", lineptr)) {
/* This is a cookie line, get it! */
lineptr += 11;
headerline = TRUE;
- while(*lineptr && ISBLANK(*lineptr))
- lineptr++;
+ curlx_str_passblanks(&lineptr);
}
Curl_cookie_add(data, ci, headerline, TRUE, lineptr, NULL, NULL, TRUE);
}
- Curl_dyn_free(&buf); /* free the line buffer */
+ curlx_dyn_free(&buf); /* free the line buffer */
/*
* Remove expired cookies from the hash. We must make sure to run this
@@ -1292,8 +1251,8 @@ struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
*/
static int cookie_sort(const void *p1, const void *p2)
{
- struct Cookie *c1 = *(struct Cookie **)p1;
- struct Cookie *c2 = *(struct Cookie **)p2;
+ const struct Cookie *c1 = *(const struct Cookie * const *)p1;
+ const struct Cookie *c2 = *(const struct Cookie * const *)p2;
size_t l1, l2;
/* 1 - compare cookie path lengths */
@@ -1301,14 +1260,14 @@ static int cookie_sort(const void *p1, const void *p2)
l2 = c2->path ? strlen(c2->path) : 0;
if(l1 != l2)
- return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
+ return (l2 > l1) ? 1 : -1; /* avoid size_t <=> int conversions */
/* 2 - compare cookie domain lengths */
l1 = c1->domain ? strlen(c1->domain) : 0;
l2 = c2->domain ? strlen(c2->domain) : 0;
if(l1 != l2)
- return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
+ return (l2 > l1) ? 1 : -1; /* avoid size_t <=> int conversions */
/* 3 - compare cookie name lengths */
l1 = c1->name ? strlen(c1->name) : 0;
@@ -1328,8 +1287,8 @@ static int cookie_sort(const void *p1, const void *p2)
*/
static int cookie_sort_ct(const void *p1, const void *p2)
{
- struct Cookie *c1 = *(struct Cookie **)p1;
- struct Cookie *c2 = *(struct Cookie **)p2;
+ const struct Cookie *c1 = *(const struct Cookie * const *)p1;
+ const struct Cookie *c2 = *(const struct Cookie * const *)p2;
return (c2->creationtime > c1->creationtime) ? 1 : -1;
}
diff --git a/libs/libcurl/src/cookie.h b/libs/libcurl/src/cookie.h
index 9064c694ec..d8e92e94b2 100644
--- a/libs/libcurl/src/cookie.h
+++ b/libs/libcurl/src/cookie.h
@@ -38,7 +38,7 @@ struct Cookie {
char *spath; /* sanitized cookie path */
char *domain; /* domain = <this> */
curl_off_t expires; /* expires = <this> */
- int creationtime; /* time when the cookie was written */
+ unsigned int creationtime; /* time when the cookie was written */
BIT(tailmatch); /* tail-match the domain name */
BIT(secure); /* the 'secure' keyword was used */
BIT(livecookie); /* updated from a server, not a stored file */
@@ -60,10 +60,10 @@ struct CookieInfo {
/* linked lists of cookies we know of */
struct Curl_llist cookielist[COOKIE_HASH_SIZE];
curl_off_t next_expiration; /* the next time at which expiration happens */
- int numcookies; /* number of cookies in the "jar" */
- int lastct; /* last creation-time used in the jar */
- bool running; /* state info, for cookie adding information */
- bool newsession; /* new session, discard session cookies on load */
+ unsigned int numcookies; /* number of cookies in the "jar" */
+ unsigned int lastct; /* last creation-time used in the jar */
+ BIT(running); /* state info, for cookie adding information */
+ BIT(newsession); /* new session, discard session cookies on load */
};
/* The maximum sizes we accept for cookies. RFC 6265 section 6.1 says
diff --git a/libs/libcurl/src/cshutdn.c b/libs/libcurl/src/cshutdn.c
new file mode 100644
index 0000000000..f6e52267ad
--- /dev/null
+++ b/libs/libcurl/src/cshutdn.c
@@ -0,0 +1,581 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Linus Nielsen Feltzing, <linus@haxx.se>
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+
+#include "urldata.h"
+#include "url.h"
+#include "cfilters.h"
+#include "progress.h"
+#include "multiif.h"
+#include "multi_ev.h"
+#include "sendf.h"
+#include "cshutdn.h"
+#include "http_negotiate.h"
+#include "http_ntlm.h"
+#include "sigpipe.h"
+#include "connect.h"
+#include "select.h"
+#include "strcase.h"
+#include "curlx/strparse.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+
+static void cshutdn_run_conn_handler(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ if(!conn->bits.shutdown_handler) {
+
+ /* Cleanup NTLM connection-related data */
+ Curl_http_auth_cleanup_ntlm(conn);
+
+ /* Cleanup NEGOTIATE connection-related data */
+ Curl_http_auth_cleanup_negotiate(conn);
+
+ if(conn->handler && conn->handler->disconnect) {
+ /* Some disconnect handlers do a blocking wait on server responses.
+ * FTP/IMAP/SMTP and SFTP are among them. When using the internal
+ * handle, set an overall short timeout so we do not hang for the
+ * default 120 seconds. */
+ if(data->state.internal) {
+ data->set.timeout = DEFAULT_SHUTDOWN_TIMEOUT_MS;
+ (void)Curl_pgrsTime(data, TIMER_STARTOP);
+ }
+
+ /* This is set if protocol-specific cleanups should be made */
+ DEBUGF(infof(data, "connection #%" FMT_OFF_T
+ ", shutdown protocol handler (aborted=%d)",
+ conn->connection_id, conn->bits.aborted));
+ /* There are protocol handlers that block on retrieving
+ * server responses here (FTP). Set a short timeout. */
+ conn->handler->disconnect(data, conn, conn->bits.aborted);
+ }
+
+ conn->bits.shutdown_handler = TRUE;
+ }
+}
+
+static void cshutdn_run_once(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool *done)
+{
+ CURLcode r1, r2;
+ bool done1, done2;
+
+ /* We expect to be attached when called */
+ DEBUGASSERT(data->conn == conn);
+
+ cshutdn_run_conn_handler(data, conn);
+
+ if(conn->bits.shutdown_filters) {
+ *done = TRUE;
+ return;
+ }
+
+ if(!conn->connect_only && Curl_conn_is_connected(conn, FIRSTSOCKET))
+ r1 = Curl_conn_shutdown(data, FIRSTSOCKET, &done1);
+ else {
+ r1 = CURLE_OK;
+ done1 = TRUE;
+ }
+
+ if(!conn->connect_only && Curl_conn_is_connected(conn, SECONDARYSOCKET))
+ r2 = Curl_conn_shutdown(data, SECONDARYSOCKET, &done2);
+ else {
+ r2 = CURLE_OK;
+ done2 = TRUE;
+ }
+
+ /* we are done when any failed or both report success */
+ *done = (r1 || r2 || (done1 && done2));
+ if(*done)
+ conn->bits.shutdown_filters = TRUE;
+}
+
+void Curl_cshutdn_run_once(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool *done)
+{
+ DEBUGASSERT(!data->conn);
+ Curl_attach_connection(data, conn);
+ cshutdn_run_once(data, conn, done);
+ CURL_TRC_M(data, "[SHUTDOWN] shutdown, done=%d", *done);
+ Curl_detach_connection(data);
+}
+
+
+void Curl_cshutdn_terminate(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool do_shutdown)
+{
+ struct Curl_easy *admin = data;
+ bool done;
+
+ /* there must be a connection to close */
+ DEBUGASSERT(conn);
+ /* it must be removed from the connection pool */
+ DEBUGASSERT(!conn->bits.in_cpool);
+ /* the transfer must be detached from the connection */
+ DEBUGASSERT(data && !data->conn);
+
+ /* If we can obtain an internal admin handle, use that to attach
+ * and terminate the connection. Some protocol will try to mess with
+ * `data` during shutdown and we do not want that with a `data` from
+ * the application. */
+ if(data->multi && data->multi->admin)
+ admin = data->multi->admin;
+
+ Curl_attach_connection(admin, conn);
+
+ cshutdn_run_conn_handler(admin, conn);
+ if(do_shutdown) {
+ /* Make a last attempt to shutdown handlers and filters, if
+ * not done so already. */
+ cshutdn_run_once(admin, conn, &done);
+ }
+ CURL_TRC_M(admin, "[SHUTDOWN] %sclosing connection #%" FMT_OFF_T,
+ conn->bits.shutdown_filters ? "" : "force ",
+ conn->connection_id);
+ Curl_conn_close(admin, SECONDARYSOCKET);
+ Curl_conn_close(admin, FIRSTSOCKET);
+ Curl_detach_connection(admin);
+
+ if(data->multi)
+ Curl_multi_ev_conn_done(data->multi, data, conn);
+ Curl_conn_free(admin, conn);
+
+ if(data->multi) {
+ CURL_TRC_M(data, "[SHUTDOWN] trigger multi connchanged");
+ Curl_multi_connchanged(data->multi);
+ }
+}
+
+static bool cshutdn_destroy_oldest(struct cshutdn *cshutdn,
+ struct Curl_easy *data,
+ const char *destination)
+{
+ struct Curl_llist_node *e;
+ struct connectdata *conn;
+
+ e = Curl_llist_head(&cshutdn->list);
+ while(e) {
+ conn = Curl_node_elem(e);
+ if(!destination || !strcmp(destination, conn->destination))
+ break;
+ e = Curl_node_next(e);
+ }
+
+ if(e) {
+ SIGPIPE_VARIABLE(pipe_st);
+ conn = Curl_node_elem(e);
+ Curl_node_remove(e);
+ sigpipe_init(&pipe_st);
+ sigpipe_apply(data, &pipe_st);
+ Curl_cshutdn_terminate(data, conn, FALSE);
+ sigpipe_restore(&pipe_st);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+bool Curl_cshutdn_close_oldest(struct Curl_easy *data,
+ const char *destination)
+{
+ if(data && data->multi) {
+ struct cshutdn *csd = &data->multi->cshutdn;
+ return cshutdn_destroy_oldest(csd, data, destination);
+ }
+ return FALSE;
+}
+
+#define NUM_POLLS_ON_STACK 10
+
+static CURLcode cshutdn_wait(struct cshutdn *cshutdn,
+ struct Curl_easy *data,
+ int timeout_ms)
+{
+ struct pollfd a_few_on_stack[NUM_POLLS_ON_STACK];
+ struct curl_pollfds cpfds;
+ CURLcode result;
+
+ Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK);
+
+ result = Curl_cshutdn_add_pollfds(cshutdn, data, &cpfds);
+ if(result)
+ goto out;
+
+ Curl_poll(cpfds.pfds, cpfds.n, CURLMIN(timeout_ms, 1000));
+
+out:
+ Curl_pollfds_cleanup(&cpfds);
+ return result;
+}
+
+
+static void cshutdn_perform(struct cshutdn *cshutdn,
+ struct Curl_easy *data)
+{
+ struct Curl_llist_node *e = Curl_llist_head(&cshutdn->list);
+ struct Curl_llist_node *enext;
+ struct connectdata *conn;
+ struct curltime *nowp = NULL;
+ struct curltime now;
+ timediff_t next_expire_ms = 0, ms;
+ bool done;
+
+ if(!e)
+ return;
+
+ CURL_TRC_M(data, "[SHUTDOWN] perform on %zu connections",
+ Curl_llist_count(&cshutdn->list));
+ while(e) {
+ enext = Curl_node_next(e);
+ conn = Curl_node_elem(e);
+ Curl_cshutdn_run_once(data, conn, &done);
+ if(done) {
+ Curl_node_remove(e);
+ Curl_cshutdn_terminate(data, conn, FALSE);
+ }
+ else {
+ /* idata has one timer list, but maybe more than one connection.
+ * Set EXPIRE_SHUTDOWN to the smallest time left for all. */
+ if(!nowp) {
+ now = curlx_now();
+ nowp = &now;
+ }
+ ms = Curl_conn_shutdown_timeleft(conn, nowp);
+ if(ms && ms < next_expire_ms)
+ next_expire_ms = ms;
+ }
+ e = enext;
+ }
+
+ if(next_expire_ms)
+ Curl_expire_ex(data, nowp, next_expire_ms, EXPIRE_SHUTDOWN);
+}
+
+
+static void cshutdn_terminate_all(struct cshutdn *cshutdn,
+ struct Curl_easy *data,
+ int timeout_ms)
+{
+ struct curltime started = curlx_now();
+ struct Curl_llist_node *e;
+ SIGPIPE_VARIABLE(pipe_st);
+
+ DEBUGASSERT(cshutdn);
+ DEBUGASSERT(data);
+
+ CURL_TRC_M(data, "[SHUTDOWN] shutdown all");
+ sigpipe_init(&pipe_st);
+ sigpipe_apply(data, &pipe_st);
+
+ while(Curl_llist_head(&cshutdn->list)) {
+ timediff_t timespent;
+ int remain_ms;
+
+ cshutdn_perform(cshutdn, data);
+
+ if(!Curl_llist_head(&cshutdn->list)) {
+ CURL_TRC_M(data, "[SHUTDOWN] shutdown finished cleanly");
+ break;
+ }
+
+ /* wait for activity, timeout or "nothing" */
+ timespent = curlx_timediff(curlx_now(), started);
+ if(timespent >= (timediff_t)timeout_ms) {
+ CURL_TRC_M(data, "[SHUTDOWN] shutdown finished, %s",
+ (timeout_ms > 0) ? "timeout" : "best effort done");
+ break;
+ }
+
+ remain_ms = timeout_ms - (int)timespent;
+ if(cshutdn_wait(cshutdn, data, remain_ms)) {
+ CURL_TRC_M(data, "[SHUTDOWN] shutdown finished, aborted");
+ break;
+ }
+ }
+
+ /* Terminate any remaining. */
+ e = Curl_llist_head(&cshutdn->list);
+ while(e) {
+ struct connectdata *conn = Curl_node_elem(e);
+ Curl_node_remove(e);
+ Curl_cshutdn_terminate(data, conn, FALSE);
+ e = Curl_llist_head(&cshutdn->list);
+ }
+ DEBUGASSERT(!Curl_llist_count(&cshutdn->list));
+
+ sigpipe_restore(&pipe_st);
+}
+
+
+int Curl_cshutdn_init(struct cshutdn *cshutdn,
+ struct Curl_multi *multi)
+{
+ DEBUGASSERT(multi);
+ cshutdn->multi = multi;
+ Curl_llist_init(&cshutdn->list, NULL);
+ cshutdn->initialised = TRUE;
+ return 0; /* good */
+}
+
+
+void Curl_cshutdn_destroy(struct cshutdn *cshutdn,
+ struct Curl_easy *data)
+{
+ if(cshutdn->initialised && data) {
+ int timeout_ms = 0;
+ /* Just for testing, run graceful shutdown */
+#ifdef DEBUGBUILD
+ {
+ const char *p = getenv("CURL_GRACEFUL_SHUTDOWN");
+ if(p) {
+ curl_off_t l;
+ if(!curlx_str_number(&p, &l, INT_MAX))
+ timeout_ms = (int)l;
+ }
+ }
+#endif
+
+ CURL_TRC_M(data, "[SHUTDOWN] destroy, %zu connections, timeout=%dms",
+ Curl_llist_count(&cshutdn->list), timeout_ms);
+ cshutdn_terminate_all(cshutdn, data, timeout_ms);
+ }
+ cshutdn->multi = NULL;
+}
+
+size_t Curl_cshutdn_count(struct Curl_easy *data)
+{
+ if(data && data->multi) {
+ struct cshutdn *csd = &data->multi->cshutdn;
+ return Curl_llist_count(&csd->list);
+ }
+ return 0;
+}
+
+size_t Curl_cshutdn_dest_count(struct Curl_easy *data,
+ const char *destination)
+{
+ if(data && data->multi) {
+ struct cshutdn *csd = &data->multi->cshutdn;
+ size_t n = 0;
+ struct Curl_llist_node *e = Curl_llist_head(&csd->list);
+ while(e) {
+ struct connectdata *conn = Curl_node_elem(e);
+ if(!strcmp(destination, conn->destination))
+ ++n;
+ e = Curl_node_next(e);
+ }
+ return n;
+ }
+ return 0;
+}
+
+
+static CURLMcode cshutdn_update_ev(struct cshutdn *cshutdn,
+ struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ CURLMcode mresult;
+
+ DEBUGASSERT(cshutdn);
+ DEBUGASSERT(cshutdn->multi->socket_cb);
+
+ Curl_attach_connection(data, conn);
+ mresult = Curl_multi_ev_assess_conn(cshutdn->multi, data, conn);
+ Curl_detach_connection(data);
+ return mresult;
+}
+
+
+void Curl_cshutdn_add(struct cshutdn *cshutdn,
+ struct connectdata *conn,
+ size_t conns_in_pool)
+{
+ struct Curl_easy *data = cshutdn->multi->admin;
+ size_t max_total = (cshutdn->multi->max_total_connections > 0) ?
+ (size_t)cshutdn->multi->max_total_connections : 0;
+
+ /* Add the connection to our shutdown list for non-blocking shutdown
+ * during multi processing. */
+ if(max_total > 0 && (max_total <=
+ (conns_in_pool + Curl_llist_count(&cshutdn->list)))) {
+ CURL_TRC_M(data, "[SHUTDOWN] discarding oldest shutdown connection "
+ "due to connection limit of %zu", max_total);
+ cshutdn_destroy_oldest(cshutdn, data, NULL);
+ }
+
+ if(cshutdn->multi->socket_cb) {
+ if(cshutdn_update_ev(cshutdn, data, conn)) {
+ CURL_TRC_M(data, "[SHUTDOWN] update events failed, discarding #%"
+ FMT_OFF_T, conn->connection_id);
+ Curl_cshutdn_terminate(data, conn, FALSE);
+ return;
+ }
+ }
+
+ Curl_llist_append(&cshutdn->list, conn, &conn->cshutdn_node);
+ CURL_TRC_M(data, "[SHUTDOWN] added #%" FMT_OFF_T
+ " to shutdowns, now %zu conns in shutdown",
+ conn->connection_id, Curl_llist_count(&cshutdn->list));
+}
+
+
+static void cshutdn_multi_socket(struct cshutdn *cshutdn,
+ struct Curl_easy *data,
+ curl_socket_t s)
+{
+ struct Curl_llist_node *e;
+ struct connectdata *conn;
+ bool done;
+
+ DEBUGASSERT(cshutdn->multi->socket_cb);
+ e = Curl_llist_head(&cshutdn->list);
+ while(e) {
+ conn = Curl_node_elem(e);
+ if(s == conn->sock[FIRSTSOCKET] || s == conn->sock[SECONDARYSOCKET]) {
+ Curl_cshutdn_run_once(data, conn, &done);
+ if(done || cshutdn_update_ev(cshutdn, data, conn)) {
+ Curl_node_remove(e);
+ Curl_cshutdn_terminate(data, conn, FALSE);
+ }
+ break;
+ }
+ e = Curl_node_next(e);
+ }
+}
+
+
+void Curl_cshutdn_perform(struct cshutdn *cshutdn,
+ struct Curl_easy *data,
+ curl_socket_t s)
+{
+ if((s == CURL_SOCKET_TIMEOUT) || (!cshutdn->multi->socket_cb))
+ cshutdn_perform(cshutdn, data);
+ else
+ cshutdn_multi_socket(cshutdn, data, s);
+}
+
+/* return fd_set info about the shutdown connections */
+void Curl_cshutdn_setfds(struct cshutdn *cshutdn,
+ struct Curl_easy *data,
+ fd_set *read_fd_set, fd_set *write_fd_set,
+ int *maxfd)
+{
+ if(Curl_llist_head(&cshutdn->list)) {
+ struct Curl_llist_node *e;
+
+ for(e = Curl_llist_head(&cshutdn->list); e;
+ e = Curl_node_next(e)) {
+ struct easy_pollset ps;
+ unsigned int i;
+ struct connectdata *conn = Curl_node_elem(e);
+ memset(&ps, 0, sizeof(ps));
+ Curl_attach_connection(data, conn);
+ Curl_conn_adjust_pollset(data, conn, &ps);
+ Curl_detach_connection(data);
+
+ for(i = 0; i < ps.num; i++) {
+#if defined(__DJGPP__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Warith-conversion"
+#endif
+ if(ps.actions[i] & CURL_POLL_IN)
+ FD_SET(ps.sockets[i], read_fd_set);
+ if(ps.actions[i] & CURL_POLL_OUT)
+ FD_SET(ps.sockets[i], write_fd_set);
+#if defined(__DJGPP__)
+#pragma GCC diagnostic pop
+#endif
+ if((ps.actions[i] & (CURL_POLL_OUT | CURL_POLL_IN)) &&
+ ((int)ps.sockets[i] > *maxfd))
+ *maxfd = (int)ps.sockets[i];
+ }
+ }
+ }
+}
+
+/* return information about the shutdown connections */
+unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn,
+ struct Curl_easy *data,
+ struct Curl_waitfds *cwfds)
+{
+ unsigned int need = 0;
+
+ if(Curl_llist_head(&cshutdn->list)) {
+ struct Curl_llist_node *e;
+ struct easy_pollset ps;
+ struct connectdata *conn;
+
+ for(e = Curl_llist_head(&cshutdn->list); e;
+ e = Curl_node_next(e)) {
+ conn = Curl_node_elem(e);
+ memset(&ps, 0, sizeof(ps));
+ Curl_attach_connection(data, conn);
+ Curl_conn_adjust_pollset(data, conn, &ps);
+ Curl_detach_connection(data);
+
+ need += Curl_waitfds_add_ps(cwfds, &ps);
+ }
+ }
+ return need;
+}
+
+CURLcode Curl_cshutdn_add_pollfds(struct cshutdn *cshutdn,
+ struct Curl_easy *data,
+ struct curl_pollfds *cpfds)
+{
+ CURLcode result = CURLE_OK;
+
+ if(Curl_llist_head(&cshutdn->list)) {
+ struct Curl_llist_node *e;
+ struct easy_pollset ps;
+ struct connectdata *conn;
+
+ for(e = Curl_llist_head(&cshutdn->list); e;
+ e = Curl_node_next(e)) {
+ conn = Curl_node_elem(e);
+ memset(&ps, 0, sizeof(ps));
+ Curl_attach_connection(data, conn);
+ Curl_conn_adjust_pollset(data, conn, &ps);
+ Curl_detach_connection(data);
+
+ result = Curl_pollfds_add_ps(cpfds, &ps);
+ if(result) {
+ Curl_pollfds_cleanup(cpfds);
+ goto out;
+ }
+ }
+ }
+out:
+ return result;
+}
diff --git a/libs/libcurl/src/cshutdn.h b/libs/libcurl/src/cshutdn.h
new file mode 100644
index 0000000000..690d8eefda
--- /dev/null
+++ b/libs/libcurl/src/cshutdn.h
@@ -0,0 +1,110 @@
+#ifndef HEADER_CURL_CSHUTDN_H
+#define HEADER_CURL_CSHUTDN_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) Linus Nielsen Feltzing, <linus@haxx.se>
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include <curl/curl.h>
+#include "curlx/timeval.h"
+
+struct connectdata;
+struct Curl_easy;
+struct curl_pollfds;
+struct Curl_waitfds;
+struct Curl_multi;
+struct Curl_share;
+
+/* Run the shutdown of the connection once.
+ * Will shortly attach/detach `data` to `conn` while doing so.
+ * `done` will be set TRUE if any error was encountered or if
+ * the connection was shut down completely. */
+void Curl_cshutdn_run_once(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool *done);
+
+/* Terminates the connection, e.g. closes and destroys it.
+ * If `run_shutdown` is TRUE, the shutdown will be run once before
+ * terminating it.
+ * Takes ownership of `conn`. */
+void Curl_cshutdn_terminate(struct Curl_easy *data,
+ struct connectdata *conn,
+ bool run_shutdown);
+
+/* A `cshutdown` is always owned by a multi handle to maintain
+ * the connections to be shut down. It registers timers and
+ * sockets to monitor via the multi handle. */
+struct cshutdn {
+ struct Curl_llist list; /* connections being shut down */
+ struct Curl_multi *multi; /* the multi owning this */
+ BIT(initialised);
+};
+
+/* Init as part of the given multi handle. */
+int Curl_cshutdn_init(struct cshutdn *cshutdn,
+ struct Curl_multi *multi);
+
+/* Terminate all remaining connections and free resources. */
+void Curl_cshutdn_destroy(struct cshutdn *cshutdn,
+ struct Curl_easy *data);
+
+/* Number of connections being shut down. */
+size_t Curl_cshutdn_count(struct Curl_easy *data);
+
+/* Number of connections to the destination being shut down. */
+size_t Curl_cshutdn_dest_count(struct Curl_easy *data,
+ const char *destination);
+
+/* Close the oldest connection in shutdown to destination or,
+ * when destination is NULL for any destination.
+ * Return TRUE if a connection has been closed. */
+bool Curl_cshutdn_close_oldest(struct Curl_easy *data,
+ const char *destination);
+
+/* Add a connection to have it shut down. Will terminate the oldest
+ * connection when total connection limit of multi is being reached. */
+void Curl_cshutdn_add(struct cshutdn *cshutdn,
+ struct connectdata *conn,
+ size_t conns_in_pool);
+
+/* Add sockets and POLLIN/OUT flags for connections being shut down. */
+CURLcode Curl_cshutdn_add_pollfds(struct cshutdn *cshutdn,
+ struct Curl_easy *data,
+ struct curl_pollfds *cpfds);
+
+unsigned int Curl_cshutdn_add_waitfds(struct cshutdn *cshutdn,
+ struct Curl_easy *data,
+ struct Curl_waitfds *cwfds);
+
+void Curl_cshutdn_setfds(struct cshutdn *cshutdn,
+ struct Curl_easy *data,
+ fd_set *read_fd_set, fd_set *write_fd_set,
+ int *maxfd);
+
+/* Run shut down connections using socket. If socket is CURL_SOCKET_TIMEOUT,
+ * run maintenance on all connections. */
+void Curl_cshutdn_perform(struct cshutdn *cshutdn,
+ struct Curl_easy *data,
+ curl_socket_t s);
+
+#endif /* HEADER_CURL_CSHUTDN_H */
diff --git a/libs/libcurl/src/curl_addrinfo.c b/libs/libcurl/src/curl_addrinfo.c
index 1f895263c8..f169932275 100644
--- a/libs/libcurl/src/curl_addrinfo.c
+++ b/libs/libcurl/src/curl_addrinfo.c
@@ -50,8 +50,9 @@
#include <stddef.h>
#include "curl_addrinfo.h"
-#include "inet_pton.h"
-#include "warnless.h"
+#include "fake_addrinfo.h"
+#include "curlx/inet_pton.h"
+#include "curlx/warnless.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
@@ -118,7 +119,7 @@ Curl_getaddrinfo_ex(const char *nodename,
*result = NULL; /* assume failure */
- error = getaddrinfo(nodename, servname, hints, &aihead);
+ error = CURL_GETADDRINFO(nodename, servname, hints, &aihead);
if(error)
return error;
@@ -184,7 +185,7 @@ Curl_getaddrinfo_ex(const char *nodename,
/* destroy the addrinfo list */
if(aihead)
- freeaddrinfo(aihead);
+ CURL_FREEADDRINFO(aihead);
/* if we failed, also destroy the Curl_addrinfo list */
if(error) {
@@ -217,7 +218,7 @@ Curl_getaddrinfo_ex(const char *nodename,
*
* This function returns a pointer to the first element of a newly allocated
* Curl_addrinfo struct linked list filled with the data of a given hostent.
- * Curl_addrinfo is meant to work like the addrinfo struct does for a IPv6
+ * Curl_addrinfo is meant to work like the addrinfo struct does for an IPv6
* stack, but usable also for IPv4, all hosts and environments.
*
* The memory allocated by this function *MUST* be free'd later on calling
@@ -318,11 +319,7 @@ Curl_he2ai(const struct hostent *he, int port)
addr = (void *)ai->ai_addr; /* storage area for this info */
memcpy(&addr->sin_addr, curr, sizeof(struct in_addr));
-#ifdef __MINGW32__
- addr->sin_family = (short)(he->h_addrtype);
-#else
addr->sin_family = (CURL_SA_FAMILY_T)(he->h_addrtype);
-#endif
addr->sin_port = htons((unsigned short)port);
break;
@@ -331,11 +328,7 @@ Curl_he2ai(const struct hostent *he, int port)
addr6 = (void *)ai->ai_addr; /* storage area for this info */
memcpy(&addr6->sin6_addr, curr, sizeof(struct in6_addr));
-#ifdef __MINGW32__
- addr6->sin6_family = (short)(he->h_addrtype);
-#else
addr6->sin6_family = (CURL_SA_FAMILY_T)(he->h_addrtype);
-#endif
addr6->sin6_port = htons((unsigned short)port);
break;
#endif
@@ -438,13 +431,13 @@ Curl_ip2addr(int af, const void *inaddr, const char *hostname, int port)
struct Curl_addrinfo *Curl_str2addr(char *address, int port)
{
struct in_addr in;
- if(Curl_inet_pton(AF_INET, address, &in) > 0)
+ if(curlx_inet_pton(AF_INET, address, &in) > 0)
/* This is a dotted IP address 123.123.123.123-style */
return Curl_ip2addr(AF_INET, &in, address, port);
#ifdef USE_IPV6
{
struct in6_addr in6;
- if(Curl_inet_pton(AF_INET6, address, &in6) > 0)
+ if(curlx_inet_pton(AF_INET6, address, &in6) > 0)
/* This is a dotted IPv6 address ::1-style */
return Curl_ip2addr(AF_INET6, &in6, address, port);
}
@@ -475,7 +468,7 @@ struct Curl_addrinfo *Curl_unix2addr(const char *path, bool *longpath,
sa_un = (void *) ai->ai_addr;
sa_un->sun_family = AF_UNIX;
- /* sun_path must be able to store the NUL-terminated path */
+ /* sun_path must be able to store the null-terminated path */
path_len = strlen(path) + 1;
if(path_len > sizeof(sa_un->sun_path)) {
free(ai);
@@ -516,8 +509,16 @@ curl_dbg_freeaddrinfo(struct addrinfo *freethis,
source, line, (void *)freethis);
#ifdef USE_LWIPSOCK
lwip_freeaddrinfo(freethis);
+#elif defined(USE_FAKE_GETADDRINFO)
+ {
+ const char *env = getenv("CURL_DNS_SERVER");
+ if(env)
+ r_freeaddrinfo(freethis);
+ else
+ freeaddrinfo(freethis);
+ }
#else
- (freeaddrinfo)(freethis);
+ freeaddrinfo(freethis);
#endif
}
#endif /* defined(CURLDEBUG) && defined(HAVE_FREEADDRINFO) */
@@ -534,15 +535,22 @@ curl_dbg_freeaddrinfo(struct addrinfo *freethis,
int
curl_dbg_getaddrinfo(const char *hostname,
- const char *service,
- const struct addrinfo *hints,
- struct addrinfo **result,
- int line, const char *source)
+ const char *service,
+ const struct addrinfo *hints,
+ struct addrinfo **result,
+ int line, const char *source)
{
#ifdef USE_LWIPSOCK
int res = lwip_getaddrinfo(hostname, service, hints, result);
+#elif defined(USE_FAKE_GETADDRINFO)
+ int res;
+ const char *env = getenv("CURL_DNS_SERVER");
+ if(env)
+ res = r_getaddrinfo(hostname, service, hints, result);
+ else
+ res = getaddrinfo(hostname, service, hints, result);
#else
- int res = (getaddrinfo)(hostname, service, hints, result);
+ int res = getaddrinfo(hostname, service, hints, result);
#endif
if(0 == res)
/* success */
diff --git a/libs/libcurl/src/curl_config.h.cmake b/libs/libcurl/src/curl_config.h.cmake
index 6f318d1cf4..bc10b15ee9 100644
--- a/libs/libcurl/src/curl_config.h.cmake
+++ b/libs/libcurl/src/curl_config.h.cmake
@@ -197,6 +197,9 @@
/* Define to 1 if you have _Atomic support. */
#cmakedefine HAVE_ATOMIC 1
+/* Define to 1 if you have the `accept4' function. */
+#cmakedefine HAVE_ACCEPT4 1
+
/* Define to 1 if you have the `fnmatch' function. */
#cmakedefine HAVE_FNMATCH 1
@@ -246,9 +249,6 @@
/* Define to 1 if you have the fseeko declaration. */
#cmakedefine HAVE_DECL_FSEEKO 1
-/* Define to 1 if you have the _fseeki64 function. */
-#cmakedefine HAVE__FSEEKI64 1
-
/* Define to 1 if you have the ftruncate function. */
#cmakedefine HAVE_FTRUNCATE 1
@@ -327,10 +327,10 @@
/* Define to 1 if you have the <ifaddrs.h> header file. */
#cmakedefine HAVE_IFADDRS_H 1
-/* Define to 1 if you have a IPv6 capable working inet_ntop function. */
+/* Define to 1 if you have an IPv6 capable working inet_ntop function. */
#cmakedefine HAVE_INET_NTOP 1
-/* Define to 1 if you have a IPv6 capable working inet_pton function. */
+/* Define to 1 if you have an IPv6 capable working inet_pton function. */
#cmakedefine HAVE_INET_PTON 1
/* Define to 1 if symbol `sa_family_t' exists */
@@ -430,12 +430,18 @@
/* Define to 1 if you have the `pipe' function. */
#cmakedefine HAVE_PIPE 1
+/* Define to 1 if you have the `pipe2' function. */
+#cmakedefine HAVE_PIPE2 1
+
/* Define to 1 if you have the `eventfd' function. */
#cmakedefine HAVE_EVENTFD 1
/* If you have poll */
#cmakedefine HAVE_POLL 1
+/* If you have realpath */
+#cmakedefine HAVE_REALPATH 1
+
/* Define to 1 if you have the <poll.h> header file. */
#cmakedefine HAVE_POLL_H 1
@@ -469,6 +475,9 @@
/* Define to 1 if you have the sendmmsg function. */
#cmakedefine HAVE_SENDMMSG 1
+/* Define to 1 if you have the <stdint.h> header file. */
+#cmakedefine HAVE_STDINT_H 1
+
/* Define to 1 if you have the 'fsetxattr' function. */
#cmakedefine HAVE_FSETXATTR 1
@@ -547,12 +556,6 @@
/* Define to 1 if you have the <stropts.h> header file. */
#cmakedefine HAVE_STROPTS_H 1
-/* Define to 1 if you have the strtok_r function. */
-#cmakedefine HAVE_STRTOK_R 1
-
-/* Define to 1 if you have the strtoll function. */
-#cmakedefine HAVE_STRTOLL 1
-
/* Define to 1 if you have the memrchr function. */
#cmakedefine HAVE_MEMRCHR 1
@@ -568,9 +571,6 @@
/* Define to 1 if you have the <sys/filio.h> header file. */
#cmakedefine HAVE_SYS_FILIO_H 1
-/* Define to 1 if you have the <sys/wait.h> header file. */
-#cmakedefine HAVE_SYS_WAIT_H 1
-
/* Define to 1 if you have the <sys/ioctl.h> header file. */
#cmakedefine HAVE_SYS_IOCTL_H 1
@@ -628,8 +628,8 @@
/* Define this symbol if your OS supports changing the contents of argv */
#cmakedefine HAVE_WRITABLE_ARGV 1
-/* Define to 1 if you need the malloc.h header file even with stdlib.h */
-#cmakedefine NEED_MALLOC_H 1
+/* Define this if time_t is unsigned */
+#cmakedefine HAVE_TIME_T_UNSIGNED 1
/* Define to 1 if _REENTRANT preprocessor symbol must be defined. */
#cmakedefine NEED_REENTRANT 1
@@ -637,24 +637,6 @@
/* cpu-machine-OS */
#cmakedefine CURL_OS ${CURL_OS}
-/* Name of package */
-#cmakedefine PACKAGE ${PACKAGE}
-
-/* Define to the address where bug reports for this package should be sent. */
-#cmakedefine PACKAGE_BUGREPORT ${PACKAGE_BUGREPORT}
-
-/* Define to the full name of this package. */
-#cmakedefine PACKAGE_NAME ${PACKAGE_NAME}
-
-/* Define to the full name and version of this package. */
-#cmakedefine PACKAGE_STRING ${PACKAGE_STRING}
-
-/* Define to the one symbol short name of this package. */
-#cmakedefine PACKAGE_TARNAME ${PACKAGE_TARNAME}
-
-/* Define to the version of this package. */
-#cmakedefine PACKAGE_VERSION ${PACKAGE_VERSION}
-
/*
Note: SIZEOF_* variables are fetched with CMake through check_type_size().
As per CMake documentation on CheckTypeSize, C preprocessor code is
@@ -706,6 +688,9 @@ ${SIZEOF_TIME_T_CODE}
/* if Secure Transport is enabled */
#cmakedefine USE_SECTRANSP 1
+/* if SSL session export support is available */
+#cmakedefine USE_SSLS_EXPORT 1
+
/* if mbedTLS is enabled */
#cmakedefine USE_MBEDTLS 1
@@ -718,14 +703,20 @@ ${SIZEOF_TIME_T_CODE}
/* if wolfSSL is enabled */
#cmakedefine USE_WOLFSSL 1
+/* if wolfSSL has the wolfSSL_get_peer_certificate function. */
+#cmakedefine HAVE_WOLFSSL_GET_PEER_CERTIFICATE 1
+
+/* if wolfSSL has the wolfSSL_UseALPN function. */
+#cmakedefine HAVE_WOLFSSL_USEALPN 1
+
/* if wolfSSL has the wolfSSL_DES_ecb_encrypt function. */
#cmakedefine HAVE_WOLFSSL_DES_ECB_ENCRYPT 1
/* if wolfSSL has the wolfSSL_BIO_new function. */
-#cmakedefine HAVE_WOLFSSL_BIO 1
+#cmakedefine HAVE_WOLFSSL_BIO_NEW 1
/* if wolfSSL has the wolfSSL_BIO_set_shutdown function. */
-#cmakedefine HAVE_WOLFSSL_FULL_BIO 1
+#cmakedefine HAVE_WOLFSSL_BIO_SET_SHUTDOWN 1
/* if libssh is in use */
#cmakedefine USE_LIBSSH 1
@@ -745,6 +736,9 @@ ${SIZEOF_TIME_T_CODE}
/* if OpenSSL is in use */
#cmakedefine USE_OPENSSL 1
+/* if AmiSSL is in use */
+#cmakedefine USE_AMISSL 1
+
/* if librtmp/rtmpdump is in use */
#cmakedefine USE_LIBRTMP 1
@@ -776,6 +770,9 @@ ${SIZEOF_TIME_T_CODE}
/* to enable openssl + nghttp3 */
#cmakedefine USE_OPENSSL_QUIC 1
+/* to enable openssl + ngtcp2 + nghttp3 */
+#cmakedefine OPENSSL_QUIC_API2 1
+
/* Define to 1 if you have the quiche_conn_set_qlog_fd function. */
#cmakedefine HAVE_QUICHE_CONN_SET_QLOG_FD 1
@@ -794,12 +791,12 @@ ${SIZEOF_TIME_T_CODE}
/* to enable Windows SSL */
#cmakedefine USE_SCHANNEL 1
+/* if Watt-32 is in use */
+#cmakedefine USE_WATT32 1
+
/* enable multiple SSL backends */
#cmakedefine CURL_WITH_MULTI_SSL 1
-/* Version number of package */
-#cmakedefine VERSION ${VERSION}
-
/* Number of bits in a file offset, on hosts where this is settable. */
#cmakedefine _FILE_OFFSET_BITS ${_FILE_OFFSET_BITS}
@@ -812,9 +809,6 @@ ${SIZEOF_TIME_T_CODE}
/* Define to empty if `const' does not conform to ANSI C. */
#cmakedefine const ${const}
-/* Type to use in place of in_addr_t when system does not provide it. */
-#cmakedefine in_addr_t ${in_addr_t}
-
/* Define to `unsigned int' if <sys/types.h> does not define. */
#cmakedefine size_t ${size_t}
@@ -844,3 +838,9 @@ ${SIZEOF_TIME_T_CODE}
/* if ECH support is available */
#cmakedefine USE_ECH 1
+
+/* Define to 1 if you have the wolfSSL_CTX_GenerateEchConfig function. */
+#cmakedefine HAVE_WOLFSSL_CTX_GENERATEECHCONFIG
+
+/* Define to 1 if you have the SSL_set1_ech_config_list function. */
+#cmakedefine HAVE_SSL_SET1_ECH_CONFIG_LIST
diff --git a/libs/libcurl/src/curl_config.h.in b/libs/libcurl/src/curl_config.h.in
index 89b1a8a6cf..ff6927a35f 100644
--- a/libs/libcurl/src/curl_config.h.in
+++ b/libs/libcurl/src/curl_config.h.in
@@ -1,10 +1,6 @@
/* lib/curl_config.h.in. Generated from configure.ac by autoheader. */
-/* Ignore c-ares deprecation warnings */
-#undef CARES_NO_DEPRECATED
-
-/* to enable curl debug memory tracking */
-#undef CURLDEBUG
+/* !checksrc! disable COPYRIGHT all */
/* Location of default ca bundle */
#undef CURL_CA_BUNDLE
@@ -162,18 +158,18 @@
/* cpu-machine-OS */
#undef CURL_OS
-/* IP address type in sockaddr */
-#undef CURL_SA_FAMILY_T
-
/* built with multiple SSL backends */
#undef CURL_WITH_MULTI_SSL
-/* enable debug build options */
-#undef DEBUGBUILD
-
/* Define to the type of arg 2 for gethostname. */
#undef GETHOSTNAME_TYPE_ARG2
+/* Define to 1 if you have the `accept4' function. */
+#undef HAVE_ACCEPT4
+
+/* Define to 1 if symbol `ADDRESS_FAMILY' exists */
+#undef HAVE_ADDRESS_FAMILY
+
/* Define to 1 if you have the alarm function. */
#undef HAVE_ALARM
@@ -214,28 +210,15 @@
/* Define to 1 if you have the CloseSocket camel case function. */
#undef HAVE_CLOSESOCKET_CAMEL
-/* Define to 1 if you have the <crypto.h> header file. */
-#undef HAVE_CRYPTO_H
-
/* Define to 1 if you have the fseeko declaration */
#undef HAVE_DECL_FSEEKO
-/* Define to 1 if you have the declaration of `getpwuid_r', and to 0 if you
- don't. */
-#undef HAVE_DECL_GETPWUID_R
-
-/* "Set if getpwuid_r() declaration is missing" */
-#undef HAVE_DECL_GETPWUID_R_MISSING
-
/* if you have <dirent.h> */
#undef HAVE_DIRENT_H
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
-/* Define to 1 if you have the <err.h> header file. */
-#undef HAVE_ERR_H
-
/* Define to 1 if you have the `eventfd' function. */
#undef HAVE_EVENTFD
@@ -278,9 +261,6 @@
/* Define to 1 if you have the `geteuid' function. */
#undef HAVE_GETEUID
-/* Define to 1 if you have the gethostbyname function. */
-#undef HAVE_GETHOSTBYNAME
-
/* Define to 1 if you have the gethostbyname_r function. */
#undef HAVE_GETHOSTBYNAME_R
@@ -347,9 +327,6 @@
/* if you have GNU GSS */
#undef HAVE_GSSGNU
-/* Define to 1 if you have the <hyper.h> header file. */
-#undef HAVE_HYPER_H
-
/* Define to 1 if you have the <idn2.h> header file. */
#undef HAVE_IDN2_H
@@ -359,18 +336,15 @@
/* Define to 1 if you have the `if_nametoindex' function. */
#undef HAVE_IF_NAMETOINDEX
-/* Define to 1 if you have a IPv6 capable working inet_ntop function. */
+/* Define to 1 if you have an IPv6 capable working inet_ntop function. */
#undef HAVE_INET_NTOP
-/* Define to 1 if you have a IPv6 capable working inet_pton function. */
+/* Define to 1 if you have an IPv6 capable working inet_pton function. */
#undef HAVE_INET_PTON
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
-/* Define to 1 if you have the ioctl function. */
-#undef HAVE_IOCTL
-
/* Define to 1 if you have the ioctlsocket function. */
#undef HAVE_IOCTLSOCKET
@@ -423,9 +397,6 @@
/* Define to 1 if you have the <libpsl.h> header file. */
#undef HAVE_LIBPSL_H
-/* Define to 1 if using LibreSSL. */
-#undef HAVE_LIBRESSL
-
/* Define to 1 if you have the <librtmp/rtmp.h> header file. */
#undef HAVE_LIBRTMP_RTMP_H
@@ -527,12 +498,12 @@
/* Define to 1 if you have the <openssl/x509.h> header file. */
#undef HAVE_OPENSSL_X509_H
-/* Define to 1 if you have the <pem.h> header file. */
-#undef HAVE_PEM_H
-
/* Define to 1 if you have the `pipe' function. */
#undef HAVE_PIPE
+/* Define to 1 if you have the `pipe2' function. */
+#undef HAVE_PIPE2
+
/* Define to 1 if you have the `poll' function. */
#undef HAVE_POLL
@@ -557,11 +528,14 @@
/* Define to 1 if you have the <quiche.h> header file. */
#undef HAVE_QUICHE_H
+/* Define to 1 if you have the `realpath' function. */
+#undef HAVE_REALPATH
+
/* Define to 1 if you have the recv function. */
#undef HAVE_RECV
-/* Define to 1 if you have the <rsa.h> header file. */
-#undef HAVE_RSA_H
+/* Define to 1 if symbol `sa_family_t' exists */
+#undef HAVE_SA_FAMILY_T
/* Define to 1 if you have the `sched_yield' function. */
#undef HAVE_SCHED_YIELD
@@ -578,9 +552,6 @@
/* Define to 1 if you have the `sendmsg' function. */
#undef HAVE_SENDMSG
-/* Define to 1 if you have the <setjmp.h> header file. */
-#undef HAVE_SETJMP_H
-
/* Define to 1 if you have the `setlocale' function. */
#undef HAVE_SETLOCALE
@@ -614,18 +585,15 @@
/* Define to 1 if you have the socketpair function. */
#undef HAVE_SOCKETPAIR
-/* Define to 1 if you have the `SSL_ech_set1_echconfig' function. */
-#undef HAVE_SSL_ECH_SET1_ECHCONFIG
-
-/* Define to 1 if you have the <ssl.h> header file. */
-#undef HAVE_SSL_H
-
/* Define to 1 if you have the `SSL_set0_wbio' function. */
#undef HAVE_SSL_SET0_WBIO
/* Define to 1 if you have the `SSL_set1_ech_config_list' function. */
#undef HAVE_SSL_SET1_ECH_CONFIG_LIST
+/* Define to 1 if you have the `SSL_set_quic_tls_cbs' function. */
+#undef HAVE_SSL_SET_QUIC_TLS_CBS
+
/* Define to 1 if you have the `SSL_set_quic_use_legacy_codepoint' function.
*/
#undef HAVE_SSL_SET_QUIC_USE_LEGACY_CODEPOINT
@@ -669,12 +637,6 @@
/* Define to 1 if you have the <stropts.h> header file. */
#undef HAVE_STROPTS_H
-/* Define to 1 if you have the strtok_r function. */
-#undef HAVE_STRTOK_R
-
-/* Define to 1 if you have the strtoll function. */
-#undef HAVE_STRTOLL
-
/* if struct sockaddr_storage is defined */
#undef HAVE_STRUCT_SOCKADDR_STORAGE
@@ -726,9 +688,6 @@
/* Define to 1 if you have the <sys/utime.h> header file. */
#undef HAVE_SYS_UTIME_H
-/* Define to 1 if you have the <sys/wait.h> header file. */
-#undef HAVE_SYS_WAIT_H
-
/* Define to 1 if you have the <sys/xattr.h> header file. */
#undef HAVE_SYS_XATTR_H
@@ -762,39 +721,37 @@
/* Define to 1 if you have the <wolfssh/ssh.h> header file. */
#undef HAVE_WOLFSSH_SSH_H
-/* if you have wolfSSL_BIO_new */
-#undef HAVE_WOLFSSL_BIO
+/* Define to 1 if you have the `wolfSSL_BIO_new' function. */
+#undef HAVE_WOLFSSL_BIO_NEW
+
+/* Define to 1 if you have the `wolfSSL_BIO_set_shutdown' function. */
+#undef HAVE_WOLFSSL_BIO_SET_SHUTDOWN
/* Define to 1 if you have the `wolfSSL_CTX_GenerateEchConfig' function. */
#undef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG
-/* if you have wolfSSL_DES_ecb_encrypt */
+/* Define to 1 if you have the `wolfSSL_DES_ecb_encrypt' function. */
#undef HAVE_WOLFSSL_DES_ECB_ENCRYPT
-/* if you have wolfSSL_BIO_set_shutdown */
-#undef HAVE_WOLFSSL_FULL_BIO
-
/* Define to 1 if you have the `wolfSSL_get_peer_certificate' function. */
#undef HAVE_WOLFSSL_GET_PEER_CERTIFICATE
+/* Define to 1 if you have the `wolfSSL_set_quic_use_legacy_codepoint'
+ function. */
+#undef HAVE_WOLFSSL_SET_QUIC_USE_LEGACY_CODEPOINT
+
/* Define to 1 if you have the `wolfSSL_UseALPN' function. */
#undef HAVE_WOLFSSL_USEALPN
/* Define this symbol if your OS supports changing the contents of argv */
#undef HAVE_WRITABLE_ARGV
-/* Define to 1 if you have the <x509.h> header file. */
-#undef HAVE_X509_H
-
/* if libzstd is in use */
#undef HAVE_ZSTD
/* Define to 1 if you have the <zstd.h> header file. */
#undef HAVE_ZSTD_H
-/* Define to 1 if you have the `_fseeki64' function. */
-#undef HAVE__FSEEKI64
-
/* Define to 1 if you have the `_setmode' function. */
#undef HAVE__SETMODE
@@ -810,6 +767,9 @@
/* Define to 1 if _THREAD_SAFE preprocessor symbol must be defined. */
#undef NEED_THREAD_SAFE
+/* openssl with new QUIC API */
+#undef OPENSSL_QUIC_API2
+
/* Name of package */
#undef PACKAGE
@@ -881,12 +841,9 @@
/* GSASL support enabled */
#undef USE_GSASL
-/* force HTTPS RR support for ECH */
+/* enable HTTPS RR support */
#undef USE_HTTPSRR
-/* if hyper is in use */
-#undef USE_HYPER
-
/* Define if you want to enable IPv6 support */
#undef USE_IPV6
@@ -920,30 +877,12 @@
/* if ngtcp2 is in use */
#undef USE_NGTCP2
-/* if ngtcp2_crypto_boringssl is in use */
-#undef USE_NGTCP2_CRYPTO_BORINGSSL
-
-/* if ngtcp2_crypto_gnutls is in use */
-#undef USE_NGTCP2_CRYPTO_GNUTLS
-
-/* if ngtcp2_crypto_quictls is in use */
-#undef USE_NGTCP2_CRYPTO_QUICTLS
-
-/* if ngtcp2_crypto_wolfssl is in use */
-#undef USE_NGTCP2_CRYPTO_WOLFSSL
-
-/* if ngtcp2 + nghttp3 is in use */
-#undef USE_NGTCP2_H3
-
/* Use OpenLDAP-specific code */
#undef USE_OPENLDAP
/* if OpenSSL is in use */
#undef USE_OPENSSL
-/* if openssl quic + nghttp3 is in use */
-#undef USE_OPENSSL_H3
-
/* if openssl QUIC is in use */
#undef USE_OPENSSL_QUIC
@@ -959,6 +898,9 @@
/* enable Secure Transport */
#undef USE_SECTRANSP
+/* if SSL session export support is available */
+#undef USE_SSLS_EXPORT
+
/* if you want POSIX threaded DNS lookup */
#undef USE_THREADS_POSIX
@@ -971,6 +913,9 @@
/* Use Unix domain sockets */
#undef USE_UNIX_SOCKETS
+/* if Watt-32 is in use */
+#undef USE_WATT32
+
/* Define to 1 if you are building a Windows target with crypto API support.
*/
#undef USE_WIN32_CRYPTO
@@ -985,10 +930,6 @@
/* Use Windows LDAP implementation */
#undef USE_WIN32_LDAP
-/* Define to 1 if you are building a Windows target without large file
- support. */
-#undef USE_WIN32_SMALL_FILES
-
/* to enable SSPI support */
#undef USE_WINDOWS_SSPI
@@ -1015,9 +956,6 @@
/* Define to empty if `const' does not conform to ANSI C. */
#undef const
-/* Type to use in place of in_addr_t when system does not provide it. */
-#undef in_addr_t
-
/* Define to `unsigned int' if <sys/types.h> does not define. */
#undef size_t
diff --git a/libs/libcurl/src/curl_ctype.h b/libs/libcurl/src/curl_ctype.h
index a04dc358ea..ba870a15ee 100644
--- a/libs/libcurl/src/curl_ctype.h
+++ b/libs/libcurl/src/curl_ctype.h
@@ -37,6 +37,7 @@
#define ISCNTRL(x) (ISLOWCNTRL(x) || IS7F(x))
#define ISALPHA(x) (ISLOWER(x) || ISUPPER(x))
#define ISXDIGIT(x) (ISDIGIT(x) || ISLOWHEXALHA(x) || ISUPHEXALHA(x))
+#define ISODIGIT(x) (((x) >= '0') && ((x) <= '7'))
#define ISALNUM(x) (ISDIGIT(x) || ISLOWER(x) || ISUPPER(x))
#define ISUPPER(x) (((x) >= 'A') && ((x) <= 'Z'))
#define ISLOWER(x) (((x) >= 'a') && ((x) <= 'z'))
@@ -46,6 +47,6 @@
#define ISURLPUNTCS(x) (((x) == '-') || ((x) == '.') || ((x) == '_') || \
((x) == '~'))
#define ISUNRESERVED(x) (ISALNUM(x) || ISURLPUNTCS(x))
-
+#define ISNEWLINE(x) (((x) == '\n') || (x) == '\r')
#endif /* HEADER_CURL_CTYPE_H */
diff --git a/libs/libcurl/src/curl_fnmatch.c b/libs/libcurl/src/curl_fnmatch.c
index f2188f8322..473f6ef40a 100644
--- a/libs/libcurl/src/curl_fnmatch.c
+++ b/libs/libcurl/src/curl_fnmatch.c
@@ -71,13 +71,13 @@ typedef enum {
#define SETCHARSET_OK 1
#define SETCHARSET_FAIL 0
-static int parsekeyword(unsigned char **pattern, unsigned char *charset)
+static int parsekeyword(const unsigned char **pattern, unsigned char *charset)
{
parsekey_state state = CURLFNM_PKW_INIT;
#define KEYLEN 10
char keyword[KEYLEN] = { 0 };
int i;
- unsigned char *p = *pattern;
+ const unsigned char *p = *pattern;
bool found = FALSE;
for(i = 0; !found; i++) {
char c = (char)*p++;
@@ -140,9 +140,9 @@ static char_class charclass(unsigned char c)
}
/* Include a character or a range in set. */
-static void setcharorrange(unsigned char **pp, unsigned char *charset)
+static void setcharorrange(const unsigned char **pp, unsigned char *charset)
{
- unsigned char *p = (*pp)++;
+ const unsigned char *p = (*pp)++;
unsigned char c = *p++;
charset[c] = 1;
@@ -162,7 +162,7 @@ static void setcharorrange(unsigned char **pp, unsigned char *charset)
}
/* returns 1 (TRUE) if pattern is OK, 0 if is bad ("p" is pattern pointer) */
-static int setcharset(unsigned char **p, unsigned char *charset)
+static int setcharset(const unsigned char **p, unsigned char *charset)
{
setcharset_state state = CURLFNM_SCHS_DEFAULT;
bool something_found = FALSE;
@@ -185,7 +185,7 @@ static int setcharset(unsigned char **p, unsigned char *charset)
(*p)++;
}
else if(c == '[') {
- unsigned char *pp = *p + 1;
+ const unsigned char *pp = *p + 1;
if(*pp++ == ':' && parsekeyword(&pp, charset))
*p = pp;
@@ -257,12 +257,12 @@ fail:
static int loop(const unsigned char *pattern, const unsigned char *string,
int maxstars)
{
- unsigned char *p = (unsigned char *)pattern;
- unsigned char *s = (unsigned char *)string;
+ const unsigned char *p = (const unsigned char *)pattern;
+ const unsigned char *s = (const unsigned char *)string;
unsigned char charset[CURLFNM_CHSET_SIZE] = { 0 };
for(;;) {
- unsigned char *pp;
+ const unsigned char *pp;
switch(*p) {
case '*':
@@ -319,7 +319,7 @@ static int loop(const unsigned char *pattern, const unsigned char *string,
else if(charset[CURLFNM_PRINT])
found = ISPRINT(*s);
else if(charset[CURLFNM_SPACE])
- found = ISSPACE(*s);
+ found = ISBLANK(*s);
else if(charset[CURLFNM_UPPER])
found = ISUPPER(*s);
else if(charset[CURLFNM_LOWER])
@@ -359,7 +359,8 @@ int Curl_fnmatch(void *ptr, const char *pattern, const char *string)
if(!pattern || !string) {
return CURL_FNMATCH_FAIL;
}
- return loop((unsigned char *)pattern, (unsigned char *)string, 2);
+ return loop((const unsigned char *)pattern,
+ (const unsigned char *)string, 2);
}
#else
#include <fnmatch.h>
diff --git a/libs/libcurl/src/curl_get_line.c b/libs/libcurl/src/curl_get_line.c
index dda1d63c1e..04f0650d0e 100644
--- a/libs/libcurl/src/curl_get_line.c
+++ b/libs/libcurl/src/curl_get_line.c
@@ -40,7 +40,7 @@ int Curl_get_line(struct dynbuf *buf, FILE *input)
{
CURLcode result;
char buffer[128];
- Curl_dyn_reset(buf);
+ curlx_dyn_reset(buf);
while(1) {
char *b = fgets(buffer, sizeof(buffer), input);
@@ -50,7 +50,7 @@ int Curl_get_line(struct dynbuf *buf, FILE *input)
if(!rlen)
break;
- result = Curl_dyn_addn(buf, b, rlen);
+ result = curlx_dyn_addn(buf, b, rlen);
if(result)
/* too long line or out of memory */
return 0; /* error */
@@ -61,13 +61,15 @@ int Curl_get_line(struct dynbuf *buf, FILE *input)
else if(feof(input)) {
/* append a newline */
- result = Curl_dyn_addn(buf, "\n", 1);
+ result = curlx_dyn_addn(buf, "\n", 1);
if(result)
/* too long line or out of memory */
return 0; /* error */
return 1; /* all good */
}
}
+ else if(curlx_dyn_len(buf))
+ return 1; /* all good */
else
break;
}
diff --git a/libs/libcurl/src/curl_get_line.h b/libs/libcurl/src/curl_get_line.h
index 9b8c0d8410..64b8081557 100644
--- a/libs/libcurl/src/curl_get_line.h
+++ b/libs/libcurl/src/curl_get_line.h
@@ -24,7 +24,7 @@
*
***************************************************************************/
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
/* Curl_get_line() returns complete lines that end with a newline. */
int Curl_get_line(struct dynbuf *buf, FILE *input);
diff --git a/libs/libcurl/src/curl_gethostname.c b/libs/libcurl/src/curl_gethostname.c
index f55514acad..9aefde3f26 100644
--- a/libs/libcurl/src/curl_gethostname.c
+++ b/libs/libcurl/src/curl_gethostname.c
@@ -73,7 +73,11 @@ int Curl_gethostname(char * const name, GETHOSTNAME_TYPE_ARG2 namelen)
#else /* DEBUGBUILD */
name[0] = '\0';
+#ifdef __AMIGA__
+ err = gethostname((unsigned char *)name, namelen);
+#else
err = gethostname(name, namelen);
+#endif
#endif
diff --git a/libs/libcurl/src/curl_gssapi.c b/libs/libcurl/src/curl_gssapi.c
index 5988348812..24f35b66bb 100644
--- a/libs/libcurl/src/curl_gssapi.c
+++ b/libs/libcurl/src/curl_gssapi.c
@@ -40,11 +40,16 @@
#define CURL_ALIGN8
#endif
+#if defined(__GNUC__) && defined(__APPLE__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
gss_OID_desc Curl_spnego_mech_oid CURL_ALIGN8 = {
- 6, (char *)"\x2b\x06\x01\x05\x05\x02"
+ 6, CURL_UNCONST("\x2b\x06\x01\x05\x05\x02")
};
gss_OID_desc Curl_krb5_mech_oid CURL_ALIGN8 = {
- 9, (char *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02"
+ 9, CURL_UNCONST("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02")
};
OM_uint32 Curl_gss_init_sec_context(
@@ -149,4 +154,8 @@ void Curl_gss_log_error(struct Curl_easy *data, const char *prefix,
#endif
}
+#if defined(__GNUC__) && defined(__APPLE__)
+#pragma GCC diagnostic pop
+#endif
+
#endif /* HAVE_GSSAPI */
diff --git a/libs/libcurl/src/curl_hmac.h b/libs/libcurl/src/curl_hmac.h
index dee12456ea..14b004d232 100644
--- a/libs/libcurl/src/curl_hmac.h
+++ b/libs/libcurl/src/curl_hmac.h
@@ -24,9 +24,9 @@
*
***************************************************************************/
-#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) \
- || !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) \
- || defined(USE_LIBSSH2)
+#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) || \
+ !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) || \
+ defined(USE_SSL)
#include <curl/curl.h>
diff --git a/libs/libcurl/src/curl_krb5.h b/libs/libcurl/src/curl_krb5.h
index 7e8e61ac23..f9bf63b1ce 100644
--- a/libs/libcurl/src/curl_krb5.h
+++ b/libs/libcurl/src/curl_krb5.h
@@ -39,14 +39,16 @@ struct Curl_sec_client_mech {
#define AUTH_CONTINUE 1
#define AUTH_ERROR 2
-#ifdef HAVE_GSSAPI
+#if defined(HAVE_GSSAPI) && !defined(CURL_DISABLE_FTP)
+void Curl_sec_conn_init(struct connectdata *);
+void Curl_sec_conn_destroy(struct connectdata *);
int Curl_sec_read_msg(struct Curl_easy *data, struct connectdata *conn, char *,
enum protection_level);
-void Curl_sec_end(struct connectdata *);
CURLcode Curl_sec_login(struct Curl_easy *, struct connectdata *);
int Curl_sec_request_prot(struct connectdata *conn, const char *level);
#else
-#define Curl_sec_end(x)
+#define Curl_sec_conn_init(x) Curl_nop_stmt
+#define Curl_sec_conn_destroy(x) Curl_nop_stmt
#endif
#endif /* HEADER_CURL_KRB5_H */
diff --git a/libs/libcurl/src/curl_memory.h b/libs/libcurl/src/curl_memory.h
index f34afa5c75..18b810b0e8 100644
--- a/libs/libcurl/src/curl_memory.h
+++ b/libs/libcurl/src/curl_memory.h
@@ -84,20 +84,6 @@
#undef socketpair
#endif
-#ifndef CURL_NO_GETADDRINFO_OVERRIDE
-#ifdef HAVE_GETADDRINFO
-#if defined(getaddrinfo) && defined(__osf__)
-#undef ogetaddrinfo
-#else
-#undef getaddrinfo
-#endif
-#endif /* HAVE_GETADDRINFO */
-
-#ifdef HAVE_FREEADDRINFO
-#undef freeaddrinfo
-#endif /* HAVE_FREEADDRINFO */
-#endif /* !CURL_NO_GETADDRINFO_OVERRIDE */
-
/* sclose is probably already defined, redefine it! */
#undef sclose
#undef fopen
diff --git a/libs/libcurl/src/curl_memrchr.c b/libs/libcurl/src/curl_memrchr.c
index 9b7ab8258b..f1d3f35c4d 100644
--- a/libs/libcurl/src/curl_memrchr.c
+++ b/libs/libcurl/src/curl_memrchr.c
@@ -33,10 +33,6 @@
#include "memdebug.h"
#ifndef HAVE_MEMRCHR
-#if (!defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)) || \
- defined(USE_OPENSSL) || \
- defined(USE_SCHANNEL)
-
/*
* Curl_memrchr()
*
@@ -57,12 +53,10 @@ Curl_memrchr(const void *s, int c, size_t n)
while(p >= q) {
if(*p == (unsigned char)c)
- return (void *)p;
+ return CURL_UNCONST(p);
p--;
}
}
return NULL;
}
-
-#endif
#endif /* HAVE_MEMRCHR */
diff --git a/libs/libcurl/src/curl_memrchr.h b/libs/libcurl/src/curl_memrchr.h
index dbced53b38..ea15561d31 100644
--- a/libs/libcurl/src/curl_memrchr.h
+++ b/libs/libcurl/src/curl_memrchr.h
@@ -34,15 +34,9 @@
#endif
#else /* HAVE_MEMRCHR */
-#if (!defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)) || \
- defined(USE_OPENSSL) || \
- defined(USE_SCHANNEL)
-
void *Curl_memrchr(const void *s, int c, size_t n);
-
#define memrchr(x,y,z) Curl_memrchr((x),(y),(z))
-#endif
#endif /* HAVE_MEMRCHR */
#endif /* HEADER_CURL_MEMRCHR_H */
diff --git a/libs/libcurl/src/curl_multibyte.c b/libs/libcurl/src/curl_multibyte.c
deleted file mode 100644
index a991df2906..0000000000
--- a/libs/libcurl/src/curl_multibyte.c
+++ /dev/null
@@ -1,162 +0,0 @@
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-/*
- * This file is 'mem-include-scan' clean, which means memdebug.h and
- * curl_memory.h are purposely not included in this file. See test 1132.
- *
- * The functions in this file are curlx functions which are not tracked by the
- * curl memory tracker memdebug.
- */
-
-#include "curl_setup.h"
-
-#if defined(_WIN32)
-
-#include "curl_multibyte.h"
-
-/*
- * MultiByte conversions using Windows kernel32 library.
- */
-
-wchar_t *curlx_convert_UTF8_to_wchar(const char *str_utf8)
-{
- wchar_t *str_w = NULL;
-
- if(str_utf8) {
- int str_w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
- str_utf8, -1, NULL, 0);
- if(str_w_len > 0) {
- str_w = malloc(str_w_len * sizeof(wchar_t));
- if(str_w) {
- if(MultiByteToWideChar(CP_UTF8, 0, str_utf8, -1, str_w,
- str_w_len) == 0) {
- free(str_w);
- return NULL;
- }
- }
- }
- }
-
- return str_w;
-}
-
-char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w)
-{
- char *str_utf8 = NULL;
-
- if(str_w) {
- int bytes = WideCharToMultiByte(CP_UTF8, 0, str_w, -1,
- NULL, 0, NULL, NULL);
- if(bytes > 0) {
- str_utf8 = malloc(bytes);
- if(str_utf8) {
- if(WideCharToMultiByte(CP_UTF8, 0, str_w, -1, str_utf8, bytes,
- NULL, NULL) == 0) {
- free(str_utf8);
- return NULL;
- }
- }
- }
- }
-
- return str_utf8;
-}
-
-#endif /* _WIN32 */
-
-#if defined(USE_WIN32_LARGE_FILES) || defined(USE_WIN32_SMALL_FILES)
-
-int curlx_win32_open(const char *filename, int oflag, ...)
-{
- int pmode = 0;
-
-#ifdef _UNICODE
- int result = -1;
- wchar_t *filename_w = curlx_convert_UTF8_to_wchar(filename);
-#endif
-
- va_list param;
- va_start(param, oflag);
- if(oflag & O_CREAT)
- pmode = va_arg(param, int);
- va_end(param);
-
-#ifdef _UNICODE
- if(filename_w) {
- result = _wopen(filename_w, oflag, pmode);
- curlx_unicodefree(filename_w);
- }
- else
- errno = EINVAL;
- return result;
-#else
- return (_open)(filename, oflag, pmode);
-#endif
-}
-
-FILE *curlx_win32_fopen(const char *filename, const char *mode)
-{
-#ifdef _UNICODE
- FILE *result = NULL;
- wchar_t *filename_w = curlx_convert_UTF8_to_wchar(filename);
- wchar_t *mode_w = curlx_convert_UTF8_to_wchar(mode);
- if(filename_w && mode_w)
- result = _wfopen(filename_w, mode_w);
- else
- errno = EINVAL;
- curlx_unicodefree(filename_w);
- curlx_unicodefree(mode_w);
- return result;
-#else
- return (fopen)(filename, mode);
-#endif
-}
-
-int curlx_win32_stat(const char *path, struct_stat *buffer)
-{
-#ifdef _UNICODE
- int result = -1;
- wchar_t *path_w = curlx_convert_UTF8_to_wchar(path);
- if(path_w) {
-#if defined(USE_WIN32_SMALL_FILES)
- result = _wstat(path_w, buffer);
-#else
- result = _wstati64(path_w, buffer);
-#endif
- curlx_unicodefree(path_w);
- }
- else
- errno = EINVAL;
- return result;
-#else
-#if defined(USE_WIN32_SMALL_FILES)
- return _stat(path, buffer);
-#else
- return _stati64(path, buffer);
-#endif
-#endif
-}
-
-#endif /* USE_WIN32_LARGE_FILES || USE_WIN32_SMALL_FILES */
diff --git a/libs/libcurl/src/curl_ntlm_core.c b/libs/libcurl/src/curl_ntlm_core.c
index 70474b269c..01e91c24fc 100644
--- a/libs/libcurl/src/curl_ntlm_core.c
+++ b/libs/libcurl/src/curl_ntlm_core.c
@@ -71,30 +71,33 @@
# include <openssl/md5.h>
# include <openssl/ssl.h>
# include <openssl/rand.h>
+# if defined(OPENSSL_IS_AWSLC)
+# define DES_set_key_unchecked (void)DES_set_key
+# define DESKEYARG(x) *x
+# define DESKEY(x) &x
+# else
+# define DESKEYARG(x) *x
+# define DESKEY(x) &x
+# endif
#else
# include <wolfssl/openssl/des.h>
# include <wolfssl/openssl/md5.h>
# include <wolfssl/openssl/ssl.h>
# include <wolfssl/openssl/rand.h>
-#endif
-
-# if (defined(OPENSSL_VERSION_NUMBER) && \
- (OPENSSL_VERSION_NUMBER < 0x00907001L)) && !defined(USE_WOLFSSL)
-# define DES_key_schedule des_key_schedule
-# define DES_cblock des_cblock
-# define DES_set_odd_parity des_set_odd_parity
-# define DES_set_key des_set_key
-# define DES_ecb_encrypt des_ecb_encrypt
-# define DESKEY(x) x
-# define DESKEYARG(x) x
-# elif defined(OPENSSL_IS_AWSLC)
-# define DES_set_key_unchecked (void)DES_set_key
+# if defined(OPENSSL_COEXIST)
+# define DES_key_schedule WOLFSSL_DES_key_schedule
+# define DES_cblock WOLFSSL_DES_cblock
+# define DES_set_odd_parity wolfSSL_DES_set_odd_parity
+# define DES_set_key wolfSSL_DES_set_key
+# define DES_set_key_unchecked wolfSSL_DES_set_key_unchecked
+# define DES_ecb_encrypt wolfSSL_DES_ecb_encrypt
+# define DESKEY(x) ((WOLFSSL_DES_key_schedule *)(x))
# define DESKEYARG(x) *x
-# define DESKEY(x) &x
# else
# define DESKEYARG(x) *x
# define DESKEY(x) &x
# endif
+#endif
#elif defined(USE_GNUTLS)
@@ -123,7 +126,7 @@
#include "curl_ntlm_core.h"
#include "curl_md5.h"
#include "curl_hmac.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
#include "curl_endian.h"
#include "curl_des.h"
#include "curl_md4.h"
@@ -132,9 +135,6 @@
#include "curl_memory.h"
#include "memdebug.h"
-#define NTLMv2_BLOB_SIGNATURE "\x01\x01\x00\x00"
-#define NTLMv2_BLOB_LEN (44 -16 + ntlm->target_info_len + 4)
-
#if !defined(CURL_NTLM_NOT_SUPPORTED)
/*
* Turns a 56-bit key into being 64-bit wide.
@@ -321,16 +321,16 @@ void Curl_ntlm_core_lm_resp(const unsigned char *keys,
DES_key_schedule ks;
setup_des_key(keys, DESKEY(ks));
- DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) results,
- DESKEY(ks), DES_ENCRYPT);
+ DES_ecb_encrypt((DES_cblock*)CURL_UNCONST(plaintext),
+ (DES_cblock*)results, DESKEY(ks), DES_ENCRYPT);
setup_des_key(keys + 7, DESKEY(ks));
- DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results + 8),
- DESKEY(ks), DES_ENCRYPT);
+ DES_ecb_encrypt((DES_cblock*)CURL_UNCONST(plaintext),
+ (DES_cblock*)(results + 8), DESKEY(ks), DES_ENCRYPT);
setup_des_key(keys + 14, DESKEY(ks));
- DES_ecb_encrypt((DES_cblock*) plaintext, (DES_cblock*) (results + 16),
- DESKEY(ks), DES_ENCRYPT);
+ DES_ecb_encrypt((DES_cblock*)CURL_UNCONST(plaintext),
+ (DES_cblock*)(results + 16), DESKEY(ks), DES_ENCRYPT);
#elif defined(USE_GNUTLS)
struct des_ctx des;
setup_des_key(keys, &des);
@@ -375,12 +375,12 @@ CURLcode Curl_ntlm_core_mk_lm_hash(const char *password,
DES_key_schedule ks;
setup_des_key(pw, DESKEY(ks));
- DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)lmbuffer,
- DESKEY(ks), DES_ENCRYPT);
+ DES_ecb_encrypt((DES_cblock *)CURL_UNCONST(magic),
+ (DES_cblock *)lmbuffer, DESKEY(ks), DES_ENCRYPT);
setup_des_key(pw + 7, DESKEY(ks));
- DES_ecb_encrypt((DES_cblock *)magic, (DES_cblock *)(lmbuffer + 8),
- DESKEY(ks), DES_ENCRYPT);
+ DES_ecb_encrypt((DES_cblock *)CURL_UNCONST(magic),
+ (DES_cblock *)(lmbuffer + 8), DESKEY(ks), DES_ENCRYPT);
#elif defined(USE_GNUTLS)
struct des_ctx des;
setup_des_key(pw, &des);
@@ -453,6 +453,9 @@ CURLcode Curl_ntlm_core_mk_nt_hash(const char *password,
#if !defined(USE_WINDOWS_SSPI)
+#define NTLMv2_BLOB_SIGNATURE "\x01\x01\x00\x00"
+#define NTLMv2_BLOB_LEN (44 -16 + ntlm->target_info_len + 4)
+
/* Timestamp in tenths of a microsecond since January 1, 1601 00:00:00 UTC. */
struct ms_filetime {
unsigned int dwLowDateTime;
@@ -614,7 +617,7 @@ CURLcode Curl_ntlm_core_mk_ntlmv2_resp(unsigned char *ntlmv2hash,
/* Concatenate the Type 2 challenge with the BLOB and do HMAC MD5 */
memcpy(ptr + 8, &ntlm->nonce[0], 8);
result = Curl_hmacit(&Curl_HMAC_MD5, ntlmv2hash, HMAC_MD5_LENGTH, ptr + 8,
- NTLMv2_BLOB_LEN + 8, hmac_output);
+ NTLMv2_BLOB_LEN + 8, hmac_output);
if(result) {
free(ptr);
return result;
diff --git a/libs/libcurl/src/curl_printf.h b/libs/libcurl/src/curl_printf.h
index 349735d000..f92bf7e4d0 100644
--- a/libs/libcurl/src/curl_printf.h
+++ b/libs/libcurl/src/curl_printf.h
@@ -24,21 +24,25 @@
*
***************************************************************************/
-/*
- * This header should be included by ALL code in libcurl that uses any
- * *rintf() functions.
- */
-
-#ifndef CURL_TEMP_PRINTF
-#error "CURL_TEMP_PRINTF must be set before including curl/mprintf.h"
-#endif
-
#include <curl/mprintf.h>
#define MERR_OK 0
#define MERR_MEM 1
#define MERR_TOO_LARGE 2
+/* Lower-case digits. */
+extern const unsigned char Curl_ldigits[];
+
+/* Upper-case digits. */
+extern const unsigned char Curl_udigits[];
+
+#ifdef BUILDING_LIBCURL
+
+/*
+ * This header should be included by ALL code in libcurl that uses any
+ * *rintf() functions.
+ */
+
# undef printf
# undef fprintf
# undef msnprintf
@@ -55,4 +59,6 @@
# define mvsnprintf curl_mvsnprintf
# define aprintf curl_maprintf
# define vaprintf curl_mvaprintf
+
+#endif /* BUILDING_LIBCURL */
#endif /* HEADER_CURL_PRINTF_H */
diff --git a/libs/libcurl/src/curl_range.c b/libs/libcurl/src/curl_range.c
index 3ab7f98b3c..2703d5c904 100644
--- a/libs/libcurl/src/curl_range.c
+++ b/libs/libcurl/src/curl_range.c
@@ -26,7 +26,7 @@
#include <curl/curl.h>
#include "curl_range.h"
#include "sendf.h"
-#include "strtoofft.h"
+#include "curlx/strparse.h"
/* Only include this function if one or more of FTP, FILE are enabled. */
#if !defined(CURL_DISABLE_FTP) || !defined(CURL_DISABLE_FILE)
@@ -37,28 +37,29 @@
*/
CURLcode Curl_range(struct Curl_easy *data)
{
- curl_off_t from, to;
- char *ptr;
- char *ptr2;
-
if(data->state.use_range && data->state.range) {
- CURLofft from_t;
- CURLofft to_t;
- from_t = curlx_strtoofft(data->state.range, &ptr, 10, &from);
- if(from_t == CURL_OFFT_FLOW)
- return CURLE_RANGE_ERROR;
- while(*ptr && (ISBLANK(*ptr) || (*ptr == '-')))
- ptr++;
- to_t = curlx_strtoofft(ptr, &ptr2, 10, &to);
- if(to_t == CURL_OFFT_FLOW)
+ curl_off_t from, to;
+ bool first_num = TRUE;
+ const char *p = data->state.range;
+ if(curlx_str_number(&p, &from, CURL_OFF_T_MAX))
+ first_num = FALSE;
+
+ if(curlx_str_single(&p, '-'))
+ /* no leading dash or after the first number is an error */
return CURLE_RANGE_ERROR;
- if((to_t == CURL_OFFT_INVAL) && !from_t) {
+
+ if(curlx_str_number(&p, &to, CURL_OFF_T_MAX)) {
+ /* no second number */
/* X - */
data->state.resume_from = from;
DEBUGF(infof(data, "RANGE %" FMT_OFF_T " to end of file", from));
}
- else if((from_t == CURL_OFFT_INVAL) && !to_t) {
+ else if(!first_num) {
/* -Y */
+ if(!to)
+ /* "-0" is just wrong */
+ return CURLE_RANGE_ERROR;
+
data->req.maxdownload = to;
data->state.resume_from = -to;
DEBUGF(infof(data, "RANGE the last %" FMT_OFF_T " bytes", to));
diff --git a/libs/libcurl/src/curl_rtmp.c b/libs/libcurl/src/curl_rtmp.c
index 9e1d578db1..6eb41040c2 100644
--- a/libs/libcurl/src/curl_rtmp.c
+++ b/libs/libcurl/src/curl_rtmp.c
@@ -29,10 +29,11 @@
#include "curl_rtmp.h"
#include "urldata.h"
-#include "nonblock.h" /* for curlx_nonblock */
+#include "url.h"
+#include "curlx/nonblock.h" /* for curlx_nonblock */
#include "progress.h" /* for Curl_pgrsSetUploadSize */
#include "transfer.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
#include <curl/curl.h>
#include <librtmp/rtmp.h>
@@ -52,6 +53,10 @@
#define DEF_BUFTIME (2*60*60*1000) /* 2 hours */
+/* meta key for storing RTMP* at connection */
+#define CURL_META_RTMP_CONN "meta:proto:rtmp:conn"
+
+
static CURLcode rtmp_setup_connection(struct Curl_easy *data,
struct connectdata *conn);
static CURLcode rtmp_do(struct Curl_easy *data, bool *done);
@@ -85,6 +90,7 @@ const struct Curl_handler Curl_handler_rtmp = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_RTMP, /* defport */
CURLPROTO_RTMP, /* protocol */
CURLPROTO_RTMP, /* family */
@@ -109,6 +115,7 @@ const struct Curl_handler Curl_handler_rtmpt = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_RTMPT, /* defport */
CURLPROTO_RTMPT, /* protocol */
CURLPROTO_RTMPT, /* family */
@@ -133,6 +140,7 @@ const struct Curl_handler Curl_handler_rtmpe = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_RTMP, /* defport */
CURLPROTO_RTMPE, /* protocol */
CURLPROTO_RTMPE, /* family */
@@ -157,6 +165,7 @@ const struct Curl_handler Curl_handler_rtmpte = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_RTMPT, /* defport */
CURLPROTO_RTMPTE, /* protocol */
CURLPROTO_RTMPTE, /* family */
@@ -181,6 +190,7 @@ const struct Curl_handler Curl_handler_rtmps = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_RTMPS, /* defport */
CURLPROTO_RTMPS, /* protocol */
CURLPROTO_RTMP, /* family */
@@ -205,17 +215,28 @@ const struct Curl_handler Curl_handler_rtmpts = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_RTMPS, /* defport */
CURLPROTO_RTMPTS, /* protocol */
CURLPROTO_RTMPT, /* family */
PROTOPT_NONE /* flags */
};
+static void rtmp_conn_dtor(void *key, size_t klen, void *entry)
+{
+ RTMP *r = entry;
+ (void)key;
+ (void)klen;
+ RTMP_Close(r);
+ RTMP_Free(r);
+}
+
static CURLcode rtmp_setup_connection(struct Curl_easy *data,
struct connectdata *conn)
{
RTMP *r = RTMP_Alloc();
- if(!r)
+ if(!r ||
+ Curl_conn_meta_set(conn, CURL_META_RTMP_CONN, r, rtmp_conn_dtor))
return CURLE_OUT_OF_MEMORY;
RTMP_Init(r);
@@ -224,16 +245,18 @@ static CURLcode rtmp_setup_connection(struct Curl_easy *data,
RTMP_Free(r);
return CURLE_URL_MALFORMAT;
}
- conn->proto.rtmp = r;
return CURLE_OK;
}
static CURLcode rtmp_connect(struct Curl_easy *data, bool *done)
{
struct connectdata *conn = data->conn;
- RTMP *r = conn->proto.rtmp;
+ RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
SET_RCVTIMEO(tv, 10);
+ if(!r)
+ return CURLE_FAILED_INIT;
+
r->m_sb.sb_socket = (int)conn->sock[FIRSTSOCKET];
/* We have to know if it is a write before we send the
@@ -266,9 +289,9 @@ static CURLcode rtmp_connect(struct Curl_easy *data, bool *done)
static CURLcode rtmp_do(struct Curl_easy *data, bool *done)
{
struct connectdata *conn = data->conn;
- RTMP *r = conn->proto.rtmp;
+ RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
- if(!RTMP_ConnectStream(r, 0))
+ if(!r || !RTMP_ConnectStream(r, 0))
return CURLE_FAILED_INIT;
if(data->state.upload) {
@@ -295,14 +318,11 @@ static CURLcode rtmp_disconnect(struct Curl_easy *data,
struct connectdata *conn,
bool dead_connection)
{
- RTMP *r = conn->proto.rtmp;
+ RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
(void)data;
(void)dead_connection;
- if(r) {
- conn->proto.rtmp = NULL;
- RTMP_Close(r);
- RTMP_Free(r);
- }
+ if(r)
+ Curl_conn_meta_remove(conn, CURL_META_RTMP_CONN);
return CURLE_OK;
}
@@ -310,10 +330,14 @@ static ssize_t rtmp_recv(struct Curl_easy *data, int sockindex, char *buf,
size_t len, CURLcode *err)
{
struct connectdata *conn = data->conn;
- RTMP *r = conn->proto.rtmp;
+ RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
ssize_t nread;
(void)sockindex; /* unused */
+ if(!r) {
+ *err = CURLE_FAILED_INIT;
+ return -1;
+ }
nread = RTMP_Read(r, buf, curlx_uztosi(len));
if(nread < 0) {
@@ -332,13 +356,17 @@ static ssize_t rtmp_send(struct Curl_easy *data, int sockindex,
const void *buf, size_t len, bool eos, CURLcode *err)
{
struct connectdata *conn = data->conn;
- RTMP *r = conn->proto.rtmp;
+ RTMP *r = Curl_conn_meta_get(conn, CURL_META_RTMP_CONN);
ssize_t num;
(void)sockindex; /* unused */
(void)eos; /* unused */
+ if(!r) {
+ *err = CURLE_FAILED_INIT;
+ return -1;
+ }
- num = RTMP_Write(r, (char *)buf, curlx_uztosi(len));
+ num = RTMP_Write(r, (const char *)buf, curlx_uztosi(len));
if(num < 0)
*err = CURLE_SEND_ERROR;
diff --git a/libs/libcurl/src/curl_sasl.c b/libs/libcurl/src/curl_sasl.c
index 729e192f41..d919fe4b0e 100644
--- a/libs/libcurl/src/curl_sasl.c
+++ b/libs/libcurl/src/curl_sasl.c
@@ -42,15 +42,14 @@
#include <curl/curl.h>
#include "urldata.h"
-#include "curl_base64.h"
+#include "curlx/base64.h"
#include "curl_md5.h"
#include "vauth/vauth.h"
#include "cfilters.h"
#include "vtls/vtls.h"
#include "curl_hmac.h"
#include "curl_sasl.h"
-#include "warnless.h"
-#include "strtok.h"
+#include "curlx/warnless.h"
#include "sendf.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -284,7 +283,7 @@ static CURLcode get_server_message(struct SASL *sasl, struct Curl_easy *data,
if(!*serverdata || *serverdata == '=')
Curl_bufref_set(out, NULL, 0, NULL);
else {
- result = Curl_base64_decode(serverdata, &msg, &msglen);
+ result = curlx_base64_decode(serverdata, &msg, &msglen);
if(!result)
Curl_bufref_set(out, msg, msglen, curl_free);
}
@@ -307,8 +306,8 @@ static CURLcode build_message(struct SASL *sasl, struct bufref *msg)
char *base64;
size_t base64len;
- result = Curl_base64_encode((const char *) Curl_bufref_ptr(msg),
- Curl_bufref_len(msg), &base64, &base64len);
+ result = curlx_base64_encode((const char *) Curl_bufref_ptr(msg),
+ Curl_bufref_len(msg), &base64, &base64len);
if(!result)
Curl_bufref_set(msg, base64, base64len, curl_free);
}
@@ -757,4 +756,101 @@ CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
return result;
}
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static void sasl_unchosen(struct Curl_easy *data, unsigned short mech,
+ unsigned short enabledmechs,
+ bool built_in, bool platform,
+ const char *param_missing)
+{
+ const char *mname = NULL;
+ size_t i;
+
+ if(!(enabledmechs & mech))
+ return;
+
+ for(i = 0; mechtable[i].name; ++i) {
+ if(mechtable[i].bit == mech) {
+ mname = mechtable[i].name;
+ break;
+ }
+ }
+ if(!mname) /* should not happen */
+ return;
+ if(!built_in)
+ infof(data, "SASL: %s not builtin", mname);
+ else if(!platform)
+ infof(data, "SASL: %s not supported by the platform/libraries", mname);
+ else {
+ if(param_missing)
+ infof(data, "SASL: %s is missing %s", mname, param_missing);
+ if(!data->state.aptr.user)
+ infof(data, "SASL: %s is missing username", mname);
+ }
+}
+#endif /* CURL_DISABLE_VERBOSE_STRINGS */
+
+CURLcode Curl_sasl_is_blocked(struct SASL *sasl, struct Curl_easy *data)
+{
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+#ifdef USE_KERBEROS5
+#define CURL_SASL_KERBEROS5 TRUE
+#else
+#define CURL_SASL_KERBEROS5 FALSE
+#endif
+#ifdef USE_GSASL
+#define CURL_SASL_GASL TRUE
+#else
+#define CURL_SASL_GASL FALSE
+#endif
+#ifdef CURL_DISABLE_DIGEST_AUTH
+#define CURL_SASL_DIGEST TRUE
+#else
+#define CURL_SASL_DIGEST FALSE
+#endif
+#ifndef USE_NTLM
+#define CURL_SASL_NTLM TRUE
+#else
+#define CURL_SASL_NTLM FALSE
+#endif
+ /* Failing SASL authentication is a pain. Give a helping hand if
+ * we were unable to select an AUTH mechanism.
+ * `sasl->authmechs` are mechanisms offered by the peer
+ * `sasl->prefmech` are mechanisms preferred by us */
+ unsigned short enabledmechs = sasl->authmechs & sasl->prefmech;
+
+ if(!sasl->authmechs)
+ infof(data, "SASL: no auth mechanism was offered or recognized");
+ else if(!enabledmechs)
+ infof(data, "SASL: no overlap between offered and configured "
+ "auth mechanisms");
+ else {
+ infof(data, "SASL: no auth mechanism offered could be selected");
+ if((enabledmechs & SASL_MECH_EXTERNAL) && data->conn->passwd[0])
+ infof(data, "SASL: auth EXTERNAL not chosen with password");
+ sasl_unchosen(data, SASL_MECH_GSSAPI, enabledmechs,
+ CURL_SASL_KERBEROS5, Curl_auth_is_gssapi_supported(), NULL);
+ sasl_unchosen(data, SASL_MECH_SCRAM_SHA_256, enabledmechs,
+ CURL_SASL_GASL, FALSE, NULL);
+ sasl_unchosen(data, SASL_MECH_SCRAM_SHA_1, enabledmechs,
+ CURL_SASL_GASL, FALSE, NULL);
+ sasl_unchosen(data, SASL_MECH_DIGEST_MD5, enabledmechs,
+ CURL_SASL_DIGEST, Curl_auth_is_digest_supported(), NULL);
+ sasl_unchosen(data, SASL_MECH_CRAM_MD5, enabledmechs,
+ CURL_SASL_DIGEST, TRUE, NULL);
+ sasl_unchosen(data, SASL_MECH_NTLM, enabledmechs,
+ CURL_SASL_NTLM, Curl_auth_is_ntlm_supported(), NULL);
+ sasl_unchosen(data, SASL_MECH_OAUTHBEARER, enabledmechs, TRUE, TRUE,
+ data->set.str[STRING_BEARER] ?
+ NULL : "CURLOPT_XOAUTH2_BEARER");
+ sasl_unchosen(data, SASL_MECH_XOAUTH2, enabledmechs, TRUE, TRUE,
+ data->set.str[STRING_BEARER] ?
+ NULL : "CURLOPT_XOAUTH2_BEARER");
+ }
+#endif /* CURL_DISABLE_VERBOSE_STRINGS */
+ (void)sasl;
+ (void)data;
+ return CURLE_LOGIN_DENIED;
+}
+
#endif /* protocols are enabled that use SASL */
diff --git a/libs/libcurl/src/curl_sasl.h b/libs/libcurl/src/curl_sasl.h
index 0c07f9f642..a11cc555c8 100644
--- a/libs/libcurl/src/curl_sasl.h
+++ b/libs/libcurl/src/curl_sasl.h
@@ -162,4 +162,6 @@ CURLcode Curl_sasl_start(struct SASL *sasl, struct Curl_easy *data,
CURLcode Curl_sasl_continue(struct SASL *sasl, struct Curl_easy *data,
int code, saslprogress *progress);
+CURLcode Curl_sasl_is_blocked(struct SASL *sasl, struct Curl_easy *data);
+
#endif /* HEADER_CURL_SASL_H */
diff --git a/libs/libcurl/src/curl_setup.h b/libs/libcurl/src/curl_setup.h
index dc4a4553aa..7fd1a98f9a 100644
--- a/libs/libcurl/src/curl_setup.h
+++ b/libs/libcurl/src/curl_setup.h
@@ -28,22 +28,12 @@
#define CURL_NO_OLDIES
#endif
-/* Tell "curl/curl.h" not to include "curl/mprintf.h" */
-#define CURL_SKIP_INCLUDE_MPRINTF
-
-/* FIXME: Delete this once the warnings have been fixed. */
-#if !defined(CURL_WARN_SIGN_CONVERSION)
-#if defined(__GNUC__) || defined(__clang__)
-#pragma GCC diagnostic ignored "-Wsign-conversion"
-#endif
-#endif
-
/* Set default _WIN32_WINNT */
#ifdef __MINGW32__
#include <_mingw.h>
#endif
-/* Workaround for Homebrew gcc 12.4.0, 13.3.0, 14.1.0 and newer (as of 14.1.0)
+/* Workaround for Homebrew gcc 12.4.0, 13.3.0, 14.1.0, 14.2.0 (initial build)
that started advertising the `availability` attribute, which then gets used
by Apple SDK, but, in a way incompatible with gcc, resulting in misc errors
inside SDK headers, e.g.:
@@ -51,16 +41,19 @@
definition
error: expected ',' or '}' before
Followed by missing declarations.
- Fix it by overriding the built-in feature-check macro used by the headers
- to enable the problematic attributes. This makes the feature check fail. */
-#if defined(__APPLE__) && \
- !defined(__clang__) && \
- defined(__GNUC__) && __GNUC__ >= 12 && \
+ Work it around by overriding the built-in feature-check macro used by the
+ headers to enable the problematic attributes. This makes the feature check
+ fail. Fixed in 14.2.0_1. Disable the workaround if the fix is detected. */
+#if defined(__APPLE__) && !defined(__clang__) && defined(__GNUC__) && \
defined(__has_attribute)
-#define availability curl_pp_attribute_disabled
+# if !defined(__has_feature)
+# define availability curl_pp_attribute_disabled
+# elif !__has_feature(attribute_availability)
+# define availability curl_pp_attribute_disabled
+# endif
#endif
-#if defined(__APPLE__)
+#ifdef __APPLE__
#include <sys/types.h>
#include <TargetConditionals.h>
/* Fixup faulty target macro initialization in macOS SDK since v14.4 (as of
@@ -82,13 +75,23 @@
#endif
#endif
-/*
- * Disable Visual Studio warnings:
- * 4127 "conditional expression is constant"
- */
+/* Visual Studio 2008 is the minimum Visual Studio version we support.
+ Workarounds for older versions of Visual Studio have been removed. */
+#if defined(_MSC_VER) && (_MSC_VER < 1500)
+#error "Ancient versions of Visual Studio are no longer supported due to bugs."
+#endif
+
#ifdef _MSC_VER
+/* Disable Visual Studio warnings: 4127 "conditional expression is constant" */
#pragma warning(disable:4127)
+/* Avoid VS2005 and upper complaining about portable C functions. */
+#ifndef _CRT_NONSTDC_NO_DEPRECATE
+#define _CRT_NONSTDC_NO_DEPRECATE /* for strdup(), write(), etc. */
+#endif
+#ifndef _CRT_SECURE_NO_DEPRECATE
+#define _CRT_SECURE_NO_DEPRECATE /* for fopen(), getenv(), etc. */
#endif
+#endif /* _MSC_VER */
#ifdef _WIN32
/*
@@ -97,16 +100,24 @@
* Make sure to define this macro before including any Windows headers.
*/
# ifndef WIN32_LEAN_AND_MEAN
-# define WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
# endif
# ifndef NOGDI
-# define NOGDI
+# define NOGDI
# endif
#endif
+/* Avoid bogus format check warnings with mingw32ce gcc 4.4.0 in
+ C99 (-std=gnu99) mode */
+#if defined(__MINGW32CE__) && !defined(CURL_NO_FMT_CHECKS) && \
+ (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) && \
+ (defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ == 4))
+#define CURL_NO_FMT_CHECKS
+#endif
+
/* Compatibility */
-#if defined(ENABLE_IPV6)
-# define USE_IPV6 1
+#ifdef ENABLE_IPV6
+#define USE_IPV6 1
#endif
/*
@@ -120,12 +131,8 @@
#else /* HAVE_CONFIG_H */
-#ifdef _WIN32_WCE
-# include "config-win32ce.h"
-#else
-# ifdef _WIN32
-# include "config-win32.h"
-# endif
+#ifdef _WIN32
+# include "config-win32.h"
#endif
#ifdef macintosh
@@ -136,10 +143,6 @@
# include "config-riscos.h"
#endif
-#ifdef __AMIGA__
-# include "config-amigaos.h"
-#endif
-
#ifdef __OS400__
# include "config-os400.h"
#endif
@@ -148,10 +151,6 @@
# include "config-plan9.h"
#endif
-#ifdef MSDOS
-# include "config-dos.h"
-#endif
-
#endif /* HAVE_CONFIG_H */
/* ================================================================ */
@@ -163,6 +162,12 @@
/* system header files in our config files, avoid this at any cost. */
/* ================================================================ */
+#ifdef HAVE_LIBZ
+# ifndef ZLIB_CONST
+# define ZLIB_CONST /* Use z_const. Supported by v1.2.5.2 and upper. */
+# endif
+#endif
+
/*
* AIX 4.3 and newer needs _THREAD_SAFE defined to build
* proper reentrant code. Others may also need it.
@@ -170,7 +175,7 @@
#ifdef NEED_THREAD_SAFE
# ifndef _THREAD_SAFE
-# define _THREAD_SAFE
+# define _THREAD_SAFE
# endif
#endif
@@ -182,14 +187,14 @@
#ifdef NEED_REENTRANT
# ifndef _REENTRANT
-# define _REENTRANT
+# define _REENTRANT
# endif
#endif
/* Solaris needs this to get a POSIX-conformant getpwuid_r */
#if defined(sun) || defined(__sun)
# ifndef _POSIX_PTHREAD_SEMANTICS
-# define _POSIX_PTHREAD_SEMANTICS 1
+# define _POSIX_PTHREAD_SEMANTICS 1
# endif
#endif
@@ -264,7 +269,7 @@
* When HTTP is disabled, disable HTTP-only features
*/
-#if defined(CURL_DISABLE_HTTP)
+#ifdef CURL_DISABLE_HTTP
# define CURL_DISABLE_ALTSVC 1
# define CURL_DISABLE_COOKIES 1
# define CURL_DISABLE_BASIC_AUTH 1
@@ -372,17 +377,23 @@
# endif
#endif
+#ifdef USE_ARES
+# ifndef CARES_NO_DEPRECATED
+# define CARES_NO_DEPRECATED /* for ares_getsock() */
+# endif
+# if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && defined(_WIN32)
+# define CARES_STATICLIB /* define it before including ares.h */
+# endif
+#endif
+
#ifdef USE_LWIPSOCK
# include <lwip/init.h>
# include <lwip/sockets.h>
# include <lwip/netdb.h>
#endif
-#ifdef HAVE_EXTRA_STRICMP_H
+#ifdef macintosh
# include <extra/stricmp.h>
-#endif
-
-#ifdef HAVE_EXTRA_STRDUP_H
# include <extra/strdup.h>
#endif
@@ -431,13 +442,18 @@
# define __NO_NET_API
#endif
+/* Whether to use eventfd() */
+#if defined(HAVE_EVENTFD) && defined(HAVE_SYS_EVENTFD_H)
+#define USE_EVENTFD
+#endif
+
#include <stdio.h>
#include <assert.h>
#ifdef __TANDEM /* for ns*-tandem-nsk systems */
-# if ! defined __LP64
-# include <floss.h> /* FLOSS is only used for 32-bit builds. */
-# endif
+# if ! defined __LP64
+# include <floss.h> /* FLOSS is only used for 32-bit builds. */
+# endif
#endif
#ifndef STDC_HEADERS /* no standard C headers! */
@@ -449,7 +465,9 @@
*/
#ifdef USE_WIN32_LARGE_FILES
+# ifdef HAVE_IO_H
# include <io.h>
+# endif
# include <sys/types.h>
# include <sys/stat.h>
# undef lseek
@@ -467,15 +485,25 @@
FILE *curlx_win32_fopen(const char *filename, const char *mode);
#endif
+#ifdef __DJGPP__
+/* Requires DJGPP 2.04 */
+# include <unistd.h>
+# undef lseek
+# define lseek(fdes,offset,whence) llseek(fdes, offset, whence)
+# define LSEEK_ERROR (offset_t)-1
+#endif
+
/*
* Small file (<2Gb) support using Win32 functions.
*/
-#ifdef USE_WIN32_SMALL_FILES
+#if defined(_WIN32) && !defined(USE_WIN32_LARGE_FILES)
+# ifdef HAVE_IO_H
# include <io.h>
+# endif
# include <sys/types.h>
# include <sys/stat.h>
-# ifndef _WIN32_WCE
+# ifndef UNDER_CE
# undef lseek
# define lseek(fdes,offset,whence) _lseek(fdes, (long)offset, whence)
# define fstat(fdes,stp) _fstat(fdes, stp)
@@ -491,11 +519,11 @@
#endif
#ifndef struct_stat
-# define struct_stat struct stat
+#define struct_stat struct stat
#endif
#ifndef LSEEK_ERROR
-# define LSEEK_ERROR (off_t)-1
+#define LSEEK_ERROR (off_t)-1
#endif
#ifndef SIZEOF_TIME_T
@@ -566,8 +594,8 @@
# endif
# define CURL_UINT64_SUFFIX CURL_SUFFIX_CURL_OFF_TU
# define CURL_UINT64_C(val) CURL_CONC_MACROS(val,CURL_UINT64_SUFFIX)
-# define FMT_PRId64 CURL_FORMAT_CURL_OFF_T
-# define FMT_PRIu64 CURL_FORMAT_CURL_OFF_TU
+# define FMT_PRId64 CURL_FORMAT_CURL_OFF_T
+# define FMT_PRIu64 CURL_FORMAT_CURL_OFF_TU
#endif
#define FMT_OFF_T CURL_FORMAT_CURL_OFF_T
@@ -650,7 +678,6 @@
# ifdef __minix
/* Minix 3 versions up to at least 3.1.3 are missing these prototypes */
- extern char *strtok_r(char *s, const char *delim, char **last);
extern struct tm *gmtime_r(const time_t * const timep, struct tm *tmp);
# endif
@@ -664,16 +691,6 @@
/* ---------------------------------------------------------------- */
/*
- * MSVC threads support requires a multi-threaded runtime library.
- * _beginthreadex() is not available in single-threaded ones.
- */
-
-#if defined(_MSC_VER) && !defined(_MT)
-# undef USE_THREADS_POSIX
-# undef USE_THREADS_WIN32
-#endif
-
-/*
* Mutually exclusive CURLRES_* definitions.
*/
@@ -686,15 +703,15 @@
# define CURLRES_IPV4
#endif
-#ifdef USE_ARES
+#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
+# define CURLRES_ASYNCH
+# define CURLRES_THREADED
+#elif defined(USE_ARES)
# define CURLRES_ASYNCH
# define CURLRES_ARES
/* now undef the stock libc functions just to avoid them being used */
# undef HAVE_GETADDRINFO
# undef HAVE_FREEADDRINFO
-#elif defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
-# define CURLRES_ASYNCH
-# define CURLRES_THREADED
#else
# define CURLRES_SYNCH
#endif
@@ -711,14 +728,23 @@
#error "libidn2 cannot be enabled with WinIDN or AppleIDN, choose one."
#endif
-#define LIBIDN_REQUIRED_VERSION "0.4.1"
-
#if defined(USE_GNUTLS) || defined(USE_OPENSSL) || defined(USE_MBEDTLS) || \
defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \
defined(USE_BEARSSL) || defined(USE_RUSTLS)
#define USE_SSL /* SSL support has been enabled */
#endif
+#if defined(USE_OPENSSL) && defined(USE_WOLFSSL)
+# include <wolfssl/version.h>
+# if LIBWOLFSSL_VERSION_HEX >= 0x05007006
+# ifndef OPENSSL_COEXIST
+# define OPENSSL_COEXIST
+# endif
+# else
+# error "OpenSSL can only coexist with wolfSSL v5.7.6 or upper"
+# endif
+#endif
+
#if defined(USE_WOLFSSL) && defined(USE_GNUTLS)
/* Avoid defining unprefixed wolfSSL SHA macros colliding with nettle ones */
#define NO_OLD_WC_NAMES
@@ -737,7 +763,7 @@
#endif
/* Single point where USE_NTLM definition might be defined */
-#if !defined(CURL_DISABLE_NTLM)
+#ifndef CURL_DISABLE_NTLM
# if defined(USE_OPENSSL) || defined(USE_MBEDTLS) || \
defined(USE_GNUTLS) || defined(USE_SECTRANSP) || \
defined(USE_OS400CRYPTO) || defined(USE_WIN32_CRYPTO) || \
@@ -749,10 +775,6 @@
# endif
#endif
-#ifdef CURL_WANTS_CA_BUNDLE_ENV
-#error "No longer supported. Set CURLOPT_CAINFO at runtime instead."
-#endif
-
#if defined(USE_LIBSSH2) || defined(USE_LIBSSH) || defined(USE_WOLFSSH)
#define USE_SSH
#endif
@@ -781,11 +803,11 @@
/* noreturn attribute */
-#if !defined(CURL_NORETURN)
+#ifndef CURL_NORETURN
#if (defined(__GNUC__) && (__GNUC__ >= 3)) || defined(__clang__) || \
defined(__IAR_SYSTEMS_ICC__)
# define CURL_NORETURN __attribute__((__noreturn__))
-#elif defined(_MSC_VER) && (_MSC_VER >= 1200)
+#elif defined(_MSC_VER)
# define CURL_NORETURN __declspec(noreturn)
#else
# define CURL_NORETURN
@@ -794,7 +816,7 @@
/* fallthrough attribute */
-#if !defined(FALLTHROUGH)
+#ifndef FALLTHROUGH
#if (defined(__GNUC__) && __GNUC__ >= 7) || \
(defined(__clang__) && __clang_major__ >= 10)
# define FALLTHROUGH() __attribute__((fallthrough))
@@ -811,12 +833,33 @@
#include "curl_setup_once.h"
#endif
+#ifdef UNDER_CE
+#define getenv curl_getenv /* Windows CE does not support getenv() */
+#define raise(s) ((void)(s))
+/* Terrible workarounds to make Windows CE compile */
+#define errno 0
+#define CURL_SETERRNO(x) ((void)(x))
+#define EINTR 4
+#define EAGAIN 11
+#define ENOMEM 12
+#define EACCES 13
+#define EEXIST 17
+#define EISDIR 21
+#define EINVAL 22
+#define ENOSPC 28
+#define strerror(x) "?"
+#undef STDIN_FILENO
+#define STDIN_FILENO 0
+#else
+#define CURL_SETERRNO(x) (errno = (x))
+#endif
+
/*
* Definition of our NOP statement Object-like macro
*/
#ifndef Curl_nop_stmt
-# define Curl_nop_stmt do { } while(0)
+#define Curl_nop_stmt do { } while(0)
#endif
/*
@@ -855,6 +898,25 @@
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
#endif
+/* For MSVC (all versions as of VS2022) */
+#ifndef STDIN_FILENO
+#define STDIN_FILENO fileno(stdin)
+#endif
+#ifndef STDOUT_FILENO
+#define STDOUT_FILENO fileno(stdout)
+#endif
+#ifndef STDERR_FILENO
+#define STDERR_FILENO fileno(stderr)
+#endif
+
+/* Since O_BINARY is used in bitmasks, setting it to zero makes it usable in
+ source code but yet it does not ruin anything */
+#ifdef O_BINARY
+#define CURL_O_BINARY O_BINARY
+#else
+#define CURL_O_BINARY 0
+#endif
+
/* In Windows the default file mode is text but an application can override it.
Therefore we specify it explicitly. https://github.com/curl/curl/pull/258
*/
@@ -884,6 +946,8 @@ endings either CRLF or LF so 't' is appropriate.
# define CURL_SA_FAMILY_T sa_family_t
# elif defined(HAVE_ADDRESS_FAMILY)
# define CURL_SA_FAMILY_T ADDRESS_FAMILY
+# elif defined(__AMIGA__)
+# define CURL_SA_FAMILY_T unsigned char
# else
/* use a sensible default */
# define CURL_SA_FAMILY_T unsigned short
@@ -900,8 +964,21 @@ endings either CRLF or LF so 't' is appropriate.
as their argument */
#define STRCONST(x) x,sizeof(x)-1
-/* Some versions of the Android SDK is missing the declaration */
-#if defined(HAVE_GETPWUID_R) && defined(HAVE_DECL_GETPWUID_R_MISSING)
+#define CURL_ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+
+#ifdef CURLDEBUG
+#define CURL_GETADDRINFO(host,serv,hint,res) \
+ curl_dbg_getaddrinfo(host, serv, hint, res, __LINE__, __FILE__)
+#define CURL_FREEADDRINFO(data) \
+ curl_dbg_freeaddrinfo(data, __LINE__, __FILE__)
+#else
+#define CURL_GETADDRINFO getaddrinfo
+#define CURL_FREEADDRINFO freeaddrinfo
+#endif
+
+/* Some versions of the Android NDK is missing the declaration */
+#if defined(HAVE_GETPWUID_R) && \
+ defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
struct passwd;
int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
size_t buflen, struct passwd **result);
@@ -913,8 +990,7 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
#define UNITTEST static
#endif
-/* Hyper supports HTTP2 also, but Curl's integration with Hyper does not */
-#if defined(USE_NGHTTP2)
+#ifdef USE_NGHTTP2
#define USE_HTTP2
#endif
@@ -923,7 +999,7 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
defined(USE_QUICHE) || defined(USE_MSH3)
#ifdef CURL_WITH_MULTI_SSL
-#error "Multi-SSL combined with QUIC is not supported"
+#error "MultiSSL combined with QUIC is not supported"
#endif
#define USE_HTTP3
@@ -943,39 +1019,45 @@ int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf,
# define UNIX_PATH_MAX 108
/* !checksrc! disable TYPEDEFSTRUCT 1 */
typedef struct sockaddr_un {
- ADDRESS_FAMILY sun_family;
+ CURL_SA_FAMILY_T sun_family;
char sun_path[UNIX_PATH_MAX];
} SOCKADDR_UN, *PSOCKADDR_UN;
# define WIN32_SOCKADDR_UN
# endif
#endif
+#ifdef USE_OPENSSL
/* OpenSSLv3 marks DES, MD5 and ENGINE functions deprecated but we have no
replacements (yet) so tell the compiler to not warn for them. */
-#ifdef USE_OPENSSL
-#define OPENSSL_SUPPRESS_DEPRECATED
+# define OPENSSL_SUPPRESS_DEPRECATED
+# ifdef _WIN32
+/* Silence LibreSSL warnings about wincrypt.h collision. Works in 3.8.2+ */
+# ifndef LIBRESSL_DISABLE_OVERRIDE_WINCRYPT_DEFINES_WARNING
+# define LIBRESSL_DISABLE_OVERRIDE_WINCRYPT_DEFINES_WARNING
+# endif
+# endif
#endif
-#if defined(inline)
- /* 'inline' is defined as macro and assumed to be correct */
- /* No need for 'inline' replacement */
+#ifdef CURL_INLINE
+/* 'CURL_INLINE' defined, use as-is */
+#elif defined(inline)
+# define CURL_INLINE inline /* 'inline' defined, assumed correct */
#elif defined(__cplusplus)
- /* The code is compiled with C++ compiler.
- C++ always supports 'inline'. */
- /* No need for 'inline' replacement */
+/* The code is compiled with C++ compiler.
+ C++ always supports 'inline'. */
+# define CURL_INLINE inline /* 'inline' keyword supported */
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901
- /* C99 (and later) supports 'inline' keyword */
- /* No need for 'inline' replacement */
+/* C99 (and later) supports 'inline' keyword */
+# define CURL_INLINE inline /* 'inline' keyword supported */
#elif defined(__GNUC__) && __GNUC__ >= 3
- /* GCC supports '__inline__' as an extension */
-# define inline __inline__
-#elif defined(_MSC_VER) && _MSC_VER >= 1400
- /* MSC supports '__inline' from VS 2005 (or even earlier) */
-# define inline __inline
+/* GCC supports '__inline__' as an extension */
+# define CURL_INLINE __inline__
+#elif defined(_MSC_VER)
+# define CURL_INLINE __inline
#else
- /* Probably 'inline' is not supported by compiler.
- Define to the empty string to be on the safe side. */
-# define inline /* empty */
+/* Probably 'inline' is not supported by compiler.
+ Define to the empty string to be on the safe side. */
+# define CURL_INLINE /* empty */
#endif
#endif /* HEADER_CURL_SETUP_H */
diff --git a/libs/libcurl/src/curl_setup_once.h b/libs/libcurl/src/curl_setup_once.h
index 2c580fc444..89ee951f9d 100644
--- a/libs/libcurl/src/curl_setup_once.h
+++ b/libs/libcurl/src/curl_setup_once.h
@@ -24,7 +24,6 @@
*
***************************************************************************/
-
/*
* Inclusion of common header files.
*/
@@ -34,20 +33,14 @@
#include <string.h>
#include <stdarg.h>
#include <time.h>
+#ifndef UNDER_CE
#include <errno.h>
+#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
-#ifdef NEED_MALLOC_H
-#include <malloc.h>
-#endif
-
-#ifdef NEED_MEMORY_H
-#include <memory.h>
-#endif
-
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
@@ -56,8 +49,11 @@
#include <sys/time.h>
#endif
-#ifdef _WIN32
+#ifdef HAVE_IO_H
#include <io.h>
+#endif
+
+#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
@@ -69,10 +65,20 @@
#include <unistd.h>
#endif
-#ifdef USE_WOLFSSL
+#if defined(HAVE_STDINT_H) || defined(USE_WOLFSSL)
#include <stdint.h>
#endif
+/* Macro to strip 'const' without triggering a compiler warning.
+ Use it for APIs that do not or cannot support the const qualifier. */
+#ifdef HAVE_STDINT_H
+# define CURL_UNCONST(p) ((void *)(uintptr_t)(const void *)(p))
+#elif defined(_WIN32) /* for VS2008 */
+# define CURL_UNCONST(p) ((void *)(ULONG_PTR)(const void *)(p))
+#else
+# define CURL_UNCONST(p) ((void *)(p)) /* Fall back to simple cast */
+#endif
+
#ifdef USE_SCHANNEL
/* Must set this before <schannel.h> is included directly or indirectly by
another Windows header. */
@@ -111,8 +117,8 @@
#ifndef HAVE_STRUCT_TIMEVAL
struct timeval {
- long tv_sec;
- long tv_usec;
+ long tv_sec;
+ long tv_usec;
};
#endif
@@ -129,7 +135,7 @@ struct timeval {
#endif
-#if defined(__minix)
+#ifdef __minix
/* Minix does not support recv on TCP sockets */
#define sread(x,y,z) (ssize_t)read((RECV_TYPE_ARG1)(x), \
(RECV_TYPE_ARG2)(y), \
@@ -169,15 +175,14 @@ struct timeval {
#endif /* HAVE_RECV */
-#if defined(__minix)
+#ifdef __minix
/* Minix does not support send on TCP sockets */
#define swrite(x,y,z) (ssize_t)write((SEND_TYPE_ARG1)(x), \
- (SEND_TYPE_ARG2)(y), \
- (SEND_TYPE_ARG3)(z))
-
+ (SEND_TYPE_ARG2)CURL_UNCONST(y), \
+ (SEND_TYPE_ARG3)(z))
#elif defined(HAVE_SEND)
#define swrite(x,y,z) (ssize_t)send((SEND_TYPE_ARG1)(x), \
- (SEND_QUAL_ARG2 SEND_TYPE_ARG2)(y), \
+ (SEND_QUAL_ARG2 SEND_TYPE_ARG2)CURL_UNCONST(y), \
(SEND_TYPE_ARG3)(z), \
(SEND_TYPE_ARG4)(SEND_4TH_ARG))
#else /* HAVE_SEND */
@@ -191,11 +196,11 @@ struct timeval {
* Function-like macro definition used to close a socket.
*/
-#if defined(HAVE_CLOSESOCKET)
+#ifdef HAVE_CLOSESOCKET
# define sclose(x) closesocket((x))
#elif defined(HAVE_CLOSESOCKET_CAMEL)
# define sclose(x) CloseSocket((x))
-#elif defined(HAVE_CLOSE_S)
+#elif defined(MSDOS) /* Watt-32 */
# define sclose(x) close_s((x))
#elif defined(USE_LWIPSOCK)
# define sclose(x) lwip_close((x))
@@ -206,7 +211,7 @@ struct timeval {
/*
* Stack-independent version of fcntl() on sockets:
*/
-#if defined(USE_LWIPSOCK)
+#ifdef USE_LWIPSOCK
# define sfcntl lwip_fcntl
#else
# define sfcntl fcntl
@@ -233,8 +238,8 @@ struct timeval {
#ifndef HAVE_BOOL_T
typedef enum {
- bool_false = 0,
- bool_true = 1
+ bool_false = 0,
+ bool_true = 1
} bool;
/*
@@ -290,7 +295,7 @@ typedef unsigned int bit;
*/
#undef DEBUGASSERT
-#if defined(DEBUGBUILD)
+#ifdef DEBUGBUILD
#define DEBUGASSERT(x) assert(x)
#else
#define DEBUGASSERT(x) do { } while(0)
@@ -316,78 +321,39 @@ typedef unsigned int bit;
*/
#ifdef USE_WINSOCK
-#undef EBADF /* override definition in errno.h */
-#define EBADF WSAEBADF
-#undef EINTR /* override definition in errno.h */
-#define EINTR WSAEINTR
-#undef EINVAL /* override definition in errno.h */
-#define EINVAL WSAEINVAL
-#undef EWOULDBLOCK /* override definition in errno.h */
-#define EWOULDBLOCK WSAEWOULDBLOCK
-#undef EINPROGRESS /* override definition in errno.h */
-#define EINPROGRESS WSAEINPROGRESS
-#undef EALREADY /* override definition in errno.h */
-#define EALREADY WSAEALREADY
-#undef ENOTSOCK /* override definition in errno.h */
-#define ENOTSOCK WSAENOTSOCK
-#undef EDESTADDRREQ /* override definition in errno.h */
-#define EDESTADDRREQ WSAEDESTADDRREQ
-#undef EMSGSIZE /* override definition in errno.h */
-#define EMSGSIZE WSAEMSGSIZE
-#undef EPROTOTYPE /* override definition in errno.h */
-#define EPROTOTYPE WSAEPROTOTYPE
-#undef ENOPROTOOPT /* override definition in errno.h */
-#define ENOPROTOOPT WSAENOPROTOOPT
-#undef EPROTONOSUPPORT /* override definition in errno.h */
-#define EPROTONOSUPPORT WSAEPROTONOSUPPORT
-#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT
-#undef EOPNOTSUPP /* override definition in errno.h */
-#define EOPNOTSUPP WSAEOPNOTSUPP
-#define EPFNOSUPPORT WSAEPFNOSUPPORT
-#undef EAFNOSUPPORT /* override definition in errno.h */
-#define EAFNOSUPPORT WSAEAFNOSUPPORT
-#undef EADDRINUSE /* override definition in errno.h */
-#define EADDRINUSE WSAEADDRINUSE
-#undef EADDRNOTAVAIL /* override definition in errno.h */
-#define EADDRNOTAVAIL WSAEADDRNOTAVAIL
-#undef ENETDOWN /* override definition in errno.h */
-#define ENETDOWN WSAENETDOWN
-#undef ENETUNREACH /* override definition in errno.h */
-#define ENETUNREACH WSAENETUNREACH
-#undef ENETRESET /* override definition in errno.h */
-#define ENETRESET WSAENETRESET
-#undef ECONNABORTED /* override definition in errno.h */
-#define ECONNABORTED WSAECONNABORTED
-#undef ECONNRESET /* override definition in errno.h */
-#define ECONNRESET WSAECONNRESET
-#undef ENOBUFS /* override definition in errno.h */
-#define ENOBUFS WSAENOBUFS
-#undef EISCONN /* override definition in errno.h */
-#define EISCONN WSAEISCONN
-#undef ENOTCONN /* override definition in errno.h */
-#define ENOTCONN WSAENOTCONN
-#define ESHUTDOWN WSAESHUTDOWN
-#define ETOOMANYREFS WSAETOOMANYREFS
-#undef ETIMEDOUT /* override definition in errno.h */
-#define ETIMEDOUT WSAETIMEDOUT
-#undef ECONNREFUSED /* override definition in errno.h */
-#define ECONNREFUSED WSAECONNREFUSED
-#undef ELOOP /* override definition in errno.h */
-#define ELOOP WSAELOOP
-#ifndef ENAMETOOLONG /* possible previous definition in errno.h */
-#define ENAMETOOLONG WSAENAMETOOLONG
-#endif
-#define EHOSTDOWN WSAEHOSTDOWN
-#undef EHOSTUNREACH /* override definition in errno.h */
-#define EHOSTUNREACH WSAEHOSTUNREACH
-#ifndef ENOTEMPTY /* possible previous definition in errno.h */
-#define ENOTEMPTY WSAENOTEMPTY
+#define SOCKEACCES WSAEACCES
+#define SOCKEADDRINUSE WSAEADDRINUSE
+#define SOCKEADDRNOTAVAIL WSAEADDRNOTAVAIL
+#define SOCKEAFNOSUPPORT WSAEAFNOSUPPORT
+#define SOCKEBADF WSAEBADF
+#define SOCKECONNREFUSED WSAECONNREFUSED
+#define SOCKECONNRESET WSAECONNRESET
+#define SOCKEINPROGRESS WSAEINPROGRESS
+#define SOCKEINTR WSAEINTR
+#define SOCKEINVAL WSAEINVAL
+#define SOCKEISCONN WSAEISCONN
+#define SOCKEMSGSIZE WSAEMSGSIZE
+#define SOCKENOMEM WSA_NOT_ENOUGH_MEMORY
+#define SOCKETIMEDOUT WSAETIMEDOUT
+#define SOCKEWOULDBLOCK WSAEWOULDBLOCK
+#else
+#define SOCKEACCES EACCES
+#define SOCKEADDRINUSE EADDRINUSE
+#define SOCKEADDRNOTAVAIL EADDRNOTAVAIL
+#define SOCKEAFNOSUPPORT EAFNOSUPPORT
+#define SOCKEBADF EBADF
+#define SOCKECONNREFUSED ECONNREFUSED
+#define SOCKECONNRESET ECONNRESET
+#define SOCKEINPROGRESS EINPROGRESS
+#define SOCKEINTR EINTR
+#define SOCKEINVAL EINVAL
+#define SOCKEISCONN EISCONN
+#define SOCKEMSGSIZE EMSGSIZE
+#define SOCKENOMEM ENOMEM
+#ifdef ETIMEDOUT
+#define SOCKETIMEDOUT ETIMEDOUT
#endif
-#define EPROCLIM WSAEPROCLIM
-#define EUSERS WSAEUSERS
-#define EDQUOT WSAEDQUOT
-#define ESTALE WSAESTALE
-#define EREMOTE WSAEREMOTE
+#define SOCKEWOULDBLOCK EWOULDBLOCK
#endif
/*
@@ -396,8 +362,8 @@ typedef unsigned int bit;
#ifdef __VMS
#define argv_item_t __char_ptr32
-#elif defined(_UNICODE)
-#define argv_item_t wchar_t *
+#elif defined(_UNICODE) && !defined(UNDER_CE)
+#define argv_item_t wchar_t *
#else
#define argv_item_t char *
#endif
diff --git a/libs/libcurl/src/curl_sha256.h b/libs/libcurl/src/curl_sha256.h
index 8e93245f39..2ab36fcd5e 100644
--- a/libs/libcurl/src/curl_sha256.h
+++ b/libs/libcurl/src/curl_sha256.h
@@ -26,7 +26,7 @@
***************************************************************************/
#if !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) \
- || defined(USE_LIBSSH2)
+ || defined(USE_LIBSSH2) || defined(USE_SSL)
#include <curl/curl.h>
#include "curl_hmac.h"
diff --git a/libs/libcurl/src/curl_sha512_256.c b/libs/libcurl/src/curl_sha512_256.c
index 4333cfb146..f68c9d7f4c 100644
--- a/libs/libcurl/src/curl_sha512_256.c
+++ b/libs/libcurl/src/curl_sha512_256.c
@@ -27,7 +27,7 @@
#if !defined(CURL_DISABLE_DIGEST_AUTH) && !defined(CURL_DISABLE_SHA512_256)
#include "curl_sha512_256.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
/* The recommended order of the TLS backends:
* * OpenSSL
@@ -82,7 +82,6 @@
# include <nettle/sha.h>
# if defined(SHA512_256_DIGEST_SIZE)
# define USE_GNUTLS_SHA512_256 1
-# define HAS_SHA512_256_IMPLEMENTATION 1
# endif
#endif /* ! HAS_SHA512_256_IMPLEMENTATION && USE_GNUTLS */
@@ -269,9 +268,6 @@ Curl_sha512_256_finish(unsigned char *digest,
#else /* No system or TLS backend SHA-512/256 implementation available */
-/* Use local implementation */
-#define HAS_SHA512_256_IMPLEMENTATION 1
-
/* ** This implementation of SHA-512/256 hash calculation was originally ** *
* ** written by Evgeny Grin (Karlson2k) for GNU libmicrohttpd. ** *
* ** The author ported the code to libcurl. The ported code is provided ** *
@@ -283,28 +279,26 @@ Curl_sha512_256_finish(unsigned char *digest,
#ifdef __GNUC__
# if defined(__has_attribute) && defined(__STDC_VERSION__)
# if __has_attribute(always_inline) && __STDC_VERSION__ >= 199901
-# define MHDX_INLINE inline __attribute__((always_inline))
+# define CURL_FORCEINLINE CURL_INLINE __attribute__((always_inline))
# endif
# endif
#endif
-#if !defined(MHDX_INLINE) && \
+#if !defined(CURL_FORCEINLINE) && \
defined(_MSC_VER) && !defined(__GNUC__) && !defined(__clang__)
-# if _MSC_VER >= 1400
-# define MHDX_INLINE __forceinline
-# endif
+# define CURL_FORCEINLINE __forceinline
#endif
-#if !defined(MHDX_INLINE)
- /* Assume that 'inline' keyword works or the
+#if !defined(CURL_FORCEINLINE)
+ /* Assume that 'CURL_INLINE' keyword works or the
* macro was already defined correctly. */
-# define MHDX_INLINE inline
+# define CURL_FORCEINLINE CURL_INLINE
#endif
/* Bits manipulation macros and functions.
Can be moved to other headers to reuse. */
-#define MHDX_GET_64BIT_BE(ptr) \
+#define CURL_GET_64BIT_BE(ptr) \
( ((curl_uint64_t)(((const unsigned char*)(ptr))[0]) << 56) | \
((curl_uint64_t)(((const unsigned char*)(ptr))[1]) << 48) | \
((curl_uint64_t)(((const unsigned char*)(ptr))[2]) << 40) | \
@@ -314,7 +308,7 @@ Curl_sha512_256_finish(unsigned char *digest,
((curl_uint64_t)(((const unsigned char*)(ptr))[6]) << 8) | \
(curl_uint64_t)(((const unsigned char*)(ptr))[7]) )
-#define MHDX_PUT_64BIT_BE(ptr,val) do { \
+#define CURL_PUT_64BIT_BE(ptr,val) do { \
((unsigned char*)(ptr))[7]=(unsigned char)((curl_uint64_t)(val)); \
((unsigned char*)(ptr))[6]=(unsigned char)(((curl_uint64_t)(val)) >> 8); \
((unsigned char*)(ptr))[5]=(unsigned char)(((curl_uint64_t)(val)) >> 16); \
@@ -328,8 +322,8 @@ Curl_sha512_256_finish(unsigned char *digest,
/* Defined as a function. The macro version may duplicate the binary code
* size as each argument is used twice, so if any calculation is used
* as an argument, the calculation could be done twice. */
-static MHDX_INLINE curl_uint64_t
-MHDx_rotr64(curl_uint64_t value, unsigned int bits)
+static CURL_FORCEINLINE curl_uint64_t
+Curl_rotr64(curl_uint64_t value, unsigned int bits)
{
bits %= 64;
if(0 == bits)
@@ -388,7 +382,7 @@ MHDx_rotr64(curl_uint64_t value, unsigned int bits)
/**
* SHA-512/256 calculation context
*/
-struct mhdx_sha512_256ctx
+struct Curl_sha512_256ctx
{
/**
* Intermediate hash value. The variable is properly aligned. Smart
@@ -416,7 +410,7 @@ struct mhdx_sha512_256ctx
/**
* Context type used for SHA-512/256 calculations
*/
-typedef struct mhdx_sha512_256ctx Curl_sha512_256_ctx;
+typedef struct Curl_sha512_256ctx Curl_sha512_256_ctx;
/**
@@ -426,9 +420,9 @@ typedef struct mhdx_sha512_256ctx Curl_sha512_256_ctx;
* @return always CURLE_OK
*/
static CURLcode
-MHDx_sha512_256_init(void *context)
+Curl_sha512_256_init(void *context)
{
- struct mhdx_sha512_256ctx *const ctx = (struct mhdx_sha512_256ctx *) context;
+ struct Curl_sha512_256ctx *const ctx = (struct Curl_sha512_256ctx *)context;
/* Check whether the header and this file use the same numbers */
DEBUGASSERT(CURL_SHA512_256_DIGEST_LENGTH == CURL_SHA512_256_DIGEST_SIZE);
@@ -462,7 +456,7 @@ MHDx_sha512_256_init(void *context)
* @param data the data buffer with #CURL_SHA512_256_BLOCK_SIZE bytes block
*/
static void
-MHDx_sha512_256_transform(curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS],
+Curl_sha512_256_transform(curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS],
const void *data)
{
/* Working variables,
@@ -488,13 +482,13 @@ MHDx_sha512_256_transform(curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS],
/* Four 'Sigma' macro functions.
See FIPS PUB 180-4 formulae 4.10, 4.11, 4.12, 4.13. */
#define SIG0(x) \
- ( MHDx_rotr64((x), 28) ^ MHDx_rotr64((x), 34) ^ MHDx_rotr64((x), 39) )
+ ( Curl_rotr64((x), 28) ^ Curl_rotr64((x), 34) ^ Curl_rotr64((x), 39) )
#define SIG1(x) \
- ( MHDx_rotr64((x), 14) ^ MHDx_rotr64((x), 18) ^ MHDx_rotr64((x), 41) )
+ ( Curl_rotr64((x), 14) ^ Curl_rotr64((x), 18) ^ Curl_rotr64((x), 41) )
#define sig0(x) \
- ( MHDx_rotr64((x), 1) ^ MHDx_rotr64((x), 8) ^ ((x) >> 7) )
+ ( Curl_rotr64((x), 1) ^ Curl_rotr64((x), 8) ^ ((x) >> 7) )
#define sig1(x) \
- ( MHDx_rotr64((x), 19) ^ MHDx_rotr64((x), 61) ^ ((x) >> 6) )
+ ( Curl_rotr64((x), 19) ^ Curl_rotr64((x), 61) ^ ((x) >> 6) )
if(1) {
unsigned int t;
@@ -577,7 +571,7 @@ MHDx_sha512_256_transform(curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS],
Input data must be read in big-endian bytes order,
see FIPS PUB 180-4 section 3.1.2. */
#define SHA512_GET_W_FROM_DATA(buf,t) \
- MHDX_GET_64BIT_BE( \
+ CURL_GET_64BIT_BE( \
((const unsigned char*) (buf)) + (t) * SHA512_256_BYTES_IN_WORD)
/* During first 16 steps, before making any calculation on each step, the
@@ -628,12 +622,12 @@ MHDx_sha512_256_transform(curl_uint64_t H[SHA512_256_HASH_SIZE_WORDS],
* @return always CURLE_OK
*/
static CURLcode
-MHDx_sha512_256_update(void *context,
+Curl_sha512_256_update(void *context,
const unsigned char *data,
size_t length)
{
unsigned int bytes_have; /**< Number of bytes in the context buffer */
- struct mhdx_sha512_256ctx *const ctx = (struct mhdx_sha512_256ctx *)context;
+ struct Curl_sha512_256ctx *const ctx = (struct Curl_sha512_256ctx *)context;
/* the void pointer here is required to mute Intel compiler warning */
void *const ctx_buf = ctx->buffer;
@@ -661,7 +655,7 @@ MHDx_sha512_256_update(void *context,
bytes_left);
data += bytes_left;
length -= bytes_left;
- MHDx_sha512_256_transform(ctx->H, ctx->buffer);
+ Curl_sha512_256_transform(ctx->H, ctx->buffer);
bytes_have = 0;
}
}
@@ -669,7 +663,7 @@ MHDx_sha512_256_update(void *context,
while(CURL_SHA512_256_BLOCK_SIZE <= length) {
/* Process any full blocks of new data directly,
without copying to the buffer. */
- MHDx_sha512_256_transform(ctx->H, data);
+ Curl_sha512_256_transform(ctx->H, data);
data += CURL_SHA512_256_BLOCK_SIZE;
length -= CURL_SHA512_256_BLOCK_SIZE;
}
@@ -705,10 +699,10 @@ MHDx_sha512_256_update(void *context,
* @return always CURLE_OK
*/
static CURLcode
-MHDx_sha512_256_finish(unsigned char *digest,
+Curl_sha512_256_finish(unsigned char *digest,
void *context)
{
- struct mhdx_sha512_256ctx *const ctx = (struct mhdx_sha512_256ctx *)context;
+ struct Curl_sha512_256ctx *const ctx = (struct Curl_sha512_256ctx *)context;
curl_uint64_t num_bits; /**< Number of processed bits */
unsigned int bytes_have; /**< Number of bytes in the context buffer */
/* the void pointer here is required to mute Intel compiler warning */
@@ -742,7 +736,7 @@ MHDx_sha512_256_finish(unsigned char *digest,
memset(((unsigned char *) ctx_buf) + bytes_have, 0,
CURL_SHA512_256_BLOCK_SIZE - bytes_have);
/* Process the full block. */
- MHDx_sha512_256_transform(ctx->H, ctx->buffer);
+ Curl_sha512_256_transform(ctx->H, ctx->buffer);
/* Start the new block. */
bytes_have = 0;
}
@@ -754,37 +748,32 @@ MHDx_sha512_256_finish(unsigned char *digest,
part of number of bits as big-endian values.
See FIPS PUB 180-4 section 5.1.2. */
/* Note: the target location is predefined and buffer is always aligned */
- MHDX_PUT_64BIT_BE(((unsigned char *) ctx_buf) \
+ CURL_PUT_64BIT_BE(((unsigned char *) ctx_buf) \
+ CURL_SHA512_256_BLOCK_SIZE \
- SHA512_256_SIZE_OF_LEN_ADD, \
ctx->count_bits_hi);
- MHDX_PUT_64BIT_BE(((unsigned char *) ctx_buf) \
+ CURL_PUT_64BIT_BE(((unsigned char *) ctx_buf) \
+ CURL_SHA512_256_BLOCK_SIZE \
- SHA512_256_SIZE_OF_LEN_ADD \
+ SHA512_256_BYTES_IN_WORD, \
num_bits);
/* Process the full final block. */
- MHDx_sha512_256_transform(ctx->H, ctx->buffer);
+ Curl_sha512_256_transform(ctx->H, ctx->buffer);
/* Put in BE mode the leftmost part of the hash as the final digest.
See FIPS PUB 180-4 section 6.7. */
- MHDX_PUT_64BIT_BE((digest + 0 * SHA512_256_BYTES_IN_WORD), ctx->H[0]);
- MHDX_PUT_64BIT_BE((digest + 1 * SHA512_256_BYTES_IN_WORD), ctx->H[1]);
- MHDX_PUT_64BIT_BE((digest + 2 * SHA512_256_BYTES_IN_WORD), ctx->H[2]);
- MHDX_PUT_64BIT_BE((digest + 3 * SHA512_256_BYTES_IN_WORD), ctx->H[3]);
+ CURL_PUT_64BIT_BE((digest + 0 * SHA512_256_BYTES_IN_WORD), ctx->H[0]);
+ CURL_PUT_64BIT_BE((digest + 1 * SHA512_256_BYTES_IN_WORD), ctx->H[1]);
+ CURL_PUT_64BIT_BE((digest + 2 * SHA512_256_BYTES_IN_WORD), ctx->H[2]);
+ CURL_PUT_64BIT_BE((digest + 3 * SHA512_256_BYTES_IN_WORD), ctx->H[3]);
/* Erase potentially sensitive data. */
- memset(ctx, 0, sizeof(struct mhdx_sha512_256ctx));
+ memset(ctx, 0, sizeof(struct Curl_sha512_256ctx));
return CURLE_OK;
}
-/* Map to the local implementation */
-#define Curl_sha512_256_init MHDx_sha512_256_init
-#define Curl_sha512_256_update MHDx_sha512_256_update
-#define Curl_sha512_256_finish MHDx_sha512_256_finish
-
#endif /* Local SHA-512/256 code */
diff --git a/libs/libcurl/src/curl_sspi.c b/libs/libcurl/src/curl_sspi.c
index a08957c7cc..3bfd85e00d 100644
--- a/libs/libcurl/src/curl_sspi.c
+++ b/libs/libcurl/src/curl_sspi.c
@@ -28,10 +28,10 @@
#include <curl/curl.h>
#include "curl_sspi.h"
-#include "curl_multibyte.h"
+#include "curlx/multibyte.h"
#include "system_win32.h"
-#include "version_win32.h"
-#include "warnless.h"
+#include "curlx/version_win32.h"
+#include "curlx/warnless.h"
/* The last #include files should be: */
#include "curl_memory.h"
@@ -42,7 +42,7 @@ typedef PSecurityFunctionTable (APIENTRY *INITSECURITYINTERFACE_FN)(VOID);
/* See definition of SECURITY_ENTRYPOINT in sspi.h */
#ifdef UNICODE
-# ifdef _WIN32_WCE
+# ifdef UNDER_CE
# define SECURITYENTRYPOINT L"InitSecurityInterfaceW"
# else
# define SECURITYENTRYPOINT "InitSecurityInterfaceW"
@@ -129,7 +129,7 @@ void Curl_sspi_global_cleanup(void)
/*
* Curl_create_sspi_identity()
*
- * This is used to populate a SSPI identity structure based on the supplied
+ * This is used to populate an SSPI identity structure based on the supplied
* username and password.
*
* Parameters:
@@ -154,7 +154,7 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp,
/* Initialize the identity */
memset(identity, 0, sizeof(*identity));
- useranddomain.tchar_ptr = curlx_convert_UTF8_to_tchar((char *)userp);
+ useranddomain.tchar_ptr = curlx_convert_UTF8_to_tchar(userp);
if(!useranddomain.tchar_ptr)
return CURLE_OUT_OF_MEMORY;
@@ -198,7 +198,7 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp,
curlx_unicodefree(useranddomain.tchar_ptr);
/* Setup the identity's password and length */
- passwd.tchar_ptr = curlx_convert_UTF8_to_tchar((char *)passwdp);
+ passwd.tchar_ptr = curlx_convert_UTF8_to_tchar(passwdp);
if(!passwd.tchar_ptr)
return CURLE_OUT_OF_MEMORY;
dup_passwd.tchar_ptr = _tcsdup(passwd.tchar_ptr);
@@ -221,7 +221,7 @@ CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp,
/*
* Curl_sspi_free_identity()
*
- * This is used to free the contents of a SSPI identifier structure.
+ * This is used to free the contents of an SSPI identifier structure.
*
* Parameters:
*
diff --git a/libs/libcurl/src/curl_sspi.h b/libs/libcurl/src/curl_sspi.h
index 9c7a2d4c04..0a4c2c10e5 100644
--- a/libs/libcurl/src/curl_sspi.h
+++ b/libs/libcurl/src/curl_sspi.h
@@ -45,7 +45,7 @@
CURLcode Curl_sspi_global_init(void);
void Curl_sspi_global_cleanup(void);
-/* This is used to populate the domain in a SSPI identity structure */
+/* This is used to populate the domain in an SSPI identity structure */
CURLcode Curl_override_sspi_http_realm(const char *chlg,
SEC_WINNT_AUTH_IDENTITY *identity);
@@ -70,6 +70,225 @@ extern PSecurityFunctionTable Curl_pSecFn;
#define ISC_REQ_USE_HTTP_STYLE 0x01000000
#endif
+#ifdef __MINGW32CE__
+#ifndef ISC_RET_REPLAY_DETECT
+#define ISC_RET_REPLAY_DETECT 0x00000004
+#endif
+#ifndef ISC_RET_SEQUENCE_DETECT
+#define ISC_RET_SEQUENCE_DETECT 0x00000008
+#endif
+#ifndef ISC_RET_CONFIDENTIALITY
+#define ISC_RET_CONFIDENTIALITY 0x00000010
+#endif
+#ifndef ISC_RET_ALLOCATED_MEMORY
+#define ISC_RET_ALLOCATED_MEMORY 0x00000100
+#endif
+#ifndef ISC_RET_STREAM
+#define ISC_RET_STREAM 0x00008000
+#endif
+
+#ifndef SEC_E_INSUFFICIENT_MEMORY
+#define SEC_E_INSUFFICIENT_MEMORY ((HRESULT)0x80090300L)
+#endif
+#ifndef SEC_E_INVALID_HANDLE
+#define SEC_E_INVALID_HANDLE ((HRESULT)0x80090301L)
+#endif
+#ifndef SEC_E_UNSUPPORTED_FUNCTION
+#define SEC_E_UNSUPPORTED_FUNCTION ((HRESULT)0x80090302L)
+#endif
+#ifndef SEC_E_TARGET_UNKNOWN
+#define SEC_E_TARGET_UNKNOWN ((HRESULT)0x80090303L)
+#endif
+#ifndef SEC_E_INTERNAL_ERROR
+#define SEC_E_INTERNAL_ERROR ((HRESULT)0x80090304L)
+#endif
+#ifndef SEC_E_SECPKG_NOT_FOUND
+#define SEC_E_SECPKG_NOT_FOUND ((HRESULT)0x80090305L)
+#endif
+#ifndef SEC_E_NOT_OWNER
+#define SEC_E_NOT_OWNER ((HRESULT)0x80090306L)
+#endif
+#ifndef SEC_E_CANNOT_INSTALL
+#define SEC_E_CANNOT_INSTALL ((HRESULT)0x80090307L)
+#endif
+#ifndef SEC_E_INVALID_TOKEN
+#define SEC_E_INVALID_TOKEN ((HRESULT)0x80090308L)
+#endif
+#ifndef SEC_E_CANNOT_PACK
+#define SEC_E_CANNOT_PACK ((HRESULT)0x80090309L)
+#endif
+#ifndef SEC_E_QOP_NOT_SUPPORTED
+#define SEC_E_QOP_NOT_SUPPORTED ((HRESULT)0x8009030AL)
+#endif
+#ifndef SEC_E_NO_IMPERSONATION
+#define SEC_E_NO_IMPERSONATION ((HRESULT)0x8009030BL)
+#endif
+#ifndef SEC_E_LOGON_DENIED
+#define SEC_E_LOGON_DENIED ((HRESULT)0x8009030CL)
+#endif
+#ifndef SEC_E_UNKNOWN_CREDENTIALS
+#define SEC_E_UNKNOWN_CREDENTIALS ((HRESULT)0x8009030DL)
+#endif
+#ifndef SEC_E_NO_CREDENTIALS
+#define SEC_E_NO_CREDENTIALS ((HRESULT)0x8009030EL)
+#endif
+#ifndef SEC_E_MESSAGE_ALTERED
+#define SEC_E_MESSAGE_ALTERED ((HRESULT)0x8009030FL)
+#endif
+#ifndef SEC_E_OUT_OF_SEQUENCE
+#define SEC_E_OUT_OF_SEQUENCE ((HRESULT)0x80090310L)
+#endif
+#ifndef SEC_E_NO_AUTHENTICATING_AUTHORITY
+#define SEC_E_NO_AUTHENTICATING_AUTHORITY ((HRESULT)0x80090311L)
+#endif
+#ifndef SEC_E_BAD_PKGID
+#define SEC_E_BAD_PKGID ((HRESULT)0x80090316L)
+#endif
+#ifndef SEC_E_CONTEXT_EXPIRED
+#define SEC_E_CONTEXT_EXPIRED ((HRESULT)0x80090317L)
+#endif
+#ifndef SEC_E_INCOMPLETE_MESSAGE
+#define SEC_E_INCOMPLETE_MESSAGE ((HRESULT)0x80090318L)
+#endif
+#ifndef SEC_E_INCOMPLETE_CREDENTIALS
+#define SEC_E_INCOMPLETE_CREDENTIALS ((HRESULT)0x80090320L)
+#endif
+#ifndef SEC_E_BUFFER_TOO_SMALL
+#define SEC_E_BUFFER_TOO_SMALL ((HRESULT)0x80090321L)
+#endif
+#ifndef SEC_E_WRONG_PRINCIPAL
+#define SEC_E_WRONG_PRINCIPAL ((HRESULT)0x80090322L)
+#endif
+#ifndef SEC_E_TIME_SKEW
+#define SEC_E_TIME_SKEW ((HRESULT)0x80090324L)
+#endif
+#ifndef SEC_E_UNTRUSTED_ROOT
+#define SEC_E_UNTRUSTED_ROOT ((HRESULT)0x80090325L)
+#endif
+#ifndef SEC_E_ILLEGAL_MESSAGE
+#define SEC_E_ILLEGAL_MESSAGE ((HRESULT)0x80090326L)
+#endif
+#ifndef SEC_E_CERT_UNKNOWN
+#define SEC_E_CERT_UNKNOWN ((HRESULT)0x80090327L)
+#endif
+#ifndef SEC_E_CERT_EXPIRED
+#define SEC_E_CERT_EXPIRED ((HRESULT)0x80090328L)
+#endif
+#ifndef SEC_E_ENCRYPT_FAILURE
+#define SEC_E_ENCRYPT_FAILURE ((HRESULT)0x80090329L)
+#endif
+#ifndef SEC_E_DECRYPT_FAILURE
+#define SEC_E_DECRYPT_FAILURE ((HRESULT)0x80090330L)
+#endif
+#ifndef SEC_E_ALGORITHM_MISMATCH
+#define SEC_E_ALGORITHM_MISMATCH ((HRESULT)0x80090331L)
+#endif
+#ifndef SEC_E_SECURITY_QOS_FAILED
+#define SEC_E_SECURITY_QOS_FAILED ((HRESULT)0x80090332L)
+#endif
+#ifndef SEC_E_UNFINISHED_CONTEXT_DELETED
+#define SEC_E_UNFINISHED_CONTEXT_DELETED ((HRESULT)0x80090333L)
+#endif
+#ifndef SEC_E_NO_TGT_REPLY
+#define SEC_E_NO_TGT_REPLY ((HRESULT)0x80090334L)
+#endif
+#ifndef SEC_E_NO_IP_ADDRESSES
+#define SEC_E_NO_IP_ADDRESSES ((HRESULT)0x80090335L)
+#endif
+#ifndef SEC_E_WRONG_CREDENTIAL_HANDLE
+#define SEC_E_WRONG_CREDENTIAL_HANDLE ((HRESULT)0x80090336L)
+#endif
+#ifndef SEC_E_CRYPTO_SYSTEM_INVALID
+#define SEC_E_CRYPTO_SYSTEM_INVALID ((HRESULT)0x80090337L)
+#endif
+#ifndef SEC_E_MAX_REFERRALS_EXCEEDED
+#define SEC_E_MAX_REFERRALS_EXCEEDED ((HRESULT)0x80090338L)
+#endif
+#ifndef SEC_E_MUST_BE_KDC
+#define SEC_E_MUST_BE_KDC ((HRESULT)0x80090339L)
+#endif
+#ifndef SEC_E_STRONG_CRYPTO_NOT_SUPPORTED
+#define SEC_E_STRONG_CRYPTO_NOT_SUPPORTED ((HRESULT)0x8009033AL)
+#endif
+#ifndef SEC_E_TOO_MANY_PRINCIPALS
+#define SEC_E_TOO_MANY_PRINCIPALS ((HRESULT)0x8009033BL)
+#endif
+#ifndef SEC_E_NO_PA_DATA
+#define SEC_E_NO_PA_DATA ((HRESULT)0x8009033CL)
+#endif
+#ifndef SEC_E_PKINIT_NAME_MISMATCH
+#define SEC_E_PKINIT_NAME_MISMATCH ((HRESULT)0x8009033DL)
+#endif
+#ifndef SEC_E_SMARTCARD_LOGON_REQUIRED
+#define SEC_E_SMARTCARD_LOGON_REQUIRED ((HRESULT)0x8009033EL)
+#endif
+#ifndef SEC_E_SHUTDOWN_IN_PROGRESS
+#define SEC_E_SHUTDOWN_IN_PROGRESS ((HRESULT)0x8009033FL)
+#endif
+#ifndef SEC_E_KDC_INVALID_REQUEST
+#define SEC_E_KDC_INVALID_REQUEST ((HRESULT)0x80090340L)
+#endif
+#ifndef SEC_E_KDC_UNABLE_TO_REFER
+#define SEC_E_KDC_UNABLE_TO_REFER ((HRESULT)0x80090341L)
+#endif
+#ifndef SEC_E_KDC_UNKNOWN_ETYPE
+#define SEC_E_KDC_UNKNOWN_ETYPE ((HRESULT)0x80090342L)
+#endif
+#ifndef SEC_E_UNSUPPORTED_PREAUTH
+#define SEC_E_UNSUPPORTED_PREAUTH ((HRESULT)0x80090343L)
+#endif
+#ifndef SEC_E_DELEGATION_REQUIRED
+#define SEC_E_DELEGATION_REQUIRED ((HRESULT)0x80090345L)
+#endif
+#ifndef SEC_E_BAD_BINDINGS
+#define SEC_E_BAD_BINDINGS ((HRESULT)0x80090346L)
+#endif
+#ifndef SEC_E_MULTIPLE_ACCOUNTS
+#define SEC_E_MULTIPLE_ACCOUNTS ((HRESULT)0x80090347L)
+#endif
+#ifndef SEC_E_NO_KERB_KEY
+#define SEC_E_NO_KERB_KEY ((HRESULT)0x80090348L)
+#endif
+#ifndef SEC_E_CERT_WRONG_USAGE
+#define SEC_E_CERT_WRONG_USAGE ((HRESULT)0x80090349L)
+#endif
+#ifndef SEC_E_DOWNGRADE_DETECTED
+#define SEC_E_DOWNGRADE_DETECTED ((HRESULT)0x80090350L)
+#endif
+#ifndef SEC_E_SMARTCARD_CERT_REVOKED
+#define SEC_E_SMARTCARD_CERT_REVOKED ((HRESULT)0x80090351L)
+#endif
+#ifndef SEC_E_ISSUING_CA_UNTRUSTED
+#define SEC_E_ISSUING_CA_UNTRUSTED ((HRESULT)0x80090352L)
+#endif
+#ifndef SEC_E_REVOCATION_OFFLINE_C
+#define SEC_E_REVOCATION_OFFLINE_C ((HRESULT)0x80090353L)
+#endif
+#ifndef SEC_E_PKINIT_CLIENT_FAILURE
+#define SEC_E_PKINIT_CLIENT_FAILURE ((HRESULT)0x80090354L)
+#endif
+#ifndef SEC_E_SMARTCARD_CERT_EXPIRED
+#define SEC_E_SMARTCARD_CERT_EXPIRED ((HRESULT)0x80090355L)
+#endif
+#ifndef SEC_E_NO_S4U_PROT_SUPPORT
+#define SEC_E_NO_S4U_PROT_SUPPORT ((HRESULT)0x80090356L)
+#endif
+#ifndef SEC_E_CROSSREALM_DELEGATION_FAILURE
+#define SEC_E_CROSSREALM_DELEGATION_FAILURE ((HRESULT)0x80090357L)
+#endif
+#ifndef SEC_E_REVOCATION_OFFLINE_KDC
+#define SEC_E_REVOCATION_OFFLINE_KDC ((HRESULT)0x80090358L)
+#endif
+#ifndef SEC_E_ISSUING_CA_UNTRUSTED_KDC
+#define SEC_E_ISSUING_CA_UNTRUSTED_KDC ((HRESULT)0x80090359L)
+#endif
+#ifndef SEC_E_KDC_CERT_EXPIRED
+#define SEC_E_KDC_CERT_EXPIRED ((HRESULT)0x8009035AL)
+#endif
+#ifndef SEC_E_KDC_CERT_REVOKED
+#define SEC_E_KDC_CERT_REVOKED ((HRESULT)0x8009035BL)
+#endif
+#endif /* __MINGW32CE__ */
#ifndef SEC_E_INVALID_PARAMETER
# define SEC_E_INVALID_PARAMETER ((HRESULT)0x8009035DL)
#endif
@@ -80,28 +299,54 @@ extern PSecurityFunctionTable Curl_pSecFn;
# define SEC_E_POLICY_NLTM_ONLY ((HRESULT)0x8009035FL)
#endif
+#ifdef __MINGW32CE__
+#ifndef SEC_I_CONTINUE_NEEDED
+#define SEC_I_CONTINUE_NEEDED ((HRESULT)0x00090312L)
+#endif
+#ifndef SEC_I_COMPLETE_NEEDED
+#define SEC_I_COMPLETE_NEEDED ((HRESULT)0x00090313L)
+#endif
+#ifndef SEC_I_COMPLETE_AND_CONTINUE
+#define SEC_I_COMPLETE_AND_CONTINUE ((HRESULT)0x00090314L)
+#endif
+#ifndef SEC_I_LOCAL_LOGON
+#define SEC_I_LOCAL_LOGON ((HRESULT)0x00090315L)
+#endif
+#ifndef SEC_I_CONTEXT_EXPIRED
+#define SEC_I_CONTEXT_EXPIRED ((HRESULT)0x00090317L)
+#endif
+#ifndef SEC_I_INCOMPLETE_CREDENTIALS
+#define SEC_I_INCOMPLETE_CREDENTIALS ((HRESULT)0x00090320L)
+#endif
+#ifndef SEC_I_RENEGOTIATE
+#define SEC_I_RENEGOTIATE ((HRESULT)0x00090321L)
+#endif
+#ifndef SEC_I_NO_LSA_CONTEXT
+#define SEC_I_NO_LSA_CONTEXT ((HRESULT)0x00090323L)
+#endif
+#endif /* __MINGW32CE__ */
#ifndef SEC_I_SIGNATURE_NEEDED
-# define SEC_I_SIGNATURE_NEEDED ((HRESULT)0x0009035CL)
+#define SEC_I_SIGNATURE_NEEDED ((HRESULT)0x0009035CL)
#endif
#ifndef CRYPT_E_REVOKED
-# define CRYPT_E_REVOKED ((HRESULT)0x80092010L)
+#define CRYPT_E_REVOKED ((HRESULT)0x80092010L)
#endif
#ifndef CRYPT_E_NO_REVOCATION_DLL
-# define CRYPT_E_NO_REVOCATION_DLL ((HRESULT)0x80092011L)
+#define CRYPT_E_NO_REVOCATION_DLL ((HRESULT)0x80092011L)
#endif
#ifndef CRYPT_E_NO_REVOCATION_CHECK
-# define CRYPT_E_NO_REVOCATION_CHECK ((HRESULT)0x80092012L)
+#define CRYPT_E_NO_REVOCATION_CHECK ((HRESULT)0x80092012L)
#endif
#ifndef CRYPT_E_REVOCATION_OFFLINE
-# define CRYPT_E_REVOCATION_OFFLINE ((HRESULT)0x80092013L)
+#define CRYPT_E_REVOCATION_OFFLINE ((HRESULT)0x80092013L)
#endif
#ifndef CRYPT_E_NOT_IN_REVOCATION_DATABASE
-# define CRYPT_E_NOT_IN_REVOCATION_DATABASE ((HRESULT)0x80092014L)
+#define CRYPT_E_NOT_IN_REVOCATION_DATABASE ((HRESULT)0x80092014L)
#endif
#ifdef UNICODE
diff --git a/libs/libcurl/src/curl_threads.c b/libs/libcurl/src/curl_threads.c
index 3879fb4355..c4831471d6 100644
--- a/libs/libcurl/src/curl_threads.c
+++ b/libs/libcurl/src/curl_threads.c
@@ -35,9 +35,7 @@
#endif
#include "curl_threads.h"
-#ifdef BUILDING_LIBCURL
#include "curl_memory.h"
-#endif
/* The last #include file should be: */
#include "memdebug.h"
@@ -82,11 +80,12 @@ err:
return curl_thread_t_null;
}
-void Curl_thread_destroy(curl_thread_t hnd)
+void Curl_thread_destroy(curl_thread_t *hnd)
{
- if(hnd != curl_thread_t_null) {
- pthread_detach(*hnd);
- free(hnd);
+ if(*hnd != curl_thread_t_null) {
+ pthread_detach(**hnd);
+ free(*hnd);
+ *hnd = curl_thread_t_null;
}
}
@@ -103,7 +102,7 @@ int Curl_thread_join(curl_thread_t *hnd)
#elif defined(USE_THREADS_WIN32)
curl_thread_t Curl_thread_create(
-#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_UWP)
+#if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE)
DWORD
#else
unsigned int
@@ -111,35 +110,39 @@ curl_thread_t Curl_thread_create(
(CURL_STDCALL *func) (void *),
void *arg)
{
-#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_UWP)
+#if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE)
typedef HANDLE curl_win_thread_handle_t;
#else
typedef uintptr_t curl_win_thread_handle_t;
#endif
curl_thread_t t;
curl_win_thread_handle_t thread_handle;
-#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_UWP)
+#if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE)
thread_handle = CreateThread(NULL, 0, func, arg, 0, NULL);
#else
thread_handle = _beginthreadex(NULL, 0, func, arg, 0, NULL);
#endif
t = (curl_thread_t)thread_handle;
if((t == 0) || (t == LongToHandle(-1L))) {
-#ifdef _WIN32_WCE
+#ifdef UNDER_CE
DWORD gle = GetLastError();
- errno = ((gle == ERROR_ACCESS_DENIED ||
- gle == ERROR_NOT_ENOUGH_MEMORY) ?
- EACCES : EINVAL);
+ /* !checksrc! disable ERRNOVAR 1 */
+ int err = (gle == ERROR_ACCESS_DENIED ||
+ gle == ERROR_NOT_ENOUGH_MEMORY) ?
+ EACCES : EINVAL;
+ CURL_SETERRNO(err);
#endif
return curl_thread_t_null;
}
return t;
}
-void Curl_thread_destroy(curl_thread_t hnd)
+void Curl_thread_destroy(curl_thread_t *hnd)
{
- if(hnd != curl_thread_t_null)
- CloseHandle(hnd);
+ if(*hnd != curl_thread_t_null) {
+ CloseHandle(*hnd);
+ *hnd = curl_thread_t_null;
+ }
}
int Curl_thread_join(curl_thread_t *hnd)
@@ -151,9 +154,7 @@ int Curl_thread_join(curl_thread_t *hnd)
int ret = (WaitForSingleObjectEx(*hnd, INFINITE, FALSE) == WAIT_OBJECT_0);
#endif
- Curl_thread_destroy(*hnd);
-
- *hnd = curl_thread_t_null;
+ Curl_thread_destroy(hnd);
return ret;
}
diff --git a/libs/libcurl/src/curl_threads.h b/libs/libcurl/src/curl_threads.h
index 3b29556ffd..8b1821d011 100644
--- a/libs/libcurl/src/curl_threads.h
+++ b/libs/libcurl/src/curl_threads.h
@@ -53,7 +53,7 @@
#if defined(USE_THREADS_POSIX) || defined(USE_THREADS_WIN32)
curl_thread_t Curl_thread_create(
-#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_UWP)
+#if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE)
DWORD
#else
unsigned int
@@ -61,7 +61,7 @@ curl_thread_t Curl_thread_create(
(CURL_STDCALL *func) (void *),
void *arg);
-void Curl_thread_destroy(curl_thread_t hnd);
+void Curl_thread_destroy(curl_thread_t *hnd);
int Curl_thread_join(curl_thread_t *hnd);
diff --git a/libs/libcurl/src/curl_trc.c b/libs/libcurl/src/curl_trc.c
index 3385cd3da2..546d3c547e 100644
--- a/libs/libcurl/src/curl_trc.c
+++ b/libs/libcurl/src/curl_trc.c
@@ -30,7 +30,6 @@
#include "urldata.h"
#include "easyif.h"
#include "cfilters.h"
-#include "timeval.h"
#include "multiif.h"
#include "strcase.h"
@@ -44,7 +43,7 @@
#include "cf-haproxy.h"
#include "cf-https-connect.h"
#include "socks.h"
-#include "strtok.h"
+#include "curlx/strparse.h"
#include "vtls/vtls.h"
#include "vquic/vquic.h"
@@ -53,27 +52,120 @@
#include "curl_memory.h"
#include "memdebug.h"
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
-#endif
+static void trc_write(struct Curl_easy *data, curl_infotype type,
+ const char *ptr, size_t size)
+{
+ if(data->set.verbose) {
+ if(data->set.fdebug) {
+ bool inCallback = Curl_is_in_callback(data);
+ Curl_set_in_callback(data, TRUE);
+ (void)(*data->set.fdebug)(data, type, CURL_UNCONST(ptr), size,
+ data->set.debugdata);
+ Curl_set_in_callback(data, inCallback);
+ }
+ else {
+ static const char s_infotype[CURLINFO_END][3] = {
+ "* ", "< ", "> ", "{ ", "} ", "{ ", "} " };
+ switch(type) {
+ case CURLINFO_TEXT:
+ case CURLINFO_HEADER_OUT:
+ case CURLINFO_HEADER_IN:
+ fwrite(s_infotype[type], 2, 1, data->set.err);
+ fwrite(ptr, size, 1, data->set.err);
+ break;
+ default: /* nada */
+ break;
+ }
+ }
+ }
+}
+
+/* max length we trace before ending in '...' */
+#define TRC_LINE_MAX 2048
+
+#define CURL_TRC_FMT_IDSC "[x-%" CURL_FORMAT_CURL_OFF_T "] "
+#define CURL_TRC_FMT_IDSD "[%" CURL_FORMAT_CURL_OFF_T "-x] "
+#define CURL_TRC_FMT_IDSDC "[%" CURL_FORMAT_CURL_OFF_T "-%" \
+ CURL_FORMAT_CURL_OFF_T "] "
+
+static struct curl_trc_feat Curl_trc_feat_ids = {
+ "LIB-IDS",
+ CURL_LOG_LVL_NONE,
+};
+#define CURL_TRC_IDS(data) \
+ (Curl_trc_is_verbose(data) && \
+ Curl_trc_feat_ids.log_level >= CURL_LOG_LVL_INFO)
+
+static size_t trc_print_ids(struct Curl_easy *data, char *buf, size_t maxlen)
+{
+ curl_off_t cid = data->conn ?
+ data->conn->connection_id : data->state.recent_conn_id;
+ if(data->id >= 0) {
+ if(cid >= 0)
+ return msnprintf(buf, maxlen, CURL_TRC_FMT_IDSDC, data->id, cid);
+ else
+ return msnprintf(buf, maxlen, CURL_TRC_FMT_IDSD, data->id);
+ }
+ else if(cid >= 0)
+ return msnprintf(buf, maxlen, CURL_TRC_FMT_IDSC, cid);
+ else {
+ return msnprintf(buf, maxlen, "[x-x] ");
+ }
+}
+
+static size_t trc_end_buf(char *buf, size_t len, size_t maxlen, bool addnl)
+{
+ /* make sure we end the trace line in `buf` properly. It needs
+ * to end with a terminating '\0' or '\n\0' */
+ if(len >= (maxlen - (addnl ? 2 : 1))) {
+ len = maxlen - 5;
+ buf[len++] = '.';
+ buf[len++] = '.';
+ buf[len++] = '.';
+ buf[len++] = '\n';
+ }
+ else if(addnl)
+ buf[len++] = '\n';
+ buf[len] = '\0';
+ return len;
+}
void Curl_debug(struct Curl_easy *data, curl_infotype type,
- char *ptr, size_t size)
+ const char *ptr, size_t size)
{
if(data->set.verbose) {
static const char s_infotype[CURLINFO_END][3] = {
"* ", "< ", "> ", "{ ", "} ", "{ ", "} " };
+ char buf[TRC_LINE_MAX];
+ size_t len;
if(data->set.fdebug) {
bool inCallback = Curl_is_in_callback(data);
- Curl_set_in_callback(data, TRUE);
- (void)(*data->set.fdebug)(data, type, ptr, size, data->set.debugdata);
- Curl_set_in_callback(data, inCallback);
+
+ if(CURL_TRC_IDS(data) && (size < TRC_LINE_MAX)) {
+ len = trc_print_ids(data, buf, TRC_LINE_MAX);
+ len += msnprintf(buf + len, TRC_LINE_MAX - len, "%.*s",
+ (int)size, ptr);
+ len = trc_end_buf(buf, len, TRC_LINE_MAX, FALSE);
+ Curl_set_in_callback(data, TRUE);
+ (void)(*data->set.fdebug)(data, type, buf, len, data->set.debugdata);
+ Curl_set_in_callback(data, inCallback);
+ }
+ else {
+ Curl_set_in_callback(data, TRUE);
+ (void)(*data->set.fdebug)(data, type, CURL_UNCONST(ptr),
+ size, data->set.debugdata);
+ Curl_set_in_callback(data, inCallback);
+ }
}
else {
switch(type) {
case CURLINFO_TEXT:
case CURLINFO_HEADER_OUT:
case CURLINFO_HEADER_IN:
+ if(CURL_TRC_IDS(data)) {
+ len = trc_print_ids(data, buf, TRC_LINE_MAX);
+ fwrite(buf, len, 1, data->set.err);
+ }
fwrite(s_infotype[type], 2, 1, data->set.err);
fwrite(ptr, size, 1, data->set.err);
break;
@@ -84,7 +176,6 @@ void Curl_debug(struct Curl_easy *data, curl_infotype type,
}
}
-
/* Curl_failf() is for messages stating why we failed.
* The message SHALL NOT include any LF or CR.
*/
@@ -93,7 +184,7 @@ void Curl_failf(struct Curl_easy *data, const char *fmt, ...)
DEBUGASSERT(!strchr(fmt, '\n'));
if(data->set.verbose || data->set.errorbuffer) {
va_list ap;
- int len;
+ size_t len;
char error[CURL_ERROR_SIZE + 2];
va_start(ap, fmt);
len = mvsnprintf(error, CURL_ERROR_SIZE, fmt, ap);
@@ -104,36 +195,41 @@ void Curl_failf(struct Curl_easy *data, const char *fmt, ...)
}
error[len++] = '\n';
error[len] = '\0';
- Curl_debug(data, CURLINFO_TEXT, error, len);
+ trc_write(data, CURLINFO_TEXT, error, len);
va_end(ap);
}
}
#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
-/* Curl_infof() is for info message along the way */
-#define MAXINFO 2048
-static void trc_infof(struct Curl_easy *data, struct curl_trc_feat *feat,
- const char * const fmt, va_list ap) CURL_PRINTF(3, 0);
+static void trc_infof(struct Curl_easy *data,
+ struct curl_trc_feat *feat,
+ const char *opt_id, int opt_id_idx,
+ const char * const fmt, va_list ap) CURL_PRINTF(5, 0);
-static void trc_infof(struct Curl_easy *data, struct curl_trc_feat *feat,
+static void trc_infof(struct Curl_easy *data,
+ struct curl_trc_feat *feat,
+ const char *opt_id, int opt_id_idx,
const char * const fmt, va_list ap)
{
- int len = 0;
- char buffer[MAXINFO + 5];
+ size_t len = 0;
+ char buf[TRC_LINE_MAX];
+
+ if(CURL_TRC_IDS(data))
+ len += trc_print_ids(data, buf + len, TRC_LINE_MAX - len);
if(feat)
- len = msnprintf(buffer, (MAXINFO + 1), "[%s] ", feat->name);
- len += mvsnprintf(buffer + len, (MAXINFO + 1) - len, fmt, ap);
- if(len >= MAXINFO) { /* too long, shorten with '...' */
- --len;
- buffer[len++] = '.';
- buffer[len++] = '.';
- buffer[len++] = '.';
+ len += msnprintf(buf + len, TRC_LINE_MAX - len, "[%s] ", feat->name);
+ if(opt_id) {
+ if(opt_id_idx > 0)
+ len += msnprintf(buf + len, TRC_LINE_MAX - len, "[%s-%d] ",
+ opt_id, opt_id_idx);
+ else
+ len += msnprintf(buf + len, TRC_LINE_MAX - len, "[%s] ", opt_id);
}
- buffer[len++] = '\n';
- buffer[len] = '\0';
- Curl_debug(data, CURLINFO_TEXT, buffer, len);
+ len += mvsnprintf(buf + len, TRC_LINE_MAX - len, fmt, ap);
+ len = trc_end_buf(buf, len, TRC_LINE_MAX, TRUE);
+ trc_write(data, CURLINFO_TEXT, buf, len);
}
void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
@@ -142,36 +238,27 @@ void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
if(Curl_trc_is_verbose(data)) {
va_list ap;
va_start(ap, fmt);
- trc_infof(data, data->state.feat, fmt, ap);
+ trc_infof(data, data->state.feat, NULL, 0, fmt, ap);
va_end(ap);
}
}
-void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf,
+void Curl_trc_cf_infof(struct Curl_easy *data, const struct Curl_cfilter *cf,
const char *fmt, ...)
{
DEBUGASSERT(cf);
if(Curl_trc_cf_is_verbose(cf, data)) {
va_list ap;
- int len = 0;
- char buffer[MAXINFO + 2];
- if(data->state.feat)
- len += msnprintf(buffer + len, MAXINFO - len, "[%s] ",
- data->state.feat->name);
- if(cf->sockindex)
- len += msnprintf(buffer + len, MAXINFO - len, "[%s-%d] ",
- cf->cft->name, cf->sockindex);
- else
- len += msnprintf(buffer + len, MAXINFO - len, "[%s] ", cf->cft->name);
va_start(ap, fmt);
- len += mvsnprintf(buffer + len, MAXINFO - len, fmt, ap);
+ trc_infof(data, data->state.feat, cf->cft->name, cf->sockindex, fmt, ap);
va_end(ap);
- buffer[len++] = '\n';
- buffer[len] = '\0';
- Curl_debug(data, CURLINFO_TEXT, buffer, len);
}
}
+struct curl_trc_feat Curl_trc_feat_multi = {
+ "MULTI",
+ CURL_LOG_LVL_NONE,
+};
struct curl_trc_feat Curl_trc_feat_read = {
"READ",
CURL_LOG_LVL_NONE,
@@ -180,6 +267,52 @@ struct curl_trc_feat Curl_trc_feat_write = {
"WRITE",
CURL_LOG_LVL_NONE,
};
+struct curl_trc_feat Curl_trc_feat_dns = {
+ "DNS",
+ CURL_LOG_LVL_NONE,
+};
+
+
+static const char * const Curl_trc_mstate_names[]={
+ "INIT",
+ "PENDING",
+ "SETUP",
+ "CONNECT",
+ "RESOLVING",
+ "CONNECTING",
+ "TUNNELING",
+ "PROTOCONNECT",
+ "PROTOCONNECTING",
+ "DO",
+ "DOING",
+ "DOING_MORE",
+ "DID",
+ "PERFORMING",
+ "RATELIMITING",
+ "DONE",
+ "COMPLETED",
+ "MSGSENT",
+};
+
+const char *Curl_trc_mstate_name(int state)
+{
+ if((state >= 0) && ((size_t)state < CURL_ARRAYSIZE(Curl_trc_mstate_names)))
+ return Curl_trc_mstate_names[(size_t)state];
+ return "?";
+}
+
+void Curl_trc_multi(struct Curl_easy *data, const char *fmt, ...)
+{
+ DEBUGASSERT(!strchr(fmt, '\n'));
+ if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_multi)) {
+ const char *sname = (data->id >= 0) ?
+ Curl_trc_mstate_name(data->mstate) : NULL;
+ va_list ap;
+ va_start(ap, fmt);
+ trc_infof(data, &Curl_trc_feat_multi, sname, 0, fmt, ap);
+ va_end(ap);
+ }
+}
void Curl_trc_read(struct Curl_easy *data, const char *fmt, ...)
{
@@ -187,7 +320,7 @@ void Curl_trc_read(struct Curl_easy *data, const char *fmt, ...)
if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_read)) {
va_list ap;
va_start(ap, fmt);
- trc_infof(data, &Curl_trc_feat_read, fmt, ap);
+ trc_infof(data, &Curl_trc_feat_read, NULL, 0, fmt, ap);
va_end(ap);
}
}
@@ -198,7 +331,18 @@ void Curl_trc_write(struct Curl_easy *data, const char *fmt, ...)
if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_write)) {
va_list ap;
va_start(ap, fmt);
- trc_infof(data, &Curl_trc_feat_write, fmt, ap);
+ trc_infof(data, &Curl_trc_feat_write, NULL, 0, fmt, ap);
+ va_end(ap);
+ }
+}
+
+void Curl_trc_dns(struct Curl_easy *data, const char *fmt, ...)
+{
+ DEBUGASSERT(!strchr(fmt, '\n'));
+ if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_dns)) {
+ va_list ap;
+ va_start(ap, fmt);
+ trc_infof(data, &Curl_trc_feat_dns, NULL, 0, fmt, ap);
va_end(ap);
}
}
@@ -215,7 +359,7 @@ void Curl_trc_ftp(struct Curl_easy *data, const char *fmt, ...)
if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ftp)) {
va_list ap;
va_start(ap, fmt);
- trc_infof(data, &Curl_trc_feat_ftp, fmt, ap);
+ trc_infof(data, &Curl_trc_feat_ftp, NULL, 0, fmt, ap);
va_end(ap);
}
}
@@ -233,12 +377,30 @@ void Curl_trc_smtp(struct Curl_easy *data, const char *fmt, ...)
if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_smtp)) {
va_list ap;
va_start(ap, fmt);
- trc_infof(data, &Curl_trc_feat_smtp, fmt, ap);
+ trc_infof(data, &Curl_trc_feat_smtp, NULL, 0, fmt, ap);
va_end(ap);
}
}
#endif /* !CURL_DISABLE_SMTP */
+#ifdef USE_SSL
+struct curl_trc_feat Curl_trc_feat_ssls = {
+ "SSLS",
+ CURL_LOG_LVL_NONE,
+};
+
+void Curl_trc_ssls(struct Curl_easy *data, const char *fmt, ...)
+{
+ DEBUGASSERT(!strchr(fmt, '\n'));
+ if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ssls)) {
+ va_list ap;
+ va_start(ap, fmt);
+ trc_infof(data, &Curl_trc_feat_ssls, NULL, 0, fmt, ap);
+ va_end(ap);
+ }
+}
+#endif /* USE_SSL */
+
#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
struct curl_trc_feat Curl_trc_feat_ws = {
"WS",
@@ -251,7 +413,7 @@ void Curl_trc_ws(struct Curl_easy *data, const char *fmt, ...)
if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ws)) {
va_list ap;
va_start(ap, fmt);
- trc_infof(data, &Curl_trc_feat_ws, fmt, ap);
+ trc_infof(data, &Curl_trc_feat_ws, NULL, 0, fmt, ap);
va_end(ap);
}
}
@@ -261,6 +423,7 @@ void Curl_trc_ws(struct Curl_easy *data, const char *fmt, ...)
#define TRC_CT_PROTOCOL (1<<(0))
#define TRC_CT_NETWORK (1<<(1))
#define TRC_CT_PROXY (1<<(2))
+#define TRC_CT_INTERNALS (1<<(3))
struct trc_feat_def {
struct curl_trc_feat *feat;
@@ -268,17 +431,22 @@ struct trc_feat_def {
};
static struct trc_feat_def trc_feats[] = {
+ { &Curl_trc_feat_ids, TRC_CT_INTERNALS },
+ { &Curl_trc_feat_multi, TRC_CT_NETWORK },
{ &Curl_trc_feat_read, TRC_CT_NONE },
{ &Curl_trc_feat_write, TRC_CT_NONE },
+ { &Curl_trc_feat_dns, TRC_CT_NETWORK },
#ifndef CURL_DISABLE_FTP
{ &Curl_trc_feat_ftp, TRC_CT_PROTOCOL },
#endif
#ifndef CURL_DISABLE_DOH
- { &Curl_doh_trc, TRC_CT_NETWORK },
#endif
#ifndef CURL_DISABLE_SMTP
{ &Curl_trc_feat_smtp, TRC_CT_PROTOCOL },
#endif
+#ifdef USE_SSL
+ { &Curl_trc_feat_ssls, TRC_CT_NETWORK },
+#endif
#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
{ &Curl_trc_feat_ws, TRC_CT_PROTOCOL },
#endif
@@ -319,23 +487,23 @@ static struct trc_cft_def trc_cfts[] = {
#ifdef USE_HTTP3
{ &Curl_cft_http3, TRC_CT_PROTOCOL },
#endif
-#if !defined(CURL_DISABLE_HTTP) && !defined(USE_HYPER)
+#if !defined(CURL_DISABLE_HTTP)
{ &Curl_cft_http_connect, TRC_CT_PROTOCOL },
#endif
};
-static void trc_apply_level_by_name(const char * const token, int lvl)
+static void trc_apply_level_by_name(struct Curl_str *token, int lvl)
{
size_t i;
- for(i = 0; i < ARRAYSIZE(trc_cfts); ++i) {
- if(strcasecompare(token, trc_cfts[i].cft->name)) {
+ for(i = 0; i < CURL_ARRAYSIZE(trc_cfts); ++i) {
+ if(curlx_str_casecompare(token, trc_cfts[i].cft->name)) {
trc_cfts[i].cft->log_level = lvl;
break;
}
}
- for(i = 0; i < ARRAYSIZE(trc_feats); ++i) {
- if(strcasecompare(token, trc_feats[i].feat->name)) {
+ for(i = 0; i < CURL_ARRAYSIZE(trc_feats); ++i) {
+ if(curlx_str_casecompare(token, trc_feats[i].feat->name)) {
trc_feats[i].feat->log_level = lvl;
break;
}
@@ -346,11 +514,11 @@ static void trc_apply_level_by_category(int category, int lvl)
{
size_t i;
- for(i = 0; i < ARRAYSIZE(trc_cfts); ++i) {
+ for(i = 0; i < CURL_ARRAYSIZE(trc_cfts); ++i) {
if(!category || (trc_cfts[i].category & category))
trc_cfts[i].cft->log_level = lvl;
}
- for(i = 0; i < ARRAYSIZE(trc_feats); ++i) {
+ for(i = 0; i < CURL_ARRAYSIZE(trc_feats); ++i) {
if(!category || (trc_feats[i].category & category))
trc_feats[i].feat->log_level = lvl;
}
@@ -358,42 +526,36 @@ static void trc_apply_level_by_category(int category, int lvl)
static CURLcode trc_opt(const char *config)
{
- char *token, *tok_buf, *tmp;
- int lvl;
-
- tmp = strdup(config);
- if(!tmp)
- return CURLE_OUT_OF_MEMORY;
-
- token = strtok_r(tmp, ", ", &tok_buf);
- while(token) {
- switch(*token) {
- case '-':
- lvl = CURL_LOG_LVL_NONE;
- ++token;
- break;
- case '+':
- lvl = CURL_LOG_LVL_INFO;
- ++token;
- break;
- default:
- lvl = CURL_LOG_LVL_INFO;
- break;
+ struct Curl_str out;
+ while(!curlx_str_until(&config, &out, 32, ',')) {
+ int lvl = CURL_LOG_LVL_INFO;
+ const char *token = curlx_str(&out);
+
+ if(*token == '-') {
+ lvl = CURL_LOG_LVL_NONE;
+ curlx_str_nudge(&out, 1);
}
- if(strcasecompare(token, "all"))
+ else if(*token == '+')
+ curlx_str_nudge(&out, 1);
+
+ if(curlx_str_casecompare(&out, "all"))
trc_apply_level_by_category(TRC_CT_NONE, lvl);
- else if(strcasecompare(token, "protocol"))
+ else if(curlx_str_casecompare(&out, "protocol"))
trc_apply_level_by_category(TRC_CT_PROTOCOL, lvl);
- else if(strcasecompare(token, "network"))
+ else if(curlx_str_casecompare(&out, "network"))
trc_apply_level_by_category(TRC_CT_NETWORK, lvl);
- else if(strcasecompare(token, "proxy"))
+ else if(curlx_str_casecompare(&out, "proxy"))
trc_apply_level_by_category(TRC_CT_PROXY, lvl);
+ else if(curlx_str_casecompare(&out, "doh")) {
+ struct Curl_str dns = { "dns", 3 };
+ trc_apply_level_by_name(&dns, lvl);
+ }
else
- trc_apply_level_by_name(token, lvl);
+ trc_apply_level_by_name(&out, lvl);
- token = strtok_r(NULL, ", ", &tok_buf);
+ if(curlx_str_single(&config, ','))
+ break;
}
- free(tmp);
return CURLE_OK;
}
@@ -427,4 +589,62 @@ CURLcode Curl_trc_init(void)
return CURLE_OK;
}
+void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
+{
+ (void)data; (void)fmt;
+}
+
+void Curl_trc_cf_infof(struct Curl_easy *data, const struct Curl_cfilter *cf,
+ const char *fmt, ...)
+{
+ (void)data; (void)cf; (void)fmt;
+}
+
+struct curl_trc_feat;
+
+void Curl_trc_multi(struct Curl_easy *data, const char *fmt, ...)
+{
+ (void)data; (void)fmt;
+}
+
+void Curl_trc_write(struct Curl_easy *data, const char *fmt, ...)
+{
+ (void)data; (void)fmt;
+}
+
+void Curl_trc_dns(struct Curl_easy *data, const char *fmt, ...)
+{
+ (void)data; (void)fmt;
+}
+
+void Curl_trc_read(struct Curl_easy *data, const char *fmt, ...)
+{
+ (void)data; (void)fmt;
+}
+
+#ifndef CURL_DISABLE_FTP
+void Curl_trc_ftp(struct Curl_easy *data, const char *fmt, ...)
+{
+ (void)data; (void)fmt;
+}
+#endif
+#ifndef CURL_DISABLE_SMTP
+void Curl_trc_smtp(struct Curl_easy *data, const char *fmt, ...)
+{
+ (void)data; (void)fmt;
+}
+#endif
+#if !defined(CURL_DISABLE_WEBSOCKETS) || !defined(CURL_DISABLE_HTTP)
+void Curl_trc_ws(struct Curl_easy *data, const char *fmt, ...)
+{
+ (void)data; (void)fmt;
+}
+#endif
+
+void Curl_trc_ssls(struct Curl_easy *data, const char *fmt, ...)
+{
+ (void)data;
+ (void)fmt;
+}
+
#endif /* !defined(CURL_DISABLE_VERBOSE_STRINGS) */
diff --git a/libs/libcurl/src/curl_trc.h b/libs/libcurl/src/curl_trc.h
index bcf598d8db..1aecf54cc7 100644
--- a/libs/libcurl/src/curl_trc.h
+++ b/libs/libcurl/src/curl_trc.h
@@ -52,7 +52,7 @@ CURLcode Curl_trc_opt(const char *config);
/* the function used to output verbose information */
void Curl_debug(struct Curl_easy *data, curl_infotype type,
- char *ptr, size_t size);
+ const char *ptr, size_t size);
/**
* Output a failure message on registered callbacks for transfer.
@@ -70,10 +70,56 @@ void Curl_failf(struct Curl_easy *data,
#define CURL_HAVE_C99
#endif
-#ifdef CURL_HAVE_C99
+/**
+ * Output an informational message when transfer's verbose logging is enabled.
+ */
+void Curl_infof(struct Curl_easy *data,
+ const char *fmt, ...) CURL_PRINTF(2, 3);
+
+/**
+ * Output an informational message when both transfer's verbose logging
+ * and connection filters verbose logging are enabled.
+ */
+void Curl_trc_cf_infof(struct Curl_easy *data, const struct Curl_cfilter *cf,
+ const char *fmt, ...) CURL_PRINTF(3, 4);
+void Curl_trc_multi(struct Curl_easy *data,
+ const char *fmt, ...) CURL_PRINTF(2, 3);
+const char *Curl_trc_mstate_name(int state);
+void Curl_trc_write(struct Curl_easy *data,
+ const char *fmt, ...) CURL_PRINTF(2, 3);
+void Curl_trc_read(struct Curl_easy *data,
+ const char *fmt, ...) CURL_PRINTF(2, 3);
+void Curl_trc_dns(struct Curl_easy *data,
+ const char *fmt, ...) CURL_PRINTF(2, 3);
+
+#ifndef CURL_DISABLE_FTP
+extern struct curl_trc_feat Curl_trc_feat_ftp;
+void Curl_trc_ftp(struct Curl_easy *data,
+ const char *fmt, ...) CURL_PRINTF(2, 3);
+#endif
+#ifndef CURL_DISABLE_SMTP
+extern struct curl_trc_feat Curl_trc_feat_smtp;
+void Curl_trc_smtp(struct Curl_easy *data,
+ const char *fmt, ...) CURL_PRINTF(2, 3);
+#endif
+#ifdef USE_SSL
+extern struct curl_trc_feat Curl_trc_feat_ssls;
+void Curl_trc_ssls(struct Curl_easy *data,
+ const char *fmt, ...) CURL_PRINTF(2, 3);
+#endif
+#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
+extern struct curl_trc_feat Curl_trc_feat_ws;
+void Curl_trc_ws(struct Curl_easy *data,
+ const char *fmt, ...) CURL_PRINTF(2, 3);
+#endif
+
+#if defined(CURL_HAVE_C99) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
#define infof(data, ...) \
do { if(Curl_trc_is_verbose(data)) \
Curl_infof(data, __VA_ARGS__); } while(0)
+#define CURL_TRC_M(data, ...) \
+ do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_multi)) \
+ Curl_trc_multi(data, __VA_ARGS__); } while(0)
#define CURL_TRC_CF(data, cf, ...) \
do { if(Curl_trc_cf_is_verbose(cf, data)) \
Curl_trc_cf_infof(data, cf, __VA_ARGS__); } while(0)
@@ -83,6 +129,9 @@ void Curl_failf(struct Curl_easy *data,
#define CURL_TRC_READ(data, ...) \
do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_read)) \
Curl_trc_read(data, __VA_ARGS__); } while(0)
+#define CURL_TRC_DNS(data, ...) \
+ do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_dns)) \
+ Curl_trc_dns(data, __VA_ARGS__); } while(0)
#ifndef CURL_DISABLE_FTP
#define CURL_TRC_FTP(data, ...) \
@@ -94,6 +143,11 @@ void Curl_failf(struct Curl_easy *data,
do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_smtp)) \
Curl_trc_smtp(data, __VA_ARGS__); } while(0)
#endif /* !CURL_DISABLE_SMTP */
+#ifdef USE_SSL
+#define CURL_TRC_SSLS(data, ...) \
+ do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ssls)) \
+ Curl_trc_ssls(data, __VA_ARGS__); } while(0)
+#endif /* USE_SSL */
#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
#define CURL_TRC_WS(data, ...) \
do { if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_ws)) \
@@ -103,9 +157,11 @@ void Curl_failf(struct Curl_easy *data,
#else /* CURL_HAVE_C99 */
#define infof Curl_infof
+#define CURL_TRC_M Curl_trc_multi
#define CURL_TRC_CF Curl_trc_cf_infof
#define CURL_TRC_WRITE Curl_trc_write
#define CURL_TRC_READ Curl_trc_read
+#define CURL_TRC_DNS Curl_trc_dns
#ifndef CURL_DISABLE_FTP
#define CURL_TRC_FTP Curl_trc_ftp
@@ -113,21 +169,27 @@ void Curl_failf(struct Curl_easy *data,
#ifndef CURL_DISABLE_SMTP
#define CURL_TRC_SMTP Curl_trc_smtp
#endif
+#ifdef USE_SSL
+#define CURL_TRC_SSLS Curl_trc_ssls
+#endif
#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
#define CURL_TRC_WS Curl_trc_ws
#endif
#endif /* !CURL_HAVE_C99 */
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
-/* informational messages enabled */
-
struct curl_trc_feat {
const char *name;
int log_level;
};
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+/* informational messages enabled */
+
+extern struct curl_trc_feat Curl_trc_feat_multi;
extern struct curl_trc_feat Curl_trc_feat_read;
extern struct curl_trc_feat Curl_trc_feat_write;
+extern struct curl_trc_feat Curl_trc_feat_dns;
#define Curl_trc_is_verbose(data) \
((data) && (data)->set.verbose && \
@@ -139,40 +201,7 @@ extern struct curl_trc_feat Curl_trc_feat_write;
#define Curl_trc_ft_is_verbose(data, ft) \
(Curl_trc_is_verbose(data) && \
(ft)->log_level >= CURL_LOG_LVL_INFO)
-
-/**
- * Output an informational message when transfer's verbose logging is enabled.
- */
-void Curl_infof(struct Curl_easy *data,
- const char *fmt, ...) CURL_PRINTF(2, 3);
-
-/**
- * Output an informational message when both transfer's verbose logging
- * and connection filters verbose logging are enabled.
- */
-void Curl_trc_cf_infof(struct Curl_easy *data, struct Curl_cfilter *cf,
- const char *fmt, ...) CURL_PRINTF(3, 4);
-void Curl_trc_write(struct Curl_easy *data,
- const char *fmt, ...) CURL_PRINTF(2, 3);
-void Curl_trc_read(struct Curl_easy *data,
- const char *fmt, ...) CURL_PRINTF(2, 3);
-
-#ifndef CURL_DISABLE_FTP
-extern struct curl_trc_feat Curl_trc_feat_ftp;
-void Curl_trc_ftp(struct Curl_easy *data,
- const char *fmt, ...) CURL_PRINTF(2, 3);
-#endif
-#ifndef CURL_DISABLE_SMTP
-extern struct curl_trc_feat Curl_trc_feat_smtp;
-void Curl_trc_smtp(struct Curl_easy *data,
- const char *fmt, ...) CURL_PRINTF(2, 3);
-#endif
-#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
-extern struct curl_trc_feat Curl_trc_feat_ws;
-void Curl_trc_ws(struct Curl_easy *data,
- const char *fmt, ...) CURL_PRINTF(2, 3);
-#endif
-
+#define CURL_MSTATE_NAME(s) Curl_trc_mstate_name((int)(s))
#else /* defined(CURL_DISABLE_VERBOSE_STRINGS) */
/* All informational messages are not compiled in for size savings */
@@ -180,49 +209,7 @@ void Curl_trc_ws(struct Curl_easy *data,
#define Curl_trc_is_verbose(d) (FALSE)
#define Curl_trc_cf_is_verbose(x,y) (FALSE)
#define Curl_trc_ft_is_verbose(x,y) (FALSE)
-
-static void Curl_infof(struct Curl_easy *data, const char *fmt, ...)
-{
- (void)data; (void)fmt;
-}
-
-static void Curl_trc_cf_infof(struct Curl_easy *data,
- struct Curl_cfilter *cf,
- const char *fmt, ...)
-{
- (void)data; (void)cf; (void)fmt;
-}
-
-struct curl_trc_feat;
-
-static void Curl_trc_write(struct Curl_easy *data, const char *fmt, ...)
-{
- (void)data; (void)fmt;
-}
-
-static void Curl_trc_read(struct Curl_easy *data, const char *fmt, ...)
-{
- (void)data; (void)fmt;
-}
-
-#ifndef CURL_DISABLE_FTP
-static void Curl_trc_ftp(struct Curl_easy *data, const char *fmt, ...)
-{
- (void)data; (void)fmt;
-}
-#endif
-#ifndef CURL_DISABLE_SMTP
-static void Curl_trc_smtp(struct Curl_easy *data, const char *fmt, ...)
-{
- (void)data; (void)fmt;
-}
-#endif
-#if !defined(CURL_DISABLE_WEBSOCKETS) || !defined(CURL_DISABLE_HTTP)
-static void Curl_trc_ws(struct Curl_easy *data, const char *fmt, ...)
-{
- (void)data; (void)fmt;
-}
-#endif
+#define CURL_MSTATE_NAME(x) ((void)(x), "-")
#endif /* !defined(CURL_DISABLE_VERBOSE_STRINGS) */
diff --git a/libs/libcurl/src/base64.c b/libs/libcurl/src/curlx/base64.c
index a60d4542b7..d988cc8e08 100644
--- a/libs/libcurl/src/base64.c
+++ b/libs/libcurl/src/curlx/base64.c
@@ -24,7 +24,7 @@
/* Base64 encoding/decoding */
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if !defined(CURL_DISABLE_HTTP_AUTH) || defined(USE_SSH) || \
!defined(CURL_DISABLE_LDAP) || \
@@ -32,21 +32,20 @@
!defined(CURL_DISABLE_POP3) || \
!defined(CURL_DISABLE_IMAP) || \
!defined(CURL_DISABLE_DIGEST_AUTH) || \
- !defined(CURL_DISABLE_DOH) || defined(USE_SSL) || defined(BUILDING_CURL)
-#include "curl/curl.h"
+ !defined(CURL_DISABLE_DOH) || defined(USE_SSL) || !defined(BUILDING_LIBCURL)
+#include <curl/curl.h>
#include "warnless.h"
-#include "curl_base64.h"
+#include "base64.h"
/* The last 2 #include files should be in this order */
#ifdef BUILDING_LIBCURL
-#include "curl_memory.h"
+#include "../curl_memory.h"
#endif
-#include "memdebug.h"
+#include "../memdebug.h"
/* ---- Base64 Encoding/Decoding Table --- */
-/* Padding character string starts at offset 64. */
-static const char base64encdec[]=
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+const char Curl_base64encdec[]=
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/* The Base 64 encoding with a URL and filename safe alphabet, RFC 4648
section 5 */
@@ -60,11 +59,11 @@ static const unsigned char decodetable[] =
29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51 };
/*
- * Curl_base64_decode()
+ * curlx_base64_decode()
*
- * Given a base64 NUL-terminated string at src, decode it and return a
- * pointer in *outptr to a newly allocated memory area holding decoded
- * data. Size of decoded data is returned in variable pointed by outlen.
+ * Given a base64 null-terminated string at src, decode it and return a
+ * pointer in *outptr to a newly allocated memory area holding decoded data.
+ * Size of decoded data is returned in variable pointed by outlen.
*
* Returns CURLE_OK on success, otherwise specific error code. Function
* output shall not be considered valid unless CURLE_OK is returned.
@@ -73,8 +72,8 @@ static const unsigned char decodetable[] =
*
* @unittest: 1302
*/
-CURLcode Curl_base64_decode(const char *src,
- unsigned char **outptr, size_t *outlen)
+CURLcode curlx_base64_decode(const char *src,
+ unsigned char **outptr, size_t *outlen)
{
size_t srclen = 0;
size_t padding = 0;
@@ -119,14 +118,6 @@ CURLcode Curl_base64_decode(const char *src,
memset(lookup, 0xff, sizeof(lookup));
memcpy(&lookup['+'], decodetable, sizeof(decodetable));
- /* replaces
- {
- unsigned char c;
- const unsigned char *p = (const unsigned char *)base64encdec;
- for(c = 0; *p; c++, p++)
- lookup[*p] = c;
- }
- */
/* Decode the complete quantums first */
for(i = 0; i < fullQuantums; i++) {
@@ -186,13 +177,13 @@ bad:
}
static CURLcode base64_encode(const char *table64,
+ unsigned char padbyte,
const char *inputbuff, size_t insize,
char **outptr, size_t *outlen)
{
char *output;
char *base64data;
- const unsigned char *in = (unsigned char *)inputbuff;
- const char *padstr = &table64[64]; /* Point to padding string. */
+ const unsigned char *in = (const unsigned char *)inputbuff;
*outptr = NULL;
*outlen = 0;
@@ -222,17 +213,17 @@ static CURLcode base64_encode(const char *table64,
*output++ = table64[ in[0] >> 2 ];
if(insize == 1) {
*output++ = table64[ ((in[0] & 0x03) << 4) ];
- if(*padstr) {
- *output++ = *padstr;
- *output++ = *padstr;
+ if(padbyte) {
+ *output++ = padbyte;
+ *output++ = padbyte;
}
}
else {
/* insize == 2 */
*output++ = table64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xF0) >> 4) ];
*output++ = table64[ ((in[1] & 0x0F) << 2) ];
- if(*padstr)
- *output++ = *padstr;
+ if(padbyte)
+ *output++ = padbyte;
}
}
@@ -249,45 +240,46 @@ static CURLcode base64_encode(const char *table64,
}
/*
- * Curl_base64_encode()
+ * curlx_base64_encode()
*
* Given a pointer to an input buffer and an input size, encode it and
* return a pointer in *outptr to a newly allocated memory area holding
* encoded data. Size of encoded data is returned in variable pointed by
* outlen.
*
- * Input length of 0 indicates input buffer holds a NUL-terminated string.
+ * Input length of 0 indicates input buffer holds a null-terminated string.
*
* Returns CURLE_OK on success, otherwise specific error code. Function
* output shall not be considered valid unless CURLE_OK is returned.
*
* @unittest: 1302
*/
-CURLcode Curl_base64_encode(const char *inputbuff, size_t insize,
- char **outptr, size_t *outlen)
+CURLcode curlx_base64_encode(const char *inputbuff, size_t insize,
+ char **outptr, size_t *outlen)
{
- return base64_encode(base64encdec, inputbuff, insize, outptr, outlen);
+ return base64_encode(Curl_base64encdec, '=',
+ inputbuff, insize, outptr, outlen);
}
/*
- * Curl_base64url_encode()
+ * curlx_base64url_encode()
*
* Given a pointer to an input buffer and an input size, encode it and
* return a pointer in *outptr to a newly allocated memory area holding
* encoded data. Size of encoded data is returned in variable pointed by
* outlen.
*
- * Input length of 0 indicates input buffer holds a NUL-terminated string.
+ * Input length of 0 indicates input buffer holds a null-terminated string.
*
* Returns CURLE_OK on success, otherwise specific error code. Function
* output shall not be considered valid unless CURLE_OK is returned.
*
* @unittest: 1302
*/
-CURLcode Curl_base64url_encode(const char *inputbuff, size_t insize,
- char **outptr, size_t *outlen)
+CURLcode curlx_base64url_encode(const char *inputbuff, size_t insize,
+ char **outptr, size_t *outlen)
{
- return base64_encode(base64url, inputbuff, insize, outptr, outlen);
+ return base64_encode(base64url, 0, inputbuff, insize, outptr, outlen);
}
#endif /* no users so disabled */
diff --git a/libs/libcurl/src/curl_base64.h b/libs/libcurl/src/curlx/base64.h
index 6f9d341ae4..ab458cfe6f 100644
--- a/libs/libcurl/src/curl_base64.h
+++ b/libs/libcurl/src/curlx/base64.h
@@ -24,18 +24,13 @@
*
***************************************************************************/
-#ifndef BUILDING_LIBCURL
-/* this renames functions so that the tool code can use the same code
- without getting symbol collisions */
-#define Curl_base64_encode(a,b,c,d) curlx_base64_encode(a,b,c,d)
-#define Curl_base64url_encode(a,b,c,d) curlx_base64url_encode(a,b,c,d)
-#define Curl_base64_decode(a,b,c) curlx_base64_decode(a,b,c)
-#endif
+CURLcode curlx_base64_encode(const char *inputbuff, size_t insize,
+ char **outptr, size_t *outlen);
+CURLcode curlx_base64url_encode(const char *inputbuff, size_t insize,
+ char **outptr, size_t *outlen);
+CURLcode curlx_base64_decode(const char *src,
+ unsigned char **outptr, size_t *outlen);
+
+extern const char Curl_base64encdec[];
-CURLcode Curl_base64_encode(const char *inputbuff, size_t insize,
- char **outptr, size_t *outlen);
-CURLcode Curl_base64url_encode(const char *inputbuff, size_t insize,
- char **outptr, size_t *outlen);
-CURLcode Curl_base64_decode(const char *src,
- unsigned char **outptr, size_t *outlen);
#endif /* HEADER_CURL_BASE64_H */
diff --git a/libs/libcurl/src/curlx.h b/libs/libcurl/src/curlx/curlx.h
index f6fb05d02e..bb1ad81b28 100644
--- a/libs/libcurl/src/curlx.h
+++ b/libs/libcurl/src/curlx/curlx.h
@@ -31,17 +31,6 @@
* be.
*/
-/* map standard printf functions to curl implementations */
-#include "curl_printf.h"
-
-#include "strcase.h"
-/* "strcase.h" provides the strcasecompare protos */
-
-#include "strtoofft.h"
-/* "strtoofft.h" provides this function: curlx_strtoofft(), returns a
- curl_off_t number from a given string.
-*/
-
#include "nonblock.h"
/* "nonblock.h" provides curlx_nonblock() */
@@ -53,8 +42,8 @@
curlx_uztosi()
*/
-#include "curl_multibyte.h"
-/* "curl_multibyte.h" provides these functions and macros:
+#include "multibyte.h"
+/* "multibyte.h" provides these functions and macros:
curlx_convert_UTF8_to_wchar()
curlx_convert_wchar_to_UTF8()
@@ -64,6 +53,22 @@
*/
#include "version_win32.h"
-/* "version_win32.h" provides curlx_verify_windows_version() */
+/* provides curlx_verify_windows_version() */
+
+#include "strparse.h"
+/* The curlx_str_* parsing functions */
+
+#include "dynbuf.h"
+/* The curlx_dyn_* functions */
+
+#include "base64.h"
+#include "timeval.h"
+#include "timediff.h"
+
+#include "winapi.h"
+/* for curlx_winapi_strerror */
+
+#include "inet_pton.h"
+/* for curlx_inet_pton */
#endif /* HEADER_CURL_CURLX_H */
diff --git a/libs/libcurl/src/dynbuf.c b/libs/libcurl/src/curlx/dynbuf.c
index f50fb055c1..fd25cc9f77 100644
--- a/libs/libcurl/src/dynbuf.c
+++ b/libs/libcurl/src/curlx/dynbuf.c
@@ -22,25 +22,28 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#include "dynbuf.h"
-#include "curl_printf.h"
+#include "../curl_printf.h"
#ifdef BUILDING_LIBCURL
-#include "curl_memory.h"
+#include "../curl_memory.h"
#endif
-#include "memdebug.h"
+#include "../memdebug.h"
#define MIN_FIRST_ALLOC 32
+#ifdef DEBUGBUILD
#define DYNINIT 0xbee51da /* random pattern */
+#endif
/*
* Init a dynbuf struct.
*/
-void Curl_dyn_init(struct dynbuf *s, size_t toobig)
+void curlx_dyn_init(struct dynbuf *s, size_t toobig)
{
DEBUGASSERT(s);
DEBUGASSERT(toobig);
+ DEBUGASSERT(toobig <= MAX_DYNBUF_SIZE); /* catch crazy mistakes */
s->bufr = NULL;
s->leng = 0;
s->allc = 0;
@@ -54,9 +57,10 @@ void Curl_dyn_init(struct dynbuf *s, size_t toobig)
* free the buffer and re-init the necessary fields. It does not touch the
* 'init' field and thus this buffer can be reused to add data to again.
*/
-void Curl_dyn_free(struct dynbuf *s)
+void curlx_dyn_free(struct dynbuf *s)
{
DEBUGASSERT(s);
+ DEBUGASSERT(s->init == DYNINIT);
Curl_safefree(s->bufr);
s->leng = s->allc = 0;
}
@@ -80,7 +84,7 @@ static CURLcode dyn_nappend(struct dynbuf *s,
DEBUGASSERT(!len || mem);
if(fit > s->toobig) {
- Curl_dyn_free(s);
+ curlx_dyn_free(s);
return CURLE_TOO_LARGE;
}
else if(!a) {
@@ -106,7 +110,7 @@ static CURLcode dyn_nappend(struct dynbuf *s,
include that as well when it uses this code */
void *p = realloc(s->bufr, a);
if(!p) {
- Curl_dyn_free(s);
+ curlx_dyn_free(s);
return CURLE_OUT_OF_MEMORY;
}
s->bufr = p;
@@ -124,7 +128,7 @@ static CURLcode dyn_nappend(struct dynbuf *s,
* Clears the string, keeps the allocation. This can also be called on a
* buffer that already was freed.
*/
-void Curl_dyn_reset(struct dynbuf *s)
+void curlx_dyn_reset(struct dynbuf *s)
{
DEBUGASSERT(s);
DEBUGASSERT(s->init == DYNINIT);
@@ -138,7 +142,7 @@ void Curl_dyn_reset(struct dynbuf *s)
* Specify the size of the tail to keep (number of bytes from the end of the
* buffer). The rest will be dropped.
*/
-CURLcode Curl_dyn_tail(struct dynbuf *s, size_t trail)
+CURLcode curlx_dyn_tail(struct dynbuf *s, size_t trail)
{
DEBUGASSERT(s);
DEBUGASSERT(s->init == DYNINIT);
@@ -148,7 +152,7 @@ CURLcode Curl_dyn_tail(struct dynbuf *s, size_t trail)
else if(trail == s->leng)
return CURLE_OK;
else if(!trail) {
- Curl_dyn_reset(s);
+ curlx_dyn_reset(s);
}
else {
memmove(&s->bufr[0], &s->bufr[s->leng - trail], trail);
@@ -162,7 +166,7 @@ CURLcode Curl_dyn_tail(struct dynbuf *s, size_t trail)
/*
* Appends a buffer with length.
*/
-CURLcode Curl_dyn_addn(struct dynbuf *s, const void *mem, size_t len)
+CURLcode curlx_dyn_addn(struct dynbuf *s, const void *mem, size_t len)
{
DEBUGASSERT(s);
DEBUGASSERT(s->init == DYNINIT);
@@ -173,7 +177,7 @@ CURLcode Curl_dyn_addn(struct dynbuf *s, const void *mem, size_t len)
/*
* Append a null-terminated string at the end.
*/
-CURLcode Curl_dyn_add(struct dynbuf *s, const char *str)
+CURLcode curlx_dyn_add(struct dynbuf *s, const char *str)
{
size_t n;
DEBUGASSERT(str);
@@ -181,13 +185,13 @@ CURLcode Curl_dyn_add(struct dynbuf *s, const char *str)
DEBUGASSERT(s->init == DYNINIT);
DEBUGASSERT(!s->leng || s->bufr);
n = strlen(str);
- return dyn_nappend(s, (unsigned char *)str, n);
+ return dyn_nappend(s, (const unsigned char *)str, n);
}
/*
* Append a string vprintf()-style
*/
-CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
+CURLcode curlx_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
{
#ifdef BUILDING_LIBCURL
int rc;
@@ -195,7 +199,7 @@ CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
DEBUGASSERT(s->init == DYNINIT);
DEBUGASSERT(!s->leng || s->bufr);
DEBUGASSERT(fmt);
- rc = Curl_dyn_vprintf(s, fmt, ap);
+ rc = curlx_dyn_vprintf(s, fmt, ap);
if(!rc)
return CURLE_OK;
@@ -204,15 +208,15 @@ CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
return CURLE_OUT_OF_MEMORY;
#else
char *str;
- str = vaprintf(fmt, ap); /* this allocs a new string to append */
+ str = curl_mvaprintf(fmt, ap); /* this allocs a new string to append */
if(str) {
- CURLcode result = dyn_nappend(s, (unsigned char *)str, strlen(str));
+ CURLcode result = dyn_nappend(s, (const unsigned char *)str, strlen(str));
free(str);
return result;
}
/* If we failed, we cleanup the whole buffer and return error */
- Curl_dyn_free(s);
+ curlx_dyn_free(s);
return CURLE_OUT_OF_MEMORY;
#endif
}
@@ -220,7 +224,7 @@ CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
/*
* Append a string printf()-style
*/
-CURLcode Curl_dyn_addf(struct dynbuf *s, const char *fmt, ...)
+CURLcode curlx_dyn_addf(struct dynbuf *s, const char *fmt, ...)
{
CURLcode result;
va_list ap;
@@ -228,7 +232,7 @@ CURLcode Curl_dyn_addf(struct dynbuf *s, const char *fmt, ...)
DEBUGASSERT(s->init == DYNINIT);
DEBUGASSERT(!s->leng || s->bufr);
va_start(ap, fmt);
- result = Curl_dyn_vaddf(s, fmt, ap);
+ result = curlx_dyn_vaddf(s, fmt, ap);
va_end(ap);
return result;
}
@@ -236,7 +240,7 @@ CURLcode Curl_dyn_addf(struct dynbuf *s, const char *fmt, ...)
/*
* Returns a pointer to the buffer.
*/
-char *Curl_dyn_ptr(const struct dynbuf *s)
+char *curlx_dyn_ptr(const struct dynbuf *s)
{
DEBUGASSERT(s);
DEBUGASSERT(s->init == DYNINIT);
@@ -244,10 +248,22 @@ char *Curl_dyn_ptr(const struct dynbuf *s)
return s->bufr;
}
+char *curlx_dyn_take(struct dynbuf *s, size_t *plen)
+{
+ char *ptr = s->bufr;
+ DEBUGASSERT(s);
+ DEBUGASSERT(s->init == DYNINIT);
+ *plen = s->leng;
+ s->bufr = NULL;
+ s->leng = 0;
+ s->allc = 0;
+ return ptr;
+}
+
/*
* Returns an unsigned pointer to the buffer.
*/
-unsigned char *Curl_dyn_uptr(const struct dynbuf *s)
+unsigned char *curlx_dyn_uptr(const struct dynbuf *s)
{
DEBUGASSERT(s);
DEBUGASSERT(s->init == DYNINIT);
@@ -258,7 +274,7 @@ unsigned char *Curl_dyn_uptr(const struct dynbuf *s)
/*
* Returns the length of the buffer.
*/
-size_t Curl_dyn_len(const struct dynbuf *s)
+size_t curlx_dyn_len(const struct dynbuf *s)
{
DEBUGASSERT(s);
DEBUGASSERT(s->init == DYNINIT);
@@ -269,7 +285,7 @@ size_t Curl_dyn_len(const struct dynbuf *s)
/*
* Set a new (smaller) length.
*/
-CURLcode Curl_dyn_setlen(struct dynbuf *s, size_t set)
+CURLcode curlx_dyn_setlen(struct dynbuf *s, size_t set)
{
DEBUGASSERT(s);
DEBUGASSERT(s->init == DYNINIT);
diff --git a/libs/libcurl/src/dynbuf.h b/libs/libcurl/src/curlx/dynbuf.h
index 2fd7d7e0ae..bd19caa343 100644
--- a/libs/libcurl/src/dynbuf.h
+++ b/libs/libcurl/src/curlx/dynbuf.h
@@ -26,24 +26,6 @@
#include <curl/curl.h>
-#ifndef BUILDING_LIBCURL
-/* this renames the functions so that the tool code can use the same code
- without getting symbol collisions */
-#define Curl_dyn_init(a,b) curlx_dyn_init(a,b)
-#define Curl_dyn_add(a,b) curlx_dyn_add(a,b)
-#define Curl_dyn_addn(a,b,c) curlx_dyn_addn(a,b,c)
-#define Curl_dyn_addf curlx_dyn_addf
-#define Curl_dyn_vaddf curlx_dyn_vaddf
-#define Curl_dyn_free(a) curlx_dyn_free(a)
-#define Curl_dyn_ptr(a) curlx_dyn_ptr(a)
-#define Curl_dyn_uptr(a) curlx_dyn_uptr(a)
-#define Curl_dyn_len(a) curlx_dyn_len(a)
-#define Curl_dyn_reset(a) curlx_dyn_reset(a)
-#define Curl_dyn_tail(a,b) curlx_dyn_tail(a,b)
-#define Curl_dyn_setlen(a,b) curlx_dyn_setlen(a,b)
-#define curlx_dynbuf dynbuf /* for the struct name */
-#endif
-
struct dynbuf {
char *bufr; /* point to a null-terminated allocated buffer */
size_t leng; /* number of bytes *EXCLUDING* the null-terminator */
@@ -54,28 +36,34 @@ struct dynbuf {
#endif
};
-void Curl_dyn_init(struct dynbuf *s, size_t toobig);
-void Curl_dyn_free(struct dynbuf *s);
-CURLcode Curl_dyn_addn(struct dynbuf *s, const void *mem, size_t len)
+void curlx_dyn_init(struct dynbuf *s, size_t toobig);
+void curlx_dyn_free(struct dynbuf *s);
+CURLcode curlx_dyn_addn(struct dynbuf *s, const void *mem, size_t len)
WARN_UNUSED_RESULT;
-CURLcode Curl_dyn_add(struct dynbuf *s, const char *str)
+CURLcode curlx_dyn_add(struct dynbuf *s, const char *str)
WARN_UNUSED_RESULT;
-CURLcode Curl_dyn_addf(struct dynbuf *s, const char *fmt, ...)
+CURLcode curlx_dyn_addf(struct dynbuf *s, const char *fmt, ...)
WARN_UNUSED_RESULT CURL_PRINTF(2, 3);
-CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
+CURLcode curlx_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
WARN_UNUSED_RESULT CURL_PRINTF(2, 0);
-void Curl_dyn_reset(struct dynbuf *s);
-CURLcode Curl_dyn_tail(struct dynbuf *s, size_t trail);
-CURLcode Curl_dyn_setlen(struct dynbuf *s, size_t set);
-char *Curl_dyn_ptr(const struct dynbuf *s);
-unsigned char *Curl_dyn_uptr(const struct dynbuf *s);
-size_t Curl_dyn_len(const struct dynbuf *s);
+void curlx_dyn_reset(struct dynbuf *s);
+CURLcode curlx_dyn_tail(struct dynbuf *s, size_t trail);
+CURLcode curlx_dyn_setlen(struct dynbuf *s, size_t set);
+char *curlx_dyn_ptr(const struct dynbuf *s);
+unsigned char *curlx_dyn_uptr(const struct dynbuf *s);
+size_t curlx_dyn_len(const struct dynbuf *s);
/* returns 0 on success, -1 on error */
/* The implementation of this function exists in mprintf.c */
-int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save);
+int curlx_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save);
+
+/* Take the buffer out of the dynbuf. Caller has ownership and
+ * dynbuf resets to initial state. */
+char *curlx_dyn_take(struct dynbuf *s, size_t *plen);
/* Dynamic buffer max sizes */
+#define MAX_DYNBUF_SIZE (SIZE_T_MAX/2)
+
#define DYN_DOH_RESPONSE 3000
#define DYN_DOH_CNAME 256
#define DYN_PAUSE_BUFFER (64 * 1024 * 1024)
@@ -90,4 +78,8 @@ int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save);
#define DYN_PINGPPONG_CMD (64*1024)
#define DYN_IMAP_CMD (64*1024)
#define DYN_MQTT_RECV (64*1024)
+#define DYN_MQTT_SEND 0xFFFFFFF
+#define DYN_CRLFILE_SIZE (400*1024*1024) /* 400mb */
+#define DYN_CERTFILE_SIZE (100*1024) /* 100KiB */
+#define DYN_KEYFILE_SIZE (100*1024) /* 100KiB */
#endif
diff --git a/libs/libcurl/src/inet_pton.c b/libs/libcurl/src/curlx/inet_pton.c
index cf0027648a..0e32e129da 100644
--- a/libs/libcurl/src/inet_pton.c
+++ b/libs/libcurl/src/curlx/inet_pton.c
@@ -18,7 +18,9 @@
* SPDX-License-Identifier: ISC
*/
-#include "curl_setup.h"
+#include "../curl_setup.h"
+#include "../curl_ctype.h"
+#include "strparse.h"
#ifndef HAVE_INET_PTON
@@ -72,16 +74,16 @@ static int inet_pton6(const char *src, unsigned char *dst);
* Paul Vixie, 1996.
*/
int
-Curl_inet_pton(int af, const char *src, void *dst)
+curlx_inet_pton(int af, const char *src, void *dst)
{
switch(af) {
case AF_INET:
- return (inet_pton4(src, (unsigned char *)dst));
+ return inet_pton4(src, (unsigned char *)dst);
case AF_INET6:
- return (inet_pton6(src, (unsigned char *)dst));
+ return inet_pton6(src, (unsigned char *)dst);
default:
- errno = EAFNOSUPPORT;
- return (-1);
+ CURL_SETERRNO(SOCKEAFNOSUPPORT);
+ return -1;
}
/* NOTREACHED */
}
@@ -99,7 +101,6 @@ Curl_inet_pton(int af, const char *src, void *dst)
static int
inet_pton4(const char *src, unsigned char *dst)
{
- static const char digits[] = "0123456789";
int saw_digit, octets, ch;
unsigned char tmp[INADDRSZ], *tp;
@@ -108,37 +109,33 @@ inet_pton4(const char *src, unsigned char *dst)
tp = tmp;
*tp = 0;
while((ch = *src++) != '\0') {
- const char *pch;
-
- pch = strchr(digits, ch);
- if(pch) {
- unsigned int val = (unsigned int)(*tp * 10) +
- (unsigned int)(pch - digits);
+ if(ISDIGIT(ch)) {
+ unsigned int val = (*tp * 10) + (ch - '0');
if(saw_digit && *tp == 0)
- return (0);
+ return 0;
if(val > 255)
- return (0);
+ return 0;
*tp = (unsigned char)val;
if(!saw_digit) {
if(++octets > 4)
- return (0);
+ return 0;
saw_digit = 1;
}
}
else if(ch == '.' && saw_digit) {
if(octets == 4)
- return (0);
+ return 0;
*++tp = 0;
saw_digit = 0;
}
else
- return (0);
+ return 0;
}
if(octets < 4)
- return (0);
+ return 0;
memcpy(dst, tmp, INADDRSZ);
- return (1);
+ return 1;
}
/* int
@@ -157,8 +154,6 @@ inet_pton4(const char *src, unsigned char *dst)
static int
inet_pton6(const char *src, unsigned char *dst)
{
- static const char xdigits_l[] = "0123456789abcdef",
- xdigits_u[] = "0123456789ABCDEF";
unsigned char tmp[IN6ADDRSZ], *tp, *endp, *colonp;
const char *curtok;
int ch, saw_xdigit;
@@ -170,34 +165,28 @@ inet_pton6(const char *src, unsigned char *dst)
/* Leading :: requires some special handling. */
if(*src == ':')
if(*++src != ':')
- return (0);
+ return 0;
curtok = src;
saw_xdigit = 0;
val = 0;
while((ch = *src++) != '\0') {
- const char *xdigits;
- const char *pch;
-
- pch = strchr((xdigits = xdigits_l), ch);
- if(!pch)
- pch = strchr((xdigits = xdigits_u), ch);
- if(pch) {
+ if(ISXDIGIT(ch)) {
val <<= 4;
- val |= (pch - xdigits);
+ val |= Curl_hexval(ch);
if(++saw_xdigit > 4)
- return (0);
+ return 0;
continue;
}
if(ch == ':') {
curtok = src;
if(!saw_xdigit) {
if(colonp)
- return (0);
+ return 0;
colonp = tp;
continue;
}
if(tp + INT16SZ > endp)
- return (0);
+ return 0;
*tp++ = (unsigned char) ((val >> 8) & 0xff);
*tp++ = (unsigned char) (val & 0xff);
saw_xdigit = 0;
@@ -210,11 +199,11 @@ inet_pton6(const char *src, unsigned char *dst)
saw_xdigit = 0;
break; /* '\0' was seen by inet_pton4(). */
}
- return (0);
+ return 0;
}
if(saw_xdigit) {
if(tp + INT16SZ > endp)
- return (0);
+ return 0;
*tp++ = (unsigned char) ((val >> 8) & 0xff);
*tp++ = (unsigned char) (val & 0xff);
}
@@ -227,7 +216,7 @@ inet_pton6(const char *src, unsigned char *dst)
ssize_t i;
if(tp == endp)
- return (0);
+ return 0;
for(i = 1; i <= n; i++) {
*(endp - i) = *(colonp + n - i);
*(colonp + n - i) = 0;
@@ -235,9 +224,9 @@ inet_pton6(const char *src, unsigned char *dst)
tp = endp;
}
if(tp != endp)
- return (0);
+ return 0;
memcpy(dst, tmp, IN6ADDRSZ);
- return (1);
+ return 1;
}
#endif /* HAVE_INET_PTON */
diff --git a/libs/libcurl/src/inet_pton.h b/libs/libcurl/src/curlx/inet_pton.h
index c3dc5d0415..4e9c87922e 100644
--- a/libs/libcurl/src/inet_pton.h
+++ b/libs/libcurl/src/curlx/inet_pton.h
@@ -24,15 +24,25 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
-int Curl_inet_pton(int, const char *, void *);
+int curlx_inet_pton(int, const char *, void *);
#ifdef HAVE_INET_PTON
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
-#define Curl_inet_pton(x,y,z) inet_pton(x,y,z)
+#ifdef __AMIGA__
+#define curlx_inet_pton(x,y,z) inet_pton(x,(unsigned char *)CURL_UNCONST(y),z)
+#else
+#define curlx_inet_pton(x,y,z) inet_pton(x,y,z)
+#endif
#endif
#endif /* HEADER_CURL_INET_PTON_H */
diff --git a/libs/libcurl/src/curlx/multibyte.c b/libs/libcurl/src/curlx/multibyte.c
new file mode 100644
index 0000000000..3337397a56
--- /dev/null
+++ b/libs/libcurl/src/curlx/multibyte.c
@@ -0,0 +1,360 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+/*
+ * This file is 'mem-include-scan' clean, which means its memory allocations
+ * are not tracked by the curl memory tracker memdebug, so they must not use
+ * `CURLDEBUG` macro replacements in memdebug.h for free, malloc, etc. To avoid
+ * these macro replacements, wrap the names in parentheses to call the original
+ * versions: `ptr = (malloc)(123)`, `(free)(ptr)`, etc.
+ */
+
+#include "../curl_setup.h"
+
+#ifdef _WIN32
+
+#include "multibyte.h"
+
+/*
+ * MultiByte conversions using Windows kernel32 library.
+ */
+
+wchar_t *curlx_convert_UTF8_to_wchar(const char *str_utf8)
+{
+ wchar_t *str_w = NULL;
+
+ if(str_utf8) {
+ int str_w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
+ str_utf8, -1, NULL, 0);
+ if(str_w_len > 0) {
+ str_w = (malloc)(str_w_len * sizeof(wchar_t));
+ if(str_w) {
+ if(MultiByteToWideChar(CP_UTF8, 0, str_utf8, -1, str_w,
+ str_w_len) == 0) {
+ (free)(str_w);
+ return NULL;
+ }
+ }
+ }
+ }
+
+ return str_w;
+}
+
+char *curlx_convert_wchar_to_UTF8(const wchar_t *str_w)
+{
+ char *str_utf8 = NULL;
+
+ if(str_w) {
+ int bytes = WideCharToMultiByte(CP_UTF8, 0, str_w, -1,
+ NULL, 0, NULL, NULL);
+ if(bytes > 0) {
+ str_utf8 = (malloc)(bytes);
+ if(str_utf8) {
+ if(WideCharToMultiByte(CP_UTF8, 0, str_w, -1, str_utf8, bytes,
+ NULL, NULL) == 0) {
+ (free)(str_utf8);
+ return NULL;
+ }
+ }
+ }
+ }
+
+ return str_utf8;
+}
+
+#ifndef UNDER_CE
+
+/* declare GetFullPathNameW for mingw-w64 UWP builds targeting old windows */
+#if defined(CURL_WINDOWS_UWP) && defined(__MINGW32__) && \
+ (_WIN32_WINNT < _WIN32_WINNT_WIN10)
+WINBASEAPI DWORD WINAPI GetFullPathNameW(LPCWSTR, DWORD, LPWSTR, LPWSTR *);
+#endif
+
+/* Fix excessive paths (paths that exceed MAX_PATH length of 260).
+ *
+ * This is a helper function to fix paths that would exceed the MAX_PATH
+ * limitation check done by Windows APIs. It does so by normalizing the passed
+ * in filename or path 'in' to its full canonical path, and if that path is
+ * longer than MAX_PATH then setting 'out' to "\\?\" prefix + that full path.
+ *
+ * For example 'in' filename255chars in current directory C:\foo\bar is
+ * fixed as \\?\C:\foo\bar\filename255chars for 'out' which will tell Windows
+ * it is ok to access that filename even though the actual full path is longer
+ * than 260 chars.
+ *
+ * For non-Unicode builds this function may fail sometimes because only the
+ * Unicode versions of some Windows API functions can access paths longer than
+ * MAX_PATH, for example GetFullPathNameW which is used in this function. When
+ * the full path is then converted from Unicode to multibyte that fails if any
+ * directories in the path contain characters not in the current codepage.
+ */
+static bool fix_excessive_path(const TCHAR *in, TCHAR **out)
+{
+ size_t needed, count;
+ const wchar_t *in_w;
+ wchar_t *fbuf = NULL;
+
+ /* MS documented "approximate" limit for the maximum path length */
+ const size_t max_path_len = 32767;
+
+#ifndef _UNICODE
+ wchar_t *ibuf = NULL;
+ char *obuf = NULL;
+#endif
+
+ *out = NULL;
+
+ /* skip paths already normalized */
+ if(!_tcsncmp(in, _T("\\\\?\\"), 4))
+ goto cleanup;
+
+#ifndef _UNICODE
+ /* convert multibyte input to unicode */
+ needed = mbstowcs(NULL, in, 0);
+ if(needed == (size_t)-1 || needed >= max_path_len)
+ goto cleanup;
+ ++needed; /* for NUL */
+ ibuf = (malloc)(needed * sizeof(wchar_t));
+ if(!ibuf)
+ goto cleanup;
+ count = mbstowcs(ibuf, in, needed);
+ if(count == (size_t)-1 || count >= needed)
+ goto cleanup;
+ in_w = ibuf;
+#else
+ in_w = in;
+#endif
+
+ /* GetFullPathNameW returns the normalized full path in unicode. It converts
+ forward slashes to backslashes, processes .. to remove directory segments,
+ etc. Unlike GetFullPathNameA it can process paths that exceed MAX_PATH. */
+ needed = (size_t)GetFullPathNameW(in_w, 0, NULL, NULL);
+ if(!needed || needed > max_path_len)
+ goto cleanup;
+ /* skip paths that are not excessive and do not need modification */
+ if(needed <= MAX_PATH)
+ goto cleanup;
+ fbuf = (malloc)(needed * sizeof(wchar_t));
+ if(!fbuf)
+ goto cleanup;
+ count = (size_t)GetFullPathNameW(in_w, (DWORD)needed, fbuf, NULL);
+ if(!count || count >= needed)
+ goto cleanup;
+
+ /* prepend \\?\ or \\?\UNC\ to the excessively long path.
+ *
+ * c:\longpath ---> \\?\c:\longpath
+ * \\.\c:\longpath ---> \\?\c:\longpath
+ * \\?\c:\longpath ---> \\?\c:\longpath (unchanged)
+ * \\server\c$\longpath ---> \\?\UNC\server\c$\longpath
+ *
+ * https://learn.microsoft.com/en-us/dotnet/standard/io/file-path-formats
+ */
+ if(!wcsncmp(fbuf, L"\\\\?\\", 4))
+ ; /* do nothing */
+ else if(!wcsncmp(fbuf, L"\\\\.\\", 4))
+ fbuf[2] = '?';
+ else if(!wcsncmp(fbuf, L"\\\\.", 3) || !wcsncmp(fbuf, L"\\\\?", 3)) {
+ /* Unexpected, not UNC. The formatting doc doesn't allow this AFAICT. */
+ goto cleanup;
+ }
+ else {
+ wchar_t *temp;
+
+ if(!wcsncmp(fbuf, L"\\\\", 2)) {
+ /* "\\?\UNC\" + full path without "\\" + null */
+ needed = 8 + (count - 2) + 1;
+ if(needed > max_path_len)
+ goto cleanup;
+
+ temp = (malloc)(needed * sizeof(wchar_t));
+ if(!temp)
+ goto cleanup;
+
+ wcsncpy(temp, L"\\\\?\\UNC\\", 8);
+ wcscpy(temp + 8, fbuf + 2);
+ }
+ else {
+ /* "\\?\" + full path + null */
+ needed = 4 + count + 1;
+ if(needed > max_path_len)
+ goto cleanup;
+
+ temp = (malloc)(needed * sizeof(wchar_t));
+ if(!temp)
+ goto cleanup;
+
+ wcsncpy(temp, L"\\\\?\\", 4);
+ wcscpy(temp + 4, fbuf);
+ }
+
+ (free)(fbuf);
+ fbuf = temp;
+ }
+
+#ifndef _UNICODE
+ /* convert unicode full path to multibyte output */
+ needed = wcstombs(NULL, fbuf, 0);
+ if(needed == (size_t)-1 || needed >= max_path_len)
+ goto cleanup;
+ ++needed; /* for NUL */
+ obuf = (malloc)(needed);
+ if(!obuf)
+ goto cleanup;
+ count = wcstombs(obuf, fbuf, needed);
+ if(count == (size_t)-1 || count >= needed)
+ goto cleanup;
+ *out = obuf;
+ obuf = NULL;
+#else
+ *out = fbuf;
+ fbuf = NULL;
+#endif
+
+cleanup:
+ (free)(fbuf);
+#ifndef _UNICODE
+ (free)(ibuf);
+ (free)(obuf);
+#endif
+ return *out ? true : false;
+}
+
+int curlx_win32_open(const char *filename, int oflag, ...)
+{
+ int pmode = 0;
+ int result = -1;
+ TCHAR *fixed = NULL;
+ const TCHAR *target = NULL;
+
+#ifdef _UNICODE
+ wchar_t *filename_w = curlx_convert_UTF8_to_wchar(filename);
+#endif
+
+ va_list param;
+ va_start(param, oflag);
+ if(oflag & O_CREAT)
+ pmode = va_arg(param, int);
+ va_end(param);
+
+#ifdef _UNICODE
+ if(filename_w) {
+ if(fix_excessive_path(filename_w, &fixed))
+ target = fixed;
+ else
+ target = filename_w;
+ result = _wopen(target, oflag, pmode);
+ curlx_unicodefree(filename_w);
+ }
+ else
+ /* !checksrc! disable ERRNOVAR 1 */
+ CURL_SETERRNO(EINVAL);
+#else
+ if(fix_excessive_path(filename, &fixed))
+ target = fixed;
+ else
+ target = filename;
+ result = _open(target, oflag, pmode);
+#endif
+
+ (free)(fixed);
+ return result;
+}
+
+FILE *curlx_win32_fopen(const char *filename, const char *mode)
+{
+ FILE *result = NULL;
+ TCHAR *fixed = NULL;
+ const TCHAR *target = NULL;
+
+#ifdef _UNICODE
+ wchar_t *filename_w = curlx_convert_UTF8_to_wchar(filename);
+ wchar_t *mode_w = curlx_convert_UTF8_to_wchar(mode);
+ if(filename_w && mode_w) {
+ if(fix_excessive_path(filename_w, &fixed))
+ target = fixed;
+ else
+ target = filename_w;
+ result = _wfopen(target, mode_w);
+ }
+ else
+ /* !checksrc! disable ERRNOVAR 1 */
+ CURL_SETERRNO(EINVAL);
+ curlx_unicodefree(filename_w);
+ curlx_unicodefree(mode_w);
+#else
+ if(fix_excessive_path(filename, &fixed))
+ target = fixed;
+ else
+ target = filename;
+ result = (fopen)(target, mode);
+#endif
+
+ (free)(fixed);
+ return result;
+}
+
+int curlx_win32_stat(const char *path, struct_stat *buffer)
+{
+ int result = -1;
+ TCHAR *fixed = NULL;
+ const TCHAR *target = NULL;
+
+#ifdef _UNICODE
+ wchar_t *path_w = curlx_convert_UTF8_to_wchar(path);
+ if(path_w) {
+ if(fix_excessive_path(path_w, &fixed))
+ target = fixed;
+ else
+ target = path_w;
+#ifndef USE_WIN32_LARGE_FILES
+ result = _wstat(target, buffer);
+#else
+ result = _wstati64(target, buffer);
+#endif
+ curlx_unicodefree(path_w);
+ }
+ else
+ /* !checksrc! disable ERRNOVAR 1 */
+ CURL_SETERRNO(EINVAL);
+#else
+ if(fix_excessive_path(path, &fixed))
+ target = fixed;
+ else
+ target = path;
+#ifndef USE_WIN32_LARGE_FILES
+ result = _stat(target, buffer);
+#else
+ result = _stati64(target, buffer);
+#endif
+#endif
+
+ (free)(fixed);
+ return result;
+}
+
+#endif /* UNDER_CE */
+
+#endif /* _WIN32 */
diff --git a/libs/libcurl/src/curl_multibyte.h b/libs/libcurl/src/curlx/multibyte.h
index 449d0ac97f..3f8c04bb70 100644
--- a/libs/libcurl/src/curl_multibyte.h
+++ b/libs/libcurl/src/curlx/multibyte.h
@@ -23,9 +23,9 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
-#if defined(_WIN32)
+#ifdef _WIN32
/*
* MultiByte conversions using Windows kernel32 library.
@@ -81,12 +81,7 @@ typedef union {
#endif /* UNICODE && _WIN32 */
-#define curlx_unicodefree(ptr) \
- do { \
- if(ptr) { \
- (free)(ptr); \
- (ptr) = NULL; \
- } \
- } while(0)
+/* the purpose of this macro is to free() without being traced by memdebug */
+#define curlx_unicodefree(ptr) (free)(CURL_UNCONST(ptr))
#endif /* HEADER_CURL_MULTIBYTE_H */
diff --git a/libs/libcurl/src/nonblock.c b/libs/libcurl/src/curlx/nonblock.c
index c4d745f543..4f8153f140 100644
--- a/libs/libcurl/src/nonblock.c
+++ b/libs/libcurl/src/curlx/nonblock.c
@@ -22,7 +22,7 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
@@ -63,6 +63,12 @@ int curlx_nonblock(curl_socket_t sockfd, /* operate on this */
flags &= ~O_NONBLOCK;
return sfcntl(sockfd, F_SETFL, flags);
+#elif defined(HAVE_IOCTLSOCKET_CAMEL_FIONBIO)
+
+ /* Amiga */
+ long flags = nonblock ? 1L : 0L;
+ return IoctlSocket(sockfd, FIONBIO, (char *)&flags);
+
#elif defined(HAVE_IOCTL_FIONBIO)
/* older Unix versions */
@@ -75,12 +81,6 @@ int curlx_nonblock(curl_socket_t sockfd, /* operate on this */
unsigned long flags = nonblock ? 1UL : 0UL;
return ioctlsocket(sockfd, (long)FIONBIO, &flags);
-#elif defined(HAVE_IOCTLSOCKET_CAMEL_FIONBIO)
-
- /* Amiga */
- long flags = nonblock ? 1L : 0L;
- return IoctlSocket(sockfd, FIONBIO, (char *)&flags);
-
#elif defined(HAVE_SETSOCKOPT_SO_NONBLOCK)
/* Orbis OS */
diff --git a/libs/libcurl/src/nonblock.h b/libs/libcurl/src/curlx/nonblock.h
index 9834ccff01..9834ccff01 100644
--- a/libs/libcurl/src/nonblock.h
+++ b/libs/libcurl/src/curlx/nonblock.h
diff --git a/libs/libcurl/src/curlx/strparse.c b/libs/libcurl/src/curlx/strparse.c
new file mode 100644
index 0000000000..b92959ec2c
--- /dev/null
+++ b/libs/libcurl/src/curlx/strparse.c
@@ -0,0 +1,303 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "strparse.h"
+#include "../strcase.h"
+
+void curlx_str_init(struct Curl_str *out)
+{
+ out->str = NULL;
+ out->len = 0;
+}
+
+void curlx_str_assign(struct Curl_str *out, const char *str, size_t len)
+{
+ out->str = str;
+ out->len = len;
+}
+
+/* Get a word until the first DELIM or end of string. At least one byte long.
+ return non-zero on error */
+int curlx_str_until(const char **linep, struct Curl_str *out,
+ const size_t max, char delim)
+{
+ const char *s = *linep;
+ size_t len = 0;
+ DEBUGASSERT(linep && *linep && out && max && delim);
+
+ curlx_str_init(out);
+ while(*s && (*s != delim)) {
+ s++;
+ if(++len > max) {
+ return STRE_BIG;
+ }
+ }
+ if(!len)
+ return STRE_SHORT;
+ out->str = *linep;
+ out->len = len;
+ *linep = s; /* point to the first byte after the word */
+ return STRE_OK;
+}
+
+/* Get a word until the first space or end of string. At least one byte long.
+ return non-zero on error */
+int curlx_str_word(const char **linep, struct Curl_str *out,
+ const size_t max)
+{
+ return curlx_str_until(linep, out, max, ' ');
+}
+
+/* Get a word until a newline byte or end of string. At least one byte long.
+ return non-zero on error */
+int curlx_str_untilnl(const char **linep, struct Curl_str *out,
+ const size_t max)
+{
+ const char *s = *linep;
+ size_t len = 0;
+ DEBUGASSERT(linep && *linep && out && max);
+
+ curlx_str_init(out);
+ while(*s && !ISNEWLINE(*s)) {
+ s++;
+ if(++len > max)
+ return STRE_BIG;
+ }
+ if(!len)
+ return STRE_SHORT;
+ out->str = *linep;
+ out->len = len;
+ *linep = s; /* point to the first byte after the word */
+ return STRE_OK;
+}
+
+
+/* Get a "quoted" word. No escaping possible.
+ return non-zero on error */
+int curlx_str_quotedword(const char **linep, struct Curl_str *out,
+ const size_t max)
+{
+ const char *s = *linep;
+ size_t len = 0;
+ DEBUGASSERT(linep && *linep && out && max);
+
+ curlx_str_init(out);
+ if(*s != '\"')
+ return STRE_BEGQUOTE;
+ s++;
+ while(*s && (*s != '\"')) {
+ s++;
+ if(++len > max)
+ return STRE_BIG;
+ }
+ if(*s != '\"')
+ return STRE_ENDQUOTE;
+ out->str = (*linep) + 1;
+ out->len = len;
+ *linep = s + 1;
+ return STRE_OK;
+}
+
+/* Advance over a single character.
+ return non-zero on error */
+int curlx_str_single(const char **linep, char byte)
+{
+ DEBUGASSERT(linep && *linep);
+ if(**linep != byte)
+ return STRE_BYTE;
+ (*linep)++; /* move over it */
+ return STRE_OK;
+}
+
+/* Advance over a single space.
+ return non-zero on error */
+int curlx_str_singlespace(const char **linep)
+{
+ return curlx_str_single(linep, ' ');
+}
+
+/* given an ASCII character and max ascii, return TRUE if valid */
+#define valid_digit(x,m) \
+ (((x) >= '0') && ((x) <= m) && Curl_hexasciitable[(x)-'0'])
+
+/* We use 16 for the zero index (and the necessary bitwise AND in the loop)
+ to be able to have a non-zero value there to make valid_digit() able to
+ use the info */
+const unsigned char Curl_hexasciitable[] = {
+ 16, 1, 2, 3, 4, 5, 6, 7, 8, 9, /* 0x30: 0 - 9 */
+ 0, 0, 0, 0, 0, 0, 0,
+ 10, 11, 12, 13, 14, 15, /* 0x41: A - F */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 10, 11, 12, 13, 14, 15 /* 0x61: a - f */
+};
+
+/* no support for 0x prefix nor leading spaces */
+static int str_num_base(const char **linep, curl_off_t *nump, curl_off_t max,
+ int base) /* 8, 10 or 16, nothing else */
+{
+ curl_off_t num = 0;
+ const char *p;
+ int m = (base == 10) ? '9' : /* the largest digit possible */
+ (base == 16) ? 'f' : '7';
+ DEBUGASSERT(linep && *linep && nump);
+ DEBUGASSERT((base == 8) || (base == 10) || (base == 16));
+ DEBUGASSERT(max >= 0); /* mostly to catch SIZE_T_MAX, which is too large */
+ *nump = 0;
+ p = *linep;
+ if(!valid_digit(*p, m))
+ return STRE_NO_NUM;
+ if(max < base) {
+ /* special-case low max scenario because check needs to be different */
+ do {
+ int n = Curl_hexval(*p++);
+ num = num * base + n;
+ if(num > max)
+ return STRE_OVERFLOW;
+ } while(valid_digit(*p, m));
+ }
+ else {
+ do {
+ int n = Curl_hexval(*p++);
+ if(num > ((max - n) / base))
+ return STRE_OVERFLOW;
+ num = num * base + n;
+ } while(valid_digit(*p, m));
+ }
+ *nump = num;
+ *linep = p;
+ return STRE_OK;
+}
+
+/* Get an unsigned decimal number with no leading space or minus. Leading
+ zeroes are accepted. return non-zero on error */
+int curlx_str_number(const char **linep, curl_off_t *nump, curl_off_t max)
+{
+ return str_num_base(linep, nump, max, 10);
+}
+
+/* Get an unsigned hexadecimal number with no leading space or minus and no
+ "0x" support. Leading zeroes are accepted. return non-zero on error */
+int curlx_str_hex(const char **linep, curl_off_t *nump, curl_off_t max)
+{
+ return str_num_base(linep, nump, max, 16);
+}
+
+/* Get an unsigned octal number with no leading space or minus and no "0"
+ prefix support. Leading zeroes are accepted. return non-zero on error */
+int curlx_str_octal(const char **linep, curl_off_t *nump, curl_off_t max)
+{
+ return str_num_base(linep, nump, max, 8);
+}
+
+/*
+ * Parse a positive number up to 63-bit number written in ASCII. Skip leading
+ * blanks. No support for prefixes.
+ */
+int curlx_str_numblanks(const char **str, curl_off_t *num)
+{
+ curlx_str_passblanks(str);
+ return curlx_str_number(str, num, CURL_OFF_T_MAX);
+}
+
+/* CR or LF
+ return non-zero on error */
+int curlx_str_newline(const char **linep)
+{
+ DEBUGASSERT(linep && *linep);
+ if(ISNEWLINE(**linep)) {
+ (*linep)++;
+ return STRE_OK; /* yessir */
+ }
+ return STRE_NEWLINE;
+}
+
+#ifndef WITHOUT_LIBCURL
+/* case insensitive compare that the parsed string matches the given string.
+ Returns non-zero on match. */
+int curlx_str_casecompare(struct Curl_str *str, const char *check)
+{
+ size_t clen = check ? strlen(check) : 0;
+ return ((str->len == clen) && strncasecompare(str->str, check, clen));
+}
+#endif
+
+/* case sensitive string compare. Returns non-zero on match. */
+int curlx_str_cmp(struct Curl_str *str, const char *check)
+{
+ if(check) {
+ size_t clen = strlen(check);
+ return ((str->len == clen) && !strncmp(str->str, check, clen));
+ }
+ return !!(str->len);
+}
+
+/* Trim off 'num' number of bytes from the beginning (left side) of the
+ string. If 'num' is larger than the string, return error. */
+int curlx_str_nudge(struct Curl_str *str, size_t num)
+{
+ if(num <= str->len) {
+ str->str += num;
+ str->len -= num;
+ return STRE_OK;
+ }
+ return STRE_OVERFLOW;
+}
+
+/* Get the following character sequence that consists only of bytes not
+ present in the 'reject' string. Like strcspn(). */
+int curlx_str_cspn(const char **linep, struct Curl_str *out,
+ const char *reject)
+{
+ const char *s = *linep;
+ size_t len;
+ DEBUGASSERT(linep && *linep);
+
+ len = strcspn(s, reject);
+ if(len) {
+ out->str = s;
+ out->len = len;
+ *linep = &s[len];
+ return STRE_OK;
+ }
+ curlx_str_init(out);
+ return STRE_SHORT;
+}
+
+/* remove ISBLANK()s from both ends of the string */
+void curlx_str_trimblanks(struct Curl_str *out)
+{
+ while(out->len && ISBLANK(*out->str))
+ curlx_str_nudge(out, 1);
+
+ /* trim trailing spaces and tabs */
+ while(out->len && ISBLANK(out->str[out->len - 1]))
+ out->len--;
+}
+
+/* increase the pointer until it has moved over all blanks */
+void curlx_str_passblanks(const char **linep)
+{
+ while(ISBLANK(**linep))
+ (*linep)++; /* move over it */
+}
diff --git a/libs/libcurl/src/curlx/strparse.h b/libs/libcurl/src/curlx/strparse.h
new file mode 100644
index 0000000000..585e058872
--- /dev/null
+++ b/libs/libcurl/src/curlx/strparse.h
@@ -0,0 +1,112 @@
+#ifndef HEADER_CURL_STRPARSE_H
+#define HEADER_CURL_STRPARSE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "../curl_setup.h"
+
+#define STRE_OK 0
+#define STRE_BIG 1
+#define STRE_SHORT 2
+#define STRE_BEGQUOTE 3
+#define STRE_ENDQUOTE 4
+#define STRE_BYTE 5
+#define STRE_NEWLINE 6
+#define STRE_OVERFLOW 7
+#define STRE_NO_NUM 8
+
+/* public struct, but all accesses should be done using the provided
+ functions */
+struct Curl_str {
+ const char *str;
+ size_t len;
+};
+
+void curlx_str_init(struct Curl_str *out);
+void curlx_str_assign(struct Curl_str *out, const char *str, size_t len);
+
+#define curlx_str(x) ((x)->str)
+#define curlx_strlen(x) ((x)->len)
+
+/* Get a word until the first space
+ return non-zero on error */
+int curlx_str_word(const char **linep, struct Curl_str *out, const size_t max);
+
+/* Get a word until the first DELIM or end of string
+ return non-zero on error */
+int curlx_str_until(const char **linep, struct Curl_str *out, const size_t max,
+ char delim);
+
+/* Get a word until a newline byte or end of string. At least one byte long.
+ return non-zero on error */
+int curlx_str_untilnl(const char **linep, struct Curl_str *out,
+ const size_t max);
+
+/* Get a "quoted" word. No escaping possible.
+ return non-zero on error */
+int curlx_str_quotedword(const char **linep, struct Curl_str *out,
+ const size_t max);
+
+/* Advance over a single character.
+ return non-zero on error */
+int curlx_str_single(const char **linep, char byte);
+
+/* Advance over a single space.
+ return non-zero on error */
+int curlx_str_singlespace(const char **linep);
+
+/* Get an unsigned decimal number. Return non-zero on error */
+int curlx_str_number(const char **linep, curl_off_t *nump, curl_off_t max);
+
+/* As above with CURL_OFF_T_MAX but also pass leading blanks */
+int curlx_str_numblanks(const char **str, curl_off_t *num);
+
+/* Get an unsigned hexadecimal number. Return non-zero on error */
+int curlx_str_hex(const char **linep, curl_off_t *nump, curl_off_t max);
+
+/* Get an unsigned octal number. Return non-zero on error */
+int curlx_str_octal(const char **linep, curl_off_t *nump, curl_off_t max);
+
+/* Check for CR or LF
+ return non-zero on error */
+int curlx_str_newline(const char **linep);
+
+/* case insensitive compare that the parsed string matches the
+ given string. */
+int curlx_str_casecompare(struct Curl_str *str, const char *check);
+int curlx_str_cmp(struct Curl_str *str, const char *check);
+
+int curlx_str_nudge(struct Curl_str *str, size_t num);
+
+int curlx_str_cspn(const char **linep, struct Curl_str *out, const char *cspn);
+void curlx_str_trimblanks(struct Curl_str *out);
+void curlx_str_passblanks(const char **linep);
+
+/* given a hexadecimal letter, return the binary value. '0' returns 0, 'a'
+ returns 10. THIS ONLY WORKS ON VALID HEXADECIMAL LETTER INPUT. Verify
+ before calling this!
+*/
+extern const unsigned char Curl_hexasciitable[];
+#define Curl_hexval(x) (unsigned char)(Curl_hexasciitable[(x) - '0'] & 0x0f)
+
+#endif /* HEADER_CURL_STRPARSE_H */
diff --git a/libs/libcurl/src/timediff.c b/libs/libcurl/src/curlx/timediff.c
index a98d5782c1..641a15c404 100644
--- a/libs/libcurl/src/timediff.c
+++ b/libs/libcurl/src/curlx/timediff.c
@@ -44,7 +44,7 @@ struct timeval *curlx_mstotv(struct timeval *tv, timediff_t ms)
if(ms > 0) {
timediff_t tv_sec = ms / 1000;
- timediff_t tv_usec = (ms % 1000) * 1000; /* max=999999 */
+ timediff_t tv_usec = (ms % 1000) * 1000; /* max=999000 */
#ifdef HAVE_SUSECONDS_T
#if TIMEDIFF_T_MAX > TIME_T_MAX
/* tv_sec overflow check in case time_t is signed */
@@ -84,5 +84,5 @@ struct timeval *curlx_mstotv(struct timeval *tv, timediff_t ms)
*/
timediff_t curlx_tvtoms(struct timeval *tv)
{
- return (tv->tv_sec*1000) + (timediff_t)(((double)tv->tv_usec)/1000.0);
+ return (tv->tv_sec*1000) + (timediff_t)(tv->tv_usec/1000);
}
diff --git a/libs/libcurl/src/timediff.h b/libs/libcurl/src/curlx/timediff.h
index d24e01803c..9a8f695982 100644
--- a/libs/libcurl/src/timediff.h
+++ b/libs/libcurl/src/curlx/timediff.h
@@ -24,7 +24,7 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
/* Use a larger type even for 32-bit time_t systems so that we can keep
microsecond accuracy in it */
diff --git a/libs/libcurl/src/timeval.c b/libs/libcurl/src/curlx/timeval.c
index cf7a0b055c..e6739b50b3 100644
--- a/libs/libcurl/src/timeval.c
+++ b/libs/libcurl/src/curlx/timeval.c
@@ -24,30 +24,52 @@
#include "timeval.h"
-#if defined(_WIN32)
+#ifdef _WIN32
#include <curl/curl.h>
-#include "system_win32.h"
+#include "version_win32.h"
+#include "../system_win32.h"
+
+LARGE_INTEGER Curl_freq;
+bool Curl_isVistaOrGreater;
+
+/* For tool or tests, we must initialize before calling curlx_now().
+ Providing this function here is wrong. */
+void curlx_now_init(void)
+{
+ if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
+ VERSION_GREATER_THAN_EQUAL))
+ Curl_isVistaOrGreater = true;
+ else
+ Curl_isVistaOrGreater = false;
+
+ QueryPerformanceFrequency(&Curl_freq);
+}
/* In case of bug fix this function has a counterpart in tool_util.c */
-struct curltime Curl_now(void)
+struct curltime curlx_now(void)
{
struct curltime now;
- if(Curl_isVistaOrGreater) { /* QPC timer might have issues pre-Vista */
+ bool isVistaOrGreater;
+ isVistaOrGreater = Curl_isVistaOrGreater;
+ if(isVistaOrGreater) { /* QPC timer might have issues pre-Vista */
LARGE_INTEGER count;
+ LARGE_INTEGER freq;
+ freq = Curl_freq;
+ DEBUGASSERT(freq.QuadPart);
QueryPerformanceCounter(&count);
- now.tv_sec = (time_t)(count.QuadPart / Curl_freq.QuadPart);
- now.tv_usec = (int)((count.QuadPart % Curl_freq.QuadPart) * 1000000 /
- Curl_freq.QuadPart);
+ now.tv_sec = (time_t)(count.QuadPart / freq.QuadPart);
+ now.tv_usec = (int)((count.QuadPart % freq.QuadPart) * 1000000 /
+ freq.QuadPart);
}
else {
/* Disable /analyze warning that GetTickCount64 is preferred */
-#if defined(_MSC_VER)
+#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:28159)
#endif
DWORD milliseconds = GetTickCount();
-#if defined(_MSC_VER)
+#ifdef _MSC_VER
#pragma warning(pop)
#endif
@@ -60,7 +82,7 @@ struct curltime Curl_now(void)
#elif defined(HAVE_CLOCK_GETTIME_MONOTONIC) || \
defined(HAVE_CLOCK_GETTIME_MONOTONIC_RAW)
-struct curltime Curl_now(void)
+struct curltime curlx_now(void)
{
/*
** clock_gettime() is granted to be increased monotonically when the
@@ -134,7 +156,7 @@ struct curltime Curl_now(void)
#include <stdint.h>
#include <mach/mach_time.h>
-struct curltime Curl_now(void)
+struct curltime curlx_now(void)
{
/*
** Monotonic timer on macOS is provided by mach_absolute_time(), which
@@ -162,7 +184,7 @@ struct curltime Curl_now(void)
#elif defined(HAVE_GETTIMEOFDAY)
-struct curltime Curl_now(void)
+struct curltime curlx_now(void)
{
/*
** gettimeofday() is not granted to be increased monotonically, due to
@@ -179,7 +201,7 @@ struct curltime Curl_now(void)
#else
-struct curltime Curl_now(void)
+struct curltime curlx_now(void)
{
/*
** time() returns the value of time in seconds since the Epoch.
@@ -198,7 +220,7 @@ struct curltime Curl_now(void)
*
* @unittest: 1323
*/
-timediff_t Curl_timediff(struct curltime newer, struct curltime older)
+timediff_t curlx_timediff(struct curltime newer, struct curltime older)
{
timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec;
if(diff >= (TIMEDIFF_T_MAX/1000))
@@ -212,7 +234,7 @@ timediff_t Curl_timediff(struct curltime newer, struct curltime older)
* Returns: time difference in number of milliseconds, rounded up.
* For too large diffs it returns max value.
*/
-timediff_t Curl_timediff_ceil(struct curltime newer, struct curltime older)
+timediff_t curlx_timediff_ceil(struct curltime newer, struct curltime older)
{
timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec;
if(diff >= (TIMEDIFF_T_MAX/1000))
@@ -226,7 +248,7 @@ timediff_t Curl_timediff_ceil(struct curltime newer, struct curltime older)
* Returns: time difference in number of microseconds. For too large diffs it
* returns max value.
*/
-timediff_t Curl_timediff_us(struct curltime newer, struct curltime older)
+timediff_t curlx_timediff_us(struct curltime newer, struct curltime older)
{
timediff_t diff = (timediff_t)newer.tv_sec-older.tv_sec;
if(diff >= (TIMEDIFF_T_MAX/1000000))
diff --git a/libs/libcurl/src/timeval.h b/libs/libcurl/src/curlx/timeval.h
index 510688944a..8e34a07967 100644
--- a/libs/libcurl/src/timeval.h
+++ b/libs/libcurl/src/curlx/timeval.h
@@ -24,7 +24,7 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#include "timediff.h"
@@ -33,7 +33,12 @@ struct curltime {
int tv_usec; /* microseconds */
};
-struct curltime Curl_now(void);
+#ifdef _WIN32
+/* For tool or tests, we must initialize before calling curlx_now() */
+void curlx_now_init(void);
+#endif
+
+struct curltime curlx_now(void);
/*
* Make sure that the first argument (newer) is the more recent time and older
@@ -41,7 +46,7 @@ struct curltime Curl_now(void);
*
* Returns: the time difference in number of milliseconds.
*/
-timediff_t Curl_timediff(struct curltime newer, struct curltime older);
+timediff_t curlx_timediff(struct curltime newer, struct curltime older);
/*
* Make sure that the first argument (newer) is the more recent time and older
@@ -49,7 +54,7 @@ timediff_t Curl_timediff(struct curltime newer, struct curltime older);
*
* Returns: the time difference in number of milliseconds, rounded up.
*/
-timediff_t Curl_timediff_ceil(struct curltime newer, struct curltime older);
+timediff_t curlx_timediff_ceil(struct curltime newer, struct curltime older);
/*
* Make sure that the first argument (newer) is the more recent time and older
@@ -57,6 +62,6 @@ timediff_t Curl_timediff_ceil(struct curltime newer, struct curltime older);
*
* Returns: the time difference in number of microseconds.
*/
-timediff_t Curl_timediff_us(struct curltime newer, struct curltime older);
+timediff_t curlx_timediff_us(struct curltime newer, struct curltime older);
#endif /* HEADER_CURL_TIMEVAL_H */
diff --git a/libs/libcurl/src/version_win32.c b/libs/libcurl/src/curlx/version_win32.c
index e0e325872d..54c02bdfe0 100644
--- a/libs/libcurl/src/version_win32.c
+++ b/libs/libcurl/src/curlx/version_win32.c
@@ -22,19 +22,17 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
-#if defined(_WIN32)
+#ifdef _WIN32
#include <curl/curl.h>
#include "version_win32.h"
#include "warnless.h"
/* The last 2 #include files should be in this order */
-#ifdef BUILDING_LIBCURL
-#include "curl_memory.h"
-#endif
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
/* This Unicode version struct works for VerifyVersionInfoW (OSVERSIONINFOEXW)
and RtlVerifyVersionInfo (RTLOSVERSIONINFOEXW) */
@@ -79,7 +77,7 @@ bool curlx_verify_windows_version(const unsigned int majorVersion,
{
bool matched = FALSE;
-#if defined(CURL_WINDOWS_UWP)
+#ifdef CURL_WINDOWS_UWP
/* We have no way to determine the Windows version from Windows apps,
so let's assume we are running on the target Windows version. */
const WORD fullVersion = MAKEWORD(minorVersion, majorVersion);
@@ -113,88 +111,12 @@ bool curlx_verify_windows_version(const unsigned int majorVersion,
/* we are always running on PLATFORM_WINNT */
matched = FALSE;
}
-#elif !defined(_WIN32_WINNT) || !defined(_WIN32_WINNT_WIN2K) || \
- (_WIN32_WINNT < _WIN32_WINNT_WIN2K)
- OSVERSIONINFO osver;
-
- memset(&osver, 0, sizeof(osver));
- osver.dwOSVersionInfoSize = sizeof(osver);
-
- /* Find out Windows version */
- if(GetVersionEx(&osver)) {
- /* Verify the Operating System version number */
- switch(condition) {
- case VERSION_LESS_THAN:
- if(osver.dwMajorVersion < majorVersion ||
- (osver.dwMajorVersion == majorVersion &&
- osver.dwMinorVersion < minorVersion) ||
- (buildVersion != 0 &&
- (osver.dwMajorVersion == majorVersion &&
- osver.dwMinorVersion == minorVersion &&
- osver.dwBuildNumber < buildVersion)))
- matched = TRUE;
- break;
-
- case VERSION_LESS_THAN_EQUAL:
- if(osver.dwMajorVersion < majorVersion ||
- (osver.dwMajorVersion == majorVersion &&
- osver.dwMinorVersion < minorVersion) ||
- (osver.dwMajorVersion == majorVersion &&
- osver.dwMinorVersion == minorVersion &&
- (buildVersion == 0 ||
- osver.dwBuildNumber <= buildVersion)))
- matched = TRUE;
- break;
-
- case VERSION_EQUAL:
- if(osver.dwMajorVersion == majorVersion &&
- osver.dwMinorVersion == minorVersion &&
- (buildVersion == 0 ||
- osver.dwBuildNumber == buildVersion))
- matched = TRUE;
- break;
-
- case VERSION_GREATER_THAN_EQUAL:
- if(osver.dwMajorVersion > majorVersion ||
- (osver.dwMajorVersion == majorVersion &&
- osver.dwMinorVersion > minorVersion) ||
- (osver.dwMajorVersion == majorVersion &&
- osver.dwMinorVersion == minorVersion &&
- (buildVersion == 0 ||
- osver.dwBuildNumber >= buildVersion)))
- matched = TRUE;
- break;
-
- case VERSION_GREATER_THAN:
- if(osver.dwMajorVersion > majorVersion ||
- (osver.dwMajorVersion == majorVersion &&
- osver.dwMinorVersion > minorVersion) ||
- (buildVersion != 0 &&
- (osver.dwMajorVersion == majorVersion &&
- osver.dwMinorVersion == minorVersion &&
- osver.dwBuildNumber > buildVersion)))
- matched = TRUE;
- break;
- }
-
- /* Verify the platform identifier (if necessary) */
- if(matched) {
- switch(platform) {
- case PLATFORM_WINDOWS:
- if(osver.dwPlatformId != VER_PLATFORM_WIN32_WINDOWS)
- matched = FALSE;
- break;
-
- case PLATFORM_WINNT:
- if(osver.dwPlatformId != VER_PLATFORM_WIN32_NT)
- matched = FALSE;
- break;
-
- default: /* like platform == PLATFORM_DONT_CARE */
- break;
- }
- }
- }
+#elif defined(UNDER_CE)
+ (void)majorVersion;
+ (void)minorVersion;
+ (void)buildVersion;
+ (void)platform;
+ (void)condition;
#else
ULONGLONG cm = 0;
struct OUR_OSVERSIONINFOEXW osver;
diff --git a/libs/libcurl/src/version_win32.h b/libs/libcurl/src/curlx/version_win32.h
index e07dfba838..0a9aa9b2e0 100644
--- a/libs/libcurl/src/version_win32.h
+++ b/libs/libcurl/src/curlx/version_win32.h
@@ -24,9 +24,9 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
-#if defined(_WIN32)
+#ifdef _WIN32
/* Version condition */
typedef enum {
diff --git a/libs/libcurl/src/warnless.c b/libs/libcurl/src/curlx/warnless.c
index 33aec8aec1..de43a2c17b 100644
--- a/libs/libcurl/src/warnless.c
+++ b/libs/libcurl/src/curlx/warnless.c
@@ -22,7 +22,7 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "warnless.h"
#if defined(__INTEL_COMPILER) && defined(__unix__)
@@ -35,8 +35,6 @@
#endif /* __INTEL_COMPILER && __unix__ */
-#include "warnless.h"
-
#ifdef _WIN32
#undef read
#undef write
@@ -45,43 +43,18 @@
#include <limits.h>
#define CURL_MASK_UCHAR ((unsigned char)~0)
-#define CURL_MASK_SCHAR (CURL_MASK_UCHAR >> 1)
#define CURL_MASK_USHORT ((unsigned short)~0)
-#define CURL_MASK_SSHORT (CURL_MASK_USHORT >> 1)
#define CURL_MASK_UINT ((unsigned int)~0)
#define CURL_MASK_SINT (CURL_MASK_UINT >> 1)
#define CURL_MASK_ULONG ((unsigned long)~0)
-#define CURL_MASK_SLONG (CURL_MASK_ULONG >> 1)
-
-#define CURL_MASK_UCOFFT ((unsigned CURL_TYPEOF_CURL_OFF_T)~0)
-#define CURL_MASK_SCOFFT (CURL_MASK_UCOFFT >> 1)
#define CURL_MASK_USIZE_T ((size_t)~0)
#define CURL_MASK_SSIZE_T (CURL_MASK_USIZE_T >> 1)
/*
-** unsigned long to unsigned short
-*/
-
-unsigned short curlx_ultous(unsigned long ulnum)
-{
-#ifdef __INTEL_COMPILER
-# pragma warning(push)
-# pragma warning(disable:810) /* conversion may lose significant bits */
-#endif
-
- DEBUGASSERT(ulnum <= (unsigned long) CURL_MASK_USHORT);
- return (unsigned short)(ulnum & (unsigned long) CURL_MASK_USHORT);
-
-#ifdef __INTEL_COMPILER
-# pragma warning(pop)
-#endif
-}
-
-/*
** unsigned long to unsigned char
*/
@@ -101,28 +74,6 @@ unsigned char curlx_ultouc(unsigned long ulnum)
}
/*
-** unsigned size_t to signed curl_off_t
-*/
-
-curl_off_t curlx_uztoso(size_t uznum)
-{
-#ifdef __INTEL_COMPILER
-# pragma warning(push)
-# pragma warning(disable:810) /* conversion may lose significant bits */
-#elif defined(_MSC_VER)
-# pragma warning(push)
-# pragma warning(disable:4310) /* cast truncates constant value */
-#endif
-
- DEBUGASSERT(uznum <= (size_t) CURL_MASK_SCOFFT);
- return (curl_off_t)(uznum & (size_t) CURL_MASK_SCOFFT);
-
-#if defined(__INTEL_COMPILER) || defined(_MSC_VER)
-# pragma warning(pop)
-#endif
-}
-
-/*
** unsigned size_t to signed int
*/
@@ -148,8 +99,8 @@ int curlx_uztosi(size_t uznum)
unsigned long curlx_uztoul(size_t uznum)
{
#ifdef __INTEL_COMPILER
-# pragma warning(push)
-# pragma warning(disable:810) /* conversion may lose significant bits */
+# pragma warning(push)
+# pragma warning(disable:810) /* conversion may lose significant bits */
#endif
#if ULONG_MAX < SIZE_T_MAX
@@ -158,7 +109,7 @@ unsigned long curlx_uztoul(size_t uznum)
return (unsigned long)(uznum & (size_t) CURL_MASK_ULONG);
#ifdef __INTEL_COMPILER
-# pragma warning(pop)
+# pragma warning(pop)
#endif
}
@@ -169,8 +120,8 @@ unsigned long curlx_uztoul(size_t uznum)
unsigned int curlx_uztoui(size_t uznum)
{
#ifdef __INTEL_COMPILER
-# pragma warning(push)
-# pragma warning(disable:810) /* conversion may lose significant bits */
+# pragma warning(push)
+# pragma warning(disable:810) /* conversion may lose significant bits */
#endif
#if UINT_MAX < SIZE_T_MAX
@@ -179,7 +130,7 @@ unsigned int curlx_uztoui(size_t uznum)
return (unsigned int)(uznum & (size_t) CURL_MASK_UINT);
#ifdef __INTEL_COMPILER
-# pragma warning(pop)
+# pragma warning(pop)
#endif
}
@@ -345,7 +296,7 @@ size_t curlx_sitouz(int sinum)
#endif
}
-#if defined(_WIN32)
+#ifdef _WIN32
ssize_t curlx_read(int fd, void *buf, size_t count)
{
diff --git a/libs/libcurl/src/warnless.h b/libs/libcurl/src/curlx/warnless.h
index 633494544d..e995a3c9d3 100644
--- a/libs/libcurl/src/warnless.h
+++ b/libs/libcurl/src/curlx/warnless.h
@@ -24,7 +24,7 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_WINSOCK
#include <curl/curl.h> /* for curl_socket_t */
@@ -33,14 +33,10 @@
#define CURLX_FUNCTION_CAST(target_type, func) \
(target_type)(void (*) (void))(func)
-unsigned short curlx_ultous(unsigned long ulnum);
-
unsigned char curlx_ultouc(unsigned long ulnum);
int curlx_uztosi(size_t uznum);
-curl_off_t curlx_uztoso(size_t uznum);
-
unsigned long curlx_uztoul(size_t uznum);
unsigned int curlx_uztoui(size_t uznum);
@@ -61,7 +57,7 @@ unsigned short curlx_uitous(unsigned int uinum);
size_t curlx_sitouz(int sinum);
-#if defined(_WIN32)
+#ifdef _WIN32
ssize_t curlx_read(int fd, void *buf, size_t count);
@@ -74,7 +70,7 @@ ssize_t curlx_write(int fd, const void *buf, size_t count);
#ifndef HEADER_CURL_WARNLESS_H_REDEFS
#define HEADER_CURL_WARNLESS_H_REDEFS
-#if defined(_WIN32)
+#ifdef _WIN32
#undef read
#define read(fd, buf, count) curlx_read(fd, buf, count)
#undef write
diff --git a/libs/libcurl/src/curlx/winapi.c b/libs/libcurl/src/curlx/winapi.c
new file mode 100644
index 0000000000..60eddc23f2
--- /dev/null
+++ b/libs/libcurl/src/curlx/winapi.c
@@ -0,0 +1,135 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "../curl_setup.h"
+
+/*
+ * curlx_winapi_strerror:
+ * Variant of Curl_strerror if the error code is definitely Windows API.
+ */
+#ifdef _WIN32
+#include "winapi.h"
+
+#ifdef BUILDING_LIBCURL
+#include <curl/mprintf.h>
+#define SNPRINTF curl_msnprintf
+#else
+/* when built for the test servers */
+
+/* adjust for old MSVC */
+#if defined(_MSC_VER) && (_MSC_VER < 1900)
+# define SNPRINTF _snprintf
+#else
+#define SNPRINTF snprintf
+#endif
+
+#endif /* !BUILDING_LIBCURL */
+
+#ifdef _WIN32
+/* This is a helper function for Curl_strerror that converts Windows API error
+ * codes (GetLastError) to error messages.
+ * Returns NULL if no error message was found for error code.
+ */
+const char *curlx_get_winapi_error(int err, char *buf, size_t buflen)
+{
+ char *p;
+ wchar_t wbuf[256];
+
+ if(!buflen)
+ return NULL;
+
+ *buf = '\0';
+ *wbuf = L'\0';
+
+ /* We return the local codepage version of the error string because if it is
+ output to the user's terminal it will likely be with functions which
+ expect the local codepage (eg fprintf, failf, infof).
+ FormatMessageW -> wcstombs is used for Windows CE compatibility. */
+ if(FormatMessageW((FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS), NULL, (DWORD)err,
+ LANG_NEUTRAL, wbuf, CURL_ARRAYSIZE(wbuf), NULL)) {
+ size_t written = wcstombs(buf, wbuf, buflen - 1);
+ if(written != (size_t)-1)
+ buf[written] = '\0';
+ else
+ *buf = '\0';
+ }
+
+ /* Truncate multiple lines */
+ p = strchr(buf, '\n');
+ if(p) {
+ if(p > buf && *(p-1) == '\r')
+ *(p-1) = '\0';
+ else
+ *p = '\0';
+ }
+
+ return *buf ? buf : NULL;
+}
+#endif /* _WIN32 */
+
+const char *curlx_winapi_strerror(DWORD err, char *buf, size_t buflen)
+{
+#ifdef _WIN32
+ DWORD old_win_err = GetLastError();
+#endif
+ int old_errno = errno;
+
+ if(!buflen)
+ return NULL;
+
+ *buf = '\0';
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ if(!curlx_get_winapi_error((int)err, buf, buflen)) {
+#if defined(__GNUC__) && __GNUC__ >= 7
+#pragma GCC diagnostic push
+#pragma GCC diagnostic warning "-Wformat-truncation=1"
+#endif
+ /* some GCC compilers cause false positive warnings if we allow this
+ warning */
+ SNPRINTF(buf, buflen, "Unknown error %lu (0x%08lX)", err, err);
+#if defined(__GNUC__) && __GNUC__ >= 7
+#pragma GCC diagnostic pop
+#endif
+
+ }
+#else
+ {
+ const char *txt = (err == ERROR_SUCCESS) ? "No error" : "Error";
+ if(strlen(txt) < buflen)
+ strcpy(buf, txt);
+ }
+#endif
+
+ if(errno != old_errno)
+ CURL_SETERRNO(old_errno);
+
+#ifdef _WIN32
+ if(old_win_err != GetLastError())
+ SetLastError(old_win_err);
+#endif
+
+ return buf;
+}
+#endif /* _WIN32 */
diff --git a/libs/libcurl/src/strtok.h b/libs/libcurl/src/curlx/winapi.h
index 0cff3213ab..65425b9a0f 100644
--- a/libs/libcurl/src/strtok.h
+++ b/libs/libcurl/src/curlx/winapi.h
@@ -1,5 +1,5 @@
-#ifndef HEADER_CURL_STRTOK_H
-#define HEADER_CURL_STRTOK_H
+#ifndef HEADER_CURLX_WINAPI_H
+#define HEADER_CURLX_WINAPI_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
@@ -23,14 +23,11 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
-#include <stddef.h>
-#ifndef HAVE_STRTOK_R
-char *Curl_strtok_r(char *s, const char *delim, char **last);
-#define strtok_r Curl_strtok_r
-#else
-#include <string.h>
+#ifdef _WIN32
+#define WINAPI_ERROR_LEN 100
+const char *curlx_get_winapi_error(int err, char *buf, size_t buflen);
+const char *curlx_winapi_strerror(DWORD err, char *buf, size_t buflen);
#endif
-#endif /* HEADER_CURL_STRTOK_H */
+#endif /* HEADER_CURLX_WINAPI_H */
diff --git a/libs/libcurl/src/cw-out.c b/libs/libcurl/src/cw-out.c
index 5e8895fae4..6f897ec376 100644
--- a/libs/libcurl/src/cw-out.c
+++ b/libs/libcurl/src/cw-out.c
@@ -32,6 +32,7 @@
#include "multiif.h"
#include "sendf.h"
#include "cw-out.h"
+#include "cw-pause.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -86,7 +87,7 @@ static struct cw_out_buf *cw_out_buf_create(cw_out_type otype)
struct cw_out_buf *cwbuf = calloc(1, sizeof(*cwbuf));
if(cwbuf) {
cwbuf->type = otype;
- Curl_dyn_init(&cwbuf->b, DYN_PAUSE_BUFFER);
+ curlx_dyn_init(&cwbuf->b, DYN_PAUSE_BUFFER);
}
return cwbuf;
}
@@ -94,7 +95,7 @@ static struct cw_out_buf *cw_out_buf_create(cw_out_type otype)
static void cw_out_buf_free(struct cw_out_buf *cwbuf)
{
if(cwbuf) {
- Curl_dyn_free(&cwbuf->b);
+ curlx_dyn_free(&cwbuf->b);
free(cwbuf);
}
}
@@ -113,7 +114,7 @@ static void cw_out_close(struct Curl_easy *data, struct Curl_cwriter *writer);
static CURLcode cw_out_init(struct Curl_easy *data,
struct Curl_cwriter *writer);
-struct Curl_cwtype Curl_cwt_out = {
+const struct Curl_cwtype Curl_cwt_out = {
"cw-out",
NULL,
cw_out_init,
@@ -145,7 +146,7 @@ static size_t cw_out_bufs_len(struct cw_out_ctx *ctx)
struct cw_out_buf *cwbuf = ctx->buf;
size_t len = 0;
while(cwbuf) {
- len += Curl_dyn_len(&cwbuf->b);
+ len += curlx_dyn_len(&cwbuf->b);
cwbuf = cwbuf->next;
}
return len;
@@ -198,7 +199,7 @@ static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx,
const char *buf, size_t blen,
size_t *pconsumed)
{
- curl_write_callback wcb;
+ curl_write_callback wcb = NULL;
void *wcb_data;
size_t max_write, min_write;
size_t wlen, nwritten;
@@ -220,9 +221,9 @@ static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx,
break;
wlen = max_write ? CURLMIN(blen, max_write) : blen;
Curl_set_in_callback(data, TRUE);
- nwritten = wcb((char *)buf, 1, wlen, wcb_data);
+ nwritten = wcb((char *)CURL_UNCONST(buf), 1, wlen, wcb_data);
Curl_set_in_callback(data, FALSE);
- CURL_TRC_WRITE(data, "cw_out, wrote %zu %s bytes -> %zu",
+ CURL_TRC_WRITE(data, "[OUT] wrote %zu %s bytes -> %zu",
wlen, (otype == CW_OUT_BODY) ? "body" : "header",
nwritten);
if(CURL_WRITEFUNC_PAUSE == nwritten) {
@@ -236,7 +237,7 @@ static CURLcode cw_out_ptr_flush(struct cw_out_ctx *ctx,
/* mark the connection as RECV paused */
data->req.keepon |= KEEP_RECV_PAUSE;
ctx->paused = TRUE;
- CURL_TRC_WRITE(data, "cw_out, PAUSE requested by client");
+ CURL_TRC_WRITE(data, "[OUT] PAUSE requested by client");
break;
}
else if(CURL_WRITEFUNC_ERROR == nwritten) {
@@ -262,23 +263,24 @@ static CURLcode cw_out_buf_flush(struct cw_out_ctx *ctx,
{
CURLcode result = CURLE_OK;
- if(Curl_dyn_len(&cwbuf->b)) {
+ if(curlx_dyn_len(&cwbuf->b)) {
size_t consumed;
result = cw_out_ptr_flush(ctx, data, cwbuf->type, flush_all,
- Curl_dyn_ptr(&cwbuf->b),
- Curl_dyn_len(&cwbuf->b),
+ curlx_dyn_ptr(&cwbuf->b),
+ curlx_dyn_len(&cwbuf->b),
&consumed);
if(result)
return result;
if(consumed) {
- if(consumed == Curl_dyn_len(&cwbuf->b)) {
- Curl_dyn_free(&cwbuf->b);
+ if(consumed == curlx_dyn_len(&cwbuf->b)) {
+ curlx_dyn_free(&cwbuf->b);
}
else {
- DEBUGASSERT(consumed < Curl_dyn_len(&cwbuf->b));
- result = Curl_dyn_tail(&cwbuf->b, Curl_dyn_len(&cwbuf->b) - consumed);
+ DEBUGASSERT(consumed < curlx_dyn_len(&cwbuf->b));
+ result = curlx_dyn_tail(&cwbuf->b,
+ curlx_dyn_len(&cwbuf->b) - consumed);
if(result)
return result;
}
@@ -318,7 +320,7 @@ static CURLcode cw_out_flush_chain(struct cw_out_ctx *ctx,
result = cw_out_buf_flush(ctx, data, cwbuf, flush_all);
if(result)
return result;
- if(!Curl_dyn_len(&cwbuf->b)) {
+ if(!curlx_dyn_len(&cwbuf->b)) {
cw_out_buf_free(cwbuf);
*pcwbuf = NULL;
}
@@ -326,11 +328,16 @@ static CURLcode cw_out_flush_chain(struct cw_out_ctx *ctx,
}
static CURLcode cw_out_append(struct cw_out_ctx *ctx,
+ struct Curl_easy *data,
cw_out_type otype,
const char *buf, size_t blen)
{
- if(cw_out_bufs_len(ctx) + blen > DYN_PAUSE_BUFFER)
+ CURL_TRC_WRITE(data, "[OUT] paused, buffering %zu more bytes (%zu/%d)",
+ blen, cw_out_bufs_len(ctx), DYN_PAUSE_BUFFER);
+ if(cw_out_bufs_len(ctx) + blen > DYN_PAUSE_BUFFER) {
+ failf(data, "pause buffer not large enough -> CURLE_TOO_LARGE");
return CURLE_TOO_LARGE;
+ }
/* if we do not have a buffer, or it is of another type, make a new one.
* And for CW_OUT_HDS always make a new one, so we "replay" headers
@@ -343,7 +350,7 @@ static CURLcode cw_out_append(struct cw_out_ctx *ctx,
ctx->buf = cwbuf;
}
DEBUGASSERT(ctx->buf && (ctx->buf->type == otype));
- return Curl_dyn_addn(&ctx->buf->b, buf, blen);
+ return curlx_dyn_addn(&ctx->buf->b, buf, blen);
}
static CURLcode cw_out_do_write(struct cw_out_ctx *ctx,
@@ -364,7 +371,7 @@ static CURLcode cw_out_do_write(struct cw_out_ctx *ctx,
if(ctx->buf) {
/* still have buffered data, append and flush */
- result = cw_out_append(ctx, otype, buf, blen);
+ result = cw_out_append(ctx, data, otype, buf, blen);
if(result)
return result;
result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all);
@@ -380,7 +387,8 @@ static CURLcode cw_out_do_write(struct cw_out_ctx *ctx,
return result;
if(consumed < blen) {
/* did not write all, append the rest */
- result = cw_out_append(ctx, otype, buf + consumed, blen - consumed);
+ result = cw_out_append(ctx, data, otype,
+ buf + consumed, blen - consumed);
if(result)
goto out;
}
@@ -430,44 +438,58 @@ bool Curl_cw_out_is_paused(struct Curl_easy *data)
return FALSE;
ctx = (struct cw_out_ctx *)cw_out;
- CURL_TRC_WRITE(data, "cw-out is%spaused", ctx->paused ? "" : " not");
return ctx->paused;
}
static CURLcode cw_out_flush(struct Curl_easy *data,
- bool unpause, bool flush_all)
+ struct Curl_cwriter *cw_out,
+ bool flush_all)
{
- struct Curl_cwriter *cw_out;
+ struct cw_out_ctx *ctx = (struct cw_out_ctx *)cw_out;
CURLcode result = CURLE_OK;
- cw_out = Curl_cwriter_get_by_type(data, &Curl_cwt_out);
- if(cw_out) {
- struct cw_out_ctx *ctx = (struct cw_out_ctx *)cw_out;
- if(ctx->errored)
- return CURLE_WRITE_ERROR;
- if(unpause && ctx->paused)
- ctx->paused = FALSE;
- if(ctx->paused)
- return CURLE_OK; /* not doing it */
+ if(ctx->errored)
+ return CURLE_WRITE_ERROR;
+ if(ctx->paused)
+ return CURLE_OK; /* not doing it */
- result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all);
- if(result) {
- ctx->errored = TRUE;
- cw_out_bufs_free(ctx);
- return result;
- }
+ result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all);
+ if(result) {
+ ctx->errored = TRUE;
+ cw_out_bufs_free(ctx);
+ return result;
}
return result;
}
CURLcode Curl_cw_out_unpause(struct Curl_easy *data)
{
- CURL_TRC_WRITE(data, "cw-out unpause");
- return cw_out_flush(data, TRUE, FALSE);
+ struct Curl_cwriter *cw_out;
+ CURLcode result = CURLE_OK;
+
+ cw_out = Curl_cwriter_get_by_type(data, &Curl_cwt_out);
+ if(cw_out) {
+ struct cw_out_ctx *ctx = (struct cw_out_ctx *)cw_out;
+ CURL_TRC_WRITE(data, "[OUT] unpause");
+ ctx->paused = FALSE;
+ result = Curl_cw_pause_flush(data);
+ if(!result)
+ result = cw_out_flush(data, cw_out, FALSE);
+ }
+ return result;
}
CURLcode Curl_cw_out_done(struct Curl_easy *data)
{
- CURL_TRC_WRITE(data, "cw-out done");
- return cw_out_flush(data, FALSE, TRUE);
+ struct Curl_cwriter *cw_out;
+ CURLcode result = CURLE_OK;
+
+ cw_out = Curl_cwriter_get_by_type(data, &Curl_cwt_out);
+ if(cw_out) {
+ CURL_TRC_WRITE(data, "[OUT] done");
+ result = Curl_cw_pause_flush(data);
+ if(!result)
+ result = cw_out_flush(data, cw_out, TRUE);
+ }
+ return result;
}
diff --git a/libs/libcurl/src/cw-out.h b/libs/libcurl/src/cw-out.h
index e5afe097a0..ecab9e50b5 100644
--- a/libs/libcurl/src/cw-out.h
+++ b/libs/libcurl/src/cw-out.h
@@ -33,7 +33,7 @@
* the client callbacks. Intended to be the last installed in the
* client writer stack of a transfer.
*/
-extern struct Curl_cwtype Curl_cwt_out;
+extern const struct Curl_cwtype Curl_cwt_out;
/**
* Return TRUE iff 'cw-out' client write has paused data.
diff --git a/libs/libcurl/src/cw-pause.c b/libs/libcurl/src/cw-pause.c
new file mode 100644
index 0000000000..8df2a32059
--- /dev/null
+++ b/libs/libcurl/src/cw-pause.c
@@ -0,0 +1,242 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+
+#include "urldata.h"
+#include "bufq.h"
+#include "cfilters.h"
+#include "headers.h"
+#include "multiif.h"
+#include "sendf.h"
+#include "cw-pause.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+
+/* body dynbuf sizes */
+#define CW_PAUSE_BUF_CHUNK (16 * 1024)
+/* when content decoding, write data in chunks */
+#define CW_PAUSE_DEC_WRITE_CHUNK (4096)
+
+struct cw_pause_buf {
+ struct cw_pause_buf *next;
+ struct bufq b;
+ int type;
+};
+
+static struct cw_pause_buf *cw_pause_buf_create(int type, size_t buflen)
+{
+ struct cw_pause_buf *cwbuf = calloc(1, sizeof(*cwbuf));
+ if(cwbuf) {
+ cwbuf->type = type;
+ if(type & CLIENTWRITE_BODY)
+ Curl_bufq_init2(&cwbuf->b, CW_PAUSE_BUF_CHUNK, 1,
+ (BUFQ_OPT_SOFT_LIMIT|BUFQ_OPT_NO_SPARES));
+ else
+ Curl_bufq_init(&cwbuf->b, buflen, 1);
+ }
+ return cwbuf;
+}
+
+static void cw_pause_buf_free(struct cw_pause_buf *cwbuf)
+{
+ if(cwbuf) {
+ Curl_bufq_free(&cwbuf->b);
+ free(cwbuf);
+ }
+}
+
+struct cw_pause_ctx {
+ struct Curl_cwriter super;
+ struct cw_pause_buf *buf;
+ size_t buf_total;
+};
+
+static CURLcode cw_pause_write(struct Curl_easy *data,
+ struct Curl_cwriter *writer, int type,
+ const char *buf, size_t nbytes);
+static void cw_pause_close(struct Curl_easy *data,
+ struct Curl_cwriter *writer);
+static CURLcode cw_pause_init(struct Curl_easy *data,
+ struct Curl_cwriter *writer);
+
+const struct Curl_cwtype Curl_cwt_pause = {
+ "cw-pause",
+ NULL,
+ cw_pause_init,
+ cw_pause_write,
+ cw_pause_close,
+ sizeof(struct cw_pause_ctx)
+};
+
+static CURLcode cw_pause_init(struct Curl_easy *data,
+ struct Curl_cwriter *writer)
+{
+ struct cw_pause_ctx *ctx = writer->ctx;
+ (void)data;
+ ctx->buf = NULL;
+ return CURLE_OK;
+}
+
+static void cw_pause_bufs_free(struct cw_pause_ctx *ctx)
+{
+ while(ctx->buf) {
+ struct cw_pause_buf *next = ctx->buf->next;
+ cw_pause_buf_free(ctx->buf);
+ ctx->buf = next;
+ }
+}
+
+static void cw_pause_close(struct Curl_easy *data, struct Curl_cwriter *writer)
+{
+ struct cw_pause_ctx *ctx = writer->ctx;
+
+ (void)data;
+ cw_pause_bufs_free(ctx);
+}
+
+static CURLcode cw_pause_flush(struct Curl_easy *data,
+ struct Curl_cwriter *cw_pause)
+{
+ struct cw_pause_ctx *ctx = (struct cw_pause_ctx *)cw_pause;
+ bool decoding = Curl_cwriter_is_content_decoding(data);
+ CURLcode result = CURLE_OK;
+
+ /* write the end of the chain until it blocks or gets empty */
+ while(ctx->buf && !Curl_cwriter_is_paused(data)) {
+ struct cw_pause_buf **plast = &ctx->buf;
+ size_t blen, wlen = 0;
+ const unsigned char *buf = NULL;
+
+ while((*plast)->next) /* got to last in list */
+ plast = &(*plast)->next;
+ if(Curl_bufq_peek(&(*plast)->b, &buf, &blen)) {
+ wlen = (decoding && ((*plast)->type & CLIENTWRITE_BODY)) ?
+ CURLMIN(blen, CW_PAUSE_DEC_WRITE_CHUNK) : blen;
+ result = Curl_cwriter_write(data, cw_pause->next, (*plast)->type,
+ (const char *)buf, wlen);
+ CURL_TRC_WRITE(data, "[PAUSE] flushed %zu/%zu bytes, type=%x -> %d",
+ wlen, ctx->buf_total, (*plast)->type, result);
+ Curl_bufq_skip(&(*plast)->b, wlen);
+ DEBUGASSERT(ctx->buf_total >= wlen);
+ ctx->buf_total -= wlen;
+ if(result)
+ return result;
+ }
+ else if((*plast)->type & CLIENTWRITE_EOS) {
+ result = Curl_cwriter_write(data, cw_pause->next, (*plast)->type,
+ (const char *)buf, 0);
+ CURL_TRC_WRITE(data, "[PAUSE] flushed 0/%zu bytes, type=%x -> %d",
+ ctx->buf_total, (*plast)->type, result);
+ }
+
+ if(Curl_bufq_is_empty(&(*plast)->b)) {
+ cw_pause_buf_free(*plast);
+ *plast = NULL;
+ }
+ }
+ return result;
+}
+
+static CURLcode cw_pause_write(struct Curl_easy *data,
+ struct Curl_cwriter *writer, int type,
+ const char *buf, size_t blen)
+{
+ struct cw_pause_ctx *ctx = writer->ctx;
+ CURLcode result = CURLE_OK;
+ size_t wlen = 0;
+ bool decoding = Curl_cwriter_is_content_decoding(data);
+
+ if(ctx->buf && !Curl_cwriter_is_paused(data)) {
+ result = cw_pause_flush(data, writer);
+ if(result)
+ return result;
+ }
+
+ while(!ctx->buf && !Curl_cwriter_is_paused(data)) {
+ int wtype = type;
+ DEBUGASSERT(!ctx->buf);
+ /* content decoding might blow up size considerably, write smaller
+ * chunks to make pausing need buffer less. */
+ wlen = (decoding && (type & CLIENTWRITE_BODY)) ?
+ CURLMIN(blen, CW_PAUSE_DEC_WRITE_CHUNK) : blen;
+ if(wlen < blen)
+ wtype &= ~CLIENTWRITE_EOS;
+ result = Curl_cwriter_write(data, writer->next, wtype, buf, wlen);
+ CURL_TRC_WRITE(data, "[PAUSE] writing %zu/%zu bytes of type %x -> %d",
+ wlen, blen, wtype, result);
+ if(result)
+ return result;
+ buf += wlen;
+ blen -= wlen;
+ if(!blen)
+ return result;
+ }
+
+ do {
+ size_t nwritten = 0;
+ if(ctx->buf && (ctx->buf->type == type) && (type & CLIENTWRITE_BODY)) {
+ /* same type and body, append to current buffer which has a soft
+ * limit and should take everything up to OOM. */
+ result = Curl_bufq_cwrite(&ctx->buf->b, buf, blen, &nwritten);
+ }
+ else {
+ /* Need a new buf, type changed */
+ struct cw_pause_buf *cwbuf = cw_pause_buf_create(type, blen);
+ if(!cwbuf)
+ return CURLE_OUT_OF_MEMORY;
+ cwbuf->next = ctx->buf;
+ ctx->buf = cwbuf;
+ result = Curl_bufq_cwrite(&ctx->buf->b, buf, blen, &nwritten);
+ }
+ CURL_TRC_WRITE(data, "[PAUSE] buffer %zu more bytes of type %x, "
+ "total=%zu -> %d", nwritten, type, ctx->buf_total + wlen,
+ result);
+ if(result)
+ return result;
+ buf += nwritten;
+ blen -= nwritten;
+ ctx->buf_total += nwritten;
+ } while(blen);
+
+ return result;
+}
+
+CURLcode Curl_cw_pause_flush(struct Curl_easy *data)
+{
+ struct Curl_cwriter *cw_pause;
+ CURLcode result = CURLE_OK;
+
+ cw_pause = Curl_cwriter_get_by_type(data, &Curl_cwt_pause);
+ if(cw_pause)
+ result = cw_pause_flush(data, cw_pause);
+
+ return result;
+}
diff --git a/libs/libcurl/src/cw-pause.h b/libs/libcurl/src/cw-pause.h
new file mode 100644
index 0000000000..4bcf656a41
--- /dev/null
+++ b/libs/libcurl/src/cw-pause.h
@@ -0,0 +1,40 @@
+#ifndef HEADER_CURL_CW_PAUSE_H
+#define HEADER_CURL_CW_PAUSE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include "sendf.h"
+
+/**
+ * The client writer type "cw-pause" that buffers writes for
+ * paused transfer writes.
+ */
+extern const struct Curl_cwtype Curl_cwt_pause;
+
+CURLcode Curl_cw_pause_flush(struct Curl_easy *data);
+
+
+#endif /* HEADER_CURL_CW_PAUSE_H */
diff --git a/libs/libcurl/src/dict.c b/libs/libcurl/src/dict.c
index 5bf76332af..851514708a 100644
--- a/libs/libcurl/src/dict.c
+++ b/libs/libcurl/src/dict.c
@@ -65,6 +65,15 @@
/* The last #include file should be: */
#include "memdebug.h"
+
+#define DICT_MATCH "/MATCH:"
+#define DICT_MATCH2 "/M:"
+#define DICT_MATCH3 "/FIND:"
+#define DICT_DEFINE "/DEFINE:"
+#define DICT_DEFINE2 "/D:"
+#define DICT_DEFINE3 "/LOOKUP:"
+
+
/*
* Forward declarations.
*/
@@ -93,6 +102,7 @@ const struct Curl_handler Curl_handler_dict = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_DICT, /* defport */
CURLPROTO_DICT, /* protocol */
CURLPROTO_DICT, /* family */
@@ -105,7 +115,7 @@ static char *unescape_word(const char *input)
struct dynbuf out;
const char *ptr;
CURLcode result = CURLE_OK;
- Curl_dyn_init(&out, DYN_DICT_WORD);
+ curlx_dyn_init(&out, DYN_DICT_WORD);
/* According to RFC2229 section 2.2, these letters need to be escaped with
\[letter] */
@@ -113,13 +123,13 @@ static char *unescape_word(const char *input)
char ch = *ptr;
if((ch <= 32) || (ch == 127) ||
(ch == '\'') || (ch == '\"') || (ch == '\\'))
- result = Curl_dyn_addn(&out, "\\", 1);
+ result = curlx_dyn_addn(&out, "\\", 1);
if(!result)
- result = Curl_dyn_addn(&out, ptr, 1);
+ result = curlx_dyn_addn(&out, ptr, 1);
if(result)
return NULL;
}
- return Curl_dyn_ptr(&out);
+ return curlx_dyn_ptr(&out);
}
/* sendf() sends formatted data to the server */
@@ -211,16 +221,8 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
if(!word || (*word == (char)0)) {
infof(data, "lookup word is missing");
- word = (char *)"default";
- }
- if(!database || (*database == (char)0)) {
- database = (char *)"!";
}
- if(!strategy || (*strategy == (char)0)) {
- strategy = (char *)".";
- }
-
- eword = unescape_word(word);
+ eword = unescape_word((!word || (*word == (char)0)) ? "default" : word);
if(!eword) {
result = CURLE_OUT_OF_MEMORY;
goto error;
@@ -233,8 +235,8 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
"%s " /* strategy */
"%s\r\n" /* word */
"QUIT\r\n",
- database,
- strategy,
+ (!database || (*database == (char)0)) ? "!" : database,
+ (!strategy || (*strategy == (char)0)) ? "." : strategy,
eword);
if(result) {
@@ -262,13 +264,8 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
if(!word || (*word == (char)0)) {
infof(data, "lookup word is missing");
- word = (char *)"default";
}
- if(!database || (*database == (char)0)) {
- database = (char *)"!";
- }
-
- eword = unescape_word(word);
+ eword = unescape_word((!word || (*word == (char)0)) ? "default" : word);
if(!eword) {
result = CURLE_OUT_OF_MEMORY;
goto error;
@@ -280,7 +277,7 @@ static CURLcode dict_do(struct Curl_easy *data, bool *done)
"%s " /* database */
"%s\r\n" /* word */
"QUIT\r\n",
- database,
+ (!database || (*database == (char)0)) ? "!" : database,
eword);
if(result) {
diff --git a/libs/libcurl/src/dllmain.c b/libs/libcurl/src/dllmain.c
index ad064c8b9d..2507cf8c47 100644
--- a/libs/libcurl/src/dllmain.c
+++ b/libs/libcurl/src/dllmain.c
@@ -28,22 +28,13 @@
#include <openssl/crypto.h>
#endif
-/* The fourth-to-last include */
-#ifdef __CYGWIN__
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#ifdef _WIN32
-#undef _WIN32
-#endif
-#endif
-
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
#include "memdebug.h"
-/* DllMain() must only be defined for Windows and Cygwin DLL builds. */
-#if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(CURL_STATICLIB)
+/* DllMain() must only be defined for Windows DLL builds. */
+#if defined(_WIN32) && !defined(CURL_STATICLIB)
#if defined(USE_OPENSSL) && \
!defined(OPENSSL_IS_AWSLC) && \
diff --git a/libs/libcurl/src/doh.c b/libs/libcurl/src/doh.c
index 725361ace0..5f9ede520d 100644
--- a/libs/libcurl/src/doh.c
+++ b/libs/libcurl/src/doh.c
@@ -34,21 +34,20 @@
#include "multiif.h"
#include "url.h"
#include "share.h"
-#include "curl_base64.h"
+#include "curlx/base64.h"
#include "connect.h"
#include "strdup.h"
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
+#include "escape.h"
+#include "urlapi-int.h"
+
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
#include "memdebug.h"
-#include "escape.h"
#define DNS_CLASS_IN 0x01
-/* doh_print_buf truncates if the hex string will be more than this */
-#define LOCAL_PB_HEXMAX 400
-
#ifndef CURL_DISABLE_VERBOSE_STRINGS
static const char * const errors[]={
"",
@@ -74,10 +73,6 @@ static const char *doh_strerror(DOHcode code)
return "bad error code";
}
-struct curl_trc_feat Curl_doh_trc = {
- "DoH",
- CURL_LOG_LVL_NONE,
-};
#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
/* @unittest 1655
@@ -120,7 +115,7 @@ UNITTEST DOHcode doh_req_encode(const char *host,
if(host[hostlen-1]!='.')
expected_len++;
- if(expected_len > (256 + 16)) /* RFCs 1034, 1035 */
+ if(expected_len > DOH_MAX_DNSREQ_SIZE)
return DOH_DNS_NAME_TOO_LONG;
if(len < expected_len)
@@ -180,18 +175,25 @@ UNITTEST DOHcode doh_req_encode(const char *host,
}
static size_t
-doh_write_cb(char *contents, size_t size, size_t nmemb, void *userp)
+doh_probe_write_cb(char *contents, size_t size, size_t nmemb, void *userp)
{
size_t realsize = size * nmemb;
- struct dynbuf *mem = (struct dynbuf *)userp;
+ struct Curl_easy *data = userp;
+ struct doh_request *doh_req = Curl_meta_get(data, CURL_EZM_DOH_PROBE);
+ if(!doh_req)
+ return CURL_WRITEFUNC_ERROR;
- if(Curl_dyn_addn(mem, contents, realsize))
+ if(curlx_dyn_addn(&doh_req->resp_body, contents, realsize))
return 0;
return realsize;
}
#if defined(USE_HTTPSRR) && defined(DEBUGBUILD)
+
+/* doh_print_buf truncates if the hex string will be more than this */
+#define LOCAL_PB_HEXMAX 400
+
static void doh_print_buf(struct Curl_easy *data,
const char *prefix,
unsigned char *buf, size_t len)
@@ -211,22 +213,45 @@ static void doh_print_buf(struct Curl_easy *data,
}
#endif
-/* called from multi.c when this DoH transfer is complete */
-static int doh_done(struct Curl_easy *doh, CURLcode result)
+/* called from multi when a sub transfer, e.g. doh probe, is done.
+ * This looks up the the probe response at its meta CURL_EZM_DOH_PROBE
+ * and copies the response body over to the struct at the master's
+ * meta at CURL_EZM_DOH_MASTER. */
+static void doh_probe_done(struct Curl_easy *data,
+ struct Curl_easy *doh, CURLcode result)
{
- struct Curl_easy *data; /* the transfer that asked for the DoH probe */
+ struct doh_probes *dohp = data->state.async.doh;
+ DEBUGASSERT(dohp);
+ if(dohp) {
+ struct doh_request *doh_req = Curl_meta_get(doh, CURL_EZM_DOH_PROBE);
+ int i;
+
+ for(i = 0; i < DOH_SLOT_COUNT; ++i) {
+ if(dohp->probe_resp[i].probe_mid == doh->mid)
+ break;
+ }
+ if(i >= DOH_SLOT_COUNT) {
+ failf(data, "unknown sub request done");
+ return;
+ }
- data = Curl_multi_get_handle(doh->multi, doh->set.dohfor_mid);
- if(!data) {
- DEBUGF(infof(doh, "doh_done: xfer for mid=%" FMT_OFF_T
- " not found", doh->set.dohfor_mid));
- DEBUGASSERT(0);
- }
- else {
- struct doh_probes *dohp = data->req.doh;
- /* one of the DoH request done for the 'data' transfer is now complete! */
dohp->pending--;
infof(doh, "a DoH request is completed, %u to go", dohp->pending);
+ dohp->probe_resp[i].result = result;
+ /* We expect either the meta data still to exist or the sub request
+ * to have already failed. */
+ DEBUGASSERT(doh_req || result);
+ if(doh_req) {
+ if(!result) {
+ dohp->probe_resp[i].dnstype = doh_req->dnstype;
+ result = curlx_dyn_addn(&dohp->probe_resp[i].body,
+ curlx_dyn_ptr(&doh_req->resp_body),
+ curlx_dyn_len(&doh_req->resp_body));
+ curlx_dyn_free(&doh_req->resp_body);
+ }
+ Curl_meta_remove(doh, CURL_EZM_DOH_PROBE);
+ }
+
if(result)
infof(doh, "DoH request %s", curl_easy_strerror(result));
@@ -235,7 +260,18 @@ static int doh_done(struct Curl_easy *doh, CURLcode result)
Curl_expire(data, 0, EXPIRE_RUN_NOW);
}
}
- return 0;
+}
+
+static void doh_probe_dtor(void *key, size_t klen, void *e)
+{
+ (void)key;
+ (void)klen;
+ if(e) {
+ struct doh_request *doh_req = e;
+ curl_slist_free_all(doh_req->req_hds);
+ curlx_dyn_free(&doh_req->resp_body);
+ free(e);
+ }
}
#define ERROR_CHECK_SETOPT(x,y) \
@@ -247,30 +283,48 @@ static int doh_done(struct Curl_easy *doh, CURLcode result)
goto error; \
} while(0)
-static CURLcode doh_run_probe(struct Curl_easy *data,
- struct doh_probe *p, DNStype dnstype,
+static CURLcode doh_probe_run(struct Curl_easy *data,
+ DNStype dnstype,
const char *host,
const char *url, CURLM *multi,
- struct curl_slist *headers)
+ unsigned int *pmid)
{
struct Curl_easy *doh = NULL;
CURLcode result = CURLE_OK;
timediff_t timeout_ms;
- DOHcode d = doh_req_encode(host, dnstype, p->req_body, sizeof(p->req_body),
- &p->req_body_len);
+ struct doh_request *doh_req;
+ DOHcode d;
+
+ *pmid = UINT_MAX;
+
+ doh_req = calloc(1, sizeof(*doh_req));
+ if(!doh_req)
+ return CURLE_OUT_OF_MEMORY;
+ doh_req->dnstype = dnstype;
+ curlx_dyn_init(&doh_req->resp_body, DYN_DOH_RESPONSE);
+
+ d = doh_req_encode(host, dnstype, doh_req->req_body,
+ sizeof(doh_req->req_body),
+ &doh_req->req_body_len);
if(d) {
failf(data, "Failed to encode DoH packet [%d]", d);
- return CURLE_OUT_OF_MEMORY;
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
}
- p->dnstype = dnstype;
- Curl_dyn_init(&p->resp_body, DYN_DOH_RESPONSE);
-
timeout_ms = Curl_timeleft(data, NULL, TRUE);
if(timeout_ms <= 0) {
result = CURLE_OPERATION_TIMEDOUT;
goto error;
}
+
+ doh_req->req_hds =
+ curl_slist_append(NULL, "Content-Type: application/dns-message");
+ if(!doh_req->req_hds) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+
/* Curl_open() is the internal version of curl_easy_init() */
result = Curl_open(&doh);
if(result)
@@ -278,33 +332,32 @@ static CURLcode doh_run_probe(struct Curl_easy *data,
/* pass in the struct pointer via a local variable to please coverity and
the gcc typecheck helpers */
- doh->state.internal = TRUE;
#ifndef CURL_DISABLE_VERBOSE_STRINGS
- doh->state.feat = &Curl_doh_trc;
+ doh->state.feat = &Curl_trc_feat_dns;
#endif
ERROR_CHECK_SETOPT(CURLOPT_URL, url);
ERROR_CHECK_SETOPT(CURLOPT_DEFAULT_PROTOCOL, "https");
- ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb);
- ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, &p->resp_body);
- ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->req_body);
- ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->req_body_len);
- ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers);
+ ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_probe_write_cb);
+ ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, doh);
+ ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, doh_req->req_body);
+ ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)doh_req->req_body_len);
+ ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, doh_req->req_hds);
#ifdef USE_HTTP2
ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
ERROR_CHECK_SETOPT(CURLOPT_PIPEWAIT, 1L);
#endif
#ifndef DEBUGBUILD
/* enforce HTTPS if not debug */
- ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
+ ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, (long)CURLPROTO_HTTPS);
#else
/* in debug mode, also allow http */
- ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, CURLPROTO_HTTP|CURLPROTO_HTTPS);
+ ERROR_CHECK_SETOPT(CURLOPT_PROTOCOLS, (long)CURLPROTO_HTTP|CURLPROTO_HTTPS);
#endif
ERROR_CHECK_SETOPT(CURLOPT_TIMEOUT_MS, (long)timeout_ms);
ERROR_CHECK_SETOPT(CURLOPT_SHARE, (CURLSH *)data->share);
if(data->set.err && data->set.err != stderr)
ERROR_CHECK_SETOPT(CURLOPT_STDERR, data->set.err);
- if(Curl_trc_ft_is_verbose(data, &Curl_doh_trc))
+ if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_dns))
ERROR_CHECK_SETOPT(CURLOPT_VERBOSE, 1L);
if(data->set.no_signal)
ERROR_CHECK_SETOPT(CURLOPT_NOSIGNAL, 1L);
@@ -357,26 +410,16 @@ static CURLcode doh_run_probe(struct Curl_easy *data,
data->set.str[STRING_SSL_EC_CURVES]);
}
- {
- long mask =
- (data->set.ssl.enable_beast ?
- CURLSSLOPT_ALLOW_BEAST : 0) |
- (data->set.ssl.no_revoke ?
- CURLSSLOPT_NO_REVOKE : 0) |
- (data->set.ssl.no_partialchain ?
- CURLSSLOPT_NO_PARTIALCHAIN : 0) |
- (data->set.ssl.revoke_best_effort ?
- CURLSSLOPT_REVOKE_BEST_EFFORT : 0) |
- (data->set.ssl.native_ca_store ?
- CURLSSLOPT_NATIVE_CA : 0) |
- (data->set.ssl.auto_client_cert ?
- CURLSSLOPT_AUTO_CLIENT_CERT : 0);
-
- (void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, mask);
- }
+ (void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS,
+ (long)data->set.ssl.primary.ssl_options);
+
+ doh->state.internal = TRUE;
+ doh->master_mid = data->mid; /* master transfer of this one */
- doh->set.fmultidone = doh_done;
- doh->set.dohfor_mid = data->mid; /* for which transfer this is done */
+ result = Curl_meta_set(doh, CURL_EZM_DOH_PROBE, doh_req, doh_probe_dtor);
+ doh_req = NULL; /* call took ownership */
+ if(result)
+ goto error;
/* DoH handles must not inherit private_data. The handles may be passed to
the user via callbacks and the user will be able to identify them as
@@ -387,12 +430,13 @@ static CURLcode doh_run_probe(struct Curl_easy *data,
if(curl_multi_add_handle(multi, doh))
goto error;
- p->easy_mid = doh->mid;
+ *pmid = doh->mid;
return CURLE_OK;
error:
Curl_close(&doh);
- p->easy_mid = -1;
+ if(doh_req)
+ doh_probe_dtor(NULL, 0, doh_req);
return result;
}
@@ -404,57 +448,59 @@ error:
struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
const char *hostname,
int port,
+ int ip_version,
int *waitp)
{
CURLcode result = CURLE_OK;
- struct doh_probes *dohp;
+ struct doh_probes *dohp = NULL;
struct connectdata *conn = data->conn;
size_t i;
-#ifdef USE_HTTPSRR
- /* for now, this is only used when ECH is enabled */
-# ifdef USE_ECH
- char *qname = NULL;
-# endif
-#endif
- *waitp = FALSE;
- (void)hostname;
- (void)port;
- DEBUGASSERT(!data->req.doh);
DEBUGASSERT(conn);
+ DEBUGASSERT(!data->state.async.doh);
+ if(data->state.async.doh)
+ Curl_doh_cleanup(data);
+
+ data->state.async.done = FALSE;
+ data->state.async.port = port;
+ data->state.async.ip_version = ip_version;
+ data->state.async.hostname = strdup(hostname);
+ if(!data->state.async.hostname)
+ return NULL;
/* start clean, consider allocating this struct on demand */
- dohp = data->req.doh = calloc(1, sizeof(struct doh_probes));
+ data->state.async.doh = dohp = calloc(1, sizeof(struct doh_probes));
if(!dohp)
return NULL;
for(i = 0; i < DOH_SLOT_COUNT; ++i) {
- dohp->probe[i].easy_mid = -1;
+ dohp->probe_resp[i].probe_mid = UINT_MAX;
+ curlx_dyn_init(&dohp->probe_resp[i].body, DYN_DOH_RESPONSE);
}
conn->bits.doh = TRUE;
- dohp->host = hostname;
- dohp->port = port;
- dohp->req_hds =
- curl_slist_append(NULL,
- "Content-Type: application/dns-message");
- if(!dohp->req_hds)
- goto error;
+ dohp->host = data->state.async.hostname;
+ dohp->port = data->state.async.port;
+ /* We are making sub easy handles and want to be called back when
+ * one is done. */
+ data->sub_xfer_done = doh_probe_done;
/* create IPv4 DoH request */
- result = doh_run_probe(data, &dohp->probe[DOH_SLOT_IPV4],
- DNS_TYPE_A, hostname, data->set.str[STRING_DOH],
- data->multi, dohp->req_hds);
+ result = doh_probe_run(data, DNS_TYPE_A,
+ hostname, data->set.str[STRING_DOH],
+ data->multi,
+ &dohp->probe_resp[DOH_SLOT_IPV4].probe_mid);
if(result)
goto error;
dohp->pending++;
#ifdef USE_IPV6
- if((conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
+ if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
/* create IPv6 DoH request */
- result = doh_run_probe(data, &dohp->probe[DOH_SLOT_IPV6],
- DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH],
- data->multi, dohp->req_hds);
+ result = doh_probe_run(data, DNS_TYPE_AAAA,
+ hostname, data->set.str[STRING_DOH],
+ data->multi,
+ &dohp->probe_resp[DOH_SLOT_IPV6].probe_mid);
if(result)
goto error;
dohp->pending++;
@@ -462,34 +508,23 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
#endif
#ifdef USE_HTTPSRR
- /*
- * TODO: Figure out the conditions under which we want to make
- * a request for an HTTPS RR when we are not doing ECH. For now,
- * making this request breaks a bunch of DoH tests, e.g. test2100,
- * where the additional request does not match the pre-cooked data
- * files, so there is a bit of work attached to making the request
- * in a non-ECH use-case. For the present, we will only make the
- * request when ECH is enabled in the build and is being used for
- * the curl operation.
- */
-# ifdef USE_ECH
- if(data->set.tls_ech & CURLECH_ENABLE
- || data->set.tls_ech & CURLECH_HARD) {
- if(port == 443)
- qname = strdup(hostname);
- else
+ if(conn->handler->protocol & PROTO_FAMILY_HTTP) {
+ /* Only use HTTPS RR for HTTP(S) transfers */
+ char *qname = NULL;
+ if(port != PORT_HTTPS) {
qname = aprintf("_%d._https.%s", port, hostname);
- if(!qname)
- goto error;
- result = doh_run_probe(data, &dohp->probe[DOH_SLOT_HTTPS_RR],
- DNS_TYPE_HTTPS, qname, data->set.str[STRING_DOH],
- data->multi, dohp->req_hds);
- Curl_safefree(qname);
+ if(!qname)
+ goto error;
+ }
+ result = doh_probe_run(data, DNS_TYPE_HTTPS,
+ qname ? qname : hostname, data->set.str[STRING_DOH],
+ data->multi,
+ &dohp->probe_resp[DOH_SLOT_HTTPS_RR].probe_mid);
+ free(qname);
if(result)
goto error;
dohp->pending++;
}
-# endif
#endif
*waitp = TRUE; /* this never returns synchronously */
return NULL;
@@ -615,14 +650,14 @@ static DOHcode doh_store_cname(const unsigned char *doh, size_t dohlen,
index++;
if(length) {
- if(Curl_dyn_len(c)) {
- if(Curl_dyn_addn(c, STRCONST(".")))
+ if(curlx_dyn_len(c)) {
+ if(curlx_dyn_addn(c, STRCONST(".")))
return DOH_OUT_OF_MEM;
}
if((index + length) > dohlen)
return DOH_DNS_BAD_LABEL;
- if(Curl_dyn_addn(c, &doh[index], length))
+ if(curlx_dyn_addn(c, &doh[index], length))
return DOH_OUT_OF_MEM;
index += length;
}
@@ -686,7 +721,7 @@ UNITTEST void de_init(struct dohentry *de)
memset(de, 0, sizeof(*de));
de->ttl = INT_MAX;
for(i = 0; i < DOH_MAX_CNAME; i++)
- Curl_dyn_init(&de->cname[i], DYN_DOH_CNAME);
+ curlx_dyn_init(&de->cname[i], DYN_DOH_CNAME);
}
@@ -873,7 +908,7 @@ static void doh_show(struct Curl_easy *data,
}
#endif
for(i = 0; i < d->numcname; i++) {
- infof(data, "CNAME: %s", Curl_dyn_ptr(&d->cname[i]));
+ infof(data, "CNAME: %s", curlx_dyn_ptr(&d->cname[i]));
}
}
#else
@@ -886,7 +921,7 @@ static void doh_show(struct Curl_easy *data,
* This function returns a pointer to the first element of a newly allocated
* Curl_addrinfo struct linked list filled with the data from a set of DoH
* lookups. Curl_addrinfo is meant to work like the addrinfo struct does for
- * a IPv6 stack, but usable also for IPv4, all hosts and environments.
+ * an IPv6 stack, but usable also for IPv4, all hosts and environments.
*
* The memory allocated by this function *MUST* be free'd later on calling
* Curl_freeaddrinfo(). For each successful call to this function there
@@ -961,11 +996,7 @@ static CURLcode doh2ai(const struct dohentry *de, const char *hostname,
addr = (void *)ai->ai_addr; /* storage area for this info */
DEBUGASSERT(sizeof(struct in_addr) == sizeof(de->addr[i].ip.v4));
memcpy(&addr->sin_addr, &de->addr[i].ip.v4, sizeof(struct in_addr));
-#ifdef __MINGW32__
- addr->sin_family = (short)addrtype;
-#else
- addr->sin_family = addrtype;
-#endif
+ addr->sin_family = (CURL_SA_FAMILY_T)addrtype;
addr->sin_port = htons((unsigned short)port);
break;
@@ -974,11 +1005,7 @@ static CURLcode doh2ai(const struct dohentry *de, const char *hostname,
addr6 = (void *)ai->ai_addr; /* storage area for this info */
DEBUGASSERT(sizeof(struct in6_addr) == sizeof(de->addr[i].ip.v6));
memcpy(&addr6->sin6_addr, &de->addr[i].ip.v6, sizeof(struct in6_addr));
-#ifdef __MINGW32__
- addr6->sin6_family = (short)addrtype;
-#else
- addr6->sin6_family = addrtype;
-#endif
+ addr6->sin6_family = (CURL_SA_FAMILY_T)addrtype;
addr6->sin6_port = htons((unsigned short)port);
break;
#endif
@@ -1018,7 +1045,7 @@ UNITTEST void de_cleanup(struct dohentry *d)
{
int i = 0;
for(i = 0; i < d->numcname; i++) {
- Curl_dyn_free(&d->cname[i]);
+ curlx_dyn_free(&d->cname[i]);
}
#ifdef USE_HTTPSRR
for(i = 0; i < d->numhttps_rrs; i++)
@@ -1042,226 +1069,121 @@ UNITTEST void de_cleanup(struct dohentry *d)
* just after the end of the DNS name encoding on output. (And
* that is why it is an "unsigned char **" :-)
*/
-static CURLcode doh_decode_rdata_name(unsigned char **buf, size_t *remaining,
- char **dnsname)
+static CURLcode doh_decode_rdata_name(const unsigned char **buf,
+ size_t *remaining, char **dnsname)
{
- unsigned char *cp = NULL;
- int rem = 0;
+ const unsigned char *cp = NULL;
+ size_t rem = 0;
unsigned char clen = 0; /* chunk len */
struct dynbuf thename;
DEBUGASSERT(buf && remaining && dnsname);
- if(!buf || !remaining || !dnsname)
- return CURLE_OUT_OF_MEMORY;
- rem = (int)*remaining;
- if(rem <= 0) {
- Curl_dyn_free(&thename);
+ if(!buf || !remaining || !dnsname || !*remaining)
return CURLE_OUT_OF_MEMORY;
- }
- Curl_dyn_init(&thename, CURL_MAXLEN_host_name);
+ curlx_dyn_init(&thename, CURL_MAXLEN_host_name);
+ rem = *remaining;
cp = *buf;
clen = *cp++;
if(clen == 0) {
/* special case - return "." as name */
- if(Curl_dyn_addn(&thename, ".", 1))
+ if(curlx_dyn_addn(&thename, ".", 1))
return CURLE_OUT_OF_MEMORY;
}
while(clen) {
if(clen >= rem) {
- Curl_dyn_free(&thename);
+ curlx_dyn_free(&thename);
return CURLE_OUT_OF_MEMORY;
}
- if(Curl_dyn_addn(&thename, cp, clen) ||
- Curl_dyn_addn(&thename, ".", 1))
+ if(curlx_dyn_addn(&thename, cp, clen) ||
+ curlx_dyn_addn(&thename, ".", 1))
return CURLE_TOO_LARGE;
cp += clen;
rem -= (clen + 1);
if(rem <= 0) {
- Curl_dyn_free(&thename);
+ curlx_dyn_free(&thename);
return CURLE_OUT_OF_MEMORY;
}
clen = *cp++;
}
*buf = cp;
*remaining = rem - 1;
- *dnsname = Curl_dyn_ptr(&thename);
+ *dnsname = curlx_dyn_ptr(&thename);
return CURLE_OK;
}
-static CURLcode doh_decode_rdata_alpn(unsigned char *rrval, size_t len,
- char **alpns)
-{
- /*
- * spec here is as per draft-ietf-dnsop-svcb-https, section-7.1.1
- * encoding is catenated list of strings each preceded by a one
- * octet length
- * output is comma-sep list of the strings
- * implementations may or may not handle quoting of comma within
- * string values, so we might see a comma within the wire format
- * version of a string, in which case we will precede that by a
- * backslash - same goes for a backslash character, and of course
- * we need to use two backslashes in strings when we mean one;-)
- */
- int remaining = (int) len;
- char *oval;
- size_t i;
- unsigned char *cp = rrval;
- struct dynbuf dval;
-
- if(!alpns)
- return CURLE_OUT_OF_MEMORY;
- Curl_dyn_init(&dval, DYN_DOH_RESPONSE);
- remaining = (int)len;
- cp = rrval;
- while(remaining > 0) {
- size_t tlen = (size_t) *cp++;
-
- /* if not 1st time, add comma */
- if(remaining != (int)len && Curl_dyn_addn(&dval, ",", 1))
- goto err;
- remaining--;
- if(tlen > (size_t)remaining)
- goto err;
- /* add escape char if needed, clunky but easier to read */
- for(i = 0; i != tlen; i++) {
- if('\\' == *cp || ',' == *cp) {
- if(Curl_dyn_addn(&dval, "\\", 1))
- goto err;
- }
- if(Curl_dyn_addn(&dval, cp++, 1))
- goto err;
- }
- remaining -= (int)tlen;
- }
- /* this string is always null terminated */
- oval = Curl_dyn_ptr(&dval);
- if(!oval)
- goto err;
- *alpns = oval;
- return CURLE_OK;
-err:
- Curl_dyn_free(&dval);
- return CURLE_BAD_CONTENT_ENCODING;
-}
+UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data,
+ const unsigned char *cp, size_t len,
+ struct Curl_https_rrinfo **hrr);
-#ifdef DEBUGBUILD
-static CURLcode doh_test_alpn_escapes(void)
+/* @unittest 1658 */
+UNITTEST CURLcode doh_resp_decode_httpsrr(struct Curl_easy *data,
+ const unsigned char *cp, size_t len,
+ struct Curl_https_rrinfo **hrr)
{
- /* we will use an example from draft-ietf-dnsop-svcb, figure 10 */
- static unsigned char example[] = {
- 0x08, /* length 8 */
- 0x66, 0x5c, 0x6f, 0x6f, 0x2c, 0x62, 0x61, 0x72, /* value "f\\oo,bar" */
- 0x02, /* length 2 */
- 0x68, 0x32 /* value "h2" */
- };
- size_t example_len = sizeof(example);
- char *aval = NULL;
- static const char *expected = "f\\\\oo\\,bar,h2";
-
- if(doh_decode_rdata_alpn(example, example_len, &aval) != CURLE_OK)
- return CURLE_BAD_CONTENT_ENCODING;
- if(strlen(aval) != strlen(expected))
- return CURLE_BAD_CONTENT_ENCODING;
- if(memcmp(aval, expected, strlen(aval)))
- return CURLE_BAD_CONTENT_ENCODING;
- return CURLE_OK;
-}
-#endif
-
-static CURLcode doh_resp_decode_httpsrr(unsigned char *rrval, size_t len,
- struct Curl_https_rrinfo **hrr)
-{
- size_t remaining = len;
- unsigned char *cp = rrval;
uint16_t pcode = 0, plen = 0;
+ uint32_t expected_min_pcode = 0;
struct Curl_https_rrinfo *lhrr = NULL;
char *dnsname = NULL;
+ CURLcode result = CURLE_OUT_OF_MEMORY;
+ size_t olen;
-#ifdef DEBUGBUILD
- /* a few tests of escaping, should not be here but ok for now */
- if(doh_test_alpn_escapes() != CURLE_OK)
- return CURLE_OUT_OF_MEMORY;
-#endif
+ *hrr = NULL;
+ if(len <= 2)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
lhrr = calloc(1, sizeof(struct Curl_https_rrinfo));
if(!lhrr)
return CURLE_OUT_OF_MEMORY;
- lhrr->val = Curl_memdup(rrval, len);
- if(!lhrr->val)
- goto err;
- lhrr->len = len;
- if(remaining <= 2)
- goto err;
- lhrr->priority = (uint16_t)((cp[0] << 8) + cp[1]);
+ lhrr->priority = doh_get16bit(cp, 0);
cp += 2;
- remaining -= (uint16_t)2;
- if(doh_decode_rdata_name(&cp, &remaining, &dnsname) != CURLE_OK)
+ len -= 2;
+ if(doh_decode_rdata_name(&cp, &len, &dnsname) != CURLE_OK)
goto err;
lhrr->target = dnsname;
- while(remaining >= 4) {
- pcode = (uint16_t)((*cp << 8) + (*(cp + 1)));
- cp += 2;
- plen = (uint16_t)((*cp << 8) + (*(cp + 1)));
- cp += 2;
- remaining -= 4;
- if(pcode == HTTPS_RR_CODE_ALPN) {
- if(doh_decode_rdata_alpn(cp, plen, &lhrr->alpns) != CURLE_OK)
- goto err;
- }
- if(pcode == HTTPS_RR_CODE_NO_DEF_ALPN)
- lhrr->no_def_alpn = TRUE;
- else if(pcode == HTTPS_RR_CODE_IPV4) {
- if(!plen)
- goto err;
- lhrr->ipv4hints = Curl_memdup(cp, plen);
- if(!lhrr->ipv4hints)
- goto err;
- lhrr->ipv4hints_len = (size_t)plen;
- }
- else if(pcode == HTTPS_RR_CODE_ECH) {
- if(!plen)
- goto err;
- lhrr->echconfiglist = Curl_memdup(cp, plen);
- if(!lhrr->echconfiglist)
- goto err;
- lhrr->echconfiglist_len = (size_t)plen;
- }
- else if(pcode == HTTPS_RR_CODE_IPV6) {
- if(!plen)
- goto err;
- lhrr->ipv6hints = Curl_memdup(cp, plen);
- if(!lhrr->ipv6hints)
- goto err;
- lhrr->ipv6hints_len = (size_t)plen;
- }
- if(plen > 0 && plen <= remaining) {
- cp += plen;
- remaining -= plen;
+ if(Curl_junkscan(dnsname, &olen, FALSE)) {
+ /* unacceptable hostname content */
+ result = CURLE_WEIRD_SERVER_REPLY;
+ goto err;
+ }
+ lhrr->port = -1; /* until set */
+ while(len >= 4) {
+ pcode = doh_get16bit(cp, 0);
+ plen = doh_get16bit(cp, 2);
+ cp += 4;
+ len -= 4;
+ if(pcode < expected_min_pcode || plen > len) {
+ result = CURLE_WEIRD_SERVER_REPLY;
+ goto err;
}
+ result = Curl_httpsrr_set(data, lhrr, pcode, cp, plen);
+ if(result)
+ goto err;
+ cp += plen;
+ len -= plen;
+ expected_min_pcode = pcode + 1;
}
- DEBUGASSERT(!remaining);
+ DEBUGASSERT(!len);
*hrr = lhrr;
return CURLE_OK;
err:
- if(lhrr) {
- Curl_safefree(lhrr->target);
- Curl_safefree(lhrr->echconfiglist);
- Curl_safefree(lhrr->val);
- Curl_safefree(lhrr->alpns);
- Curl_safefree(lhrr);
- }
- return CURLE_OUT_OF_MEMORY;
+ Curl_httpsrr_cleanup(lhrr);
+ Curl_safefree(lhrr);
+ return result;
}
-# ifdef DEBUGBUILD
-static void doh_print_httpsrr(struct Curl_easy *data,
- struct Curl_https_rrinfo *hrr)
+#ifdef DEBUGBUILD
+UNITTEST void doh_print_httpsrr(struct Curl_easy *data,
+ struct Curl_https_rrinfo *hrr);
+
+UNITTEST void doh_print_httpsrr(struct Curl_easy *data,
+ struct Curl_https_rrinfo *hrr)
{
DEBUGASSERT(hrr);
infof(data, "HTTPS RR: priority %d, target: %s",
hrr->priority, hrr->target);
- if(hrr->alpns)
- infof(data, "HTTPS RR: alpns %s", hrr->alpns);
+ if(hrr->alpns[0] != ALPN_none)
+ infof(data, "HTTPS RR: alpns %u %u %u %u",
+ hrr->alpns[0], hrr->alpns[1], hrr->alpns[2], hrr->alpns[3]);
else
infof(data, "HTTPS RR: no alpns");
if(hrr->no_def_alpn)
@@ -1295,14 +1217,14 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
struct Curl_dns_entry **dnsp)
{
CURLcode result;
- struct doh_probes *dohp = data->req.doh;
+ struct doh_probes *dohp = data->state.async.doh;
*dnsp = NULL; /* defaults to no response */
if(!dohp)
return CURLE_OUT_OF_MEMORY;
- if(dohp->probe[DOH_SLOT_IPV4].easy_mid < 0 &&
- dohp->probe[DOH_SLOT_IPV6].easy_mid < 0) {
- failf(data, "Could not DoH-resolve: %s", data->state.async.hostname);
+ if(dohp->probe_resp[DOH_SLOT_IPV4].probe_mid == UINT_MAX &&
+ dohp->probe_resp[DOH_SLOT_IPV6].probe_mid == UINT_MAX) {
+ failf(data, "Could not DoH-resolve: %s", dohp->host);
return CONN_IS_PROXIED(data->conn) ? CURLE_COULDNT_RESOLVE_PROXY :
CURLE_COULDNT_RESOLVE_HOST;
}
@@ -1311,19 +1233,21 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
struct dohentry de;
int slot;
+ /* Clear any result the might still be there */
+ Curl_resolv_unlink(data, &data->state.async.dns);
+
memset(rc, 0, sizeof(rc));
/* remove DoH handles from multi handle and close them */
Curl_doh_close(data);
/* parse the responses, create the struct and return it! */
de_init(&de);
for(slot = 0; slot < DOH_SLOT_COUNT; slot++) {
- struct doh_probe *p = &dohp->probe[slot];
+ struct doh_response *p = &dohp->probe_resp[slot];
if(!p->dnstype)
continue;
- rc[slot] = doh_resp_decode(Curl_dyn_uptr(&p->resp_body),
- Curl_dyn_len(&p->resp_body),
+ rc[slot] = doh_resp_decode(curlx_dyn_uptr(&p->body),
+ curlx_dyn_len(&p->body),
p->dnstype, &de);
- Curl_dyn_free(&p->resp_body);
#ifndef CURL_DISABLE_VERBOSE_STRINGS
if(rc[slot]) {
infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]),
@@ -1339,8 +1263,8 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
struct Curl_addrinfo *ai;
- if(Curl_trc_ft_is_verbose(data, &Curl_doh_trc)) {
- infof(data, "[DoH] hostname: %s", dohp->host);
+ if(Curl_trc_ft_is_verbose(data, &Curl_trc_feat_dns)) {
+ CURL_TRC_DNS(data, "hostname: %s", dohp->host);
doh_show(data, &de);
}
@@ -1350,45 +1274,35 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
return result;
}
- if(data->share)
- Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
- /* we got a response, store it in the cache */
- dns = Curl_cache_addr(data, ai, dohp->host, 0, dohp->port, FALSE);
-
- if(data->share)
- Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
-
- if(!dns) {
- /* returned failure, bail out nicely */
- Curl_freeaddrinfo(ai);
- }
- else {
- data->state.async.dns = dns;
- *dnsp = dns;
- result = CURLE_OK; /* address resolution OK */
- }
- } /* address processing done */
-
- /* Now process any build-specific attributes retrieved from DNS */
+ /* we got a response, create a dns entry. */
+ dns = Curl_dnscache_mk_entry(data, ai, dohp->host, 0, dohp->port, FALSE);
+ if(dns) {
+ /* Now add and HTTPSRR information if we have */
#ifdef USE_HTTPSRR
- if(de.numhttps_rrs > 0 && result == CURLE_OK && *dnsp) {
- struct Curl_https_rrinfo *hrr = NULL;
- result = doh_resp_decode_httpsrr(de.https_rrs->val, de.https_rrs->len,
- &hrr);
- if(result) {
- infof(data, "Failed to decode HTTPS RR");
- return result;
- }
- infof(data, "Some HTTPS RR to process");
+ if(de.numhttps_rrs > 0 && result == CURLE_OK) {
+ struct Curl_https_rrinfo *hrr = NULL;
+ result = doh_resp_decode_httpsrr(data, de.https_rrs->val,
+ de.https_rrs->len, &hrr);
+ if(result) {
+ infof(data, "Failed to decode HTTPS RR");
+ return result;
+ }
+ infof(data, "Some HTTPS RR to process");
# ifdef DEBUGBUILD
- doh_print_httpsrr(data, hrr);
+ doh_print_httpsrr(data, hrr);
# endif
- (*dnsp)->hinfo = hrr;
- }
+ dns->hinfo = hrr;
+ }
#endif
+ /* and add the entry to the cache */
+ data->state.async.dns = dns;
+ result = Curl_dnscache_add(data, dns);
+ *dnsp = data->state.async.dns;
+ }
+ } /* address processing done */
/* All done */
+ data->state.async.done = TRUE;
de_cleanup(&de);
Curl_doh_cleanup(data);
return result;
@@ -1401,41 +1315,43 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
void Curl_doh_close(struct Curl_easy *data)
{
- struct doh_probes *doh = data->req.doh;
+ struct doh_probes *doh = data->state.async.doh;
if(doh && data->multi) {
struct Curl_easy *probe_data;
- curl_off_t mid;
+ unsigned int mid;
size_t slot;
for(slot = 0; slot < DOH_SLOT_COUNT; slot++) {
- mid = doh->probe[slot].easy_mid;
- if(mid < 0)
+ mid = doh->probe_resp[slot].probe_mid;
+ if(mid == UINT_MAX)
continue;
- doh->probe[slot].easy_mid = -1;
+ doh->probe_resp[slot].probe_mid = UINT_MAX;
/* should have been called before data is removed from multi handle */
DEBUGASSERT(data->multi);
- probe_data = data->multi ? Curl_multi_get_handle(data->multi, mid) :
+ probe_data = data->multi ? Curl_multi_get_easy(data->multi, mid) :
NULL;
if(!probe_data) {
- DEBUGF(infof(data, "Curl_doh_close: xfer for mid=%"
- FMT_OFF_T " not found!",
- doh->probe[slot].easy_mid));
+ DEBUGF(infof(data, "Curl_doh_close: xfer for mid=%u not found!",
+ doh->probe_resp[slot].probe_mid));
continue;
}
/* data->multi might already be reset at this time */
curl_multi_remove_handle(data->multi, probe_data);
Curl_close(&probe_data);
}
+ data->sub_xfer_done = NULL;
}
}
void Curl_doh_cleanup(struct Curl_easy *data)
{
- struct doh_probes *doh = data->req.doh;
- if(doh) {
+ struct doh_probes *dohp = data->state.async.doh;
+ if(dohp) {
+ int i;
Curl_doh_close(data);
- curl_slist_free_all(doh->req_hds);
- data->req.doh->req_hds = NULL;
- Curl_safefree(data->req.doh);
+ for(i = 0; i < DOH_SLOT_COUNT; ++i) {
+ curlx_dyn_free(&dohp->probe_resp[i].body);
+ }
+ Curl_safefree(data->state.async.doh);
}
}
diff --git a/libs/libcurl/src/doh.h b/libs/libcurl/src/doh.h
index cc0b3dd3ec..65312c113d 100644
--- a/libs/libcurl/src/doh.h
+++ b/libs/libcurl/src/doh.h
@@ -28,6 +28,7 @@
#include "curl_addrinfo.h"
#ifdef USE_HTTPSRR
# include <stdint.h>
+# include "httpsrr.h"
#endif
#ifndef CURL_DISABLE_DOH
@@ -58,15 +59,6 @@ typedef enum {
DNS_TYPE_HTTPS = 65
} DNStype;
-/* one of these for each DoH request */
-struct doh_probe {
- curl_off_t easy_mid; /* multi id of easy handle doing the lookup */
- DNStype dnstype;
- unsigned char req_body[512];
- size_t req_body_len;
- struct dynbuf resp_body;
-};
-
enum doh_slot_num {
/* Explicit values for first two symbols so as to match hard-coded
* constants in existing code
@@ -88,9 +80,32 @@ enum doh_slot_num {
DOH_SLOT_COUNT
};
-struct doh_probes {
+#define CURL_EZM_DOH_PROBE "ezm:doh-p"
+
+/* the largest one we can make, based on RFCs 1034, 1035 */
+#define DOH_MAX_DNSREQ_SIZE (256 + 16)
+
+/* each DoH probe request has this
+ * as easy meta for CURL_EZM_DOH_PROBE */
+struct doh_request {
+ unsigned char req_body[DOH_MAX_DNSREQ_SIZE];
struct curl_slist *req_hds;
- struct doh_probe probe[DOH_SLOT_COUNT];
+ struct dynbuf resp_body;
+ size_t req_body_len;
+ DNStype dnstype;
+};
+
+struct doh_response {
+ unsigned int probe_mid;
+ struct dynbuf body;
+ DNStype dnstype;
+ CURLcode result;
+};
+
+/* each transfer firing off DoH requests has this
+ * as easy meta for CURL_EZM_DOH_MASTER */
+struct doh_probes {
+ struct doh_response probe_resp[DOH_SLOT_COUNT];
unsigned int pending; /* still outstanding probes */
int port;
const char *host;
@@ -104,6 +119,7 @@ struct doh_probes {
struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
const char *hostname,
int port,
+ int ip_version,
int *waitp);
CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
@@ -124,19 +140,6 @@ struct dohaddr {
#ifdef USE_HTTPSRR
/*
- * These are the code points for DNS wire format SvcParams as
- * per draft-ietf-dnsop-svcb-https
- * Not all are supported now, and even those that are may need
- * more work in future to fully support the spec.
- */
-#define HTTPS_RR_CODE_ALPN 0x01
-#define HTTPS_RR_CODE_NO_DEF_ALPN 0x02
-#define HTTPS_RR_CODE_PORT 0x03
-#define HTTPS_RR_CODE_IPV4 0x04
-#define HTTPS_RR_CODE_ECH 0x05
-#define HTTPS_RR_CODE_IPV6 0x06
-
-/*
* These may need escaping when found within an ALPN string
* value.
*/
@@ -179,10 +182,8 @@ UNITTEST void de_init(struct dohentry *d);
UNITTEST void de_cleanup(struct dohentry *d);
#endif
-extern struct curl_trc_feat Curl_doh_trc;
-
#else /* if DoH is disabled */
-#define Curl_doh(a,b,c,d) NULL
+#define Curl_doh(a,b,c,d,e) NULL
#define Curl_doh_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST
#endif
diff --git a/libs/libcurl/src/dynhds.c b/libs/libcurl/src/dynhds.c
index 289bde68fc..6c39356761 100644
--- a/libs/libcurl/src/dynhds.c
+++ b/libs/libcurl/src/dynhds.c
@@ -359,9 +359,10 @@ CURLcode Curl_dynhds_h1_dprint(struct dynhds *dynhds, struct dynbuf *dbuf)
return result;
for(i = 0; i < dynhds->hds_len; ++i) {
- result = Curl_dyn_addf(dbuf, "%.*s: %.*s\r\n",
- (int)dynhds->hds[i]->namelen, dynhds->hds[i]->name,
- (int)dynhds->hds[i]->valuelen, dynhds->hds[i]->value);
+ result = curlx_dyn_addf(dbuf, "%.*s: %.*s\r\n",
+ (int)dynhds->hds[i]->namelen, dynhds->hds[i]->name,
+ (int)dynhds->hds[i]->valuelen,
+ dynhds->hds[i]->value);
if(result)
break;
}
diff --git a/libs/libcurl/src/dynhds.h b/libs/libcurl/src/dynhds.h
index f2af6668d6..3b95934b11 100644
--- a/libs/libcurl/src/dynhds.h
+++ b/libs/libcurl/src/dynhds.h
@@ -26,13 +26,13 @@
#include "curl_setup.h"
#include <curl/curl.h>
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
struct dynbuf;
/**
* A single header entry.
- * `name` and `value` are non-NULL and always NUL terminated.
+ * `name` and `value` are non-NULL and always null-terminated.
*/
struct dynhds_entry {
char *name;
@@ -113,7 +113,7 @@ size_t Curl_dynhds_count_name(struct dynhds *dynhds,
const char *name, size_t namelen);
/**
- * Return how often the given 0-terminated name appears in `dynhds`.
+ * Return how often the given null-terminated name appears in `dynhds`.
* Names are case-insensitive.
*/
size_t Curl_dynhds_ccount_name(struct dynhds *dynhds, const char *name);
@@ -156,14 +156,14 @@ CURLcode Curl_dynhds_cadd(struct dynhds *dynhds,
/**
* Add a single header from an HTTP/1.1 formatted line at the end. Line
- * may contain a delimiting \r\n or just \n. Any characters after
+ * may contain a delimiting CRLF or just LF. Any characters after
* that will be ignored.
*/
CURLcode Curl_dynhds_h1_cadd_line(struct dynhds *dynhds, const char *line);
/**
* Add a single header from an HTTP/1.1 formatted line at the end. Line
- * may contain a delimiting \r\n or just \n. Any characters after
+ * may contain a delimiting CRLF or just LF. Any characters after
* that will be ignored.
*/
CURLcode Curl_dynhds_h1_add_line(struct dynhds *dynhds,
diff --git a/libs/libcurl/src/easy.c b/libs/libcurl/src/easy.c
index ae99f6d84c..ff55df8e20 100644
--- a/libs/libcurl/src/easy.c
+++ b/libs/libcurl/src/easy.c
@@ -48,6 +48,8 @@
#include <curl/curl.h>
#include "transfer.h"
#include "vtls/vtls.h"
+#include "vtls/vtls_scache.h"
+#include "vquic/vquic.h"
#include "url.h"
#include "getinfo.h"
#include "hostip.h"
@@ -64,14 +66,14 @@
#include "mime.h"
#include "amigaos.h"
#include "macos.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
#include "sigpipe.h"
#include "vssh/ssh.h"
#include "setopt.h"
#include "http_digest.h"
#include "system_win32.h"
#include "http2.h"
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
#include "altsvc.h"
#include "hsts.h"
@@ -104,7 +106,7 @@ static curl_simple_lock s_lock = CURL_SIMPLE_LOCK_INIT;
* ways, but at this point it must be defined as the system-supplied strdup
* so the callback pointer is initialized correctly.
*/
-#if defined(_WIN32_WCE)
+#if defined(UNDER_CE)
#define system_strdup _strdup
#elif !defined(HAVE_STRDUP)
#define system_strdup Curl_strdup
@@ -169,6 +171,11 @@ static CURLcode global_init(long flags, bool memoryfuncs)
goto fail;
}
+ if(!Curl_vquic_init()) {
+ DEBUGF(fprintf(stderr, "Error: Curl_vquic_init failed\n"));
+ goto fail;
+ }
+
if(Curl_win32_init(flags)) {
DEBUGF(fprintf(stderr, "Error: win32_init failed\n"));
goto fail;
@@ -184,7 +191,7 @@ static CURLcode global_init(long flags, bool memoryfuncs)
goto fail;
}
- if(Curl_resolver_global_init()) {
+ if(Curl_async_global_init()) {
DEBUGF(fprintf(stderr, "Error: resolver_global_init failed\n"));
goto fail;
}
@@ -287,7 +294,7 @@ void curl_global_cleanup(void)
}
Curl_ssl_cleanup();
- Curl_resolver_global_cleanup();
+ Curl_async_global_cleanup();
#ifdef _WIN32
Curl_win32_cleanup(easy_init_flags);
@@ -541,12 +548,34 @@ static void events_setup(struct Curl_multi *multi, struct events *ev)
curl_multi_setopt(multi, CURLMOPT_SOCKETDATA, ev);
}
+/* populate_fds()
+ *
+ * populate the fds[] array
+ */
+static unsigned int populate_fds(struct pollfd *fds, struct events *ev)
+{
+ unsigned int numfds = 0;
+ struct pollfd *f;
+ struct socketmonitor *m;
+
+ f = &fds[0];
+ for(m = ev->list; m; m = m->next) {
+ f->fd = m->socket.fd;
+ f->events = m->socket.events;
+ f->revents = 0;
+#if DEBUG_EV_POLL
+ fprintf(stderr, "poll() %d check socket %d\n", numfds, f->fd);
+#endif
+ f++;
+ numfds++;
+ }
+ return numfds;
+}
/* wait_or_timeout()
*
* waits for activity on any of the given sockets, or the timeout to trigger.
*/
-
static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev)
{
bool done = FALSE;
@@ -555,37 +584,22 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev)
while(!done) {
CURLMsg *msg;
- struct socketmonitor *m;
- struct pollfd *f;
struct pollfd fds[4];
- int numfds = 0;
int pollrc;
- int i;
struct curltime before;
-
- /* populate the fds[] array */
- for(m = ev->list, f = &fds[0]; m; m = m->next) {
- f->fd = m->socket.fd;
- f->events = m->socket.events;
- f->revents = 0;
-#if DEBUG_EV_POLL
- fprintf(stderr, "poll() %d check socket %d\n", numfds, f->fd);
-#endif
- f++;
- numfds++;
- }
+ const unsigned int numfds = populate_fds(fds, ev);
/* get the time stamp to use to figure out how long poll takes */
- before = Curl_now();
+ before = curlx_now();
if(numfds) {
/* wait for activity or timeout */
#if DEBUG_EV_POLL
- fprintf(stderr, "poll(numfds=%d, timeout=%ldms)\n", numfds, ev->ms);
+ fprintf(stderr, "poll(numfds=%u, timeout=%ldms)\n", numfds, ev->ms);
#endif
- pollrc = Curl_poll(fds, (unsigned int)numfds, ev->ms);
+ pollrc = Curl_poll(fds, numfds, ev->ms);
#if DEBUG_EV_POLL
- fprintf(stderr, "poll(numfds=%d, timeout=%ldms) -> %d\n",
+ fprintf(stderr, "poll(numfds=%u, timeout=%ldms) -> %d\n",
numfds, ev->ms, pollrc);
#endif
if(pollrc < 0)
@@ -611,20 +625,15 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev)
}
else {
/* here pollrc is > 0 */
- struct Curl_llist_node *e = Curl_llist_head(&multi->process);
- struct Curl_easy *data;
- DEBUGASSERT(e);
- data = Curl_node_elem(e);
- DEBUGASSERT(data);
-
/* loop over the monitored sockets to see which ones had activity */
+ unsigned int i;
for(i = 0; i < numfds; i++) {
if(fds[i].revents) {
/* socket activity, tell libcurl */
int act = poll2cselect(fds[i].revents); /* convert */
/* sending infof "randomly" to the first easy handle */
- infof(data, "call curl_multi_socket_action(socket "
+ infof(multi->admin, "call curl_multi_socket_action(socket "
"%" FMT_SOCKET_T ")", (curl_socket_t)fds[i].fd);
mcode = curl_multi_socket_action(multi, fds[i].fd, act,
&ev->running_handles);
@@ -636,7 +645,7 @@ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev)
/* If nothing updated the timeout, we decrease it by the spent time.
* If it was updated, it has the new timeout time stored already.
*/
- timediff_t timediff = Curl_timediff(Curl_now(), before);
+ timediff_t timediff = curlx_timediff(curlx_now(), before);
if(timediff > 0) {
#if DEBUG_EV_POLL
fprintf(stderr, "poll timeout %ldms not updated, decrease by "
@@ -761,12 +770,25 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events)
return CURLE_FAILED_INIT;
}
+ /* if the handle has a connection still attached (it is/was a connect-only
+ handle) then disconnect before performing */
+ if(data->conn) {
+ struct connectdata *c;
+ curl_socket_t s;
+ Curl_detach_connection(data);
+ s = Curl_getconnectinfo(data, &c);
+ if((s != CURL_SOCKET_BAD) && c) {
+ Curl_conn_terminate(data, c, TRUE);
+ }
+ DEBUGASSERT(!data->conn);
+ }
+
if(data->multi_easy)
multi = data->multi_easy;
else {
- /* this multi handle will only ever have a single easy handled attached
- to it, so make it use minimal hashes */
- multi = Curl_multi_handle(1, 3, 7);
+ /* this multi handle will only ever have a single easy handle attached to
+ it, so make it use minimal hash sizes */
+ multi = Curl_multi_handle(16, 1, 3, 7, 3);
if(!multi)
return CURLE_OUT_OF_MEMORY;
}
@@ -916,6 +938,15 @@ static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src)
return result;
}
+static void dupeasy_meta_freeentry(void *p)
+{
+ (void)p;
+ /* Will always be FALSE. Cannot use a 0 assert here since compilers
+ * are not in agreement if they then want a NORETURN attribute or
+ * not. *sigh* */
+ DEBUGASSERT(p == NULL);
+}
+
/*
* curl_easy_duphandle() is an external interface to allow duplication of a
* given input easy handle. The returned handle will be a new working handle
@@ -935,18 +966,28 @@ CURL *curl_easy_duphandle(CURL *d)
*/
outcurl->set.buffer_size = data->set.buffer_size;
- /* copy all userdefined values */
- if(dupset(outcurl, data))
- goto fail;
-
- Curl_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER);
+ Curl_hash_init(&outcurl->meta_hash, 23,
+ Curl_hash_str, curlx_str_key_compare, dupeasy_meta_freeentry);
+ curlx_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER);
+ Curl_netrc_init(&outcurl->state.netrc);
/* the connection pool is setup on demand */
outcurl->state.lastconnect_id = -1;
outcurl->state.recent_conn_id = -1;
outcurl->id = -1;
+ outcurl->mid = UINT_MAX;
+ outcurl->master_mid = UINT_MAX;
+
+#ifndef CURL_DISABLE_HTTP
+ Curl_llist_init(&outcurl->state.httphdrs, NULL);
+#endif
+ Curl_initinfo(outcurl);
- outcurl->progress.flags = data->progress.flags;
+ /* copy all userdefined values */
+ if(dupset(outcurl, data))
+ goto fail;
+
+ outcurl->progress.hide = data->progress.hide;
outcurl->progress.callback = data->progress.callback;
#ifndef CURL_DISABLE_COOKIES
@@ -1009,40 +1050,6 @@ CURL *curl_easy_duphandle(CURL *d)
}
#endif
-#ifdef CURLRES_ASYNCH
- /* Clone the resolver handle, if present, for the new handle */
- if(Curl_resolver_duphandle(outcurl,
- &outcurl->state.async.resolver,
- data->state.async.resolver))
- goto fail;
-#endif
-
-#ifdef USE_ARES
- {
- CURLcode rc;
-
- rc = Curl_set_dns_servers(outcurl, data->set.str[STRING_DNS_SERVERS]);
- if(rc && rc != CURLE_NOT_BUILT_IN)
- goto fail;
-
- rc = Curl_set_dns_interface(outcurl, data->set.str[STRING_DNS_INTERFACE]);
- if(rc && rc != CURLE_NOT_BUILT_IN)
- goto fail;
-
- rc = Curl_set_dns_local_ip4(outcurl, data->set.str[STRING_DNS_LOCAL_IP4]);
- if(rc && rc != CURLE_NOT_BUILT_IN)
- goto fail;
-
- rc = Curl_set_dns_local_ip6(outcurl, data->set.str[STRING_DNS_LOCAL_IP6]);
- if(rc && rc != CURLE_NOT_BUILT_IN)
- goto fail;
- }
-#endif /* USE_ARES */
-#ifndef CURL_DISABLE_HTTP
- Curl_llist_init(&outcurl->state.httphdrs, NULL);
-#endif
- Curl_initinfo(outcurl);
-
outcurl->magic = CURLEASY_MAGIC_NUMBER;
/* we reach this point and thus we are OK */
@@ -1055,7 +1062,7 @@ fail:
#ifndef CURL_DISABLE_COOKIES
free(outcurl->cookies);
#endif
- Curl_dyn_free(&outcurl->state.headerb);
+ curlx_dyn_free(&outcurl->state.headerb);
Curl_altsvc_cleanup(&outcurl->asi);
Curl_hsts_cleanup(&outcurl->hsts);
Curl_freeset(outcurl);
@@ -1073,7 +1080,14 @@ void curl_easy_reset(CURL *d)
{
struct Curl_easy *data = d;
Curl_req_hard_reset(&data->req, data);
-
+ Curl_hash_clean(&data->meta_hash);
+
+ /* clear all meta data */
+ Curl_meta_reset(data);
+ /* clear any resolve data */
+ Curl_async_shutdown(data);
+ Curl_resolv_unlink(data, &data->state.dns[0]);
+ Curl_resolv_unlink(data, &data->state.dns[1]);
/* zero out UserDefined data: */
Curl_freeset(data);
memset(&data->set, 0, sizeof(struct UserDefined));
@@ -1085,7 +1099,7 @@ void curl_easy_reset(CURL *d)
/* zero out PureInfo data: */
Curl_initinfo(data);
- data->progress.flags |= PGRS_HIDE;
+ data->progress.hide = TRUE;
data->state.current_speed = -1; /* init to negative == impossible */
data->state.retrycount = 0; /* reset the retry counter */
@@ -1096,6 +1110,7 @@ void curl_easy_reset(CURL *d)
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH)
Curl_http_auth_cleanup_digest(data);
#endif
+ data->master_mid = UINT_MAX;
}
/*
@@ -1180,10 +1195,10 @@ CURLcode curl_easy_pause(CURL *d, int action)
}
out:
- if(!result && !data->state.done && keep_changed)
- /* This transfer may have been moved in or out of the bundle, update the
- corresponding socket callback, if used */
- result = Curl_updatesocket(data);
+ if(!result && !data->state.done && keep_changed && data->multi)
+ /* pause/unpausing may result in multi event changes */
+ if(Curl_multi_ev_assess_xfer(data->multi, data))
+ result = CURLE_ABORTED_BY_CALLBACK;
if(recursive)
/* this might have called a callback recursively which might have set this
@@ -1335,3 +1350,68 @@ CURLcode curl_easy_upkeep(CURL *d)
/* Use the common function to keep connections alive. */
return Curl_cpool_upkeep(data);
}
+
+CURLcode curl_easy_ssls_import(CURL *d, const char *session_key,
+ const unsigned char *shmac, size_t shmac_len,
+ const unsigned char *sdata, size_t sdata_len)
+{
+#ifdef USE_SSLS_EXPORT
+ struct Curl_easy *data = d;
+ if(!GOOD_EASY_HANDLE(data))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ return Curl_ssl_session_import(data, session_key,
+ shmac, shmac_len, sdata, sdata_len);
+#else
+ (void)d;
+ (void)session_key;
+ (void)shmac;
+ (void)shmac_len;
+ (void)sdata;
+ (void)sdata_len;
+ return CURLE_NOT_BUILT_IN;
+#endif
+}
+
+CURLcode curl_easy_ssls_export(CURL *d,
+ curl_ssls_export_cb *export_fn,
+ void *userptr)
+{
+#ifdef USE_SSLS_EXPORT
+ struct Curl_easy *data = d;
+ if(!GOOD_EASY_HANDLE(data))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ return Curl_ssl_session_export(data, export_fn, userptr);
+#else
+ (void)d;
+ (void)export_fn;
+ (void)userptr;
+ return CURLE_NOT_BUILT_IN;
+#endif
+}
+
+CURLcode Curl_meta_set(struct Curl_easy *data, const char *key,
+ void *meta_data, Curl_meta_dtor *meta_dtor)
+{
+ DEBUGASSERT(meta_data); /* never set to NULL */
+ if(!Curl_hash_add2(&data->meta_hash, CURL_UNCONST(key), strlen(key) + 1,
+ meta_data, meta_dtor)) {
+ meta_dtor(CURL_UNCONST(key), strlen(key) + 1, meta_data);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ return CURLE_OK;
+}
+
+void Curl_meta_remove(struct Curl_easy *data, const char *key)
+{
+ Curl_hash_delete(&data->meta_hash, CURL_UNCONST(key), strlen(key) + 1);
+}
+
+void *Curl_meta_get(struct Curl_easy *data, const char *key)
+{
+ return Curl_hash_pick(&data->meta_hash, CURL_UNCONST(key), strlen(key) + 1);
+}
+
+void Curl_meta_reset(struct Curl_easy *data)
+{
+ Curl_hash_clean(&data->meta_hash);
+}
diff --git a/libs/libcurl/src/easy_lock.h b/libs/libcurl/src/easy_lock.h
index 52f63dd874..3e3fc60d85 100644
--- a/libs/libcurl/src/easy_lock.h
+++ b/libs/libcurl/src/easy_lock.h
@@ -69,7 +69,7 @@
#endif
-static inline void curl_simple_lock_lock(curl_simple_lock *lock)
+static CURL_INLINE void curl_simple_lock_lock(curl_simple_lock *lock)
{
for(;;) {
if(!atomic_exchange_explicit(lock, true, memory_order_acquire))
@@ -81,6 +81,8 @@ static inline void curl_simple_lock_lock(curl_simple_lock *lock)
__builtin_ia32_pause();
#elif defined(__aarch64__)
__asm__ volatile("yield" ::: "memory");
+#elif defined(_WIN32)
+ Sleep(1);
#elif defined(HAVE_SCHED_YIELD)
sched_yield();
#endif
@@ -88,7 +90,7 @@ static inline void curl_simple_lock_lock(curl_simple_lock *lock)
}
}
-static inline void curl_simple_lock_unlock(curl_simple_lock *lock)
+static CURL_INLINE void curl_simple_lock_unlock(curl_simple_lock *lock)
{
atomic_store_explicit(lock, false, memory_order_release);
}
diff --git a/libs/libcurl/src/easygetopt.c b/libs/libcurl/src/easygetopt.c
index b54f1db880..e126075854 100644
--- a/libs/libcurl/src/easygetopt.c
+++ b/libs/libcurl/src/easygetopt.c
@@ -29,12 +29,12 @@
#ifndef CURL_DISABLE_GETOPTIONS
/* Lookups easy options at runtime */
-static struct curl_easyoption *lookup(const char *name, CURLoption id)
+static const struct curl_easyoption *lookup(const char *name, CURLoption id)
{
DEBUGASSERT(name || id);
DEBUGASSERT(!Curl_easyopts_check());
if(name || id) {
- struct curl_easyoption *o = &Curl_easyopts[0];
+ const struct curl_easyoption *o = &Curl_easyopts[0];
do {
if(name) {
if(strcasecompare(o->name, name))
diff --git a/libs/libcurl/src/easyoptions.c b/libs/libcurl/src/easyoptions.c
index 66c0ef13c3..8aadeec3ba 100644
--- a/libs/libcurl/src/easyoptions.c
+++ b/libs/libcurl/src/easyoptions.c
@@ -28,7 +28,7 @@
#include "easyoptions.h"
/* all easy setopt options listed in alphabetical order */
-struct curl_easyoption Curl_easyopts[] = {
+const struct curl_easyoption Curl_easyopts[] = {
{"ABSTRACT_UNIX_SOCKET", CURLOPT_ABSTRACT_UNIX_SOCKET, CURLOT_STRING, 0},
{"ACCEPTTIMEOUT_MS", CURLOPT_ACCEPTTIMEOUT_MS, CURLOT_LONG, 0},
{"ACCEPT_ENCODING", CURLOPT_ACCEPT_ENCODING, CURLOT_STRING, 0},
@@ -317,6 +317,8 @@ struct curl_easyoption Curl_easyopts[] = {
{"SSL_FALSESTART", CURLOPT_SSL_FALSESTART, CURLOT_LONG, 0},
{"SSL_OPTIONS", CURLOPT_SSL_OPTIONS, CURLOT_VALUES, 0},
{"SSL_SESSIONID_CACHE", CURLOPT_SSL_SESSIONID_CACHE, CURLOT_LONG, 0},
+ {"SSL_SIGNATURE_ALGORITHMS", CURLOPT_SSL_SIGNATURE_ALGORITHMS,
+ CURLOT_STRING, 0},
{"SSL_VERIFYHOST", CURLOPT_SSL_VERIFYHOST, CURLOT_LONG, 0},
{"SSL_VERIFYPEER", CURLOPT_SSL_VERIFYPEER, CURLOT_LONG, 0},
{"SSL_VERIFYSTATUS", CURLOPT_SSL_VERIFYSTATUS, CURLOT_LONG, 0},
@@ -353,6 +355,7 @@ struct curl_easyoption Curl_easyopts[] = {
{"UPKEEP_INTERVAL_MS", CURLOPT_UPKEEP_INTERVAL_MS, CURLOT_LONG, 0},
{"UPLOAD", CURLOPT_UPLOAD, CURLOT_LONG, 0},
{"UPLOAD_BUFFERSIZE", CURLOPT_UPLOAD_BUFFERSIZE, CURLOT_LONG, 0},
+ {"UPLOAD_FLAGS", CURLOPT_UPLOAD_FLAGS, CURLOT_LONG, 0},
{"URL", CURLOPT_URL, CURLOT_STRING, 0},
{"USERAGENT", CURLOPT_USERAGENT, CURLOT_STRING, 0},
{"USERNAME", CURLOPT_USERNAME, CURLOT_STRING, 0},
@@ -377,6 +380,6 @@ struct curl_easyoption Curl_easyopts[] = {
*/
int Curl_easyopts_check(void)
{
- return ((CURLOPT_LASTENTRY%10000) != (326 + 1));
+ return (CURLOPT_LASTENTRY % 10000) != (328 + 1);
}
#endif
diff --git a/libs/libcurl/src/easyoptions.h b/libs/libcurl/src/easyoptions.h
index 4958aeca62..a60854fb89 100644
--- a/libs/libcurl/src/easyoptions.h
+++ b/libs/libcurl/src/easyoptions.h
@@ -29,7 +29,7 @@
#include <curl/curl.h>
/* generated table with all easy options */
-extern struct curl_easyoption Curl_easyopts[];
+extern const struct curl_easyoption Curl_easyopts[];
#ifdef DEBUGBUILD
int Curl_easyopts_check(void);
diff --git a/libs/libcurl/src/escape.c b/libs/libcurl/src/escape.c
index e7796713c7..0f836bcfac 100644
--- a/libs/libcurl/src/escape.c
+++ b/libs/libcurl/src/escape.c
@@ -32,9 +32,11 @@
struct Curl_easy;
#include "urldata.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
#include "escape.h"
#include "strdup.h"
+#include "curlx/strparse.h"
+
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
@@ -69,7 +71,7 @@ char *curl_easy_escape(CURL *data, const char *string,
if(!length)
return strdup("");
- Curl_dyn_init(&d, length * 3 + 1);
+ curlx_dyn_init(&d, length * 3 + 1);
while(length--) {
/* treat the characters unsigned */
@@ -77,33 +79,21 @@ char *curl_easy_escape(CURL *data, const char *string,
if(ISUNRESERVED(in)) {
/* append this */
- if(Curl_dyn_addn(&d, &in, 1))
+ if(curlx_dyn_addn(&d, &in, 1))
return NULL;
}
else {
/* encode it */
- const char hex[] = "0123456789ABCDEF";
- char out[3]={'%'};
- out[1] = hex[in >> 4];
- out[2] = hex[in & 0xf];
- if(Curl_dyn_addn(&d, out, 3))
+ unsigned char out[3]={'%'};
+ Curl_hexbyte(&out[1], in, FALSE);
+ if(curlx_dyn_addn(&d, out, 3))
return NULL;
}
}
- return Curl_dyn_ptr(&d);
+ return curlx_dyn_ptr(&d);
}
-static const unsigned char hextable[] = {
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */
- 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */
- 0, 10, 11, 12, 13, 14, 15 /* 0x60 - 0x66 */
-};
-
-/* the input is a single hex digit */
-#define onehex2dec(x) hextable[x - '0']
-
/*
* Curl_urldecode() URL decodes the given string.
*
@@ -144,8 +134,8 @@ CURLcode Curl_urldecode(const char *string, size_t length,
if(('%' == in) && (alloc > 2) &&
ISXDIGIT(string[1]) && ISXDIGIT(string[2])) {
/* this is two hexadecimal digits following a '%' */
- in = (unsigned char)(onehex2dec(string[1]) << 4) | onehex2dec(string[2]);
-
+ in = (unsigned char)((Curl_hexval(string[1]) << 4) |
+ Curl_hexval(string[2]));
string += 3;
alloc -= 3;
}
@@ -219,15 +209,12 @@ void curl_free(void *p)
void Curl_hexencode(const unsigned char *src, size_t len, /* input length */
unsigned char *out, size_t olen) /* output buffer size */
{
- const char *hex = "0123456789abcdef";
DEBUGASSERT(src && len && (olen >= 3));
if(src && len && (olen >= 3)) {
while(len-- && (olen >= 3)) {
- /* clang-tidy warns on this line without this comment: */
- /* NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult) */
- *out++ = (unsigned char)hex[(*src & 0xF0) >> 4];
- *out++ = (unsigned char)hex[*src & 0x0F];
+ Curl_hexbyte(out, *src, TRUE);
++src;
+ out += 2;
olen -= 2;
}
*out = 0;
@@ -235,3 +222,17 @@ void Curl_hexencode(const unsigned char *src, size_t len, /* input length */
else if(olen)
*out = 0;
}
+
+/* Curl_hexbyte
+ *
+ * Output a single unsigned char as a two-digit hex number, lowercase or
+ * uppercase
+ */
+void Curl_hexbyte(unsigned char *dest, /* must fit two bytes */
+ unsigned char val,
+ bool lowercase)
+{
+ const unsigned char *t = lowercase ? Curl_ldigits : Curl_udigits;
+ dest[0] = t[val >> 4];
+ dest[1] = t[val & 0x0F];
+}
diff --git a/libs/libcurl/src/escape.h b/libs/libcurl/src/escape.h
index d2039af1e1..baa3f51ec3 100644
--- a/libs/libcurl/src/escape.h
+++ b/libs/libcurl/src/escape.h
@@ -41,4 +41,8 @@ CURLcode Curl_urldecode(const char *string, size_t length,
void Curl_hexencode(const unsigned char *src, size_t len, /* input length */
unsigned char *out, size_t olen); /* output buffer size */
+void Curl_hexbyte(unsigned char *dest, /* must fit two bytes */
+ unsigned char val,
+ bool lowercase);
+
#endif /* HEADER_CURL_ESCAPE_H */
diff --git a/libs/libcurl/src/fake_addrinfo.c b/libs/libcurl/src/fake_addrinfo.c
new file mode 100644
index 0000000000..f0272f69e0
--- /dev/null
+++ b/libs/libcurl/src/fake_addrinfo.c
@@ -0,0 +1,210 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+#include "fake_addrinfo.h"
+
+#ifdef USE_FAKE_GETADDRINFO
+
+#include <string.h>
+#include <stdlib.h>
+#include <ares.h>
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+void r_freeaddrinfo(struct addrinfo *cahead)
+{
+ struct addrinfo *canext;
+ struct addrinfo *ca;
+
+ for(ca = cahead; ca; ca = canext) {
+ canext = ca->ai_next;
+ free(ca);
+ }
+}
+
+struct context {
+ struct ares_addrinfo *result;
+};
+
+static void async_addrinfo_cb(void *userp, int status, int timeouts,
+ struct ares_addrinfo *result)
+{
+ struct context *ctx = (struct context *)userp;
+ (void)timeouts;
+ if(ARES_SUCCESS == status) {
+ ctx->result = result;
+ }
+}
+
+/* convert the c-ares version into the "native" version */
+static struct addrinfo *mk_getaddrinfo(const struct ares_addrinfo *aihead)
+{
+ const struct ares_addrinfo_node *ai;
+ struct addrinfo *ca;
+ struct addrinfo *cafirst = NULL;
+ struct addrinfo *calast = NULL;
+ const char *name = aihead->name;
+
+ /* traverse the addrinfo list */
+ for(ai = aihead->nodes; ai != NULL; ai = ai->ai_next) {
+ size_t ss_size;
+ size_t namelen = name ? strlen(name) + 1 : 0;
+ /* ignore elements with unsupported address family, */
+ /* settle family-specific sockaddr structure size. */
+ if(ai->ai_family == AF_INET)
+ ss_size = sizeof(struct sockaddr_in);
+ else if(ai->ai_family == AF_INET6)
+ ss_size = sizeof(struct sockaddr_in6);
+ else
+ continue;
+
+ /* ignore elements without required address info */
+ if(!ai->ai_addr || !(ai->ai_addrlen > 0))
+ continue;
+
+ /* ignore elements with bogus address size */
+ if((size_t)ai->ai_addrlen < ss_size)
+ continue;
+
+ ca = malloc(sizeof(struct addrinfo) + ss_size + namelen);
+ if(!ca) {
+ r_freeaddrinfo(cafirst);
+ return NULL;
+ }
+
+ /* copy each structure member individually, member ordering, */
+ /* size, or padding might be different for each platform. */
+
+ ca->ai_flags = ai->ai_flags;
+ ca->ai_family = ai->ai_family;
+ ca->ai_socktype = ai->ai_socktype;
+ ca->ai_protocol = ai->ai_protocol;
+ ca->ai_addrlen = (curl_socklen_t)ss_size;
+ ca->ai_addr = NULL;
+ ca->ai_canonname = NULL;
+ ca->ai_next = NULL;
+
+ ca->ai_addr = (void *)((char *)ca + sizeof(struct addrinfo));
+ memcpy(ca->ai_addr, ai->ai_addr, ss_size);
+
+ if(namelen) {
+ ca->ai_canonname = (void *)((char *)ca->ai_addr + ss_size);
+ memcpy(ca->ai_canonname, name, namelen);
+
+ /* the name is only pointed to by the first entry in the "real"
+ addrinfo chain, so stop now */
+ name = NULL;
+ }
+
+ /* if the return list is empty, this becomes the first element */
+ if(!cafirst)
+ cafirst = ca;
+
+ /* add this element last in the return list */
+ if(calast)
+ calast->ai_next = ca;
+ calast = ca;
+ }
+
+ return cafirst;
+}
+
+/*
+ RETURN VALUE
+
+ getaddrinfo() returns 0 if it succeeds, or one of the following nonzero
+ error codes:
+
+ ...
+*/
+int r_getaddrinfo(const char *node,
+ const char *service,
+ const struct addrinfo *hints,
+ struct addrinfo **res)
+{
+ int status;
+ struct context ctx;
+ struct ares_options options;
+ int optmask = 0;
+ struct ares_addrinfo_hints ahints;
+ ares_channel channel;
+ int rc = 0;
+
+ memset(&options, 0, sizeof(options));
+ optmask |= ARES_OPT_EVENT_THREAD;
+ options.evsys = ARES_EVSYS_DEFAULT;
+
+ memset(&ahints, 0, sizeof(ahints));
+ memset(&ctx, 0, sizeof(ctx));
+
+ if(hints) {
+ ahints.ai_flags = hints->ai_flags;
+ ahints.ai_family = hints->ai_family;
+ ahints.ai_socktype = hints->ai_socktype;
+ ahints.ai_protocol = hints->ai_protocol;
+ }
+
+ status = ares_init_options(&channel, &options, optmask);
+ if(status)
+ return EAI_MEMORY; /* major problem */
+
+ else {
+ const char *env = getenv("CURL_DNS_SERVER");
+ if(env) {
+ rc = ares_set_servers_ports_csv(channel, env);
+ if(rc) {
+ fprintf(stderr, "ares_set_servers_ports_csv failed: %d", rc);
+ /* Cleanup */
+ ares_destroy(channel);
+ return EAI_MEMORY; /* we can't run */
+ }
+ }
+ }
+
+ ares_getaddrinfo(channel, node, service, &ahints,
+ async_addrinfo_cb, &ctx);
+
+ /* Wait until no more requests are left to be processed */
+ ares_queue_wait_empty(channel, -1);
+
+ if(ctx.result) {
+ /* convert the c-ares version */
+ *res = mk_getaddrinfo(ctx.result);
+ /* free the old */
+ ares_freeaddrinfo(ctx.result);
+ }
+ else
+ rc = EAI_NONAME; /* got nothing */
+
+ /* Cleanup */
+ ares_destroy(channel);
+
+ return rc;
+}
+
+#endif /* USE_FAKE_GETADDRINFO */
diff --git a/libs/libcurl/src/fake_addrinfo.h b/libs/libcurl/src/fake_addrinfo.h
new file mode 100644
index 0000000000..4483870e63
--- /dev/null
+++ b/libs/libcurl/src/fake_addrinfo.h
@@ -0,0 +1,54 @@
+#ifndef HEADER_FAKE_ADDRINFO_H
+#define HEADER_FAKE_ADDRINFO_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_ARES
+#include <ares.h>
+#endif
+
+#if defined(CURLDEBUG) && defined(USE_ARES) && defined(HAVE_GETADDRINFO) && \
+ (ARES_VERSION >= 0x011a00) /* >= 1.26. 0 */
+#define USE_FAKE_GETADDRINFO 1
+#endif
+
+#ifdef USE_FAKE_GETADDRINFO
+
+#ifdef HAVE_NETDB_H
+# include <netdb.h>
+#endif
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+
+void r_freeaddrinfo(struct addrinfo *res);
+int r_getaddrinfo(const char *node,
+ const char *service,
+ const struct addrinfo *hints,
+ struct addrinfo **res);
+#endif /* USE_FAKE_GETADDRINFO */
+
+#endif /* HEADER_FAKE_ADDRINFO_H */
diff --git a/libs/libcurl/src/file.c b/libs/libcurl/src/file.c
index 1c8b2b2c85..8b514cfd2d 100644
--- a/libs/libcurl/src/file.c
+++ b/libs/libcurl/src/file.c
@@ -58,7 +58,6 @@
#include <dirent.h>
#endif
-#include "strtoofft.h"
#include "urldata.h"
#include <curl/curl.h>
#include "progress.h"
@@ -71,24 +70,28 @@
#include "transfer.h"
#include "url.h"
#include "parsedate.h" /* for the week day and month names */
-#include "warnless.h"
+#include "curlx/warnless.h"
#include "curl_range.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
#include "memdebug.h"
-#if defined(_WIN32) || defined(MSDOS) || defined(__EMX__)
+#if defined(_WIN32) || defined(MSDOS)
#define DOS_FILESYSTEM 1
#elif defined(__amigaos4__)
#define AMIGA_FILESYSTEM 1
#endif
-#ifdef OPEN_NEEDS_ARG3
-# define open_readonly(p,f) open((p),(f),(0))
-#else
-# define open_readonly(p,f) open((p),(f))
-#endif
+/* meta key for storing protocol meta at easy handle */
+#define CURL_META_FILE_EASY "meta:proto:file:easy"
+
+struct FILEPROTO {
+ char *path; /* the path we operate on */
+ char *freepath; /* pointer to the allocated block we must free, this might
+ differ from the 'path' pointer */
+ int fd; /* open file descriptor to read from! */
+};
/*
* Forward declarations.
@@ -126,6 +129,7 @@ const struct Curl_handler Curl_handler_file = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
0, /* defport */
CURLPROTO_FILE, /* protocol */
CURLPROTO_FILE, /* family */
@@ -133,13 +137,34 @@ const struct Curl_handler Curl_handler_file = {
};
+static void file_cleanup(struct FILEPROTO *file)
+{
+ Curl_safefree(file->freepath);
+ file->path = NULL;
+ if(file->fd != -1) {
+ close(file->fd);
+ file->fd = -1;
+ }
+}
+
+static void file_easy_dtor(void *key, size_t klen, void *entry)
+{
+ struct FILEPROTO *file = entry;
+ (void)key;
+ (void)klen;
+ file_cleanup(file);
+ free(file);
+}
+
static CURLcode file_setup_connection(struct Curl_easy *data,
struct connectdata *conn)
{
+ struct FILEPROTO *filep;
(void)conn;
/* allocate the FILE specific struct */
- data->req.p.file = calloc(1, sizeof(struct FILEPROTO));
- if(!data->req.p.file)
+ filep = calloc(1, sizeof(*filep));
+ if(!filep ||
+ Curl_meta_set(data, CURL_META_FILE_EASY, filep, file_easy_dtor))
return CURLE_OUT_OF_MEMORY;
return CURLE_OK;
@@ -153,7 +178,7 @@ static CURLcode file_setup_connection(struct Curl_easy *data,
static CURLcode file_connect(struct Curl_easy *data, bool *done)
{
char *real_path;
- struct FILEPROTO *file = data->req.p.file;
+ struct FILEPROTO *file = Curl_meta_get(data, CURL_META_FILE_EASY);
int fd;
#ifdef DOS_FILESYSTEM
size_t i;
@@ -162,6 +187,9 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done)
size_t real_path_len;
CURLcode result;
+ if(!file)
+ return CURLE_FAILED_INIT;
+
if(file->path) {
/* already connected.
* the handler->connect_it() is normally only called once, but
@@ -209,7 +237,7 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done)
return CURLE_URL_MALFORMAT;
}
- fd = open_readonly(actual_path, O_RDONLY|O_BINARY);
+ fd = open(actual_path, O_RDONLY|CURL_O_BINARY);
file->path = actual_path;
#else
if(memchr(real_path, 0, real_path_len)) {
@@ -233,20 +261,20 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done)
extern int __unix_path_semantics;
if(strchr(real_path + 1, ':')) {
/* Amiga absolute path */
- fd = open_readonly(real_path + 1, O_RDONLY);
+ fd = open(real_path + 1, O_RDONLY);
file->path++;
}
else if(__unix_path_semantics) {
/* -lunix fallback */
- fd = open_readonly(real_path, O_RDONLY);
+ fd = open(real_path, O_RDONLY);
}
}
#else
- fd = open_readonly(real_path, O_RDONLY);
+ fd = open(real_path, O_RDONLY);
file->path = real_path;
#endif
#endif
- Curl_safefree(file->freepath);
+ free(file->freepath);
file->freepath = real_path; /* free this when done */
file->fd = fd;
@@ -263,17 +291,12 @@ static CURLcode file_connect(struct Curl_easy *data, bool *done)
static CURLcode file_done(struct Curl_easy *data,
CURLcode status, bool premature)
{
- struct FILEPROTO *file = data->req.p.file;
+ struct FILEPROTO *file = Curl_meta_get(data, CURL_META_FILE_EASY);
(void)status; /* not used */
(void)premature; /* not used */
- if(file) {
- Curl_safefree(file->freepath);
- file->path = NULL;
- if(file->fd != -1)
- close(file->fd);
- file->fd = -1;
- }
+ if(file)
+ file_cleanup(file);
return CURLE_OK;
}
@@ -293,9 +316,9 @@ static CURLcode file_disconnect(struct Curl_easy *data,
#define DIRSEP '/'
#endif
-static CURLcode file_upload(struct Curl_easy *data)
+static CURLcode file_upload(struct Curl_easy *data,
+ struct FILEPROTO *file)
{
- struct FILEPROTO *file = data->req.p.file;
const char *dir = strchr(file->path, DIRSEP);
int fd;
int mode;
@@ -318,18 +341,18 @@ static CURLcode file_upload(struct Curl_easy *data)
if(!dir[1])
return CURLE_FILE_COULDNT_READ_FILE; /* fix: better error code */
-#ifdef O_BINARY
-#define MODE_DEFAULT O_WRONLY|O_CREAT|O_BINARY
-#else
-#define MODE_DEFAULT O_WRONLY|O_CREAT
-#endif
-
+ mode = O_WRONLY|O_CREAT|CURL_O_BINARY;
if(data->state.resume_from)
- mode = MODE_DEFAULT|O_APPEND;
+ mode |= O_APPEND;
else
- mode = MODE_DEFAULT|O_TRUNC;
+ mode |= O_TRUNC;
+#if (defined(ANDROID) || defined(__ANDROID__)) && \
+ (defined(__i386__) || defined(__arm__))
+ fd = open(file->path, mode, (mode_t)data->set.new_file_perms);
+#else
fd = open(file->path, mode, data->set.new_file_perms);
+#endif
if(fd < 0) {
failf(data, "cannot open %s for writing", file->path);
return CURLE_WRITE_ERROR;
@@ -397,7 +420,7 @@ static CURLcode file_upload(struct Curl_easy *data)
if(Curl_pgrsUpdate(data))
result = CURLE_ABORTED_BY_CALLBACK;
else
- result = Curl_speedcheck(data, Curl_now());
+ result = Curl_speedcheck(data, curlx_now());
}
if(!result && Curl_pgrsUpdate(data))
result = CURLE_ABORTED_BY_CALLBACK;
@@ -424,6 +447,7 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
are supported. This means that files on remotely mounted directories
(via NFS, Samba, NT sharing) can be accessed through a file:// URL
*/
+ struct FILEPROTO *file = Curl_meta_get(data, CURL_META_FILE_EASY);
CURLcode result = CURLE_OK;
struct_stat statbuf; /* struct_stat instead of struct stat just to allow the
Windows version to have a different struct without
@@ -432,16 +456,15 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
bool size_known;
bool fstated = FALSE;
int fd;
- struct FILEPROTO *file;
char *xfer_buf;
size_t xfer_blen;
*done = TRUE; /* unconditionally */
+ if(!file)
+ return CURLE_FAILED_INIT;
if(data->state.upload)
- return file_upload(data);
-
- file = data->req.p.file;
+ return file_upload(data, file);
/* get the fd from the connection phase */
fd = file->fd;
@@ -553,8 +576,13 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
if(data->state.resume_from) {
if(!S_ISDIR(statbuf.st_mode)) {
+#if defined(__AMIGA__) || defined(__MINGW32CE__)
+ if(data->state.resume_from !=
+ lseek(fd, (off_t)data->state.resume_from, SEEK_SET))
+#else
if(data->state.resume_from !=
lseek(fd, data->state.resume_from, SEEK_SET))
+#endif
return CURLE_BAD_DOWNLOAD_RESUME;
}
else {
@@ -597,7 +625,7 @@ static CURLcode file_do(struct Curl_easy *data, bool *done)
if(Curl_pgrsUpdate(data))
result = CURLE_ABORTED_BY_CALLBACK;
else
- result = Curl_speedcheck(data, Curl_now());
+ result = Curl_speedcheck(data, curlx_now());
if(result)
goto out;
}
diff --git a/libs/libcurl/src/file.h b/libs/libcurl/src/file.h
index 00d1adda54..5923f7e8fb 100644
--- a/libs/libcurl/src/file.h
+++ b/libs/libcurl/src/file.h
@@ -24,17 +24,6 @@
*
***************************************************************************/
-
-/****************************************************************************
- * FILE unique setup
- ***************************************************************************/
-struct FILEPROTO {
- char *path; /* the path we operate on */
- char *freepath; /* pointer to the allocated block we must free, this might
- differ from the 'path' pointer */
- int fd; /* open file descriptor to read from! */
-};
-
#ifndef CURL_DISABLE_FILE
extern const struct Curl_handler Curl_handler_file;
#endif
diff --git a/libs/libcurl/src/fileinfo.c b/libs/libcurl/src/fileinfo.c
index 1c313e5f2c..9613bcd004 100644
--- a/libs/libcurl/src/fileinfo.c
+++ b/libs/libcurl/src/fileinfo.c
@@ -40,7 +40,7 @@ void Curl_fileinfo_cleanup(struct fileinfo *finfo)
if(!finfo)
return;
- Curl_dyn_free(&finfo->buf);
+ curlx_dyn_free(&finfo->buf);
free(finfo);
}
#endif
diff --git a/libs/libcurl/src/fileinfo.h b/libs/libcurl/src/fileinfo.h
index bf32b3a003..cf465d6096 100644
--- a/libs/libcurl/src/fileinfo.h
+++ b/libs/libcurl/src/fileinfo.h
@@ -26,7 +26,7 @@
#include <curl/curl.h>
#include "llist.h"
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
struct fileinfo {
struct curl_fileinfo info;
diff --git a/libs/libcurl/src/fopen.c b/libs/libcurl/src/fopen.c
index 110345a067..30489fcc30 100644
--- a/libs/libcurl/src/fopen.c
+++ b/libs/libcurl/src/fopen.c
@@ -53,7 +53,7 @@
#ifdef _WIN32
#define PATHSEP "\\"
#define IS_SEP(x) (((x) == '/') || ((x) == '\\'))
-#elif defined(MSDOS) || defined(__EMX__) || defined(OS2)
+#elif defined(MSDOS) || defined(OS2)
#define PATHSEP "\\"
#define IS_SEP(x) ((x) == '\\')
#else
@@ -66,7 +66,7 @@ static char *dirslash(const char *path)
size_t n;
struct dynbuf out;
DEBUGASSERT(path);
- Curl_dyn_init(&out, CURL_MAX_INPUT_LENGTH);
+ curlx_dyn_init(&out, CURL_MAX_INPUT_LENGTH);
n = strlen(path);
if(n) {
/* find the rightmost path separator, if any */
@@ -76,12 +76,12 @@ static char *dirslash(const char *path)
while(n && IS_SEP(path[n-1]))
--n;
}
- if(Curl_dyn_addn(&out, path, n))
+ if(curlx_dyn_addn(&out, path, n))
return NULL;
/* if there was a directory, append a single trailing slash */
- if(n && Curl_dyn_addn(&out, PATHSEP, 1))
+ if(n && curlx_dyn_addn(&out, PATHSEP, 1))
return NULL;
- return Curl_dyn_ptr(&out);
+ return curlx_dyn_ptr(&out);
}
/*
@@ -105,7 +105,13 @@ CURLcode Curl_fopen(struct Curl_easy *data, const char *filename,
*fh = fopen(filename, FOPEN_WRITETEXT);
if(!*fh)
goto fail;
- if(fstat(fileno(*fh), &sb) == -1 || !S_ISREG(sb.st_mode)) {
+ if(
+#ifdef UNDER_CE
+ stat(filename, &sb) == -1
+#else
+ fstat(fileno(*fh), &sb) == -1
+#endif
+ || !S_ISREG(sb.st_mode)) {
return CURLE_OK;
}
fclose(*fh);
diff --git a/libs/libcurl/src/formdata.c b/libs/libcurl/src/formdata.c
index 2f45376413..1105bba8e2 100644
--- a/libs/libcurl/src/formdata.c
+++ b/libs/libcurl/src/formdata.c
@@ -31,10 +31,6 @@ struct Curl_easy;
#include "formdata.h"
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_FORM_API)
-#if defined(HAVE_LIBGEN_H) && defined(HAVE_BASENAME)
-#include <libgen.h>
-#endif
-
#include "urldata.h" /* for struct Curl_easy */
#include "mime.h"
#include "vtls/vtls.h"
@@ -42,7 +38,7 @@ struct Curl_easy;
#include "sendf.h"
#include "strdup.h"
#include "rand.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
@@ -68,36 +64,31 @@ struct Curl_easy;
*
***************************************************************************/
static struct curl_httppost *
-AddHttpPost(char *name, size_t namelength,
- char *value, curl_off_t contentslength,
- char *buffer, size_t bufferlength,
- char *contenttype,
- long flags,
- struct curl_slist *contentHeader,
- char *showfilename, char *userp,
+AddHttpPost(struct FormInfo *src,
struct curl_httppost *parent_post,
struct curl_httppost **httppost,
struct curl_httppost **last_post)
{
struct curl_httppost *post;
- if(!namelength && name)
- namelength = strlen(name);
- if((bufferlength > LONG_MAX) || (namelength > LONG_MAX))
+ size_t namelength = src->namelength;
+ if(!namelength && src->name)
+ namelength = strlen(src->name);
+ if((src->bufferlength > LONG_MAX) || (namelength > LONG_MAX))
/* avoid overflow in typecasts below */
return NULL;
post = calloc(1, sizeof(struct curl_httppost));
if(post) {
- post->name = name;
+ post->name = src->name;
post->namelength = (long)namelength;
- post->contents = value;
- post->contentlen = contentslength;
- post->buffer = buffer;
- post->bufferlength = (long)bufferlength;
- post->contenttype = contenttype;
- post->contentheader = contentHeader;
- post->showfilename = showfilename;
- post->userp = userp;
- post->flags = flags | CURL_HTTPPOST_LARGE;
+ post->contents = src->value;
+ post->contentlen = src->contentslength;
+ post->buffer = src->buffer;
+ post->bufferlength = (long)src->bufferlength;
+ post->contenttype = src->contenttype;
+ post->flags = src->flags | CURL_HTTPPOST_LARGE;
+ post->contentheader = src->contentheader;
+ post->showfilename = src->showfilename;
+ post->userp = src->userp;
}
else
return NULL;
@@ -156,6 +147,28 @@ static struct FormInfo *AddFormInfo(char *value,
return form_info;
}
+static void free_formlist(struct FormInfo *ptr)
+{
+ for(; ptr != NULL; ptr = ptr->more) {
+ if(ptr->name_alloc) {
+ Curl_safefree(ptr->name);
+ ptr->name_alloc = FALSE;
+ }
+ if(ptr->value_alloc) {
+ Curl_safefree(ptr->value);
+ ptr->value_alloc = FALSE;
+ }
+ if(ptr->contenttype_alloc) {
+ Curl_safefree(ptr->contenttype);
+ ptr->contenttype_alloc = FALSE;
+ }
+ if(ptr->showfilename_alloc) {
+ Curl_safefree(ptr->showfilename);
+ ptr->showfilename_alloc = FALSE;
+ }
+ }
+}
+
/***************************************************************************
*
* FormAdd()
@@ -205,18 +218,123 @@ static struct FormInfo *AddFormInfo(char *value,
*
***************************************************************************/
+static CURLFORMcode FormAddCheck(struct FormInfo *first_form,
+ struct curl_httppost **httppost,
+ struct curl_httppost **last_post)
+{
+ const char *prevtype = NULL;
+ struct FormInfo *form = NULL;
+ struct curl_httppost *post = NULL;
+
+ /* go through the list, check for completeness and if everything is
+ * alright add the HttpPost item otherwise set retval accordingly */
+
+ for(form = first_form;
+ form != NULL;
+ form = form->more) {
+ if(((!form->name || !form->value) && !post) ||
+ ( (form->contentslength) &&
+ (form->flags & HTTPPOST_FILENAME) ) ||
+ ( (form->flags & HTTPPOST_FILENAME) &&
+ (form->flags & HTTPPOST_PTRCONTENTS) ) ||
+
+ ( (!form->buffer) &&
+ (form->flags & HTTPPOST_BUFFER) &&
+ (form->flags & HTTPPOST_PTRBUFFER) ) ||
+
+ ( (form->flags & HTTPPOST_READFILE) &&
+ (form->flags & HTTPPOST_PTRCONTENTS) )
+ ) {
+ return CURL_FORMADD_INCOMPLETE;
+ }
+ if(((form->flags & HTTPPOST_FILENAME) ||
+ (form->flags & HTTPPOST_BUFFER)) &&
+ !form->contenttype) {
+ char *f = (form->flags & HTTPPOST_BUFFER) ?
+ form->showfilename : form->value;
+ char const *type;
+ type = Curl_mime_contenttype(f);
+ if(!type)
+ type = prevtype;
+ if(!type)
+ type = FILE_CONTENTTYPE_DEFAULT;
+
+ /* our contenttype is missing */
+ form->contenttype = strdup(type);
+ if(!form->contenttype)
+ return CURL_FORMADD_MEMORY;
+
+ form->contenttype_alloc = TRUE;
+ }
+ if(form->name && form->namelength) {
+ if(memchr(form->name, 0, form->namelength))
+ return CURL_FORMADD_NULL;
+ }
+ if(!(form->flags & HTTPPOST_PTRNAME) && form->name) {
+ /* Note that there is small risk that form->name is NULL here if the app
+ passed in a bad combo, so we check for that. */
+
+ /* copy name (without strdup; possibly not null-terminated) */
+ char *dupname = Curl_memdup0(form->name, form->namelength ?
+ form->namelength : strlen(form->name));
+ if(!dupname)
+ return CURL_FORMADD_MEMORY;
+
+ form->name = dupname;
+ form->name_alloc = TRUE;
+ }
+ if(!(form->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE |
+ HTTPPOST_PTRCONTENTS | HTTPPOST_PTRBUFFER |
+ HTTPPOST_CALLBACK)) && form->value) {
+ /* copy value (without strdup; possibly contains null characters) */
+ size_t clen = (size_t) form->contentslength;
+ if(!clen)
+ clen = strlen(form->value) + 1;
+
+ form->value = Curl_memdup(form->value, clen);
+
+ if(!form->value)
+ return CURL_FORMADD_MEMORY;
+
+ form->value_alloc = TRUE;
+ }
+ post = AddHttpPost(form, post, httppost, last_post);
+
+ if(!post)
+ return CURL_FORMADD_MEMORY;
+
+ if(form->contenttype)
+ prevtype = form->contenttype;
+ }
+
+ return CURL_FORMADD_OK;
+}
+
+/* Shallow cleanup. Remove the newly created chain, the structs only and not
+ the content they point to */
+static void free_chain(struct curl_httppost *c)
+{
+ while(c) {
+ struct curl_httppost *next = c->next;
+ if(c->more)
+ free_chain(c->more);
+ free(c);
+ c = next;
+ }
+}
+
static
CURLFORMcode FormAdd(struct curl_httppost **httppost,
struct curl_httppost **last_post,
va_list params)
{
- struct FormInfo *first_form, *current_form, *form = NULL;
- CURLFORMcode return_value = CURL_FORMADD_OK;
- const char *prevtype = NULL;
- struct curl_httppost *post = NULL;
+ struct FormInfo *first_form, *curr, *form = NULL;
+ CURLFORMcode retval = CURL_FORMADD_OK;
CURLformoption option;
struct curl_forms *forms = NULL;
- char *array_value = NULL; /* value read from an array */
+ char *avalue = NULL;
+ struct curl_httppost *newchain = NULL;
+ struct curl_httppost *lastnode = NULL;
/* This is a state variable, that if TRUE means that we are parsing an
array that we got passed to us. If FALSE we are parsing the input
@@ -230,18 +348,18 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
if(!first_form)
return CURL_FORMADD_MEMORY;
- current_form = first_form;
+ curr = first_form;
/*
* Loop through all the options set. Break if we have an error to report.
*/
- while(return_value == CURL_FORMADD_OK) {
+ while(retval == CURL_FORMADD_OK) {
/* first see if we have more parts of the array param */
if(array_state && forms) {
/* get the upcoming option from the given array */
option = forms->option;
- array_value = (char *)forms->value;
+ avalue = (char *)CURL_UNCONST(forms->value);
forms++; /* advance this to next entry */
if(CURLFORM_END == option) {
@@ -263,13 +381,13 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
case CURLFORM_ARRAY:
if(array_state)
/* we do not support an array from within an array */
- return_value = CURL_FORMADD_ILLEGAL_ARRAY;
+ retval = CURL_FORMADD_ILLEGAL_ARRAY;
else {
forms = va_arg(params, struct curl_forms *);
if(forms)
array_state = TRUE;
else
- return_value = CURL_FORMADD_NULL;
+ retval = CURL_FORMADD_NULL;
}
break;
@@ -277,403 +395,253 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
* Set the Name property.
*/
case CURLFORM_PTRNAME:
- current_form->flags |= HTTPPOST_PTRNAME; /* fall through */
+ curr->flags |= HTTPPOST_PTRNAME; /* fall through */
FALLTHROUGH();
case CURLFORM_COPYNAME:
- if(current_form->name)
- return_value = CURL_FORMADD_OPTION_TWICE;
+ if(curr->name)
+ retval = CURL_FORMADD_OPTION_TWICE;
else {
- char *name = array_state ?
- array_value : va_arg(params, char *);
- if(name)
- current_form->name = name; /* store for the moment */
+ if(!array_state)
+ avalue = va_arg(params, char *);
+ if(avalue)
+ curr->name = avalue; /* store for the moment */
else
- return_value = CURL_FORMADD_NULL;
+ retval = CURL_FORMADD_NULL;
}
break;
case CURLFORM_NAMELENGTH:
- if(current_form->namelength)
- return_value = CURL_FORMADD_OPTION_TWICE;
+ if(curr->namelength)
+ retval = CURL_FORMADD_OPTION_TWICE;
else
- current_form->namelength =
- array_state ? (size_t)array_value : (size_t)va_arg(params, long);
+ curr->namelength =
+ array_state ? (size_t)avalue : (size_t)va_arg(params, long);
break;
/*
* Set the contents property.
*/
case CURLFORM_PTRCONTENTS:
- current_form->flags |= HTTPPOST_PTRCONTENTS;
+ curr->flags |= HTTPPOST_PTRCONTENTS;
FALLTHROUGH();
case CURLFORM_COPYCONTENTS:
- if(current_form->value)
- return_value = CURL_FORMADD_OPTION_TWICE;
+ if(curr->value)
+ retval = CURL_FORMADD_OPTION_TWICE;
else {
- char *value =
- array_state ? array_value : va_arg(params, char *);
- if(value)
- current_form->value = value; /* store for the moment */
+ if(!array_state)
+ avalue = va_arg(params, char *);
+ if(avalue)
+ curr->value = avalue; /* store for the moment */
else
- return_value = CURL_FORMADD_NULL;
+ retval = CURL_FORMADD_NULL;
}
break;
case CURLFORM_CONTENTSLENGTH:
- current_form->contentslength =
- array_state ? (size_t)array_value : (size_t)va_arg(params, long);
+ curr->contentslength =
+ array_state ? (size_t)avalue : (size_t)va_arg(params, long);
break;
case CURLFORM_CONTENTLEN:
- current_form->flags |= CURL_HTTPPOST_LARGE;
- current_form->contentslength =
- array_state ? (curl_off_t)(size_t)array_value :
+ curr->flags |= CURL_HTTPPOST_LARGE;
+ curr->contentslength =
+ array_state ? (curl_off_t)(size_t)avalue :
va_arg(params, curl_off_t);
break;
/* Get contents from a given filename */
case CURLFORM_FILECONTENT:
- if(current_form->flags & (HTTPPOST_PTRCONTENTS|HTTPPOST_READFILE))
- return_value = CURL_FORMADD_OPTION_TWICE;
+ if(curr->flags & (HTTPPOST_PTRCONTENTS|HTTPPOST_READFILE))
+ retval = CURL_FORMADD_OPTION_TWICE;
else {
- const char *filename = array_state ?
- array_value : va_arg(params, char *);
- if(filename) {
- current_form->value = strdup(filename);
- if(!current_form->value)
- return_value = CURL_FORMADD_MEMORY;
+ if(!array_state)
+ avalue = va_arg(params, char *);
+ if(avalue) {
+ curr->value = strdup(avalue);
+ if(!curr->value)
+ retval = CURL_FORMADD_MEMORY;
else {
- current_form->flags |= HTTPPOST_READFILE;
- current_form->value_alloc = TRUE;
+ curr->flags |= HTTPPOST_READFILE;
+ curr->value_alloc = TRUE;
}
}
else
- return_value = CURL_FORMADD_NULL;
+ retval = CURL_FORMADD_NULL;
}
break;
/* We upload a file */
case CURLFORM_FILE:
- {
- const char *filename = array_state ? array_value :
- va_arg(params, char *);
-
- if(current_form->value) {
- if(current_form->flags & HTTPPOST_FILENAME) {
- if(filename) {
- char *fname = strdup(filename);
- if(!fname)
- return_value = CURL_FORMADD_MEMORY;
+ if(!array_state)
+ avalue = va_arg(params, char *);
+
+ if(curr->value) {
+ if(curr->flags & HTTPPOST_FILENAME) {
+ if(avalue) {
+ char *fname = strdup(avalue);
+ if(!fname)
+ retval = CURL_FORMADD_MEMORY;
+ else {
+ form = AddFormInfo(fname, NULL, curr);
+ if(!form) {
+ free(fname);
+ retval = CURL_FORMADD_MEMORY;
+ }
else {
- form = AddFormInfo(fname, NULL, current_form);
- if(!form) {
- free(fname);
- return_value = CURL_FORMADD_MEMORY;
- }
- else {
- form->value_alloc = TRUE;
- current_form = form;
- form = NULL;
- }
+ form->value_alloc = TRUE;
+ curr = form;
+ form = NULL;
}
}
- else
- return_value = CURL_FORMADD_NULL;
}
else
- return_value = CURL_FORMADD_OPTION_TWICE;
+ retval = CURL_FORMADD_NULL;
}
- else {
- if(filename) {
- current_form->value = strdup(filename);
- if(!current_form->value)
- return_value = CURL_FORMADD_MEMORY;
- else {
- current_form->flags |= HTTPPOST_FILENAME;
- current_form->value_alloc = TRUE;
- }
+ else
+ retval = CURL_FORMADD_OPTION_TWICE;
+ }
+ else {
+ if(avalue) {
+ curr->value = strdup(avalue);
+ if(!curr->value)
+ retval = CURL_FORMADD_MEMORY;
+ else {
+ curr->flags |= HTTPPOST_FILENAME;
+ curr->value_alloc = TRUE;
}
- else
- return_value = CURL_FORMADD_NULL;
}
- break;
+ else
+ retval = CURL_FORMADD_NULL;
}
+ break;
case CURLFORM_BUFFERPTR:
- current_form->flags |= HTTPPOST_PTRBUFFER|HTTPPOST_BUFFER;
- if(current_form->buffer)
- return_value = CURL_FORMADD_OPTION_TWICE;
+ curr->flags |= HTTPPOST_PTRBUFFER|HTTPPOST_BUFFER;
+ if(curr->buffer)
+ retval = CURL_FORMADD_OPTION_TWICE;
else {
- char *buffer =
- array_state ? array_value : va_arg(params, char *);
- if(buffer) {
- current_form->buffer = buffer; /* store for the moment */
- current_form->value = buffer; /* make it non-NULL to be accepted
+ if(!array_state)
+ avalue = va_arg(params, char *);
+ if(avalue) {
+ curr->buffer = avalue; /* store for the moment */
+ curr->value = avalue; /* make it non-NULL to be accepted
as fine */
}
else
- return_value = CURL_FORMADD_NULL;
+ retval = CURL_FORMADD_NULL;
}
break;
case CURLFORM_BUFFERLENGTH:
- if(current_form->bufferlength)
- return_value = CURL_FORMADD_OPTION_TWICE;
+ if(curr->bufferlength)
+ retval = CURL_FORMADD_OPTION_TWICE;
else
- current_form->bufferlength =
- array_state ? (size_t)array_value : (size_t)va_arg(params, long);
+ curr->bufferlength =
+ array_state ? (size_t)avalue : (size_t)va_arg(params, long);
break;
case CURLFORM_STREAM:
- current_form->flags |= HTTPPOST_CALLBACK;
- if(current_form->userp)
- return_value = CURL_FORMADD_OPTION_TWICE;
+ curr->flags |= HTTPPOST_CALLBACK;
+ if(curr->userp)
+ retval = CURL_FORMADD_OPTION_TWICE;
else {
- char *userp =
- array_state ? array_value : va_arg(params, char *);
- if(userp) {
- current_form->userp = userp;
- current_form->value = userp; /* this is not strictly true but we
- derive a value from this later on
- and we need this non-NULL to be
- accepted as a fine form part */
+ if(!array_state)
+ avalue = va_arg(params, char *);
+ if(avalue) {
+ curr->userp = avalue;
+ curr->value = avalue; /* this is not strictly true but we derive a
+ value from this later on and we need this
+ non-NULL to be accepted as a fine form
+ part */
}
else
- return_value = CURL_FORMADD_NULL;
+ retval = CURL_FORMADD_NULL;
}
break;
case CURLFORM_CONTENTTYPE:
- {
- const char *contenttype =
- array_state ? array_value : va_arg(params, char *);
- if(current_form->contenttype) {
- if(current_form->flags & HTTPPOST_FILENAME) {
- if(contenttype) {
- char *type = strdup(contenttype);
- if(!type)
- return_value = CURL_FORMADD_MEMORY;
+ if(!array_state)
+ avalue = va_arg(params, char *);
+ if(curr->contenttype) {
+ if(curr->flags & HTTPPOST_FILENAME) {
+ if(avalue) {
+ char *type = strdup(avalue);
+ if(!type)
+ retval = CURL_FORMADD_MEMORY;
+ else {
+ form = AddFormInfo(NULL, type, curr);
+ if(!form) {
+ free(type);
+ retval = CURL_FORMADD_MEMORY;
+ }
else {
- form = AddFormInfo(NULL, type, current_form);
- if(!form) {
- free(type);
- return_value = CURL_FORMADD_MEMORY;
- }
- else {
- form->contenttype_alloc = TRUE;
- current_form = form;
- form = NULL;
- }
+ form->contenttype_alloc = TRUE;
+ curr = form;
+ form = NULL;
}
}
- else
- return_value = CURL_FORMADD_NULL;
}
else
- return_value = CURL_FORMADD_OPTION_TWICE;
+ retval = CURL_FORMADD_NULL;
}
- else {
- if(contenttype) {
- current_form->contenttype = strdup(contenttype);
- if(!current_form->contenttype)
- return_value = CURL_FORMADD_MEMORY;
- else
- current_form->contenttype_alloc = TRUE;
- }
+ else
+ retval = CURL_FORMADD_OPTION_TWICE;
+ }
+ else {
+ if(avalue) {
+ curr->contenttype = strdup(avalue);
+ if(!curr->contenttype)
+ retval = CURL_FORMADD_MEMORY;
else
- return_value = CURL_FORMADD_NULL;
+ curr->contenttype_alloc = TRUE;
}
- break;
+ else
+ retval = CURL_FORMADD_NULL;
}
+ break;
+
case CURLFORM_CONTENTHEADER:
{
/* this "cast increases required alignment of target type" but
we consider it OK anyway */
struct curl_slist *list = array_state ?
- (struct curl_slist *)(void *)array_value :
+ (struct curl_slist *)(void *)avalue :
va_arg(params, struct curl_slist *);
- if(current_form->contentheader)
- return_value = CURL_FORMADD_OPTION_TWICE;
+ if(curr->contentheader)
+ retval = CURL_FORMADD_OPTION_TWICE;
else
- current_form->contentheader = list;
+ curr->contentheader = list;
break;
}
case CURLFORM_FILENAME:
case CURLFORM_BUFFER:
- {
- const char *filename = array_state ? array_value :
- va_arg(params, char *);
- if(current_form->showfilename)
- return_value = CURL_FORMADD_OPTION_TWICE;
- else {
- current_form->showfilename = strdup(filename);
- if(!current_form->showfilename)
- return_value = CURL_FORMADD_MEMORY;
- else
- current_form->showfilename_alloc = TRUE;
- }
- break;
+ if(!array_state)
+ avalue = va_arg(params, char *);
+ if(curr->showfilename)
+ retval = CURL_FORMADD_OPTION_TWICE;
+ else {
+ curr->showfilename = strdup(avalue);
+ if(!curr->showfilename)
+ retval = CURL_FORMADD_MEMORY;
+ else
+ curr->showfilename_alloc = TRUE;
}
+ break;
+
default:
- return_value = CURL_FORMADD_UNKNOWN_OPTION;
+ retval = CURL_FORMADD_UNKNOWN_OPTION;
break;
}
}
- if(CURL_FORMADD_OK != return_value) {
+ if(!retval)
+ retval = FormAddCheck(first_form, &newchain, &lastnode);
+
+ if(retval)
/* On error, free allocated fields for all nodes of the FormInfo linked
list without deallocating nodes. List nodes are deallocated later on */
- struct FormInfo *ptr;
- for(ptr = first_form; ptr != NULL; ptr = ptr->more) {
- if(ptr->name_alloc) {
- Curl_safefree(ptr->name);
- ptr->name_alloc = FALSE;
- }
- if(ptr->value_alloc) {
- Curl_safefree(ptr->value);
- ptr->value_alloc = FALSE;
- }
- if(ptr->contenttype_alloc) {
- Curl_safefree(ptr->contenttype);
- ptr->contenttype_alloc = FALSE;
- }
- if(ptr->showfilename_alloc) {
- Curl_safefree(ptr->showfilename);
- ptr->showfilename_alloc = FALSE;
- }
- }
- }
-
- if(CURL_FORMADD_OK == return_value) {
- /* go through the list, check for completeness and if everything is
- * alright add the HttpPost item otherwise set return_value accordingly */
-
- post = NULL;
- for(form = first_form;
- form != NULL;
- form = form->more) {
- if(((!form->name || !form->value) && !post) ||
- ( (form->contentslength) &&
- (form->flags & HTTPPOST_FILENAME) ) ||
- ( (form->flags & HTTPPOST_FILENAME) &&
- (form->flags & HTTPPOST_PTRCONTENTS) ) ||
-
- ( (!form->buffer) &&
- (form->flags & HTTPPOST_BUFFER) &&
- (form->flags & HTTPPOST_PTRBUFFER) ) ||
-
- ( (form->flags & HTTPPOST_READFILE) &&
- (form->flags & HTTPPOST_PTRCONTENTS) )
- ) {
- return_value = CURL_FORMADD_INCOMPLETE;
- break;
- }
- if(((form->flags & HTTPPOST_FILENAME) ||
- (form->flags & HTTPPOST_BUFFER)) &&
- !form->contenttype) {
- char *f = (form->flags & HTTPPOST_BUFFER) ?
- form->showfilename : form->value;
- char const *type;
- type = Curl_mime_contenttype(f);
- if(!type)
- type = prevtype;
- if(!type)
- type = FILE_CONTENTTYPE_DEFAULT;
-
- /* our contenttype is missing */
- form->contenttype = strdup(type);
- if(!form->contenttype) {
- return_value = CURL_FORMADD_MEMORY;
- break;
- }
- form->contenttype_alloc = TRUE;
- }
- if(form->name && form->namelength) {
- /* Name should not contain nul bytes. */
- size_t i;
- for(i = 0; i < form->namelength; i++)
- if(!form->name[i]) {
- return_value = CURL_FORMADD_NULL;
- break;
- }
- if(return_value != CURL_FORMADD_OK)
- break;
- }
- if(!(form->flags & HTTPPOST_PTRNAME) &&
- (form == first_form) ) {
- /* Note that there is small risk that form->name is NULL here if the
- app passed in a bad combo, so we better check for that first. */
- if(form->name) {
- /* copy name (without strdup; possibly not null-terminated) */
- form->name = Curl_memdup0(form->name, form->namelength ?
- form->namelength :
- strlen(form->name));
- }
- if(!form->name) {
- return_value = CURL_FORMADD_MEMORY;
- break;
- }
- form->name_alloc = TRUE;
- }
- if(!(form->flags & (HTTPPOST_FILENAME | HTTPPOST_READFILE |
- HTTPPOST_PTRCONTENTS | HTTPPOST_PTRBUFFER |
- HTTPPOST_CALLBACK)) && form->value) {
- /* copy value (without strdup; possibly contains null characters) */
- size_t clen = (size_t) form->contentslength;
- if(!clen)
- clen = strlen(form->value) + 1;
-
- form->value = Curl_memdup(form->value, clen);
-
- if(!form->value) {
- return_value = CURL_FORMADD_MEMORY;
- break;
- }
- form->value_alloc = TRUE;
- }
- post = AddHttpPost(form->name, form->namelength,
- form->value, form->contentslength,
- form->buffer, form->bufferlength,
- form->contenttype, form->flags,
- form->contentheader, form->showfilename,
- form->userp,
- post, httppost,
- last_post);
-
- if(!post) {
- return_value = CURL_FORMADD_MEMORY;
- break;
- }
-
- if(form->contenttype)
- prevtype = form->contenttype;
- }
- if(CURL_FORMADD_OK != return_value) {
- /* On error, free allocated fields for nodes of the FormInfo linked
- list which are not already owned by the httppost linked list
- without deallocating nodes. List nodes are deallocated later on */
- struct FormInfo *ptr;
- for(ptr = form; ptr != NULL; ptr = ptr->more) {
- if(ptr->name_alloc) {
- Curl_safefree(ptr->name);
- ptr->name_alloc = FALSE;
- }
- if(ptr->value_alloc) {
- Curl_safefree(ptr->value);
- ptr->value_alloc = FALSE;
- }
- if(ptr->contenttype_alloc) {
- Curl_safefree(ptr->contenttype);
- ptr->contenttype_alloc = FALSE;
- }
- if(ptr->showfilename_alloc) {
- Curl_safefree(ptr->showfilename);
- ptr->showfilename_alloc = FALSE;
- }
- }
- }
- }
+ free_formlist(first_form);
/* Always deallocate FormInfo linked list nodes without touching node
fields given that these have either been deallocated or are owned
@@ -684,7 +652,19 @@ CURLFORMcode FormAdd(struct curl_httppost **httppost,
first_form = ptr;
}
- return return_value;
+ if(!retval) {
+ /* Only if all is fine, link the new chain into the provided list */
+ if(*last_post)
+ (*last_post)->next = newchain;
+ else
+ (*httppost) = newchain;
+
+ (*last_post) = lastnode;
+ }
+ else
+ free_chain(newchain);
+
+ return retval;
}
/*
@@ -793,7 +773,7 @@ static CURLcode setname(curl_mimepart *part, const char *name, size_t len)
/* wrap call to fseeko so it matches the calling convention of callback */
static int fseeko_wrapper(void *stream, curl_off_t offset, int whence)
{
-#if defined(HAVE__FSEEKI64)
+#if defined(_WIN32) && defined(USE_WIN32_LARGE_FILES)
return _fseeki64(stream, (__int64)offset, whence);
#elif defined(HAVE_FSEEKO) && defined(HAVE_DECL_FSEEKO)
return fseeko(stream, (off_t)offset, whence);
diff --git a/libs/libcurl/src/formdata.h b/libs/libcurl/src/formdata.h
index 9c5f3cd76b..ba30e79dbf 100644
--- a/libs/libcurl/src/formdata.h
+++ b/libs/libcurl/src/formdata.h
@@ -35,7 +35,6 @@ struct FormInfo {
char *value;
curl_off_t contentslength;
char *contenttype;
- long flags;
char *buffer; /* pointer to existing buffer used for file upload */
size_t bufferlength;
char *showfilename; /* The filename to show. If not set, the actual
@@ -43,10 +42,11 @@ struct FormInfo {
char *userp; /* pointer for the read callback */
struct curl_slist *contentheader;
struct FormInfo *more;
- bool name_alloc;
- bool value_alloc;
- bool contenttype_alloc;
- bool showfilename_alloc;
+ unsigned char flags;
+ BIT(name_alloc);
+ BIT(value_alloc);
+ BIT(contenttype_alloc);
+ BIT(showfilename_alloc);
};
CURLcode Curl_getformdata(CURL *data,
diff --git a/libs/libcurl/src/ftp.c b/libs/libcurl/src/ftp.c
index 20991676f1..8682e56988 100644
--- a/libs/libcurl/src/ftp.c
+++ b/libs/libcurl/src/ftp.c
@@ -54,7 +54,6 @@
#include "ftplistparser.h"
#include "curl_range.h"
#include "curl_krb5.h"
-#include "strtoofft.h"
#include "strcase.h"
#include "vtls/vtls.h"
#include "cfilters.h"
@@ -62,17 +61,18 @@
#include "connect.h"
#include "strerror.h"
#include "inet_ntop.h"
-#include "inet_pton.h"
+#include "curlx/inet_pton.h"
#include "select.h"
#include "parsedate.h" /* for the week day and month names */
#include "sockaddr.h" /* required for Curl_sockaddr_storage */
#include "multiif.h"
#include "url.h"
#include "speedcheck.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
#include "http_proxy.h"
#include "socks.h"
#include "strdup.h"
+#include "curlx/strparse.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
@@ -95,8 +95,7 @@
#ifdef CURL_DISABLE_VERBOSE_STRINGS
#define ftp_pasv_verbose(a,b,c,d) Curl_nop_stmt
-#define FTP_CSTATE(c) ""
-#define FTP_DSTATE(d) ""
+#define FTP_CSTATE(c) ((void)(c), "")
#else /* CURL_DISABLE_VERBOSE_STRINGS */
/* for tracing purposes */
static const char * const ftp_state_names[]={
@@ -136,23 +135,19 @@ static const char * const ftp_state_names[]={
"STOR",
"QUIT"
};
-#define FTP_CSTATE(c) ((c)? ftp_state_names[(c)->proto.ftpc.state] : "???")
-#define FTP_DSTATE(d) (((d) && (d)->conn)? \
- ftp_state_names[(d)->conn->proto.ftpc.state] : "???")
+#define FTP_CSTATE(ftpc) ((ftpc)? ftp_state_names[(ftpc)->state] : "???")
#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
/* This is the ONLY way to change FTP state! */
static void _ftp_state(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
ftpstate newstate
#ifdef DEBUGBUILD
, int lineno
#endif
)
{
- struct connectdata *conn = data->conn;
- struct ftp_conn *ftpc = &conn->proto.ftpc;
-
#if defined(CURL_DISABLE_VERBOSE_STRINGS)
#ifdef DEBUGBUILD
(void)lineno;
@@ -160,10 +155,10 @@ static void _ftp_state(struct Curl_easy *data,
#else /* CURL_DISABLE_VERBOSE_STRINGS */
if(ftpc->state != newstate)
#ifdef DEBUGBUILD
- CURL_TRC_FTP(data, "[%s] -> [%s] (line %d)", FTP_DSTATE(data),
+ CURL_TRC_FTP(data, "[%s] -> [%s] (line %d)", FTP_CSTATE(ftpc),
ftp_state_names[newstate], lineno);
#else
- CURL_TRC_FTP(data, "[%s] -> [%s]", FTP_DSTATE(data),
+ CURL_TRC_FTP(data, "[%s] -> [%s]", FTP_CSTATE(ftpc),
ftp_state_names[newstate]);
#endif
#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
@@ -174,32 +169,40 @@ static void _ftp_state(struct Curl_easy *data,
/* Local API functions */
#ifndef DEBUGBUILD
-#define ftp_state(x,y) _ftp_state(x,y)
+#define ftp_state(x,y,z) _ftp_state(x,y,z)
#else /* !DEBUGBUILD */
-#define ftp_state(x,y) _ftp_state(x,y,__LINE__)
+#define ftp_state(x,y,z) _ftp_state(x,y,z,__LINE__)
#endif /* DEBUGBUILD */
static CURLcode ftp_sendquote(struct Curl_easy *data,
- struct connectdata *conn,
+ struct ftp_conn *ftpc,
struct curl_slist *quote);
-static CURLcode ftp_quit(struct Curl_easy *data, struct connectdata *conn);
-static CURLcode ftp_parse_url_path(struct Curl_easy *data);
-static CURLcode ftp_regular_transfer(struct Curl_easy *data, bool *done);
+static CURLcode ftp_quit(struct Curl_easy *data, struct ftp_conn *ftpc);
+static CURLcode ftp_parse_url_path(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp);
+static CURLcode ftp_regular_transfer(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp,
+ bool *done);
#ifndef CURL_DISABLE_VERBOSE_STRINGS
static void ftp_pasv_verbose(struct Curl_easy *data,
struct Curl_addrinfo *ai,
char *newhost, /* ASCII version */
int port);
#endif
-static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data);
-static CURLcode ftp_state_mdtm(struct Curl_easy *data);
+static CURLcode ftp_state_mdtm(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp);
static CURLcode ftp_state_quote(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp,
bool init, ftpstate instate);
static CURLcode ftp_nb_type(struct Curl_easy *data,
- struct connectdata *conn,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp,
bool ascii, ftpstate newstate);
-static int ftp_need_type(struct connectdata *conn,
- bool ascii);
+static int ftp_need_type(struct ftp_conn *ftpc, bool ascii);
static CURLcode ftp_do(struct Curl_easy *data, bool *done);
static CURLcode ftp_done(struct Curl_easy *data,
CURLcode, bool premature);
@@ -216,16 +219,26 @@ static CURLcode ftp_doing(struct Curl_easy *data,
bool *dophase_done);
static CURLcode ftp_setup_connection(struct Curl_easy *data,
struct connectdata *conn);
-static CURLcode init_wc_data(struct Curl_easy *data);
-static CURLcode wc_statemach(struct Curl_easy *data);
+static CURLcode init_wc_data(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp);
+static CURLcode wc_statemach(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp);
static void wc_data_dtor(void *ptr);
-static CURLcode ftp_state_retr(struct Curl_easy *data, curl_off_t filesize);
+static CURLcode ftp_state_retr(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp,
+ curl_off_t filesize);
static CURLcode ftp_readresp(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
int sockindex,
struct pingpong *pp,
int *ftpcode,
size_t *size);
static CURLcode ftp_dophase_done(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp,
bool connected);
/*
@@ -250,6 +263,7 @@ const struct Curl_handler Curl_handler_ftp = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_FTP, /* defport */
CURLPROTO_FTP, /* protocol */
CURLPROTO_FTP, /* family */
@@ -282,6 +296,7 @@ const struct Curl_handler Curl_handler_ftps = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_FTPS, /* defport */
CURLPROTO_FTPS, /* protocol */
CURLPROTO_FTP, /* family */
@@ -290,9 +305,11 @@ const struct Curl_handler Curl_handler_ftps = {
};
#endif
-static void close_secondarysocket(struct Curl_easy *data)
+static void close_secondarysocket(struct Curl_easy *data,
+ struct ftp_conn *ftpc)
{
- CURL_TRC_FTP(data, "[%s] closing DATA connection", FTP_DSTATE(data));
+ (void)ftpc;
+ CURL_TRC_FTP(data, "[%s] closing DATA connection", FTP_CSTATE(ftpc));
Curl_conn_close(data, SECONDARYSOCKET);
Curl_conn_cf_discard_all(data, data->conn, SECONDARYSOCKET);
}
@@ -328,8 +345,7 @@ static void freedirs(struct ftp_conn *ftpc)
}
#ifdef CURL_PREFER_LF_LINEENDS
-/***********************************************************************
- *
+/*
* Lineend Conversions
* On ASCII transfers, e.g. directory listings, we might get lines
* ending in '\r\n' and we prefer just '\n'.
@@ -346,9 +362,12 @@ static CURLcode ftp_cw_lc_write(struct Curl_easy *data,
{
static const char nl = '\n';
struct ftp_cw_lc_ctx *ctx = writer->ctx;
+ struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
+
+ if(!ftpc)
+ return CURLE_FAILED_INIT;
- if(!(type & CLIENTWRITE_BODY) ||
- data->conn->proto.ftpc.transfertype != 'A')
+ if(!(type & CLIENTWRITE_BODY) || ftpc->transfertype != 'A')
return Curl_cwriter_write(data, writer->next, type, buf, blen);
/* ASCII mode BODY data, convert lineends */
@@ -422,18 +441,18 @@ static const struct Curl_cwtype ftp_cw_lc = {
* ftp_check_ctrl_on_data_wait()
*
*/
-static CURLcode ftp_check_ctrl_on_data_wait(struct Curl_easy *data)
+static CURLcode ftp_check_ctrl_on_data_wait(struct Curl_easy *data,
+ struct ftp_conn *ftpc)
{
struct connectdata *conn = data->conn;
curl_socket_t ctrl_sock = conn->sock[FIRSTSOCKET];
- struct ftp_conn *ftpc = &conn->proto.ftpc;
struct pingpong *pp = &ftpc->pp;
ssize_t nread;
int ftpcode;
bool response = FALSE;
/* First check whether there is a cached response from server */
- if(Curl_dyn_len(&pp->recvbuf) && (*Curl_dyn_ptr(&pp->recvbuf) > '3')) {
+ if(curlx_dyn_len(&pp->recvbuf) && (*curlx_dyn_ptr(&pp->recvbuf) > '3')) {
/* Data connection could not be established, let's return */
infof(data, "There is negative response in cache while serv connect");
(void)Curl_GetFTPResponse(data, &nread, &ftpcode);
@@ -462,16 +481,16 @@ static CURLcode ftp_check_ctrl_on_data_wait(struct Curl_easy *data)
if(response) {
infof(data, "Ctrl conn has data while waiting for data conn");
if(pp->overflow > 3) {
- char *r = Curl_dyn_ptr(&pp->recvbuf);
+ const char *r = curlx_dyn_ptr(&pp->recvbuf);
DEBUGASSERT((pp->overflow + pp->nfinal) <=
- Curl_dyn_len(&pp->recvbuf));
+ curlx_dyn_len(&pp->recvbuf));
/* move over the most recently handled response line */
r += pp->nfinal;
if(LASTLINE(r)) {
- int status = curlx_sltosi(strtol(r, NULL, 10));
- if(status == 226) {
+ curl_off_t status;
+ if(!curlx_str_number(&r, &status, 999) && (status == 226)) {
/* funny timing situation where we get the final message on the
control connection before traffic on the data connection has been
noticed. Leave the 226 in there and use this as a trigger to read
@@ -497,30 +516,30 @@ static CURLcode ftp_check_ctrl_on_data_wait(struct Curl_easy *data)
/***********************************************************************
*
- * InitiateTransfer()
+ * ftp_initiate_transfer()
*
* After connection from server is accepted this function is called to
* setup transfer parameters and initiate the data transfer.
*
*/
-static CURLcode InitiateTransfer(struct Curl_easy *data)
+static CURLcode ftp_initiate_transfer(struct Curl_easy *data,
+ struct ftp_conn *ftpc)
{
CURLcode result = CURLE_OK;
- struct connectdata *conn = data->conn;
bool connected;
- CURL_TRC_FTP(data, "InitiateTransfer()");
+ CURL_TRC_FTP(data, "ftp_initiate_transfer()");
result = Curl_conn_connect(data, SECONDARYSOCKET, TRUE, &connected);
if(result || !connected)
return result;
- if(conn->proto.ftpc.state_saved == FTP_STOR) {
+ if(ftpc->state_saved == FTP_STOR) {
/* When we know we are uploading a specified file, we can get the file
size prior to the actual upload. */
Curl_pgrsSetUploadSize(data, data->state.infilesize);
/* set the SO_SNDBUF for the secondary socket for those who need it */
- Curl_sndbuf_init(conn->sock[SECONDARYSOCKET]);
+ Curl_sndbuf_init(data->conn->sock[SECONDARYSOCKET]);
/* FTP upload, shutdown DATA, ignore shutdown errors, as we rely
* on the server response on the CONTROL connection. */
@@ -529,23 +548,24 @@ static CURLcode InitiateTransfer(struct Curl_easy *data)
else {
/* FTP download, shutdown, do not ignore errors */
Curl_xfer_setup2(data, CURL_XFER_RECV,
- conn->proto.ftpc.retr_size_saved, TRUE, FALSE);
+ ftpc->retr_size_saved, TRUE, FALSE);
}
- conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
- ftp_state(data, FTP_STOP);
+ ftpc->pp.pending_resp = TRUE; /* expect server response */
+ ftp_state(data, ftpc, FTP_STOP);
return CURLE_OK;
}
static bool ftp_endofresp(struct Curl_easy *data, struct connectdata *conn,
- char *line, size_t len, int *code)
+ const char *line, size_t len, int *code)
{
+ curl_off_t status;
(void)data;
(void)conn;
- if((len > 3) && LASTLINE(line)) {
- *code = curlx_sltosi(strtol(line, NULL, 10));
+ if((len > 3) && LASTLINE(line) && !curlx_str_number(&line, &status, 999)) {
+ *code = (int)status;
return TRUE;
}
@@ -553,6 +573,7 @@ static bool ftp_endofresp(struct Curl_easy *data, struct connectdata *conn,
}
static CURLcode ftp_readresp(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
int sockindex,
struct pingpong *pp,
int *ftpcode, /* return the ftp-code if done */
@@ -564,7 +585,7 @@ static CURLcode ftp_readresp(struct Curl_easy *data,
#ifdef HAVE_GSSAPI
{
struct connectdata *conn = data->conn;
- char * const buf = Curl_dyn_ptr(&data->conn->proto.ftpc.pp.recvbuf);
+ char * const buf = curlx_dyn_ptr(&ftpc->pp.recvbuf);
/* handle the security-oriented responses 6xx ***/
switch(code) {
@@ -584,8 +605,9 @@ static CURLcode ftp_readresp(struct Curl_easy *data,
}
#endif
- /* store the latest code for later retrieval */
- data->info.httpcode = code;
+ /* store the latest code for later retrieval, except during shutdown */
+ if(!ftpc->shutdown)
+ data->info.httpcode = code;
if(ftpcode)
*ftpcode = code;
@@ -599,7 +621,7 @@ static CURLcode ftp_readresp(struct Curl_easy *data,
* generically is a good idea.
*/
infof(data, "We got a 421 - timeout");
- ftp_state(data, FTP_STOP);
+ ftp_state(data, ftpc, FTP_STOP);
return CURLE_OPERATION_TIMEDOUT;
}
@@ -628,21 +650,22 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
struct connectdata *conn = data->conn;
curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
CURLcode result = CURLE_OK;
- struct ftp_conn *ftpc = &conn->proto.ftpc;
+ struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
struct pingpong *pp = &ftpc->pp;
size_t nread;
int cache_skip = 0;
int value_to_be_ignored = 0;
CURL_TRC_FTP(data, "getFTPResponse start");
-
+ *nreadp = 0;
if(ftpcode)
*ftpcode = 0; /* 0 for errors */
else
/* make the pointer point to something for the rest of this function */
ftpcode = &value_to_be_ignored;
- *nreadp = 0;
+ if(!ftpc)
+ return CURLE_FAILED_INIT;
while(!*ftpcode && !result) {
/* check and reset timeout value every lap */
@@ -672,7 +695,7 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
*
*/
- if(Curl_dyn_len(&pp->recvbuf) && (cache_skip < 2)) {
+ if(curlx_dyn_len(&pp->recvbuf) && (cache_skip < 2)) {
/*
* There is a cache left since before. We then skipping the wait for
* socket action, unless this is the same cache like the previous round
@@ -702,11 +725,11 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
break;
}
- result = ftp_readresp(data, FIRSTSOCKET, pp, ftpcode, &nread);
+ result = ftp_readresp(data, ftpc, FIRSTSOCKET, pp, ftpcode, &nread);
if(result)
break;
- if(!nread && Curl_dyn_len(&pp->recvbuf))
+ if(!nread && curlx_dyn_len(&pp->recvbuf))
/* bump cache skip counter as on repeated skips we must wait for more
data */
cache_skip++;
@@ -727,25 +750,24 @@ CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
}
static CURLcode ftp_state_user(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
struct connectdata *conn)
{
- CURLcode result = Curl_pp_sendf(data,
- &conn->proto.ftpc.pp, "USER %s",
+ CURLcode result = Curl_pp_sendf(data, &ftpc->pp, "USER %s",
conn->user ? conn->user : "");
if(!result) {
- struct ftp_conn *ftpc = &conn->proto.ftpc;
ftpc->ftp_trying_alternative = FALSE;
- ftp_state(data, FTP_USER);
+ ftp_state(data, ftpc, FTP_USER);
}
return result;
}
static CURLcode ftp_state_pwd(struct Curl_easy *data,
- struct connectdata *conn)
+ struct ftp_conn *ftpc)
{
- CURLcode result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "PWD");
+ CURLcode result = Curl_pp_sendf(data, &ftpc->pp, "%s", "PWD");
if(!result)
- ftp_state(data, FTP_PWD);
+ ftp_state(data, ftpc, FTP_PWD);
return result;
}
@@ -755,21 +777,25 @@ static int ftp_getsock(struct Curl_easy *data,
struct connectdata *conn,
curl_socket_t *socks)
{
- return Curl_pp_getsock(data, &conn->proto.ftpc.pp, socks);
+ struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN);
+ return ftpc ? Curl_pp_getsock(data, &ftpc->pp, socks) : GETSOCK_BLANK;
}
/* For the FTP "DO_MORE" phase only */
static int ftp_domore_getsock(struct Curl_easy *data,
struct connectdata *conn, curl_socket_t *socks)
{
- struct ftp_conn *ftpc = &conn->proto.ftpc;
+ struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN);
(void)data;
+ if(!ftpc)
+ return GETSOCK_BLANK;
+
/* When in DO_MORE state, we could be either waiting for us to connect to a
* remote site, or we could wait for that site to connect to us. Or just
* handle ordinary commands.
*/
- CURL_TRC_FTP(data, "[%s] ftp_domore_getsock()", FTP_DSTATE(data));
+ CURL_TRC_FTP(data, "[%s] ftp_domore_getsock()", FTP_CSTATE(ftpc));
if(FTP_STOP == ftpc->state) {
/* if stopped and still in this state, then we are also waiting for a
@@ -782,7 +808,7 @@ static int ftp_domore_getsock(struct Curl_easy *data,
* via its adjust_pollset() */
return GETSOCK_READSOCK(0);
}
- return Curl_pp_getsock(data, &conn->proto.ftpc.pp, socks);
+ return Curl_pp_getsock(data, &ftpc->pp, socks);
}
/* This is called after the FTP_QUOTE state is passed.
@@ -792,14 +818,14 @@ static int ftp_domore_getsock(struct Curl_easy *data,
missing ones, if that option is enabled.
*/
static CURLcode ftp_state_cwd(struct Curl_easy *data,
- struct connectdata *conn)
+ struct ftp_conn *ftpc,
+ struct FTP *ftp)
{
CURLcode result = CURLE_OK;
- struct ftp_conn *ftpc = &conn->proto.ftpc;
if(ftpc->cwddone)
/* already done and fine */
- result = ftp_state_mdtm(data);
+ result = ftp_state_mdtm(data, ftpc, ftp);
else {
/* FTPFILE_NOCWD with full path: expect ftpc->cwddone! */
DEBUGASSERT((data->set.ftp_filemethod != FTPFILE_NOCWD) ||
@@ -807,7 +833,7 @@ static CURLcode ftp_state_cwd(struct Curl_easy *data,
ftpc->count2 = 0; /* count2 counts failed CWDs */
- if(conn->bits.reuse && ftpc->entrypath &&
+ if(data->conn->bits.reuse && ftpc->entrypath &&
/* no need to go to entrypath when we have an absolute path */
!(ftpc->dirdepth && ftpc->dirs[0][0] == '/')) {
/* This is a reused connection. Since we change directory to where the
@@ -817,7 +843,7 @@ static CURLcode ftp_state_cwd(struct Curl_easy *data,
for all upcoming ones in the ftp->dirs[] array */
result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s", ftpc->entrypath);
if(!result)
- ftp_state(data, FTP_CWD);
+ ftp_state(data, ftpc, FTP_CWD);
}
else {
if(ftpc->dirdepth) {
@@ -827,11 +853,11 @@ static CURLcode ftp_state_cwd(struct Curl_easy *data,
result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s",
ftpc->dirs[ftpc->cwdcount -1]);
if(!result)
- ftp_state(data, FTP_CWD);
+ ftp_state(data, ftpc, FTP_CWD);
}
else {
/* No CWD necessary */
- result = ftp_state_mdtm(data);
+ result = ftp_state_mdtm(data, ftpc, ftp);
}
}
}
@@ -845,11 +871,11 @@ typedef enum {
} ftpport;
static CURLcode ftp_state_use_port(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
ftpport fcmd) /* start with this */
{
CURLcode result = CURLE_FTP_PORT_FAILED;
struct connectdata *conn = data->conn;
- struct ftp_conn *ftpc = &conn->proto.ftpc;
curl_socket_t portsock = CURL_SOCKET_BAD;
char myhost[MAX_IPADR_LEN + 1] = "";
@@ -863,7 +889,6 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
struct sockaddr_in6 * const sa6 = (void *)sa;
#endif
static const char mode[][5] = { "EPRT", "PORT" };
- enum resolve_t rc;
int error;
char *host = NULL;
char *string_ftpport = data->set.str[STRING_FTPPORT];
@@ -909,7 +934,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
/* either ipv6 or (ipv4|domain|interface):port(-range) */
addrlen = ip_end - string_ftpport;
#ifdef USE_IPV6
- if(Curl_inet_pton(AF_INET6, string_ftpport, &sa6->sin6_addr) == 1) {
+ if(curlx_inet_pton(AF_INET6, string_ftpport, &sa6->sin6_addr) == 1) {
/* ipv6 */
port_min = port_max = 0;
ip_end = NULL; /* this got no port ! */
@@ -923,13 +948,20 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
/* parse the port */
if(ip_end) {
- char *port_sep = NULL;
- char *port_start = strchr(ip_end, ':');
- if(port_start) {
- port_min = curlx_ultous(strtoul(port_start + 1, NULL, 10));
- port_sep = strchr(port_start, '-');
- if(port_sep) {
- port_max = curlx_ultous(strtoul(port_sep + 1, NULL, 10));
+ const char *portp = strchr(ip_end, ':');
+ if(portp) {
+ curl_off_t start;
+ curl_off_t end;
+ portp++;
+ if(!curlx_str_number(&portp, &start, 0xffff)) {
+ /* got the first number */
+ port_min = (unsigned short)start;
+ if(!curlx_str_single(&portp, '-')) {
+ /* got the dash */
+ if(!curlx_str_number(&portp, &end, 0xffff))
+ /* got the second number */
+ port_max = (unsigned short)end;
+ }
}
else
port_max = port_min;
@@ -1003,14 +1035,12 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
}
/* resolv ip/host to ip */
- rc = Curl_resolv(data, host, 0, FALSE, &dns_entry);
- if(rc == CURLRESOLV_PENDING)
- (void)Curl_resolver_wait_resolv(data, &dns_entry);
- if(dns_entry) {
+ res = NULL;
+ result = Curl_resolv_blocking(data, host, 0, conn->ip_version, &dns_entry);
+ if(!result) {
+ DEBUGASSERT(dns_entry);
res = dns_entry->addr;
}
- else
- res = NULL; /* failure! */
if(!res) {
failf(data, "failed to resolve the address provided to PORT: %s", host);
@@ -1034,7 +1064,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
goto out;
}
CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), opened socket",
- FTP_DSTATE(data));
+ FTP_CSTATE(ftpc));
/* step 3, bind to a suitable local address */
@@ -1052,7 +1082,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
if(bind(portsock, sa, sslen) ) {
/* It failed. */
error = SOCKERRNO;
- if(possibly_non_local && (error == EADDRNOTAVAIL)) {
+ if(possibly_non_local && (error == SOCKEADDRNOTAVAIL)) {
/* The requested bind address is not local. Use the address used for
* the control connection instead and restart the port loop
*/
@@ -1069,7 +1099,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
possibly_non_local = FALSE; /* do not try this again */
continue;
}
- if(error != EADDRINUSE && error != EACCES) {
+ if(error != SOCKEADDRINUSE && error != SOCKEACCES) {
failf(data, "bind(port=%hu) failed: %s", port,
Curl_strerror(error, buffer, sizeof(buffer)));
goto out;
@@ -1096,7 +1126,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
goto out;
}
CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), socket bound to port %d",
- FTP_DSTATE(data), port);
+ FTP_CSTATE(ftpc), port);
/* step 4, listen on the socket */
@@ -1106,7 +1136,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
goto out;
}
CURL_TRC_FTP(data, "[%s] ftp_state_use_port(), listening on %d",
- FTP_DSTATE(data), port);
+ FTP_CSTATE(ftpc), port);
/* step 5, send the proper FTP command */
@@ -1116,7 +1146,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
#ifdef USE_IPV6
if(!conn->bits.ftp_use_eprt && conn->bits.ipv6)
- /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the
+ /* EPRT is disabled but we are connected to an IPv6 host, so we ignore the
request and enable EPRT again! */
conn->bits.ftp_use_eprt = TRUE;
#endif
@@ -1193,7 +1223,7 @@ static CURLcode ftp_state_use_port(struct Curl_easy *data,
/* store which command was sent */
ftpc->count1 = fcmd;
- ftp_state(data, FTP_PORT);
+ ftp_state(data, ftpc, FTP_PORT);
/* Replace any filter on SECONDARY with one listening on this socket */
result = Curl_conn_tcp_listen_set(data, conn, SECONDARYSOCKET, &portsock);
@@ -1205,7 +1235,7 @@ out:
if(dns_entry)
Curl_resolv_unlink(data, &dns_entry);
if(result) {
- ftp_state(data, FTP_STOP);
+ ftp_state(data, ftpc, FTP_STOP);
}
else {
/* successfully setup the list socket filter. Do we need more? */
@@ -1225,9 +1255,9 @@ out:
}
static CURLcode ftp_state_use_pasv(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
struct connectdata *conn)
{
- struct ftp_conn *ftpc = &conn->proto.ftpc;
CURLcode result = CURLE_OK;
/*
Here's the executive summary on what to do:
@@ -1248,7 +1278,7 @@ static CURLcode ftp_state_use_pasv(struct Curl_easy *data,
#ifdef PF_INET6
if(!conn->bits.ftp_use_epsv && conn->bits.ipv6)
- /* EPSV is disabled but we are connected to a IPv6 host, so we ignore the
+ /* EPSV is disabled but we are connected to an IPv6 host, so we ignore the
request and enable EPSV again! */
conn->bits.ftp_use_epsv = TRUE;
#endif
@@ -1258,7 +1288,7 @@ static CURLcode ftp_state_use_pasv(struct Curl_easy *data,
result = Curl_pp_sendf(data, &ftpc->pp, "%s", mode[modeoff]);
if(!result) {
ftpc->count1 = modeoff;
- ftp_state(data, FTP_PASV);
+ ftp_state(data, ftpc, FTP_PASV);
infof(data, "Connect data stream passively");
}
return result;
@@ -1271,55 +1301,52 @@ static CURLcode ftp_state_use_pasv(struct Curl_easy *data,
* request is made. Thus, if an actual transfer is to be made this is where we
* take off for real.
*/
-static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data)
+static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp)
{
CURLcode result = CURLE_OK;
- struct FTP *ftp = data->req.p.ftp;
struct connectdata *conn = data->conn;
if(ftp->transfer != PPTRANSFER_BODY) {
/* does not transfer any data */
/* still possibly do PRE QUOTE jobs */
- ftp_state(data, FTP_RETR_PREQUOTE);
- result = ftp_state_quote(data, TRUE, FTP_RETR_PREQUOTE);
+ ftp_state(data, ftpc, FTP_RETR_PREQUOTE);
+ result = ftp_state_quote(data, ftpc, ftp, TRUE, FTP_RETR_PREQUOTE);
}
else if(data->set.ftp_use_port) {
/* We have chosen to use the PORT (or similar) command */
- result = ftp_state_use_port(data, EPRT);
+ result = ftp_state_use_port(data, ftpc, EPRT);
}
else {
/* We have chosen (this is default) to use the PASV (or similar) command */
if(data->set.ftp_use_pret) {
/* The user has requested that we send a PRET command
to prepare the server for the upcoming PASV */
- struct ftp_conn *ftpc = &conn->proto.ftpc;
- if(!conn->proto.ftpc.file)
+ if(!ftpc->file)
result = Curl_pp_sendf(data, &ftpc->pp, "PRET %s",
data->set.str[STRING_CUSTOMREQUEST] ?
data->set.str[STRING_CUSTOMREQUEST] :
(data->state.list_only ? "NLST" : "LIST"));
else if(data->state.upload)
- result = Curl_pp_sendf(data, &ftpc->pp, "PRET STOR %s",
- conn->proto.ftpc.file);
+ result = Curl_pp_sendf(data, &ftpc->pp, "PRET STOR %s", ftpc->file);
else
- result = Curl_pp_sendf(data, &ftpc->pp, "PRET RETR %s",
- conn->proto.ftpc.file);
+ result = Curl_pp_sendf(data, &ftpc->pp, "PRET RETR %s", ftpc->file);
if(!result)
- ftp_state(data, FTP_PRET);
+ ftp_state(data, ftpc, FTP_PRET);
}
else
- result = ftp_state_use_pasv(data, conn);
+ result = ftp_state_use_pasv(data, ftpc, conn);
}
return result;
}
static CURLcode ftp_state_rest(struct Curl_easy *data,
- struct connectdata *conn)
+ struct ftp_conn *ftpc,
+ struct FTP *ftp)
{
CURLcode result = CURLE_OK;
- struct FTP *ftp = data->req.p.ftp;
- struct ftp_conn *ftpc = &conn->proto.ftpc;
if((ftp->transfer != PPTRANSFER_BODY) && ftpc->file) {
/* if a "head"-like request is being made (on a file) */
@@ -1328,20 +1355,19 @@ static CURLcode ftp_state_rest(struct Curl_easy *data,
whether it supports range */
result = Curl_pp_sendf(data, &ftpc->pp, "REST %d", 0);
if(!result)
- ftp_state(data, FTP_REST);
+ ftp_state(data, ftpc, FTP_REST);
}
else
- result = ftp_state_prepare_transfer(data);
+ result = ftp_state_prepare_transfer(data, ftpc, ftp);
return result;
}
static CURLcode ftp_state_size(struct Curl_easy *data,
- struct connectdata *conn)
+ struct ftp_conn *ftpc,
+ struct FTP *ftp)
{
CURLcode result = CURLE_OK;
- struct FTP *ftp = data->req.p.ftp;
- struct ftp_conn *ftpc = &conn->proto.ftpc;
if((ftp->transfer == PPTRANSFER_INFO) && ftpc->file) {
/* if a "head"-like request is being made (on a file) */
@@ -1349,19 +1375,19 @@ static CURLcode ftp_state_size(struct Curl_easy *data,
/* we know ftpc->file is a valid pointer to a filename */
result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
if(!result)
- ftp_state(data, FTP_SIZE);
+ ftp_state(data, ftpc, FTP_SIZE);
}
else
- result = ftp_state_rest(data, conn);
+ result = ftp_state_rest(data, ftpc, ftp);
return result;
}
-static CURLcode ftp_state_list(struct Curl_easy *data)
+static CURLcode ftp_state_list(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp)
{
CURLcode result = CURLE_OK;
- struct FTP *ftp = data->req.p.ftp;
- struct connectdata *conn = data->conn;
/* If this output is to be machine-parsed, the NLST command might be better
to use, since the LIST command output is not specified or standard in any
@@ -1413,39 +1439,42 @@ static CURLcode ftp_state_list(struct Curl_easy *data)
if(!cmd)
return CURLE_OUT_OF_MEMORY;
- result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", cmd);
+ result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd);
free(cmd);
if(!result)
- ftp_state(data, FTP_LIST);
+ ftp_state(data, ftpc, FTP_LIST);
return result;
}
-static CURLcode ftp_state_retr_prequote(struct Curl_easy *data)
+static CURLcode ftp_state_retr_prequote(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp)
{
/* We have sent the TYPE, now we must send the list of prequote strings */
- return ftp_state_quote(data, TRUE, FTP_RETR_PREQUOTE);
+ return ftp_state_quote(data, ftpc, ftp, TRUE, FTP_RETR_PREQUOTE);
}
-static CURLcode ftp_state_stor_prequote(struct Curl_easy *data)
+static CURLcode ftp_state_stor_prequote(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp)
{
/* We have sent the TYPE, now we must send the list of prequote strings */
- return ftp_state_quote(data, TRUE, FTP_STOR_PREQUOTE);
+ return ftp_state_quote(data, ftpc, ftp, TRUE, FTP_STOR_PREQUOTE);
}
-static CURLcode ftp_state_type(struct Curl_easy *data)
+static CURLcode ftp_state_type(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp)
{
CURLcode result = CURLE_OK;
- struct FTP *ftp = data->req.p.ftp;
- struct connectdata *conn = data->conn;
- struct ftp_conn *ftpc = &conn->proto.ftpc;
/* If we have selected NOBODY and HEADER, it means that we only want file
information. Which in FTP cannot be much more than the file size and
date. */
if(data->req.no_body && ftpc->file &&
- ftp_need_type(conn, data->state.prefer_ascii)) {
+ ftp_need_type(ftpc, data->state.prefer_ascii)) {
/* The SIZE command is _not_ RFC 959 specified, and therefore many servers
may not support it! It is however the only way we have to get a file's
size! */
@@ -1455,23 +1484,23 @@ static CURLcode ftp_state_type(struct Curl_easy *data)
/* Some servers return different sizes for different modes, and thus we
must set the proper type before we check the size */
- result = ftp_nb_type(data, conn, data->state.prefer_ascii, FTP_TYPE);
+ result = ftp_nb_type(data, ftpc, ftp, data->state.prefer_ascii, FTP_TYPE);
if(result)
return result;
}
else
- result = ftp_state_size(data, conn);
+ result = ftp_state_size(data, ftpc, ftp);
return result;
}
/* This is called after the CWD commands have been done in the beginning of
the DO phase */
-static CURLcode ftp_state_mdtm(struct Curl_easy *data)
+static CURLcode ftp_state_mdtm(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp)
{
CURLcode result = CURLE_OK;
- struct connectdata *conn = data->conn;
- struct ftp_conn *ftpc = &conn->proto.ftpc;
/* Requested time of file or time-depended transfer? */
if((data->set.get_filetime || data->set.timecondition) && ftpc->file) {
@@ -1481,10 +1510,10 @@ static CURLcode ftp_state_mdtm(struct Curl_easy *data)
result = Curl_pp_sendf(data, &ftpc->pp, "MDTM %s", ftpc->file);
if(!result)
- ftp_state(data, FTP_MDTM);
+ ftp_state(data, ftpc, FTP_MDTM);
}
else
- result = ftp_state_type(data);
+ result = ftp_state_type(data, ftpc, ftp);
return result;
}
@@ -1492,12 +1521,11 @@ static CURLcode ftp_state_mdtm(struct Curl_easy *data)
/* This is called after the TYPE and possible quote commands have been sent */
static CURLcode ftp_state_ul_setup(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp,
bool sizechecked)
{
CURLcode result = CURLE_OK;
- struct connectdata *conn = data->conn;
- struct FTP *ftp = data->req.p.ftp;
- struct ftp_conn *ftpc = &conn->proto.ftpc;
bool append = data->set.remote_append;
if((data->state.resume_from && !sizechecked) ||
@@ -1520,7 +1548,7 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data,
/* Got no given size to start from, figure it out */
result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
if(!result)
- ftp_state(data, FTP_STOR_SIZE);
+ ftp_state(data, ftpc, FTP_STOR_SIZE);
return result;
}
@@ -1576,7 +1604,7 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data,
* ftp_done() because we did not transfer anything! */
ftp->transfer = PPTRANSFER_NONE;
- ftp_state(data, FTP_STOP);
+ ftp_state(data, ftpc, FTP_STOP);
return CURLE_OK;
}
}
@@ -1586,19 +1614,18 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data,
result = Curl_pp_sendf(data, &ftpc->pp, append ? "APPE %s" : "STOR %s",
ftpc->file);
if(!result)
- ftp_state(data, FTP_STOR);
+ ftp_state(data, ftpc, FTP_STOR);
return result;
}
static CURLcode ftp_state_quote(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp,
bool init,
ftpstate instate)
{
CURLcode result = CURLE_OK;
- struct FTP *ftp = data->req.p.ftp;
- struct connectdata *conn = data->conn;
- struct ftp_conn *ftpc = &conn->proto.ftpc;
bool quote = FALSE;
struct curl_slist *item;
@@ -1647,7 +1674,7 @@ static CURLcode ftp_state_quote(struct Curl_easy *data,
result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd);
if(result)
return result;
- ftp_state(data, instate);
+ ftp_state(data, ftpc, instate);
quote = TRUE;
}
}
@@ -1657,15 +1684,15 @@ static CURLcode ftp_state_quote(struct Curl_easy *data,
switch(instate) {
case FTP_QUOTE:
default:
- result = ftp_state_cwd(data, conn);
+ result = ftp_state_cwd(data, ftpc, ftp);
break;
case FTP_RETR_PREQUOTE:
if(ftp->transfer != PPTRANSFER_BODY)
- ftp_state(data, FTP_STOP);
+ ftp_state(data, ftpc, FTP_STOP);
else {
if(ftpc->known_filesize != -1) {
Curl_pgrsSetDownloadSize(data, ftpc->known_filesize);
- result = ftp_state_retr(data, ftpc->known_filesize);
+ result = ftp_state_retr(data, ftpc, ftp, ftpc->known_filesize);
}
else {
if(data->set.ignorecl || data->state.prefer_ascii) {
@@ -1683,18 +1710,18 @@ static CURLcode ftp_state_quote(struct Curl_easy *data,
*/
result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
if(!result)
- ftp_state(data, FTP_RETR);
+ ftp_state(data, ftpc, FTP_RETR);
}
else {
result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
if(!result)
- ftp_state(data, FTP_RETR_SIZE);
+ ftp_state(data, ftpc, FTP_RETR_SIZE);
}
}
}
break;
case FTP_STOR_PREQUOTE:
- result = ftp_state_ul_setup(data, FALSE);
+ result = ftp_state_ul_setup(data, ftpc, ftp, FALSE);
break;
case FTP_POSTQUOTE:
break;
@@ -1707,6 +1734,7 @@ static CURLcode ftp_state_quote(struct Curl_easy *data,
/* called from ftp_state_pasv_resp to switch to PASV in case of EPSV
problems */
static CURLcode ftp_epsv_disable(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
struct connectdata *conn)
{
CURLcode result = CURLE_OK;
@@ -1724,15 +1752,14 @@ static CURLcode ftp_epsv_disable(struct Curl_easy *data,
infof(data, "Failed EPSV attempt. Disabling EPSV");
/* disable it for next transfer */
conn->bits.ftp_use_epsv = FALSE;
- Curl_conn_close(data, SECONDARYSOCKET);
- Curl_conn_cf_discard_all(data, conn, SECONDARYSOCKET);
+ close_secondarysocket(data, ftpc);
data->state.errorbuf = FALSE; /* allow error message to get
rewritten */
- result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "PASV");
+ result = Curl_pp_sendf(data, &ftpc->pp, "%s", "PASV");
if(!result) {
- conn->proto.ftpc.count1++;
+ ftpc->count1++;
/* remain in/go to the FTP_PASV state */
- ftp_state(data, FTP_PASV);
+ ftp_state(data, ftpc, FTP_PASV);
}
return result;
}
@@ -1756,36 +1783,30 @@ static bool match_pasv_6nums(const char *p,
{
int i;
for(i = 0; i < 6; i++) {
- unsigned long num;
- char *endp;
+ curl_off_t num;
if(i) {
if(*p != ',')
return FALSE;
p++;
}
- if(!ISDIGIT(*p))
- return FALSE;
- num = strtoul(p, &endp, 10);
- if(num > 255)
+ if(curlx_str_number(&p, &num, 0xff))
return FALSE;
array[i] = (unsigned int)num;
- p = endp;
}
return TRUE;
}
static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
int ftpcode)
{
struct connectdata *conn = data->conn;
- struct ftp_conn *ftpc = &conn->proto.ftpc;
CURLcode result;
- struct Curl_dns_entry *addr = NULL;
- enum resolve_t rc;
+ struct Curl_dns_entry *dns = NULL;
unsigned short connectport; /* the local port connect() should use! */
struct pingpong *pp = &ftpc->pp;
char *str =
- Curl_dyn_ptr(&pp->recvbuf) + 4; /* start on the first letter */
+ curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first letter */
/* if we come here again, make sure the former name is cleared */
Curl_safefree(ftpc->newhost);
@@ -1799,23 +1820,17 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
ptr++;
/* |||12345| */
sep = ptr[0];
- /* the ISDIGIT() check here is because strtoul() accepts leading minus
- etc */
if((ptr[1] == sep) && (ptr[2] == sep) && ISDIGIT(ptr[3])) {
- char *endp;
- unsigned long num = strtoul(&ptr[3], &endp, 10);
- if(*endp != sep)
- ptr = NULL;
- else if(num > 0xffff) {
+ const char *p = &ptr[3];
+ curl_off_t num;
+ if(curlx_str_number(&p, &num, 0xffff) || (*p != sep)) {
failf(data, "Illegal port number in EPSV reply");
return CURLE_FTP_WEIRD_PASV_REPLY;
}
- if(ptr) {
- ftpc->newport = (unsigned short)(num & 0xffff);
- ftpc->newhost = strdup(control_address(conn));
- if(!ftpc->newhost)
- return CURLE_OUT_OF_MEMORY;
- }
+ ftpc->newport = (unsigned short)num;
+ ftpc->newhost = strdup(control_address(conn));
+ if(!ftpc->newhost)
+ return CURLE_OUT_OF_MEMORY;
}
else
ptr = NULL;
@@ -1869,7 +1884,7 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
}
else if(ftpc->count1 == 0) {
/* EPSV failed, move on to PASV */
- return ftp_epsv_disable(data, conn);
+ return ftp_epsv_disable(data, ftpc, conn);
}
else {
failf(data, "Bad PASV/EPSV response: %03d", ftpcode);
@@ -1885,16 +1900,12 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
*/
const char * const host_name = conn->bits.socksproxy ?
conn->socks_proxy.host.name : conn->http_proxy.host.name;
- rc = Curl_resolv(data, host_name, conn->primary.remote_port, FALSE, &addr);
- if(rc == CURLRESOLV_PENDING)
- /* BLOCKING, ignores the return code but 'addr' will be NULL in
- case of failure */
- (void)Curl_resolver_wait_resolv(data, &addr);
-
+ (void)Curl_resolv_blocking(data, host_name, conn->primary.remote_port,
+ conn->ip_version, &dns);
/* we connect to the proxy's port */
connectport = (unsigned short)conn->primary.remote_port;
- if(!addr) {
+ if(!dns) {
failf(data, "cannot resolve proxy host %s:%hu", host_name, connectport);
return CURLE_COULDNT_RESOLVE_PROXY;
}
@@ -1907,34 +1918,30 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
/* postponed address resolution in case of tcp fastopen */
if(conn->bits.tcp_fastopen && !conn->bits.reuse && !ftpc->newhost[0]) {
- Curl_safefree(ftpc->newhost);
+ free(ftpc->newhost);
ftpc->newhost = strdup(control_address(conn));
if(!ftpc->newhost)
return CURLE_OUT_OF_MEMORY;
}
- rc = Curl_resolv(data, ftpc->newhost, ftpc->newport, FALSE, &addr);
- if(rc == CURLRESOLV_PENDING)
- /* BLOCKING */
- (void)Curl_resolver_wait_resolv(data, &addr);
-
+ (void)Curl_resolv_blocking(data, ftpc->newhost, ftpc->newport,
+ conn->ip_version, &dns);
connectport = ftpc->newport; /* we connect to the remote port */
- if(!addr) {
+ if(!dns) {
failf(data, "cannot resolve new host %s:%hu",
ftpc->newhost, connectport);
return CURLE_FTP_CANT_GET_HOST;
}
}
- result = Curl_conn_setup(data, conn, SECONDARYSOCKET, addr,
+ result = Curl_conn_setup(data, conn, SECONDARYSOCKET, dns,
conn->bits.ftp_use_data_ssl ?
CURL_CF_SSL_ENABLE : CURL_CF_SSL_DISABLE);
if(result) {
- Curl_resolv_unlink(data, &addr); /* we are done using this address */
if(ftpc->count1 == 0 && ftpcode == 229)
- return ftp_epsv_disable(data, conn);
+ return ftp_epsv_disable(data, ftpc, conn);
return result;
}
@@ -1948,27 +1955,26 @@ static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
if(data->set.verbose)
/* this just dumps information about this second connection */
- ftp_pasv_verbose(data, addr->addr, ftpc->newhost, connectport);
-
- Curl_resolv_unlink(data, &addr); /* we are done using this address */
+ ftp_pasv_verbose(data, dns->addr, ftpc->newhost, connectport);
- Curl_safefree(conn->secondaryhostname);
+ free(conn->secondaryhostname);
conn->secondary_port = ftpc->newport;
conn->secondaryhostname = strdup(ftpc->newhost);
if(!conn->secondaryhostname)
return CURLE_OUT_OF_MEMORY;
conn->bits.do_more = TRUE;
- ftp_state(data, FTP_STOP); /* this phase is completed */
+ ftp_state(data, ftpc, FTP_STOP); /* this phase is completed */
return result;
}
static CURLcode ftp_state_port_resp(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp,
int ftpcode)
{
struct connectdata *conn = data->conn;
- struct ftp_conn *ftpc = &conn->proto.ftpc;
ftpport fcmd = (ftpport)ftpc->count1;
CURLcode result = CURLE_OK;
@@ -1989,12 +1995,12 @@ static CURLcode ftp_state_port_resp(struct Curl_easy *data,
}
else
/* try next */
- result = ftp_state_use_port(data, fcmd);
+ result = ftp_state_use_port(data, ftpc, fcmd);
}
else {
infof(data, "Connect data stream actively");
- ftp_state(data, FTP_STOP); /* end of DO phase */
- result = ftp_dophase_done(data, FALSE);
+ ftp_state(data, ftpc, FTP_STOP); /* end of DO phase */
+ result = ftp_dophase_done(data, ftpc, ftp, FALSE);
}
return result;
@@ -2050,12 +2056,11 @@ static CURLcode client_write_header(struct Curl_easy *data,
}
static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp,
int ftpcode)
{
CURLcode result = CURLE_OK;
- struct FTP *ftp = data->req.p.ftp;
- struct connectdata *conn = data->conn;
- struct ftp_conn *ftpc = &conn->proto.ftpc;
switch(ftpcode) {
case 213:
@@ -2064,7 +2069,7 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
last .sss part is optional and means fractions of a second */
int year, month, day, hour, minute, second;
struct pingpong *pp = &ftpc->pp;
- char *resp = Curl_dyn_ptr(&pp->recvbuf) + 4;
+ char *resp = curlx_dyn_ptr(&pp->recvbuf) + 4;
if(ftp_213_date(resp, &year, &month, &day, &hour, &minute, &second)) {
/* we have a time, reformat it */
char timebuf[24];
@@ -2079,10 +2084,19 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
/* If we asked for a time of the file and we actually got one as well,
we "emulate" an HTTP-style header in our output. */
+#if defined(__GNUC__) && (defined(__DJGPP__) || defined(__AMIGA__))
+#pragma GCC diagnostic push
+/* 'time_t' is unsigned in MSDOS and AmigaOS. Silence:
+ warning: comparison of unsigned expression in '>= 0' is always true */
+#pragma GCC diagnostic ignored "-Wtype-limits"
+#endif
if(data->req.no_body &&
ftpc->file &&
data->set.get_filetime &&
(data->info.filetime >= 0) ) {
+#if defined(__GNUC__) && (defined(__DJGPP__) || defined(__AMIGA__))
+#pragma GCC diagnostic pop
+#endif
char headerbuf[128];
int headerbuflen;
time_t filetime = data->info.filetime;
@@ -2131,7 +2145,7 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
infof(data, "The requested document is not new enough");
ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */
data->info.timecond = TRUE;
- ftp_state(data, FTP_STOP);
+ ftp_state(data, ftpc, FTP_STOP);
return CURLE_OK;
}
break;
@@ -2140,7 +2154,7 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
infof(data, "The requested document is not old enough");
ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */
data->info.timecond = TRUE;
- ftp_state(data, FTP_STOP);
+ ftp_state(data, ftpc, FTP_STOP);
return CURLE_OK;
}
break;
@@ -2152,17 +2166,18 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
}
if(!result)
- result = ftp_state_type(data);
+ result = ftp_state_type(data, ftpc, ftp);
return result;
}
static CURLcode ftp_state_type_resp(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp,
int ftpcode,
ftpstate instate)
{
CURLcode result = CURLE_OK;
- struct connectdata *conn = data->conn;
if(ftpcode/100 != 2) {
/* "sasserftpd" and "(u)r(x)bot ftpd" both responds with 226 after a
@@ -2176,26 +2191,25 @@ static CURLcode ftp_state_type_resp(struct Curl_easy *data,
ftpcode);
if(instate == FTP_TYPE)
- result = ftp_state_size(data, conn);
+ result = ftp_state_size(data, ftpc, ftp);
else if(instate == FTP_LIST_TYPE)
- result = ftp_state_list(data);
+ result = ftp_state_list(data, ftpc, ftp);
else if(instate == FTP_RETR_TYPE)
- result = ftp_state_retr_prequote(data);
+ result = ftp_state_retr_prequote(data, ftpc, ftp);
else if(instate == FTP_STOR_TYPE)
- result = ftp_state_stor_prequote(data);
+ result = ftp_state_stor_prequote(data, ftpc, ftp);
return result;
}
static CURLcode ftp_state_retr(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp,
curl_off_t filesize)
{
CURLcode result = CURLE_OK;
- struct FTP *ftp = data->req.p.ftp;
- struct connectdata *conn = data->conn;
- struct ftp_conn *ftpc = &conn->proto.ftpc;
- CURL_TRC_FTP(data, "[%s] ftp_state_retr()", FTP_DSTATE(data));
+ CURL_TRC_FTP(data, "[%s] ftp_state_retr()", FTP_CSTATE(ftpc));
if(data->set.max_filesize && (filesize > data->set.max_filesize)) {
failf(data, "Maximum file size exceeded");
return CURLE_FILESIZE_EXCEEDED;
@@ -2248,7 +2262,7 @@ static CURLcode ftp_state_retr(struct Curl_easy *data,
/* Set ->transfer so that we will not get any error in ftp_done()
* because we did not transfer the any file */
ftp->transfer = PPTRANSFER_NONE;
- ftp_state(data, FTP_STOP);
+ ftp_state(data, ftpc, FTP_STOP);
return CURLE_OK;
}
@@ -2259,26 +2273,28 @@ static CURLcode ftp_state_retr(struct Curl_easy *data,
result = Curl_pp_sendf(data, &ftpc->pp, "REST %" FMT_OFF_T,
data->state.resume_from);
if(!result)
- ftp_state(data, FTP_RETR_REST);
+ ftp_state(data, ftpc, FTP_RETR_REST);
}
else {
/* no resume */
result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
if(!result)
- ftp_state(data, FTP_RETR);
+ ftp_state(data, ftpc, FTP_RETR);
}
return result;
}
static CURLcode ftp_state_size_resp(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp,
int ftpcode,
ftpstate instate)
{
CURLcode result = CURLE_OK;
curl_off_t filesize = -1;
- char *buf = Curl_dyn_ptr(&data->conn->proto.ftpc.pp.recvbuf);
- size_t len = data->conn->proto.ftpc.pp.nfinal;
+ char *buf = curlx_dyn_ptr(&ftpc->pp.recvbuf);
+ size_t len = ftpc->pp.nfinal;
/* get the size from the ascii string: */
if(ftpcode == 213) {
@@ -2286,7 +2302,7 @@ static CURLcode ftp_state_size_resp(struct Curl_easy *data,
for all the digits at the end of the response and parse only those as a
number. */
char *start = &buf[4];
- char *fdigit = memchr(start, '\r', len);
+ const char *fdigit = memchr(start, '\r', len);
if(fdigit) {
fdigit--;
if(*fdigit == '\n')
@@ -2296,9 +2312,8 @@ static CURLcode ftp_state_size_resp(struct Curl_easy *data,
}
else
fdigit = start;
- /* ignores parsing errors, which will make the size remain unknown */
- (void)curlx_strtoofft(fdigit, NULL, 10, &filesize);
-
+ if(curlx_str_number(&fdigit, &filesize, CURL_OFF_T_MAX))
+ filesize = -1; /* size remain unknown */
}
else if(ftpcode == 550) { /* "No such file or directory" */
/* allow a SIZE failure for (resumed) uploads, when probing what command
@@ -2321,27 +2336,27 @@ static CURLcode ftp_state_size_resp(struct Curl_easy *data,
}
#endif
Curl_pgrsSetDownloadSize(data, filesize);
- result = ftp_state_rest(data, data->conn);
+ result = ftp_state_rest(data, ftpc, ftp);
}
else if(instate == FTP_RETR_SIZE) {
Curl_pgrsSetDownloadSize(data, filesize);
- result = ftp_state_retr(data, filesize);
+ result = ftp_state_retr(data, ftpc, ftp, filesize);
}
else if(instate == FTP_STOR_SIZE) {
data->state.resume_from = filesize;
- result = ftp_state_ul_setup(data, TRUE);
+ result = ftp_state_ul_setup(data, ftpc, ftp, TRUE);
}
return result;
}
static CURLcode ftp_state_rest_resp(struct Curl_easy *data,
- struct connectdata *conn,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp,
int ftpcode,
ftpstate instate)
{
CURLcode result = CURLE_OK;
- struct ftp_conn *ftpc = &conn->proto.ftpc;
switch(instate) {
case FTP_REST:
@@ -2354,7 +2369,7 @@ static CURLcode ftp_state_rest_resp(struct Curl_easy *data,
return result;
}
#endif
- result = ftp_state_prepare_transfer(data);
+ result = ftp_state_prepare_transfer(data, ftpc, ftp);
break;
case FTP_RETR_REST:
@@ -2365,7 +2380,7 @@ static CURLcode ftp_state_rest_resp(struct Curl_easy *data,
else {
result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
if(!result)
- ftp_state(data, FTP_RETR);
+ ftp_state(data, ftpc, FTP_RETR);
}
break;
}
@@ -2374,26 +2389,25 @@ static CURLcode ftp_state_rest_resp(struct Curl_easy *data,
}
static CURLcode ftp_state_stor_resp(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
int ftpcode, ftpstate instate)
{
CURLcode result = CURLE_OK;
- struct connectdata *conn = data->conn;
if(ftpcode >= 400) {
failf(data, "Failed FTP upload: %0d", ftpcode);
- ftp_state(data, FTP_STOP);
+ ftp_state(data, ftpc, FTP_STOP);
/* oops, we never close the sockets! */
return CURLE_UPLOAD_FAILED;
}
- conn->proto.ftpc.state_saved = instate;
+ ftpc->state_saved = instate;
/* PORT means we are now awaiting the server to connect to us. */
if(data->set.ftp_use_port) {
- struct ftp_conn *ftpc = &conn->proto.ftpc;
bool connected;
- ftp_state(data, FTP_STOP); /* no longer in STOR state */
+ ftp_state(data, ftpc, FTP_STOP); /* no longer in STOR state */
result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected);
if(result)
@@ -2402,21 +2416,21 @@ static CURLcode ftp_state_stor_resp(struct Curl_easy *data,
if(!connected) {
infof(data, "Data conn was not available immediately");
ftpc->wait_data_conn = TRUE;
- return ftp_check_ctrl_on_data_wait(data);
+ return ftp_check_ctrl_on_data_wait(data, ftpc);
}
ftpc->wait_data_conn = FALSE;
}
- return InitiateTransfer(data);
+ return ftp_initiate_transfer(data, ftpc);
}
/* for LIST and RETR responses */
static CURLcode ftp_state_get_resp(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp,
int ftpcode,
ftpstate instate)
{
CURLcode result = CURLE_OK;
- struct FTP *ftp = data->req.p.ftp;
- struct connectdata *conn = data->conn;
if((ftpcode == 150) || (ftpcode == 125)) {
@@ -2460,8 +2474,8 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data,
* those cases only confuses us.
*
* Example D above makes this parsing a little tricky */
- char *bytes;
- char *buf = Curl_dyn_ptr(&conn->proto.ftpc.pp.recvbuf);
+ const char *bytes;
+ char *buf = curlx_dyn_ptr(&ftpc->pp.recvbuf);
bytes = strstr(buf, " bytes");
if(bytes) {
long in = (long)(--bytes-buf);
@@ -2482,7 +2496,8 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data,
if(bytes) {
++bytes;
/* get the number! */
- (void)curlx_strtoofft(bytes, NULL, 10, &size);
+ if(curlx_str_number(&bytes, &size, CURL_OFF_T_MAX))
+ size = 1;
}
}
}
@@ -2500,11 +2515,10 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data,
infof(data, "Getting file with size: %" FMT_OFF_T, size);
/* FTP download: */
- conn->proto.ftpc.state_saved = instate;
- conn->proto.ftpc.retr_size_saved = size;
+ ftpc->state_saved = instate;
+ ftpc->retr_size_saved = size;
if(data->set.ftp_use_port) {
- struct ftp_conn *ftpc = &conn->proto.ftpc;
bool connected;
result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected);
@@ -2513,19 +2527,19 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data,
if(!connected) {
infof(data, "Data conn was not available immediately");
- ftp_state(data, FTP_STOP);
+ ftp_state(data, ftpc, FTP_STOP);
ftpc->wait_data_conn = TRUE;
- return ftp_check_ctrl_on_data_wait(data);
+ return ftp_check_ctrl_on_data_wait(data, ftpc);
}
ftpc->wait_data_conn = FALSE;
}
- return InitiateTransfer(data);
+ return ftp_initiate_transfer(data, ftpc);
}
else {
if((instate == FTP_LIST) && (ftpcode == 450)) {
/* simply no matching files in the dir listing */
ftp->transfer = PPTRANSFER_NONE; /* do not download anything */
- ftp_state(data, FTP_STOP); /* this phase is over */
+ ftp_state(data, ftpc, FTP_STOP); /* this phase is over */
}
else {
failf(data, "RETR response: %03d", ftpcode);
@@ -2539,12 +2553,12 @@ static CURLcode ftp_state_get_resp(struct Curl_easy *data,
}
/* after USER, PASS and ACCT */
-static CURLcode ftp_state_loggedin(struct Curl_easy *data)
+static CURLcode ftp_state_loggedin(struct Curl_easy *data,
+ struct ftp_conn *ftpc)
{
CURLcode result = CURLE_OK;
- struct connectdata *conn = data->conn;
- if(conn->bits.ftp_use_control_ssl) {
+ if(data->conn->bits.ftp_use_control_ssl) {
/* PBSZ = PROTECTION BUFFER SIZE.
The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
@@ -2559,44 +2573,43 @@ static CURLcode ftp_state_loggedin(struct Curl_easy *data)
parameter of '0' to indicate that no buffering is taking place
and the data connection should not be encapsulated.
*/
- result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "PBSZ %d", 0);
+ result = Curl_pp_sendf(data, &ftpc->pp, "PBSZ %d", 0);
if(!result)
- ftp_state(data, FTP_PBSZ);
+ ftp_state(data, ftpc, FTP_PBSZ);
}
else {
- result = ftp_state_pwd(data, conn);
+ result = ftp_state_pwd(data, ftpc);
}
return result;
}
/* for USER and PASS responses */
static CURLcode ftp_state_user_resp(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
int ftpcode)
{
CURLcode result = CURLE_OK;
- struct connectdata *conn = data->conn;
- struct ftp_conn *ftpc = &conn->proto.ftpc;
/* some need password anyway, and others just return 2xx ignored */
if((ftpcode == 331) && (ftpc->state == FTP_USER)) {
/* 331 Password required for ...
(the server requires to send the user's password too) */
result = Curl_pp_sendf(data, &ftpc->pp, "PASS %s",
- conn->passwd ? conn->passwd : "");
+ data->conn->passwd ? data->conn->passwd : "");
if(!result)
- ftp_state(data, FTP_PASS);
+ ftp_state(data, ftpc, FTP_PASS);
}
else if(ftpcode/100 == 2) {
/* 230 User ... logged in.
(the user logged in with or without password) */
- result = ftp_state_loggedin(data);
+ result = ftp_state_loggedin(data, ftpc);
}
else if(ftpcode == 332) {
if(data->set.str[STRING_FTP_ACCOUNT]) {
result = Curl_pp_sendf(data, &ftpc->pp, "ACCT %s",
data->set.str[STRING_FTP_ACCOUNT]);
if(!result)
- ftp_state(data, FTP_ACCT);
+ ftp_state(data, ftpc, FTP_ACCT);
}
else {
failf(data, "ACCT requested but none available");
@@ -2617,7 +2630,7 @@ static CURLcode ftp_state_user_resp(struct Curl_easy *data,
data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
if(!result) {
ftpc->ftp_trying_alternative = TRUE;
- ftp_state(data, FTP_USER);
+ ftp_state(data, ftpc, FTP_USER);
}
}
else {
@@ -2630,6 +2643,7 @@ static CURLcode ftp_state_user_resp(struct Curl_easy *data,
/* for ACCT response */
static CURLcode ftp_state_acct_resp(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
int ftpcode)
{
CURLcode result = CURLE_OK;
@@ -2638,26 +2652,30 @@ static CURLcode ftp_state_acct_resp(struct Curl_easy *data,
result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */
}
else
- result = ftp_state_loggedin(data);
+ result = ftp_state_loggedin(data, ftpc);
return result;
}
-static CURLcode ftp_statemachine(struct Curl_easy *data,
- struct connectdata *conn)
+static CURLcode ftp_pp_statemachine(struct Curl_easy *data,
+ struct connectdata *conn)
{
CURLcode result;
int ftpcode;
- struct ftp_conn *ftpc = &conn->proto.ftpc;
- struct pingpong *pp = &ftpc->pp;
+ struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN);
+ struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY);
+ struct pingpong *pp;
static const char * const ftpauth[] = { "SSL", "TLS" };
size_t nread = 0;
+ if(!ftpc || !ftp)
+ return CURLE_FAILED_INIT;
+ pp = &ftpc->pp;
if(pp->sendleft)
return Curl_pp_flushsend(data, pp);
- result = ftp_readresp(data, FIRSTSOCKET, pp, &ftpcode, &nread);
+ result = ftp_readresp(data, ftpc, FIRSTSOCKET, pp, &ftpcode, &nread);
if(result)
return result;
@@ -2669,7 +2687,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
/* 230 User logged in - already! Take as 220 if TLS required. */
if(data->set.use_ssl <= CURLUSESSL_TRY ||
conn->bits.ftp_use_control_ssl)
- return ftp_state_user_resp(data, ftpcode);
+ return ftp_state_user_resp(data, ftpc, ftpcode);
}
else if(ftpcode != 220) {
failf(data, "Got a %03d ftp-server response when 220 was expected",
@@ -2697,8 +2715,8 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
#endif
if(data->set.use_ssl && !conn->bits.ftp_use_control_ssl) {
- /* We do not have a SSL/TLS control connection yet, but FTPS is
- requested. Try a FTPS connection now */
+ /* We do not have an SSL/TLS control connection yet, but FTPS is
+ requested. Try an FTPS connection now */
ftpc->count3 = 0;
switch(data->set.ftpsslauth) {
@@ -2719,10 +2737,10 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s",
ftpauth[ftpc->count1]);
if(!result)
- ftp_state(data, FTP_AUTH);
+ ftp_state(data, ftpc, FTP_AUTH);
}
else
- result = ftp_state_user(data, conn);
+ result = ftp_state_user(data, ftpc, conn);
break;
case FTP_AUTH:
@@ -2752,7 +2770,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
if(!result) {
conn->bits.ftp_use_data_ssl = FALSE; /* clear-text data */
conn->bits.ftp_use_control_ssl = TRUE; /* SSL on control */
- result = ftp_state_user(data, conn);
+ result = ftp_state_user(data, ftpc, conn);
}
}
else if(ftpc->count3 < 1) {
@@ -2768,17 +2786,17 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
result = CURLE_USE_SSL_FAILED;
else
/* ignore the failure and continue */
- result = ftp_state_user(data, conn);
+ result = ftp_state_user(data, ftpc, conn);
}
break;
case FTP_USER:
case FTP_PASS:
- result = ftp_state_user_resp(data, ftpcode);
+ result = ftp_state_user_resp(data, ftpc, ftpcode);
break;
case FTP_ACCT:
- result = ftp_state_acct_resp(data, ftpcode);
+ result = ftp_state_acct_resp(data, ftpc, ftpcode);
break;
case FTP_PBSZ:
@@ -2786,7 +2804,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
Curl_pp_sendf(data, &ftpc->pp, "PROT %c",
data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
if(!result)
- ftp_state(data, FTP_PROT);
+ ftp_state(data, ftpc, FTP_PROT);
break;
case FTP_PROT:
@@ -2805,10 +2823,10 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
*/
result = Curl_pp_sendf(data, &ftpc->pp, "%s", "CCC");
if(!result)
- ftp_state(data, FTP_CCC);
+ ftp_state(data, ftpc, FTP_CCC);
}
else
- result = ftp_state_pwd(data, conn);
+ result = ftp_state_pwd(data, ftpc);
break;
case FTP_CCC:
@@ -2827,16 +2845,16 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
}
if(!result)
/* Then continue as normal */
- result = ftp_state_pwd(data, conn);
+ result = ftp_state_pwd(data, ftpc);
break;
case FTP_PWD:
if(ftpcode == 257) {
- char *ptr = Curl_dyn_ptr(&pp->recvbuf) + 4; /* start on the first
- letter */
+ char *ptr = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first
+ letter */
bool entry_extracted = FALSE;
struct dynbuf out;
- Curl_dyn_init(&out, 1000);
+ curlx_dyn_init(&out, 1000);
/* Reply format is like
257<space>[rubbish]"<directory-name>"<space><commentary> and the
@@ -2857,18 +2875,18 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
if('\"' == *ptr) {
if('\"' == ptr[1]) {
/* "quote-doubling" */
- result = Curl_dyn_addn(&out, &ptr[1], 1);
+ result = curlx_dyn_addn(&out, &ptr[1], 1);
ptr++;
}
else {
/* end of path */
- if(Curl_dyn_len(&out))
+ if(curlx_dyn_len(&out))
entry_extracted = TRUE;
break; /* get out of this loop */
}
}
else
- result = Curl_dyn_addn(&out, ptr, 1);
+ result = curlx_dyn_addn(&out, ptr, 1);
if(result)
return result;
}
@@ -2885,7 +2903,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
The method used here is to check the server OS: we do it only
if the path name looks strange to minimize overhead on other
systems. */
- char *dir = Curl_dyn_ptr(&out);
+ char *dir = curlx_dyn_ptr(&out);
if(!ftpc->server_os && dir[0] != '/') {
result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SYST");
@@ -2893,34 +2911,40 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
free(dir);
return result;
}
- Curl_safefree(ftpc->entrypath);
+ free(ftpc->entrypath);
ftpc->entrypath = dir; /* remember this */
infof(data, "Entry path is '%s'", ftpc->entrypath);
/* also save it where getinfo can access it: */
- data->state.most_recent_ftp_entrypath = ftpc->entrypath;
- ftp_state(data, FTP_SYST);
+ free(data->state.most_recent_ftp_entrypath);
+ data->state.most_recent_ftp_entrypath = strdup(ftpc->entrypath);
+ if(!data->state.most_recent_ftp_entrypath)
+ return CURLE_OUT_OF_MEMORY;
+ ftp_state(data, ftpc, FTP_SYST);
break;
}
- Curl_safefree(ftpc->entrypath);
+ free(ftpc->entrypath);
ftpc->entrypath = dir; /* remember this */
infof(data, "Entry path is '%s'", ftpc->entrypath);
/* also save it where getinfo can access it: */
- data->state.most_recent_ftp_entrypath = ftpc->entrypath;
+ free(data->state.most_recent_ftp_entrypath);
+ data->state.most_recent_ftp_entrypath = strdup(ftpc->entrypath);
+ if(!data->state.most_recent_ftp_entrypath)
+ return CURLE_OUT_OF_MEMORY;
}
else {
/* could not get the path */
- Curl_dyn_free(&out);
+ curlx_dyn_free(&out);
infof(data, "Failed to figure out path");
}
}
- ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */
- CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_DSTATE(data));
+ ftp_state(data, ftpc, FTP_STOP); /* we are done with CONNECT phase! */
+ CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_CSTATE(ftpc));
break;
case FTP_SYST:
if(ftpcode == 215) {
- char *ptr = Curl_dyn_ptr(&pp->recvbuf) + 4; /* start on the first
+ char *ptr = curlx_dyn_ptr(&pp->recvbuf) + 4; /* start on the first
letter */
char *os;
char *start;
@@ -2945,33 +2969,33 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
return result;
}
/* remember target server OS */
- Curl_safefree(ftpc->server_os);
+ free(ftpc->server_os);
ftpc->server_os = os;
- ftp_state(data, FTP_NAMEFMT);
+ ftp_state(data, ftpc, FTP_NAMEFMT);
break;
}
/* Nothing special for the target server. */
/* remember target server OS */
- Curl_safefree(ftpc->server_os);
+ free(ftpc->server_os);
ftpc->server_os = os;
}
else {
/* Cannot identify server OS. Continue anyway and cross fingers. */
}
- ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */
- CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_DSTATE(data));
+ ftp_state(data, ftpc, FTP_STOP); /* we are done with CONNECT phase! */
+ CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_CSTATE(ftpc));
break;
case FTP_NAMEFMT:
if(ftpcode == 250) {
/* Name format change successful: reload initial path. */
- ftp_state_pwd(data, conn);
+ ftp_state_pwd(data, ftpc);
break;
}
- ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */
- CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_DSTATE(data));
+ ftp_state(data, ftpc, FTP_STOP); /* we are done with CONNECT phase! */
+ CURL_TRC_FTP(data, "[%s] protocol connect phase DONE", FTP_CSTATE(ftpc));
break;
case FTP_QUOTE:
@@ -2984,7 +3008,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
result = CURLE_QUOTE_ERROR;
}
else
- result = ftp_state_quote(data, FALSE, ftpc->state);
+ result = ftp_state_quote(data, ftpc, ftp, FALSE, ftpc->state);
break;
case FTP_CWD:
@@ -3003,7 +3027,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
result = Curl_pp_sendf(data, &ftpc->pp, "MKD %s",
ftpc->dirs[ftpc->cwdcount - 1]);
if(!result)
- ftp_state(data, FTP_MKD);
+ ftp_state(data, ftpc, FTP_MKD);
}
else {
/* return failure */
@@ -3021,7 +3045,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s",
ftpc->dirs[ftpc->cwdcount - 1]);
else
- result = ftp_state_mdtm(data);
+ result = ftp_state_mdtm(data, ftpc, ftp);
}
break;
@@ -3032,7 +3056,7 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
result = CURLE_REMOTE_ACCESS_DENIED;
}
else {
- ftp_state(data, FTP_CWD);
+ ftp_state(data, ftpc, FTP_CWD);
/* send CWD */
result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s",
ftpc->dirs[ftpc->cwdcount - 1]);
@@ -3040,25 +3064,25 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
break;
case FTP_MDTM:
- result = ftp_state_mdtm_resp(data, ftpcode);
+ result = ftp_state_mdtm_resp(data, ftpc, ftp, ftpcode);
break;
case FTP_TYPE:
case FTP_LIST_TYPE:
case FTP_RETR_TYPE:
case FTP_STOR_TYPE:
- result = ftp_state_type_resp(data, ftpcode, ftpc->state);
+ result = ftp_state_type_resp(data, ftpc, ftp, ftpcode, ftpc->state);
break;
case FTP_SIZE:
case FTP_RETR_SIZE:
case FTP_STOR_SIZE:
- result = ftp_state_size_resp(data, ftpcode, ftpc->state);
+ result = ftp_state_size_resp(data, ftpc, ftp, ftpcode, ftpc->state);
break;
case FTP_REST:
case FTP_RETR_REST:
- result = ftp_state_rest_resp(data, conn, ftpcode, ftpc->state);
+ result = ftp_state_rest_resp(data, ftpc, ftp, ftpcode, ftpc->state);
break;
case FTP_PRET:
@@ -3067,30 +3091,30 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
failf(data, "PRET command not accepted: %03d", ftpcode);
return CURLE_FTP_PRET_FAILED;
}
- result = ftp_state_use_pasv(data, conn);
+ result = ftp_state_use_pasv(data, ftpc, conn);
break;
case FTP_PASV:
- result = ftp_state_pasv_resp(data, ftpcode);
+ result = ftp_state_pasv_resp(data, ftpc, ftpcode);
break;
case FTP_PORT:
- result = ftp_state_port_resp(data, ftpcode);
+ result = ftp_state_port_resp(data, ftpc, ftp, ftpcode);
break;
case FTP_LIST:
case FTP_RETR:
- result = ftp_state_get_resp(data, ftpcode, ftpc->state);
+ result = ftp_state_get_resp(data, ftpc, ftp, ftpcode, ftpc->state);
break;
case FTP_STOR:
- result = ftp_state_stor_resp(data, ftpcode, ftpc->state);
+ result = ftp_state_stor_resp(data, ftpc, ftpcode, ftpc->state);
break;
case FTP_QUIT:
default:
/* internal error */
- ftp_state(data, FTP_STOP);
+ ftp_state(data, ftpc, FTP_STOP);
break;
}
} /* if(ftpcode) */
@@ -3100,11 +3124,10 @@ static CURLcode ftp_statemachine(struct Curl_easy *data,
/* called repeatedly until done from multi.c */
-static CURLcode ftp_multi_statemach(struct Curl_easy *data,
- bool *done)
+static CURLcode ftp_statemach(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ bool *done)
{
- struct connectdata *conn = data->conn;
- struct ftp_conn *ftpc = &conn->proto.ftpc;
CURLcode result = Curl_pp_statemach(data, &ftpc->pp, FALSE, FALSE);
/* Check for the state outside of the Curl_socket_check() return code checks
@@ -3115,14 +3138,23 @@ static CURLcode ftp_multi_statemach(struct Curl_easy *data,
return result;
}
+/* called repeatedly until done from multi.c */
+static CURLcode ftp_multi_statemach(struct Curl_easy *data,
+ bool *done)
+{
+ struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
+ return ftpc ? ftp_statemach(data, ftpc, done) : CURLE_FAILED_INIT;
+}
+
static CURLcode ftp_block_statemach(struct Curl_easy *data,
- struct connectdata *conn)
+ struct ftp_conn *ftpc)
{
- struct ftp_conn *ftpc = &conn->proto.ftpc;
struct pingpong *pp = &ftpc->pp;
CURLcode result = CURLE_OK;
while(ftpc->state != FTP_STOP) {
+ if(ftpc->shutdown)
+ CURL_TRC_FTP(data, "in shutdown, waiting for server response");
result = Curl_pp_statemach(data, pp, TRUE, TRUE /* disconnecting */);
if(result)
break;
@@ -3144,17 +3176,19 @@ static CURLcode ftp_connect(struct Curl_easy *data,
{
CURLcode result;
struct connectdata *conn = data->conn;
- struct ftp_conn *ftpc = &conn->proto.ftpc;
- struct pingpong *pp = &ftpc->pp;
+ struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
+ struct pingpong *pp;
*done = FALSE; /* default to not done yet */
-
+ if(!ftpc)
+ return CURLE_FAILED_INIT;
+ pp = &ftpc->pp;
/* We always support persistent connections on ftp */
connkeep(conn, "FTP default");
- PINGPONG_SETUP(pp, ftp_statemachine, ftp_endofresp);
+ PINGPONG_SETUP(pp, ftp_pp_statemachine, ftp_endofresp);
- if(conn->handler->flags & PROTOPT_SSL) {
+ if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
/* BLOCKING */
result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done);
if(result)
@@ -3166,9 +3200,9 @@ static CURLcode ftp_connect(struct Curl_easy *data,
/* When we connect, we start in the state where we await the 220
response */
- ftp_state(data, FTP_WAIT220);
+ ftp_state(data, ftpc, FTP_WAIT220);
- result = ftp_multi_statemach(data, done);
+ result = ftp_statemach(data, ftpc, done);
return result;
}
@@ -3186,18 +3220,19 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
bool premature)
{
struct connectdata *conn = data->conn;
- struct FTP *ftp = data->req.p.ftp;
- struct ftp_conn *ftpc = &conn->proto.ftpc;
- struct pingpong *pp = &ftpc->pp;
+ struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY);
+ struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
+ struct pingpong *pp;
ssize_t nread;
int ftpcode;
CURLcode result = CURLE_OK;
char *rawPath = NULL;
size_t pathLen = 0;
- if(!ftp)
+ if(!ftp || !ftpc)
return CURLE_OK;
+ pp = &ftpc->pp;
switch(status) {
case CURLE_BAD_DOWNLOAD_RESUME:
case CURLE_FTP_WEIRD_PASV_REPLY:
@@ -3282,11 +3317,11 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
/* shut down the socket to inform the server we are done */
-#ifdef _WIN32_WCE
+#ifdef UNDER_CE
shutdown(conn->sock[SECONDARYSOCKET], 2); /* SD_BOTH */
#endif
- if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
+ if(Curl_conn_is_setup(conn, SECONDARYSOCKET)) {
if(!result && ftpc->dont_check && data->req.maxdownload > 0) {
/* partial download completed */
result = Curl_pp_sendf(data, pp, "%s", "ABOR");
@@ -3298,7 +3333,7 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
}
}
- close_secondarysocket(data);
+ close_secondarysocket(data, ftpc);
}
if(!result && (ftp->transfer == PPTRANSFER_BODY) && ftpc->ctl_valid &&
@@ -3312,7 +3347,7 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
timediff_t old_time = pp->response_time;
pp->response_time = 60*1000; /* give it only a minute for now */
- pp->response = Curl_now(); /* timeout relative now */
+ pp->response = curlx_now(); /* timeout relative now */
result = Curl_GetFTPResponse(data, &nread, &ftpcode);
@@ -3324,10 +3359,8 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
connclose(conn, "Timeout or similar in FTP DONE operation"); /* close */
}
- if(result) {
- Curl_safefree(ftp->pathalloc);
+ if(result)
return result;
- }
if(ftpc->dont_check && data->req.maxdownload > 0) {
/* we have just sent ABOR and there is no reliable way to check if it was
@@ -3360,10 +3393,13 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
use checking further */
;
else if(data->state.upload) {
- if((-1 != data->state.infilesize) &&
- (data->state.infilesize != data->req.writebytecount) &&
- !data->set.crlf &&
- (ftp->transfer == PPTRANSFER_BODY)) {
+ if((ftp->transfer == PPTRANSFER_BODY) &&
+ (data->state.infilesize != -1) && /* upload with known size */
+ ((!data->set.crlf && !data->state.prefer_ascii && /* no conversion */
+ (data->state.infilesize != data->req.writebytecount)) ||
+ ((data->set.crlf || data->state.prefer_ascii) && /* maybe crlf conv */
+ (data->state.infilesize > data->req.writebytecount))
+ )) {
failf(data, "Uploaded unaligned file size (%" FMT_OFF_T
" out of %" FMT_OFF_T " bytes)",
data->req.writebytecount, data->state.infilesize);
@@ -3392,9 +3428,8 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
/* Send any post-transfer QUOTE strings? */
if(!status && !result && !premature && data->set.postquote)
- result = ftp_sendquote(data, conn, data->set.postquote);
- CURL_TRC_FTP(data, "[%s] done, result=%d", FTP_DSTATE(data), result);
- Curl_safefree(ftp->pathalloc);
+ result = ftp_sendquote(data, ftpc, data->set.postquote);
+ CURL_TRC_FTP(data, "[%s] done, result=%d", FTP_CSTATE(ftpc), result);
return result;
}
@@ -3410,10 +3445,10 @@ static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
static
CURLcode ftp_sendquote(struct Curl_easy *data,
- struct connectdata *conn, struct curl_slist *quote)
+ struct ftp_conn *ftpc,
+ struct curl_slist *quote)
{
struct curl_slist *item;
- struct ftp_conn *ftpc = &conn->proto.ftpc;
struct pingpong *pp = &ftpc->pp;
item = quote;
@@ -3437,7 +3472,7 @@ CURLcode ftp_sendquote(struct Curl_easy *data,
result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd);
if(!result) {
- pp->response = Curl_now(); /* timeout relative now */
+ pp->response = curlx_now(); /* timeout relative now */
result = Curl_GetFTPResponse(data, &nread, &ftpcode);
}
if(result)
@@ -3461,10 +3496,10 @@ CURLcode ftp_sendquote(struct Curl_easy *data,
*
* Returns TRUE if we in the current situation should send TYPE
*/
-static int ftp_need_type(struct connectdata *conn,
+static int ftp_need_type(struct ftp_conn *ftpc,
bool ascii_wanted)
{
- return conn->proto.ftpc.transfertype != (ascii_wanted ? 'A' : 'I');
+ return ftpc->transfertype != (ascii_wanted ? 'A' : 'I');
}
/***********************************************************************
@@ -3476,21 +3511,21 @@ static int ftp_need_type(struct connectdata *conn,
* If the transfer type is not sent, simulate on OK response in newstate
*/
static CURLcode ftp_nb_type(struct Curl_easy *data,
- struct connectdata *conn,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp,
bool ascii, ftpstate newstate)
{
- struct ftp_conn *ftpc = &conn->proto.ftpc;
CURLcode result;
char want = (char)(ascii ? 'A' : 'I');
if(ftpc->transfertype == want) {
- ftp_state(data, newstate);
- return ftp_state_type_resp(data, 200, newstate);
+ ftp_state(data, ftpc, newstate);
+ return ftp_state_type_resp(data, ftpc, ftp, 200, newstate);
}
result = Curl_pp_sendf(data, &ftpc->pp, "TYPE %c", want);
if(!result) {
- ftp_state(data, newstate);
+ ftp_state(data, ftpc, newstate);
/* keep track of our current transfer type */
ftpc->transfertype = want;
@@ -3534,16 +3569,17 @@ ftp_pasv_verbose(struct Curl_easy *data,
static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
{
struct connectdata *conn = data->conn;
- struct ftp_conn *ftpc = &conn->proto.ftpc;
+ struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
+ struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY);
CURLcode result = CURLE_OK;
bool connected = FALSE;
bool complete = FALSE;
-
/* the ftp struct is inited in ftp_connect(). If we are connecting to an HTTP
* proxy then the state will not be valid until after that connection is
* complete */
- struct FTP *ftp = NULL;
+ if(!ftpc || !ftp)
+ return CURLE_FAILED_INIT;
/* if the second connection has been set up, try to connect it fully
* to the remote host. This may not complete at this time, for several
* reasons:
@@ -3560,20 +3596,17 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
if(result && !is_eptr && (ftpc->count1 == 0)) {
*completep = -1; /* go back to DOING please */
/* this is a EPSV connect failing, try PASV instead */
- return ftp_epsv_disable(data, conn);
+ return ftp_epsv_disable(data, ftpc, conn);
}
*completep = (int)complete;
return result;
}
}
- /* Curl_proxy_connect might have moved the protocol state */
- ftp = data->req.p.ftp;
-
if(ftpc->state) {
/* already in a state so skip the initial commands.
They are only done to kickstart the do_more state */
- result = ftp_multi_statemach(data, &complete);
+ result = ftp_statemach(data, ftpc, &complete);
*completep = (int)complete;
@@ -3595,14 +3628,14 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
if(ftpc->wait_data_conn) {
bool serv_conned;
- result = Curl_conn_connect(data, SECONDARYSOCKET, TRUE, &serv_conned);
+ result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &serv_conned);
if(result)
return result; /* Failed to accept data connection */
if(serv_conned) {
/* It looks data connection is established */
ftpc->wait_data_conn = FALSE;
- result = InitiateTransfer(data);
+ result = ftp_initiate_transfer(data, ftpc);
if(result)
return result;
@@ -3611,19 +3644,23 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
connected back to us */
}
else {
- result = ftp_check_ctrl_on_data_wait(data);
+ result = ftp_check_ctrl_on_data_wait(data, ftpc);
if(result)
return result;
}
}
else if(data->state.upload) {
- result = ftp_nb_type(data, conn, data->state.prefer_ascii,
+ result = ftp_nb_type(data, ftpc, ftp, data->state.prefer_ascii,
FTP_STOR_TYPE);
if(result)
return result;
- result = ftp_multi_statemach(data, &complete);
- *completep = (int)complete;
+ result = ftp_statemach(data, ftpc, &complete);
+ /* ftp_nb_type() might have skipped sending `TYPE A|I` when not
+ * deemed necessary and directly sent `STORE name`. If this was
+ * then complete, but we are still waiting on the data connection,
+ * the transfer has not been initiated yet. */
+ *completep = (int)(ftpc->wait_data_conn ? 0 : complete);
}
else {
/* download */
@@ -3645,20 +3682,20 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
/* But only if a body transfer was requested. */
if(ftp->transfer == PPTRANSFER_BODY) {
- result = ftp_nb_type(data, conn, TRUE, FTP_LIST_TYPE);
+ result = ftp_nb_type(data, ftpc, ftp, TRUE, FTP_LIST_TYPE);
if(result)
return result;
}
/* otherwise just fall through */
}
else {
- result = ftp_nb_type(data, conn, data->state.prefer_ascii,
+ result = ftp_nb_type(data, ftpc, ftp, data->state.prefer_ascii,
FTP_RETR_TYPE);
if(result)
return result;
}
- result = ftp_multi_statemach(data, &complete);
+ result = ftp_statemach(data, ftpc, &complete);
*completep = (int)complete;
}
return result;
@@ -3670,7 +3707,7 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
if(!ftpc->wait_data_conn) {
/* no waiting for the data connection so this is now complete */
*completep = 1;
- CURL_TRC_FTP(data, "[%s] DO-MORE phase ends with %d", FTP_DSTATE(data),
+ CURL_TRC_FTP(data, "[%s] DO-MORE phase ends with %d", FTP_CSTATE(ftpc),
(int)result);
}
@@ -3689,41 +3726,42 @@ static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
static
CURLcode ftp_perform(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp,
bool *connected, /* connect status after PASV / PORT */
bool *dophase_done)
{
/* this is FTP and no proxy */
CURLcode result = CURLE_OK;
- CURL_TRC_FTP(data, "[%s] DO phase starts", FTP_DSTATE(data));
+ CURL_TRC_FTP(data, "[%s] DO phase starts", FTP_CSTATE(ftpc));
if(data->req.no_body) {
/* requested no body means no transfer... */
- struct FTP *ftp = data->req.p.ftp;
ftp->transfer = PPTRANSFER_INFO;
}
*dophase_done = FALSE; /* not done yet */
/* start the first command in the DO phase */
- result = ftp_state_quote(data, TRUE, FTP_QUOTE);
+ result = ftp_state_quote(data, ftpc, ftp, TRUE, FTP_QUOTE);
if(result)
return result;
/* run the state-machine */
- result = ftp_multi_statemach(data, dophase_done);
+ result = ftp_statemach(data, ftpc, dophase_done);
*connected = Curl_conn_is_connected(data->conn, SECONDARYSOCKET);
if(*connected)
infof(data, "[FTP] [%s] perform, DATA connection established",
- FTP_DSTATE(data));
+ FTP_CSTATE(ftpc));
else
CURL_TRC_FTP(data, "[%s] perform, awaiting DATA connect",
- FTP_DSTATE(data));
+ FTP_CSTATE(ftpc));
if(*dophase_done)
- CURL_TRC_FTP(data, "[%s] DO phase is complete1", FTP_DSTATE(data));
+ CURL_TRC_FTP(data, "[%s] DO phase is complete1", FTP_CSTATE(ftpc));
return result;
}
@@ -3736,10 +3774,11 @@ static void wc_data_dtor(void *ptr)
free(ftpwc);
}
-static CURLcode init_wc_data(struct Curl_easy *data)
+static CURLcode init_wc_data(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp)
{
char *last_slash;
- struct FTP *ftp = data->req.p.ftp;
char *path = ftp->path;
struct WildcardData *wildcard = data->wildcard;
CURLcode result = CURLE_OK;
@@ -3750,7 +3789,7 @@ static CURLcode init_wc_data(struct Curl_easy *data)
last_slash++;
if(last_slash[0] == '\0') {
wildcard->state = CURLWC_CLEAN;
- return ftp_parse_url_path(data);
+ return ftp_parse_url_path(data, ftpc, ftp);
}
wildcard->pattern = strdup(last_slash);
if(!wildcard->pattern)
@@ -3766,7 +3805,7 @@ static CURLcode init_wc_data(struct Curl_easy *data)
}
else { /* only list */
wildcard->state = CURLWC_CLEAN;
- return ftp_parse_url_path(data);
+ return ftp_parse_url_path(data, ftpc, ftp);
}
}
@@ -3795,7 +3834,7 @@ static CURLcode init_wc_data(struct Curl_easy *data)
data->set.ftp_filemethod = FTPFILE_MULTICWD;
/* try to parse ftp URL */
- result = ftp_parse_url_path(data);
+ result = ftp_parse_url_path(data, ftpc, ftp);
if(result) {
goto fail;
}
@@ -3829,16 +3868,17 @@ fail:
return result;
}
-static CURLcode wc_statemach(struct Curl_easy *data)
+static CURLcode wc_statemach(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp)
{
struct WildcardData * const wildcard = data->wildcard;
- struct connectdata *conn = data->conn;
CURLcode result = CURLE_OK;
for(;;) {
switch(wildcard->state) {
case CURLWC_INIT:
- result = init_wc_data(data);
+ result = init_wc_data(data, ftpc, ftp);
if(wildcard->state == CURLWC_CLEAN)
/* only listing! */
return result;
@@ -3870,10 +3910,8 @@ static CURLcode wc_statemach(struct Curl_easy *data)
case CURLWC_DOWNLOADING: {
/* filelist has at least one file, lets get first one */
- struct ftp_conn *ftpc = &conn->proto.ftpc;
struct Curl_llist_node *head = Curl_llist_head(&wildcard->filelist);
struct curl_fileinfo *finfo = Curl_node_elem(head);
- struct FTP *ftp = data->req.p.ftp;
char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename);
if(!tmp_path)
@@ -3910,7 +3948,7 @@ static CURLcode wc_statemach(struct Curl_easy *data)
if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE)
ftpc->known_filesize = finfo->size;
- result = ftp_parse_url_path(data);
+ result = ftp_parse_url_path(data, ftpc, ftp);
if(result)
return result;
@@ -3974,10 +4012,12 @@ static CURLcode wc_statemach(struct Curl_easy *data)
static CURLcode ftp_do(struct Curl_easy *data, bool *done)
{
CURLcode result = CURLE_OK;
- struct connectdata *conn = data->conn;
- struct ftp_conn *ftpc = &conn->proto.ftpc;
+ struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
+ struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY);
*done = FALSE; /* default to false */
+ if(!ftpc || !ftp)
+ return CURLE_FAILED_INIT;
ftpc->wait_data_conn = FALSE; /* default to no such wait */
#ifdef CURL_PREFER_LF_LINEENDS
@@ -3999,7 +4039,7 @@ static CURLcode ftp_do(struct Curl_easy *data, bool *done)
#endif /* CURL_PREFER_LF_LINEENDS */
if(data->state.wildcardmatch) {
- result = wc_statemach(data);
+ result = wc_statemach(data, ftpc, ftp);
if(data->wildcard->state == CURLWC_SKIP ||
data->wildcard->state == CURLWC_DONE) {
/* do not call ftp_regular_transfer */
@@ -4009,12 +4049,12 @@ static CURLcode ftp_do(struct Curl_easy *data, bool *done)
return result;
}
else { /* no wildcard FSM needed */
- result = ftp_parse_url_path(data);
+ result = ftp_parse_url_path(data, ftpc, ftp);
if(result)
return result;
}
- result = ftp_regular_transfer(data, done);
+ result = ftp_regular_transfer(data, ftpc, ftp, done);
return result;
}
@@ -4029,24 +4069,26 @@ static CURLcode ftp_do(struct Curl_easy *data, bool *done)
* connection.
*
*/
-static CURLcode ftp_quit(struct Curl_easy *data, struct connectdata *conn)
+static CURLcode ftp_quit(struct Curl_easy *data,
+ struct ftp_conn *ftpc)
{
CURLcode result = CURLE_OK;
- if(conn->proto.ftpc.ctl_valid) {
- result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "QUIT");
+ if(ftpc->ctl_valid) {
+ CURL_TRC_FTP(data, "sending QUIT to close session");
+ result = Curl_pp_sendf(data, &ftpc->pp, "%s", "QUIT");
if(result) {
failf(data, "Failure sending QUIT command: %s",
curl_easy_strerror(result));
- conn->proto.ftpc.ctl_valid = FALSE; /* mark control connection as bad */
- connclose(conn, "QUIT command failed"); /* mark for connection closure */
- ftp_state(data, FTP_STOP);
+ ftpc->ctl_valid = FALSE; /* mark control connection as bad */
+ connclose(data->conn, "QUIT command failed"); /* mark for closure */
+ ftp_state(data, ftpc, FTP_STOP);
return result;
}
- ftp_state(data, FTP_QUIT);
+ ftp_state(data, ftpc, FTP_QUIT);
- result = ftp_block_statemach(data, conn);
+ result = ftp_block_statemach(data, ftpc);
}
return result;
@@ -4063,9 +4105,10 @@ static CURLcode ftp_disconnect(struct Curl_easy *data,
struct connectdata *conn,
bool dead_connection)
{
- struct ftp_conn *ftpc = &conn->proto.ftpc;
- struct pingpong *pp = &ftpc->pp;
+ struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN);
+ if(!ftpc)
+ return CURLE_FAILED_INIT;
/* We cannot send quit unconditionally. If this connection is stale or
bad in any way, sending quit and waiting around here will make the
disconnect wait in vain and cause more problems than we need to.
@@ -4073,34 +4116,15 @@ static CURLcode ftp_disconnect(struct Curl_easy *data,
ftp_quit() will check the state of ftp->ctl_valid. If it is ok it
will try to send the QUIT command, otherwise it will just return.
*/
+ ftpc->shutdown = TRUE;
if(dead_connection)
ftpc->ctl_valid = FALSE;
/* The FTP session may or may not have been allocated/setup at this point! */
- (void)ftp_quit(data, conn); /* ignore errors on the QUIT */
-
- if(ftpc->entrypath) {
- if(data->state.most_recent_ftp_entrypath == ftpc->entrypath) {
- data->state.most_recent_ftp_entrypath = NULL;
- }
- Curl_safefree(ftpc->entrypath);
- }
-
- freedirs(ftpc);
- Curl_safefree(ftpc->account);
- Curl_safefree(ftpc->alternative_to_user);
- Curl_safefree(ftpc->prevpath);
- Curl_safefree(ftpc->server_os);
- Curl_pp_disconnect(pp);
- Curl_sec_end(conn);
+ (void)ftp_quit(data, ftpc); /* ignore errors on the QUIT */
return CURLE_OK;
}
-#ifdef _MSC_VER
-/* warning C4706: assignment within conditional expression */
-#pragma warning(disable:4706)
-#endif
-
/***********************************************************************
*
* ftp_parse_url_path()
@@ -4109,12 +4133,10 @@ static CURLcode ftp_disconnect(struct Curl_easy *data,
*
*/
static
-CURLcode ftp_parse_url_path(struct Curl_easy *data)
+CURLcode ftp_parse_url_path(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp)
{
- /* the ftp struct is already inited in ftp_connect() */
- struct FTP *ftp = data->req.p.ftp;
- struct connectdata *conn = data->conn;
- struct ftp_conn *ftpc = &conn->proto.ftpc;
const char *slashPos = NULL;
const char *fileName = NULL;
CURLcode result = CURLE_OK;
@@ -4191,7 +4213,8 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data)
}
/* parse the URL path into separate path components */
- while((slashPos = strchr(curPos, '/'))) {
+ /* !checksrc! disable EQUALSNULL 1 */
+ while((slashPos = strchr(curPos, '/')) != NULL) {
size_t compLen = slashPos - curPos;
/* path starts with a slash: add that as a directory */
@@ -4236,7 +4259,7 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data)
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 : "";
+ const char *oldPath = data->conn->bits.reuse ? ftpc->prevpath : "";
if(oldPath) {
size_t n = pathLen;
if(data->set.ftp_filemethod == FTPFILE_NOCWD)
@@ -4244,7 +4267,7 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data)
else
n -= ftpc->file ? strlen(ftpc->file) : 0;
- if((strlen(oldPath) == n) && !strncmp(rawPath, oldPath, n)) {
+ if((strlen(oldPath) == n) && rawPath && !strncmp(rawPath, oldPath, n)) {
infof(data, "Request has same path as previous transfer");
ftpc->cwddone = TRUE;
}
@@ -4256,18 +4279,17 @@ CURLcode ftp_parse_url_path(struct Curl_easy *data)
}
/* call this when the DO phase has completed */
-static CURLcode ftp_dophase_done(struct Curl_easy *data, bool connected)
+static CURLcode ftp_dophase_done(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp,
+ bool connected)
{
- struct connectdata *conn = data->conn;
- struct FTP *ftp = data->req.p.ftp;
- struct ftp_conn *ftpc = &conn->proto.ftpc;
-
if(connected) {
int completed;
CURLcode result = ftp_do_more(data, &completed);
if(result) {
- close_secondarysocket(data);
+ close_secondarysocket(data, ftpc);
return result;
}
}
@@ -4277,7 +4299,7 @@ static CURLcode ftp_dophase_done(struct Curl_easy *data, bool connected)
Curl_xfer_setup_nop(data);
else if(!connected)
/* since we did not connect now, we want do_more to get called */
- conn->bits.do_more = TRUE;
+ data->conn->bits.do_more = TRUE;
ftpc->ctl_valid = TRUE; /* seems good */
@@ -4288,14 +4310,20 @@ static CURLcode ftp_dophase_done(struct Curl_easy *data, bool connected)
static CURLcode ftp_doing(struct Curl_easy *data,
bool *dophase_done)
{
- CURLcode result = ftp_multi_statemach(data, dophase_done);
+ struct ftp_conn *ftpc = Curl_conn_meta_get(data->conn, CURL_META_FTP_CONN);
+ struct FTP *ftp = Curl_meta_get(data, CURL_META_FTP_EASY);
+ CURLcode result;
+
+ if(!ftpc || !ftp)
+ return CURLE_FAILED_INIT;
+ result = ftp_statemach(data, ftpc, dophase_done);
if(result)
- CURL_TRC_FTP(data, "[%s] DO phase failed", FTP_DSTATE(data));
+ CURL_TRC_FTP(data, "[%s] DO phase failed", FTP_CSTATE(ftpc));
else if(*dophase_done) {
- result = ftp_dophase_done(data, FALSE /* not connected */);
+ result = ftp_dophase_done(data, ftpc, ftp, FALSE /* not connected */);
- CURL_TRC_FTP(data, "[%s] DO phase is complete2", FTP_DSTATE(data));
+ CURL_TRC_FTP(data, "[%s] DO phase is complete2", FTP_CSTATE(ftpc));
}
return result;
}
@@ -4314,12 +4342,12 @@ static CURLcode ftp_doing(struct Curl_easy *data,
*/
static
CURLcode ftp_regular_transfer(struct Curl_easy *data,
+ struct ftp_conn *ftpc,
+ struct FTP *ftp,
bool *dophase_done)
{
CURLcode result = CURLE_OK;
bool connected = FALSE;
- struct connectdata *conn = data->conn;
- struct ftp_conn *ftpc = &conn->proto.ftpc;
data->req.size = -1; /* make sure this is unknown at this point */
Curl_pgrsSetUploadCounter(data, 0);
@@ -4329,7 +4357,7 @@ CURLcode ftp_regular_transfer(struct Curl_easy *data,
ftpc->ctl_valid = TRUE; /* starts good */
- result = ftp_perform(data,
+ result = ftp_perform(data, ftpc, ftp,
&connected, /* have we connected after PASV/PORT */
dophase_done); /* all commands in the DO-phase done? */
@@ -4339,7 +4367,7 @@ CURLcode ftp_regular_transfer(struct Curl_easy *data,
/* the DO phase has not completed yet */
return CURLE_OK;
- result = ftp_dophase_done(data, connected);
+ result = ftp_dophase_done(data, ftpc, ftp, connected);
if(result)
return result;
@@ -4350,23 +4378,53 @@ CURLcode ftp_regular_transfer(struct Curl_easy *data,
return result;
}
+static void ftp_easy_dtor(void *key, size_t klen, void *entry)
+{
+ struct FTP *ftp = entry;
+ (void)key;
+ (void)klen;
+ Curl_safefree(ftp->pathalloc);
+ free(ftp);
+}
+
+static void ftp_conn_dtor(void *key, size_t klen, void *entry)
+{
+ struct ftp_conn *ftpc = entry;
+ (void)key;
+ (void)klen;
+ freedirs(ftpc);
+ Curl_safefree(ftpc->account);
+ Curl_safefree(ftpc->alternative_to_user);
+ Curl_safefree(ftpc->entrypath);
+ Curl_safefree(ftpc->prevpath);
+ Curl_safefree(ftpc->server_os);
+ Curl_pp_disconnect(&ftpc->pp);
+ free(ftpc);
+}
+
static CURLcode ftp_setup_connection(struct Curl_easy *data,
struct connectdata *conn)
{
char *type;
struct FTP *ftp;
CURLcode result = CURLE_OK;
- struct ftp_conn *ftpc = &conn->proto.ftpc;
+ struct ftp_conn *ftpc;
- ftp = calloc(1, sizeof(struct FTP));
- if(!ftp)
+ ftp = calloc(1, sizeof(*ftp));
+ if(!ftp ||
+ Curl_meta_set(data, CURL_META_FTP_EASY, ftp, ftp_easy_dtor))
+ return CURLE_OUT_OF_MEMORY;
+
+ ftpc = calloc(1, sizeof(*ftpc));
+ if(!ftpc ||
+ Curl_conn_meta_set(conn, CURL_META_FTP_CONN, ftpc, ftp_conn_dtor))
return CURLE_OUT_OF_MEMORY;
/* clone connection related data that is FTP specific */
if(data->set.str[STRING_FTP_ACCOUNT]) {
ftpc->account = strdup(data->set.str[STRING_FTP_ACCOUNT]);
if(!ftpc->account) {
- free(ftp);
+ Curl_conn_meta_remove(conn, CURL_META_FTP_CONN);
return CURLE_OUT_OF_MEMORY;
}
}
@@ -4375,11 +4433,10 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data,
strdup(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
if(!ftpc->alternative_to_user) {
Curl_safefree(ftpc->account);
- free(ftp);
+ Curl_conn_meta_remove(conn, CURL_META_FTP_CONN);
return CURLE_OUT_OF_MEMORY;
}
}
- data->req.p.ftp = ftp;
ftp->path = &data->state.up.path[1]; /* do not include the initial slash */
@@ -4419,8 +4476,23 @@ static CURLcode ftp_setup_connection(struct Curl_easy *data,
ftpc->use_ssl = data->set.use_ssl;
ftpc->ccc = data->set.ftp_ccc;
- CURL_TRC_FTP(data, "[%s] setup connection -> %d", FTP_CSTATE(conn), result);
+ CURL_TRC_FTP(data, "[%s] setup connection -> %d", FTP_CSTATE(ftpc), result);
return result;
}
+bool ftp_conns_match(struct connectdata *needle, struct connectdata *conn)
+{
+ struct ftp_conn *nftpc = Curl_conn_meta_get(needle, CURL_META_FTP_CONN);
+ struct ftp_conn *cftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN);
+ /* Also match ACCOUNT, ALTERNATIVE-TO-USER, USE_SSL and CCC options */
+ if(!nftpc || !cftpc ||
+ Curl_timestrcmp(nftpc->account, cftpc->account) ||
+ Curl_timestrcmp(nftpc->alternative_to_user,
+ cftpc->alternative_to_user) ||
+ (nftpc->use_ssl != cftpc->use_ssl) ||
+ (nftpc->ccc != cftpc->ccc))
+ return FALSE;
+ return TRUE;
+}
+
#endif /* CURL_DISABLE_FTP */
diff --git a/libs/libcurl/src/ftp.h b/libs/libcurl/src/ftp.h
index ebbe2fc48d..c210cbc87c 100644
--- a/libs/libcurl/src/ftp.h
+++ b/libs/libcurl/src/ftp.h
@@ -37,6 +37,9 @@ extern const struct Curl_handler Curl_handler_ftps;
CURLcode Curl_GetFTPResponse(struct Curl_easy *data, ssize_t *nread,
int *ftpcode);
+
+bool ftp_conns_match(struct connectdata *needle, struct connectdata *conn);
+
#endif /* CURL_DISABLE_FTP */
/****************************************************************************
@@ -125,7 +128,8 @@ struct ftp_conn {
char *entrypath; /* the PWD reply when we logged on */
char *file; /* url-decoded filename (or path) */
char **dirs; /* realloc()ed array for path components */
- char *newhost;
+ char *newhost; /* the (allocated) IP addr or hostname to connect the data
+ connection to */
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) */
@@ -139,9 +143,8 @@ struct ftp_conn {
int count1; /* general purpose counter for the state machine */
int count2; /* general purpose counter for the state machine */
int count3; /* general purpose counter for the state machine */
- /* newhost is the (allocated) IP addr or hostname to connect the data
- connection to */
- unsigned short newport;
+ unsigned short newport; /* the port of 'newhost' to connect the data
+ connection to */
ftpstate state; /* always use ftp.c:state() to change state! */
ftpstate state_saved; /* transfer type saved to be reloaded after data
connection is established */
@@ -160,8 +163,14 @@ struct ftp_conn {
BIT(cwdfail); /* set TRUE if a CWD command fails, as then we must prevent
caching the current directory */
BIT(wait_data_conn); /* this is set TRUE if data connection is waited */
+ BIT(shutdown); /* connection is being shutdown, e.g. QUIT */
};
+/* meta key for storing `struct FTP` as easy meta data */
+#define CURL_META_FTP_EASY "meta:proto:ftp:easy"
+/* meta key for storing `struct ftp_conn` as connection meta data */
+#define CURL_META_FTP_CONN "meta:proto:ftp:conn"
+
#define DEFAULT_ACCEPT_TIMEOUT 60000 /* milliseconds == one minute */
#endif /* HEADER_CURL_FTP_H */
diff --git a/libs/libcurl/src/ftplistparser.c b/libs/libcurl/src/ftplistparser.c
index 166f6771b5..67470bd55f 100644
--- a/libs/libcurl/src/ftplistparser.c
+++ b/libs/libcurl/src/ftplistparser.c
@@ -46,13 +46,15 @@
#include "urldata.h"
#include "fileinfo.h"
#include "llist.h"
-#include "strtoofft.h"
#include "ftp.h"
#include "ftplistparser.h"
#include "curl_fnmatch.h"
-#include "curl_memory.h"
#include "multiif.h"
-/* The last #include file should be: */
+#include "curlx/strparse.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
#include "memdebug.h"
typedef enum {
@@ -316,7 +318,7 @@ static CURLcode ftp_pl_insert_finfo(struct Curl_easy *data,
struct curl_fileinfo *finfo = &infop->info;
/* set the finfo pointers */
- char *str = Curl_dyn_ptr(&infop->buf);
+ char *str = curlx_dyn_ptr(&infop->buf);
finfo->filename = str + parser->offsets.filename;
finfo->strings.group = parser->offsets.group ?
str + parser->offsets.group : NULL;
@@ -361,6 +363,574 @@ static CURLcode ftp_pl_insert_finfo(struct Curl_easy *data,
#define MAX_FTPLIST_BUFFER 10000 /* arbitrarily set */
+static CURLcode unix_filetype(const char c, curlfiletype *t)
+{
+ switch(c) {
+ case '-':
+ *t = CURLFILETYPE_FILE;
+ break;
+ case 'd':
+ *t = CURLFILETYPE_DIRECTORY;
+ break;
+ case 'l':
+ *t = CURLFILETYPE_SYMLINK;
+ break;
+ case 'p':
+ *t = CURLFILETYPE_NAMEDPIPE;
+ break;
+ case 's':
+ *t = CURLFILETYPE_SOCKET;
+ break;
+ case 'c':
+ *t = CURLFILETYPE_DEVICE_CHAR;
+ break;
+ case 'b':
+ *t = CURLFILETYPE_DEVICE_BLOCK;
+ break;
+ case 'D':
+ *t = CURLFILETYPE_DOOR;
+ break;
+ default:
+ return CURLE_FTP_BAD_FILE_LIST;
+ }
+ return CURLE_OK;
+}
+
+static CURLcode parse_unix(struct Curl_easy *data,
+ struct ftp_parselist_data *parser,
+ struct fileinfo *infop,
+ const char c)
+{
+ struct curl_fileinfo *finfo = &infop->info;
+ size_t len = curlx_dyn_len(&infop->buf);
+ char *mem = curlx_dyn_ptr(&infop->buf);
+ CURLcode result = CURLE_OK;
+
+ switch(parser->state.UNIX.main) {
+ case PL_UNIX_TOTALSIZE:
+ switch(parser->state.UNIX.sub.total_dirsize) {
+ case PL_UNIX_TOTALSIZE_INIT:
+ if(c == 't') {
+ parser->state.UNIX.sub.total_dirsize = PL_UNIX_TOTALSIZE_READING;
+ parser->item_length++;
+ }
+ else {
+ parser->state.UNIX.main = PL_UNIX_FILETYPE;
+ /* continue to fall through */
+ }
+ break;
+ case PL_UNIX_TOTALSIZE_READING:
+ parser->item_length++;
+ if(c == '\r') {
+ parser->item_length--;
+ if(len)
+ curlx_dyn_setlen(&infop->buf, --len);
+ }
+ else if(c == '\n') {
+ mem[parser->item_length - 1] = 0;
+ if(!strncmp("total ", mem, 6)) {
+ const char *endptr = mem + 6;
+ /* here we can deal with directory size, pass the leading
+ whitespace and then the digits */
+ curlx_str_passblanks(&endptr);
+ while(ISDIGIT(*endptr))
+ endptr++;
+ if(*endptr) {
+ return CURLE_FTP_BAD_FILE_LIST;
+ }
+ parser->state.UNIX.main = PL_UNIX_FILETYPE;
+ curlx_dyn_reset(&infop->buf);
+ }
+ else
+ return CURLE_FTP_BAD_FILE_LIST;
+
+ }
+ break;
+ }
+ if(parser->state.UNIX.main != PL_UNIX_FILETYPE)
+ break;
+ FALLTHROUGH();
+ case PL_UNIX_FILETYPE:
+ result = unix_filetype(c, &finfo->filetype);
+ if(result)
+ return result;
+ parser->state.UNIX.main = PL_UNIX_PERMISSION;
+ parser->item_length = 0;
+ parser->item_offset = 1;
+ break;
+ case PL_UNIX_PERMISSION:
+ parser->item_length++;
+ if((parser->item_length <= 9) && !strchr("rwx-tTsS", c))
+ return CURLE_FTP_BAD_FILE_LIST;
+
+ else if(parser->item_length == 10) {
+ unsigned int perm;
+ if(c != ' ')
+ return CURLE_FTP_BAD_FILE_LIST;
+
+ mem[10] = 0; /* terminate permissions */
+ perm = ftp_pl_get_permission(mem + parser->item_offset);
+ if(perm & FTP_LP_MALFORMATED_PERM)
+ return CURLE_FTP_BAD_FILE_LIST;
+
+ parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_PERM;
+ parser->file_data->info.perm = perm;
+ parser->offsets.perm = parser->item_offset;
+
+ parser->item_length = 0;
+ parser->state.UNIX.main = PL_UNIX_HLINKS;
+ parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_PRESPACE;
+ }
+ break;
+ case PL_UNIX_HLINKS:
+ switch(parser->state.UNIX.sub.hlinks) {
+ case PL_UNIX_HLINKS_PRESPACE:
+ if(c != ' ') {
+ if(ISDIGIT(c) && len) {
+ parser->item_offset = len - 1;
+ parser->item_length = 1;
+ parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER;
+ }
+ else
+ return CURLE_FTP_BAD_FILE_LIST;
+ }
+ break;
+ case PL_UNIX_HLINKS_NUMBER:
+ parser->item_length ++;
+ if(c == ' ') {
+ const char *p = &mem[parser->item_offset];
+ curl_off_t hlinks;
+ mem[parser->item_offset + parser->item_length - 1] = 0;
+
+ if(!curlx_str_number(&p, &hlinks, LONG_MAX)) {
+ parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT;
+ parser->file_data->info.hardlinks = (long)hlinks;
+ }
+ parser->item_length = 0;
+ parser->item_offset = 0;
+ parser->state.UNIX.main = PL_UNIX_USER;
+ parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE;
+ }
+ else if(!ISDIGIT(c))
+ return CURLE_FTP_BAD_FILE_LIST;
+
+ break;
+ }
+ break;
+ case PL_UNIX_USER:
+ switch(parser->state.UNIX.sub.user) {
+ case PL_UNIX_USER_PRESPACE:
+ if(c != ' ' && len) {
+ parser->item_offset = len - 1;
+ parser->item_length = 1;
+ parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING;
+ }
+ break;
+ case PL_UNIX_USER_PARSING:
+ parser->item_length++;
+ if(c == ' ') {
+ mem[parser->item_offset + parser->item_length - 1] = 0;
+ parser->offsets.user = parser->item_offset;
+ parser->state.UNIX.main = PL_UNIX_GROUP;
+ parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE;
+ parser->item_offset = 0;
+ parser->item_length = 0;
+ }
+ break;
+ }
+ break;
+ case PL_UNIX_GROUP:
+ switch(parser->state.UNIX.sub.group) {
+ case PL_UNIX_GROUP_PRESPACE:
+ if(c != ' ' && len) {
+ parser->item_offset = len - 1;
+ parser->item_length = 1;
+ parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME;
+ }
+ break;
+ case PL_UNIX_GROUP_NAME:
+ parser->item_length++;
+ if(c == ' ') {
+ mem[parser->item_offset + parser->item_length - 1] = 0;
+ parser->offsets.group = parser->item_offset;
+ parser->state.UNIX.main = PL_UNIX_SIZE;
+ parser->state.UNIX.sub.size = PL_UNIX_SIZE_PRESPACE;
+ parser->item_offset = 0;
+ parser->item_length = 0;
+ }
+ break;
+ }
+ break;
+ case PL_UNIX_SIZE:
+ switch(parser->state.UNIX.sub.size) {
+ case PL_UNIX_SIZE_PRESPACE:
+ if(c != ' ') {
+ if(ISDIGIT(c) && len) {
+ parser->item_offset = len - 1;
+ parser->item_length = 1;
+ parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER;
+ }
+ else
+ return CURLE_FTP_BAD_FILE_LIST;
+ }
+ break;
+ case PL_UNIX_SIZE_NUMBER:
+ parser->item_length++;
+ if(c == ' ') {
+ const char *p = mem + parser->item_offset;
+ curl_off_t fsize;
+ mem[parser->item_offset + parser->item_length - 1] = 0;
+ if(!curlx_str_numblanks(&p, &fsize)) {
+ if(p[0] == '\0' && fsize != CURL_OFF_T_MAX) {
+ parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
+ parser->file_data->info.size = fsize;
+ }
+ parser->item_length = 0;
+ parser->item_offset = 0;
+ parser->state.UNIX.main = PL_UNIX_TIME;
+ parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1;
+ }
+ }
+ else if(!ISDIGIT(c))
+ return CURLE_FTP_BAD_FILE_LIST;
+
+ break;
+ }
+ break;
+ case PL_UNIX_TIME:
+ switch(parser->state.UNIX.sub.time) {
+ case PL_UNIX_TIME_PREPART1:
+ if(c != ' ') {
+ if(ISALNUM(c) && len) {
+ parser->item_offset = len -1;
+ parser->item_length = 1;
+ parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1;
+ }
+ else
+ return CURLE_FTP_BAD_FILE_LIST;
+ }
+ break;
+ case PL_UNIX_TIME_PART1:
+ parser->item_length++;
+ if(c == ' ')
+ parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART2;
+
+ else if(!ISALNUM(c) && c != '.')
+ return CURLE_FTP_BAD_FILE_LIST;
+
+ break;
+ case PL_UNIX_TIME_PREPART2:
+ parser->item_length++;
+ if(c != ' ') {
+ if(ISALNUM(c))
+ parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2;
+ else
+ return CURLE_FTP_BAD_FILE_LIST;
+ }
+ break;
+ case PL_UNIX_TIME_PART2:
+ parser->item_length++;
+ if(c == ' ')
+ parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART3;
+ else if(!ISALNUM(c) && c != '.')
+ return CURLE_FTP_BAD_FILE_LIST;
+ break;
+ case PL_UNIX_TIME_PREPART3:
+ parser->item_length++;
+ if(c != ' ') {
+ if(ISALNUM(c))
+ parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3;
+ else
+ return CURLE_FTP_BAD_FILE_LIST;
+ }
+ break;
+ case PL_UNIX_TIME_PART3:
+ parser->item_length++;
+ if(c == ' ') {
+ mem[parser->item_offset + parser->item_length -1] = 0;
+ parser->offsets.time = parser->item_offset;
+ if(finfo->filetype == CURLFILETYPE_SYMLINK) {
+ parser->state.UNIX.main = PL_UNIX_SYMLINK;
+ parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRESPACE;
+ }
+ else {
+ parser->state.UNIX.main = PL_UNIX_FILENAME;
+ parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_PRESPACE;
+ }
+ }
+ else if(!ISALNUM(c) && c != '.' && c != ':')
+ return CURLE_FTP_BAD_FILE_LIST;
+ break;
+ }
+ break;
+ case PL_UNIX_FILENAME:
+ switch(parser->state.UNIX.sub.filename) {
+ case PL_UNIX_FILENAME_PRESPACE:
+ if(c != ' ' && len) {
+ parser->item_offset = len - 1;
+ parser->item_length = 1;
+ parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME;
+ }
+ break;
+ case PL_UNIX_FILENAME_NAME:
+ parser->item_length++;
+ if(c == '\r')
+ parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL;
+
+ else if(c == '\n') {
+ mem[parser->item_offset + parser->item_length - 1] = 0;
+ parser->offsets.filename = parser->item_offset;
+ parser->state.UNIX.main = PL_UNIX_FILETYPE;
+ result = ftp_pl_insert_finfo(data, infop);
+ if(result)
+ return result;
+ }
+ break;
+ case PL_UNIX_FILENAME_WINDOWSEOL:
+ if(c == '\n') {
+ mem[parser->item_offset + parser->item_length - 1] = 0;
+ parser->offsets.filename = parser->item_offset;
+ parser->state.UNIX.main = PL_UNIX_FILETYPE;
+ result = ftp_pl_insert_finfo(data, infop);
+ if(result)
+ return result;
+ }
+ else
+ return CURLE_FTP_BAD_FILE_LIST;
+
+ break;
+ }
+ break;
+ case PL_UNIX_SYMLINK:
+ switch(parser->state.UNIX.sub.symlink) {
+ case PL_UNIX_SYMLINK_PRESPACE:
+ if(c != ' ' && len) {
+ parser->item_offset = len - 1;
+ parser->item_length = 1;
+ parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
+ }
+ break;
+ case PL_UNIX_SYMLINK_NAME:
+ parser->item_length++;
+ if(c == ' ')
+ parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1;
+
+ else if(c == '\r' || c == '\n')
+ return CURLE_FTP_BAD_FILE_LIST;
+
+ break;
+ case PL_UNIX_SYMLINK_PRETARGET1:
+ parser->item_length++;
+ if(c == '-')
+ parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2;
+
+ else if(c == '\r' || c == '\n')
+ return CURLE_FTP_BAD_FILE_LIST;
+ else
+ parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
+ break;
+ case PL_UNIX_SYMLINK_PRETARGET2:
+ parser->item_length++;
+ if(c == '>')
+ parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3;
+ else if(c == '\r' || c == '\n')
+ return CURLE_FTP_BAD_FILE_LIST;
+ else
+ parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
+
+ break;
+ case PL_UNIX_SYMLINK_PRETARGET3:
+ parser->item_length++;
+ if(c == ' ') {
+ parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4;
+ /* now place where is symlink following */
+ mem[parser->item_offset + parser->item_length - 4] = 0;
+ parser->offsets.filename = parser->item_offset;
+ parser->item_length = 0;
+ parser->item_offset = 0;
+ }
+ else if(c == '\r' || c == '\n')
+ return CURLE_FTP_BAD_FILE_LIST;
+ else
+ parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
+ break;
+ case PL_UNIX_SYMLINK_PRETARGET4:
+ if(c != '\r' && c != '\n' && len) {
+ parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET;
+ parser->item_offset = len - 1;
+ parser->item_length = 1;
+ }
+ else
+ return CURLE_FTP_BAD_FILE_LIST;
+
+ break;
+ case PL_UNIX_SYMLINK_TARGET:
+ parser->item_length++;
+ if(c == '\r')
+ parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL;
+
+ else if(c == '\n') {
+ mem[parser->item_offset + parser->item_length - 1] = 0;
+ parser->offsets.symlink_target = parser->item_offset;
+ result = ftp_pl_insert_finfo(data, infop);
+ if(result)
+ return result;
+
+ parser->state.UNIX.main = PL_UNIX_FILETYPE;
+ }
+ break;
+ case PL_UNIX_SYMLINK_WINDOWSEOL:
+ if(c == '\n') {
+ mem[parser->item_offset + parser->item_length - 1] = 0;
+ parser->offsets.symlink_target = parser->item_offset;
+ result = ftp_pl_insert_finfo(data, infop);
+ if(result)
+ return result;
+
+ parser->state.UNIX.main = PL_UNIX_FILETYPE;
+ }
+ else
+ return CURLE_FTP_BAD_FILE_LIST;
+
+ break;
+ }
+ break;
+ }
+ return CURLE_OK;
+}
+
+static CURLcode parse_winnt(struct Curl_easy *data,
+ struct ftp_parselist_data *parser,
+ struct fileinfo *infop,
+ const char c)
+{
+ struct curl_fileinfo *finfo = &infop->info;
+ size_t len = curlx_dyn_len(&infop->buf);
+ char *mem = curlx_dyn_ptr(&infop->buf);
+ CURLcode result = CURLE_OK;
+
+ switch(parser->state.NT.main) {
+ case PL_WINNT_DATE:
+ parser->item_length++;
+ if(parser->item_length < 9) {
+ if(!strchr("0123456789-", c)) { /* only simple control */
+ return CURLE_FTP_BAD_FILE_LIST;
+ }
+ }
+ else if(parser->item_length == 9) {
+ if(c == ' ') {
+ parser->state.NT.main = PL_WINNT_TIME;
+ parser->state.NT.sub.time = PL_WINNT_TIME_PRESPACE;
+ }
+ else
+ return CURLE_FTP_BAD_FILE_LIST;
+ }
+ else
+ return CURLE_FTP_BAD_FILE_LIST;
+ break;
+ case PL_WINNT_TIME:
+ parser->item_length++;
+ switch(parser->state.NT.sub.time) {
+ case PL_WINNT_TIME_PRESPACE:
+ if(!ISBLANK(c))
+ parser->state.NT.sub.time = PL_WINNT_TIME_TIME;
+ break;
+ case PL_WINNT_TIME_TIME:
+ if(c == ' ') {
+ parser->offsets.time = parser->item_offset;
+ mem[parser->item_offset + parser->item_length -1] = 0;
+ parser->state.NT.main = PL_WINNT_DIRORSIZE;
+ parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_PRESPACE;
+ parser->item_length = 0;
+ }
+ else if(!strchr("APM0123456789:", c))
+ return CURLE_FTP_BAD_FILE_LIST;
+ break;
+ }
+ break;
+ case PL_WINNT_DIRORSIZE:
+ switch(parser->state.NT.sub.dirorsize) {
+ case PL_WINNT_DIRORSIZE_PRESPACE:
+ if(c != ' ' && len) {
+ parser->item_offset = len - 1;
+ parser->item_length = 1;
+ parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_CONTENT;
+ }
+ break;
+ case PL_WINNT_DIRORSIZE_CONTENT:
+ parser->item_length ++;
+ if(c == ' ') {
+ mem[parser->item_offset + parser->item_length - 1] = 0;
+ if(strcmp("<DIR>", mem + parser->item_offset) == 0) {
+ finfo->filetype = CURLFILETYPE_DIRECTORY;
+ finfo->size = 0;
+ }
+ else {
+ const char *p = mem + parser->item_offset;
+ if(curlx_str_numblanks(&p, &finfo->size)) {
+ return CURLE_FTP_BAD_FILE_LIST;
+ }
+ /* correct file type */
+ parser->file_data->info.filetype = CURLFILETYPE_FILE;
+ }
+
+ parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
+ parser->item_length = 0;
+ parser->state.NT.main = PL_WINNT_FILENAME;
+ parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
+ }
+ break;
+ }
+ break;
+ case PL_WINNT_FILENAME:
+ switch(parser->state.NT.sub.filename) {
+ case PL_WINNT_FILENAME_PRESPACE:
+ if(c != ' ' && len) {
+ parser->item_offset = len -1;
+ parser->item_length = 1;
+ parser->state.NT.sub.filename = PL_WINNT_FILENAME_CONTENT;
+ }
+ break;
+ case PL_WINNT_FILENAME_CONTENT:
+ parser->item_length++;
+ if(!len)
+ return CURLE_FTP_BAD_FILE_LIST;
+ if(c == '\r') {
+ parser->state.NT.sub.filename = PL_WINNT_FILENAME_WINEOL;
+ mem[len - 1] = 0;
+ }
+ else if(c == '\n') {
+ parser->offsets.filename = parser->item_offset;
+ mem[len - 1] = 0;
+ result = ftp_pl_insert_finfo(data, infop);
+ if(result)
+ return result;
+
+ parser->state.NT.main = PL_WINNT_DATE;
+ parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
+ }
+ break;
+ case PL_WINNT_FILENAME_WINEOL:
+ if(c == '\n') {
+ parser->offsets.filename = parser->item_offset;
+ result = ftp_pl_insert_finfo(data, infop);
+ if(result)
+ return result;
+
+ parser->state.NT.main = PL_WINNT_DATE;
+ parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
+ }
+ else
+ return CURLE_FTP_BAD_FILE_LIST;
+
+ break;
+ }
+ break;
+ }
+
+ return CURLE_OK;
+}
+
size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
void *connptr)
{
@@ -388,11 +958,8 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
}
while(i < bufflen) { /* FSM */
- char *mem;
- size_t len; /* number of bytes of data in the dynbuf */
char c = buffer[i];
struct fileinfo *infop;
- struct curl_fileinfo *finfo;
if(!parser->file_data) { /* tmp file data is not allocated yet */
parser->file_data = Curl_fileinfo_alloc();
if(!parser->file_data) {
@@ -401,627 +968,31 @@ size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
}
parser->item_offset = 0;
parser->item_length = 0;
- Curl_dyn_init(&parser->file_data->buf, MAX_FTPLIST_BUFFER);
+ curlx_dyn_init(&parser->file_data->buf, MAX_FTPLIST_BUFFER);
}
infop = parser->file_data;
- finfo = &infop->info;
- if(Curl_dyn_addn(&infop->buf, &c, 1)) {
+ if(curlx_dyn_addn(&infop->buf, &c, 1)) {
parser->error = CURLE_OUT_OF_MEMORY;
goto fail;
}
- len = Curl_dyn_len(&infop->buf);
- mem = Curl_dyn_ptr(&infop->buf);
switch(parser->os_type) {
case OS_TYPE_UNIX:
- switch(parser->state.UNIX.main) {
- case PL_UNIX_TOTALSIZE:
- switch(parser->state.UNIX.sub.total_dirsize) {
- case PL_UNIX_TOTALSIZE_INIT:
- if(c == 't') {
- parser->state.UNIX.sub.total_dirsize = PL_UNIX_TOTALSIZE_READING;
- parser->item_length++;
- }
- else {
- parser->state.UNIX.main = PL_UNIX_FILETYPE;
- /* start FSM again not considering size of directory */
- Curl_dyn_reset(&infop->buf);
- continue;
- }
- break;
- case PL_UNIX_TOTALSIZE_READING:
- parser->item_length++;
- if(c == '\r') {
- parser->item_length--;
- Curl_dyn_setlen(&infop->buf, --len);
- }
- else if(c == '\n') {
- mem[parser->item_length - 1] = 0;
- if(!strncmp("total ", mem, 6)) {
- char *endptr = mem + 6;
- /* here we can deal with directory size, pass the leading
- whitespace and then the digits */
- while(ISBLANK(*endptr))
- endptr++;
- while(ISDIGIT(*endptr))
- endptr++;
- if(*endptr) {
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- parser->state.UNIX.main = PL_UNIX_FILETYPE;
- Curl_dyn_reset(&infop->buf);
- }
- else {
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- }
- break;
- }
- break;
- case PL_UNIX_FILETYPE:
- switch(c) {
- case '-':
- finfo->filetype = CURLFILETYPE_FILE;
- break;
- case 'd':
- finfo->filetype = CURLFILETYPE_DIRECTORY;
- break;
- case 'l':
- finfo->filetype = CURLFILETYPE_SYMLINK;
- break;
- case 'p':
- finfo->filetype = CURLFILETYPE_NAMEDPIPE;
- break;
- case 's':
- finfo->filetype = CURLFILETYPE_SOCKET;
- break;
- case 'c':
- finfo->filetype = CURLFILETYPE_DEVICE_CHAR;
- break;
- case 'b':
- finfo->filetype = CURLFILETYPE_DEVICE_BLOCK;
- break;
- case 'D':
- finfo->filetype = CURLFILETYPE_DOOR;
- break;
- default:
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- parser->state.UNIX.main = PL_UNIX_PERMISSION;
- parser->item_length = 0;
- parser->item_offset = 1;
- break;
- case PL_UNIX_PERMISSION:
- parser->item_length++;
- if(parser->item_length <= 9) {
- if(!strchr("rwx-tTsS", c)) {
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- }
- else if(parser->item_length == 10) {
- unsigned int perm;
- if(c != ' ') {
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- mem[10] = 0; /* terminate permissions */
- perm = ftp_pl_get_permission(mem + parser->item_offset);
- if(perm & FTP_LP_MALFORMATED_PERM) {
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_PERM;
- parser->file_data->info.perm = perm;
- parser->offsets.perm = parser->item_offset;
-
- parser->item_length = 0;
- parser->state.UNIX.main = PL_UNIX_HLINKS;
- parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_PRESPACE;
- }
- break;
- case PL_UNIX_HLINKS:
- switch(parser->state.UNIX.sub.hlinks) {
- case PL_UNIX_HLINKS_PRESPACE:
- if(c != ' ') {
- if(ISDIGIT(c)) {
- parser->item_offset = len - 1;
- parser->item_length = 1;
- parser->state.UNIX.sub.hlinks = PL_UNIX_HLINKS_NUMBER;
- }
- else {
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- }
- break;
- case PL_UNIX_HLINKS_NUMBER:
- parser->item_length ++;
- if(c == ' ') {
- char *p;
- long int hlinks;
- mem[parser->item_offset + parser->item_length - 1] = 0;
- hlinks = strtol(mem + parser->item_offset, &p, 10);
- if(p[0] == '\0' && hlinks != LONG_MAX && hlinks != LONG_MIN) {
- parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_HLINKCOUNT;
- parser->file_data->info.hardlinks = hlinks;
- }
- parser->item_length = 0;
- parser->item_offset = 0;
- parser->state.UNIX.main = PL_UNIX_USER;
- parser->state.UNIX.sub.user = PL_UNIX_USER_PRESPACE;
- }
- else if(!ISDIGIT(c)) {
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- break;
- }
- break;
- case PL_UNIX_USER:
- switch(parser->state.UNIX.sub.user) {
- case PL_UNIX_USER_PRESPACE:
- if(c != ' ') {
- parser->item_offset = len - 1;
- parser->item_length = 1;
- parser->state.UNIX.sub.user = PL_UNIX_USER_PARSING;
- }
- break;
- case PL_UNIX_USER_PARSING:
- parser->item_length++;
- if(c == ' ') {
- mem[parser->item_offset + parser->item_length - 1] = 0;
- parser->offsets.user = parser->item_offset;
- parser->state.UNIX.main = PL_UNIX_GROUP;
- parser->state.UNIX.sub.group = PL_UNIX_GROUP_PRESPACE;
- parser->item_offset = 0;
- parser->item_length = 0;
- }
- break;
- }
- break;
- case PL_UNIX_GROUP:
- switch(parser->state.UNIX.sub.group) {
- case PL_UNIX_GROUP_PRESPACE:
- if(c != ' ') {
- parser->item_offset = len - 1;
- parser->item_length = 1;
- parser->state.UNIX.sub.group = PL_UNIX_GROUP_NAME;
- }
- break;
- case PL_UNIX_GROUP_NAME:
- parser->item_length++;
- if(c == ' ') {
- mem[parser->item_offset + parser->item_length - 1] = 0;
- parser->offsets.group = parser->item_offset;
- parser->state.UNIX.main = PL_UNIX_SIZE;
- parser->state.UNIX.sub.size = PL_UNIX_SIZE_PRESPACE;
- parser->item_offset = 0;
- parser->item_length = 0;
- }
- break;
- }
- break;
- case PL_UNIX_SIZE:
- switch(parser->state.UNIX.sub.size) {
- case PL_UNIX_SIZE_PRESPACE:
- if(c != ' ') {
- if(ISDIGIT(c)) {
- parser->item_offset = len - 1;
- parser->item_length = 1;
- parser->state.UNIX.sub.size = PL_UNIX_SIZE_NUMBER;
- }
- else {
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- }
- break;
- case PL_UNIX_SIZE_NUMBER:
- parser->item_length++;
- if(c == ' ') {
- char *p;
- curl_off_t fsize;
- mem[parser->item_offset + parser->item_length - 1] = 0;
- if(!curlx_strtoofft(mem + parser->item_offset,
- &p, 10, &fsize)) {
- if(p[0] == '\0' && fsize != CURL_OFF_T_MAX &&
- fsize != CURL_OFF_T_MIN) {
- parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
- parser->file_data->info.size = fsize;
- }
- parser->item_length = 0;
- parser->item_offset = 0;
- parser->state.UNIX.main = PL_UNIX_TIME;
- parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART1;
- }
- }
- else if(!ISDIGIT(c)) {
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- break;
- }
- break;
- case PL_UNIX_TIME:
- switch(parser->state.UNIX.sub.time) {
- case PL_UNIX_TIME_PREPART1:
- if(c != ' ') {
- if(ISALNUM(c)) {
- parser->item_offset = len -1;
- parser->item_length = 1;
- parser->state.UNIX.sub.time = PL_UNIX_TIME_PART1;
- }
- else {
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- }
- break;
- case PL_UNIX_TIME_PART1:
- parser->item_length++;
- if(c == ' ') {
- parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART2;
- }
- else if(!ISALNUM(c) && c != '.') {
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- break;
- case PL_UNIX_TIME_PREPART2:
- parser->item_length++;
- if(c != ' ') {
- if(ISALNUM(c)) {
- parser->state.UNIX.sub.time = PL_UNIX_TIME_PART2;
- }
- else {
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- }
- break;
- case PL_UNIX_TIME_PART2:
- parser->item_length++;
- if(c == ' ') {
- parser->state.UNIX.sub.time = PL_UNIX_TIME_PREPART3;
- }
- else if(!ISALNUM(c) && c != '.') {
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- break;
- case PL_UNIX_TIME_PREPART3:
- parser->item_length++;
- if(c != ' ') {
- if(ISALNUM(c)) {
- parser->state.UNIX.sub.time = PL_UNIX_TIME_PART3;
- }
- else {
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- }
- break;
- case PL_UNIX_TIME_PART3:
- parser->item_length++;
- if(c == ' ') {
- mem[parser->item_offset + parser->item_length -1] = 0;
- parser->offsets.time = parser->item_offset;
- /*
- if(ftp_pl_gettime(parser, finfo->mem + parser->item_offset)) {
- parser->file_data->flags |= CURLFINFOFLAG_KNOWN_TIME;
- }
- */
- if(finfo->filetype == CURLFILETYPE_SYMLINK) {
- parser->state.UNIX.main = PL_UNIX_SYMLINK;
- parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRESPACE;
- }
- else {
- parser->state.UNIX.main = PL_UNIX_FILENAME;
- parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_PRESPACE;
- }
- }
- else if(!ISALNUM(c) && c != '.' && c != ':') {
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- break;
- }
- break;
- case PL_UNIX_FILENAME:
- switch(parser->state.UNIX.sub.filename) {
- case PL_UNIX_FILENAME_PRESPACE:
- if(c != ' ') {
- parser->item_offset = len - 1;
- parser->item_length = 1;
- parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_NAME;
- }
- break;
- case PL_UNIX_FILENAME_NAME:
- parser->item_length++;
- if(c == '\r') {
- parser->state.UNIX.sub.filename = PL_UNIX_FILENAME_WINDOWSEOL;
- }
- else if(c == '\n') {
- mem[parser->item_offset + parser->item_length - 1] = 0;
- parser->offsets.filename = parser->item_offset;
- parser->state.UNIX.main = PL_UNIX_FILETYPE;
- result = ftp_pl_insert_finfo(data, infop);
- if(result) {
- parser->error = result;
- goto fail;
- }
- }
- break;
- case PL_UNIX_FILENAME_WINDOWSEOL:
- if(c == '\n') {
- mem[parser->item_offset + parser->item_length - 1] = 0;
- parser->offsets.filename = parser->item_offset;
- parser->state.UNIX.main = PL_UNIX_FILETYPE;
- result = ftp_pl_insert_finfo(data, infop);
- if(result) {
- parser->error = result;
- goto fail;
- }
- }
- else {
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- break;
- }
- break;
- case PL_UNIX_SYMLINK:
- switch(parser->state.UNIX.sub.symlink) {
- case PL_UNIX_SYMLINK_PRESPACE:
- if(c != ' ') {
- parser->item_offset = len - 1;
- parser->item_length = 1;
- parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
- }
- break;
- case PL_UNIX_SYMLINK_NAME:
- parser->item_length++;
- if(c == ' ') {
- parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET1;
- }
- else if(c == '\r' || c == '\n') {
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- break;
- case PL_UNIX_SYMLINK_PRETARGET1:
- parser->item_length++;
- if(c == '-') {
- parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET2;
- }
- else if(c == '\r' || c == '\n') {
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- else {
- parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
- }
- break;
- case PL_UNIX_SYMLINK_PRETARGET2:
- parser->item_length++;
- if(c == '>') {
- parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET3;
- }
- else if(c == '\r' || c == '\n') {
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- else {
- parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
- }
- break;
- case PL_UNIX_SYMLINK_PRETARGET3:
- parser->item_length++;
- if(c == ' ') {
- parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_PRETARGET4;
- /* now place where is symlink following */
- mem[parser->item_offset + parser->item_length - 4] = 0;
- parser->offsets.filename = parser->item_offset;
- parser->item_length = 0;
- parser->item_offset = 0;
- }
- else if(c == '\r' || c == '\n') {
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- else {
- parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_NAME;
- }
- break;
- case PL_UNIX_SYMLINK_PRETARGET4:
- if(c != '\r' && c != '\n') {
- parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_TARGET;
- parser->item_offset = len - 1;
- parser->item_length = 1;
- }
- else {
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- break;
- case PL_UNIX_SYMLINK_TARGET:
- parser->item_length++;
- if(c == '\r') {
- parser->state.UNIX.sub.symlink = PL_UNIX_SYMLINK_WINDOWSEOL;
- }
- else if(c == '\n') {
- mem[parser->item_offset + parser->item_length - 1] = 0;
- parser->offsets.symlink_target = parser->item_offset;
- result = ftp_pl_insert_finfo(data, infop);
- if(result) {
- parser->error = result;
- goto fail;
- }
- parser->state.UNIX.main = PL_UNIX_FILETYPE;
- }
- break;
- case PL_UNIX_SYMLINK_WINDOWSEOL:
- if(c == '\n') {
- mem[parser->item_offset + parser->item_length - 1] = 0;
- parser->offsets.symlink_target = parser->item_offset;
- result = ftp_pl_insert_finfo(data, infop);
- if(result) {
- parser->error = result;
- goto fail;
- }
- parser->state.UNIX.main = PL_UNIX_FILETYPE;
- }
- else {
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- break;
- }
- break;
- }
+ result = parse_unix(data, parser, infop, c);
break;
case OS_TYPE_WIN_NT:
- switch(parser->state.NT.main) {
- case PL_WINNT_DATE:
- parser->item_length++;
- if(parser->item_length < 9) {
- if(!strchr("0123456789-", c)) { /* only simple control */
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- }
- else if(parser->item_length == 9) {
- if(c == ' ') {
- parser->state.NT.main = PL_WINNT_TIME;
- parser->state.NT.sub.time = PL_WINNT_TIME_PRESPACE;
- }
- else {
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- }
- else {
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- break;
- case PL_WINNT_TIME:
- parser->item_length++;
- switch(parser->state.NT.sub.time) {
- case PL_WINNT_TIME_PRESPACE:
- if(!ISBLANK(c)) {
- parser->state.NT.sub.time = PL_WINNT_TIME_TIME;
- }
- break;
- case PL_WINNT_TIME_TIME:
- if(c == ' ') {
- parser->offsets.time = parser->item_offset;
- mem[parser->item_offset + parser->item_length -1] = 0;
- parser->state.NT.main = PL_WINNT_DIRORSIZE;
- parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_PRESPACE;
- parser->item_length = 0;
- }
- else if(!strchr("APM0123456789:", c)) {
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- break;
- }
- break;
- case PL_WINNT_DIRORSIZE:
- switch(parser->state.NT.sub.dirorsize) {
- case PL_WINNT_DIRORSIZE_PRESPACE:
- if(c != ' ') {
- parser->item_offset = len - 1;
- parser->item_length = 1;
- parser->state.NT.sub.dirorsize = PL_WINNT_DIRORSIZE_CONTENT;
- }
- break;
- case PL_WINNT_DIRORSIZE_CONTENT:
- parser->item_length ++;
- if(c == ' ') {
- mem[parser->item_offset + parser->item_length - 1] = 0;
- if(strcmp("<DIR>", mem + parser->item_offset) == 0) {
- finfo->filetype = CURLFILETYPE_DIRECTORY;
- finfo->size = 0;
- }
- else {
- char *endptr;
- if(curlx_strtoofft(mem +
- parser->item_offset,
- &endptr, 10, &finfo->size)) {
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- /* correct file type */
- parser->file_data->info.filetype = CURLFILETYPE_FILE;
- }
-
- parser->file_data->info.flags |= CURLFINFOFLAG_KNOWN_SIZE;
- parser->item_length = 0;
- parser->state.NT.main = PL_WINNT_FILENAME;
- parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
- }
- break;
- }
- break;
- case PL_WINNT_FILENAME:
- switch(parser->state.NT.sub.filename) {
- case PL_WINNT_FILENAME_PRESPACE:
- if(c != ' ') {
- parser->item_offset = len -1;
- parser->item_length = 1;
- parser->state.NT.sub.filename = PL_WINNT_FILENAME_CONTENT;
- }
- break;
- case PL_WINNT_FILENAME_CONTENT:
- parser->item_length++;
- if(c == '\r') {
- parser->state.NT.sub.filename = PL_WINNT_FILENAME_WINEOL;
- mem[len - 1] = 0;
- }
- else if(c == '\n') {
- parser->offsets.filename = parser->item_offset;
- mem[len - 1] = 0;
- result = ftp_pl_insert_finfo(data, infop);
- if(result) {
- parser->error = result;
- goto fail;
- }
- parser->state.NT.main = PL_WINNT_DATE;
- parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
- }
- break;
- case PL_WINNT_FILENAME_WINEOL:
- if(c == '\n') {
- parser->offsets.filename = parser->item_offset;
- result = ftp_pl_insert_finfo(data, infop);
- if(result) {
- parser->error = result;
- goto fail;
- }
- parser->state.NT.main = PL_WINNT_DATE;
- parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
- }
- else {
- parser->error = CURLE_FTP_BAD_FILE_LIST;
- goto fail;
- }
- break;
- }
- break;
- }
+ result = parse_winnt(data, parser, infop, c);
break;
default:
retsize = bufflen + 1;
goto fail;
}
+ if(result) {
+ parser->error = result;
+ goto fail;
+ }
i++;
}
diff --git a/libs/libcurl/src/functypes.h b/libs/libcurl/src/functypes.h
index 2ee699f7a5..bec5215b07 100644
--- a/libs/libcurl/src/functypes.h
+++ b/libs/libcurl/src/functypes.h
@@ -62,6 +62,7 @@
/* int send(int, const char *, int, int); */
#define SEND_TYPE_ARG1 int
+#define SEND_QUAL_ARG2
#define SEND_TYPE_ARG2 char *
#define SEND_TYPE_ARG3 int
#define SEND_TYPE_RETV int
diff --git a/libs/libcurl/src/getenv.c b/libs/libcurl/src/getenv.c
index edb085f8cd..0749117a7f 100644
--- a/libs/libcurl/src/getenv.c
+++ b/libs/libcurl/src/getenv.c
@@ -31,7 +31,7 @@
static char *GetEnv(const char *variable)
{
-#if defined(_WIN32_WCE) || defined(CURL_WINDOWS_UWP) || \
+#if defined(CURL_WINDOWS_UWP) || defined(UNDER_CE) || \
defined(__ORBIS__) || defined(__PROSPERO__) /* PlayStation 4 and 5 */
(void)variable;
return NULL;
diff --git a/libs/libcurl/src/getinfo.c b/libs/libcurl/src/getinfo.c
index a3f159e446..2e5c0d5cbe 100644
--- a/libs/libcurl/src/getinfo.c
+++ b/libs/libcurl/src/getinfo.c
@@ -28,10 +28,10 @@
#include "urldata.h"
#include "getinfo.h"
-
#include "vtls/vtls.h"
#include "connect.h" /* Curl_getconnectinfo() */
#include "progress.h"
+#include "curlx/strparse.h"
/* The last #include files should be: */
#include "curl_memory.h"
@@ -69,6 +69,8 @@ CURLcode Curl_initinfo(struct Curl_easy *data)
info->request_size = 0;
info->proxyauthavail = 0;
info->httpauthavail = 0;
+ info->proxyauthpicked = 0;
+ info->httpauthpicked = 0;
info->numconnects = 0;
free(info->contenttype);
@@ -96,7 +98,7 @@ static CURLcode getinfo_char(struct Curl_easy *data, CURLINFO info,
{
switch(info) {
case CURLINFO_EFFECTIVE_URL:
- *param_charp = data->state.url ? data->state.url : (char *)"";
+ *param_charp = data->state.url ? data->state.url : "";
break;
case CURLINFO_EFFECTIVE_METHOD: {
const char *m = data->set.str[STRING_CUSTOMREQUEST];
@@ -202,9 +204,10 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info,
} lptr;
#ifdef DEBUGBUILD
- char *timestr = getenv("CURL_TIME");
+ const char *timestr = getenv("CURL_TIME");
if(timestr) {
- unsigned long val = strtoul(timestr, NULL, 10);
+ curl_off_t val;
+ curlx_str_number(&timestr, &val, TIME_T_MAX);
switch(info) {
case CURLINFO_LOCAL_PORT:
*param_longp = (long)val;
@@ -216,7 +219,8 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info,
/* use another variable for this to allow different values */
timestr = getenv("CURL_DEBUG_SIZE");
if(timestr) {
- unsigned long val = strtoul(timestr, NULL, 10);
+ curl_off_t val;
+ curlx_str_number(&timestr, &val, LONG_MAX);
switch(info) {
case CURLINFO_HEADER_SIZE:
case CURLINFO_REQUEST_SIZE:
@@ -238,8 +242,10 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info,
case CURLINFO_FILETIME:
if(data->info.filetime > LONG_MAX)
*param_longp = LONG_MAX;
+#if !defined(MSDOS) && !defined(__AMIGA__)
else if(data->info.filetime < LONG_MIN)
*param_longp = LONG_MIN;
+#endif
else
*param_longp = (long)data->info.filetime;
break;
@@ -270,6 +276,14 @@ static CURLcode getinfo_long(struct Curl_easy *data, CURLINFO info,
lptr.to_long = param_longp;
*lptr.to_ulong = data->info.proxyauthavail;
break;
+ case CURLINFO_HTTPAUTH_USED:
+ lptr.to_long = param_longp;
+ *lptr.to_ulong = data->info.httpauthpicked;
+ break;
+ case CURLINFO_PROXYAUTH_USED:
+ lptr.to_long = param_longp;
+ *lptr.to_ulong = data->info.proxyauthpicked;
+ break;
case CURLINFO_OS_ERRNO:
*param_longp = data->state.os_errno;
break;
@@ -367,9 +381,11 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info,
curl_off_t *param_offt)
{
#ifdef DEBUGBUILD
- char *timestr = getenv("CURL_TIME");
+ const char *timestr = getenv("CURL_TIME");
if(timestr) {
- unsigned long val = strtoul(timestr, NULL, 10);
+ curl_off_t val;
+ curlx_str_number(&timestr, &val, CURL_OFF_T_MAX);
+
switch(info) {
case CURLINFO_TOTAL_TIME_T:
case CURLINFO_NAMELOOKUP_TIME_T:
@@ -377,6 +393,7 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info,
case CURLINFO_APPCONNECT_TIME_T:
case CURLINFO_PRETRANSFER_TIME_T:
case CURLINFO_POSTTRANSFER_TIME_T:
+ case CURLINFO_QUEUE_TIME_T:
case CURLINFO_STARTTRANSFER_TIME_T:
case CURLINFO_REDIRECT_TIME_T:
case CURLINFO_SPEED_DOWNLOAD_T:
@@ -405,11 +422,11 @@ static CURLcode getinfo_offt(struct Curl_easy *data, CURLINFO info,
*param_offt = data->progress.ul.speed;
break;
case CURLINFO_CONTENT_LENGTH_DOWNLOAD_T:
- *param_offt = (data->progress.flags & PGRS_DL_SIZE_KNOWN) ?
+ *param_offt = data->progress.dl_size_known ?
data->progress.dl.total_size : -1;
break;
case CURLINFO_CONTENT_LENGTH_UPLOAD_T:
- *param_offt = (data->progress.flags & PGRS_UL_SIZE_KNOWN) ?
+ *param_offt = data->progress.ul_size_known ?
data->progress.ul.total_size : -1;
break;
case CURLINFO_TOTAL_TIME_T:
@@ -463,9 +480,11 @@ static CURLcode getinfo_double(struct Curl_easy *data, CURLINFO info,
double *param_doublep)
{
#ifdef DEBUGBUILD
- char *timestr = getenv("CURL_TIME");
+ const char *timestr = getenv("CURL_TIME");
if(timestr) {
- unsigned long val = strtoul(timestr, NULL, 10);
+ curl_off_t val;
+ curlx_str_number(&timestr, &val, CURL_OFF_T_MAX);
+
switch(info) {
case CURLINFO_TOTAL_TIME:
case CURLINFO_NAMELOOKUP_TIME:
@@ -515,11 +534,11 @@ static CURLcode getinfo_double(struct Curl_easy *data, CURLINFO info,
*param_doublep = (double)data->progress.ul.speed;
break;
case CURLINFO_CONTENT_LENGTH_DOWNLOAD:
- *param_doublep = (data->progress.flags & PGRS_DL_SIZE_KNOWN) ?
+ *param_doublep = data->progress.dl_size_known ?
(double)data->progress.dl.total_size : -1;
break;
case CURLINFO_CONTENT_LENGTH_UPLOAD:
- *param_doublep = (data->progress.flags & PGRS_UL_SIZE_KNOWN) ?
+ *param_doublep = data->progress.ul_size_known ?
(double)data->progress.ul.total_size : -1;
break;
case CURLINFO_REDIRECT_TIME:
diff --git a/libs/libcurl/src/gopher.c b/libs/libcurl/src/gopher.c
index 220dc21807..fd7ff2cb19 100644
--- a/libs/libcurl/src/gopher.c
+++ b/libs/libcurl/src/gopher.c
@@ -39,7 +39,7 @@
#include "vtls/vtls.h"
#include "url.h"
#include "escape.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
#include "curl_printf.h"
#include "curl_memory.h"
/* The last #include file should be: */
@@ -79,6 +79,7 @@ const struct Curl_handler Curl_handler_gopher = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_GOPHER, /* defport */
CURLPROTO_GOPHER, /* protocol */
CURLPROTO_GOPHER, /* family */
@@ -104,6 +105,7 @@ const struct Curl_handler Curl_handler_gophers = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_GOPHER, /* defport */
CURLPROTO_GOPHERS, /* protocol */
CURLPROTO_GOPHER, /* family */
@@ -160,7 +162,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done)
/* Create selector. Degenerate cases: / and /1 => convert to "" */
if(strlen(gopherpath) <= 2) {
- sel = (char *)"";
+ sel = (char *)CURL_UNCONST("");
len = strlen(sel);
free(gopherpath);
}
@@ -234,7 +236,7 @@ static CURLcode gopher_do(struct Curl_easy *data, bool *done)
failf(data, "Failed sending Gopher request");
return result;
}
- result = Curl_client_write(data, CLIENTWRITE_HEADER, (char *)"\r\n", 2);
+ result = Curl_client_write(data, CLIENTWRITE_HEADER, "\r\n", 2);
if(result)
return result;
diff --git a/libs/libcurl/src/hash.c b/libs/libcurl/src/hash.c
index 64810bd1df..1b98c2b3ae 100644
--- a/libs/libcurl/src/hash.c
+++ b/libs/libcurl/src/hash.c
@@ -34,27 +34,49 @@
#include "memdebug.h"
/* random patterns for API verification */
+#ifdef DEBUGBUILD
#define HASHINIT 0x7017e781
#define ITERINIT 0x5FEDCBA9
+#endif
-static void
-hash_element_dtor(void *user, void *element)
+
+#if 0 /* useful function for debugging hashes and their contents */
+void Curl_hash_print(struct Curl_hash *h,
+ void (*func)(void *))
{
- struct Curl_hash *h = (struct Curl_hash *) user;
- struct Curl_hash_element *e = (struct Curl_hash_element *) element;
+ struct Curl_hash_iterator iter;
+ struct Curl_hash_element *he;
+ size_t last_index = UINT_MAX;
- if(e->ptr) {
- if(e->dtor)
- e->dtor(e->key, e->key_len, e->ptr);
- else
- h->dtor(e->ptr);
- e->ptr = NULL;
- }
+ if(!h)
+ return;
+
+ fprintf(stderr, "=Hash dump=\n");
- e->key_len = 0;
+ Curl_hash_start_iterate(h, &iter);
+
+ he = Curl_hash_next_element(&iter);
+ while(he) {
+ if(iter.slot_index != last_index) {
+ fprintf(stderr, "index %d:", (int)iter.slot_index);
+ if(last_index != UINT_MAX) {
+ fprintf(stderr, "\n");
+ }
+ last_index = iter.slot_index;
+ }
- free(e);
+ if(func)
+ func(he->ptr);
+ else
+ fprintf(stderr, " [key=%.*s, he=%p, ptr=%p]",
+ (int)he->key_len, (char *)he->key,
+ (void *)he, (void *)he->ptr);
+
+ he = Curl_hash_next_element(&iter);
+ }
+ fprintf(stderr, "\n");
}
+#endif
/* Initializes a hash structure.
* Return 1 on error, 0 is fine.
@@ -87,62 +109,96 @@ Curl_hash_init(struct Curl_hash *h,
}
static struct Curl_hash_element *
-mk_hash_element(const void *key, size_t key_len, const void *p,
- Curl_hash_elem_dtor dtor)
+hash_elem_create(const void *key, size_t key_len, const void *p,
+ Curl_hash_elem_dtor dtor)
{
+ struct Curl_hash_element *he;
+
/* allocate the struct plus memory after it to store the key */
- struct Curl_hash_element *he = malloc(sizeof(struct Curl_hash_element) +
- key_len);
+ he = malloc(sizeof(struct Curl_hash_element) + key_len);
if(he) {
+ he->next = NULL;
/* copy the key */
memcpy(he->key, key, key_len);
he->key_len = key_len;
- he->ptr = (void *) p;
+ he->ptr = CURL_UNCONST(p);
he->dtor = dtor;
}
return he;
}
-#define FETCH_LIST(x,y,z) &x->table[x->hash_func(y, z, x->slots)]
+static void hash_elem_clear_ptr(struct Curl_hash *h,
+ struct Curl_hash_element *he)
+{
+ DEBUGASSERT(h);
+ DEBUGASSERT(he);
+ if(he->ptr) {
+ if(he->dtor)
+ he->dtor(he->key, he->key_len, he->ptr);
+ else
+ h->dtor(he->ptr);
+ he->ptr = NULL;
+ }
+}
+
+static void hash_elem_destroy(struct Curl_hash *h,
+ struct Curl_hash_element *he)
+{
+ hash_elem_clear_ptr(h, he);
+ free(he);
+}
+
+static void hash_elem_unlink(struct Curl_hash *h,
+ struct Curl_hash_element **he_anchor,
+ struct Curl_hash_element *he)
+{
+ *he_anchor = he->next;
+ --h->size;
+}
+
+static void hash_elem_link(struct Curl_hash *h,
+ struct Curl_hash_element **he_anchor,
+ struct Curl_hash_element *he)
+{
+ he->next = *he_anchor;
+ *he_anchor = he;
+ ++h->size;
+}
+
+#define CURL_HASH_SLOT(x,y,z) x->table[x->hash_func(y, z, x->slots)]
+#define CURL_HASH_SLOT_ADDR(x,y,z) &CURL_HASH_SLOT(x,y,z)
void *Curl_hash_add2(struct Curl_hash *h, void *key, size_t key_len, void *p,
Curl_hash_elem_dtor dtor)
{
- struct Curl_hash_element *he;
- struct Curl_llist_node *le;
- struct Curl_llist *l;
+ struct Curl_hash_element *he, **slot;
DEBUGASSERT(h);
DEBUGASSERT(h->slots);
DEBUGASSERT(h->init == HASHINIT);
if(!h->table) {
- size_t i;
- h->table = malloc(h->slots * sizeof(struct Curl_llist));
+ h->table = calloc(h->slots, sizeof(struct Curl_hash_element *));
if(!h->table)
return NULL; /* OOM */
- for(i = 0; i < h->slots; ++i)
- Curl_llist_init(&h->table[i], hash_element_dtor);
}
- l = FETCH_LIST(h, key, key_len);
-
- for(le = Curl_llist_head(l); le; le = Curl_node_next(le)) {
- he = (struct Curl_hash_element *) Curl_node_elem(le);
+ slot = CURL_HASH_SLOT_ADDR(h, key, key_len);
+ for(he = *slot; he; he = he->next) {
if(h->comp_func(he->key, he->key_len, key, key_len)) {
- Curl_node_uremove(le, (void *)h);
- --h->size;
- break;
+ /* existing key entry, overwrite by clearing old pointer */
+ hash_elem_clear_ptr(h, he);
+ he->ptr = (void *)p;
+ he->dtor = dtor;
+ return p;
}
}
- he = mk_hash_element(key, key_len, p, dtor);
- if(he) {
- Curl_llist_append(l, he, &he->list);
- ++h->size;
- return p; /* return the new entry */
- }
+ he = hash_elem_create(key, key_len, p, dtor);
+ if(!he)
+ return NULL; /* OOM */
- return NULL; /* failure */
+ hash_elem_link(h, slot, he);
+ return p; /* return the new entry */
}
/* Insert the data in the hash. If there already was a match in the hash, that
@@ -170,16 +226,17 @@ int Curl_hash_delete(struct Curl_hash *h, void *key, size_t key_len)
DEBUGASSERT(h->slots);
DEBUGASSERT(h->init == HASHINIT);
if(h->table) {
- struct Curl_llist_node *le;
- struct Curl_llist *l = FETCH_LIST(h, key, key_len);
+ struct Curl_hash_element *he, **he_anchor;
- for(le = Curl_llist_head(l); le; le = Curl_node_next(le)) {
- struct Curl_hash_element *he = Curl_node_elem(le);
+ he_anchor = CURL_HASH_SLOT_ADDR(h, key, key_len);
+ while(*he_anchor) {
+ he = *he_anchor;
if(h->comp_func(he->key, he->key_len, key, key_len)) {
- Curl_node_uremove(le, (void *) h);
- --h->size;
+ hash_elem_unlink(h, he_anchor, he);
+ hash_elem_destroy(h, he);
return 0;
}
+ he_anchor = &he->next;
}
}
return 1;
@@ -195,18 +252,16 @@ Curl_hash_pick(struct Curl_hash *h, void *key, size_t key_len)
DEBUGASSERT(h);
DEBUGASSERT(h->init == HASHINIT);
if(h->table) {
- struct Curl_llist_node *le;
- struct Curl_llist *l;
+ struct Curl_hash_element *he;
DEBUGASSERT(h->slots);
- l = FETCH_LIST(h, key, key_len);
- for(le = Curl_llist_head(l); le; le = Curl_node_next(le)) {
- struct Curl_hash_element *he = Curl_node_elem(le);
+ he = CURL_HASH_SLOT(h, key, key_len);
+ while(he) {
if(h->comp_func(he->key, he->key_len, key, key_len)) {
return he->ptr;
}
+ he = he->next;
}
}
-
return NULL;
}
@@ -222,13 +277,10 @@ Curl_hash_destroy(struct Curl_hash *h)
{
DEBUGASSERT(h->init == HASHINIT);
if(h->table) {
- size_t i;
- for(i = 0; i < h->slots; ++i) {
- Curl_llist_destroy(&h->table[i], (void *) h);
- }
+ Curl_hash_clean(h);
Curl_safefree(h->table);
}
- h->size = 0;
+ DEBUGASSERT(h->size == 0);
h->slots = 0;
}
@@ -236,10 +288,21 @@ Curl_hash_destroy(struct Curl_hash *h)
*
* @unittest: 1602
*/
-void
-Curl_hash_clean(struct Curl_hash *h)
+void Curl_hash_clean(struct Curl_hash *h)
{
- Curl_hash_clean_with_criterium(h, NULL, NULL);
+ if(h && h->table) {
+ struct Curl_hash_element *he, **he_anchor;
+ size_t i;
+ DEBUGASSERT(h->init == HASHINIT);
+ for(i = 0; i < h->slots; ++i) {
+ he_anchor = &h->table[i];
+ while(*he_anchor) {
+ he = *he_anchor;
+ hash_elem_unlink(h, he_anchor, he);
+ hash_elem_destroy(h, he);
+ }
+ }
+ }
}
size_t Curl_hash_count(struct Curl_hash *h)
@@ -260,18 +323,16 @@ Curl_hash_clean_with_criterium(struct Curl_hash *h, void *user,
DEBUGASSERT(h->init == HASHINIT);
for(i = 0; i < h->slots; ++i) {
- struct Curl_llist *list = &h->table[i];
- struct Curl_llist_node *le =
- Curl_llist_head(list); /* get first list entry */
- while(le) {
- struct Curl_hash_element *he = Curl_node_elem(le);
- struct Curl_llist_node *lnext = Curl_node_next(le);
+ struct Curl_hash_element *he, **he_anchor = &h->table[i];
+ while(*he_anchor) {
/* ask the callback function if we shall remove this entry or not */
- if(!comp || comp(user, he->ptr)) {
- Curl_node_uremove(le, (void *) h);
- --h->size; /* one less entry in the hash now */
+ if(!comp || comp(user, (*he_anchor)->ptr)) {
+ he = *he_anchor;
+ hash_elem_unlink(h, he_anchor, he);
+ hash_elem_destroy(h, he);
}
- le = lnext;
+ else
+ he_anchor = &(*he_anchor)->next;
}
}
}
@@ -291,7 +352,7 @@ size_t Curl_hash_str(void *key, size_t key_length, size_t slots_num)
return (h % slots_num);
}
-size_t Curl_str_key_compare(void *k1, size_t key1_len,
+size_t curlx_str_key_compare(void *k1, size_t key1_len,
void *k2, size_t key2_len)
{
if((key1_len == key2_len) && !memcmp(k1, k2, key1_len))
@@ -306,7 +367,7 @@ void Curl_hash_start_iterate(struct Curl_hash *hash,
DEBUGASSERT(hash->init == HASHINIT);
iter->hash = hash;
iter->slot_index = 0;
- iter->current_element = NULL;
+ iter->current = NULL;
#ifdef DEBUGBUILD
iter->init = ITERINIT;
#endif
@@ -322,82 +383,20 @@ Curl_hash_next_element(struct Curl_hash_iterator *iter)
return NULL; /* empty hash, nothing to return */
/* Get the next element in the current list, if any */
- if(iter->current_element)
- iter->current_element = Curl_node_next(iter->current_element);
+ if(iter->current)
+ iter->current = iter->current->next;
/* If we have reached the end of the list, find the next one */
- if(!iter->current_element) {
+ if(!iter->current) {
size_t i;
for(i = iter->slot_index; i < h->slots; i++) {
- if(Curl_llist_head(&h->table[i])) {
- iter->current_element = Curl_llist_head(&h->table[i]);
+ if(h->table[i]) {
+ iter->current = h->table[i];
iter->slot_index = i + 1;
break;
}
}
}
- if(iter->current_element) {
- struct Curl_hash_element *he = Curl_node_elem(iter->current_element);
- return he;
- }
- return NULL;
-}
-
-#if 0 /* useful function for debugging hashes and their contents */
-void Curl_hash_print(struct Curl_hash *h,
- void (*func)(void *))
-{
- struct Curl_hash_iterator iter;
- struct Curl_hash_element *he;
- size_t last_index = ~0;
-
- if(!h)
- return;
-
- fprintf(stderr, "=Hash dump=\n");
-
- Curl_hash_start_iterate(h, &iter);
-
- he = Curl_hash_next_element(&iter);
- while(he) {
- if(iter.slot_index != last_index) {
- fprintf(stderr, "index %d:", iter.slot_index);
- if(last_index != ~0) {
- fprintf(stderr, "\n");
- }
- last_index = iter.slot_index;
- }
-
- if(func)
- func(he->ptr);
- else
- fprintf(stderr, " [%p]", (void *)he->ptr);
-
- he = Curl_hash_next_element(&iter);
- }
- fprintf(stderr, "\n");
-}
-#endif
-
-void Curl_hash_offt_init(struct Curl_hash *h,
- size_t slots,
- Curl_hash_dtor dtor)
-{
- Curl_hash_init(h, slots, Curl_hash_str, Curl_str_key_compare, dtor);
-}
-
-void *Curl_hash_offt_set(struct Curl_hash *h, curl_off_t id, void *elem)
-{
- return Curl_hash_add(h, &id, sizeof(id), elem);
-}
-
-int Curl_hash_offt_remove(struct Curl_hash *h, curl_off_t id)
-{
- return Curl_hash_delete(h, &id, sizeof(id));
-}
-
-void *Curl_hash_offt_get(struct Curl_hash *h, curl_off_t id)
-{
- return Curl_hash_pick(h, &id, sizeof(id));
+ return iter->current;
}
diff --git a/libs/libcurl/src/hash.h b/libs/libcurl/src/hash.h
index c3387fbde6..61daf8fd21 100644
--- a/libs/libcurl/src/hash.h
+++ b/libs/libcurl/src/hash.h
@@ -45,14 +45,24 @@ typedef size_t (*comp_function) (void *key1,
typedef void (*Curl_hash_dtor)(void *);
+typedef void (*Curl_hash_elem_dtor)(void *key, size_t key_len, void *p);
+
+struct Curl_hash_element {
+ struct Curl_hash_element *next;
+ void *ptr;
+ Curl_hash_elem_dtor dtor;
+ size_t key_len;
+ char key[1]; /* allocated memory following the struct */
+};
+
struct Curl_hash {
- struct Curl_llist *table;
+ struct Curl_hash_element **table;
/* Hash function to be used for this hash table */
hash_function hash_func;
-
/* Comparator function to compare keys */
comp_function comp_func;
+ /* General element construct, unless element itself carries one */
Curl_hash_dtor dtor;
size_t slots;
size_t size;
@@ -61,23 +71,10 @@ struct Curl_hash {
#endif
};
-typedef void (*Curl_hash_elem_dtor)(void *key, size_t key_len, void *p);
-
-struct Curl_hash_element {
- struct Curl_llist_node list;
- void *ptr;
- Curl_hash_elem_dtor dtor;
- size_t key_len;
-#ifdef DEBUGBUILD
- int init;
-#endif
- char key[1]; /* allocated memory following the struct */
-};
-
struct Curl_hash_iterator {
struct Curl_hash *hash;
size_t slot_index;
- struct Curl_llist_node *current_element;
+ struct Curl_hash_element *current;
#ifdef DEBUGBUILD
int init;
#endif
@@ -101,7 +98,7 @@ void Curl_hash_clean(struct Curl_hash *h);
void Curl_hash_clean_with_criterium(struct Curl_hash *h, void *user,
int (*comp)(void *, void *));
size_t Curl_hash_str(void *key, size_t key_length, size_t slots_num);
-size_t Curl_str_key_compare(void *k1, size_t key1_len, void *k2,
+size_t curlx_str_key_compare(void *k1, size_t key1_len, void *k2,
size_t key2_len);
void Curl_hash_start_iterate(struct Curl_hash *hash,
struct Curl_hash_iterator *iter);
@@ -111,13 +108,4 @@ Curl_hash_next_element(struct Curl_hash_iterator *iter);
void Curl_hash_print(struct Curl_hash *h,
void (*func)(void *));
-/* Hash for `curl_off_t` as key */
-void Curl_hash_offt_init(struct Curl_hash *h, size_t slots,
- Curl_hash_dtor dtor);
-
-void *Curl_hash_offt_set(struct Curl_hash *h, curl_off_t id, void *elem);
-int Curl_hash_offt_remove(struct Curl_hash *h, curl_off_t id);
-void *Curl_hash_offt_get(struct Curl_hash *h, curl_off_t id);
-
-
#endif /* HEADER_CURL_HASH_H */
diff --git a/libs/libcurl/src/headers.c b/libs/libcurl/src/headers.c
index def9605b47..ca6527062c 100644
--- a/libs/libcurl/src/headers.c
+++ b/libs/libcurl/src/headers.c
@@ -29,6 +29,7 @@
#include "strcase.h"
#include "sendf.h"
#include "headers.h"
+#include "curlx/strparse.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -208,15 +209,15 @@ static CURLcode namevalue(char *header, size_t hlen, unsigned int type,
else
return CURLE_BAD_FUNCTION_ARGUMENT;
- /* skip all leading space letters */
- while(*header && ISBLANK(*header))
+ /* skip all leading blank letters */
+ while(ISBLANK(*header))
header++;
*value = header;
/* skip all trailing space letters */
- while((end > header) && ISSPACE(*end))
- *end-- = 0; /* nul terminate */
+ while((end > header) && ISBLANK(*end))
+ *end-- = 0; /* null-terminate */
return CURLE_OK;
}
@@ -235,7 +236,7 @@ static CURLcode unfold_value(struct Curl_easy *data, const char *value,
oalloc = olen + offset + 1;
/* skip all trailing space letters */
- while(vlen && ISSPACE(value[vlen - 1]))
+ while(vlen && ISBLANK(value[vlen - 1]))
vlen--;
/* save only one leading space */
@@ -312,12 +313,17 @@ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header,
return CURLE_WEIRD_SERVER_REPLY;
}
}
+ if(Curl_llist_count(&data->state.httphdrs) >= MAX_HTTP_RESP_HEADER_COUNT) {
+ failf(data, "Too many response headers, %d is max",
+ MAX_HTTP_RESP_HEADER_COUNT);
+ return CURLE_TOO_LARGE;
+ }
hs = calloc(1, sizeof(*hs) + hlen);
if(!hs)
return CURLE_OUT_OF_MEMORY;
memcpy(hs->buffer, header, hlen);
- hs->buffer[hlen] = 0; /* nul terminate */
+ hs->buffer[hlen] = 0; /* null-terminate */
result = namevalue(hs->buffer, hlen, type, &name, &value);
if(!result) {
@@ -330,8 +336,10 @@ CURLcode Curl_headers_push(struct Curl_easy *data, const char *header,
Curl_llist_append(&data->state.httphdrs, hs, &hs->node);
data->state.prevhead = hs;
}
- else
+ else {
+ failf(data, "Invalid response header");
free(hs);
+ }
return result;
}
diff --git a/libs/libcurl/src/hmac.c b/libs/libcurl/src/hmac.c
index 4a8cffe798..3d8743dcf2 100644
--- a/libs/libcurl/src/hmac.c
+++ b/libs/libcurl/src/hmac.c
@@ -26,14 +26,15 @@
#include "curl_setup.h"
-#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) \
- || !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH)
+#if (defined(USE_CURL_NTLM_CORE) && !defined(USE_WINDOWS_SSPI)) || \
+ !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) || \
+ defined(USE_SSL)
#include <curl/curl.h>
#include "curl_hmac.h"
#include "curl_memory.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
/* The last #include file should be: */
#include "memdebug.h"
diff --git a/libs/libcurl/src/hostasyn.c b/libs/libcurl/src/hostasyn.c
deleted file mode 100644
index 87b38602fc..0000000000
--- a/libs/libcurl/src/hostasyn.c
+++ /dev/null
@@ -1,123 +0,0 @@
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-/***********************************************************************
- * Only for builds using asynchronous name resolves
- **********************************************************************/
-#ifdef CURLRES_ASYNCH
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef __VMS
-#include <in.h>
-#include <inet.h>
-#endif
-
-#include "urldata.h"
-#include "sendf.h"
-#include "hostip.h"
-#include "hash.h"
-#include "share.h"
-#include "url.h"
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "memdebug.h"
-
-/*
- * Curl_addrinfo_callback() gets called by ares, gethostbyname_thread()
- * or getaddrinfo_thread() when we got the name resolved (or not!).
- *
- * If the status argument is CURL_ASYNC_SUCCESS, this function takes
- * ownership of the Curl_addrinfo passed, storing the resolved data
- * in the DNS cache.
- *
- * The storage operation locks and unlocks the DNS cache.
- */
-CURLcode Curl_addrinfo_callback(struct Curl_easy *data,
- int status,
- struct Curl_addrinfo *ai)
-{
- struct Curl_dns_entry *dns = NULL;
- CURLcode result = CURLE_OK;
-
- data->state.async.status = status;
-
- if(CURL_ASYNC_SUCCESS == status) {
- if(ai) {
- if(data->share)
- Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
- dns = Curl_cache_addr(data, ai,
- data->state.async.hostname, 0,
- data->state.async.port, FALSE);
- if(data->share)
- Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
-
- if(!dns) {
- /* failed to store, cleanup and return error */
- Curl_freeaddrinfo(ai);
- result = CURLE_OUT_OF_MEMORY;
- }
- }
- else {
- result = CURLE_OUT_OF_MEMORY;
- }
- }
-
- data->state.async.dns = dns;
-
- /* Set async.done TRUE last in this function since it may be used multi-
- threaded and once this is TRUE the other thread may read fields from the
- async struct */
- data->state.async.done = TRUE;
-
- /* IPv4: The input hostent struct will be freed by ares when we return from
- this function */
- return result;
-}
-
-/*
- * Curl_getaddrinfo() is the generic low-level name resolve API within this
- * source file. There are several versions of this function - for different
- * name resolve layers (selected at build-time). They all take this same set
- * of arguments
- */
-struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data,
- const char *hostname,
- int port,
- int *waitp)
-{
- return Curl_resolver_getaddrinfo(data, hostname, port, waitp);
-}
-
-#endif /* CURLRES_ASYNCH */
diff --git a/libs/libcurl/src/hostip.c b/libs/libcurl/src/hostip.c
index 2629492504..97c7ea9e68 100644
--- a/libs/libcurl/src/hostip.c
+++ b/libs/libcurl/src/hostip.c
@@ -42,22 +42,28 @@
#endif
#include <setjmp.h>
+#ifndef UNDER_CE
#include <signal.h>
+#endif
#include "urldata.h"
#include "sendf.h"
+#include "connect.h"
#include "hostip.h"
#include "hash.h"
#include "rand.h"
#include "share.h"
#include "url.h"
#include "inet_ntop.h"
-#include "inet_pton.h"
+#include "curlx/inet_pton.h"
#include "multiif.h"
#include "doh.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
+#include "select.h"
#include "strcase.h"
#include "easy_lock.h"
+#include "curlx/strparse.h"
+
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
@@ -102,20 +108,18 @@
* The host*.c sources files are split up like this:
*
* hostip.c - method-independent resolver functions and utility functions
- * hostasyn.c - functions for asynchronous name resolves
- * hostsyn.c - functions for synchronous name resolves
* hostip4.c - IPv4 specific functions
* hostip6.c - IPv6 specific functions
- *
+ * asyn.h - common functions for all async resolvers
* The two asynchronous name resolver backends are implemented in:
- * asyn-ares.c - functions for ares-using name resolves
- * asyn-thread.c - functions for threaded name resolves
-
+ * asyn-ares.c - async resolver using c-ares
+ * asyn-thread.c - async resolver using POSIX threads
+ *
* The hostip.h is the united header file for all this. It defines the
* CURLRES_* defines based on the config*.h and curl_setup.h defines.
*/
-static void hostcache_unlink_entry(void *entry);
+static void dnscache_entry_free(struct Curl_dns_entry *dns);
#ifndef CURL_DISABLE_VERBOSE_STRINGS
static void show_resolve_info(struct Curl_easy *data,
@@ -162,9 +166,9 @@ void Curl_printable_address(const struct Curl_addrinfo *ai, char *buf,
* the DNS caching. Without alloc. Return length of the id string.
*/
static size_t
-create_hostcache_id(const char *name,
- size_t nlen, /* 0 or actual name length */
- int port, char *ptr, size_t buflen)
+create_dnscache_id(const char *name,
+ size_t nlen, /* 0 or actual name length */
+ int port, char *ptr, size_t buflen)
{
size_t len = nlen ? nlen : strlen(name);
DEBUGASSERT(buflen >= MAX_HOSTCACHE_LEN);
@@ -175,7 +179,7 @@ create_hostcache_id(const char *name,
return msnprintf(&ptr[len], 7, ":%u", port) + len;
}
-struct hostcache_prune_data {
+struct dnscache_prune_data {
time_t now;
time_t oldest; /* oldest time in cache not pruned. */
int max_age_sec;
@@ -189,16 +193,16 @@ struct hostcache_prune_data {
* cache.
*/
static int
-hostcache_entry_is_stale(void *datap, void *hc)
+dnscache_entry_is_stale(void *datap, void *hc)
{
- struct hostcache_prune_data *prune =
- (struct hostcache_prune_data *) datap;
+ struct dnscache_prune_data *prune =
+ (struct dnscache_prune_data *) datap;
struct Curl_dns_entry *dns = (struct Curl_dns_entry *) hc;
if(dns->timestamp) {
/* age in seconds */
time_t age = prune->now - dns->timestamp;
- if(age >= prune->max_age_sec)
+ if(age >= (time_t)prune->max_age_sec)
return TRUE;
if(age > prune->oldest)
prune->oldest = age;
@@ -211,10 +215,10 @@ hostcache_entry_is_stale(void *datap, void *hc)
* Returns the 'age' of the oldest still kept entry.
*/
static time_t
-hostcache_prune(struct Curl_hash *hostcache, int cache_timeout,
- time_t now)
+dnscache_prune(struct Curl_hash *hostcache, int cache_timeout,
+ time_t now)
{
- struct hostcache_prune_data user;
+ struct dnscache_prune_data user;
user.max_age_sec = cache_timeout;
user.now = now;
@@ -222,33 +226,56 @@ hostcache_prune(struct Curl_hash *hostcache, int cache_timeout,
Curl_hash_clean_with_criterium(hostcache,
(void *) &user,
- hostcache_entry_is_stale);
+ dnscache_entry_is_stale);
return user.oldest;
}
+static struct Curl_dnscache *dnscache_get(struct Curl_easy *data)
+{
+ if(data->share && data->share->specifier & (1 << CURL_LOCK_DATA_DNS))
+ return &data->share->dnscache;
+ if(data->multi)
+ return &data->multi->dnscache;
+ return NULL;
+}
+
+static void dnscache_lock(struct Curl_easy *data,
+ struct Curl_dnscache *dnscache)
+{
+ if(data->share && dnscache == &data->share->dnscache)
+ Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+}
+
+static void dnscache_unlock(struct Curl_easy *data,
+ struct Curl_dnscache *dnscache)
+{
+ if(data->share && dnscache == &data->share->dnscache)
+ Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+}
+
/*
* Library-wide function for pruning the DNS cache. This function takes and
* returns the appropriate locks.
*/
-void Curl_hostcache_prune(struct Curl_easy *data)
+void Curl_dnscache_prune(struct Curl_easy *data)
{
+ struct Curl_dnscache *dnscache = dnscache_get(data);
time_t now;
/* the timeout may be set -1 (forever) */
int timeout = data->set.dns_cache_timeout;
- if(!data->dns.hostcache)
+ if(!dnscache)
/* NULL hostcache means we cannot do it */
return;
- if(data->share)
- Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+ dnscache_lock(data, dnscache);
now = time(NULL);
do {
/* Remove outdated and unused entries from the hostcache */
- time_t oldest = hostcache_prune(data->dns.hostcache, timeout, now);
+ time_t oldest = dnscache_prune(&dnscache->entries, timeout, now);
if(oldest < INT_MAX)
timeout = (int)oldest; /* we know it fits */
@@ -258,10 +285,9 @@ void Curl_hostcache_prune(struct Curl_easy *data)
/* if the cache size is still too big, use the oldest age as new
prune limit */
} while(timeout &&
- (Curl_hash_count(data->dns.hostcache) > MAX_DNS_CACHE_SIZE));
+ (Curl_hash_count(&dnscache->entries) > MAX_DNS_CACHE_SIZE));
- if(data->share)
- Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+ dnscache_unlock(data, dnscache);
}
#ifdef USE_ALARM_TIMEOUT
@@ -274,50 +300,56 @@ static curl_simple_lock curl_jmpenv_lock;
/* lookup address, returns entry if found and not stale */
static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data,
+ struct Curl_dnscache *dnscache,
const char *hostname,
- int port)
+ int port,
+ int ip_version)
{
struct Curl_dns_entry *dns = NULL;
char entry_id[MAX_HOSTCACHE_LEN];
+ size_t entry_len;
+
+ if(!dnscache)
+ return NULL;
/* Create an entry id, based upon the hostname and port */
- size_t entry_len = create_hostcache_id(hostname, 0, port,
- entry_id, sizeof(entry_id));
+ entry_len = create_dnscache_id(hostname, 0, port,
+ entry_id, sizeof(entry_id));
/* See if it is already in our dns cache */
- dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
+ dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1);
/* No entry found in cache, check if we might have a wildcard entry */
if(!dns && data->state.wildcard_resolve) {
- entry_len = create_hostcache_id("*", 1, port, entry_id, sizeof(entry_id));
+ entry_len = create_dnscache_id("*", 1, port, entry_id, sizeof(entry_id));
/* See if it is already in our dns cache */
- dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
+ dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1);
}
if(dns && (data->set.dns_cache_timeout != -1)) {
/* See whether the returned entry is stale. Done before we release lock */
- struct hostcache_prune_data user;
+ struct dnscache_prune_data user;
user.now = time(NULL);
user.max_age_sec = data->set.dns_cache_timeout;
user.oldest = 0;
- if(hostcache_entry_is_stale(&user, dns)) {
+ if(dnscache_entry_is_stale(&user, dns)) {
infof(data, "Hostname in DNS cache was stale, zapped");
dns = NULL; /* the memory deallocation is being handled by the hash */
- Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
+ Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
}
}
/* See if the returned entry matches the required resolve mode */
- if(dns && data->conn->ip_version != CURL_IPRESOLVE_WHATEVER) {
+ if(dns && ip_version != CURL_IPRESOLVE_WHATEVER) {
int pf = PF_INET;
bool found = FALSE;
struct Curl_addrinfo *addr = dns->addr;
#ifdef PF_INET6
- if(data->conn->ip_version == CURL_IPRESOLVE_V6)
+ if(ip_version == CURL_IPRESOLVE_V6)
pf = PF_INET6;
#endif
@@ -332,14 +364,14 @@ static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data,
if(!found) {
infof(data, "Hostname in DNS cache does not have needed family, zapped");
dns = NULL; /* the memory deallocation is being handled by the hash */
- Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
+ Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
}
}
return dns;
}
/*
- * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache.
+ * Curl_dnscache_get() fetches a 'Curl_dns_entry' already in the DNS cache.
*
* Curl_resolv() checks initially and multi_runsingle() checks each time
* it discovers the handle in the state WAITRESOLVE whether the hostname
@@ -353,22 +385,21 @@ static struct Curl_dns_entry *fetch_addr(struct Curl_easy *data,
* use, or we will leak memory!
*/
struct Curl_dns_entry *
-Curl_fetch_addr(struct Curl_easy *data,
- const char *hostname,
- int port)
+Curl_dnscache_get(struct Curl_easy *data,
+ const char *hostname,
+ int port,
+ int ip_version)
{
+ struct Curl_dnscache *dnscache = dnscache_get(data);
struct Curl_dns_entry *dns = NULL;
- if(data->share)
- Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
- dns = fetch_addr(data, hostname, port);
+ dnscache_lock(data, dnscache);
+ dns = fetch_addr(data, dnscache, hostname, port, ip_version);
if(dns)
dns->refcount++; /* we use it! */
- if(data->share)
- Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+ dnscache_unlock(data, dnscache);
return dns;
}
@@ -455,34 +486,24 @@ UNITTEST CURLcode Curl_shuffle_addr(struct Curl_easy *data,
}
#endif
-/*
- * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
- *
- * When calling Curl_resolv() has resulted in a response with a returned
- * address, we call this function to store the information in the dns
- * cache etc
- *
- * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
- */
struct Curl_dns_entry *
-Curl_cache_addr(struct Curl_easy *data,
- struct Curl_addrinfo *addr,
- const char *hostname,
- size_t hostlen, /* length or zero */
- int port,
- bool permanent)
+Curl_dnscache_mk_entry(struct Curl_easy *data,
+ struct Curl_addrinfo *addr,
+ const char *hostname,
+ size_t hostlen, /* length or zero */
+ int port,
+ bool permanent)
{
- char entry_id[MAX_HOSTCACHE_LEN];
- size_t entry_len;
struct Curl_dns_entry *dns;
- struct Curl_dns_entry *dns2;
#ifndef CURL_DISABLE_SHUFFLE_DNS
/* shuffle addresses if requested */
if(data->set.dns_shuffle_addresses) {
CURLcode result = Curl_shuffle_addr(data, &addr);
- if(result)
+ if(result) {
+ Curl_freeaddrinfo(addr);
return NULL;
+ }
}
#endif
if(!hostlen)
@@ -491,13 +512,10 @@ Curl_cache_addr(struct Curl_easy *data,
/* Create a new cache entry */
dns = calloc(1, sizeof(struct Curl_dns_entry) + hostlen);
if(!dns) {
+ Curl_freeaddrinfo(addr);
return NULL;
}
- /* Create an entry id, based upon the hostname and port */
- entry_len = create_hostcache_id(hostname, hostlen, port,
- entry_id, sizeof(entry_id));
-
dns->refcount = 1; /* the cache has the first reference */
dns->addr = addr; /* this is the address(es) */
if(permanent)
@@ -511,11 +529,36 @@ Curl_cache_addr(struct Curl_easy *data,
if(hostlen)
memcpy(dns->hostname, hostname, hostlen);
+ return dns;
+}
+
+static struct Curl_dns_entry *
+dnscache_add_addr(struct Curl_easy *data,
+ struct Curl_dnscache *dnscache,
+ struct Curl_addrinfo *addr,
+ const char *hostname,
+ size_t hlen, /* length or zero */
+ int port,
+ bool permanent)
+{
+ char entry_id[MAX_HOSTCACHE_LEN];
+ size_t entry_len;
+ struct Curl_dns_entry *dns;
+ struct Curl_dns_entry *dns2;
+
+ dns = Curl_dnscache_mk_entry(data, addr, hostname, hlen, port, permanent);
+ if(!dns)
+ return NULL;
+
+ /* Create an entry id, based upon the hostname and port */
+ entry_len = create_dnscache_id(hostname, hlen, port,
+ entry_id, sizeof(entry_id));
+
/* Store the resolved data in our DNS cache. */
- dns2 = Curl_hash_add(data->dns.hostcache, entry_id, entry_len + 1,
+ dns2 = Curl_hash_add(&dnscache->entries, entry_id, entry_len + 1,
(void *)dns);
if(!dns2) {
- free(dns);
+ dnscache_entry_free(dns);
return NULL;
}
@@ -524,6 +567,30 @@ Curl_cache_addr(struct Curl_easy *data,
return dns;
}
+CURLcode Curl_dnscache_add(struct Curl_easy *data,
+ struct Curl_dns_entry *entry)
+{
+ struct Curl_dnscache *dnscache = dnscache_get(data);
+ char id[MAX_HOSTCACHE_LEN];
+ size_t idlen;
+
+ if(!dnscache)
+ return CURLE_FAILED_INIT;
+ /* Create an entry id, based upon the hostname and port */
+ idlen = create_dnscache_id(entry->hostname, 0, entry->hostport,
+ id, sizeof(id));
+
+ /* Store the resolved data in our DNS cache and up ref count */
+ dnscache_lock(data, dnscache);
+ if(!Curl_hash_add(&dnscache->entries, id, idlen + 1, (void *)entry)) {
+ dnscache_unlock(data, dnscache);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ entry->refcount++;
+ dnscache_unlock(data, dnscache);
+ return CURLE_OK;
+}
+
#ifdef USE_IPV6
/* return a static IPv6 ::1 for the name */
static struct Curl_addrinfo *get_localhost6(int port, const char *name)
@@ -541,9 +608,11 @@ static struct Curl_addrinfo *get_localhost6(int port, const char *name)
sa6.sin6_family = AF_INET6;
sa6.sin6_port = htons(port16);
sa6.sin6_flowinfo = 0;
+#ifdef HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID
sa6.sin6_scope_id = 0;
+#endif
- (void)Curl_inet_pton(AF_INET6, "::1", ipv6);
+ (void)curlx_inet_pton(AF_INET6, "::1", ipv6);
memcpy(&sa6.sin6_addr, ipv6, sizeof(ipv6));
ca->ai_flags = 0;
@@ -577,7 +646,7 @@ static struct Curl_addrinfo *get_localhost(int port, const char *name)
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(port16);
- if(Curl_inet_pton(AF_INET, "127.0.0.1", (char *)&ipv4) < 1)
+ if(curlx_inet_pton(AF_INET, "127.0.0.1", (char *)&ipv4) < 1)
return NULL;
memcpy(&sa.sin_addr, &ipv4, sizeof(ipv4));
@@ -630,7 +699,7 @@ bool Curl_ipv6works(struct Curl_easy *data)
ipv6_works = 1;
sclose(s);
}
- return (ipv6_works > 0);
+ return ipv6_works > 0;
}
}
#endif /* USE_IPV6 */
@@ -645,9 +714,9 @@ bool Curl_host_is_ipnum(const char *hostname)
#ifdef USE_IPV6
struct in6_addr in6;
#endif
- if(Curl_inet_pton(AF_INET, hostname, &in) > 0
+ if(curlx_inet_pton(AF_INET, hostname, &in) > 0
#ifdef USE_IPV6
- || Curl_inet_pton(AF_INET6, hostname, &in6) > 0
+ || curlx_inet_pton(AF_INET6, hostname, &in6) > 0
#endif
)
return TRUE;
@@ -656,15 +725,63 @@ bool Curl_host_is_ipnum(const char *hostname)
/* return TRUE if 'part' is a case insensitive tail of 'full' */
-static bool tailmatch(const char *full, const char *part)
+static bool tailmatch(const char *full, size_t flen,
+ const char *part, size_t plen)
{
- size_t plen = strlen(part);
- size_t flen = strlen(full);
if(plen > flen)
return FALSE;
return strncasecompare(part, &full[flen - plen], plen);
}
+static struct Curl_addrinfo *
+convert_ipaddr_direct(const char *hostname, int port, bool *is_ipaddr)
+{
+ struct in_addr in;
+ *is_ipaddr = FALSE;
+ /* First check if this is an IPv4 address string */
+ if(curlx_inet_pton(AF_INET, hostname, &in) > 0) {
+ /* This is a dotted IP address 123.123.123.123-style */
+ *is_ipaddr = TRUE;
+#ifdef USE_RESOLVE_ON_IPS
+ (void)port;
+ return NULL;
+#else
+ return Curl_ip2addr(AF_INET, &in, hostname, port);
+#endif
+ }
+#ifdef USE_IPV6
+ else {
+ struct in6_addr in6;
+ /* check if this is an IPv6 address string */
+ if(curlx_inet_pton(AF_INET6, hostname, &in6) > 0) {
+ /* This is an IPv6 address literal */
+ *is_ipaddr = TRUE;
+#ifdef USE_RESOLVE_ON_IPS
+ return NULL;
+#else
+ return Curl_ip2addr(AF_INET6, &in6, hostname, port);
+#endif
+ }
+ }
+#endif /* USE_IPV6 */
+ return NULL;
+}
+
+static bool can_resolve_ip_version(struct Curl_easy *data, int ip_version)
+{
+#ifdef CURLRES_IPV6
+ if(ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data))
+ return FALSE;
+#elif defined(CURLRES_IPV4)
+ (void)data;
+ if(ip_version == CURL_IPRESOLVE_V6)
+ return FALSE;
+#else
+#error either CURLRES_IPV6 or CURLRES_IPV4 need to be defined
+#endif
+ return TRUE;
+}
+
/*
* Curl_resolv() is the main name resolve function within libcurl. It resolves
* a name and returns a pointer to the entry in the 'entry' argument (if one
@@ -676,182 +793,168 @@ static bool tailmatch(const char *full, const char *part)
* done using this struct) to decrease the reference counter again.
*
* Return codes:
- *
- * CURLRESOLV_ERROR (-1) = error, no pointer
- * CURLRESOLV_RESOLVED (0) = OK, pointer provided
- * CURLRESOLV_PENDING (1) = waiting for response, no pointer
+ * CURLE_OK = success, *entry set to non-NULL
+ * CURLE_AGAIN = resolving in progress, *entry == NULL
+ * CURLE_COULDNT_RESOLVE_HOST = error, *entry == NULL
+ * CURLE_OPERATION_TIMEDOUT = timeout expired, *entry == NULL
*/
-
-enum resolve_t Curl_resolv(struct Curl_easy *data,
- const char *hostname,
- int port,
- bool allowDOH,
- struct Curl_dns_entry **entry)
+CURLcode Curl_resolv(struct Curl_easy *data,
+ const char *hostname,
+ int port,
+ int ip_version,
+ bool allowDOH,
+ struct Curl_dns_entry **entry)
{
+ struct Curl_dnscache *dnscache = dnscache_get(data);
struct Curl_dns_entry *dns = NULL;
- CURLcode result;
- enum resolve_t rc = CURLRESOLV_ERROR; /* default to failure */
- struct connectdata *conn = data->conn;
+ struct Curl_addrinfo *addr = NULL;
+ int respwait = 0;
+ bool is_ipaddr;
+ size_t hostname_len;
+
+#ifndef CURL_DISABLE_DOH
+ data->conn->bits.doh = FALSE; /* default is not */
+#else
+ (void)allowDOH;
+#endif
+ if(!dnscache)
+ goto error;
+
/* We should intentionally error and not resolve .onion TLDs */
- size_t hostname_len = strlen(hostname);
+ hostname_len = strlen(hostname);
if(hostname_len >= 7 &&
(curl_strequal(&hostname[hostname_len - 6], ".onion") ||
curl_strequal(&hostname[hostname_len - 7], ".onion."))) {
failf(data, "Not resolving .onion address (RFC 7686)");
- return CURLRESOLV_ERROR;
+ goto error;
}
- *entry = NULL;
-#ifndef CURL_DISABLE_DOH
- conn->bits.doh = FALSE; /* default is not */
-#else
- (void)allowDOH;
-#endif
-
- if(data->share)
- Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
- dns = fetch_addr(data, hostname, port);
+ /* Let's check our DNS cache first */
+ dnscache_lock(data, dnscache);
+ dns = fetch_addr(data, dnscache, hostname, port, ip_version);
+ if(dns)
+ dns->refcount++; /* we pass out the reference. */
+ dnscache_unlock(data, dnscache);
if(dns) {
infof(data, "Hostname %s was found in DNS cache", hostname);
- dns->refcount++; /* we use it! */
- rc = CURLRESOLV_RESOLVED;
+ goto out;
}
- if(data->share)
- Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
-
- if(!dns) {
- /* The entry was not in the cache. Resolve it to IP address */
-
- struct Curl_addrinfo *addr = NULL;
- int respwait = 0;
-#if !defined(CURL_DISABLE_DOH) || !defined(USE_RESOLVE_ON_IPS)
- struct in_addr in;
-#endif
-#ifndef CURL_DISABLE_DOH
-#ifndef USE_RESOLVE_ON_IPS
- const
-#endif
- bool ipnum = FALSE;
+ /* No luck, we need to resolve hostname. Notify user callback. */
+ if(data->set.resolver_start) {
+ void *resolver = NULL;
+ int st;
+#ifdef CURLRES_ASYNCH
+ if(Curl_async_get_impl(data, &resolver))
+ goto error;
#endif
+ Curl_set_in_callback(data, TRUE);
+ st = data->set.resolver_start(resolver, NULL,
+ data->set.resolver_start_client);
+ Curl_set_in_callback(data, FALSE);
+ if(st)
+ goto error;
+ }
- /* notify the resolver start callback */
- if(data->set.resolver_start) {
- int st;
- Curl_set_in_callback(data, TRUE);
- st = data->set.resolver_start(
-#ifdef USE_CURL_ASYNC
- data->state.async.resolver,
-#else
- NULL,
-#endif
- NULL,
- data->set.resolver_start_client);
- Curl_set_in_callback(data, FALSE);
- if(st)
- return CURLRESOLV_ERROR;
- }
+ /* shortcut literal IP addresses, if we are not told to resolve them. */
+ addr = convert_ipaddr_direct(hostname, port, &is_ipaddr);
+ if(addr)
+ goto out;
#ifndef USE_RESOLVE_ON_IPS
- /* First check if this is an IPv4 address string */
- if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
- /* This is a dotted IP address 123.123.123.123-style */
- addr = Curl_ip2addr(AF_INET, &in, hostname, port);
- if(!addr)
- return CURLRESOLV_ERROR;
- }
-#ifdef USE_IPV6
- else {
- struct in6_addr in6;
- /* check if this is an IPv6 address string */
- if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) {
- /* This is an IPv6 address literal */
- addr = Curl_ip2addr(AF_INET6, &in6, hostname, port);
- if(!addr)
- return CURLRESOLV_ERROR;
- }
- }
-#endif /* USE_IPV6 */
-
-#else /* if USE_RESOLVE_ON_IPS */
-#ifndef CURL_DISABLE_DOH
- /* First check if this is an IPv4 address string */
- if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
- /* This is a dotted IP address 123.123.123.123-style */
- ipnum = TRUE;
-#ifdef USE_IPV6
- else {
- struct in6_addr in6;
- /* check if this is an IPv6 address string */
- if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0)
- /* This is an IPv6 address literal */
- ipnum = TRUE;
- }
-#endif /* USE_IPV6 */
-#endif /* CURL_DISABLE_DOH */
-
-#endif /* !USE_RESOLVE_ON_IPS */
+ /* allowed to convert, hostname is IP address, then NULL means error */
+ if(is_ipaddr)
+ goto error;
+#endif
- if(!addr) {
- if(conn->ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data))
- return CURLRESOLV_ERROR;
+ /* Really need a resolver for hostname. */
+ if(ip_version == CURL_IPRESOLVE_V6 && !Curl_ipv6works(data))
+ goto error;
- if(strcasecompare(hostname, "localhost") ||
- tailmatch(hostname, ".localhost"))
- addr = get_localhost(port, hostname);
+ if(!is_ipaddr &&
+ (strcasecompare(hostname, "localhost") ||
+ strcasecompare(hostname, "localhost.") ||
+ tailmatch(hostname, hostname_len, STRCONST(".localhost")) ||
+ tailmatch(hostname, hostname_len, STRCONST(".localhost.")))) {
+ addr = get_localhost(port, hostname);
+ }
#ifndef CURL_DISABLE_DOH
- else if(allowDOH && data->set.doh && !ipnum)
- addr = Curl_doh(data, hostname, port, &respwait);
+ else if(!is_ipaddr && allowDOH && data->set.doh) {
+ addr = Curl_doh(data, hostname, port, ip_version, &respwait);
+ }
#endif
- else {
- /* Check what IP specifics the app has requested and if we can provide
- * it. If not, bail out. */
- if(!Curl_ipvalid(data, conn))
- return CURLRESOLV_ERROR;
- /* If Curl_getaddrinfo() returns NULL, 'respwait' might be set to a
- non-zero value indicating that we need to wait for the response to
- the resolve call */
- addr = Curl_getaddrinfo(data, hostname, port, &respwait);
- }
- }
- if(!addr) {
- if(respwait) {
- /* the response to our resolve call will come asynchronously at
- a later time, good or bad */
- /* First, check that we have not received the info by now */
- result = Curl_resolv_check(data, &dns);
- if(result) /* error detected */
- return CURLRESOLV_ERROR;
- if(dns)
- rc = CURLRESOLV_RESOLVED; /* pointer provided */
- else
- rc = CURLRESOLV_PENDING; /* no info yet */
- }
- }
- else {
- if(data->share)
- Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
- /* we got a response, store it in the cache */
- dns = Curl_cache_addr(data, addr, hostname, 0, port, FALSE);
+ else {
+ /* Can we provide the requested IP specifics in resolving? */
+ if(!can_resolve_ip_version(data, ip_version))
+ goto error;
- if(data->share)
- Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+#ifdef CURLRES_ASYNCH
+ addr = Curl_async_getaddrinfo(data, hostname, port, ip_version, &respwait);
+#else
+ respwait = 0; /* no async waiting here */
+ addr = Curl_sync_getaddrinfo(data, hostname, port, ip_version);
+#endif
+ }
- if(!dns)
- /* returned failure, bail out nicely */
- Curl_freeaddrinfo(addr);
- else {
- rc = CURLRESOLV_RESOLVED;
- show_resolve_info(data, dns);
- }
+out:
+ /* We either have found a `dns` or looked up the `addr`
+ * or `respwait` is set for an async operation.
+ * Everything else is a failure to resolve. */
+ if(dns) {
+ *entry = dns;
+ return CURLE_OK;
+ }
+ else if(addr) {
+ /* we got a response, create a dns entry, add to cache, return */
+ dns = Curl_dnscache_mk_entry(data, addr, hostname, 0, port, FALSE);
+ if(!dns)
+ goto error;
+ if(Curl_dnscache_add(data, dns))
+ goto error;
+ show_resolve_info(data, dns);
+ *entry = dns;
+ return CURLE_OK;
+ }
+ else if(respwait) {
+ if(!Curl_resolv_check(data, &dns)) {
+ *entry = dns;
+ return dns ? CURLE_OK : CURLE_AGAIN;
}
}
+error:
+ if(dns)
+ Curl_resolv_unlink(data, &dns);
+ *entry = NULL;
+ Curl_async_shutdown(data);
+ return CURLE_COULDNT_RESOLVE_HOST;
+}
- *entry = dns;
+CURLcode Curl_resolv_blocking(struct Curl_easy *data,
+ const char *hostname,
+ int port,
+ int ip_version,
+ struct Curl_dns_entry **dnsentry)
+{
+ CURLcode result;
- return rc;
+ *dnsentry = NULL;
+ result = Curl_resolv(data, hostname, port, ip_version, FALSE, dnsentry);
+ switch(result) {
+ case CURLE_OK:
+ DEBUGASSERT(*dnsentry);
+ return CURLE_OK;
+ case CURLE_AGAIN:
+ DEBUGASSERT(!*dnsentry);
+ result = Curl_async_await(data, dnsentry);
+ if(result || !*dnsentry) {
+ /* close the connection, since we cannot return failure here without
+ cleaning up this connection properly. */
+ connclose(data->conn, "async resolve failed");
+ }
+ return result;
+ default:
+ return result;
+ }
}
#ifdef USE_ALARM_TIMEOUT
@@ -883,18 +986,18 @@ void alarmfunc(int sig)
* is ignored.
*
* Return codes:
- *
- * CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired
- * CURLRESOLV_ERROR (-1) = error, no pointer
- * CURLRESOLV_RESOLVED (0) = OK, pointer provided
- * CURLRESOLV_PENDING (1) = waiting for response, no pointer
+ * CURLE_OK = success, *entry set to non-NULL
+ * CURLE_AGAIN = resolving in progress, *entry == NULL
+ * CURLE_COULDNT_RESOLVE_HOST = error, *entry == NULL
+ * CURLE_OPERATION_TIMEDOUT = timeout expired, *entry == NULL
*/
-enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
- const char *hostname,
- int port,
- struct Curl_dns_entry **entry,
- timediff_t timeoutms)
+CURLcode Curl_resolv_timeout(struct Curl_easy *data,
+ const char *hostname,
+ int port,
+ int ip_version,
+ struct Curl_dns_entry **entry,
+ timediff_t timeoutms)
{
#ifdef USE_ALARM_TIMEOUT
#ifdef HAVE_SIGACTION
@@ -909,13 +1012,13 @@ enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
volatile long timeout;
volatile unsigned int prev_alarm = 0;
#endif /* USE_ALARM_TIMEOUT */
- enum resolve_t rc;
+ CURLcode result;
*entry = NULL;
if(timeoutms < 0)
/* got an already expired timeout */
- return CURLRESOLV_TIMEDOUT;
+ return CURLE_OPERATION_TIMEDOUT;
#ifdef USE_ALARM_TIMEOUT
if(data->set.no_signal)
@@ -924,9 +1027,14 @@ enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
else
timeout = (timeoutms > LONG_MAX) ? LONG_MAX : (long)timeoutms;
- if(!timeout)
- /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */
- return Curl_resolv(data, hostname, port, TRUE, entry);
+ if(!timeout
+#ifndef CURL_DISABLE_DOH
+ || data->set.doh
+#endif
+ )
+ /* USE_ALARM_TIMEOUT defined, but no timeout actually requested or resolve
+ done using DoH */
+ return Curl_resolv(data, hostname, port, ip_version, TRUE, entry);
if(timeout < 1000) {
/* The alarm() function only provides integer second resolution, so if
@@ -934,7 +1042,7 @@ enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
failf(data,
"remaining timeout of %ld too small to resolve via SIGALRM method",
timeout);
- return CURLRESOLV_TIMEDOUT;
+ return CURLE_OPERATION_TIMEDOUT;
}
/* This allows us to time-out from the name resolver, as the timeout
will generate a signal and we will siglongjmp() from that here.
@@ -947,7 +1055,7 @@ enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
if(sigsetjmp(curl_jmpenv, 1)) {
/* this is coming from a siglongjmp() after an alarm signal */
failf(data, "name lookup timed out");
- rc = CURLRESOLV_ERROR;
+ result = CURLE_OPERATION_TIMEDOUT;
goto clean_up;
}
else {
@@ -978,19 +1086,19 @@ enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
prev_alarm = alarm(curlx_sltoui(timeout/1000L));
}
-#else
+#else /* USE_ALARM_TIMEOUT */
#ifndef CURLRES_ASYNCH
if(timeoutms)
infof(data, "timeout on name lookup is not supported");
#else
(void)timeoutms; /* timeoutms not used with an async resolver */
#endif
-#endif /* USE_ALARM_TIMEOUT */
+#endif /* else USE_ALARM_TIMEOUT */
/* Perform the actual name resolution. This might be interrupted by an
* alarm if it takes too long.
*/
- rc = Curl_resolv(data, hostname, port, TRUE, entry);
+ result = Curl_resolv(data, hostname, port, ip_version, TRUE, entry);
#ifdef USE_ALARM_TIMEOUT
clean_up:
@@ -1018,7 +1126,7 @@ clean_up:
the time we spent until now! */
if(prev_alarm) {
/* there was an alarm() set before us, now put it back */
- timediff_t elapsed_secs = Curl_timediff(Curl_now(),
+ timediff_t elapsed_secs = curlx_timediff(curlx_now(),
data->conn->created) / 1000;
/* the alarm period is counted in even number of seconds */
@@ -1031,7 +1139,7 @@ clean_up:
will not, and zero would be to switch it off so we never set it to
less than 1! */
alarm(1);
- rc = CURLRESOLV_TIMEDOUT;
+ result = CURLE_OPERATION_TIMEDOUT;
failf(data, "Previous alarm fired off");
}
else
@@ -1039,7 +1147,19 @@ clean_up:
}
#endif /* USE_ALARM_TIMEOUT */
- return rc;
+ return result;
+}
+
+static void dnscache_entry_free(struct Curl_dns_entry *dns)
+{
+ Curl_freeaddrinfo(dns->addr);
+#ifdef USE_HTTPSRR
+ if(dns->hinfo) {
+ Curl_httpsrr_cleanup(dns->hinfo);
+ free(dns->hinfo);
+ }
+#endif
+ free(dns);
}
/*
@@ -1051,118 +1171,85 @@ clean_up:
*/
void Curl_resolv_unlink(struct Curl_easy *data, struct Curl_dns_entry **pdns)
{
- struct Curl_dns_entry *dns = *pdns;
- *pdns = NULL;
- if(data && data->share)
- Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
- hostcache_unlink_entry(dns);
-
- if(data && data->share)
- Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+ if(*pdns) {
+ struct Curl_dnscache *dnscache = dnscache_get(data);
+ struct Curl_dns_entry *dns = *pdns;
+ *pdns = NULL;
+ dnscache_lock(data, dnscache);
+ dns->refcount--;
+ if(dns->refcount == 0)
+ dnscache_entry_free(dns);
+ dnscache_unlock(data, dnscache);
+ }
}
-/*
- * File-internal: release cache dns entry reference, free if inuse drops to 0
- */
-static void hostcache_unlink_entry(void *entry)
+static void dnscache_entry_dtor(void *entry)
{
struct Curl_dns_entry *dns = (struct Curl_dns_entry *) entry;
DEBUGASSERT(dns && (dns->refcount > 0));
-
dns->refcount--;
- if(dns->refcount == 0) {
- Curl_freeaddrinfo(dns->addr);
-#ifdef USE_HTTPSRR
- if(dns->hinfo) {
- if(dns->hinfo->target)
- free(dns->hinfo->target);
- if(dns->hinfo->alpns)
- free(dns->hinfo->alpns);
- if(dns->hinfo->ipv4hints)
- free(dns->hinfo->ipv4hints);
- if(dns->hinfo->echconfiglist)
- free(dns->hinfo->echconfiglist);
- if(dns->hinfo->ipv6hints)
- free(dns->hinfo->ipv6hints);
- if(dns->hinfo->val)
- free(dns->hinfo->val);
- free(dns->hinfo);
- }
-#endif
- free(dns);
- }
+ if(dns->refcount == 0)
+ dnscache_entry_free(dns);
}
/*
- * Curl_init_dnscache() inits a new DNS cache.
+ * Curl_dnscache_init() inits a new DNS cache.
*/
-void Curl_init_dnscache(struct Curl_hash *hash, size_t size)
+void Curl_dnscache_init(struct Curl_dnscache *dns, size_t size)
{
- Curl_hash_init(hash, size, Curl_hash_str, Curl_str_key_compare,
- hostcache_unlink_entry);
+ Curl_hash_init(&dns->entries, size, Curl_hash_str, curlx_str_key_compare,
+ dnscache_entry_dtor);
}
-/*
- * Curl_hostcache_clean()
- *
- * This _can_ be called with 'data' == NULL but then of course no locking
- * can be done!
- */
-
-void Curl_hostcache_clean(struct Curl_easy *data,
- struct Curl_hash *hash)
+void Curl_dnscache_destroy(struct Curl_dnscache *dns)
{
- if(data && data->share)
- Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
-
- Curl_hash_clean(hash);
-
- if(data && data->share)
- Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+ Curl_hash_destroy(&dns->entries);
}
-
CURLcode Curl_loadhostpairs(struct Curl_easy *data)
{
+ struct Curl_dnscache *dnscache = dnscache_get(data);
struct curl_slist *hostp;
- char *host_end;
+
+ if(!dnscache)
+ return CURLE_FAILED_INIT;
/* Default is no wildcard found */
data->state.wildcard_resolve = FALSE;
for(hostp = data->state.resolve; hostp; hostp = hostp->next) {
char entry_id[MAX_HOSTCACHE_LEN];
- if(!hostp->data)
+ const char *host = hostp->data;
+ struct Curl_str source;
+ if(!host)
continue;
- if(hostp->data[0] == '-') {
- unsigned long num = 0;
+ if(*host == '-') {
+ curl_off_t num = 0;
size_t entry_len;
- size_t hlen = 0;
- host_end = strchr(&hostp->data[1], ':');
-
- if(host_end) {
- hlen = host_end - &hostp->data[1];
- num = strtoul(++host_end, NULL, 10);
- if(!hlen || (num > 0xffff))
- host_end = NULL;
+ host++;
+ if(!curlx_str_single(&host, '[')) {
+ if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') ||
+ curlx_str_single(&host, ']') ||
+ curlx_str_single(&host, ':'))
+ continue;
}
- if(!host_end) {
- infof(data, "Bad syntax CURLOPT_RESOLVE removal entry '%s'",
- hostp->data);
- continue;
+ else {
+ if(curlx_str_until(&host, &source, 4096, ':') ||
+ curlx_str_single(&host, ':')) {
+ continue;
+ }
}
- /* Create an entry id, based upon the hostname and port */
- entry_len = create_hostcache_id(&hostp->data[1], hlen, (int)num,
- entry_id, sizeof(entry_id));
- if(data->share)
- Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
- /* delete entry, ignore if it did not exist */
- Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
-
- if(data->share)
- Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+ if(!curlx_str_number(&host, &num, 0xffff)) {
+ /* Create an entry id, based upon the hostname and port */
+ entry_len = create_dnscache_id(curlx_str(&source),
+ curlx_strlen(&source), (int)num,
+ entry_id, sizeof(entry_id));
+ dnscache_lock(data, dnscache);
+ /* delete entry, ignore if it did not exist */
+ Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
+ dnscache_unlock(data, dnscache);
+ }
}
else {
struct Curl_dns_entry *dns;
@@ -1170,75 +1257,69 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
size_t entry_len;
char address[64];
#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
- char *addresses = NULL;
+ const char *addresses = NULL;
#endif
- char *addr_begin;
- char *addr_end;
- char *port_ptr;
- int port = 0;
- char *end_ptr;
+ curl_off_t port = 0;
bool permanent = TRUE;
- unsigned long tmp_port;
bool error = TRUE;
- char *host_begin = hostp->data;
- size_t hlen = 0;
- if(host_begin[0] == '+') {
- host_begin++;
+ if(*host == '+') {
+ host++;
permanent = FALSE;
}
- host_end = strchr(host_begin, ':');
- if(!host_end)
- goto err;
- hlen = host_end - host_begin;
-
- port_ptr = host_end + 1;
- tmp_port = strtoul(port_ptr, &end_ptr, 10);
- if(tmp_port > USHRT_MAX || end_ptr == port_ptr || *end_ptr != ':')
+ if(!curlx_str_single(&host, '[')) {
+ if(curlx_str_until(&host, &source, MAX_IPADR_LEN, ']') ||
+ curlx_str_single(&host, ']'))
+ continue;
+ }
+ else {
+ if(curlx_str_until(&host, &source, 4096, ':'))
+ continue;
+ }
+ if(curlx_str_single(&host, ':') ||
+ curlx_str_number(&host, &port, 0xffff) ||
+ curlx_str_single(&host, ':'))
goto err;
- port = (int)tmp_port;
#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
- addresses = end_ptr + 1;
+ addresses = host;
#endif
- while(*end_ptr) {
- size_t alen;
+ /* start the address section */
+ while(*host) {
+ struct Curl_str target;
struct Curl_addrinfo *ai;
- addr_begin = end_ptr + 1;
- addr_end = strchr(addr_begin, ',');
- if(!addr_end)
- addr_end = addr_begin + strlen(addr_begin);
- end_ptr = addr_end;
-
- /* allow IP(v6) address within [brackets] */
- if(*addr_begin == '[') {
- if(addr_end == addr_begin || *(addr_end - 1) != ']')
+ if(!curlx_str_single(&host, '[')) {
+ if(curlx_str_until(&host, &target, MAX_IPADR_LEN, ']') ||
+ curlx_str_single(&host, ']'))
goto err;
- ++addr_begin;
- --addr_end;
}
-
- alen = addr_end - addr_begin;
- if(!alen)
- continue;
-
- if(alen >= sizeof(address))
- goto err;
-
- memcpy(address, addr_begin, alen);
- address[alen] = '\0';
-
+ else {
+ if(curlx_str_until(&host, &target, 4096, ',')) {
+ if(curlx_str_single(&host, ','))
+ goto err;
+ /* survive nothing but just a comma */
+ continue;
+ }
+ }
#ifndef USE_IPV6
- if(strchr(address, ':')) {
+ if(memchr(target.str, ':', target.len)) {
infof(data, "Ignoring resolve address '%s', missing IPv6 support.",
address);
+ if(curlx_str_single(&host, ','))
+ goto err;
continue;
}
#endif
- ai = Curl_str2addr(address, port);
+ if(curlx_strlen(&target) >= sizeof(address))
+ goto err;
+
+ memcpy(address, curlx_str(&target), curlx_strlen(&target));
+ address[curlx_strlen(&target)] = '\0';
+
+ ai = Curl_str2addr(address, (int)port);
if(!ai) {
infof(data, "Resolve address '%s' found illegal", address);
goto err;
@@ -1251,6 +1332,8 @@ CURLcode Curl_loadhostpairs(struct Curl_easy *data)
else {
head = tail = ai;
}
+ if(curlx_str_single(&host, ','))
+ break;
}
if(!head)
@@ -1266,18 +1349,19 @@ err:
}
/* Create an entry id, based upon the hostname and port */
- entry_len = create_hostcache_id(host_begin, hlen, port,
- entry_id, sizeof(entry_id));
+ entry_len = create_dnscache_id(curlx_str(&source), curlx_strlen(&source),
+ (int)port,
+ entry_id, sizeof(entry_id));
- if(data->share)
- Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
+ dnscache_lock(data, dnscache);
/* See if it is already in our dns cache */
- dns = Curl_hash_pick(data->dns.hostcache, entry_id, entry_len + 1);
+ dns = Curl_hash_pick(&dnscache->entries, entry_id, entry_len + 1);
if(dns) {
- infof(data, "RESOLVE %.*s:%d - old addresses discarded",
- (int)hlen, host_begin, port);
+ infof(data, "RESOLVE %.*s:%" CURL_FORMAT_CURL_OFF_T
+ " - old addresses discarded", (int)curlx_strlen(&source),
+ curlx_str(&source), port);
/* delete old entry, there are two reasons for this
1. old entry may have different addresses.
2. even if entry with correct addresses is already in the cache,
@@ -1289,33 +1373,33 @@ err:
4. when adding a non-permanent entry, we want it to get a "fresh"
timeout that starts _now_. */
- Curl_hash_delete(data->dns.hostcache, entry_id, entry_len + 1);
+ Curl_hash_delete(&dnscache->entries, entry_id, entry_len + 1);
}
/* put this new host in the cache */
- dns = Curl_cache_addr(data, head, host_begin, hlen, port, permanent);
+ dns = dnscache_add_addr(data, dnscache, head, curlx_str(&source),
+ curlx_strlen(&source), (int)port, permanent);
if(dns) {
/* release the returned reference; the cache itself will keep the
* entry alive: */
dns->refcount--;
}
- if(data->share)
- Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
+ dnscache_unlock(data, dnscache);
- if(!dns) {
- Curl_freeaddrinfo(head);
+ if(!dns)
return CURLE_OUT_OF_MEMORY;
- }
+
#ifndef CURL_DISABLE_VERBOSE_STRINGS
- infof(data, "Added %.*s:%d:%s to DNS cache%s",
- (int)hlen, host_begin, port, addresses,
+ infof(data, "Added %.*s:%" CURL_FORMAT_CURL_OFF_T ":%s to DNS cache%s",
+ (int)curlx_strlen(&source), curlx_str(&source), port, addresses,
permanent ? "" : " (non-permanent)");
#endif
/* Wildcard hostname */
- if((hlen == 1) && (host_begin[0] == '*')) {
- infof(data, "RESOLVE *:%d using wildcard", port);
+ if(curlx_str_casecompare(&source, "*")) {
+ infof(data, "RESOLVE *:%" CURL_FORMAT_CURL_OFF_T " using wildcard",
+ port);
data->state.wildcard_resolve = TRUE;
}
}
@@ -1349,9 +1433,9 @@ static void show_resolve_info(struct Curl_easy *data,
infof(data, "Host %s:%d was resolved.",
(dns->hostname[0] ? dns->hostname : "(none)"), dns->hostport);
- Curl_dyn_init(&out[0], 1024);
+ curlx_dyn_init(&out[0], 1024);
#ifdef CURLRES_IPV6
- Curl_dyn_init(&out[1], 1024);
+ curlx_dyn_init(&out[1], 1024);
#endif
while(a) {
@@ -1363,10 +1447,10 @@ static void show_resolve_info(struct Curl_easy *data,
char buf[MAX_IPADR_LEN];
struct dynbuf *d = &out[(a->ai_family != PF_INET)];
Curl_printable_address(a, buf, sizeof(buf));
- if(Curl_dyn_len(d))
- result = Curl_dyn_addn(d, ", ", 2);
+ if(curlx_dyn_len(d))
+ result = curlx_dyn_addn(d, ", ", 2);
if(!result)
- result = Curl_dyn_add(d, buf);
+ result = curlx_dyn_add(d, buf);
if(result) {
infof(data, "too many IP, cannot show");
goto fail;
@@ -1377,38 +1461,55 @@ static void show_resolve_info(struct Curl_easy *data,
#ifdef CURLRES_IPV6
infof(data, "IPv6: %s",
- (Curl_dyn_len(&out[1]) ? Curl_dyn_ptr(&out[1]) : "(none)"));
+ (curlx_dyn_len(&out[1]) ? curlx_dyn_ptr(&out[1]) : "(none)"));
#endif
infof(data, "IPv4: %s",
- (Curl_dyn_len(&out[0]) ? Curl_dyn_ptr(&out[0]) : "(none)"));
+ (curlx_dyn_len(&out[0]) ? curlx_dyn_ptr(&out[0]) : "(none)"));
fail:
- Curl_dyn_free(&out[0]);
+ curlx_dyn_free(&out[0]);
#ifdef CURLRES_IPV6
- Curl_dyn_free(&out[1]);
+ curlx_dyn_free(&out[1]);
#endif
}
#endif
+#ifdef USE_CURL_ASYNC
CURLcode Curl_resolv_check(struct Curl_easy *data,
struct Curl_dns_entry **dns)
{
CURLcode result;
-#if defined(CURL_DISABLE_DOH) && !defined(CURLRES_ASYNCH)
- (void)data;
- (void)dns;
-#endif
+
+ /* If async resolving is ongoing, this must be set */
+ if(!data->state.async.hostname)
+ return CURLE_FAILED_INIT;
+
+ /* check if we have the name resolved by now (from someone else) */
+ *dns = Curl_dnscache_get(data, data->state.async.hostname,
+ data->state.async.port,
+ data->state.async.ip_version);
+ if(*dns) {
+ /* Tell a possibly async resolver we no longer need the results. */
+ infof(data, "Hostname '%s' was found in DNS cache",
+ data->state.async.hostname);
+ Curl_async_shutdown(data);
+ data->state.async.dns = *dns;
+ data->state.async.done = TRUE;
+ return CURLE_OK;
+ }
+
#ifndef CURL_DISABLE_DOH
if(data->conn->bits.doh) {
result = Curl_doh_is_resolved(data, dns);
}
else
#endif
- result = Curl_resolver_is_resolved(data, dns);
+ result = Curl_async_is_resolved(data, dns);
if(*dns)
show_resolve_info(data, *dns);
return result;
}
+#endif
int Curl_resolv_getsock(struct Curl_easy *data,
curl_socket_t *socks)
@@ -1420,7 +1521,7 @@ int Curl_resolv_getsock(struct Curl_easy *data,
sockets */
return GETSOCK_BLANK;
#endif
- return Curl_resolver_getsock(data, socks);
+ return Curl_async_getsock(data, socks);
#else
(void)data;
(void)socks;
@@ -1433,23 +1534,25 @@ int Curl_resolv_getsock(struct Curl_easy *data,
Note: this function disconnects and frees the conn data in case of
resolve failure */
-CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_done)
+CURLcode Curl_once_resolved(struct Curl_easy *data,
+ struct Curl_dns_entry *dns,
+ bool *protocol_done)
{
CURLcode result;
struct connectdata *conn = data->conn;
#ifdef USE_CURL_ASYNC
if(data->state.async.dns) {
- conn->dns_entry = data->state.async.dns;
+ DEBUGASSERT(data->state.async.dns == dns);
data->state.async.dns = NULL;
}
#endif
- result = Curl_setup_conn(data, protocol_done);
+ result = Curl_setup_conn(data, dns, protocol_done);
if(result) {
Curl_detach_connection(data);
- Curl_cpool_disconnect(data, conn, TRUE);
+ Curl_conn_terminate(data, conn, TRUE);
}
return result;
}
@@ -1462,25 +1565,21 @@ CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_done)
#ifdef USE_CURL_ASYNC
CURLcode Curl_resolver_error(struct Curl_easy *data)
{
- const char *host_or_proxy;
- CURLcode result;
+ struct connectdata *conn = data->conn;
+ const char *host_or_proxy = "host";
+ const char *name = conn->host.dispname;
+ CURLcode result = CURLE_COULDNT_RESOLVE_HOST;
#ifndef CURL_DISABLE_PROXY
- struct connectdata *conn = data->conn;
- if(conn->bits.httpproxy) {
+ if(conn->bits.proxy) {
host_or_proxy = "proxy";
result = CURLE_COULDNT_RESOLVE_PROXY;
+ name = conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname :
+ conn->http_proxy.host.dispname;
}
- else
#endif
- {
- host_or_proxy = "host";
- result = CURLE_COULDNT_RESOLVE_HOST;
- }
-
- failf(data, "Could not resolve %s: %s", host_or_proxy,
- data->state.async.hostname);
+ failf(data, "Could not resolve %s: %s", host_or_proxy, name);
return result;
}
#endif /* USE_CURL_ASYNC */
diff --git a/libs/libcurl/src/hostip.h b/libs/libcurl/src/hostip.h
index af0bde47f8..331a4a57d8 100644
--- a/libs/libcurl/src/hostip.h
+++ b/libs/libcurl/src/hostip.h
@@ -27,8 +27,9 @@
#include "curl_setup.h"
#include "hash.h"
#include "curl_addrinfo.h"
-#include "timeval.h" /* for timediff_t */
+#include "curlx/timeval.h" /* for timediff_t */
#include "asyn.h"
+#include "httpsrr.h"
#include <setjmp.h>
@@ -53,44 +54,12 @@ struct hostent;
struct Curl_easy;
struct connectdata;
-/*
- * Curl_global_host_cache_init() initializes and sets up a global DNS cache.
- * Global DNS cache is general badness. Do not use. This will be removed in
- * a future version. Use the share interface instead!
- *
- * Returns a struct Curl_hash pointer on success, NULL on failure.
- */
-struct Curl_hash *Curl_global_host_cache_init(void);
-
-#ifdef USE_HTTPSRR
-
-#define CURL_MAXLEN_host_name 253
-
-struct Curl_https_rrinfo {
- size_t len; /* raw encoded length */
- unsigned char *val; /* raw encoded octets */
- /*
- * fields from HTTPS RR, with the mandatory fields
- * first (priority, target), then the others in the
- * order of the keytag numbers defined at
- * https://datatracker.ietf.org/doc/html/rfc9460#section-14.3.2
- */
- uint16_t priority;
- char *target;
- char *alpns; /* keytag = 1 */
- bool no_def_alpn; /* keytag = 2 */
- /*
- * we do not support ports (keytag = 3) as we do not support
- * port-switching yet
- */
- unsigned char *ipv4hints; /* keytag = 4 */
- size_t ipv4hints_len;
- unsigned char *echconfiglist; /* keytag = 5 */
- size_t echconfiglist_len;
- unsigned char *ipv6hints; /* keytag = 6 */
- size_t ipv6hints_len;
+enum alpnid {
+ ALPN_none = 0,
+ ALPN_h1 = CURLALTSVC_H1,
+ ALPN_h2 = CURLALTSVC_H2,
+ ALPN_h3 = CURLALTSVC_H3
};
-#endif
struct Curl_dns_entry {
struct Curl_addrinfo *addr;
@@ -107,6 +76,10 @@ struct Curl_dns_entry {
char hostname[1];
};
+struct Curl_dnscache {
+ struct Curl_hash entries;
+};
+
bool Curl_host_is_ipnum(const char *hostname);
/*
@@ -116,22 +89,24 @@ bool Curl_host_is_ipnum(const char *hostname);
* The returned data *MUST* be "released" with Curl_resolv_unlink() after
* use, or we will leak memory!
*/
-/* return codes */
-enum resolve_t {
- CURLRESOLV_TIMEDOUT = -2,
- CURLRESOLV_ERROR = -1,
- CURLRESOLV_RESOLVED = 0,
- CURLRESOLV_PENDING = 1
-};
-enum resolve_t Curl_resolv(struct Curl_easy *data,
- const char *hostname,
- int port,
- bool allowDOH,
- struct Curl_dns_entry **dnsentry);
-enum resolve_t Curl_resolv_timeout(struct Curl_easy *data,
- const char *hostname, int port,
- struct Curl_dns_entry **dnsentry,
- timediff_t timeoutms);
+CURLcode Curl_resolv(struct Curl_easy *data,
+ const char *hostname,
+ int port,
+ int ip_version,
+ bool allowDOH,
+ struct Curl_dns_entry **dnsentry);
+
+CURLcode Curl_resolv_blocking(struct Curl_easy *data,
+ const char *hostname,
+ int port,
+ int ip_version,
+ struct Curl_dns_entry **dnsentry);
+
+CURLcode Curl_resolv_timeout(struct Curl_easy *data,
+ const char *hostname, int port,
+ int ip_version,
+ struct Curl_dns_entry **dnsentry,
+ timediff_t timeoutms);
#ifdef USE_IPV6
/*
@@ -142,49 +117,25 @@ bool Curl_ipv6works(struct Curl_easy *data);
#define Curl_ipv6works(x) FALSE
#endif
-/*
- * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've
- * been set and returns TRUE if they are OK.
- */
-bool Curl_ipvalid(struct Curl_easy *data, struct connectdata *conn);
-
-
-/*
- * Curl_getaddrinfo() is the generic low-level name resolve API within this
- * source file. There are several versions of this function - for different
- * name resolve layers (selected at build-time). They all take this same set
- * of arguments
- */
-struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data,
- const char *hostname,
- int port,
- int *waitp);
-
/* unlink a dns entry, potentially shared with a cache */
void Curl_resolv_unlink(struct Curl_easy *data,
struct Curl_dns_entry **pdns);
/* init a new dns cache */
-void Curl_init_dnscache(struct Curl_hash *hash, size_t hashsize);
+void Curl_dnscache_init(struct Curl_dnscache *dns, size_t hashsize);
+
+void Curl_dnscache_destroy(struct Curl_dnscache *dns);
/* prune old entries from the DNS cache */
-void Curl_hostcache_prune(struct Curl_easy *data);
+void Curl_dnscache_prune(struct Curl_easy *data);
/* IPv4 threadsafe resolve function used for synch and asynch builds */
struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname, int port);
-CURLcode Curl_once_resolved(struct Curl_easy *data, bool *protocol_connect);
-
-/*
- * Curl_addrinfo_callback() is used when we build with any asynch specialty.
- * Handles end of async request processing. Inserts ai into hostcache when
- * status is CURL_ASYNC_SUCCESS. Twiddles fields in conn to indicate async
- * request completed whether successful or failed.
- */
-CURLcode Curl_addrinfo_callback(struct Curl_easy *data,
- int status,
- struct Curl_addrinfo *ai);
+CURLcode Curl_once_resolved(struct Curl_easy *data,
+ struct Curl_dns_entry *dns,
+ bool *protocol_connect);
/*
* Curl_printable_address() returns a printable version of the 1st address
@@ -195,73 +146,72 @@ void Curl_printable_address(const struct Curl_addrinfo *ip,
char *buf, size_t bufsize);
/*
- * Curl_fetch_addr() fetches a 'Curl_dns_entry' already in the DNS cache.
+ * Make a `Curl_dns_entry`.
+ * Creates a dnscache entry *without* adding it to a dnscache. This allows
+ * further modifications of the entry *before* then adding it to a cache.
*
- * Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
+ * The entry is created with a reference count of 1.
+ * Use `Curl_resolv_unlink()` to release your hold on it.
*
- * The returned data *MUST* be "released" with Curl_resolv_unlink() after
- * use, or we will leak memory!
+ * The call takes ownership of `addr`and makes a copy of `hostname`.
+ *
+ * Returns entry or NULL on OOM.
*/
struct Curl_dns_entry *
-Curl_fetch_addr(struct Curl_easy *data,
- const char *hostname,
- int port);
+Curl_dnscache_mk_entry(struct Curl_easy *data,
+ struct Curl_addrinfo *addr,
+ const char *hostname,
+ size_t hostlen, /* length or zero */
+ int port,
+ bool permanent);
/*
- * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
- * @param permanent iff TRUE, entry will never become stale
- * Returns the Curl_dns_entry entry pointer or NULL if the storage failed.
+ * Curl_dnscache_get() fetches a 'Curl_dns_entry' already in the DNS cache.
+ *
+ * Returns the Curl_dns_entry entry pointer or NULL if not in the cache.
+ *
+ * The returned data *MUST* be "released" with Curl_resolv_unlink() after
+ * use, or we will leak memory!
*/
struct Curl_dns_entry *
-Curl_cache_addr(struct Curl_easy *data, struct Curl_addrinfo *addr,
- const char *hostname, size_t hostlen, int port,
- bool permanent);
-
-#ifndef INADDR_NONE
-#define CURL_INADDR_NONE (in_addr_t) ~0
-#else
-#define CURL_INADDR_NONE INADDR_NONE
-#endif
-
-/*
- * Function provided by the resolver backend to set DNS servers to use.
- */
-CURLcode Curl_set_dns_servers(struct Curl_easy *data, char *servers);
+Curl_dnscache_get(struct Curl_easy *data,
+ const char *hostname,
+ int port, int ip_version);
/*
- * Function provided by the resolver backend to set
- * outgoing interface to use for DNS requests
+ * Curl_dnscache_addr() adds `entry` to the cache, increasing its
+ * reference count on success.
*/
-CURLcode Curl_set_dns_interface(struct Curl_easy *data,
- const char *interf);
-
-/*
- * Function provided by the resolver backend to set
- * local IPv4 address to use as source address for DNS requests
- */
-CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
- const char *local_ip4);
-
-/*
- * Function provided by the resolver backend to set
- * local IPv6 address to use as source address for DNS requests
- */
-CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
- const char *local_ip6);
-
-/*
- * Clean off entries from the cache
- */
-void Curl_hostcache_clean(struct Curl_easy *data, struct Curl_hash *hash);
+CURLcode Curl_dnscache_add(struct Curl_easy *data,
+ struct Curl_dns_entry *entry);
/*
* Populate the cache with specified entries from CURLOPT_RESOLVE.
*/
CURLcode Curl_loadhostpairs(struct Curl_easy *data);
+
+#ifdef USE_CURL_ASYNC
CURLcode Curl_resolv_check(struct Curl_easy *data,
struct Curl_dns_entry **dns);
+#else
+#define Curl_resolv_check(x,y) CURLE_NOT_BUILT_IN
+#endif
int Curl_resolv_getsock(struct Curl_easy *data,
curl_socket_t *socks);
CURLcode Curl_resolver_error(struct Curl_easy *data);
+
+#ifdef CURLRES_SYNCH
+/*
+ * Curl_sync_getaddrinfo() is the non-async low-level name resolve API.
+ * There are several versions of this function - depending on IPV6
+ * support and platform.
+ */
+struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data,
+ const char *hostname,
+ int port,
+ int ip_version);
+
+#endif
+
#endif /* HEADER_CURL_HOSTIP_H */
diff --git a/libs/libcurl/src/hostip4.c b/libs/libcurl/src/hostip4.c
index 10d91471a1..bdce12f5d5 100644
--- a/libs/libcurl/src/hostip4.c
+++ b/libs/libcurl/src/hostip4.c
@@ -54,24 +54,11 @@
#include "curl_memory.h"
#include "memdebug.h"
-/*
- * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've
- * been set and returns TRUE if they are OK.
- */
-bool Curl_ipvalid(struct Curl_easy *data, struct connectdata *conn)
-{
- (void)data;
- if(conn->ip_version == CURL_IPRESOLVE_V6)
- /* An IPv6 address was requested and we cannot get/use one */
- return FALSE;
-
- return TRUE; /* OK, proceed */
-}
#ifdef CURLRES_SYNCH
/*
- * Curl_getaddrinfo() - the IPv4 synchronous version.
+ * Curl_sync_getaddrinfo() - the IPv4 synchronous version.
*
* The original code to this function was from the Dancer source code, written
* by Bjorn Reese, it has since been patched and modified considerably.
@@ -86,19 +73,18 @@ bool Curl_ipvalid(struct Curl_easy *data, struct connectdata *conn)
* flavours have thread-safe versions of the plain gethostbyname() etc.
*
*/
-struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data,
- const char *hostname,
- int port,
- int *waitp)
+struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data,
+ const char *hostname,
+ int port,
+ int ip_version)
{
struct Curl_addrinfo *ai = NULL;
+ (void)ip_version;
#ifdef CURL_DISABLE_VERBOSE_STRINGS
(void)data;
#endif
- *waitp = 0; /* synchronous response only */
-
ai = Curl_ipv4_resolve_r(hostname, port);
if(!ai)
infof(data, "Curl_ipv4_resolve_r failed for %s", hostname);
@@ -286,7 +272,7 @@ struct Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname,
* getaddrinfo() nor gethostbyname_r() function or for which
* gethostbyname() is the preferred one.
*/
- h = gethostbyname((void *)hostname);
+ h = gethostbyname(CURL_UNCONST(hostname));
#endif /* (HAVE_GETADDRINFO && HAVE_GETADDRINFO_THREADSAFE) ||
HAVE_GETHOSTBYNAME_R */
diff --git a/libs/libcurl/src/hostip6.c b/libs/libcurl/src/hostip6.c
index 8de74abf97..4a61ead046 100644
--- a/libs/libcurl/src/hostip6.c
+++ b/libs/libcurl/src/hostip6.c
@@ -49,25 +49,13 @@
#include "hash.h"
#include "share.h"
#include "url.h"
-#include "inet_pton.h"
+#include "curlx/inet_pton.h"
#include "connect.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
#include "memdebug.h"
-/*
- * Curl_ipvalid() checks what CURL_IPRESOLVE_* requirements that might've
- * been set and returns TRUE if they are OK.
- */
-bool Curl_ipvalid(struct Curl_easy *data, struct connectdata *conn)
-{
- if(conn->ip_version == CURL_IPRESOLVE_V6)
- return Curl_ipv6works(data);
-
- return TRUE;
-}
-
#if defined(CURLRES_SYNCH)
#ifdef DEBUG_ADDRINFO
@@ -87,7 +75,7 @@ static void dump_addrinfo(const struct Curl_addrinfo *ai)
#endif
/*
- * Curl_getaddrinfo() when built IPv6-enabled (non-threading and
+ * Curl_sync_getaddrinfo() when built IPv6-enabled (non-threading and
* non-ares version).
*
* Returns name information about the given hostname and port number. If
@@ -95,10 +83,10 @@ static void dump_addrinfo(const struct Curl_addrinfo *ai)
* to memory we need to free after use. That memory *MUST* be freed with
* Curl_freeaddrinfo(), nothing else.
*/
-struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data,
- const char *hostname,
- int port,
- int *waitp)
+struct Curl_addrinfo *Curl_sync_getaddrinfo(struct Curl_easy *data,
+ const char *hostname,
+ int port,
+ int ip_version)
{
struct addrinfo hints;
struct Curl_addrinfo *res;
@@ -110,9 +98,7 @@ struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data,
#endif
int pf = PF_INET;
- *waitp = 0; /* synchronous response only */
-
- if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data))
+ if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data))
/* The stack seems to be IPv6-enabled */
pf = PF_UNSPEC;
@@ -126,8 +112,8 @@ struct Curl_addrinfo *Curl_getaddrinfo(struct Curl_easy *data,
* The AI_NUMERICHOST must not be set to get synthesized IPv6 address from
* an IPv4 address on iOS and macOS.
*/
- if((1 == Curl_inet_pton(AF_INET, hostname, addrbuf)) ||
- (1 == Curl_inet_pton(AF_INET6, hostname, addrbuf))) {
+ if((1 == curlx_inet_pton(AF_INET, hostname, addrbuf)) ||
+ (1 == curlx_inet_pton(AF_INET6, hostname, addrbuf))) {
/* the given address is numerical only, prevent a reverse lookup */
hints.ai_flags = AI_NUMERICHOST;
}
diff --git a/libs/libcurl/src/hostsyn.c b/libs/libcurl/src/hostsyn.c
deleted file mode 100644
index 6cbe679a23..0000000000
--- a/libs/libcurl/src/hostsyn.c
+++ /dev/null
@@ -1,104 +0,0 @@
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-/***********************************************************************
- * Only for builds using synchronous name resolves
- **********************************************************************/
-#ifdef CURLRES_SYNCH
-
-#ifdef HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-#ifdef HAVE_NETDB_H
-#include <netdb.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-#include <arpa/inet.h>
-#endif
-#ifdef __VMS
-#include <in.h>
-#include <inet.h>
-#endif
-
-#include "urldata.h"
-#include "sendf.h"
-#include "hostip.h"
-#include "hash.h"
-#include "share.h"
-#include "url.h"
-#include "curl_memory.h"
-/* The last #include file should be: */
-#include "memdebug.h"
-
-/*
- * Function provided by the resolver backend to set DNS servers to use.
- */
-CURLcode Curl_set_dns_servers(struct Curl_easy *data,
- char *servers)
-{
- (void)data;
- (void)servers;
- return CURLE_NOT_BUILT_IN;
-
-}
-
-/*
- * Function provided by the resolver backend to set
- * outgoing interface to use for DNS requests
- */
-CURLcode Curl_set_dns_interface(struct Curl_easy *data,
- const char *interf)
-{
- (void)data;
- (void)interf;
- return CURLE_NOT_BUILT_IN;
-}
-
-/*
- * Function provided by the resolver backend to set
- * local IPv4 address to use as source address for DNS requests
- */
-CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
- const char *local_ip4)
-{
- (void)data;
- (void)local_ip4;
- return CURLE_NOT_BUILT_IN;
-}
-
-/*
- * Function provided by the resolver backend to set
- * local IPv6 address to use as source address for DNS requests
- */
-CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
- const char *local_ip6)
-{
- (void)data;
- (void)local_ip6;
- return CURLE_NOT_BUILT_IN;
-}
-
-#endif /* truly sync */
diff --git a/libs/libcurl/src/hsts.c b/libs/libcurl/src/hsts.c
index 03c310ed09..3ee266a50f 100644
--- a/libs/libcurl/src/hsts.c
+++ b/libs/libcurl/src/hsts.c
@@ -35,12 +35,12 @@
#include "curl_get_line.h"
#include "strcase.h"
#include "sendf.h"
-#include "strtoofft.h"
#include "parsedate.h"
#include "fopen.h"
#include "rename.h"
#include "share.h"
#include "strdup.h"
+#include "curlx/strparse.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -48,10 +48,8 @@
#include "memdebug.h"
#define MAX_HSTS_LINE 4095
-#define MAX_HSTS_HOSTLEN 256
-#define MAX_HSTS_HOSTLENSTR "256"
-#define MAX_HSTS_DATELEN 64
-#define MAX_HSTS_DATELENSTR "64"
+#define MAX_HSTS_HOSTLEN 2048
+#define MAX_HSTS_DATELEN 256
#define UNLIMITED "unlimited"
#if defined(DEBUGBUILD) || defined(UNITTESTS)
@@ -60,13 +58,12 @@
time_t deltatime; /* allow for "adjustments" for unit test purposes */
static time_t hsts_debugtime(void *unused)
{
- char *timestr = getenv("CURL_TIME");
+ const char *timestr = getenv("CURL_TIME");
(void)unused;
if(timestr) {
curl_off_t val;
- (void)curlx_strtoofft(timestr, NULL, 10, &val);
-
- val += (curl_off_t)deltatime;
+ if(!curlx_str_number(&timestr, &val, TIME_T_MAX))
+ val += (curl_off_t)deltatime;
return (time_t)val;
}
return time(NULL);
@@ -86,7 +83,7 @@ struct hsts *Curl_hsts_init(void)
static void hsts_free(struct stsentry *e)
{
- free((char *)e->host);
+ free(CURL_UNCONST(e->host));
free(e);
}
@@ -109,14 +106,13 @@ void Curl_hsts_cleanup(struct hsts **hp)
static CURLcode hsts_create(struct hsts *h,
const char *hostname,
+ size_t hlen,
bool subdomains,
curl_off_t expires)
{
- size_t hlen;
DEBUGASSERT(h);
DEBUGASSERT(hostname);
- hlen = strlen(hostname);
if(hlen && (hostname[hlen - 1] == '.'))
/* strip off any trailing dot */
--hlen;
@@ -150,6 +146,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
bool subdomains = FALSE;
struct stsentry *sts;
time_t now = time(NULL);
+ size_t hlen = strlen(hostname);
if(Curl_host_is_ipnum(hostname))
/* "explicit IP address identification of all forms is excluded."
@@ -157,35 +154,30 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
return CURLE_OK;
do {
- while(*p && ISBLANK(*p))
- p++;
+ curlx_str_passblanks(&p);
if(strncasecompare("max-age", p, 7)) {
bool quoted = FALSE;
- CURLofft offt;
- char *endp;
+ int rc;
if(gotma)
return CURLE_BAD_FUNCTION_ARGUMENT;
p += 7;
- while(*p && ISBLANK(*p))
- p++;
- if(*p++ != '=')
+ curlx_str_passblanks(&p);
+ if(curlx_str_single(&p, '='))
return CURLE_BAD_FUNCTION_ARGUMENT;
- while(*p && ISBLANK(*p))
- p++;
+ curlx_str_passblanks(&p);
- if(*p == '\"') {
- p++;
+ if(!curlx_str_single(&p, '\"'))
quoted = TRUE;
- }
- offt = curlx_strtoofft(p, &endp, 10, &expires);
- if(offt == CURL_OFFT_FLOW)
+
+ rc = curlx_str_number(&p, &expires, TIME_T_MAX);
+ if(rc == STRE_OVERFLOW)
expires = CURL_OFF_T_MAX;
- else if(offt)
+ else if(rc)
/* invalid max-age */
return CURLE_BAD_FUNCTION_ARGUMENT;
- p = endp;
+
if(quoted) {
if(*p != '\"')
return CURLE_BAD_FUNCTION_ARGUMENT;
@@ -206,8 +198,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
p++;
}
- while(*p && ISBLANK(*p))
- p++;
+ curlx_str_passblanks(&p);
if(*p == ';')
p++;
} while(*p);
@@ -218,7 +209,7 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
if(!expires) {
/* remove the entry if present verbatim (without subdomain match) */
- sts = Curl_hsts(h, hostname, FALSE);
+ sts = Curl_hsts(h, hostname, hlen, FALSE);
if(sts) {
Curl_node_remove(&sts->node);
hsts_free(sts);
@@ -233,14 +224,14 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
expires += now;
/* check if it already exists */
- sts = Curl_hsts(h, hostname, FALSE);
+ sts = Curl_hsts(h, hostname, hlen, FALSE);
if(sts) {
/* just update these fields */
sts->expires = expires;
sts->includeSubDomains = subdomains;
}
else
- return hsts_create(h, hostname, subdomains, expires);
+ return hsts_create(h, hostname, hlen, subdomains, expires);
return CURLE_OK;
}
@@ -252,12 +243,11 @@ CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
* attempted.
*/
struct stsentry *Curl_hsts(struct hsts *h, const char *hostname,
- bool subdomain)
+ size_t hlen, bool subdomain)
{
struct stsentry *bestsub = NULL;
if(h) {
time_t now = time(NULL);
- size_t hlen = strlen(hostname);
struct Curl_llist_node *e;
struct Curl_llist_node *n;
size_t blen = 0;
@@ -289,7 +279,7 @@ struct stsentry *Curl_hsts(struct hsts *h, const char *hostname,
blen = ntail;
}
}
- /* avoid strcasecompare because the host name is not null terminated */
+ /* avoid strcasecompare because the host name is not null-terminated */
if((hlen == ntail) && strncasecompare(hostname, sts->host, hlen))
return sts;
}
@@ -310,7 +300,7 @@ static CURLcode hsts_push(struct Curl_easy *data,
struct tm stamp;
CURLcode result;
- e.name = (char *)sts->host;
+ e.name = (char *)CURL_UNCONST(sts->host);
e.namelen = strlen(sts->host);
e.includeSubDomains = sts->includeSubDomains;
@@ -418,35 +408,46 @@ skipsave:
}
/* only returns SERIOUS errors */
-static CURLcode hsts_add(struct hsts *h, char *line)
+static CURLcode hsts_add(struct hsts *h, const char *line)
{
/* Example lines:
example.com "20191231 10:00:00"
.example.net "20191231 10:00:00"
*/
- char host[MAX_HSTS_HOSTLEN + 1];
- char date[MAX_HSTS_DATELEN + 1];
- int rc;
-
- rc = sscanf(line,
- "%" MAX_HSTS_HOSTLENSTR "s \"%" MAX_HSTS_DATELENSTR "[^\"]\"",
- host, date);
- if(2 == rc) {
- time_t expires = strcmp(date, UNLIMITED) ? Curl_getdate_capped(date) :
- TIME_T_MAX;
+ struct Curl_str host;
+ struct Curl_str date;
+
+ if(curlx_str_word(&line, &host, MAX_HSTS_HOSTLEN) ||
+ curlx_str_singlespace(&line) ||
+ curlx_str_quotedword(&line, &date, MAX_HSTS_DATELEN) ||
+ curlx_str_newline(&line))
+ ;
+ else {
CURLcode result = CURLE_OK;
- char *p = host;
bool subdomain = FALSE;
struct stsentry *e;
- if(p[0] == '.') {
- p++;
+ char dbuf[MAX_HSTS_DATELEN + 1];
+ time_t expires;
+ const char *hp = curlx_str(&host);
+
+ /* The date parser works on a null-terminated string. The maximum length
+ is upheld by curlx_str_quotedword(). */
+ memcpy(dbuf, curlx_str(&date), curlx_strlen(&date));
+ dbuf[curlx_strlen(&date)] = 0;
+
+ expires = strcmp(dbuf, UNLIMITED) ? Curl_getdate_capped(dbuf) :
+ TIME_T_MAX;
+
+ if(hp[0] == '.') {
+ curlx_str_nudge(&host, 1);
subdomain = TRUE;
}
/* only add it if not already present */
- e = Curl_hsts(h, p, subdomain);
+ e = Curl_hsts(h, curlx_str(&host), curlx_strlen(&host), subdomain);
if(!e)
- result = hsts_create(h, p, subdomain, expires);
- else if(strcasecompare(p, e->host)) {
+ result = hsts_create(h, curlx_str(&host), curlx_strlen(&host),
+ subdomain, expires);
+ else if(curlx_str_casecompare(&host, e->host)) {
/* the same hostname, use the largest expire time */
if(expires > e->expires)
e->expires = expires;
@@ -488,7 +489,7 @@ static CURLcode hsts_pull(struct Curl_easy *data, struct hsts *h)
expires = Curl_getdate_capped(e.expire);
else
expires = TIME_T_MAX; /* the end of time */
- result = hsts_create(h, e.name,
+ result = hsts_create(h, e.name, strlen(e.name),
/* bitfield to bool conversion: */
e.includeSubDomains ? TRUE : FALSE,
expires);
@@ -525,11 +526,11 @@ static CURLcode hsts_load(struct hsts *h, const char *file)
fp = fopen(file, FOPEN_READTEXT);
if(fp) {
struct dynbuf buf;
- Curl_dyn_init(&buf, MAX_HSTS_LINE);
+ curlx_dyn_init(&buf, MAX_HSTS_LINE);
while(Curl_get_line(&buf, fp)) {
- char *lineptr = Curl_dyn_ptr(&buf);
- while(*lineptr && ISBLANK(*lineptr))
- lineptr++;
+ const char *lineptr = curlx_dyn_ptr(&buf);
+ curlx_str_passblanks(&lineptr);
+
/*
* Skip empty or commented lines, since we know the line will have a
* trailing newline from Curl_get_line we can treat length 1 as empty.
@@ -539,7 +540,7 @@ static CURLcode hsts_load(struct hsts *h, const char *file)
hsts_add(h, lineptr);
}
- Curl_dyn_free(&buf); /* free the line buffer */
+ curlx_dyn_free(&buf); /* free the line buffer */
fclose(fp);
}
return result;
diff --git a/libs/libcurl/src/hsts.h b/libs/libcurl/src/hsts.h
index de08a1066a..5eaa81e112 100644
--- a/libs/libcurl/src/hsts.h
+++ b/libs/libcurl/src/hsts.h
@@ -36,8 +36,8 @@ extern time_t deltatime;
struct stsentry {
struct Curl_llist_node node;
const char *host;
- bool includeSubDomains;
curl_off_t expires; /* the timestamp of this entry's expiry */
+ BIT(includeSubDomains);
};
/* The HSTS cache. Needs to be able to tailmatch hostnames. */
@@ -52,7 +52,7 @@ void Curl_hsts_cleanup(struct hsts **hp);
CURLcode Curl_hsts_parse(struct hsts *h, const char *hostname,
const char *sts);
struct stsentry *Curl_hsts(struct hsts *h, const char *hostname,
- bool subdomain);
+ size_t hlen, bool subdomain);
CURLcode Curl_hsts_save(struct Curl_easy *data, struct hsts *h,
const char *file);
CURLcode Curl_hsts_loadfile(struct Curl_easy *data,
diff --git a/libs/libcurl/src/http.c b/libs/libcurl/src/http.c
index 6d61a9ec34..f3e4121d3a 100644
--- a/libs/libcurl/src/http.c
+++ b/libs/libcurl/src/http.c
@@ -47,10 +47,6 @@
#include <sys/param.h>
#endif
-#ifdef USE_HYPER
-#include <hyper.h>
-#endif
-
#include "urldata.h"
#include <curl/curl.h>
#include "transfer.h"
@@ -58,7 +54,7 @@
#include "formdata.h"
#include "mime.h"
#include "progress.h"
-#include "curl_base64.h"
+#include "curlx/base64.h"
#include "cookie.h"
#include "vauth/vauth.h"
#include "vtls/vtls.h"
@@ -68,6 +64,7 @@
#include "http_negotiate.h"
#include "http_aws_sigv4.h"
#include "url.h"
+#include "urlapi-int.h"
#include "share.h"
#include "hostip.h"
#include "dynhds.h"
@@ -75,12 +72,11 @@
#include "headers.h"
#include "select.h"
#include "parsedate.h" /* for the week day and month names */
-#include "strtoofft.h"
#include "multiif.h"
#include "strcase.h"
#include "content_encoding.h"
#include "http_proxy.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
#include "http2.h"
#include "cfilters.h"
#include "connect.h"
@@ -88,8 +84,8 @@
#include "altsvc.h"
#include "hsts.h"
#include "ws.h"
-#include "c-hyper.h"
#include "curl_ctype.h"
+#include "curlx/strparse.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -104,6 +100,30 @@ static bool http_should_fail(struct Curl_easy *data, int httpcode);
static bool http_exp100_is_waiting(struct Curl_easy *data);
static CURLcode http_exp100_add_reader(struct Curl_easy *data);
static void http_exp100_send_anyway(struct Curl_easy *data);
+static bool http_exp100_is_selected(struct Curl_easy *data);
+static void http_exp100_got100(struct Curl_easy *data);
+static CURLcode http_firstwrite(struct Curl_easy *data);
+static CURLcode http_header(struct Curl_easy *data,
+ const char *hd, size_t hdlen);
+static CURLcode http_host(struct Curl_easy *data, struct connectdata *conn);
+static CURLcode http_range(struct Curl_easy *data,
+ Curl_HttpReq httpreq);
+static CURLcode http_req_complete(struct Curl_easy *data,
+ struct dynbuf *r, int httpversion,
+ Curl_HttpReq httpreq);
+static CURLcode http_req_set_reader(struct Curl_easy *data,
+ Curl_HttpReq httpreq, int httpversion,
+ const char **tep);
+static CURLcode http_size(struct Curl_easy *data);
+static CURLcode http_statusline(struct Curl_easy *data,
+ struct connectdata *conn);
+static CURLcode http_target(struct Curl_easy *data, struct connectdata *conn,
+ struct dynbuf *req);
+static CURLcode http_useragent(struct Curl_easy *data);
+#ifdef HAVE_LIBZ
+static CURLcode http_transferencode(struct Curl_easy *data);
+#endif
+
/*
* HTTP handler interface.
@@ -126,6 +146,7 @@ const struct Curl_handler Curl_handler_http = {
Curl_http_write_resp_hd, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ Curl_http_follow, /* follow */
PORT_HTTP, /* defport */
CURLPROTO_HTTP, /* protocol */
CURLPROTO_HTTP, /* family */
@@ -155,6 +176,7 @@ const struct Curl_handler Curl_handler_https = {
Curl_http_write_resp_hd, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ Curl_http_follow, /* follow */
PORT_HTTPS, /* defport */
CURLPROTO_HTTPS, /* protocol */
CURLPROTO_HTTP, /* family */
@@ -164,19 +186,56 @@ const struct Curl_handler Curl_handler_https = {
#endif
+void Curl_http_neg_init(struct Curl_easy *data, struct http_negotiation *neg)
+{
+ memset(neg, 0, sizeof(*neg));
+ neg->accept_09 = data->set.http09_allowed;
+ switch(data->set.httpwant) {
+ case CURL_HTTP_VERSION_1_0:
+ neg->wanted = neg->allowed = (CURL_HTTP_V1x);
+ neg->only_10 = TRUE;
+ break;
+ case CURL_HTTP_VERSION_1_1:
+ neg->wanted = neg->allowed = (CURL_HTTP_V1x);
+ break;
+ case CURL_HTTP_VERSION_2_0:
+ neg->wanted = neg->allowed = (CURL_HTTP_V1x | CURL_HTTP_V2x);
+ neg->h2_upgrade = TRUE;
+ break;
+ case CURL_HTTP_VERSION_2TLS:
+ neg->wanted = neg->allowed = (CURL_HTTP_V1x | CURL_HTTP_V2x);
+ break;
+ case CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE:
+ neg->wanted = neg->allowed = (CURL_HTTP_V2x);
+ data->state.http_neg.h2_prior_knowledge = TRUE;
+ break;
+ case CURL_HTTP_VERSION_3:
+ neg->wanted = (CURL_HTTP_V1x | CURL_HTTP_V2x | CURL_HTTP_V3x);
+ neg->allowed = neg->wanted;
+ break;
+ case CURL_HTTP_VERSION_3ONLY:
+ neg->wanted = neg->allowed = (CURL_HTTP_V3x);
+ break;
+ case CURL_HTTP_VERSION_NONE:
+ default:
+ neg->wanted = (CURL_HTTP_V1x | CURL_HTTP_V2x);
+ neg->allowed = (CURL_HTTP_V1x | CURL_HTTP_V2x | CURL_HTTP_V3x);
+ break;
+ }
+}
+
CURLcode Curl_http_setup_conn(struct Curl_easy *data,
struct connectdata *conn)
{
/* allocate the HTTP-specific struct for the Curl_easy, only to survive
during this request */
connkeep(conn, "HTTP default");
-
- if(data->state.httpwant == CURL_HTTP_VERSION_3ONLY) {
+ if(data->state.http_neg.wanted == CURL_HTTP_V3x) {
+ /* only HTTP/3, needs to work */
CURLcode result = Curl_conn_may_http3(data, conn);
if(result)
return result;
}
-
return CURLE_OK;
}
@@ -213,46 +272,28 @@ char *Curl_checkProxyheaders(struct Curl_easy *data,
#endif
/*
- * Strip off leading and trailing whitespace from the value in the
- * given HTTP header line and return a strdupped copy. Returns NULL in
- * case of allocation failure. Returns an empty string if the header value
- * consists entirely of whitespace.
+ * Strip off leading and trailing whitespace from the value in the given HTTP
+ * header line and return a strdup()ed copy. Returns NULL in case of
+ * allocation failure or bad input. Returns an empty string if the header
+ * value consists entirely of whitespace.
+ *
+ * If the header is provided as "name;", ending with a semicolon, it must
+ * return a blank string.
*/
char *Curl_copy_header_value(const char *header)
{
- const char *start;
- const char *end;
- size_t len;
+ struct Curl_str out;
- /* Find the end of the header name */
- while(*header && (*header != ':'))
- ++header;
+ /* find the end of the header name */
+ if(!curlx_str_cspn(&header, &out, ";:") &&
+ (!curlx_str_single(&header, ':') || !curlx_str_single(&header, ';'))) {
+ curlx_str_untilnl(&header, &out, MAX_HTTP_RESP_HEADER_SIZE);
+ curlx_str_trimblanks(&out);
- if(*header)
- /* Skip over colon */
- ++header;
-
- /* Find the first non-space letter */
- start = header;
- while(*start && ISSPACE(*start))
- start++;
-
- end = strchr(start, '\r');
- if(!end)
- end = strchr(start, '\n');
- if(!end)
- end = strchr(start, '\0');
- if(!end)
- return NULL;
-
- /* skip all trailing space letters */
- while((end > start) && ISSPACE(*end))
- end--;
-
- /* get length of the type */
- len = end - start + 1;
-
- return Curl_memdup0(start, len);
+ return Curl_memdup0(curlx_str(&out), curlx_strlen(&out));
+ }
+ /* bad input */
+ return NULL;
}
#ifndef CURL_DISABLE_HTTP_AUTH
@@ -295,7 +336,7 @@ static CURLcode http_output_basic(struct Curl_easy *data, bool proxy)
if(!out)
return CURLE_OUT_OF_MEMORY;
- result = Curl_base64_encode(out, strlen(out), &authorization, &size);
+ result = curlx_base64_encode(out, strlen(out), &authorization, &size);
if(result)
goto fail;
@@ -436,7 +477,7 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data,
#if defined(USE_NTLM)
if((data->state.authproxy.picked == CURLAUTH_NTLM) ||
(data->state.authhost.picked == CURLAUTH_NTLM)) {
- ongoing_auth = "NTML";
+ ongoing_auth = "NTLM";
if((conn->http_ntlm_state != NTLMSTATE_NONE) ||
(conn->proxy_ntlm_state != NTLMSTATE_NONE)) {
/* The NTLM-negotiation has started, keep on sending.
@@ -473,7 +514,6 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data,
ongoing_auth ? " send, " : "");
/* We decided to abort the ongoing transfer */
streamclose(conn, "Mid-auth HTTP and much data left to send");
- /* FIXME: questionable manipulation here, can we do this differently? */
data->req.size = 0; /* do not download any more than 0 bytes */
}
return CURLE_OK;
@@ -510,11 +550,14 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data)
pickhost = pickoneauth(&data->state.authhost, authmask);
if(!pickhost)
data->state.authproblem = TRUE;
+ else
+ data->info.httpauthpicked = data->state.authhost.picked;
if(data->state.authhost.picked == CURLAUTH_NTLM &&
- conn->httpversion > 11) {
+ (data->req.httpversion_sent > 11)) {
infof(data, "Forcing HTTP/1.1 for NTLM");
connclose(conn, "Force HTTP/1.1 connection");
- data->state.httpwant = CURL_HTTP_VERSION_1_1;
+ data->state.http_neg.wanted = CURL_HTTP_V1x;
+ data->state.http_neg.allowed = CURL_HTTP_V1x;
}
}
#ifndef CURL_DISABLE_PROXY
@@ -525,6 +568,9 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data)
authmask & ~CURLAUTH_BEARER);
if(!pickproxy)
data->state.authproblem = TRUE;
+ else
+ data->info.proxyauthpicked = data->state.authproxy.picked;
+
}
#endif
@@ -536,7 +582,7 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data)
/* In case this is GSS auth, the newurl field is already allocated so
we must make sure to free it before allocating a new one. As figured
out in bug #2284386 */
- Curl_safefree(data->req.newurl);
+ free(data->req.newurl);
data->req.newurl = strdup(data->state.url); /* clone URL */
if(!data->req.newurl)
return CURLE_OUT_OF_MEMORY;
@@ -587,9 +633,10 @@ output_auth_headers(struct Curl_easy *data,
(void)path;
#endif
#ifndef CURL_DISABLE_AWS
- if(authstatus->picked == CURLAUTH_AWS_SIGV4) {
+ if((authstatus->picked == CURLAUTH_AWS_SIGV4) && !proxy) {
+ /* this method is never for proxy */
auth = "AWS_SIGV4";
- result = Curl_output_aws_sigv4(data, proxy);
+ result = Curl_output_aws_sigv4(data);
if(result)
return result;
}
@@ -721,12 +768,12 @@ Curl_http_output_auth(struct Curl_easy *data,
#ifndef CURL_DISABLE_PROXY
(conn->bits.httpproxy && conn->bits.proxy_user_passwd) ||
#endif
- data->state.aptr.user ||
+ data->state.aptr.user ||
#ifdef USE_SPNEGO
- authhost->want & CURLAUTH_NEGOTIATE ||
- authproxy->want & CURLAUTH_NEGOTIATE ||
+ authhost->want & CURLAUTH_NEGOTIATE ||
+ authproxy->want & CURLAUTH_NEGOTIATE ||
#endif
- data->set.str[STRING_BEARER])
+ data->set.str[STRING_BEARER])
/* continue please */;
else {
authhost->done = TRUE;
@@ -811,9 +858,137 @@ Curl_http_output_auth(struct Curl_easy *data,
!defined(CURL_DISABLE_DIGEST_AUTH) || \
!defined(CURL_DISABLE_BASIC_AUTH) || \
!defined(CURL_DISABLE_BEARER_AUTH)
-static int is_valid_auth_separator(char ch)
+static bool authcmp(const char *auth, const char *line)
{
- return ch == '\0' || ch == ',' || ISSPACE(ch);
+ /* the auth string must not have an alnum following */
+ size_t n = strlen(auth);
+ return strncasecompare(auth, line, n) && !ISALNUM(line[n]);
+}
+#endif
+
+#ifdef USE_SPNEGO
+static CURLcode auth_spnego(struct Curl_easy *data,
+ bool proxy,
+ const char *auth,
+ struct auth *authp,
+ unsigned long *availp)
+{
+ if((authp->avail & CURLAUTH_NEGOTIATE) || Curl_auth_is_spnego_supported()) {
+ *availp |= CURLAUTH_NEGOTIATE;
+ authp->avail |= CURLAUTH_NEGOTIATE;
+
+ if(authp->picked == CURLAUTH_NEGOTIATE) {
+ struct connectdata *conn = data->conn;
+ CURLcode result = Curl_input_negotiate(data, conn, proxy, auth);
+ curlnegotiate *negstate = proxy ? &conn->proxy_negotiate_state :
+ &conn->http_negotiate_state;
+ if(!result) {
+ free(data->req.newurl);
+ data->req.newurl = strdup(data->state.url);
+ if(!data->req.newurl)
+ return CURLE_OUT_OF_MEMORY;
+ data->state.authproblem = FALSE;
+ /* we received a GSS auth token and we dealt with it fine */
+ *negstate = GSS_AUTHRECV;
+ }
+ else
+ data->state.authproblem = TRUE;
+ }
+ }
+ return CURLE_OK;
+}
+#endif
+
+#ifdef USE_NTLM
+static CURLcode auth_ntlm(struct Curl_easy *data,
+ bool proxy,
+ const char *auth,
+ struct auth *authp,
+ unsigned long *availp)
+{
+ /* NTLM support requires the SSL crypto libs */
+ if((authp->avail & CURLAUTH_NTLM) || Curl_auth_is_ntlm_supported()) {
+ *availp |= CURLAUTH_NTLM;
+ authp->avail |= CURLAUTH_NTLM;
+
+ if(authp->picked == CURLAUTH_NTLM) {
+ /* NTLM authentication is picked and activated */
+ CURLcode result = Curl_input_ntlm(data, proxy, auth);
+ if(!result)
+ data->state.authproblem = FALSE;
+ else {
+ infof(data, "NTLM authentication problem, ignoring.");
+ data->state.authproblem = TRUE;
+ }
+ }
+ }
+ return CURLE_OK;
+}
+#endif
+
+#ifndef CURL_DISABLE_DIGEST_AUTH
+static CURLcode auth_digest(struct Curl_easy *data,
+ bool proxy,
+ const char *auth,
+ struct auth *authp,
+ unsigned long *availp)
+{
+ if(authp->avail & CURLAUTH_DIGEST)
+ infof(data, "Ignoring duplicate digest auth header.");
+ else if(Curl_auth_is_digest_supported()) {
+ CURLcode result;
+
+ *availp |= CURLAUTH_DIGEST;
+ authp->avail |= CURLAUTH_DIGEST;
+
+ /* We call this function on input Digest headers even if Digest
+ * authentication is not activated yet, as we need to store the
+ * incoming data from this header in case we are going to use
+ * Digest */
+ result = Curl_input_digest(data, proxy, auth);
+ if(result) {
+ infof(data, "Digest authentication problem, ignoring.");
+ data->state.authproblem = TRUE;
+ }
+ }
+ return CURLE_OK;
+}
+#endif
+
+#ifndef CURL_DISABLE_BASIC_AUTH
+static CURLcode auth_basic(struct Curl_easy *data,
+ struct auth *authp,
+ unsigned long *availp)
+{
+ *availp |= CURLAUTH_BASIC;
+ authp->avail |= CURLAUTH_BASIC;
+ if(authp->picked == CURLAUTH_BASIC) {
+ /* We asked for Basic authentication but got a 40X back
+ anyway, which basically means our name+password is not
+ valid. */
+ authp->avail = CURLAUTH_NONE;
+ infof(data, "Basic authentication problem, ignoring.");
+ data->state.authproblem = TRUE;
+ }
+ return CURLE_OK;
+}
+#endif
+
+#ifndef CURL_DISABLE_BEARER_AUTH
+static CURLcode auth_bearer(struct Curl_easy *data,
+ struct auth *authp,
+ unsigned long *availp)
+{
+ *availp |= CURLAUTH_BEARER;
+ authp->avail |= CURLAUTH_BEARER;
+ if(authp->picked == CURLAUTH_BEARER) {
+ /* We asked for Bearer authentication but got a 40X back
+ anyway, which basically means our token is not valid. */
+ authp->avail = CURLAUTH_NONE;
+ infof(data, "Bearer authentication problem, ignoring.");
+ data->state.authproblem = TRUE;
+ }
+ return CURLE_OK;
}
#endif
@@ -821,6 +996,8 @@ static int is_valid_auth_separator(char ch)
* Curl_http_input_auth() deals with Proxy-Authenticate: and WWW-Authenticate:
* headers. They are dealt with both in the transfer.c main loop and in the
* proxy CONNECT loop.
+ *
+ * The 'auth' line ends with a null byte without CR or LF present.
*/
CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
const char *auth) /* the first non-space */
@@ -828,19 +1005,17 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
/*
* This resource requires authentication
*/
- struct connectdata *conn = data->conn;
-#ifdef USE_SPNEGO
- curlnegotiate *negstate = proxy ? &conn->proxy_negotiate_state :
- &conn->http_negotiate_state;
-#endif
-#if defined(USE_SPNEGO) || \
- defined(USE_NTLM) || \
- !defined(CURL_DISABLE_DIGEST_AUTH) || \
- !defined(CURL_DISABLE_BASIC_AUTH) || \
+#if defined(USE_SPNEGO) || \
+ defined(USE_NTLM) || \
+ !defined(CURL_DISABLE_DIGEST_AUTH) || \
+ !defined(CURL_DISABLE_BASIC_AUTH) || \
!defined(CURL_DISABLE_BEARER_AUTH)
unsigned long *availp;
struct auth *authp;
+ CURLcode result = CURLE_OK;
+ DEBUGASSERT(auth);
+ DEBUGASSERT(data);
if(proxy) {
availp = &data->info.proxyauthavail;
@@ -850,11 +1025,6 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
availp = &data->info.httpauthavail;
authp = &data->state.authhost;
}
-#else
- (void) proxy;
-#endif
-
- (void) conn; /* In case conditionals make it unused. */
/*
* Here we check if we want the specific single authentication (using ==) and
@@ -874,126 +1044,43 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
while(*auth) {
#ifdef USE_SPNEGO
- if(checkprefix("Negotiate", auth) && is_valid_auth_separator(auth[9])) {
- if((authp->avail & CURLAUTH_NEGOTIATE) ||
- Curl_auth_is_spnego_supported()) {
- *availp |= CURLAUTH_NEGOTIATE;
- authp->avail |= CURLAUTH_NEGOTIATE;
-
- if(authp->picked == CURLAUTH_NEGOTIATE) {
- CURLcode result = Curl_input_negotiate(data, conn, proxy, auth);
- if(!result) {
- free(data->req.newurl);
- data->req.newurl = strdup(data->state.url);
- if(!data->req.newurl)
- return CURLE_OUT_OF_MEMORY;
- data->state.authproblem = FALSE;
- /* we received a GSS auth token and we dealt with it fine */
- *negstate = GSS_AUTHRECV;
- }
- else
- data->state.authproblem = TRUE;
- }
- }
- }
- else
+ if(authcmp("Negotiate", auth))
+ result = auth_spnego(data, proxy, auth, authp, availp);
#endif
#ifdef USE_NTLM
- /* NTLM support requires the SSL crypto libs */
- if(checkprefix("NTLM", auth) && is_valid_auth_separator(auth[4])) {
- if((authp->avail & CURLAUTH_NTLM) ||
- Curl_auth_is_ntlm_supported()) {
- *availp |= CURLAUTH_NTLM;
- authp->avail |= CURLAUTH_NTLM;
-
- if(authp->picked == CURLAUTH_NTLM) {
- /* NTLM authentication is picked and activated */
- CURLcode result = Curl_input_ntlm(data, proxy, auth);
- if(!result) {
- data->state.authproblem = FALSE;
- }
- else {
- infof(data, "Authentication problem. Ignoring this.");
- data->state.authproblem = TRUE;
- }
- }
- }
- }
- else
+ if(!result && authcmp("NTLM", auth))
+ result = auth_ntlm(data, proxy, auth, authp, availp);
#endif
#ifndef CURL_DISABLE_DIGEST_AUTH
- if(checkprefix("Digest", auth) && is_valid_auth_separator(auth[6])) {
- if((authp->avail & CURLAUTH_DIGEST) != 0)
- infof(data, "Ignoring duplicate digest auth header.");
- else if(Curl_auth_is_digest_supported()) {
- CURLcode result;
-
- *availp |= CURLAUTH_DIGEST;
- authp->avail |= CURLAUTH_DIGEST;
-
- /* We call this function on input Digest headers even if Digest
- * authentication is not activated yet, as we need to store the
- * incoming data from this header in case we are going to use
- * Digest */
- result = Curl_input_digest(data, proxy, auth);
- if(result) {
- infof(data, "Authentication problem. Ignoring this.");
- data->state.authproblem = TRUE;
- }
- }
- }
- else
+ if(!result && authcmp("Digest", auth))
+ result = auth_digest(data, proxy, auth, authp, availp);
#endif
#ifndef CURL_DISABLE_BASIC_AUTH
- if(checkprefix("Basic", auth) &&
- is_valid_auth_separator(auth[5])) {
- *availp |= CURLAUTH_BASIC;
- authp->avail |= CURLAUTH_BASIC;
- if(authp->picked == CURLAUTH_BASIC) {
- /* We asked for Basic authentication but got a 40X back
- anyway, which basically means our name+password is not
- valid. */
- authp->avail = CURLAUTH_NONE;
- infof(data, "Authentication problem. Ignoring this.");
- data->state.authproblem = TRUE;
- }
- }
- else
+ if(!result && authcmp("Basic", auth))
+ result = auth_basic(data, authp, availp);
#endif
#ifndef CURL_DISABLE_BEARER_AUTH
- if(checkprefix("Bearer", auth) &&
- is_valid_auth_separator(auth[6])) {
- *availp |= CURLAUTH_BEARER;
- authp->avail |= CURLAUTH_BEARER;
- if(authp->picked == CURLAUTH_BEARER) {
- /* We asked for Bearer authentication but got a 40X back
- anyway, which basically means our token is not valid. */
- authp->avail = CURLAUTH_NONE;
- infof(data, "Authentication problem. Ignoring this.");
- data->state.authproblem = TRUE;
- }
- }
-#else
- {
- /*
- * Empty block to terminate the if-else chain correctly.
- *
- * A semicolon would yield the same result here, but can cause a
- * compiler warning when -Wextra is enabled.
- */
- }
+ if(authcmp("Bearer", auth))
+ result = auth_bearer(data, authp, availp);
#endif
+ if(result)
+ break;
+
/* there may be multiple methods on one line, so keep reading */
- while(*auth && *auth != ',') /* read up to the next comma */
- auth++;
- if(*auth == ',') /* if we are on a comma, skip it */
- auth++;
- while(*auth && ISSPACE(*auth))
+ auth = strchr(auth, ',');
+ if(auth) /* if we are on a comma, skip it */
auth++;
+ else
+ break;
+ curlx_str_passblanks(&auth);
}
-
+ return result;
+#else
+ (void) proxy;
+ /* nothing to do when disabled */
return CURLE_OK;
+#endif
}
/**
@@ -1067,6 +1154,309 @@ static bool http_should_fail(struct Curl_easy *data, int httpcode)
return data->state.authproblem;
}
+static void http_switch_to_get(struct Curl_easy *data, int code)
+{
+ const char *req = data->set.str[STRING_CUSTOMREQUEST];
+ if((req || data->state.httpreq != HTTPREQ_GET) &&
+ (data->set.http_follow_mode == CURLFOLLOW_OBEYCODE)) {
+ infof(data, "Switch to GET because of %d response", code);
+ data->state.http_ignorecustom = TRUE;
+ }
+ else if(req && (data->set.http_follow_mode != CURLFOLLOW_FIRSTONLY))
+ infof(data, "Stick to %s instead of GET", req);
+
+ data->state.httpreq = HTTPREQ_GET;
+ Curl_creader_set_rewind(data, FALSE);
+}
+
+CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl,
+ followtype type)
+{
+ bool disallowport = FALSE;
+ bool reachedmax = FALSE;
+ char *follow_url = NULL;
+ CURLUcode uc;
+ CURLcode rewind_result;
+ bool switch_to_get = FALSE;
+
+ DEBUGASSERT(type != FOLLOW_NONE);
+
+ if(type != FOLLOW_FAKE)
+ data->state.requests++; /* count all real follows */
+ if(type == FOLLOW_REDIR) {
+ if((data->set.maxredirs != -1) &&
+ (data->state.followlocation >= data->set.maxredirs)) {
+ reachedmax = TRUE;
+ type = FOLLOW_FAKE; /* switch to fake to store the would-be-redirected
+ to URL */
+ }
+ else {
+ data->state.followlocation++; /* count redirect-followings, including
+ auth reloads */
+
+ if(data->set.http_auto_referer) {
+ CURLU *u;
+ char *referer = NULL;
+
+ /* We are asked to automatically set the previous URL as the referer
+ when we get the next URL. We pick the ->url field, which may or may
+ not be 100% correct */
+
+ if(data->state.referer_alloc) {
+ Curl_safefree(data->state.referer);
+ data->state.referer_alloc = FALSE;
+ }
+
+ /* Make a copy of the URL without credentials and fragment */
+ u = curl_url();
+ if(!u)
+ return CURLE_OUT_OF_MEMORY;
+
+ uc = curl_url_set(u, CURLUPART_URL, data->state.url, 0);
+ if(!uc)
+ uc = curl_url_set(u, CURLUPART_FRAGMENT, NULL, 0);
+ if(!uc)
+ uc = curl_url_set(u, CURLUPART_USER, NULL, 0);
+ if(!uc)
+ uc = curl_url_set(u, CURLUPART_PASSWORD, NULL, 0);
+ if(!uc)
+ uc = curl_url_get(u, CURLUPART_URL, &referer, 0);
+
+ curl_url_cleanup(u);
+
+ if(uc || !referer)
+ return CURLE_OUT_OF_MEMORY;
+
+ data->state.referer = referer;
+ data->state.referer_alloc = TRUE; /* yes, free this later */
+ }
+ }
+ }
+
+ if((type != FOLLOW_RETRY) &&
+ (data->req.httpcode != 401) && (data->req.httpcode != 407) &&
+ Curl_is_absolute_url(newurl, NULL, 0, FALSE)) {
+ /* If this is not redirect due to a 401 or 407 response and an absolute
+ URL: do not allow a custom port number */
+ disallowport = TRUE;
+ }
+
+ DEBUGASSERT(data->state.uh);
+ uc = curl_url_set(data->state.uh, CURLUPART_URL, newurl, (unsigned int)
+ ((type == FOLLOW_FAKE) ? CURLU_NON_SUPPORT_SCHEME :
+ ((type == FOLLOW_REDIR) ? CURLU_URLENCODE : 0) |
+ CURLU_ALLOW_SPACE |
+ (data->set.path_as_is ? CURLU_PATH_AS_IS : 0)));
+ if(uc) {
+ if(type != FOLLOW_FAKE) {
+ failf(data, "The redirect target URL could not be parsed: %s",
+ curl_url_strerror(uc));
+ return Curl_uc_to_curlcode(uc);
+ }
+
+ /* the URL could not be parsed for some reason, but since this is FAKE
+ mode, just duplicate the field as-is */
+ follow_url = strdup(newurl);
+ if(!follow_url)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ else {
+ uc = curl_url_get(data->state.uh, CURLUPART_URL, &follow_url, 0);
+ if(uc)
+ return Curl_uc_to_curlcode(uc);
+
+ /* Clear auth if this redirects to a different port number or protocol,
+ unless permitted */
+ if(!data->set.allow_auth_to_other_hosts && (type != FOLLOW_FAKE)) {
+ char *portnum;
+ int port;
+ bool clear = FALSE;
+
+ if(data->set.use_port && data->state.allow_port)
+ /* a custom port is used */
+ port = (int)data->set.use_port;
+ else {
+ uc = curl_url_get(data->state.uh, CURLUPART_PORT, &portnum,
+ CURLU_DEFAULT_PORT);
+ if(uc) {
+ free(follow_url);
+ return Curl_uc_to_curlcode(uc);
+ }
+ port = atoi(portnum);
+ free(portnum);
+ }
+ if(port != data->info.conn_remote_port) {
+ infof(data, "Clear auth, redirects to port from %u to %u",
+ data->info.conn_remote_port, port);
+ clear = TRUE;
+ }
+ else {
+ char *scheme;
+ const struct Curl_handler *p;
+ uc = curl_url_get(data->state.uh, CURLUPART_SCHEME, &scheme, 0);
+ if(uc) {
+ free(follow_url);
+ return Curl_uc_to_curlcode(uc);
+ }
+
+ p = Curl_get_scheme_handler(scheme);
+ if(p && (p->protocol != data->info.conn_protocol)) {
+ infof(data, "Clear auth, redirects scheme from %s to %s",
+ data->info.conn_scheme, scheme);
+ clear = TRUE;
+ }
+ free(scheme);
+ }
+ if(clear) {
+ Curl_safefree(data->state.aptr.user);
+ Curl_safefree(data->state.aptr.passwd);
+ }
+ }
+ }
+ DEBUGASSERT(follow_url);
+
+ if(type == FOLLOW_FAKE) {
+ /* we are only figuring out the new URL if we would have followed locations
+ but now we are done so we can get out! */
+ data->info.wouldredirect = follow_url;
+
+ if(reachedmax) {
+ failf(data, "Maximum (%ld) redirects followed", data->set.maxredirs);
+ return CURLE_TOO_MANY_REDIRECTS;
+ }
+ return CURLE_OK;
+ }
+
+ if(disallowport)
+ data->state.allow_port = FALSE;
+
+ if(data->state.url_alloc)
+ Curl_safefree(data->state.url);
+
+ data->state.url = follow_url;
+ data->state.url_alloc = TRUE;
+ rewind_result = Curl_req_soft_reset(&data->req, data);
+ infof(data, "Issue another request to this URL: '%s'", data->state.url);
+ if((data->set.http_follow_mode == CURLFOLLOW_FIRSTONLY) &&
+ data->set.str[STRING_CUSTOMREQUEST] &&
+ !data->state.http_ignorecustom) {
+ data->state.http_ignorecustom = TRUE;
+ infof(data, "Drop custom request method for next request");
+ }
+
+ /*
+ * We get here when the HTTP code is 300-399 (and 401). We need to perform
+ * differently based on exactly what return code there was.
+ *
+ * News from 7.10.6: we can also get here on a 401 or 407, in case we act on
+ * an HTTP (proxy-) authentication scheme other than Basic.
+ */
+ switch(data->info.httpcode) {
+ /* 401 - Act on a WWW-Authenticate, we keep on moving and do the
+ Authorization: XXXX header in the HTTP request code snippet */
+ /* 407 - Act on a Proxy-Authenticate, we keep on moving and do the
+ Proxy-Authorization: XXXX header in the HTTP request code snippet */
+ /* 300 - Multiple Choices */
+ /* 306 - Not used */
+ /* 307 - Temporary Redirect */
+ default: /* for all above (and the unknown ones) */
+ /* Some codes are explicitly mentioned since I have checked RFC2616 and
+ * they seem to be OK to POST to.
+ */
+ break;
+ case 301: /* Moved Permanently */
+ /* (quote from RFC7231, section 6.4.2)
+ *
+ * Note: For historical reasons, a user agent MAY change the request
+ * method from POST to GET for the subsequent request. If this
+ * behavior is undesired, the 307 (Temporary Redirect) status code
+ * can be used instead.
+ *
+ * ----
+ *
+ * Many webservers expect this, so these servers often answers to a POST
+ * request with an error page. To be sure that libcurl gets the page that
+ * most user agents would get, libcurl has to force GET.
+ *
+ * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and
+ * can be overridden with CURLOPT_POSTREDIR.
+ */
+ if((data->state.httpreq == HTTPREQ_POST
+ || data->state.httpreq == HTTPREQ_POST_FORM
+ || data->state.httpreq == HTTPREQ_POST_MIME)
+ && !(data->set.keep_post & CURL_REDIR_POST_301)) {
+ http_switch_to_get(data, 301);
+ switch_to_get = TRUE;
+ }
+ break;
+ case 302: /* Found */
+ /* (quote from RFC7231, section 6.4.3)
+ *
+ * Note: For historical reasons, a user agent MAY change the request
+ * method from POST to GET for the subsequent request. If this
+ * behavior is undesired, the 307 (Temporary Redirect) status code
+ * can be used instead.
+ *
+ * ----
+ *
+ * Many webservers expect this, so these servers often answers to a POST
+ * request with an error page. To be sure that libcurl gets the page that
+ * most user agents would get, libcurl has to force GET.
+ *
+ * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and
+ * can be overridden with CURLOPT_POSTREDIR.
+ */
+ if((data->state.httpreq == HTTPREQ_POST
+ || data->state.httpreq == HTTPREQ_POST_FORM
+ || data->state.httpreq == HTTPREQ_POST_MIME)
+ && !(data->set.keep_post & CURL_REDIR_POST_302)) {
+ http_switch_to_get(data, 302);
+ switch_to_get = TRUE;
+ }
+ break;
+
+ case 303: /* See Other */
+ /* 'See Other' location is not the resource but a substitute for the
+ * resource. In this case we switch the method to GET/HEAD, unless the
+ * method is POST and the user specified to keep it as POST.
+ * https://github.com/curl/curl/issues/5237#issuecomment-614641049
+ */
+ if(data->state.httpreq != HTTPREQ_GET &&
+ ((data->state.httpreq != HTTPREQ_POST &&
+ data->state.httpreq != HTTPREQ_POST_FORM &&
+ data->state.httpreq != HTTPREQ_POST_MIME) ||
+ !(data->set.keep_post & CURL_REDIR_POST_303))) {
+ http_switch_to_get(data, 303);
+ switch_to_get = TRUE;
+ }
+ break;
+ case 304: /* Not Modified */
+ /* 304 means we did a conditional request and it was "Not modified".
+ * We should not get any Location: header in this response!
+ */
+ break;
+ case 305: /* Use Proxy */
+ /* (quote from RFC2616, section 10.3.6):
+ * "The requested resource MUST be accessed through the proxy given
+ * by the Location field. The Location field gives the URI of the
+ * proxy. The recipient is expected to repeat this single request
+ * via the proxy. 305 responses MUST only be generated by origin
+ * servers."
+ */
+ break;
+ }
+
+ /* When rewind of upload data failed and we are not switching to GET,
+ * we need to fail the follow, as we cannot send the data again. */
+ if(rewind_result && !switch_to_get)
+ return rewind_result;
+
+ Curl_pgrsTime(data, TIMER_REDIRECT);
+ Curl_pgrsResetTransferSizes(data);
+
+ return CURLE_OK;
+}
+
/*
* Curl_compareheader()
*
@@ -1085,9 +1475,8 @@ Curl_compareheader(const char *headerline, /* line to check */
* The field value MAY be preceded by any amount of LWS, though a single SP
* is preferred." */
- size_t len;
- const char *start;
- const char *end;
+ const char *p;
+ struct Curl_str val;
DEBUGASSERT(hlen);
DEBUGASSERT(clen);
DEBUGASSERT(header);
@@ -1097,31 +1486,21 @@ Curl_compareheader(const char *headerline, /* line to check */
return FALSE; /* does not start with header */
/* pass the header */
- start = &headerline[hlen];
-
- /* pass all whitespace */
- while(*start && ISSPACE(*start))
- start++;
-
- /* find the end of the header line */
- end = strchr(start, '\r'); /* lines end with CRLF */
- if(!end) {
- /* in case there is a non-standard compliant line here */
- end = strchr(start, '\n');
-
- if(!end)
- /* hm, there is no line ending here, use the zero byte! */
- end = strchr(start, '\0');
- }
+ p = &headerline[hlen];
- len = end-start; /* length of the content part of the input line */
+ if(curlx_str_untilnl(&p, &val, MAX_HTTP_RESP_HEADER_SIZE))
+ return FALSE;
+ curlx_str_trimblanks(&val);
/* find the content string in the rest of the line */
- for(; len >= clen; len--, start++) {
- if(strncasecompare(start, content, clen))
- return TRUE; /* match! */
+ if(curlx_strlen(&val) >= clen) {
+ size_t len;
+ p = curlx_str(&val);
+ for(len = curlx_strlen(&val); len >= curlx_strlen(&val); len--, p++) {
+ if(strncasecompare(p, content, clen))
+ return TRUE; /* match! */
+ }
}
-
return FALSE; /* no match */
}
@@ -1168,8 +1547,7 @@ CURLcode Curl_http_done(struct Curl_easy *data,
data->state.authhost.multipass = FALSE;
data->state.authproxy.multipass = FALSE;
- Curl_dyn_reset(&data->state.headerb);
- Curl_hyper_done(data);
+ curlx_dyn_reset(&data->state.headerb);
if(status)
return status;
@@ -1193,75 +1571,66 @@ CURLcode Curl_http_done(struct Curl_easy *data,
return CURLE_OK;
}
-/*
- * Determine if we should use HTTP 1.1 (OR BETTER) for this request. Reasons
- * to avoid it include:
- *
- * - if the user specifically requested HTTP 1.0
- * - if the server we are connected to only supports 1.0
- * - if any server previously contacted to handle this request only supports
- * 1.0.
- */
-bool Curl_use_http_1_1plus(const struct Curl_easy *data,
- const struct connectdata *conn)
+/* Determine if we may use HTTP 1.1 for this request. */
+static bool http_may_use_1_1(const struct Curl_easy *data)
{
- if((data->state.httpversion == 10) || (conn->httpversion == 10))
+ const struct connectdata *conn = data->conn;
+ /* We have seen a previous response for *this* transfer with 1.0,
+ * on another connection or the same one. */
+ if(data->state.http_neg.rcvd_min == 10)
return FALSE;
- if((data->state.httpwant == CURL_HTTP_VERSION_1_0) &&
- (conn->httpversion <= 10))
+ /* We have seen a previous response on *this* connection with 1.0. */
+ if(conn && conn->httpversion_seen == 10)
return FALSE;
- return ((data->state.httpwant == CURL_HTTP_VERSION_NONE) ||
- (data->state.httpwant >= CURL_HTTP_VERSION_1_1));
+ /* We want 1.0 and have seen no previous response on *this* connection
+ with a higher version (maybe no response at all yet). */
+ if((data->state.http_neg.only_10) &&
+ (!conn || conn->httpversion_seen <= 10))
+ return FALSE;
+ /* We are not restricted to use 1.0 only. */
+ return !data->state.http_neg.only_10;
}
-#ifndef USE_HYPER
-static const char *get_http_string(const struct Curl_easy *data,
- const struct connectdata *conn)
+static unsigned char http_request_version(struct Curl_easy *data)
{
- if(Curl_conn_is_http3(data, conn, FIRSTSOCKET))
- return "3";
- if(Curl_conn_is_http2(data, conn, FIRSTSOCKET))
- return "2";
- if(Curl_use_http_1_1plus(data, conn))
- return "1.1";
-
- return "1.0";
+ unsigned char v = Curl_conn_http_version(data, data->conn);
+ if(!v) {
+ /* No specific HTTP connection filter installed. */
+ v = http_may_use_1_1(data) ? 11 : 10;
+ }
+ return v;
}
-#endif
-
-enum proxy_use {
- HEADER_SERVER, /* direct to server */
- HEADER_PROXY, /* regular request to proxy */
- HEADER_CONNECT /* sending CONNECT to a proxy */
-};
-static bool hd_name_eq(const char *n1, size_t n1len,
- const char *n2, size_t n2len)
+static const char *get_http_string(int httpversion)
{
- if(n1len == n2len) {
- return strncasecompare(n1, n2, n1len);
+ switch(httpversion) {
+ case 30:
+ return "3";
+ case 20:
+ return "2";
+ case 11:
+ return "1.1";
+ default:
+ return "1.0";
}
- return FALSE;
}
-CURLcode Curl_dynhds_add_custom(struct Curl_easy *data,
- bool is_connect,
- struct dynhds *hds)
+CURLcode Curl_add_custom_headers(struct Curl_easy *data,
+ bool is_connect, int httpversion,
+ struct dynbuf *req)
{
- struct connectdata *conn = data->conn;
- char *ptr;
struct curl_slist *h[2];
struct curl_slist *headers;
int numlists = 1; /* by default */
int i;
#ifndef CURL_DISABLE_PROXY
- enum proxy_use proxy;
+ enum Curl_proxy_use proxy;
if(is_connect)
proxy = HEADER_CONNECT;
else
- proxy = conn->bits.httpproxy && !conn->bits.tunnel_proxy ?
+ proxy = data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy ?
HEADER_PROXY : HEADER_SERVER;
switch(proxy) {
@@ -1290,250 +1659,80 @@ CURLcode Curl_dynhds_add_custom(struct Curl_easy *data,
/* loop through one or two lists */
for(i = 0; i < numlists; i++) {
for(headers = h[i]; headers; headers = headers->next) {
- const char *name, *value;
- size_t namelen, valuelen;
-
- /* There are 2 quirks in place for custom headers:
- * 1. setting only 'name:' to suppress a header from being sent
- * 2. setting only 'name;' to send an empty (illegal) header
- */
- ptr = strchr(headers->data, ':');
- if(ptr) {
- name = headers->data;
- namelen = ptr - headers->data;
- ptr++; /* pass the colon */
- while(*ptr && ISSPACE(*ptr))
- ptr++;
- if(*ptr) {
- value = ptr;
- valuelen = strlen(value);
- }
- else {
- /* quirk #1, suppress this header */
- continue;
- }
- }
+ CURLcode result = CURLE_OK;
+ bool blankheader = FALSE;
+ struct Curl_str name;
+ const char *p = headers->data;
+ const char *origp = p;
+
+ /* explicitly asked to send header without content is done by a header
+ that ends with a semicolon, but there must be no colon present in the
+ name */
+ if(!curlx_str_until(&p, &name, MAX_HTTP_RESP_HEADER_SIZE, ';') &&
+ !curlx_str_single(&p, ';') &&
+ !curlx_str_single(&p, '\0') &&
+ !memchr(curlx_str(&name), ':', curlx_strlen(&name)))
+ blankheader = TRUE;
else {
- ptr = strchr(headers->data, ';');
-
- if(!ptr) {
- /* neither : nor ; in provided header value. We seem
- * to ignore this silently */
- continue;
- }
-
- name = headers->data;
- namelen = ptr - headers->data;
- ptr++; /* pass the semicolon */
- while(*ptr && ISSPACE(*ptr))
- ptr++;
- if(!*ptr) {
- /* quirk #2, send an empty header */
- value = "";
- valuelen = 0;
+ p = origp;
+ if(!curlx_str_until(&p, &name, MAX_HTTP_RESP_HEADER_SIZE, ':') &&
+ !curlx_str_single(&p, ':')) {
+ struct Curl_str val;
+ curlx_str_untilnl(&p, &val, MAX_HTTP_RESP_HEADER_SIZE);
+ curlx_str_trimblanks(&val);
+ if(!curlx_strlen(&val))
+ /* no content, don't send this */
+ continue;
}
- else {
- /* this may be used for something else in the future,
- * ignore this for now */
+ else
+ /* no colon */
continue;
- }
}
- DEBUGASSERT(name && value);
+ /* only send this if the contents was non-blank or done special */
+
if(data->state.aptr.host &&
- /* a Host: header was sent already, do not pass on any custom Host:
- header as that will produce *two* in the same request! */
- hd_name_eq(name, namelen, STRCONST("Host:")))
+ /* a Host: header was sent already, do not pass on any custom
+ Host: header as that will produce *two* in the same
+ request! */
+ curlx_str_casecompare(&name, "Host"))
;
else if(data->state.httpreq == HTTPREQ_POST_FORM &&
/* this header (extended by formdata.c) is sent later */
- hd_name_eq(name, namelen, STRCONST("Content-Type:")))
+ curlx_str_casecompare(&name, "Content-Type"))
;
else if(data->state.httpreq == HTTPREQ_POST_MIME &&
/* this header is sent later */
- hd_name_eq(name, namelen, STRCONST("Content-Type:")))
+ curlx_str_casecompare(&name, "Content-Type"))
;
else if(data->req.authneg &&
/* while doing auth neg, do not allow the custom length since
we will force length zero then */
- hd_name_eq(name, namelen, STRCONST("Content-Length:")))
+ curlx_str_casecompare(&name, "Content-Length"))
;
else if(data->state.aptr.te &&
/* when asking for Transfer-Encoding, do not pass on a custom
Connection: */
- hd_name_eq(name, namelen, STRCONST("Connection:")))
+ curlx_str_casecompare(&name, "Connection"))
;
- else if((conn->httpversion >= 20) &&
- hd_name_eq(name, namelen, STRCONST("Transfer-Encoding:")))
+ else if((httpversion >= 20) &&
+ curlx_str_casecompare(&name, "Transfer-Encoding"))
/* HTTP/2 does not support chunked requests */
;
- else if((hd_name_eq(name, namelen, STRCONST("Authorization:")) ||
- hd_name_eq(name, namelen, STRCONST("Cookie:"))) &&
+ else if((curlx_str_casecompare(&name, "Authorization") ||
+ curlx_str_casecompare(&name, "Cookie")) &&
/* be careful of sending this potentially sensitive header to
other hosts */
!Curl_auth_allowed_to_host(data))
;
- else {
- CURLcode result;
-
- result = Curl_dynhds_add(hds, name, namelen, value, valuelen);
- if(result)
- return result;
- }
- }
- }
-
- return CURLE_OK;
-}
-
-CURLcode Curl_add_custom_headers(struct Curl_easy *data,
- bool is_connect,
-#ifndef USE_HYPER
- struct dynbuf *req
-#else
- void *req
-#endif
- )
-{
- struct connectdata *conn = data->conn;
- char *ptr;
- struct curl_slist *h[2];
- struct curl_slist *headers;
- int numlists = 1; /* by default */
- int i;
-
-#ifndef CURL_DISABLE_PROXY
- enum proxy_use proxy;
-
- if(is_connect)
- proxy = HEADER_CONNECT;
- else
- proxy = conn->bits.httpproxy && !conn->bits.tunnel_proxy ?
- HEADER_PROXY : HEADER_SERVER;
-
- switch(proxy) {
- case HEADER_SERVER:
- h[0] = data->set.headers;
- break;
- case HEADER_PROXY:
- h[0] = data->set.headers;
- if(data->set.sep_headers) {
- h[1] = data->set.proxyheaders;
- numlists++;
- }
- break;
- case HEADER_CONNECT:
- if(data->set.sep_headers)
- h[0] = data->set.proxyheaders;
- else
- h[0] = data->set.headers;
- break;
- }
-#else
- (void)is_connect;
- h[0] = data->set.headers;
-#endif
+ else if(blankheader)
+ result = curlx_dyn_addf(req, "%.*s:\r\n", (int)curlx_strlen(&name),
+ curlx_str(&name));
+ else
+ result = curlx_dyn_addf(req, "%s\r\n", origp);
- /* loop through one or two lists */
- for(i = 0; i < numlists; i++) {
- headers = h[i];
-
- while(headers) {
- char *semicolonp = NULL;
- ptr = strchr(headers->data, ':');
- if(!ptr) {
- char *optr;
- /* no colon, semicolon? */
- ptr = strchr(headers->data, ';');
- if(ptr) {
- optr = ptr;
- ptr++; /* pass the semicolon */
- while(*ptr && ISSPACE(*ptr))
- ptr++;
-
- if(*ptr) {
- /* this may be used for something else in the future */
- optr = NULL;
- }
- else {
- if(*(--ptr) == ';') {
- /* copy the source */
- semicolonp = strdup(headers->data);
- if(!semicolonp) {
-#ifndef USE_HYPER
- Curl_dyn_free(req);
-#endif
- return CURLE_OUT_OF_MEMORY;
- }
- /* put a colon where the semicolon is */
- semicolonp[ptr - headers->data] = ':';
- /* point at the colon */
- optr = &semicolonp [ptr - headers->data];
- }
- }
- ptr = optr;
- }
- }
- if(ptr && (ptr != headers->data)) {
- /* we require a colon for this to be a true header */
-
- ptr++; /* pass the colon */
- while(*ptr && ISSPACE(*ptr))
- ptr++;
-
- if(*ptr || semicolonp) {
- /* only send this if the contents was non-blank or done special */
- CURLcode result = CURLE_OK;
- char *compare = semicolonp ? semicolonp : headers->data;
-
- if(data->state.aptr.host &&
- /* a Host: header was sent already, do not pass on any custom
- Host: header as that will produce *two* in the same
- request! */
- checkprefix("Host:", compare))
- ;
- else if(data->state.httpreq == HTTPREQ_POST_FORM &&
- /* this header (extended by formdata.c) is sent later */
- checkprefix("Content-Type:", compare))
- ;
- else if(data->state.httpreq == HTTPREQ_POST_MIME &&
- /* this header is sent later */
- checkprefix("Content-Type:", compare))
- ;
- else if(data->req.authneg &&
- /* while doing auth neg, do not allow the custom length since
- we will force length zero then */
- checkprefix("Content-Length:", compare))
- ;
- else if(data->state.aptr.te &&
- /* when asking for Transfer-Encoding, do not pass on a custom
- Connection: */
- checkprefix("Connection:", compare))
- ;
- else if((conn->httpversion >= 20) &&
- checkprefix("Transfer-Encoding:", compare))
- /* HTTP/2 does not support chunked requests */
- ;
- else if((checkprefix("Authorization:", compare) ||
- checkprefix("Cookie:", compare)) &&
- /* be careful of sending this potentially sensitive header to
- other hosts */
- !Curl_auth_allowed_to_host(data))
- ;
- else {
-#ifdef USE_HYPER
- result = Curl_hyper_header(data, req, compare);
-#else
- result = Curl_dyn_addf(req, "%s\r\n", compare);
-#endif
- }
- if(semicolonp)
- free(semicolonp);
- if(result)
- return result;
- }
- }
- headers = headers->next;
+ if(result)
+ return result;
}
}
@@ -1542,12 +1741,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data,
#ifndef CURL_DISABLE_PARSEDATE
CURLcode Curl_add_timecondition(struct Curl_easy *data,
-#ifndef USE_HYPER
- struct dynbuf *req
-#else
- void *req
-#endif
- )
+ struct dynbuf *req)
{
const struct tm *tm;
struct tm keeptime;
@@ -1610,12 +1804,7 @@ CURLcode Curl_add_timecondition(struct Curl_easy *data,
tm->tm_min,
tm->tm_sec);
-#ifndef USE_HYPER
- result = Curl_dyn_add(req, datestr);
-#else
- result = Curl_hyper_header(data, req, datestr);
-#endif
-
+ result = curlx_dyn_add(req, datestr);
return result;
}
#else
@@ -1639,8 +1828,10 @@ void Curl_http_method(struct Curl_easy *data, struct connectdata *conn,
httpreq = HTTPREQ_PUT;
/* Now set the 'request' pointer to the proper request string */
- if(data->set.str[STRING_CUSTOMREQUEST])
+ if(data->set.str[STRING_CUSTOMREQUEST] &&
+ !data->state.http_ignorecustom) {
request = data->set.str[STRING_CUSTOMREQUEST];
+ }
else {
if(data->req.no_body)
request = "HEAD";
@@ -1669,7 +1860,7 @@ void Curl_http_method(struct Curl_easy *data, struct connectdata *conn,
*reqp = httpreq;
}
-CURLcode Curl_http_useragent(struct Curl_easy *data)
+static CURLcode http_useragent(struct Curl_easy *data)
{
/* The User-Agent string might have been allocated in url.c already, because
it might have been used in the proxy connect, but if we have got a header
@@ -1683,7 +1874,7 @@ CURLcode Curl_http_useragent(struct Curl_easy *data)
}
-CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn)
+static CURLcode http_host(struct Curl_easy *data, struct connectdata *conn)
{
const char *ptr;
struct dynamically_allocated_data *aptr = &data->state.aptr;
@@ -1733,7 +1924,7 @@ CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn)
if(colon)
*colon = 0; /* The host must not include an embedded port number */
}
- Curl_safefree(aptr->cookiehost);
+ free(aptr->cookiehost);
aptr->cookiehost = cookiehost;
}
#endif
@@ -1773,9 +1964,9 @@ CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn)
/*
* Append the request-target to the HTTP request
*/
-CURLcode Curl_http_target(struct Curl_easy *data,
- struct connectdata *conn,
- struct dynbuf *r)
+static CURLcode http_target(struct Curl_easy *data,
+ struct connectdata *conn,
+ struct dynbuf *r)
{
CURLcode result = CURLE_OK;
const char *path = data->state.up.path;
@@ -1837,11 +2028,11 @@ CURLcode Curl_http_target(struct Curl_easy *data,
curl_url_cleanup(h);
/* target or URL */
- result = Curl_dyn_add(r, data->set.str[STRING_TARGET] ?
+ result = curlx_dyn_add(r, data->set.str[STRING_TARGET] ?
data->set.str[STRING_TARGET] : url);
free(url);
if(result)
- return (result);
+ return result;
if(strcasecompare("ftp", data->state.up.scheme)) {
if(data->set.proxy_transfer_mode) {
@@ -1858,8 +2049,8 @@ CURLcode Curl_http_target(struct Curl_easy *data,
}
}
if(!type) {
- result = Curl_dyn_addf(r, ";type=%c",
- data->state.prefer_ascii ? 'a' : 'i');
+ result = curlx_dyn_addf(r, ";type=%c",
+ data->state.prefer_ascii ? 'a' : 'i');
if(result)
return result;
}
@@ -1872,11 +2063,11 @@ CURLcode Curl_http_target(struct Curl_easy *data,
(void)conn; /* not used in disabled-proxy builds */
#endif
{
- result = Curl_dyn_add(r, path);
+ result = curlx_dyn_add(r, path);
if(result)
return result;
if(query)
- result = Curl_dyn_addf(r, "?%s", query);
+ result = curlx_dyn_addf(r, "?%s", query);
}
return result;
@@ -2055,9 +2246,9 @@ static CURLcode http_resume(struct Curl_easy *data, Curl_HttpReq httpreq)
return CURLE_OK;
}
-CURLcode Curl_http_req_set_reader(struct Curl_easy *data,
- Curl_HttpReq httpreq,
- const char **tep)
+static CURLcode http_req_set_reader(struct Curl_easy *data,
+ Curl_HttpReq httpreq, int httpversion,
+ const char **tep)
{
CURLcode result = CURLE_OK;
const char *ptr;
@@ -2076,12 +2267,10 @@ CURLcode Curl_http_req_set_reader(struct Curl_easy *data,
data->req.upload_chunky =
Curl_compareheader(ptr,
STRCONST("Transfer-Encoding:"), STRCONST("chunked"));
- if(data->req.upload_chunky &&
- Curl_use_http_1_1plus(data, data->conn) &&
- (data->conn->httpversion >= 20)) {
- infof(data, "suppressing chunked transfer encoding on connection "
- "using HTTP version 2 or higher");
- data->req.upload_chunky = FALSE;
+ if(data->req.upload_chunky && (httpversion >= 20)) {
+ infof(data, "suppressing chunked transfer encoding on connection "
+ "using HTTP version 2 or higher");
+ data->req.upload_chunky = FALSE;
}
}
else {
@@ -2089,10 +2278,10 @@ CURLcode Curl_http_req_set_reader(struct Curl_easy *data,
if(req_clen < 0) {
/* indeterminate request content length */
- if(Curl_use_http_1_1plus(data, data->conn)) {
+ if(httpversion > 10) {
/* On HTTP/1.1, enable chunked, on HTTP/2 and later we do not
* need it */
- data->req.upload_chunky = (data->conn->httpversion < 20);
+ data->req.upload_chunky = (httpversion < 20);
}
else {
failf(data, "Chunky upload is not supported by HTTP 1.0");
@@ -2111,7 +2300,7 @@ CURLcode Curl_http_req_set_reader(struct Curl_easy *data,
}
static CURLcode addexpect(struct Curl_easy *data, struct dynbuf *r,
- bool *announced_exp100)
+ int httpversion, bool *announced_exp100)
{
CURLcode result;
char *ptr;
@@ -2130,15 +2319,13 @@ static CURLcode addexpect(struct Curl_easy *data, struct dynbuf *r,
*announced_exp100 =
Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue"));
}
- else if(!data->state.disableexpect &&
- Curl_use_http_1_1plus(data, data->conn) &&
- (data->conn->httpversion < 20)) {
+ else if(!data->state.disableexpect && (httpversion == 11)) {
/* if not doing HTTP 1.0 or version 2, or disabled explicitly, we add an
Expect: 100-continue to the headers which actually speeds up post
operations (as there is one packet coming back from the web server) */
curl_off_t client_len = Curl_creader_client_length(data);
if(client_len > EXPECT_100_THRESHOLD || client_len < 0) {
- result = Curl_dyn_addn(r, STRCONST("Expect: 100-continue\r\n"));
+ result = curlx_dyn_addn(r, STRCONST("Expect: 100-continue\r\n"));
if(result)
return result;
*announced_exp100 = TRUE;
@@ -2147,21 +2334,20 @@ static CURLcode addexpect(struct Curl_easy *data, struct dynbuf *r,
return CURLE_OK;
}
-CURLcode Curl_http_req_complete(struct Curl_easy *data,
- struct dynbuf *r, Curl_HttpReq httpreq)
+static CURLcode http_req_complete(struct Curl_easy *data,
+ struct dynbuf *r, int httpversion,
+ Curl_HttpReq httpreq)
{
CURLcode result = CURLE_OK;
curl_off_t req_clen;
bool announced_exp100 = FALSE;
DEBUGASSERT(data->conn);
-#ifndef USE_HYPER
if(data->req.upload_chunky) {
result = Curl_httpchunk_add_reader(data);
if(result)
return result;
}
-#endif
/* Get the request body length that has been set up */
req_clen = Curl_creader_total_length(data);
@@ -2183,8 +2369,8 @@ CURLcode Curl_http_req_complete(struct Curl_easy *data,
!Curl_checkheaders(data, STRCONST("Content-Length")))) {
/* we allow replacing this header if not during auth negotiation,
although it is not very wise to actually set your own */
- result = Curl_dyn_addf(r, "Content-Length: %" FMT_OFF_T "\r\n",
- req_clen);
+ result = curlx_dyn_addf(r, "Content-Length: %" FMT_OFF_T "\r\n",
+ req_clen);
}
if(result)
goto out;
@@ -2196,7 +2382,7 @@ CURLcode Curl_http_req_complete(struct Curl_easy *data,
struct curl_slist *hdr;
for(hdr = data->state.mimepost->curlheaders; hdr; hdr = hdr->next) {
- result = Curl_dyn_addf(r, "%s\r\n", hdr->data);
+ result = curlx_dyn_addf(r, "%s\r\n", hdr->data);
if(result)
goto out;
}
@@ -2204,13 +2390,13 @@ CURLcode Curl_http_req_complete(struct Curl_easy *data,
#endif
if(httpreq == HTTPREQ_POST) {
if(!Curl_checkheaders(data, STRCONST("Content-Type"))) {
- result = Curl_dyn_addn(r, STRCONST("Content-Type: application/"
- "x-www-form-urlencoded\r\n"));
+ result = curlx_dyn_addn(r, STRCONST("Content-Type: application/"
+ "x-www-form-urlencoded\r\n"));
if(result)
goto out;
}
}
- result = addexpect(data, r, &announced_exp100);
+ result = addexpect(data, r, httpversion, &announced_exp100);
if(result)
goto out;
break;
@@ -2219,7 +2405,7 @@ CURLcode Curl_http_req_complete(struct Curl_easy *data,
}
/* end of headers */
- result = Curl_dyn_addn(r, STRCONST("\r\n"));
+ result = curlx_dyn_addn(r, STRCONST("\r\n"));
if(!result) {
Curl_pgrsSetUploadSize(data, req_clen);
if(announced_exp100)
@@ -2236,9 +2422,9 @@ out:
#if !defined(CURL_DISABLE_COOKIES)
-CURLcode Curl_http_cookies(struct Curl_easy *data,
- struct connectdata *conn,
- struct dynbuf *r)
+static CURLcode http_cookies(struct Curl_easy *data,
+ struct connectdata *conn,
+ struct dynbuf *r)
{
CURLcode result = CURLE_OK;
char *addcookies = NULL;
@@ -2275,7 +2461,7 @@ CURLcode Curl_http_cookies(struct Curl_easy *data,
if(co->value) {
size_t add;
if(!count) {
- result = Curl_dyn_addn(r, STRCONST("Cookie: "));
+ result = curlx_dyn_addn(r, STRCONST("Cookie: "));
if(result)
break;
}
@@ -2286,8 +2472,8 @@ CURLcode Curl_http_cookies(struct Curl_easy *data,
linecap = TRUE;
break;
}
- result = Curl_dyn_addf(r, "%s%s=%s", count ? "; " : "",
- co->name, co->value);
+ result = curlx_dyn_addf(r, "%s%s=%s", count ? "; " : "",
+ co->name, co->value);
if(result)
break;
clen += add + (count ? 2 : 0);
@@ -2298,24 +2484,26 @@ CURLcode Curl_http_cookies(struct Curl_easy *data,
}
if(addcookies && !result && !linecap) {
if(!count)
- result = Curl_dyn_addn(r, STRCONST("Cookie: "));
+ result = curlx_dyn_addn(r, STRCONST("Cookie: "));
if(!result) {
- result = Curl_dyn_addf(r, "%s%s", count ? "; " : "", addcookies);
+ result = curlx_dyn_addf(r, "%s%s", count ? "; " : "", addcookies);
count++;
}
}
if(count && !result)
- result = Curl_dyn_addn(r, STRCONST("\r\n"));
+ result = curlx_dyn_addn(r, STRCONST("\r\n"));
if(result)
return result;
}
return result;
}
+#else
+#define http_cookies(a,b,c) CURLE_OK
#endif
-CURLcode Curl_http_range(struct Curl_easy *data,
- Curl_HttpReq httpreq)
+static CURLcode http_range(struct Curl_easy *data,
+ Curl_HttpReq httpreq)
{
if(data->state.use_range) {
/*
@@ -2347,7 +2535,7 @@ CURLcode Curl_http_range(struct Curl_easy *data,
}
else if(data->state.resume_from) {
/* This is because "resume" was selected */
- /* TODO: not sure if we want to send this header during authentication
+ /* Not sure if we want to send this header during authentication
* negotiation, but test1084 checks for it. In which case we have a
* "null" client reader installed that gives an unexpected length. */
curl_off_t total_len = data->req.authneg ?
@@ -2371,7 +2559,7 @@ CURLcode Curl_http_range(struct Curl_easy *data,
return CURLE_OK;
}
-CURLcode Curl_http_firstwrite(struct Curl_easy *data)
+static CURLcode http_firstwrite(struct Curl_easy *data)
{
struct connectdata *conn = data->conn;
struct SingleRequest *k = &data->req;
@@ -2434,7 +2622,7 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data)
}
#ifdef HAVE_LIBZ
-CURLcode Curl_transferencode(struct Curl_easy *data)
+static CURLcode http_transferencode(struct Curl_easy *data)
{
if(!Curl_checkheaders(data, STRCONST("TE")) &&
data->set.http_transfer_encoding) {
@@ -2466,7 +2654,6 @@ CURLcode Curl_transferencode(struct Curl_easy *data)
}
#endif
-#ifndef USE_HYPER
/*
* Curl_http() gets called from the generic multi_do() function when an HTTP
* request is to be performed. This creates and sends a properly constructed
@@ -2483,6 +2670,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
struct dynbuf req;
char *altused = NULL;
const char *p_accept; /* Accept: string */
+ unsigned char httpversion;
/* Always consider the DO phase done after this function call, even if there
may be parts of the request that are not yet sent, since we can deal with
@@ -2491,29 +2679,29 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
switch(conn->alpn) {
case CURL_HTTP_VERSION_3:
- DEBUGASSERT(Curl_conn_is_http3(data, conn, FIRSTSOCKET));
+ DEBUGASSERT(Curl_conn_http_version(data, conn) == 30);
break;
case CURL_HTTP_VERSION_2:
#ifndef CURL_DISABLE_PROXY
- if(!Curl_conn_is_http2(data, conn, FIRSTSOCKET) &&
+ if((Curl_conn_http_version(data, conn) != 20) &&
conn->bits.proxy && !conn->bits.tunnel_proxy
) {
- result = Curl_http2_switch(data, conn, FIRSTSOCKET);
+ result = Curl_http2_switch(data);
if(result)
goto fail;
}
else
#endif
- DEBUGASSERT(Curl_conn_is_http2(data, conn, FIRSTSOCKET));
+ DEBUGASSERT(Curl_conn_http_version(data, conn) == 20);
break;
case CURL_HTTP_VERSION_1_1:
/* continue with HTTP/1.x when explicitly requested */
break;
default:
/* Check if user wants to use HTTP/2 with clear TCP */
- if(Curl_http2_may_switch(data, conn, FIRSTSOCKET)) {
+ if(Curl_http2_may_switch(data)) {
DEBUGF(infof(data, "HTTP/2 over clean TCP"));
- result = Curl_http2_switch(data, conn, FIRSTSOCKET);
+ result = Curl_http2_switch(data);
if(result)
goto fail;
}
@@ -2527,11 +2715,11 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
if(result)
goto fail;
- result = Curl_http_host(data, conn);
+ result = http_host(data, conn);
if(result)
goto fail;
- result = Curl_http_useragent(data);
+ result = http_useragent(data);
if(result)
goto fail;
@@ -2561,7 +2749,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) &&
data->set.str[STRING_ENCODING]) {
- Curl_safefree(data->state.aptr.accept_encoding);
+ free(data->state.aptr.accept_encoding);
data->state.aptr.accept_encoding =
aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
if(!data->state.aptr.accept_encoding)
@@ -2572,38 +2760,39 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
#ifdef HAVE_LIBZ
/* we only consider transfer-encoding magic if libz support is built-in */
- result = Curl_transferencode(data);
+ result = http_transferencode(data);
if(result)
goto fail;
#endif
- result = Curl_http_req_set_reader(data, httpreq, &te);
+ httpversion = http_request_version(data);
+ httpstring = get_http_string(httpversion);
+
+ result = http_req_set_reader(data, httpreq, httpversion, &te);
if(result)
goto fail;
p_accept = Curl_checkheaders(data,
STRCONST("Accept")) ? NULL : "Accept: */*\r\n";
- result = Curl_http_range(data, httpreq);
+ result = http_range(data, httpreq);
if(result)
goto fail;
- httpstring = get_http_string(data, conn);
-
/* initialize a dynamic send-buffer */
- Curl_dyn_init(&req, DYN_HTTP_REQUEST);
+ curlx_dyn_init(&req, DYN_HTTP_REQUEST);
/* make sure the header buffer is reset - if there are leftovers from a
previous transfer */
- Curl_dyn_reset(&data->state.headerb);
+ curlx_dyn_reset(&data->state.headerb);
/* add the main request stuff */
/* GET/HEAD/POST/PUT */
- result = Curl_dyn_addf(&req, "%s ", request);
+ result = curlx_dyn_addf(&req, "%s ", request);
if(!result)
- result = Curl_http_target(data, conn, &req);
+ result = http_target(data, conn, &req);
if(result) {
- Curl_dyn_free(&req);
+ curlx_dyn_free(&req);
goto fail;
}
@@ -2612,62 +2801,62 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
altused = aprintf("Alt-Used: %s:%d\r\n",
conn->conn_to_host.name, conn->conn_to_port);
if(!altused) {
- Curl_dyn_free(&req);
+ curlx_dyn_free(&req);
return CURLE_OUT_OF_MEMORY;
}
}
#endif
result =
- Curl_dyn_addf(&req,
- " HTTP/%s\r\n" /* HTTP version */
- "%s" /* host */
- "%s" /* proxyuserpwd */
- "%s" /* userpwd */
- "%s" /* range */
- "%s" /* user agent */
- "%s" /* accept */
- "%s" /* TE: */
- "%s" /* accept-encoding */
- "%s" /* referer */
- "%s" /* Proxy-Connection */
- "%s" /* transfer-encoding */
- "%s",/* Alt-Used */
-
- httpstring,
- (data->state.aptr.host ? data->state.aptr.host : ""),
+ curlx_dyn_addf(&req,
+ " HTTP/%s\r\n" /* HTTP version */
+ "%s" /* host */
+ "%s" /* proxyuserpwd */
+ "%s" /* userpwd */
+ "%s" /* range */
+ "%s" /* user agent */
+ "%s" /* accept */
+ "%s" /* TE: */
+ "%s" /* accept-encoding */
+ "%s" /* referer */
+ "%s" /* Proxy-Connection */
+ "%s" /* transfer-encoding */
+ "%s",/* Alt-Used */
+
+ httpstring,
+ (data->state.aptr.host ? data->state.aptr.host : ""),
#ifndef CURL_DISABLE_PROXY
- data->state.aptr.proxyuserpwd ?
- data->state.aptr.proxyuserpwd : "",
+ data->state.aptr.proxyuserpwd ?
+ data->state.aptr.proxyuserpwd : "",
#else
- "",
+ "",
#endif
- data->state.aptr.userpwd ? data->state.aptr.userpwd : "",
- (data->state.use_range && data->state.aptr.rangeline) ?
- data->state.aptr.rangeline : "",
- (data->set.str[STRING_USERAGENT] &&
- *data->set.str[STRING_USERAGENT] &&
- data->state.aptr.uagent) ?
- data->state.aptr.uagent : "",
- p_accept ? p_accept : "",
- data->state.aptr.te ? data->state.aptr.te : "",
- (data->set.str[STRING_ENCODING] &&
- *data->set.str[STRING_ENCODING] &&
- data->state.aptr.accept_encoding) ?
- data->state.aptr.accept_encoding : "",
- (data->state.referer && data->state.aptr.ref) ?
- data->state.aptr.ref : "" /* Referer: <data> */,
+ data->state.aptr.userpwd ? data->state.aptr.userpwd : "",
+ (data->state.use_range && data->state.aptr.rangeline) ?
+ data->state.aptr.rangeline : "",
+ (data->set.str[STRING_USERAGENT] &&
+ *data->set.str[STRING_USERAGENT] &&
+ data->state.aptr.uagent) ?
+ data->state.aptr.uagent : "",
+ p_accept ? p_accept : "",
+ data->state.aptr.te ? data->state.aptr.te : "",
+ (data->set.str[STRING_ENCODING] &&
+ *data->set.str[STRING_ENCODING] &&
+ data->state.aptr.accept_encoding) ?
+ data->state.aptr.accept_encoding : "",
+ (data->state.referer && data->state.aptr.ref) ?
+ data->state.aptr.ref : "" /* Referer: <data> */,
#ifndef CURL_DISABLE_PROXY
- (conn->bits.httpproxy &&
- !conn->bits.tunnel_proxy &&
- !Curl_checkheaders(data, STRCONST("Proxy-Connection")) &&
- !Curl_checkProxyheaders(data, conn,
- STRCONST("Proxy-Connection"))) ?
- "Proxy-Connection: Keep-Alive\r\n":"",
+ (conn->bits.httpproxy &&
+ !conn->bits.tunnel_proxy &&
+ !Curl_checkheaders(data, STRCONST("Proxy-Connection")) &&
+ !Curl_checkProxyheaders(data, conn,
+ STRCONST("Proxy-Connection"))) ?
+ "Proxy-Connection: Keep-Alive\r\n":"",
#else
- "",
+ "",
#endif
- te,
- altused ? altused : ""
+ te,
+ altused ? altused : ""
);
/* clear userpwd and proxyuserpwd to avoid reusing old credentials
@@ -2679,23 +2868,23 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
free(altused);
if(result) {
- Curl_dyn_free(&req);
+ curlx_dyn_free(&req);
goto fail;
}
- if(!(conn->handler->flags&PROTOPT_SSL) &&
- conn->httpversion < 20 &&
- (data->state.httpwant == CURL_HTTP_VERSION_2)) {
+ if(!Curl_conn_is_ssl(conn, FIRSTSOCKET) && (httpversion < 20) &&
+ (data->state.http_neg.wanted & CURL_HTTP_V2x) &&
+ data->state.http_neg.h2_upgrade) {
/* append HTTP2 upgrade magic stuff to the HTTP request if it is not done
over SSL */
result = Curl_http2_request_upgrade(&req, data);
if(result) {
- Curl_dyn_free(&req);
+ curlx_dyn_free(&req);
return result;
}
}
- result = Curl_http_cookies(data, conn, &req);
+ result = http_cookies(data, conn, &req);
#ifndef CURL_DISABLE_WEBSOCKETS
if(!result && conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS))
result = Curl_ws_request(data, &req);
@@ -2703,19 +2892,19 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
if(!result)
result = Curl_add_timecondition(data, &req);
if(!result)
- result = Curl_add_custom_headers(data, FALSE, &req);
+ result = Curl_add_custom_headers(data, FALSE, httpversion, &req);
if(!result) {
/* req_send takes ownership of the 'req' memory on success */
- result = Curl_http_req_complete(data, &req, httpreq);
+ result = http_req_complete(data, &req, httpversion, httpreq);
if(!result)
- result = Curl_req_send(data, &req);
+ result = Curl_req_send(data, &req, httpversion);
}
- Curl_dyn_free(&req);
+ curlx_dyn_free(&req);
if(result)
goto fail;
- if((conn->httpversion >= 20) && data->req.upload_chunky)
+ if((httpversion >= 20) && data->req.upload_chunky)
/* upload_chunky was set above to set up the request in a chunky fashion,
but is disabled here again to avoid that the chunked encoded version is
actually used when sending the request body over h2 */
@@ -2726,8 +2915,6 @@ fail:
return result;
}
-#endif /* USE_HYPER */
-
typedef enum {
STATUS_UNKNOWN, /* not enough data to tell yet */
STATUS_DONE, /* a status line was read */
@@ -2814,10 +3001,10 @@ checkprotoprefix(struct Curl_easy *data, struct connectdata *conn,
Curl_compareheader(hd, STRCONST(n), STRCONST(v)))
/*
- * Curl_http_header() parses a single response header.
+ * http_header() parses a single response header.
*/
-CURLcode Curl_http_header(struct Curl_easy *data,
- const char *hd, size_t hdlen)
+static CURLcode http_header(struct Curl_easy *data,
+ const char *hd, size_t hdlen)
{
struct connectdata *conn = data->conn;
CURLcode result;
@@ -2829,7 +3016,7 @@ CURLcode Curl_http_header(struct Curl_easy *data,
case 'A':
#ifndef CURL_DISABLE_ALTSVC
v = (data->asi &&
- ((data->conn->handler->flags & PROTOPT_SSL) ||
+ (Curl_conn_is_ssl(data->conn, FIRSTSOCKET) ||
#ifdef DEBUGBUILD
/* allow debug builds to circumvent the HTTPS restriction */
getenv("CURL_ALTSVC_HTTP")
@@ -2839,8 +3026,8 @@ CURLcode Curl_http_header(struct Curl_easy *data,
)) ? HD_VAL(hd, hdlen, "Alt-Svc:") : NULL;
if(v) {
/* the ALPN of the current request */
- enum alpnid id = (conn->httpversion == 30) ? ALPN_h3 :
- (conn->httpversion == 20) ? ALPN_h2 : ALPN_h1;
+ enum alpnid id = (k->httpversion == 30) ? ALPN_h3 :
+ (k->httpversion == 20) ? ALPN_h2 : ALPN_h1;
return Curl_altsvc_parse(data, data->asi, v, id, conn->host.name,
curlx_uitous((unsigned int)conn->remote_port));
}
@@ -2853,13 +3040,13 @@ CURLcode Curl_http_header(struct Curl_easy *data,
HD_VAL(hd, hdlen, "Content-Length:") : NULL;
if(v) {
curl_off_t contentlength;
- CURLofft offt = curlx_strtoofft(v, NULL, 10, &contentlength);
+ int offt = curlx_str_numblanks(&v, &contentlength);
- if(offt == CURL_OFFT_OK) {
+ if(offt == STRE_OK) {
k->size = contentlength;
k->maxdownload = k->size;
}
- else if(offt == CURL_OFFT_FLOW) {
+ else if(offt == STRE_OVERFLOW) {
/* out of range */
if(data->set.max_filesize) {
failf(data, "Maximum file size exceeded");
@@ -2897,7 +3084,7 @@ CURLcode Curl_http_header(struct Curl_easy *data,
/* ignore empty data */
free(contenttype);
else {
- Curl_safefree(data->info.contenttype);
+ free(data->info.contenttype);
data->info.contenttype = contenttype;
}
return CURLE_OK;
@@ -2912,7 +3099,7 @@ CURLcode Curl_http_header(struct Curl_easy *data,
streamclose(conn, "Connection: close used");
return CURLE_OK;
}
- if((conn->httpversion == 10) &&
+ if((k->httpversion == 10) &&
HD_IS_AND_SAYS(hd, hdlen, "Connection:", "keep-alive")) {
/*
* An HTTP/1.0 reply with the 'Connection: keep-alive' line
@@ -2945,11 +3132,10 @@ CURLcode Curl_http_header(struct Curl_easy *data,
/* if it truly stopped on a digit */
if(ISDIGIT(*ptr)) {
- if(!curlx_strtoofft(ptr, NULL, 10, &k->offset)) {
- if(data->state.resume_from == k->offset)
- /* we asked for a resume and we got it */
- k->content_range = TRUE;
- }
+ if(!curlx_str_number(&ptr, &k->offset, CURL_OFF_T_MAX) &&
+ (data->state.resume_from == k->offset))
+ /* we asked for a resume and we got it */
+ k->content_range = TRUE;
}
else if(k->httpcode < 300)
data->state.resume_from = 0; /* get everything */
@@ -2979,7 +3165,7 @@ CURLcode Curl_http_header(struct Curl_easy *data,
else {
data->req.location = location;
- if(data->set.http_follow_location) {
+ if(data->set.http_follow_mode) {
DEBUGASSERT(!data->req.newurl);
data->req.newurl = strdup(data->req.location); /* clone */
if(!data->req.newurl)
@@ -3002,7 +3188,7 @@ CURLcode Curl_http_header(struct Curl_easy *data,
#ifndef CURL_DISABLE_PROXY
v = HD_VAL(hd, hdlen, "Proxy-Connection:");
if(v) {
- if((conn->httpversion == 10) && conn->bits.httpproxy &&
+ if((k->httpversion == 10) && conn->bits.httpproxy &&
HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "keep-alive")) {
/*
* When an HTTP/1.0 reply comes when using a proxy, the
@@ -3013,7 +3199,7 @@ CURLcode Curl_http_header(struct Curl_easy *data,
connkeep(conn, "Proxy-Connection keep-alive"); /* do not close */
infof(data, "HTTP/1.0 proxy connection set to keep alive");
}
- else if((conn->httpversion == 11) && conn->bits.httpproxy &&
+ else if((k->httpversion == 11) && conn->bits.httpproxy &&
HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "close")) {
/*
* We get an HTTP/1.1 response from a proxy and it says it will
@@ -3056,15 +3242,27 @@ CURLcode Curl_http_header(struct Curl_easy *data,
if(v) {
/* Retry-After = HTTP-date / delay-seconds */
curl_off_t retry_after = 0; /* zero for unknown or "now" */
- /* Try it as a decimal number, if it works it is not a date */
- (void)curlx_strtoofft(v, NULL, 10, &retry_after);
- if(!retry_after) {
- time_t date = Curl_getdate_capped(v);
- if(-1 != date)
+ time_t date;
+ curlx_str_passblanks(&v);
+
+ /* try it as a date first, because a date can otherwise start with and
+ get treated as a number */
+ date = Curl_getdate_capped(v);
+
+ if((time_t)-1 != date) {
+ time_t current = time(NULL);
+ if(date >= current)
/* convert date to number of seconds into the future */
- retry_after = date - time(NULL);
+ retry_after = date - current;
}
- data->info.retry_after = retry_after; /* store it */
+ else
+ /* Try it as a decimal number */
+ curlx_str_number(&v, &retry_after, CURL_OFF_T_MAX);
+ /* limit to 6 hours max. this is not documented so that it can be changed
+ in the future if necessary. */
+ if(retry_after > 21600)
+ retry_after = 21600;
+ data->info.retry_after = retry_after;
return CURLE_OK;
}
break;
@@ -3095,7 +3293,7 @@ CURLcode Curl_http_header(struct Curl_easy *data,
#ifndef CURL_DISABLE_HSTS
/* If enabled, the header is incoming and this is over HTTPS */
v = (data->hsts &&
- ((conn->handler->flags & PROTOPT_SSL) ||
+ (Curl_conn_is_ssl(conn, FIRSTSOCKET) ||
#ifdef DEBUGBUILD
/* allow debug builds to circumvent the HTTPS restriction */
getenv("CURL_HSTS_HTTP")
@@ -3179,8 +3377,8 @@ CURLcode Curl_http_header(struct Curl_easy *data,
* Called after the first HTTP response line (the status line) has been
* received and parsed.
*/
-CURLcode Curl_http_statusline(struct Curl_easy *data,
- struct connectdata *conn)
+static CURLcode http_statusline(struct Curl_easy *data,
+ struct connectdata *conn)
{
struct SingleRequest *k = &data->req;
@@ -3194,11 +3392,11 @@ CURLcode Curl_http_statusline(struct Curl_easy *data,
case 30:
#endif
/* no major version switch mid-connection */
- if(conn->httpversion &&
- (k->httpversion/10 != conn->httpversion/10)) {
+ if(k->httpversion_sent &&
+ (k->httpversion/10 != k->httpversion_sent/10)) {
failf(data, "Version mismatch (from HTTP/%u to HTTP/%u)",
- conn->httpversion/10, k->httpversion/10);
- return CURLE_UNSUPPORTED_PROTOCOL;
+ k->httpversion_sent/10, k->httpversion/10);
+ return CURLE_WEIRD_SERVER_REPLY;
}
break;
default:
@@ -3209,11 +3407,12 @@ CURLcode Curl_http_statusline(struct Curl_easy *data,
data->info.httpcode = k->httpcode;
data->info.httpversion = k->httpversion;
- conn->httpversion = (unsigned char)k->httpversion;
+ conn->httpversion_seen = (unsigned char)k->httpversion;
- if(!data->state.httpversion || data->state.httpversion > k->httpversion)
+ if(!data->state.http_neg.rcvd_min ||
+ data->state.http_neg.rcvd_min > k->httpversion)
/* store the lowest server version we encounter */
- data->state.httpversion = (unsigned char)k->httpversion;
+ data->state.http_neg.rcvd_min = (unsigned char)k->httpversion;
/*
* This code executes as part of processing the header. As a
@@ -3273,7 +3472,7 @@ CURLcode Curl_http_statusline(struct Curl_easy *data,
figured out here after all headers have been received but before the final
call to the user's header callback, so that a valid content length can be
retrieved by the user in the final call. */
-CURLcode Curl_http_size(struct Curl_easy *data)
+static CURLcode http_size(struct Curl_easy *data)
{
struct SingleRequest *k = &data->req;
if(data->req.ignore_cl || k->chunk) {
@@ -3356,7 +3555,7 @@ static CURLcode http_write_header(struct Curl_easy *data,
/* now, only output this if the header AND body are requested:
*/
- Curl_debug(data, CURLINFO_HEADER_IN, (char *)hd, hdlen);
+ Curl_debug(data, CURLINFO_HEADER_IN, hd, hdlen);
writetype = CLIENTWRITE_HEADER |
((data->req.httpcode/100 == 1) ? CLIENTWRITE_1XX : 0);
@@ -3389,7 +3588,7 @@ static CURLcode http_on_response(struct Curl_easy *data,
if(k->upgr101 == UPGR101_RECEIVED) {
/* supposedly upgraded to http2 now */
- if(conn->httpversion != 20)
+ if(data->req.httpversion != 20)
infof(data, "Lying server, not serving HTTP/2");
}
@@ -3421,12 +3620,11 @@ static CURLcode http_on_response(struct Curl_easy *data,
* that tells us that the server is OK with this and ready
* to receive the data.
*/
- Curl_http_exp100_got100(data);
+ http_exp100_got100(data);
break;
case 101:
/* Switching Protocols only allowed from HTTP/1.1 */
-
- if(conn->httpversion != 11) {
+ if(k->httpversion_sent != 11) {
/* invalid for other HTTP versions */
failf(data, "unexpected 101 response code");
result = CURLE_WEIRD_SERVER_REPLY;
@@ -3440,6 +3638,7 @@ static CURLcode http_on_response(struct Curl_easy *data,
/* We expect more response from HTTP/2 later */
k->header = TRUE;
k->headerline = 0; /* restart the header line counter */
+ k->httpversion_sent = 20; /* It's an HTTP/2 request now */
/* Any remaining `buf` bytes are already HTTP/2 and passed to
* be processed. */
result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen);
@@ -3461,10 +3660,9 @@ static CURLcode http_on_response(struct Curl_easy *data,
}
#endif
else {
- /* We silently accept this as the final response.
- * TODO: this looks, uhm, wrong. What are we switching to if we
- * did not ask for an Upgrade? Maybe the application provided an
- * `Upgrade: xxx` header? */
+ /* We silently accept this as the final response. What are we
+ * switching to if we did not ask for an Upgrade? Maybe the
+ * application provided an `Upgrade: xxx` header? */
k->header = FALSE;
}
break;
@@ -3488,7 +3686,7 @@ static CURLcode http_on_response(struct Curl_easy *data,
}
if((k->size == -1) && !k->chunk && !conn->bits.close &&
- (conn->httpversion == 11) &&
+ (k->httpversion == 11) &&
!(conn->handler->protocol & CURLPROTO_RTSP) &&
data->state.httpreq != HTTPREQ_HEAD) {
/* On HTTP 1.1, when connection is not to get closed, but no
@@ -3580,7 +3778,7 @@ static CURLcode http_on_response(struct Curl_easy *data,
* connection for closure after we have read the entire response.
*/
if(!Curl_req_done_sending(data)) {
- if((k->httpcode == 417) && Curl_http_exp100_is_selected(data)) {
+ if((k->httpcode == 417) && http_exp100_is_selected(data)) {
/* 417 Expectation Failed - try again without the Expect
header */
if(!k->writebytecount && http_exp100_is_waiting(data)) {
@@ -3637,19 +3835,17 @@ static CURLcode http_on_response(struct Curl_easy *data,
like to call http2_handle_stream_close to properly close a
stream. In order to do this, we keep reading until we
close the stream. */
- if(0 == k->maxdownload
- && !Curl_conn_is_http2(data, conn, FIRSTSOCKET)
- && !Curl_conn_is_http3(data, conn, FIRSTSOCKET))
+ if((0 == k->maxdownload) && (k->httpversion_sent < 20))
k->download_done = TRUE;
/* final response without error, prepare to receive the body */
- result = Curl_http_firstwrite(data);
+ result = http_firstwrite(data);
if(!result)
/* This is the last response that we get for the current request.
* Check on the body size and determine if the response is complete.
*/
- result = Curl_http_size(data);
+ result = http_size(data);
out:
if(last_hd) {
@@ -3676,8 +3872,8 @@ static CURLcode http_rw_hd(struct Curl_easy *data,
struct dynbuf last_header;
size_t consumed;
- Curl_dyn_init(&last_header, hdlen + 1);
- result = Curl_dyn_addn(&last_header, hd, hdlen);
+ curlx_dyn_init(&last_header, hdlen + 1);
+ result = curlx_dyn_addn(&last_header, hd, hdlen);
if(result)
return result;
@@ -3685,12 +3881,12 @@ static CURLcode http_rw_hd(struct Curl_easy *data,
/* Caveat: we clear anything in the header brigade, because a
* response might switch HTTP version which may call use recursively.
* Not nice, but that is currently the way of things. */
- Curl_dyn_reset(&data->state.headerb);
- result = http_on_response(data, Curl_dyn_ptr(&last_header),
- Curl_dyn_len(&last_header),
+ curlx_dyn_reset(&data->state.headerb);
+ result = http_on_response(data, curlx_dyn_ptr(&last_header),
+ curlx_dyn_len(&last_header),
buf_remain, blen, &consumed);
*pconsumed += consumed;
- Curl_dyn_free(&last_header);
+ curlx_dyn_free(&last_header);
return result;
}
@@ -3715,8 +3911,7 @@ static CURLcode http_rw_hd(struct Curl_easy *data,
*/
const char *p = hd;
- while(*p && ISBLANK(*p))
- p++;
+ curlx_str_passblanks(&p);
if(!strncmp(p, "HTTP/", 5)) {
p += 5;
switch(*p) {
@@ -3724,14 +3919,14 @@ static CURLcode http_rw_hd(struct Curl_easy *data,
p++;
if((p[0] == '.') && (p[1] == '0' || p[1] == '1')) {
if(ISBLANK(p[2])) {
- k->httpversion = 10 + (p[1] - '0');
+ k->httpversion = (unsigned char)(10 + (p[1] - '0'));
p += 3;
if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
(p[2] - '0');
- p += 3;
- if(ISSPACE(*p))
- fine_statusline = TRUE;
+ /* RFC 9112 requires a single space following the status code,
+ but the browsers don't so let's not insist */
+ fine_statusline = TRUE;
}
}
}
@@ -3744,13 +3939,13 @@ static CURLcode http_rw_hd(struct Curl_easy *data,
case '3':
if(!ISBLANK(p[1]))
break;
- k->httpversion = (*p - '0') * 10;
+ k->httpversion = (unsigned char)((*p - '0') * 10);
p += 2;
if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
(p[2] - '0');
p += 3;
- if(!ISSPACE(*p))
+ if(!ISBLANK(*p))
break;
fine_statusline = TRUE;
}
@@ -3775,34 +3970,26 @@ static CURLcode http_rw_hd(struct Curl_easy *data,
}
else if(data->conn->handler->protocol & CURLPROTO_RTSP) {
const char *p = hd;
- while(*p && ISBLANK(*p))
- p++;
- if(!strncmp(p, "RTSP/", 5)) {
- p += 5;
- if(ISDIGIT(*p)) {
- p++;
- if((p[0] == '.') && ISDIGIT(p[1])) {
- if(ISBLANK(p[2])) {
- p += 3;
- if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
- k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
- (p[2] - '0');
- p += 3;
- if(ISSPACE(*p)) {
- fine_statusline = TRUE;
- k->httpversion = 11; /* RTSP acts like HTTP 1.1 */
- }
- }
- }
- }
+ struct Curl_str ver;
+ curl_off_t status;
+ /* we set the max string a little excessive to forgive some leading
+ spaces */
+ if(!curlx_str_until(&p, &ver, 32, ' ') &&
+ !curlx_str_single(&p, ' ') &&
+ !curlx_str_number(&p, &status, 999)) {
+ curlx_str_trimblanks(&ver);
+ if(curlx_str_cmp(&ver, "RTSP/1.0")) {
+ k->httpcode = (int)status;
+ fine_statusline = TRUE;
+ k->httpversion = 11; /* RTSP acts like HTTP 1.1 */
}
- if(!fine_statusline)
- return CURLE_WEIRD_SERVER_REPLY;
}
+ if(!fine_statusline)
+ return CURLE_WEIRD_SERVER_REPLY;
}
if(fine_statusline) {
- result = Curl_http_statusline(data, data->conn);
+ result = http_statusline(data, data->conn);
if(result)
return result;
writetype |= CLIENTWRITE_STATUS;
@@ -3817,14 +4004,14 @@ static CURLcode http_rw_hd(struct Curl_easy *data,
if(result)
return result;
- result = Curl_http_header(data, hd, hdlen);
+ result = http_header(data, hd, hdlen);
if(result)
return result;
/*
* Taken in one (more) header. Write it to the client.
*/
- Curl_debug(data, CURLINFO_HEADER_IN, (char *)hd, hdlen);
+ Curl_debug(data, CURLINFO_HEADER_IN, hd, hdlen);
if(k->httpcode/100 == 1)
writetype |= CLIENTWRITE_1XX;
@@ -3861,7 +4048,7 @@ static CURLcode http_parse_headers(struct Curl_easy *data,
if(!end_ptr) {
/* Not a complete header line within buffer, append the data to
the end of the headerbuff. */
- result = Curl_dyn_addn(&data->state.headerb, buf, blen);
+ result = curlx_dyn_addn(&data->state.headerb, buf, blen);
if(result)
return result;
*pconsumed += blen;
@@ -3870,18 +4057,19 @@ static CURLcode http_parse_headers(struct Curl_easy *data,
/* check if this looks like a protocol header */
statusline st =
checkprotoprefix(data, conn,
- Curl_dyn_ptr(&data->state.headerb),
- Curl_dyn_len(&data->state.headerb));
+ curlx_dyn_ptr(&data->state.headerb),
+ curlx_dyn_len(&data->state.headerb));
if(st == STATUS_BAD) {
- /* this is not the beginning of a protocol first header line */
+ /* this is not the beginning of a protocol first header line.
+ * Cannot be 0.9 if version was detected or connection was reused. */
k->header = FALSE;
streamclose(conn, "bad HTTP: No end-of-message indicator");
- if(conn->httpversion >= 10) {
+ if((k->httpversion >= 10) || conn->bits.reuse) {
failf(data, "Invalid status line");
return CURLE_WEIRD_SERVER_REPLY;
}
- if(!data->set.http09_allowed) {
+ if(!data->state.http_neg.accept_09) {
failf(data, "Received HTTP/0.9 when not allowed");
return CURLE_UNSUPPORTED_PROTOCOL;
}
@@ -3894,7 +4082,7 @@ static CURLcode http_parse_headers(struct Curl_easy *data,
/* decrease the size of the remaining (supposed) header line */
consumed = (end_ptr - buf) + 1;
- result = Curl_dyn_addn(&data->state.headerb, buf, consumed);
+ result = curlx_dyn_addn(&data->state.headerb, buf, consumed);
if(result)
return result;
blen -= consumed;
@@ -3908,16 +4096,17 @@ static CURLcode http_parse_headers(struct Curl_easy *data,
if(!k->headerline) {
/* the first read header */
statusline st = checkprotoprefix(data, conn,
- Curl_dyn_ptr(&data->state.headerb),
- Curl_dyn_len(&data->state.headerb));
+ curlx_dyn_ptr(&data->state.headerb),
+ curlx_dyn_len(&data->state.headerb));
if(st == STATUS_BAD) {
streamclose(conn, "bad HTTP: No end-of-message indicator");
- /* this is not the beginning of a protocol first header line */
- if(conn->httpversion >= 10) {
+ /* this is not the beginning of a protocol first header line.
+ * Cannot be 0.9 if version was detected or connection was reused. */
+ if((k->httpversion >= 10) || conn->bits.reuse) {
failf(data, "Invalid status line");
return CURLE_WEIRD_SERVER_REPLY;
}
- if(!data->set.http09_allowed) {
+ if(!data->state.http_neg.accept_09) {
failf(data, "Received HTTP/0.9 when not allowed");
return CURLE_UNSUPPORTED_PROTOCOL;
}
@@ -3927,13 +4116,13 @@ static CURLcode http_parse_headers(struct Curl_easy *data,
}
}
- result = http_rw_hd(data, Curl_dyn_ptr(&data->state.headerb),
- Curl_dyn_len(&data->state.headerb),
+ result = http_rw_hd(data, curlx_dyn_ptr(&data->state.headerb),
+ curlx_dyn_len(&data->state.headerb),
buf, blen, &consumed);
/* We are done with this line. We reset because response
* processing might switch to HTTP/2 and that might call us
* directly again. */
- Curl_dyn_reset(&data->state.headerb);
+ curlx_dyn_reset(&data->state.headerb);
if(consumed) {
blen -= consumed;
buf += consumed;
@@ -3948,7 +4137,7 @@ static CURLcode http_parse_headers(struct Curl_easy *data,
buffer. */
out:
if(!k->header && !leftover_body) {
- Curl_dyn_free(&data->state.headerb);
+ curlx_dyn_free(&data->state.headerb);
}
return CURLE_OK;
}
@@ -3986,15 +4175,15 @@ CURLcode Curl_http_write_resp_hds(struct Curl_easy *data,
result = http_parse_headers(data, buf, blen, pconsumed);
if(!result && !data->req.header) {
- if(!data->req.no_body && Curl_dyn_len(&data->state.headerb)) {
+ if(!data->req.no_body && curlx_dyn_len(&data->state.headerb)) {
/* leftover from parsing something that turned out not
* to be a header, only happens if we allow for
* HTTP/0.9 like responses */
result = Curl_client_write(data, CLIENTWRITE_BODY,
- Curl_dyn_ptr(&data->state.headerb),
- Curl_dyn_len(&data->state.headerb));
+ curlx_dyn_ptr(&data->state.headerb),
+ curlx_dyn_len(&data->state.headerb));
}
- Curl_dyn_free(&data->state.headerb);
+ curlx_dyn_free(&data->state.headerb);
}
return result;
}
@@ -4023,7 +4212,7 @@ CURLcode Curl_http_write_resp(struct Curl_easy *data,
flags = CLIENTWRITE_BODY;
if(is_eos)
flags |= CLIENTWRITE_EOS;
- result = Curl_client_write(data, flags, (char *)buf, blen);
+ result = Curl_client_write(data, flags, buf, blen);
}
out:
return result;
@@ -4063,11 +4252,9 @@ CURLcode Curl_http_req_make(struct httpreq **preq,
struct httpreq *req;
CURLcode result = CURLE_OUT_OF_MEMORY;
- DEBUGASSERT(method);
- if(m_len + 1 > sizeof(req->method))
- return CURLE_BAD_FUNCTION_ARGUMENT;
+ DEBUGASSERT(method && m_len);
- req = calloc(1, sizeof(*req));
+ req = calloc(1, sizeof(*req) + m_len);
if(!req)
goto out;
memcpy(req->method, method, m_len);
@@ -4105,7 +4292,7 @@ static CURLcode req_assign_url_authority(struct httpreq *req, CURLU *url)
CURLcode result = CURLE_URL_MALFORMAT;
user = pass = host = port = NULL;
- Curl_dyn_init(&buf, DYN_HTTP_REQUEST);
+ curlx_dyn_init(&buf, DYN_HTTP_REQUEST);
uc = curl_url_get(url, CURLUPART_HOST, &host, 0);
if(uc && uc != CURLUE_NO_HOST)
@@ -4129,27 +4316,27 @@ static CURLcode req_assign_url_authority(struct httpreq *req, CURLU *url)
}
if(user) {
- result = Curl_dyn_add(&buf, user);
+ result = curlx_dyn_add(&buf, user);
if(result)
goto out;
if(pass) {
- result = Curl_dyn_addf(&buf, ":%s", pass);
+ result = curlx_dyn_addf(&buf, ":%s", pass);
if(result)
goto out;
}
- result = Curl_dyn_add(&buf, "@");
+ result = curlx_dyn_add(&buf, "@");
if(result)
goto out;
}
- result = Curl_dyn_add(&buf, host);
+ result = curlx_dyn_add(&buf, host);
if(result)
goto out;
if(port) {
- result = Curl_dyn_addf(&buf, ":%s", port);
+ result = curlx_dyn_addf(&buf, ":%s", port);
if(result)
goto out;
}
- req->authority = strdup(Curl_dyn_ptr(&buf));
+ req->authority = strdup(curlx_dyn_ptr(&buf));
if(!req->authority)
goto out;
result = CURLE_OK;
@@ -4159,7 +4346,7 @@ out:
free(pass);
free(host);
free(port);
- Curl_dyn_free(&buf);
+ curlx_dyn_free(&buf);
return result;
}
@@ -4171,7 +4358,7 @@ static CURLcode req_assign_url_path(struct httpreq *req, CURLU *url)
CURLcode result = CURLE_URL_MALFORMAT;
path = query = NULL;
- Curl_dyn_init(&buf, DYN_HTTP_REQUEST);
+ curlx_dyn_init(&buf, DYN_HTTP_REQUEST);
uc = curl_url_get(url, CURLUPART_PATH, &path, CURLU_PATH_AS_IS);
if(uc)
@@ -4189,16 +4376,16 @@ static CURLcode req_assign_url_path(struct httpreq *req, CURLU *url)
}
else {
if(path) {
- result = Curl_dyn_add(&buf, path);
+ result = curlx_dyn_add(&buf, path);
if(result)
goto out;
}
if(query) {
- result = Curl_dyn_addf(&buf, "?%s", query);
+ result = curlx_dyn_addf(&buf, "?%s", query);
if(result)
goto out;
}
- req->path = strdup(Curl_dyn_ptr(&buf));
+ req->path = strdup(curlx_dyn_ptr(&buf));
if(!req->path)
goto out;
}
@@ -4207,7 +4394,7 @@ static CURLcode req_assign_url_path(struct httpreq *req, CURLU *url)
out:
free(path);
free(query);
- Curl_dyn_free(&buf);
+ curlx_dyn_free(&buf);
return result;
}
@@ -4219,11 +4406,9 @@ CURLcode Curl_http_req_make2(struct httpreq **preq,
CURLcode result = CURLE_OUT_OF_MEMORY;
CURLUcode uc;
- DEBUGASSERT(method);
- if(m_len + 1 > sizeof(req->method))
- return CURLE_BAD_FUNCTION_ARGUMENT;
+ DEBUGASSERT(method && m_len);
- req = calloc(1, sizeof(*req));
+ req = calloc(1, sizeof(*req) + m_len);
if(!req)
goto out;
memcpy(req->method, method, m_len);
@@ -4272,6 +4457,7 @@ struct name_const {
size_t namelen;
};
+/* keep them sorted by length! */
static struct name_const H2_NON_FIELD[] = {
{ STRCONST("Host") },
{ STRCONST("Upgrade") },
@@ -4281,15 +4467,44 @@ static struct name_const H2_NON_FIELD[] = {
{ STRCONST("Transfer-Encoding") },
};
-static bool h2_non_field(const char *name, size_t namelen)
+static bool h2_permissible_field(struct dynhds_entry *e)
{
size_t i;
- for(i = 0; i < sizeof(H2_NON_FIELD)/sizeof(H2_NON_FIELD[0]); ++i) {
- if(namelen < H2_NON_FIELD[i].namelen)
+ for(i = 0; i < CURL_ARRAYSIZE(H2_NON_FIELD); ++i) {
+ if(e->namelen < H2_NON_FIELD[i].namelen)
+ return TRUE;
+ if(e->namelen == H2_NON_FIELD[i].namelen &&
+ strcasecompare(H2_NON_FIELD[i].name, e->name))
return FALSE;
- if(namelen == H2_NON_FIELD[i].namelen &&
- strcasecompare(H2_NON_FIELD[i].name, name))
+ }
+ return TRUE;
+}
+
+static bool http_TE_has_token(const char *fvalue, const char *token)
+{
+ while(*fvalue) {
+ struct Curl_str name;
+
+ /* skip to first token */
+ while(ISBLANK(*fvalue) || *fvalue == ',')
+ fvalue++;
+ if(curlx_str_cspn(&fvalue, &name, " \t\r;,"))
+ return FALSE;
+ if(curlx_str_casecompare(&name, token))
return TRUE;
+
+ /* skip any remainder after token, e.g. parameters with quoted strings */
+ while(*fvalue && *fvalue != ',') {
+ if(*fvalue == '"') {
+ struct Curl_str qw;
+ /* if we do not cleanly find a quoted word here, the header value
+ * does not follow HTTP syntax and we reject */
+ if(curlx_str_quotedword(&fvalue, &qw, CURL_MAX_HTTP_HEADER))
+ return FALSE;
+ }
+ else
+ fvalue++;
+ }
}
return FALSE;
}
@@ -4312,12 +4527,11 @@ CURLcode Curl_http_req_to_h2(struct dynhds *h2_headers,
scheme = Curl_checkheaders(data, STRCONST(HTTP_PSEUDO_SCHEME));
if(scheme) {
scheme += sizeof(HTTP_PSEUDO_SCHEME);
- while(*scheme && ISBLANK(*scheme))
- scheme++;
+ curlx_str_passblanks(&scheme);
infof(data, "set pseudo header %s to %s", HTTP_PSEUDO_SCHEME, scheme);
}
else {
- scheme = (data->conn && data->conn->handler->flags & PROTOPT_SSL) ?
+ scheme = Curl_conn_is_ssl(data->conn, FIRSTSOCKET) ?
"https" : "http";
}
}
@@ -4349,7 +4563,14 @@ CURLcode Curl_http_req_to_h2(struct dynhds *h2_headers,
}
for(i = 0; !result && i < Curl_dynhds_count(&req->headers); ++i) {
e = Curl_dynhds_getn(&req->headers, i);
- if(!h2_non_field(e->name, e->namelen)) {
+ /* "TE" is special in that it is only permissible when it
+ * has only value "trailers". RFC 9113 ch. 8.2.2 */
+ if(e->namelen == 2 && strcasecompare("TE", e->name)) {
+ if(http_TE_has_token(e->value, "trailers"))
+ result = Curl_dynhds_add(h2_headers, e->name, e->namelen,
+ "trailers", sizeof("trailers") - 1);
+ }
+ else if(h2_permissible_field(e)) {
result = Curl_dynhds_add(h2_headers, e->name, e->namelen,
e->value, e->valuelen);
}
@@ -4441,7 +4662,7 @@ static CURLcode cr_exp100_read(struct Curl_easy *data,
DEBUGF(infof(data, "cr_exp100_read, start AWAITING_CONTINUE, "
"timeout %ldms", data->set.expect_100_timeout));
ctx->state = EXP100_AWAITING_CONTINUE;
- ctx->start = Curl_now();
+ ctx->start = curlx_now();
Curl_expire(data, data->set.expect_100_timeout, EXPIRE_100_TIMEOUT);
data->req.keepon &= ~KEEP_SEND;
data->req.keepon |= KEEP_SEND_TIMED;
@@ -4454,7 +4675,7 @@ static CURLcode cr_exp100_read(struct Curl_easy *data,
*eos = FALSE;
return CURLE_READ_ERROR;
case EXP100_AWAITING_CONTINUE:
- ms = Curl_timediff(Curl_now(), ctx->start);
+ ms = curlx_timediff(curlx_now(), ctx->start);
if(ms < data->set.expect_100_timeout) {
DEBUGF(infof(data, "cr_exp100_read, AWAITING_CONTINUE, not expired"));
data->req.keepon &= ~KEEP_SEND;
@@ -4516,7 +4737,7 @@ static CURLcode http_exp100_add_reader(struct Curl_easy *data)
return result;
}
-void Curl_http_exp100_got100(struct Curl_easy *data)
+static void http_exp100_got100(struct Curl_easy *data)
{
struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100);
if(r)
@@ -4528,7 +4749,7 @@ static bool http_exp100_is_waiting(struct Curl_easy *data)
struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100);
if(r) {
struct cr_exp100_ctx *ctx = r->ctx;
- return (ctx->state == EXP100_AWAITING_CONTINUE);
+ return ctx->state == EXP100_AWAITING_CONTINUE;
}
return FALSE;
}
@@ -4540,7 +4761,7 @@ static void http_exp100_send_anyway(struct Curl_easy *data)
http_exp100_continue(data, r);
}
-bool Curl_http_exp100_is_selected(struct Curl_easy *data)
+static bool http_exp100_is_selected(struct Curl_easy *data)
{
struct Curl_creader *r = Curl_creader_get_by_type(data, &cr_exp100);
return !!r;
diff --git a/libs/libcurl/src/http.h b/libs/libcurl/src/http.h
index 677825ae73..5b05e74be9 100644
--- a/libs/libcurl/src/http.h
+++ b/libs/libcurl/src/http.h
@@ -42,6 +42,24 @@ typedef enum {
HTTPREQ_HEAD
} Curl_HttpReq;
+
+/* When redirecting transfers. */
+typedef enum {
+ FOLLOW_NONE, /* not used within the function, just a placeholder to
+ allow initing to this */
+ FOLLOW_FAKE, /* only records stuff, not actually following */
+ FOLLOW_RETRY, /* set if this is a request retry as opposed to a real
+ redirect following */
+ FOLLOW_REDIR /* a full true redirect */
+} followtype;
+
+#define CURL_HTTP_V1x (1 << 0)
+#define CURL_HTTP_V2x (1 << 1)
+#define CURL_HTTP_V3x (1 << 2)
+/* bitmask of CURL_HTTP_V* values */
+typedef unsigned char http_majors;
+
+
#ifndef CURL_DISABLE_HTTP
#if defined(USE_HTTP3)
@@ -56,6 +74,18 @@ extern const struct Curl_handler Curl_handler_https;
struct dynhds;
+struct http_negotiation {
+ unsigned char rcvd_min; /* minimum version seen in responses, 09, 10, 11 */
+ http_majors wanted; /* wanted major versions when talking to server */
+ http_majors allowed; /* allowed major versions when talking to server */
+ BIT(h2_upgrade); /* Do HTTP Upgrade from 1.1 to 2 */
+ BIT(h2_prior_knowledge); /* Directly do HTTP/2 without ALPN/SSL */
+ BIT(accept_09); /* Accept an HTTP/0.9 response */
+ BIT(only_10); /* When using major version 1x, use only 1.0 */
+};
+
+void Curl_http_neg_init(struct Curl_easy *data, struct http_negotiation *neg);
+
CURLcode Curl_bump_headersize(struct Curl_easy *data,
size_t delta,
bool connect_only);
@@ -74,53 +104,14 @@ char *Curl_checkProxyheaders(struct Curl_easy *data,
const char *thisheader,
const size_t thislen);
-CURLcode Curl_add_timecondition(struct Curl_easy *data,
-#ifndef USE_HYPER
- struct dynbuf *req
-#else
- void *headers
-#endif
- );
-CURLcode Curl_add_custom_headers(struct Curl_easy *data,
- bool is_connect,
-#ifndef USE_HYPER
- struct dynbuf *req
-#else
- void *headers
-#endif
- );
-CURLcode Curl_dynhds_add_custom(struct Curl_easy *data,
- bool is_connect,
+CURLcode Curl_add_timecondition(struct Curl_easy *data, struct dynbuf *req);
+CURLcode Curl_add_custom_headers(struct Curl_easy *data, bool is_connect,
+ int httpversion, struct dynbuf *req);
+CURLcode Curl_dynhds_add_custom(struct Curl_easy *data, bool is_connect,
struct dynhds *hds);
void Curl_http_method(struct Curl_easy *data, struct connectdata *conn,
const char **method, Curl_HttpReq *);
-CURLcode Curl_http_useragent(struct Curl_easy *data);
-CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn);
-CURLcode Curl_http_target(struct Curl_easy *data, struct connectdata *conn,
- struct dynbuf *req);
-CURLcode Curl_http_statusline(struct Curl_easy *data,
- struct connectdata *conn);
-CURLcode Curl_http_header(struct Curl_easy *data,
- const char *hd, size_t hdlen);
-CURLcode Curl_transferencode(struct Curl_easy *data);
-CURLcode Curl_http_req_set_reader(struct Curl_easy *data,
- Curl_HttpReq httpreq,
- const char **tep);
-CURLcode Curl_http_req_complete(struct Curl_easy *data,
- struct dynbuf *r, Curl_HttpReq httpreq);
-bool Curl_use_http_1_1plus(const struct Curl_easy *data,
- const struct connectdata *conn);
-#ifndef CURL_DISABLE_COOKIES
-CURLcode Curl_http_cookies(struct Curl_easy *data,
- struct connectdata *conn,
- struct dynbuf *r);
-#else
-#define Curl_http_cookies(a,b,c) CURLE_OK
-#endif
-CURLcode Curl_http_range(struct Curl_easy *data,
- Curl_HttpReq httpreq);
-CURLcode Curl_http_firstwrite(struct Curl_easy *data);
/* protocol-specific functions set up to be called by the main engine */
CURLcode Curl_http_setup_conn(struct Curl_easy *data,
@@ -140,8 +131,13 @@ CURLcode Curl_http_write_resp_hd(struct Curl_easy *data,
/* These functions are in http.c */
CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
const char *auth);
+
CURLcode Curl_http_auth_act(struct Curl_easy *data);
+/* follow a redirect or not */
+CURLcode Curl_http_follow(struct Curl_easy *data, const char *newurl,
+ followtype type);
+
/* If only the PICKNONE bit is set, there has been a round-trip and we
selected to use no auth at all. Ie, we actively select no auth, as opposed
to not having one selected. The other CURLAUTH_* defines are present in the
@@ -178,8 +174,10 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data);
version. This count includes CONNECT response headers. */
#define MAX_HTTP_RESP_HEADER_SIZE (300*1024)
-bool Curl_http_exp100_is_selected(struct Curl_easy *data);
-void Curl_http_exp100_got100(struct Curl_easy *data);
+/* MAX_HTTP_RESP_HEADER_COUNT is the maximum number of response headers that
+ libcurl allows for a single HTTP response, including CONNECT and
+ redirects. */
+#define MAX_HTTP_RESP_HEADER_COUNT 5000
#endif /* CURL_DISABLE_HTTP */
@@ -187,8 +185,6 @@ void Curl_http_exp100_got100(struct Curl_easy *data);
* HTTP unique setup
***************************************************************************/
-CURLcode Curl_http_size(struct Curl_easy *data);
-
CURLcode Curl_http_write_resp_hds(struct Curl_easy *data,
const char *buf, size_t blen,
size_t *pconsumed);
@@ -226,12 +222,12 @@ CURLcode Curl_http_decode_status(int *pstatus, const char *s, size_t len);
* All about a core HTTP request, excluding body and trailers
*/
struct httpreq {
- char method[24];
+ struct dynhds headers;
+ struct dynhds trailers;
char *scheme;
char *authority;
char *path;
- struct dynhds headers;
- struct dynhds trailers;
+ char method[1];
};
/**
diff --git a/libs/libcurl/src/http1.c b/libs/libcurl/src/http1.c
index ac472a4733..57e1732c13 100644
--- a/libs/libcurl/src/http1.c
+++ b/libs/libcurl/src/http1.c
@@ -44,14 +44,14 @@ void Curl_h1_req_parse_init(struct h1_req_parser *parser, size_t max_line_len)
{
memset(parser, 0, sizeof(*parser));
parser->max_line_len = max_line_len;
- Curl_dyn_init(&parser->scratch, max_line_len);
+ curlx_dyn_init(&parser->scratch, max_line_len);
}
void Curl_h1_req_parse_free(struct h1_req_parser *parser)
{
if(parser) {
Curl_http_req_free(parser->req);
- Curl_dyn_free(&parser->scratch);
+ curlx_dyn_free(&parser->scratch);
parser->req = NULL;
parser->done = FALSE;
}
@@ -108,18 +108,18 @@ static ssize_t next_line(struct h1_req_parser *parser,
if(parser->line) {
parser->line = NULL;
parser->line_len = 0;
- Curl_dyn_reset(&parser->scratch);
+ curlx_dyn_reset(&parser->scratch);
}
nread = detect_line(parser, buf, buflen, err);
if(nread >= 0) {
- if(Curl_dyn_len(&parser->scratch)) {
+ if(curlx_dyn_len(&parser->scratch)) {
/* append detected line to scratch to have the complete line */
- *err = Curl_dyn_addn(&parser->scratch, parser->line, parser->line_len);
+ *err = curlx_dyn_addn(&parser->scratch, parser->line, parser->line_len);
if(*err)
return -1;
- parser->line = Curl_dyn_ptr(&parser->scratch);
- parser->line_len = Curl_dyn_len(&parser->scratch);
+ parser->line = curlx_dyn_ptr(&parser->scratch);
+ parser->line_len = curlx_dyn_len(&parser->scratch);
}
*err = trim_line(parser, options);
if(*err)
@@ -127,7 +127,8 @@ static ssize_t next_line(struct h1_req_parser *parser,
}
else if(*err == CURLE_AGAIN) {
/* no line end in `buf`, add it to our scratch */
- *err = Curl_dyn_addn(&parser->scratch, (const unsigned char *)buf, buflen);
+ *err = curlx_dyn_addn(&parser->scratch, (const unsigned char *)buf,
+ buflen);
nread = (*err) ? -1 : (ssize_t)buflen;
}
return nread;
@@ -167,8 +168,6 @@ static CURLcode start_req(struct h1_req_parser *parser,
if(!target_len || !hv_len)
goto out;
- /* TODO: we do not check HTTP_VERSION for conformity, should
- + do that when STRICT option is supplied. */
(void)hv;
/* The TARGET can be (rfc 9112, ch. 3.2):
@@ -210,7 +209,7 @@ static CURLcode start_req(struct h1_req_parser *parser,
path = target;
path_len = target_len;
- /* URL parser wants 0-termination */
+ /* URL parser wants null-termination */
if(target_len >= sizeof(tmp))
goto out;
memcpy(tmp, target, target_len);
@@ -301,7 +300,7 @@ ssize_t Curl_h1_req_parse_read(struct h1_req_parser *parser,
goto out;
}
parser->done = TRUE;
- Curl_dyn_reset(&parser->scratch);
+ curlx_dyn_reset(&parser->scratch);
/* last chance adjustments */
}
else {
@@ -323,13 +322,13 @@ CURLcode Curl_h1_req_write_head(struct httpreq *req, int http_minor,
{
CURLcode result;
- result = Curl_dyn_addf(dbuf, "%s %s%s%s%s HTTP/1.%d\r\n",
- req->method,
- req->scheme ? req->scheme : "",
- req->scheme ? "://" : "",
- req->authority ? req->authority : "",
- req->path ? req->path : "",
- http_minor);
+ result = curlx_dyn_addf(dbuf, "%s %s%s%s%s HTTP/1.%d\r\n",
+ req->method,
+ req->scheme ? req->scheme : "",
+ req->scheme ? "://" : "",
+ req->authority ? req->authority : "",
+ req->path ? req->path : "",
+ http_minor);
if(result)
goto out;
@@ -337,7 +336,7 @@ CURLcode Curl_h1_req_write_head(struct httpreq *req, int http_minor,
if(result)
goto out;
- result = Curl_dyn_addn(dbuf, STRCONST("\r\n"));
+ result = curlx_dyn_addn(dbuf, STRCONST("\r\n"));
out:
return result;
diff --git a/libs/libcurl/src/http1.h b/libs/libcurl/src/http1.h
index 650c984922..581048c944 100644
--- a/libs/libcurl/src/http1.h
+++ b/libs/libcurl/src/http1.h
@@ -42,7 +42,7 @@ struct h1_req_parser {
const char *line;
size_t max_line_len;
size_t line_len;
- bool done;
+ BIT(done);
};
void Curl_h1_req_parse_init(struct h1_req_parser *parser, size_t max_line_len);
diff --git a/libs/libcurl/src/http2.c b/libs/libcurl/src/http2.c
index b9a21d2987..6d072b454c 100644
--- a/libs/libcurl/src/http2.c
+++ b/libs/libcurl/src/http2.c
@@ -29,13 +29,13 @@
#include <nghttp2/nghttp2.h>
#include "urldata.h"
#include "bufq.h"
-#include "hash.h"
+#include "uint-hash.h"
#include "http1.h"
#include "http2.h"
#include "http.h"
#include "sendf.h"
#include "select.h"
-#include "curl_base64.h"
+#include "curlx/base64.h"
#include "strcase.h"
#include "multiif.h"
#include "url.h"
@@ -43,10 +43,10 @@
#include "cfilters.h"
#include "connect.h"
#include "rand.h"
-#include "strtoofft.h"
#include "strdup.h"
+#include "curlx/strparse.h"
#include "transfer.h"
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
#include "headers.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -136,12 +136,15 @@ struct cf_h2_ctx {
struct bufc_pool stream_bufcp; /* spares for stream buffers */
struct dynbuf scratch; /* scratch buffer for temp use */
- struct Curl_hash streams; /* hash of `data->mid` to `h2_stream_ctx` */
+ struct uint_hash streams; /* hash of `data->mid` to `h2_stream_ctx` */
size_t drain_total; /* sum of all stream's UrlState drain */
uint32_t max_concurrent_streams;
uint32_t goaway_error; /* goaway error code from server */
int32_t remote_max_sid; /* max id processed by server */
int32_t local_max_sid; /* max id processed by us */
+#ifdef DEBUGBUILD
+ int32_t stream_win_max; /* max h2 stream window size */
+#endif
BIT(initialized);
BIT(via_h1_upgrade);
BIT(conn_closed);
@@ -156,17 +159,29 @@ struct cf_h2_ctx {
#define CF_CTX_CALL_DATA(cf) \
((struct cf_h2_ctx *)(cf)->ctx)->call_data
-static void h2_stream_hash_free(void *stream);
+static void h2_stream_hash_free(unsigned int id, void *stream);
static void cf_h2_ctx_init(struct cf_h2_ctx *ctx, bool via_h1_upgrade)
{
Curl_bufcp_init(&ctx->stream_bufcp, H2_CHUNK_SIZE, H2_STREAM_POOL_SPARES);
Curl_bufq_initp(&ctx->inbufq, &ctx->stream_bufcp, H2_NW_RECV_CHUNKS, 0);
Curl_bufq_initp(&ctx->outbufq, &ctx->stream_bufcp, H2_NW_SEND_CHUNKS, 0);
- Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER);
- Curl_hash_offt_init(&ctx->streams, 63, h2_stream_hash_free);
+ curlx_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER);
+ Curl_uint_hash_init(&ctx->streams, 63, h2_stream_hash_free);
ctx->remote_max_sid = 2147483647;
ctx->via_h1_upgrade = via_h1_upgrade;
+#ifdef DEBUGBUILD
+ {
+ const char *p = getenv("CURL_H2_STREAM_WIN_MAX");
+
+ ctx->stream_win_max = H2_STREAM_WINDOW_SIZE_MAX;
+ if(p) {
+ curl_off_t l;
+ if(!curlx_str_number(&p, &l, INT_MAX))
+ ctx->stream_win_max = (int32_t)l;
+ }
+ }
+#endif
ctx->initialized = TRUE;
}
@@ -176,9 +191,8 @@ static void cf_h2_ctx_free(struct cf_h2_ctx *ctx)
Curl_bufq_free(&ctx->inbufq);
Curl_bufq_free(&ctx->outbufq);
Curl_bufcp_free(&ctx->stream_bufcp);
- Curl_dyn_free(&ctx->scratch);
- Curl_hash_clean(&ctx->streams);
- Curl_hash_destroy(&ctx->streams);
+ curlx_dyn_free(&ctx->scratch);
+ Curl_uint_hash_destroy(&ctx->streams);
memset(ctx, 0, sizeof(*ctx));
}
free(ctx);
@@ -192,13 +206,12 @@ static void cf_h2_ctx_close(struct cf_h2_ctx *ctx)
}
static CURLcode h2_progress_egress(struct Curl_cfilter *cf,
- struct Curl_easy *data);
+ struct Curl_easy *data);
/**
* All about the H2 internals of a stream
*/
struct h2_stream_ctx {
- struct bufq recvbuf; /* response buffer */
struct bufq sendbuf; /* request buffer */
struct h1_req_parser h1; /* parsing the request */
struct dynhds resp_trailers; /* response trailer fields */
@@ -221,10 +234,12 @@ struct h2_stream_ctx {
BIT(bodystarted);
BIT(body_eos); /* the complete body has been added to `sendbuf` and
* is being/has been processed from there. */
+ BIT(write_paused); /* stream write is paused */
};
-#define H2_STREAM_CTX(ctx,data) ((struct h2_stream_ctx *)(\
- data? Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL))
+#define H2_STREAM_CTX(ctx,data) \
+ ((struct h2_stream_ctx *)( \
+ data? Curl_uint_hash_get(&(ctx)->streams, (data)->mid) : NULL))
static struct h2_stream_ctx *h2_stream_ctx_create(struct cf_h2_ctx *ctx)
{
@@ -240,7 +255,6 @@ static struct h2_stream_ctx *h2_stream_ctx_create(struct cf_h2_ctx *ctx)
H2_STREAM_SEND_CHUNKS, BUFQ_OPT_NONE);
Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
Curl_dynhds_init(&stream->resp_trailers, 0, DYN_HTTP_REQUEST);
- stream->resp_hds_len = 0;
stream->bodystarted = FALSE;
stream->status_code = -1;
stream->closed = FALSE;
@@ -269,8 +283,9 @@ static void h2_stream_ctx_free(struct h2_stream_ctx *stream)
free(stream);
}
-static void h2_stream_hash_free(void *stream)
+static void h2_stream_hash_free(unsigned int id, void *stream)
{
+ (void)id;
DEBUGASSERT(stream);
h2_stream_ctx_free((struct h2_stream_ctx *)stream);
}
@@ -287,23 +302,38 @@ static int32_t cf_h2_get_desired_local_win(struct Curl_cfilter *cf,
* This gets less precise the higher the latency. */
return (int32_t)data->set.max_recv_speed;
}
+#ifdef DEBUGBUILD
+ else {
+ struct cf_h2_ctx *ctx = cf->ctx;
+ CURL_TRC_CF(data, cf, "stream_win_max=%d", ctx->stream_win_max);
+ return ctx->stream_win_max;
+ }
+#else
return H2_STREAM_WINDOW_SIZE_MAX;
+#endif
}
static CURLcode cf_h2_update_local_win(struct Curl_cfilter *cf,
struct Curl_easy *data,
- struct h2_stream_ctx *stream,
- bool paused)
+ struct h2_stream_ctx *stream)
{
struct cf_h2_ctx *ctx = cf->ctx;
int32_t dwsize;
int rv;
- dwsize = paused ? 0 : cf_h2_get_desired_local_win(cf, data);
+ dwsize = (stream->write_paused || stream->xfer_result) ?
+ 0 : cf_h2_get_desired_local_win(cf, data);
if(dwsize != stream->local_window_size) {
int32_t wsize = nghttp2_session_get_stream_effective_local_window_size(
ctx->h2, stream->id);
if(dwsize > wsize) {
+ rv = nghttp2_session_set_local_window_size(ctx->h2, NGHTTP2_FLAG_NONE,
+ stream->id, dwsize);
+ if(rv) {
+ failf(data, "[%d] nghttp2 set_local_window_size(%d) failed: "
+ "%s(%d)", stream->id, dwsize, nghttp2_strerror(rv), rv);
+ return CURLE_HTTP2;
+ }
rv = nghttp2_submit_window_update(ctx->h2, NGHTTP2_FLAG_NONE,
stream->id, dwsize - wsize);
if(rv) {
@@ -335,13 +365,11 @@ static CURLcode cf_h2_update_local_win(struct Curl_cfilter *cf,
static CURLcode cf_h2_update_local_win(struct Curl_cfilter *cf,
struct Curl_easy *data,
- struct h2_stream_ctx *stream,
- bool paused)
+ struct h2_stream_ctx *stream)
{
(void)cf;
(void)data;
(void)stream;
- (void)paused;
return CURLE_OK;
}
#endif /* !NGHTTP2_HAS_SET_LOCAL_WINDOW_SIZE */
@@ -387,7 +415,7 @@ static CURLcode http2_data_setup(struct Curl_cfilter *cf,
if(!stream)
return CURLE_OUT_OF_MEMORY;
- if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) {
+ if(!Curl_uint_hash_set(&ctx->streams, data->mid, stream)) {
h2_stream_ctx_free(stream);
return CURLE_OUT_OF_MEMORY;
}
@@ -425,7 +453,7 @@ static void http2_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
nghttp2_session_send(ctx->h2);
}
- Curl_hash_offt_remove(&ctx->streams, data->mid);
+ Curl_uint_hash_remove(&ctx->streams, data->mid);
}
static int h2_client_new(struct Curl_cfilter *cf,
@@ -433,6 +461,8 @@ static int h2_client_new(struct Curl_cfilter *cf,
{
struct cf_h2_ctx *ctx = cf->ctx;
nghttp2_option *o;
+ nghttp2_mem mem = {NULL, Curl_nghttp2_malloc, Curl_nghttp2_free,
+ Curl_nghttp2_calloc, Curl_nghttp2_realloc};
int rc = nghttp2_option_new(&o);
if(rc)
@@ -445,7 +475,7 @@ static int h2_client_new(struct Curl_cfilter *cf,
HTTP field value. */
nghttp2_option_set_no_rfc9113_leading_and_trailing_ws_validation(o, 1);
#endif
- rc = nghttp2_session_client_new2(&ctx->h2, cbs, cf, o);
+ rc = nghttp2_session_client_new3(&ctx->h2, cbs, cf, o, &mem);
nghttp2_option_del(o);
return rc;
}
@@ -482,6 +512,10 @@ static ssize_t send_callback(nghttp2_session *h2,
void *userp);
static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
void *userp);
+static int cf_h2_on_invalid_frame_recv(nghttp2_session *session,
+ const nghttp2_frame *frame,
+ int lib_error_code,
+ void *user_data);
#ifndef CURL_DISABLE_VERBOSE_STRINGS
static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame,
void *userp);
@@ -522,6 +556,8 @@ static CURLcode cf_h2_ctx_open(struct Curl_cfilter *cf,
nghttp2_session_callbacks_set_send_callback(cbs, send_callback);
nghttp2_session_callbacks_set_on_frame_recv_callback(cbs, on_frame_recv);
+ nghttp2_session_callbacks_set_on_invalid_frame_recv_callback(cbs,
+ cf_h2_on_invalid_frame_recv);
#ifndef CURL_DISABLE_VERBOSE_STRINGS
nghttp2_session_callbacks_set_on_frame_send_callback(cbs, on_frame_send);
#endif
@@ -644,10 +680,8 @@ static int h2_process_pending_input(struct Curl_cfilter *cf,
rv = nghttp2_session_mem_recv(ctx->h2, (const uint8_t *)buf, blen);
if(rv < 0) {
- failf(data,
- "process_pending_input: nghttp2_session_mem_recv() returned "
- "%zd:%s", rv, nghttp2_strerror((int)rv));
- *err = CURLE_RECV_ERROR;
+ failf(data, "nghttp2 recv error %zd: %s", rv, nghttp2_strerror((int)rv));
+ *err = CURLE_HTTP2;
return -1;
}
Curl_bufq_skip(&ctx->inbufq, (size_t)rv);
@@ -965,9 +999,6 @@ static int push_promise(struct Curl_cfilter *cf,
goto fail;
}
- /* ask the application */
- CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ask application");
-
stream = H2_STREAM_CTX(ctx, data);
if(!stream) {
failf(data, "Internal NULL stream");
@@ -982,20 +1013,13 @@ static int push_promise(struct Curl_cfilter *cf,
rv = set_transfer_url(newhandle, &heads);
if(rv) {
+ CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE, failed to set url -> %d",
+ frame->promised_stream_id, rv);
discard_newhandle(cf, newhandle);
rv = CURL_PUSH_DENY;
goto fail;
}
- result = http2_data_setup(cf, newhandle, &newstream);
- if(result) {
- failf(data, "error setting up stream: %d", result);
- discard_newhandle(cf, newhandle);
- rv = CURL_PUSH_DENY;
- goto fail;
- }
- DEBUGASSERT(stream);
-
Curl_set_in_callback(data, TRUE);
rv = data->multi->push_cb(data, newhandle,
stream->push_headers_used, &heads,
@@ -1008,16 +1032,15 @@ static int push_promise(struct Curl_cfilter *cf,
if(rv) {
DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT));
/* denied, kill off the new handle again */
+ CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE, denied by application -> %d",
+ frame->promised_stream_id, rv);
discard_newhandle(cf, newhandle);
goto fail;
}
- newstream->id = frame->promised_stream_id;
- newhandle->req.maxdownload = -1;
- newhandle->req.size = -1;
-
- /* approved, add to the multi handle and immediately switch to PERFORM
- state with the given connection !*/
+ /* approved, add to the multi handle for processing. This
+ * assigns newhandle->mid. For the new `mid` we assign the
+ * h2_stream instance and remember the stream_id already known. */
rc = Curl_multi_add_perform(data->multi, newhandle, cf->conn);
if(rc) {
infof(data, "failed to add handle to multi");
@@ -1026,6 +1049,21 @@ static int push_promise(struct Curl_cfilter *cf,
goto fail;
}
+ result = http2_data_setup(cf, newhandle, &newstream);
+ if(result) {
+ failf(data, "error setting up stream: %d", result);
+ discard_newhandle(cf, newhandle);
+ rv = CURL_PUSH_DENY;
+ goto fail;
+ }
+
+ DEBUGASSERT(newstream);
+ newstream->id = frame->promised_stream_id;
+ newhandle->req.maxdownload = -1;
+ newhandle->req.size = -1;
+
+ CURL_TRC_CF(data, cf, "promise easy handle added to multi, mid=%u",
+ newhandle->mid);
rv = nghttp2_session_set_stream_user_data(ctx->h2,
newstream->id,
newhandle);
@@ -1059,7 +1097,7 @@ static void h2_xfer_write_resp_hd(struct Curl_cfilter *cf,
if(!stream->xfer_result) {
stream->xfer_result = Curl_xfer_write_resp_hd(data, buf, blen, eos);
if(!stream->xfer_result && !eos)
- stream->xfer_result = cf_h2_update_local_win(cf, data, stream, FALSE);
+ stream->xfer_result = cf_h2_update_local_win(cf, data, stream);
if(stream->xfer_result)
CURL_TRC_CF(data, cf, "[%d] error %d writing %zu bytes of headers",
stream->id, stream->xfer_result, blen);
@@ -1075,8 +1113,6 @@ static void h2_xfer_write_resp(struct Curl_cfilter *cf,
/* If we already encountered an error, skip further writes */
if(!stream->xfer_result)
stream->xfer_result = Curl_xfer_write_resp(data, buf, blen, eos);
- if(!stream->xfer_result && !eos)
- stream->xfer_result = cf_h2_update_local_win(cf, data, stream, FALSE);
/* If the transfer write is errored, we do not want any more data */
if(stream->xfer_result) {
struct cf_h2_ctx *ctx = cf->ctx;
@@ -1086,6 +1122,17 @@ static void h2_xfer_write_resp(struct Curl_cfilter *cf,
nghttp2_submit_rst_stream(ctx->h2, 0, stream->id,
(uint32_t)NGHTTP2_ERR_CALLBACK_FAILURE);
}
+ else if(!stream->write_paused && Curl_xfer_write_is_paused(data)) {
+ CURL_TRC_CF(data, cf, "[%d] stream output paused", stream->id);
+ stream->write_paused = TRUE;
+ }
+ else if(stream->write_paused && !Curl_xfer_write_is_paused(data)) {
+ CURL_TRC_CF(data, cf, "[%d] stream output unpaused", stream->id);
+ stream->write_paused = FALSE;
+ }
+
+ if(!stream->xfer_result && !eos)
+ stream->xfer_result = cf_h2_update_local_win(cf, data, stream);
}
static CURLcode on_stream_frame(struct Curl_cfilter *cf,
@@ -1253,7 +1300,7 @@ static int fr_print(const nghttp2_frame *frame, char *buffer, size_t blen)
}
case NGHTTP2_GOAWAY: {
char scratch[128];
- size_t s_len = sizeof(scratch)/sizeof(scratch[0]);
+ size_t s_len = CURL_ARRAYSIZE(scratch);
size_t len = (frame->goaway.opaque_data_len < s_len) ?
frame->goaway.opaque_data_len : s_len-1;
if(len)
@@ -1279,6 +1326,7 @@ static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame,
void *userp)
{
struct Curl_cfilter *cf = userp;
+ struct cf_h2_ctx *ctx = cf->ctx;
struct Curl_easy *data = CF_DATA_CURRENT(cf);
(void)session;
@@ -1290,6 +1338,13 @@ static int on_frame_send(nghttp2_session *session, const nghttp2_frame *frame,
buffer[len] = 0;
CURL_TRC_CF(data, cf, "[%d] -> %s", frame->hd.stream_id, buffer);
}
+ if((frame->hd.type == NGHTTP2_GOAWAY) && !ctx->sent_goaway) {
+ /* A GOAWAY not initiated by us, but by nghttp2 itself on detecting
+ * a protocol error on the connection */
+ failf(data, "nghttp2 shuts down connection with error %d: %s",
+ frame->goaway.error_code,
+ nghttp2_http2_strerror(frame->goaway.error_code));
+ }
return 0;
}
#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
@@ -1372,6 +1427,39 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
return on_stream_frame(cf, data_s, frame) ? NGHTTP2_ERR_CALLBACK_FAILURE : 0;
}
+static int cf_h2_on_invalid_frame_recv(nghttp2_session *session,
+ const nghttp2_frame *frame,
+ int ngerr, void *userp)
+{
+ struct Curl_cfilter *cf = userp;
+ struct cf_h2_ctx *ctx = cf->ctx;
+ struct Curl_easy *data;
+ int32_t stream_id = frame->hd.stream_id;
+
+ data = nghttp2_session_get_stream_user_data(session, stream_id);
+ if(data) {
+ struct h2_stream_ctx *stream;
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+ char buffer[256];
+ int len;
+ len = fr_print(frame, buffer, sizeof(buffer)-1);
+ buffer[len] = 0;
+ failf(data, "[HTTP2] [%d] received invalid frame: %s, error %d: %s",
+ stream_id, buffer, ngerr, nghttp2_strerror(ngerr));
+#endif /* !CURL_DISABLE_VERBOSE_STRINGS */
+ stream = H2_STREAM_CTX(ctx, data);
+ if(stream) {
+ nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE,
+ stream->id, NGHTTP2_STREAM_CLOSED);
+ stream->error = ngerr;
+ stream->closed = TRUE;
+ stream->reset = TRUE;
+ return 0; /* keep the connection alive */
+ }
+ }
+ return NGHTTP2_ERR_CALLBACK_FAILURE;
+}
+
static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
int32_t stream_id,
const uint8_t *mem, size_t len, void *userp)
@@ -1402,7 +1490,7 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
if(!stream)
return NGHTTP2_ERR_CALLBACK_FAILURE;
- h2_xfer_write_resp(cf, data_s, stream, (char *)mem, len, FALSE);
+ h2_xfer_write_resp(cf, data_s, stream, (const char *)mem, len, FALSE);
nghttp2_session_consume(ctx->h2, stream_id, len);
stream->nrcvd_data += (curl_off_t)len;
@@ -1493,6 +1581,23 @@ static int on_begin_headers(nghttp2_session *session,
return 0;
}
+static void cf_h2_header_error(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct h2_stream_ctx *stream,
+ CURLcode result)
+{
+ struct cf_h2_ctx *ctx = cf->ctx;
+
+ failf(data, "Error receiving HTTP2 header: %d(%s)", result,
+ curl_easy_strerror(result));
+ if(stream) {
+ nghttp2_submit_rst_stream(ctx->h2, NGHTTP2_FLAG_NONE,
+ stream->id, NGHTTP2_STREAM_CLOSED);
+ stream->closed = TRUE;
+ stream->reset = TRUE;
+ }
+}
+
/* frame->hd.type is either NGHTTP2_HEADERS or NGHTTP2_PUSH_PROMISE */
static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
const uint8_t *name, size_t namelen,
@@ -1512,7 +1617,7 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
/* get the stream from the hash based on Stream ID */
data_s = nghttp2_session_get_stream_user_data(session, stream_id);
- if(!data_s)
+ if(!GOOD_EASY_HANDLE(data_s))
/* Receiving a Stream ID not in the hash should not happen, this is an
internal error more than anything else! */
return NGHTTP2_ERR_CALLBACK_FAILURE;
@@ -1592,8 +1697,10 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
result = Curl_dynhds_add(&stream->resp_trailers,
(const char *)name, namelen,
(const char *)value, valuelen);
- if(result)
+ if(result) {
+ cf_h2_header_error(cf, data_s, stream, result);
return NGHTTP2_ERR_CALLBACK_FAILURE;
+ }
return 0;
}
@@ -1604,24 +1711,30 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
char buffer[32];
result = Curl_http_decode_status(&stream->status_code,
(const char *)value, valuelen);
- if(result)
+ if(result) {
+ cf_h2_header_error(cf, data_s, stream, result);
return NGHTTP2_ERR_CALLBACK_FAILURE;
+ }
msnprintf(buffer, sizeof(buffer), HTTP_PSEUDO_STATUS ":%u\r",
stream->status_code);
result = Curl_headers_push(data_s, buffer, CURLH_PSEUDO);
- if(result)
+ if(result) {
+ cf_h2_header_error(cf, data_s, stream, result);
return NGHTTP2_ERR_CALLBACK_FAILURE;
- Curl_dyn_reset(&ctx->scratch);
- result = Curl_dyn_addn(&ctx->scratch, STRCONST("HTTP/2 "));
+ }
+ curlx_dyn_reset(&ctx->scratch);
+ result = curlx_dyn_addn(&ctx->scratch, STRCONST("HTTP/2 "));
if(!result)
- result = Curl_dyn_addn(&ctx->scratch, value, valuelen);
+ result = curlx_dyn_addn(&ctx->scratch, value, valuelen);
if(!result)
- result = Curl_dyn_addn(&ctx->scratch, STRCONST(" \r\n"));
+ result = curlx_dyn_addn(&ctx->scratch, STRCONST(" \r\n"));
if(!result)
- h2_xfer_write_resp_hd(cf, data_s, stream, Curl_dyn_ptr(&ctx->scratch),
- Curl_dyn_len(&ctx->scratch), FALSE);
- if(result)
+ h2_xfer_write_resp_hd(cf, data_s, stream, curlx_dyn_ptr(&ctx->scratch),
+ curlx_dyn_len(&ctx->scratch), FALSE);
+ if(result) {
+ cf_h2_header_error(cf, data_s, stream, result);
return NGHTTP2_ERR_CALLBACK_FAILURE;
+ }
/* if we receive data for another handle, wake that up */
if(CF_DATA_CURRENT(cf) != data_s)
Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
@@ -1634,19 +1747,21 @@ static int on_header(nghttp2_session *session, const nghttp2_frame *frame,
/* nghttp2 guarantees that namelen > 0, and :status was already
received, and this is not pseudo-header field . */
/* convert to an HTTP1-style header */
- Curl_dyn_reset(&ctx->scratch);
- result = Curl_dyn_addn(&ctx->scratch, (const char *)name, namelen);
+ curlx_dyn_reset(&ctx->scratch);
+ result = curlx_dyn_addn(&ctx->scratch, (const char *)name, namelen);
if(!result)
- result = Curl_dyn_addn(&ctx->scratch, STRCONST(": "));
+ result = curlx_dyn_addn(&ctx->scratch, STRCONST(": "));
if(!result)
- result = Curl_dyn_addn(&ctx->scratch, (const char *)value, valuelen);
+ result = curlx_dyn_addn(&ctx->scratch, (const char *)value, valuelen);
if(!result)
- result = Curl_dyn_addn(&ctx->scratch, STRCONST("\r\n"));
+ result = curlx_dyn_addn(&ctx->scratch, STRCONST("\r\n"));
if(!result)
- h2_xfer_write_resp_hd(cf, data_s, stream, Curl_dyn_ptr(&ctx->scratch),
- Curl_dyn_len(&ctx->scratch), FALSE);
- if(result)
+ h2_xfer_write_resp_hd(cf, data_s, stream, curlx_dyn_ptr(&ctx->scratch),
+ curlx_dyn_len(&ctx->scratch), FALSE);
+ if(result) {
+ cf_h2_header_error(cf, data_s, stream, result);
return NGHTTP2_ERR_CALLBACK_FAILURE;
+ }
/* if we receive data for another handle, wake that up */
if(CF_DATA_CURRENT(cf) != data_s)
Curl_expire(data_s, 0, EXPIRE_RUN_NOW);
@@ -1735,22 +1850,22 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
binlen = populate_binsettings(binsettings, data);
if(binlen <= 0) {
failf(data, "nghttp2 unexpectedly failed on pack_settings_payload");
- Curl_dyn_free(req);
+ curlx_dyn_free(req);
return CURLE_FAILED_INIT;
}
- result = Curl_base64url_encode((const char *)binsettings, (size_t)binlen,
- &base64, &blen);
+ result = curlx_base64url_encode((const char *)binsettings, (size_t)binlen,
+ &base64, &blen);
if(result) {
- Curl_dyn_free(req);
+ curlx_dyn_free(req);
return result;
}
- result = Curl_dyn_addf(req,
- "Connection: Upgrade, HTTP2-Settings\r\n"
- "Upgrade: %s\r\n"
- "HTTP2-Settings: %s\r\n",
- NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
+ result = curlx_dyn_addf(req,
+ "Connection: Upgrade, HTTP2-Settings\r\n"
+ "Upgrade: %s\r\n"
+ "HTTP2-Settings: %s\r\n",
+ NGHTTP2_CLEARTEXT_PROTO_VERSION_ID, base64);
free(base64);
k->upgr101 = UPGR101_H2;
@@ -1810,25 +1925,25 @@ static ssize_t http2_handle_stream_close(struct Curl_cfilter *cf,
size_t i;
*err = CURLE_OK;
- Curl_dyn_init(&dbuf, DYN_TRAILERS);
+ curlx_dyn_init(&dbuf, DYN_TRAILERS);
for(i = 0; i < Curl_dynhds_count(&stream->resp_trailers); ++i) {
e = Curl_dynhds_getn(&stream->resp_trailers, i);
if(!e)
break;
- Curl_dyn_reset(&dbuf);
- *err = Curl_dyn_addf(&dbuf, "%.*s: %.*s\x0d\x0a",
- (int)e->namelen, e->name,
- (int)e->valuelen, e->value);
+ curlx_dyn_reset(&dbuf);
+ *err = curlx_dyn_addf(&dbuf, "%.*s: %.*s\x0d\x0a",
+ (int)e->namelen, e->name,
+ (int)e->valuelen, e->value);
if(*err)
break;
- Curl_debug(data, CURLINFO_HEADER_IN, Curl_dyn_ptr(&dbuf),
- Curl_dyn_len(&dbuf));
+ Curl_debug(data, CURLINFO_HEADER_IN, curlx_dyn_ptr(&dbuf),
+ curlx_dyn_len(&dbuf));
*err = Curl_client_write(data, CLIENTWRITE_HEADER|CLIENTWRITE_TRAILER,
- Curl_dyn_ptr(&dbuf), Curl_dyn_len(&dbuf));
+ curlx_dyn_ptr(&dbuf), curlx_dyn_len(&dbuf));
if(*err)
break;
}
- Curl_dyn_free(&dbuf);
+ curlx_dyn_free(&dbuf);
if(*err)
goto out;
}
@@ -1963,6 +2078,11 @@ static CURLcode h2_progress_ingress(struct Curl_cfilter *cf,
CURLcode result = CURLE_OK;
ssize_t nread;
+ if(should_close_session(ctx)) {
+ CURL_TRC_CF(data, cf, "progress ingress, session is closed");
+ return CURLE_HTTP2;
+ }
+
/* Process network input buffer fist */
if(!Curl_bufq_is_empty(&ctx->inbufq)) {
CURL_TRC_CF(data, cf, "Process %zu bytes in connection buffer",
@@ -2035,7 +2155,7 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
* a read() is called anyway. It is not clear what the calling sequence
* is for such a case. */
failf(data, "http/2 recv on a transfer never opened "
- "or already cleared, mid=%" FMT_OFF_T, data->mid);
+ "or already cleared, mid=%u", data->mid);
*err = CURLE_HTTP2;
return -1;
}
@@ -2055,23 +2175,10 @@ static ssize_t cf_h2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
}
if(nread > 0) {
- size_t data_consumed = (size_t)nread;
/* Now that we transferred this to the upper layer, we report
* the actual amount of DATA consumed to the H2 session, so
* that it adjusts stream flow control */
- if(stream->resp_hds_len >= data_consumed) {
- stream->resp_hds_len -= data_consumed; /* no DATA */
- }
- else {
- if(stream->resp_hds_len) {
- data_consumed -= stream->resp_hds_len;
- stream->resp_hds_len = 0;
- }
- if(data_consumed) {
- nghttp2_session_consume(ctx->h2, stream->id, data_consumed);
- }
- }
-
+ nghttp2_session_consume(ctx->h2, stream->id, (size_t)nread);
if(stream->closed) {
CURL_TRC_CF(data, cf, "[%d] DRAIN closed stream", stream->id);
drain_stream(cf, data, stream);
@@ -2335,7 +2442,7 @@ static ssize_t cf_h2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
if(should_close_session(ctx)) {
/* nghttp2 thinks this session is done. If the stream has not been
* closed, this is an error state for out transfer */
- if(stream->closed) {
+ if(stream && stream->closed) {
nwritten = http2_handle_stream_close(cf, data, stream, err);
}
else {
@@ -2455,7 +2562,7 @@ static void cf_h2_adjust_pollset(struct Curl_cfilter *cf,
static CURLcode cf_h2_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool blocking, bool *done)
+ bool *done)
{
struct cf_h2_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
@@ -2469,7 +2576,7 @@ static CURLcode cf_h2_connect(struct Curl_cfilter *cf,
/* Connect the lower filters first */
if(!cf->next->connected) {
- result = Curl_conn_cf_connect(cf->next, data, blocking, done);
+ result = Curl_conn_cf_connect(cf->next, data, done);
if(result || !*done)
return result;
}
@@ -2494,9 +2601,7 @@ static CURLcode cf_h2_connect(struct Curl_cfilter *cf,
/* Send out our SETTINGS and ACKs and such. If that blocks, we
* have it buffered and can count this filter as being connected */
result = h2_progress_egress(cf, data);
- if(result == CURLE_AGAIN)
- result = CURLE_OK;
- else if(result)
+ if(result && (result != CURLE_AGAIN))
goto out;
*done = TRUE;
@@ -2552,6 +2657,7 @@ static CURLcode cf_h2_shutdown(struct Curl_cfilter *cf,
CF_DATA_SAVE(save, cf, data);
if(!ctx->sent_goaway) {
+ ctx->sent_goaway = TRUE;
rv = nghttp2_submit_goaway(ctx->h2, NGHTTP2_FLAG_NONE,
ctx->local_max_sid, 0,
(const uint8_t *)"shutdown",
@@ -2562,7 +2668,6 @@ static CURLcode cf_h2_shutdown(struct Curl_cfilter *cf,
result = CURLE_SEND_ERROR;
goto out;
}
- ctx->sent_goaway = TRUE;
}
/* GOAWAY submitted, process egress and ingress until nghttp2 is done. */
result = CURLE_OK;
@@ -2595,7 +2700,10 @@ static CURLcode http2_data_pause(struct Curl_cfilter *cf,
DEBUGASSERT(data);
if(ctx && ctx->h2 && stream) {
- CURLcode result = cf_h2_update_local_win(cf, data, stream, pause);
+ CURLcode result;
+
+ stream->write_paused = pause;
+ result = cf_h2_update_local_win(cf, data, stream);
if(result)
return result;
@@ -2636,9 +2744,6 @@ static CURLcode cf_h2_cntrl(struct Curl_cfilter *cf,
case CF_CTRL_FLUSH:
result = cf_h2_flush(cf, data);
break;
- case CF_CTRL_DATA_DETACH:
- http2_data_done(cf, data);
- break;
case CF_CTRL_DATA_DONE:
http2_data_done(cf, data);
break;
@@ -2653,10 +2758,8 @@ static bool cf_h2_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data)
{
struct cf_h2_ctx *ctx = cf->ctx;
- struct h2_stream_ctx *stream = H2_STREAM_CTX(ctx, data);
- if(ctx && (!Curl_bufq_is_empty(&ctx->inbufq)
- || (stream && !Curl_bufq_is_empty(&stream->sendbuf))))
+ if(ctx && !Curl_bufq_is_empty(&ctx->inbufq))
return TRUE;
return cf->next ? cf->next->cft->has_data_pending(cf->next, data) : FALSE;
}
@@ -2704,7 +2807,7 @@ static CURLcode cf_h2_query(struct Curl_cfilter *cf,
CF_DATA_SAVE(save, cf, data);
if(nghttp2_session_check_request_allowed(ctx->h2) == 0) {
/* the limit is what we have in use right now */
- effective_max = CONN_INUSE(cf->conn);
+ effective_max = CONN_ATTACHED(cf->conn);
}
else {
effective_max = ctx->max_concurrent_streams;
@@ -2726,6 +2829,9 @@ static CURLcode cf_h2_query(struct Curl_cfilter *cf,
}
break;
}
+ case CF_QUERY_HTTP_VERSION:
+ *pres1 = 20;
+ return CURLE_OK;
default:
break;
}
@@ -2736,7 +2842,7 @@ static CURLcode cf_h2_query(struct Curl_cfilter *cf,
struct Curl_cftype Curl_cft_nghttp2 = {
"HTTP/2",
- CF_TYPE_MULTIPLEX,
+ CF_TYPE_MULTIPLEX | CF_TYPE_HTTP,
CURL_LOG_LVL_NONE,
cf_h2_destroy,
cf_h2_connect,
@@ -2810,35 +2916,13 @@ out:
return result;
}
-static bool cf_is_http2(struct Curl_cfilter *cf,
- const struct Curl_easy *data)
+bool Curl_http2_may_switch(struct Curl_easy *data)
{
- (void)data;
- for(; cf; cf = cf->next) {
- if(cf->cft == &Curl_cft_nghttp2)
- return TRUE;
- if(cf->cft->flags & CF_TYPE_IP_CONNECT)
- return FALSE;
- }
- return FALSE;
-}
-
-bool Curl_conn_is_http2(const struct Curl_easy *data,
- const struct connectdata *conn,
- int sockindex)
-{
- return conn ? cf_is_http2(conn->cfilter[sockindex], data) : FALSE;
-}
-
-bool Curl_http2_may_switch(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex)
-{
- (void)sockindex;
- if(!Curl_conn_is_http2(data, conn, sockindex) &&
- data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) {
+ if(Curl_conn_http_version(data, data->conn) < 20 &&
+ (data->state.http_neg.wanted & CURL_HTTP_V2x) &&
+ data->state.http_neg.h2_prior_knowledge) {
#ifndef CURL_DISABLE_PROXY
- if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
+ if(data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy) {
/* We do not support HTTP/2 proxies yet. Also it is debatable
whether or not this setting should apply to HTTP/2 proxies. */
infof(data, "Ignoring HTTP/2 prior knowledge due to proxy");
@@ -2850,26 +2934,24 @@ bool Curl_http2_may_switch(struct Curl_easy *data,
return FALSE;
}
-CURLcode Curl_http2_switch(struct Curl_easy *data,
- struct connectdata *conn, int sockindex)
+CURLcode Curl_http2_switch(struct Curl_easy *data)
{
struct Curl_cfilter *cf;
CURLcode result;
- DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex));
+ DEBUGASSERT(Curl_conn_http_version(data, data->conn) < 20);
- result = http2_cfilter_add(&cf, data, conn, sockindex, FALSE);
+ result = http2_cfilter_add(&cf, data, data->conn, FIRSTSOCKET, FALSE);
if(result)
return result;
CURL_TRC_CF(data, cf, "switching connection to HTTP/2");
- conn->httpversion = 20; /* we know we are on HTTP/2 now */
- conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
+ data->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
Curl_multi_connchanged(data->multi);
if(cf->next) {
bool done;
- return Curl_conn_cf_connect(cf, data, FALSE, &done);
+ return Curl_conn_cf_connect(cf, data, &done);
}
return CURLE_OK;
}
@@ -2879,20 +2961,19 @@ CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data)
struct Curl_cfilter *cf_h2;
CURLcode result;
- DEBUGASSERT(!cf_is_http2(cf, data));
+ DEBUGASSERT(Curl_conn_http_version(data, data->conn) < 20);
result = http2_cfilter_insert_after(cf, data, FALSE);
if(result)
return result;
cf_h2 = cf->next;
- cf->conn->httpversion = 20; /* we know we are on HTTP/2 now */
cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
Curl_multi_connchanged(data->multi);
if(cf_h2->next) {
bool done;
- return Curl_conn_cf_connect(cf_h2, data, FALSE, &done);
+ return Curl_conn_cf_connect(cf_h2, data, &done);
}
return CURLE_OK;
}
@@ -2905,7 +2986,7 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data,
struct cf_h2_ctx *ctx;
CURLcode result;
- DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex));
+ DEBUGASSERT(Curl_conn_http_version(data, conn) < 20);
DEBUGASSERT(data->req.upgr101 == UPGR101_RECEIVED);
result = http2_cfilter_add(&cf, data, conn, sockindex, TRUE);
@@ -2938,13 +3019,12 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data,
" after upgrade: len=%zu", nread);
}
- conn->httpversion = 20; /* we know we are on HTTP/2 now */
conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
Curl_multi_connchanged(data->multi);
if(cf->next) {
bool done;
- return Curl_conn_cf_connect(cf, data, FALSE, &done);
+ return Curl_conn_cf_connect(cf, data, &done);
}
return CURLE_OK;
}
@@ -2953,13 +3033,37 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data,
CURLE_HTTP2_STREAM error! */
bool Curl_h2_http_1_1_error(struct Curl_easy *data)
{
- if(Curl_conn_is_http2(data, data->conn, FIRSTSOCKET)) {
+ if(Curl_conn_http_version(data, data->conn) == 20) {
int err = Curl_conn_get_stream_error(data, data->conn, FIRSTSOCKET);
- return (err == NGHTTP2_HTTP_1_1_REQUIRED);
+ return err == NGHTTP2_HTTP_1_1_REQUIRED;
}
return FALSE;
}
+void *Curl_nghttp2_malloc(size_t size, void *user_data)
+{
+ (void)user_data;
+ return Curl_cmalloc(size);
+}
+
+void Curl_nghttp2_free(void *ptr, void *user_data)
+{
+ (void)user_data;
+ Curl_cfree(ptr);
+}
+
+void *Curl_nghttp2_calloc(size_t nmemb, size_t size, void *user_data)
+{
+ (void)user_data;
+ return Curl_ccalloc(nmemb, size);
+}
+
+void *Curl_nghttp2_realloc(void *ptr, size_t size, void *user_data)
+{
+ (void)user_data;
+ return Curl_crealloc(ptr, size);
+}
+
#else /* !USE_NGHTTP2 */
/* Satisfy external references even if http2 is not compiled in. */
diff --git a/libs/libcurl/src/http2.h b/libs/libcurl/src/http2.h
index 3c6ffae278..f7da3c43e3 100644
--- a/libs/libcurl/src/http2.h
+++ b/libs/libcurl/src/http2.h
@@ -44,15 +44,9 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
/* returns true if the HTTP/2 stream error was HTTP_1_1_REQUIRED */
bool Curl_h2_http_1_1_error(struct Curl_easy *data);
-bool Curl_conn_is_http2(const struct Curl_easy *data,
- const struct connectdata *conn,
- int sockindex);
-bool Curl_http2_may_switch(struct Curl_easy *data,
- struct connectdata *conn,
- int sockindex);
+bool Curl_http2_may_switch(struct Curl_easy *data);
-CURLcode Curl_http2_switch(struct Curl_easy *data,
- struct connectdata *conn, int sockindex);
+CURLcode Curl_http2_switch(struct Curl_easy *data);
CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data);
@@ -60,16 +54,19 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data,
struct connectdata *conn, int sockindex,
const char *ptr, size_t nread);
+void *Curl_nghttp2_malloc(size_t size, void *user_data);
+void Curl_nghttp2_free(void *ptr, void *user_data);
+void *Curl_nghttp2_calloc(size_t nmemb, size_t size, void *user_data);
+void *Curl_nghttp2_realloc(void *ptr, size_t size, void *user_data);
+
extern struct Curl_cftype Curl_cft_nghttp2;
#else /* USE_NGHTTP2 */
-#define Curl_cf_is_http2(a,b) FALSE
-#define Curl_conn_is_http2(a,b,c) FALSE
-#define Curl_http2_may_switch(a,b,c) FALSE
+#define Curl_http2_may_switch(a) FALSE
#define Curl_http2_request_upgrade(x,y) CURLE_UNSUPPORTED_PROTOCOL
-#define Curl_http2_switch(a,b,c) CURLE_UNSUPPORTED_PROTOCOL
+#define Curl_http2_switch(a) CURLE_UNSUPPORTED_PROTOCOL
#define Curl_http2_upgrade(a,b,c,d,e) CURLE_UNSUPPORTED_PROTOCOL
#define Curl_h2_http_1_1_error(x) 0
#endif
diff --git a/libs/libcurl/src/http_aws_sigv4.c b/libs/libcurl/src/http_aws_sigv4.c
index 007c355642..38c2e882b9 100644
--- a/libs/libcurl/src/http_aws_sigv4.c
+++ b/libs/libcurl/src/http_aws_sigv4.c
@@ -9,7 +9,7 @@
*
* This software is licensed 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.
+ * are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
@@ -35,6 +35,7 @@
#include "parsedate.h"
#include "sendf.h"
#include "escape.h"
+#include "curlx/strparse.h"
#include <time.h>
@@ -45,16 +46,16 @@
#include "slist.h"
-#define HMAC_SHA256(k, kl, d, dl, o) \
- do { \
- result = Curl_hmacit(&Curl_HMAC_SHA256, \
- (unsigned char *)k, \
- kl, \
- (unsigned char *)d, \
- dl, o); \
- if(result) { \
- goto fail; \
- } \
+#define HMAC_SHA256(k, kl, d, dl, o) \
+ do { \
+ result = Curl_hmacit(&Curl_HMAC_SHA256, \
+ (const unsigned char *)k, \
+ kl, \
+ (const unsigned char *)d, \
+ dl, o); \
+ if(result) { \
+ goto fail; \
+ } \
} while(0)
#define TIMESTAMP_SIZE 17
@@ -62,6 +63,27 @@
/* hex-encoded with trailing null */
#define SHA256_HEX_LENGTH (2 * CURL_SHA256_DIGEST_LENGTH + 1)
+#define MAX_QUERY_COMPONENTS 128
+
+struct pair {
+ struct dynbuf key;
+ struct dynbuf value;
+};
+
+static void dyn_array_free(struct dynbuf *db, size_t num_elements);
+static void pair_array_free(struct pair *pair_array, size_t num_elements);
+static CURLcode split_to_dyn_array(const char *source,
+ struct dynbuf db[MAX_QUERY_COMPONENTS],
+ size_t *num_splits);
+static bool is_reserved_char(const char c);
+static CURLcode uri_encode_path(struct Curl_str *original_path,
+ struct dynbuf *new_path);
+static CURLcode encode_query_component(char *component, size_t len,
+ struct dynbuf *db);
+static CURLcode http_aws_decode_encode(const char *in, size_t in_len,
+ struct dynbuf *out);
+static bool should_urlencode(struct Curl_str *service_name);
+
static void sha256_to_hex(char *dst, unsigned char *sha)
{
Curl_hexencode(sha, CURL_SHA256_DIGEST_LENGTH,
@@ -82,7 +104,7 @@ static void trim_headers(struct curl_slist *head)
{
struct curl_slist *l;
for(l = head; l; l = l->next) {
- char *value; /* to read from */
+ const char *value; /* to read from */
char *store;
size_t colon = strcspn(l->data, ":");
Curl_strntolower(l->data, l->data, colon);
@@ -91,15 +113,14 @@ static void trim_headers(struct curl_slist *head)
if(!*value)
continue;
++value;
- store = value;
+ store = (char *)CURL_UNCONST(value);
/* skip leading whitespace */
- while(*value && ISBLANK(*value))
- value++;
+ curlx_str_passblanks(&value);
while(*value) {
int space = 0;
- while(*value && ISBLANK(*value)) {
+ while(ISBLANK(*value)) {
value++;
space++;
}
@@ -112,14 +133,12 @@ static void trim_headers(struct curl_slist *head)
else
*store++ = *value++;
}
- *store = 0; /* null terminate */
+ *store = 0; /* null-terminate */
}
}
/* maximum length for the aws sivg4 parts */
#define MAX_SIGV4_LEN 64
-#define MAX_SIGV4_LEN_TXT "64"
-
#define DATE_HDR_KEY_LEN (MAX_SIGV4_LEN + sizeof("X--Date"))
/* string been x-PROVIDER-date:TIMESTAMP, I need +1 for ':' */
@@ -156,11 +175,63 @@ static int compare_header_names(const char *a, const char *b)
return cmp;
}
+/* Merge duplicate header definitions by comma delimiting their values
+ in the order defined the headers are defined, expecting headers to
+ be alpha-sorted and use ':' at this point */
+static CURLcode merge_duplicate_headers(struct curl_slist *head)
+{
+ struct curl_slist *curr = head;
+ CURLcode result = CURLE_OK;
+
+ while(curr) {
+ struct curl_slist *next = curr->next;
+ if(!next)
+ break;
+
+ if(compare_header_names(curr->data, next->data) == 0) {
+ struct dynbuf buf;
+ char *colon_next;
+ char *val_next;
+
+ curlx_dyn_init(&buf, CURL_MAX_HTTP_HEADER);
+
+ result = curlx_dyn_add(&buf, curr->data);
+ if(result)
+ return result;
+
+ colon_next = strchr(next->data, ':');
+ DEBUGASSERT(colon_next);
+ val_next = colon_next + 1;
+
+ result = curlx_dyn_addn(&buf, ",", 1);
+ if(result)
+ return result;
+
+ result = curlx_dyn_add(&buf, val_next);
+ if(result)
+ return result;
+
+ free(curr->data);
+ curr->data = curlx_dyn_ptr(&buf);
+
+ curr->next = next->next;
+ free(next->data);
+ free(next);
+ }
+ else {
+ curr = curr->next;
+ }
+ }
+
+ return CURLE_OK;
+}
+
/* timestamp should point to a buffer of at last TIMESTAMP_SIZE bytes */
static CURLcode make_headers(struct Curl_easy *data,
const char *hostname,
char *timestamp,
- char *provider1,
+ const char *provider1,
+ size_t plen, /* length of provider1 */
char **date_header,
char *content_sha256_header,
struct dynbuf *canonical_headers,
@@ -174,16 +245,16 @@ static CURLcode make_headers(struct Curl_easy *data,
struct curl_slist *l;
bool again = TRUE;
- /* provider1 mid */
- Curl_strntolower(provider1, provider1, strlen(provider1));
- provider1[0] = Curl_raw_toupper(provider1[0]);
+ msnprintf(date_hdr_key, DATE_HDR_KEY_LEN, "X-%.*s-Date",
+ (int)plen, provider1);
+ /* provider1 ucfirst */
+ Curl_strntolower(&date_hdr_key[2], provider1, plen);
+ date_hdr_key[2] = Curl_raw_toupper(provider1[0]);
- msnprintf(date_hdr_key, DATE_HDR_KEY_LEN, "X-%s-Date", provider1);
-
- /* provider1 lowercase */
- Curl_strntolower(provider1, provider1, 1); /* first byte only */
msnprintf(date_full_hdr, DATE_FULL_HDR_LEN,
- "x-%s-date:%s", provider1, timestamp);
+ "x-%.*s-date:%s", (int)plen, provider1, timestamp);
+ /* provider1 lowercase */
+ Curl_strntolower(&date_full_hdr[2], provider1, plen);
if(!Curl_checkheaders(data, STRCONST("Host"))) {
char *fullhost;
@@ -234,7 +305,7 @@ static CURLcode make_headers(struct Curl_easy *data,
sep = strchr(l->data, ';');
if(!sep || (*sep == ':' && !*(sep + 1)))
continue;
- for(ptr = sep + 1; ISSPACE(*ptr); ++ptr)
+ for(ptr = sep + 1; ISBLANK(*ptr); ++ptr)
;
if(!*ptr && ptr != sep + 1) /* a value of whitespace only */
continue;
@@ -261,16 +332,15 @@ static CURLcode make_headers(struct Curl_easy *data,
*date_header = aprintf("%s: %s\r\n", date_hdr_key, timestamp);
}
else {
- char *value;
- char *endp;
+ const char *value;
+ const char *endp;
value = strchr(*date_header, ':');
if(!value) {
*date_header = NULL;
goto fail;
}
++value;
- while(ISBLANK(*value))
- ++value;
+ curlx_str_passblanks(&value);
endp = value;
while(*endp && ISALNUM(*endp))
++endp;
@@ -301,12 +371,16 @@ static CURLcode make_headers(struct Curl_easy *data,
}
} while(again);
+ ret = merge_duplicate_headers(head);
+ if(ret)
+ goto fail;
+
for(l = head; l; l = l->next) {
char *tmp;
- if(Curl_dyn_add(canonical_headers, l->data))
+ if(curlx_dyn_add(canonical_headers, l->data))
goto fail;
- if(Curl_dyn_add(canonical_headers, "\n"))
+ if(curlx_dyn_add(canonical_headers, "\n"))
goto fail;
tmp = strchr(l->data, ':');
@@ -314,10 +388,10 @@ static CURLcode make_headers(struct Curl_easy *data,
*tmp = 0;
if(l != head) {
- if(Curl_dyn_add(signed_headers, ";"))
+ if(curlx_dyn_add(signed_headers, ";"))
goto fail;
}
- if(Curl_dyn_add(signed_headers, l->data))
+ if(curlx_dyn_add(signed_headers, l->data))
goto fail;
}
@@ -334,16 +408,17 @@ fail:
SHA256_HEX_LENGTH)
/* try to parse a payload hash from the content-sha256 header */
-static char *parse_content_sha_hdr(struct Curl_easy *data,
- const char *provider1,
- size_t *value_len)
-{
+static const char *parse_content_sha_hdr(struct Curl_easy *data,
+ const char *provider1,
+ size_t plen,
+ size_t *value_len) {
char key[CONTENT_SHA256_KEY_LEN];
size_t key_len;
- char *value;
+ const char *value;
size_t len;
- key_len = msnprintf(key, sizeof(key), "x-%s-content-sha256", provider1);
+ key_len = msnprintf(key, sizeof(key), "x-%.*s-content-sha256",
+ (int)plen, provider1);
value = Curl_checkheaders(data, key, key_len);
if(!value)
@@ -354,8 +429,7 @@ static char *parse_content_sha_hdr(struct Curl_easy *data,
return NULL;
++value;
- while(*value && ISBLANK(*value))
- ++value;
+ curlx_str_passblanks(&value);
len = strlen(value);
while(len > 0 && ISBLANK(value[len-1]))
@@ -388,7 +462,9 @@ static CURLcode calc_payload_hash(struct Curl_easy *data,
#define S3_UNSIGNED_PAYLOAD "UNSIGNED-PAYLOAD"
static CURLcode calc_s3_payload_hash(struct Curl_easy *data,
- Curl_HttpReq httpreq, char *provider1,
+ Curl_HttpReq httpreq,
+ const char *provider1,
+ size_t plen,
unsigned char *sha_hash,
char *sha_hex, char *header)
{
@@ -415,168 +491,192 @@ static CURLcode calc_s3_payload_hash(struct Curl_easy *data,
/* format the required content-sha256 header */
msnprintf(header, CONTENT_SHA256_HDR_LEN,
- "x-%s-content-sha256: %s", provider1, sha_hex);
+ "x-%.*s-content-sha256: %s", (int)plen, provider1, sha_hex);
ret = CURLE_OK;
fail:
return ret;
}
-struct pair {
- const char *p;
- size_t len;
-};
-
static int compare_func(const void *a, const void *b)
{
+
const struct pair *aa = a;
const struct pair *bb = b;
+ const size_t aa_key_len = curlx_dyn_len(&aa->key);
+ const size_t bb_key_len = curlx_dyn_len(&bb->key);
+ const size_t aa_value_len = curlx_dyn_len(&aa->value);
+ const size_t bb_value_len = curlx_dyn_len(&bb->value);
+ int compare;
+
/* If one element is empty, the other is always sorted higher */
- if(aa->len == 0)
+
+ /* Compare keys */
+ if((aa_key_len == 0) && (bb_key_len == 0))
+ return 0;
+ if(aa_key_len == 0)
return -1;
- if(bb->len == 0)
+ if(bb_key_len == 0)
return 1;
- return strncmp(aa->p, bb->p, aa->len < bb->len ? aa->len : bb->len);
-}
+ compare = strcmp(curlx_dyn_ptr(&aa->key), curlx_dyn_ptr(&bb->key));
+ if(compare) {
+ return compare;
+ }
+
+ /* Compare values */
+ if((aa_value_len == 0) && (bb_value_len == 0))
+ return 0;
+ if(aa_value_len == 0)
+ return -1;
+ if(bb_value_len == 0)
+ return 1;
+ compare = strcmp(curlx_dyn_ptr(&aa->value), curlx_dyn_ptr(&bb->value));
-#define MAX_QUERYPAIRS 64
+ return compare;
-/**
- * found_equals have a double meaning,
- * detect if an equal have been found when called from canon_query,
- * and mark that this function is called to compute the path,
- * if found_equals is NULL.
- */
-static CURLcode canon_string(const char *q, size_t len,
- struct dynbuf *dq, bool *found_equals)
+}
+
+UNITTEST CURLcode canon_path(const char *q, size_t len,
+ struct dynbuf *new_path,
+ bool do_uri_encode)
{
CURLcode result = CURLE_OK;
- for(; len && !result; q++, len--) {
- if(ISALNUM(*q))
- result = Curl_dyn_addn(dq, q, 1);
- else {
- switch(*q) {
- case '-':
- case '.':
- case '_':
- case '~':
- /* allowed as-is */
- result = Curl_dyn_addn(dq, q, 1);
- break;
- case '%':
- /* uppercase the following if hexadecimal */
- if(ISXDIGIT(q[1]) && ISXDIGIT(q[2])) {
- char tmp[3]="%";
- tmp[1] = Curl_raw_toupper(q[1]);
- tmp[2] = Curl_raw_toupper(q[2]);
- result = Curl_dyn_addn(dq, tmp, 3);
- q += 2;
- len -= 2;
- }
- else
- /* '%' without a following two-digit hex, encode it */
- result = Curl_dyn_addn(dq, "%25", 3);
- break;
- default: {
- const char hex[] = "0123456789ABCDEF";
- char out[3]={'%'};
-
- if(!found_equals) {
- /* if found_equals is NULL assuming, been in path */
- if(*q == '/') {
- /* allowed as if */
- result = Curl_dyn_addn(dq, q, 1);
- break;
- }
- }
- else {
- /* allowed as-is */
- if(*q == '=') {
- result = Curl_dyn_addn(dq, q, 1);
- *found_equals = TRUE;
- break;
- }
- }
- /* URL encode */
- out[1] = hex[((unsigned char)*q) >> 4];
- out[2] = hex[*q & 0xf];
- result = Curl_dyn_addn(dq, out, 3);
- break;
- }
- }
- }
+ struct Curl_str original_path;
+
+ curlx_str_assign(&original_path, q, len);
+
+ /* Normalized path will be either the same or shorter than the original
+ * path, plus trailing slash */
+
+ if(do_uri_encode)
+ result = uri_encode_path(&original_path, new_path);
+ else
+ result = curlx_dyn_addn(new_path, q, len);
+
+ if(!result) {
+ if(curlx_dyn_len(new_path) == 0)
+ result = curlx_dyn_add(new_path, "/");
}
+
return result;
}
-
-static CURLcode canon_query(struct Curl_easy *data,
- const char *query, struct dynbuf *dq)
+UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq)
{
CURLcode result = CURLE_OK;
- int entry = 0;
- int i;
- const char *p = query;
- struct pair array[MAX_QUERYPAIRS];
- struct pair *ap = &array[0];
+
+ struct dynbuf query_array[MAX_QUERY_COMPONENTS];
+ struct pair encoded_query_array[MAX_QUERY_COMPONENTS];
+ size_t num_query_components;
+ size_t counted_query_components = 0;
+ size_t index;
+
if(!query)
return result;
- /* sort the name=value pairs first */
- do {
- char *amp;
- entry++;
- ap->p = p;
- amp = strchr(p, '&');
- if(amp)
- ap->len = amp - p; /* excluding the ampersand */
+ result = split_to_dyn_array(query, &query_array[0],
+ &num_query_components);
+ if(result) {
+ goto fail;
+ }
+
+ /* Create list of pairs, each pair containing an encoded query
+ * component */
+
+ for(index = 0; index < num_query_components; index++) {
+ const char *in_key;
+ size_t in_key_len;
+ char *offset;
+ size_t query_part_len = curlx_dyn_len(&query_array[index]);
+ char *query_part = curlx_dyn_ptr(&query_array[index]);
+
+ in_key = query_part;
+
+ offset = strchr(query_part, '=');
+ /* If there is no equals, this key has no value */
+ if(!offset) {
+ in_key_len = strlen(in_key);
+ }
else {
- ap->len = strlen(p);
- break;
+ in_key_len = offset - in_key;
}
- ap++;
- p = amp + 1;
- } while(entry < MAX_QUERYPAIRS);
- if(entry == MAX_QUERYPAIRS) {
- /* too many query pairs for us */
- failf(data, "aws-sigv4: too many query pairs in URL");
- return CURLE_URL_MALFORMAT;
- }
-
- qsort(&array[0], entry, sizeof(struct pair), compare_func);
-
- ap = &array[0];
- for(i = 0; !result && (i < entry); i++, ap++) {
- const char *q = ap->p;
- bool found_equals = FALSE;
- if(!ap->len)
- continue;
- result = canon_string(q, ap->len, dq, &found_equals);
- if(!result && !found_equals) {
- /* queries without value still need an equals */
- result = Curl_dyn_addn(dq, "=", 1);
+
+ curlx_dyn_init(&encoded_query_array[index].key, query_part_len*3 + 1);
+ curlx_dyn_init(&encoded_query_array[index].value, query_part_len*3 + 1);
+ counted_query_components++;
+
+ /* Decode/encode the key */
+ result = http_aws_decode_encode(in_key, in_key_len,
+ &encoded_query_array[index].key);
+ if(result) {
+ goto fail;
+ }
+
+ /* Decode/encode the value if it exists */
+ if(offset && offset != (query_part + query_part_len - 1)) {
+ size_t in_value_len;
+ const char *in_value = offset + 1;
+ in_value_len = query_part + query_part_len - (offset + 1);
+ result = http_aws_decode_encode(in_value, in_value_len,
+ &encoded_query_array[index].value);
+ if(result) {
+ goto fail;
+ }
+ }
+ else {
+ /* If there is no value, the value is an empty string */
+ curlx_dyn_init(&encoded_query_array[index].value, 2);
+ result = curlx_dyn_addn(&encoded_query_array[index].value, "", 1);
}
- if(!result && i < entry - 1) {
- /* insert ampersands between query pairs */
- result = Curl_dyn_addn(dq, "&", 1);
+
+ if(result) {
+ goto fail;
+ }
+ }
+
+ /* Sort the encoded query components by key and value */
+ qsort(&encoded_query_array, num_query_components,
+ sizeof(struct pair), compare_func);
+
+ /* Append the query components together to make a full query string */
+ for(index = 0; index < num_query_components; index++) {
+
+ if(index)
+ result = curlx_dyn_addn(dq, "&", 1);
+ if(!result) {
+ char *key_ptr = curlx_dyn_ptr(&encoded_query_array[index].key);
+ char *value_ptr = curlx_dyn_ptr(&encoded_query_array[index].value);
+ size_t vlen = curlx_dyn_len(&encoded_query_array[index].value);
+ if(value_ptr && vlen) {
+ result = curlx_dyn_addf(dq, "%s=%s", key_ptr, value_ptr);
+ }
+ else {
+ /* Empty value is always encoded to key= */
+ result = curlx_dyn_addf(dq, "%s=", key_ptr);
+ }
}
+ if(result)
+ break;
}
+
+fail:
+ if(counted_query_components)
+ /* the encoded_query_array might not be initialized yet */
+ pair_array_free(&encoded_query_array[0], counted_query_components);
+ dyn_array_free(&query_array[0], num_query_components);
return result;
}
-
-CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
+CURLcode Curl_output_aws_sigv4(struct Curl_easy *data)
{
CURLcode result = CURLE_OUT_OF_MEMORY;
struct connectdata *conn = data->conn;
- size_t len;
- const char *arg;
- char provider0[MAX_SIGV4_LEN + 1]="";
- char provider1[MAX_SIGV4_LEN + 1]="";
- char region[MAX_SIGV4_LEN + 1]="";
- char service[MAX_SIGV4_LEN + 1]="";
- bool sign_as_s3 = FALSE;
+ const char *line;
+ struct Curl_str provider0;
+ struct Curl_str provider1;
+ struct Curl_str region = { NULL, 0};
+ struct Curl_str service = { NULL, 0};
const char *hostname = conn->host.name;
time_t clock;
struct tm tm;
@@ -589,7 +689,7 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
char *date_header = NULL;
Curl_HttpReq httpreq;
const char *method = NULL;
- char *payload_hash = NULL;
+ const char *payload_hash = NULL;
size_t payload_hash_len = 0;
unsigned char sha_hash[CURL_SHA256_DIGEST_LENGTH];
char sha_hex[SHA256_HEX_LENGTH];
@@ -604,8 +704,10 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
unsigned char sign1[CURL_SHA256_DIGEST_LENGTH] = {0};
char *auth_headers = NULL;
- DEBUGASSERT(!proxy);
- (void)proxy;
+ if(data->set.path_as_is) {
+ failf(data, "Cannot use sigv4 authentication with path-as-is flag");
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
if(Curl_checkheaders(data, STRCONST("Authorization"))) {
/* Authorization already present, Bailing out */
@@ -613,10 +715,10 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
}
/* we init those buffers here, so goto fail will free initialized dynbuf */
- Curl_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER);
- Curl_dyn_init(&canonical_query, CURL_MAX_HTTP_HEADER);
- Curl_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER);
- Curl_dyn_init(&canonical_path, CURL_MAX_HTTP_HEADER);
+ curlx_dyn_init(&canonical_headers, CURL_MAX_HTTP_HEADER);
+ curlx_dyn_init(&canonical_query, CURL_MAX_HTTP_HEADER);
+ curlx_dyn_init(&signed_headers, CURL_MAX_HTTP_HEADER);
+ curlx_dyn_init(&canonical_path, CURL_MAX_HTTP_HEADER);
/*
* Parameters parsing
@@ -625,76 +727,69 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
* AWS is the default because most of non-amazon providers
* are still using aws:amz as a prefix.
*/
- arg = data->set.str[STRING_AWS_SIGV4] ?
- data->set.str[STRING_AWS_SIGV4] : "aws:amz";
+ line = data->set.str[STRING_AWS_SIGV4];
+ if(!line || !*line)
+ line = "aws:amz";
- /* provider1[:provider2[:region[:service]]]
+ /* provider0[:provider1[:region[:service]]]
No string can be longer than N bytes of non-whitespace
*/
- (void)sscanf(arg, "%" MAX_SIGV4_LEN_TXT "[^:]"
- ":%" MAX_SIGV4_LEN_TXT "[^:]"
- ":%" MAX_SIGV4_LEN_TXT "[^:]"
- ":%" MAX_SIGV4_LEN_TXT "s",
- provider0, provider1, region, service);
- if(!provider0[0]) {
+ if(curlx_str_until(&line, &provider0, MAX_SIGV4_LEN, ':')) {
failf(data, "first aws-sigv4 provider cannot be empty");
result = CURLE_BAD_FUNCTION_ARGUMENT;
goto fail;
}
- else if(!provider1[0])
- strcpy(provider1, provider0);
+ if(curlx_str_single(&line, ':') ||
+ curlx_str_until(&line, &provider1, MAX_SIGV4_LEN, ':')) {
+ provider1 = provider0;
+ }
+ else if(curlx_str_single(&line, ':') ||
+ curlx_str_until(&line, &region, MAX_SIGV4_LEN, ':') ||
+ curlx_str_single(&line, ':') ||
+ curlx_str_until(&line, &service, MAX_SIGV4_LEN, ':')) {
+ /* nothing to do */
+ }
- if(!service[0]) {
- char *hostdot = strchr(hostname, '.');
- if(!hostdot) {
+ if(!curlx_strlen(&service)) {
+ const char *p = hostname;
+ if(curlx_str_until(&p, &service, MAX_SIGV4_LEN, '.') ||
+ curlx_str_single(&p, '.')) {
failf(data, "aws-sigv4: service missing in parameters and hostname");
result = CURLE_URL_MALFORMAT;
goto fail;
}
- len = hostdot - hostname;
- if(len > MAX_SIGV4_LEN) {
- failf(data, "aws-sigv4: service too long in hostname");
- result = CURLE_URL_MALFORMAT;
- goto fail;
- }
- memcpy(service, hostname, len);
- service[len] = '\0';
- infof(data, "aws_sigv4: picked service %s from host", service);
+ infof(data, "aws_sigv4: picked service %.*s from host",
+ (int)curlx_strlen(&service), curlx_str(&service));
- if(!region[0]) {
- const char *reg = hostdot + 1;
- const char *hostreg = strchr(reg, '.');
- if(!hostreg) {
+ if(!curlx_strlen(&region)) {
+ if(curlx_str_until(&p, &region, MAX_SIGV4_LEN, '.') ||
+ curlx_str_single(&p, '.')) {
failf(data, "aws-sigv4: region missing in parameters and hostname");
result = CURLE_URL_MALFORMAT;
goto fail;
}
- len = hostreg - reg;
- if(len > MAX_SIGV4_LEN) {
- failf(data, "aws-sigv4: region too long in hostname");
- result = CURLE_URL_MALFORMAT;
- goto fail;
- }
- memcpy(region, reg, len);
- region[len] = '\0';
- infof(data, "aws_sigv4: picked region %s from host", region);
+ infof(data, "aws_sigv4: picked region %.*s from host",
+ (int)curlx_strlen(&region), curlx_str(&region));
}
}
Curl_http_method(data, conn, &method, &httpreq);
- /* AWS S3 requires a x-amz-content-sha256 header, and supports special
- * values like UNSIGNED-PAYLOAD */
- sign_as_s3 = (strcasecompare(provider0, "aws") &&
- strcasecompare(service, "s3"));
-
- payload_hash = parse_content_sha_hdr(data, provider1, &payload_hash_len);
+ payload_hash =
+ parse_content_sha_hdr(data, curlx_str(&provider1),
+ curlx_strlen(&provider1), &payload_hash_len);
if(!payload_hash) {
+ /* AWS S3 requires a x-amz-content-sha256 header, and supports special
+ * values like UNSIGNED-PAYLOAD */
+ bool sign_as_s3 = curlx_str_casecompare(&provider0, "aws") &&
+ curlx_str_casecompare(&service, "s3");
+
if(sign_as_s3)
- result = calc_s3_payload_hash(data, httpreq, provider1, sha_hash,
+ result = calc_s3_payload_hash(data, httpreq, curlx_str(&provider1),
+ curlx_strlen(&provider1), sha_hash,
sha_hex, content_sha256_hdr);
else
result = calc_payload_hash(data, sha_hash, sha_hex);
@@ -726,7 +821,8 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
goto fail;
}
- result = make_headers(data, hostname, timestamp, provider1,
+ result = make_headers(data, hostname, timestamp,
+ curlx_str(&provider1), curlx_strlen(&provider1),
&date_header, content_sha256_hdr,
&canonical_headers, &signed_headers);
if(result)
@@ -742,12 +838,13 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
memcpy(date, timestamp, sizeof(date));
date[sizeof(date) - 1] = 0;
- result = canon_query(data, data->state.up.query, &canonical_query);
+ result = canon_query(data->state.up.query, &canonical_query);
if(result)
goto fail;
- result = canon_string(data->state.up.path, strlen(data->state.up.path),
- &canonical_path, NULL);
+ result = canon_path(data->state.up.path, strlen(data->state.up.path),
+ &canonical_path,
+ should_urlencode(&service));
if(result)
goto fail;
result = CURLE_OUT_OF_MEMORY;
@@ -760,25 +857,31 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
"%s\n" /* SignedHeaders */
"%.*s", /* HashedRequestPayload in hex */
method,
- Curl_dyn_ptr(&canonical_path),
- Curl_dyn_ptr(&canonical_query) ?
- Curl_dyn_ptr(&canonical_query) : "",
- Curl_dyn_ptr(&canonical_headers),
- Curl_dyn_ptr(&signed_headers),
+ curlx_dyn_ptr(&canonical_path),
+ curlx_dyn_ptr(&canonical_query) ?
+ curlx_dyn_ptr(&canonical_query) : "",
+ curlx_dyn_ptr(&canonical_headers),
+ curlx_dyn_ptr(&signed_headers),
(int)payload_hash_len, payload_hash);
if(!canonical_request)
goto fail;
- DEBUGF(infof(data, "Canonical request: %s", canonical_request));
+ infof(data, "aws_sigv4: Canonical request (enclosed in []) - [%s]",
+ canonical_request);
- /* provider 0 lowercase */
- Curl_strntolower(provider0, provider0, strlen(provider0));
- request_type = aprintf("%s4_request", provider0);
+ request_type = aprintf("%.*s4_request",
+ (int)curlx_strlen(&provider0), curlx_str(&provider0));
if(!request_type)
goto fail;
- credential_scope = aprintf("%s/%s/%s/%s",
- date, region, service, request_type);
+ /* provider0 is lowercased *after* aprintf() so that the buffer can be
+ written to */
+ Curl_strntolower(request_type, request_type, curlx_strlen(&provider0));
+
+ credential_scope = aprintf("%s/%.*s/%.*s/%s", date,
+ (int)curlx_strlen(&region), curlx_str(&region),
+ (int)curlx_strlen(&service), curlx_str(&service),
+ request_type);
if(!credential_scope)
goto fail;
@@ -788,42 +891,49 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
sha256_to_hex(sha_hex, sha_hash);
- /* provider 0 uppercase */
- Curl_strntoupper(provider0, provider0, strlen(provider0));
-
/*
* Google allows using RSA key instead of HMAC, so this code might change
* in the future. For now we only support HMAC.
*/
- str_to_sign = aprintf("%s4-HMAC-SHA256\n" /* Algorithm */
+ str_to_sign = aprintf("%.*s4-HMAC-SHA256\n" /* Algorithm */
"%s\n" /* RequestDateTime */
"%s\n" /* CredentialScope */
"%s", /* HashedCanonicalRequest in hex */
- provider0,
+ (int)curlx_strlen(&provider0), curlx_str(&provider0),
timestamp,
credential_scope,
sha_hex);
- if(!str_to_sign) {
+ if(!str_to_sign)
goto fail;
- }
- /* provider 0 uppercase */
- secret = aprintf("%s4%s", provider0,
- data->state.aptr.passwd ?
+ /* make provider0 part done uppercase */
+ Curl_strntoupper(str_to_sign, curlx_str(&provider0),
+ curlx_strlen(&provider0));
+
+ infof(data, "aws_sigv4: String to sign (enclosed in []) - [%s]",
+ str_to_sign);
+
+ secret = aprintf("%.*s4%s", (int)curlx_strlen(&provider0),
+ curlx_str(&provider0), data->state.aptr.passwd ?
data->state.aptr.passwd : "");
if(!secret)
goto fail;
+ /* make provider0 part done uppercase */
+ Curl_strntoupper(secret, curlx_str(&provider0), curlx_strlen(&provider0));
HMAC_SHA256(secret, strlen(secret), date, strlen(date), sign0);
- HMAC_SHA256(sign0, sizeof(sign0), region, strlen(region), sign1);
- HMAC_SHA256(sign1, sizeof(sign1), service, strlen(service), sign0);
+ HMAC_SHA256(sign0, sizeof(sign0),
+ curlx_str(&region), curlx_strlen(&region), sign1);
+ HMAC_SHA256(sign1, sizeof(sign1),
+ curlx_str(&service), curlx_strlen(&service), sign0);
HMAC_SHA256(sign0, sizeof(sign0), request_type, strlen(request_type), sign1);
HMAC_SHA256(sign1, sizeof(sign1), str_to_sign, strlen(str_to_sign), sign0);
sha256_to_hex(sha_hex, sign0);
- /* provider 0 uppercase */
- auth_headers = aprintf("Authorization: %s4-HMAC-SHA256 "
+ infof(data, "aws_sigv4: Signature - %s", sha_hex);
+
+ auth_headers = aprintf("Authorization: %.*s4-HMAC-SHA256 "
"Credential=%s/%s, "
"SignedHeaders=%s, "
"Signature=%s\r\n"
@@ -834,27 +944,30 @@ CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy)
*/
"%s"
"%s", /* optional sha256 header includes \r\n */
- provider0,
+ (int)curlx_strlen(&provider0), curlx_str(&provider0),
user,
credential_scope,
- Curl_dyn_ptr(&signed_headers),
+ curlx_dyn_ptr(&signed_headers),
sha_hex,
date_header ? date_header : "",
content_sha256_hdr);
if(!auth_headers) {
goto fail;
}
+ /* provider 0 uppercase */
+ Curl_strntoupper(&auth_headers[sizeof("Authorization: ") - 1],
+ curlx_str(&provider0), curlx_strlen(&provider0));
- Curl_safefree(data->state.aptr.userpwd);
+ free(data->state.aptr.userpwd);
data->state.aptr.userpwd = auth_headers;
data->state.authhost.done = TRUE;
result = CURLE_OK;
fail:
- Curl_dyn_free(&canonical_query);
- Curl_dyn_free(&canonical_path);
- Curl_dyn_free(&canonical_headers);
- Curl_dyn_free(&signed_headers);
+ curlx_dyn_free(&canonical_query);
+ curlx_dyn_free(&canonical_path);
+ curlx_dyn_free(&canonical_headers);
+ curlx_dyn_free(&signed_headers);
free(canonical_request);
free(request_type);
free(credential_scope);
@@ -864,4 +977,176 @@ fail:
return result;
}
+/*
+* Frees all allocated strings in a dynbuf pair array, and the dynbuf itself
+*/
+
+static void pair_array_free(struct pair *pair_array, size_t num_elements)
+{
+ size_t index;
+
+ for(index = 0; index != num_elements; index++) {
+ curlx_dyn_free(&pair_array[index].key);
+ curlx_dyn_free(&pair_array[index].value);
+ }
+
+}
+
+/*
+* Frees all allocated strings in a split dynbuf, and the dynbuf itself
+*/
+
+static void dyn_array_free(struct dynbuf *db, size_t num_elements)
+{
+ size_t index;
+
+ for(index = 0; index < num_elements; index++)
+ curlx_dyn_free((&db[index]));
+}
+
+/*
+* Splits source string by SPLIT_BY, and creates an array of dynbuf in db.
+* db is initialized by this function.
+* Caller is responsible for freeing the array elements with dyn_array_free
+*/
+
+#define SPLIT_BY '&'
+
+static CURLcode split_to_dyn_array(const char *source,
+ struct dynbuf db[MAX_QUERY_COMPONENTS],
+ size_t *num_splits_out)
+{
+ CURLcode result = CURLE_OK;
+ size_t len = strlen(source);
+ size_t pos; /* Position in result buffer */
+ size_t start = 0; /* Start of current segment */
+ size_t segment_length = 0;
+ size_t index = 0;
+ size_t num_splits = 0;
+
+ /* Split source_ptr on SPLIT_BY and store the segment offsets and length in
+ * array */
+ for(pos = 0; pos < len; pos++) {
+ if(source[pos] == SPLIT_BY) {
+ if(segment_length) {
+ curlx_dyn_init(&db[index], segment_length + 1);
+ result = curlx_dyn_addn(&db[index], &source[start],
+ segment_length);
+ if(result)
+ goto fail;
+
+ segment_length = 0;
+ index++;
+ if(++num_splits == MAX_QUERY_COMPONENTS) {
+ result = CURLE_TOO_LARGE;
+ goto fail;
+ }
+ }
+ start = pos + 1;
+ }
+ else {
+ segment_length++;
+ }
+ }
+
+ if(segment_length) {
+ curlx_dyn_init(&db[index], segment_length + 1);
+ result = curlx_dyn_addn(&db[index], &source[start], segment_length);
+ if(!result) {
+ if(++num_splits == MAX_QUERY_COMPONENTS)
+ result = CURLE_TOO_LARGE;
+ }
+ }
+fail:
+ *num_splits_out = num_splits;
+ return result;
+}
+
+
+static bool is_reserved_char(const char c)
+{
+ return (ISALNUM(c) || ISURLPUNTCS(c));
+}
+
+static CURLcode uri_encode_path(struct Curl_str *original_path,
+ struct dynbuf *new_path)
+{
+ const char *p = curlx_str(original_path);
+ size_t i;
+
+ for(i = 0; i < curlx_strlen(original_path); i++) {
+ /* Do not encode slashes or unreserved chars from RFC 3986 */
+ CURLcode result = CURLE_OK;
+ unsigned char c = p[i];
+ if(is_reserved_char(c) || c == '/')
+ result = curlx_dyn_addn(new_path, &c, 1);
+ else
+ result = curlx_dyn_addf(new_path, "%%%02X", c);
+ if(result)
+ return result;
+ }
+
+ return CURLE_OK;
+}
+
+
+static CURLcode encode_query_component(char *component, size_t len,
+ struct dynbuf *db)
+{
+ size_t i;
+ for(i = 0; i < len; i++) {
+ CURLcode result = CURLE_OK;
+ unsigned char this_char = component[i];
+
+ if(is_reserved_char(this_char))
+ /* Escape unreserved chars from RFC 3986 */
+ result = curlx_dyn_addn(db, &this_char, 1);
+ else if(this_char == '+')
+ /* Encode '+' as space */
+ result = curlx_dyn_add(db, "%20");
+ else
+ result = curlx_dyn_addf(db, "%%%02X", this_char);
+ if(result)
+ return result;
+ }
+
+ return CURLE_OK;
+}
+
+/*
+* Populates a dynbuf containing url_encode(url_decode(in))
+*/
+
+static CURLcode http_aws_decode_encode(const char *in, size_t in_len,
+ struct dynbuf *out)
+{
+ char *out_s;
+ size_t out_s_len;
+ CURLcode result =
+ Curl_urldecode(in, in_len, &out_s, &out_s_len, REJECT_NADA);
+
+ if(!result) {
+ result = encode_query_component(out_s, out_s_len, out);
+ Curl_safefree(out_s);
+ }
+ return result;
+}
+
+static bool should_urlencode(struct Curl_str *service_name)
+{
+ /*
+ * These services require unmodified (not additionally url encoded) URL
+ * paths.
+ * should_urlencode == true is equivalent to should_urlencode_uri_path
+ * from the AWS SDK. Urls are already normalized by the curl url parser
+ */
+
+ if(curlx_str_cmp(service_name, "s3") ||
+ curlx_str_cmp(service_name, "s3-express") ||
+ curlx_str_cmp(service_name, "s3-outposts")) {
+ return false;
+ }
+ return true;
+}
+
#endif /* !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_AWS) */
diff --git a/libs/libcurl/src/http_aws_sigv4.h b/libs/libcurl/src/http_aws_sigv4.h
index 2c2def4246..f095588ab7 100644
--- a/libs/libcurl/src/http_aws_sigv4.h
+++ b/libs/libcurl/src/http_aws_sigv4.h
@@ -11,7 +11,7 @@
*
* This software is licensed 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.
+ * are also available at https://curl.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
@@ -24,8 +24,18 @@
*
***************************************************************************/
#include "curl_setup.h"
+#include "curlx/dynbuf.h"
+#include "urldata.h"
+#include "curlx/strparse.h"
/* this is for creating aws_sigv4 header output */
-CURLcode Curl_output_aws_sigv4(struct Curl_easy *data, bool proxy);
+CURLcode Curl_output_aws_sigv4(struct Curl_easy *data);
+
+#ifdef UNITTESTS
+UNITTEST CURLcode canon_path(const char *q, size_t len,
+ struct dynbuf *new_path,
+ bool normalize);
+UNITTEST CURLcode canon_query(const char *query, struct dynbuf *dq);
+#endif
#endif /* HEADER_CURL_HTTP_AWS_SIGV4_H */
diff --git a/libs/libcurl/src/http_chunks.c b/libs/libcurl/src/http_chunks.c
index 2d2b3440e2..1180c0bcfa 100644
--- a/libs/libcurl/src/http_chunks.c
+++ b/libs/libcurl/src/http_chunks.c
@@ -30,12 +30,12 @@
#include "curl_printf.h"
#include "curl_trc.h"
#include "sendf.h" /* for the client write stuff */
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
#include "content_encoding.h"
#include "http.h"
#include "multiif.h"
-#include "strtoofft.h"
-#include "warnless.h"
+#include "curlx/strparse.h"
+#include "curlx/warnless.h"
/* The last #include files should be: */
#include "curl_memory.h"
@@ -85,7 +85,7 @@ void Curl_httpchunk_init(struct Curl_easy *data, struct Curl_chunker *ch,
ch->hexindex = 0; /* start at 0 */
ch->state = CHUNK_HEX; /* we get hex first! */
ch->last_code = CHUNKE_OK;
- Curl_dyn_init(&ch->trailer, DYN_H1_TRAILER);
+ curlx_dyn_init(&ch->trailer, DYN_H1_TRAILER);
ch->ignore_body = ignore_body;
}
@@ -96,14 +96,14 @@ void Curl_httpchunk_reset(struct Curl_easy *data, struct Curl_chunker *ch,
ch->hexindex = 0; /* start at 0 */
ch->state = CHUNK_HEX; /* we get hex first! */
ch->last_code = CHUNKE_OK;
- Curl_dyn_reset(&ch->trailer);
+ curlx_dyn_reset(&ch->trailer);
ch->ignore_body = ignore_body;
}
void Curl_httpchunk_free(struct Curl_easy *data, struct Curl_chunker *ch)
{
(void)data;
- Curl_dyn_free(&ch->trailer);
+ curlx_dyn_free(&ch->trailer);
}
bool Curl_httpchunk_is_done(struct Curl_easy *data, struct Curl_chunker *ch)
@@ -134,7 +134,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
if(cw_next)
result = Curl_cwriter_write(data, cw_next, CLIENTWRITE_BODY, buf, blen);
else
- result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)buf, blen);
+ result = Curl_client_write(data, CLIENTWRITE_BODY, buf, blen);
if(result) {
ch->state = CHUNK_FAILED;
ch->last_code = CHUNKE_PASSTHRU_ERROR;
@@ -158,6 +158,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
(*pconsumed)++;
}
else {
+ const char *p;
if(0 == ch->hexindex) {
/* This is illegal data, we received junk where we expected
a hexadecimal digit. */
@@ -166,11 +167,11 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
ch->last_code = CHUNKE_ILLEGAL_HEX;
return CURLE_RECV_ERROR;
}
-
/* blen and buf are unmodified */
ch->hexbuffer[ch->hexindex] = 0;
- if(curlx_strtoofft(ch->hexbuffer, NULL, 16, &ch->datasize)) {
- failf(data, "chunk hex-length not valid: '%s'", ch->hexbuffer);
+ p = &ch->hexbuffer[0];
+ if(curlx_str_hex(&p, &ch->datasize, CURL_OFF_T_MAX)) {
+ failf(data, "invalid chunk size: '%s'", ch->hexbuffer);
ch->state = CHUNK_FAILED;
ch->last_code = CHUNKE_ILLEGAL_HEX;
return CURLE_RECV_ERROR;
@@ -212,8 +213,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
result = Curl_cwriter_write(data, cw_next, CLIENTWRITE_BODY,
buf, piece);
else
- result = Curl_client_write(data, CLIENTWRITE_BODY,
- (char *)buf, piece);
+ result = Curl_client_write(data, CLIENTWRITE_BODY, buf, piece);
if(result) {
ch->state = CHUNK_FAILED;
ch->last_code = CHUNKE_PASSTHRU_ERROR;
@@ -251,21 +251,20 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
case CHUNK_TRAILER:
if((*buf == 0x0d) || (*buf == 0x0a)) {
- char *tr = Curl_dyn_ptr(&ch->trailer);
+ char *tr = curlx_dyn_ptr(&ch->trailer);
/* this is the end of a trailer, but if the trailer was zero bytes
there was no trailer and we move on */
if(tr) {
- size_t trlen;
- result = Curl_dyn_addn(&ch->trailer, (char *)STRCONST("\x0d\x0a"));
+ result = curlx_dyn_addn(&ch->trailer, STRCONST("\x0d\x0a"));
if(result) {
ch->state = CHUNK_FAILED;
ch->last_code = CHUNKE_OUT_OF_MEMORY;
return result;
}
- tr = Curl_dyn_ptr(&ch->trailer);
- trlen = Curl_dyn_len(&ch->trailer);
+ tr = curlx_dyn_ptr(&ch->trailer);
if(!data->set.http_te_skip) {
+ size_t trlen = curlx_dyn_len(&ch->trailer);
if(cw_next)
result = Curl_cwriter_write(data, cw_next,
CLIENTWRITE_HEADER|
@@ -282,7 +281,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
return result;
}
}
- Curl_dyn_reset(&ch->trailer);
+ curlx_dyn_reset(&ch->trailer);
ch->state = CHUNK_TRAILER_CR;
if(*buf == 0x0a)
/* already on the LF */
@@ -295,7 +294,7 @@ static CURLcode httpchunk_readwrite(struct Curl_easy *data,
}
}
else {
- result = Curl_dyn_addn(&ch->trailer, buf, 1);
+ result = curlx_dyn_addn(&ch->trailer, buf, 1);
if(result) {
ch->state = CHUNK_FAILED;
ch->last_code = CHUNKE_OUT_OF_MEMORY;
diff --git a/libs/libcurl/src/http_chunks.h b/libs/libcurl/src/http_chunks.h
index e124882203..46983a50dd 100644
--- a/libs/libcurl/src/http_chunks.h
+++ b/libs/libcurl/src/http_chunks.h
@@ -26,7 +26,7 @@
#ifndef CURL_DISABLE_HTTP
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
struct connectdata;
@@ -49,7 +49,7 @@ typedef enum {
POST_CR state. */
CHUNK_DATA,
- /* POSTLF should get a CR and then a LF and nothing else, then move back to
+ /* POSTLF should get a CR and then an LF and nothing else, then move back to
HEX as the CRLF combination marks the end of a chunk. A missing CR is no
big deal. */
CHUNK_POSTLF,
@@ -64,7 +64,7 @@ typedef enum {
CHUNK_TRAILER,
/* A trailer CR has been found - next state is CHUNK_TRAILER_POSTCR.
- Next char must be a LF */
+ Next char must be an LF */
CHUNK_TRAILER_CR,
/* A trailer LF must be found now, otherwise CHUNKE_BAD_CHUNK will be
diff --git a/libs/libcurl/src/http_digest.c b/libs/libcurl/src/http_digest.c
index f63006f03b..2dee57057e 100644
--- a/libs/libcurl/src/http_digest.c
+++ b/libs/libcurl/src/http_digest.c
@@ -30,6 +30,7 @@
#include "strcase.h"
#include "vauth/vauth.h"
#include "http_digest.h"
+#include "curlx/strparse.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -62,8 +63,7 @@ CURLcode Curl_input_digest(struct Curl_easy *data,
return CURLE_BAD_CONTENT_ENCODING;
header += strlen("Digest");
- while(*header && ISBLANK(*header))
- header++;
+ curlx_str_passblanks(&header);
return Curl_auth_decode_digest_http_message(header, digest);
}
@@ -145,15 +145,15 @@ CURLcode Curl_output_digest(struct Curl_easy *data,
*/
if(authp->iestyle) {
- tmp = strchr((char *)uripath, '?');
+ tmp = strchr((const char *)uripath, '?');
if(tmp) {
- size_t urilen = tmp - (char *)uripath;
+ size_t urilen = tmp - (const char *)uripath;
/* typecast is fine here since the value is always less than 32 bits */
path = (unsigned char *) aprintf("%.*s", (int)urilen, uripath);
}
}
if(!tmp)
- path = (unsigned char *) strdup((char *) uripath);
+ path = (unsigned char *) strdup((const char *) uripath);
if(!path)
return CURLE_OUT_OF_MEMORY;
diff --git a/libs/libcurl/src/http_negotiate.c b/libs/libcurl/src/http_negotiate.c
index 858a797d8b..ffa6906b74 100644
--- a/libs/libcurl/src/http_negotiate.c
+++ b/libs/libcurl/src/http_negotiate.c
@@ -27,10 +27,12 @@
#if !defined(CURL_DISABLE_HTTP) && defined(USE_SPNEGO)
#include "urldata.h"
+#include "cfilters.h"
#include "sendf.h"
#include "http_negotiate.h"
#include "vauth/vauth.h"
#include "vtls/vtls.h"
+#include "curlx/strparse.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -85,8 +87,7 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
/* Obtain the input token, if any */
header += strlen("Negotiate");
- while(*header && ISBLANK(*header))
- header++;
+ curlx_str_passblanks(&header);
len = strlen(header);
neg_ctx->havenegdata = len != 0;
@@ -108,9 +109,10 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
neg_ctx->sslContext = conn->sslContext;
#endif
/* Check if the connection is using SSL and get the channel binding data */
-#if defined(USE_SSL) && defined(HAVE_GSSAPI)
- if(conn->handler->flags & PROTOPT_SSL) {
- Curl_dyn_init(&neg_ctx->channel_binding_data, SSL_CB_MAX_SIZE);
+#ifdef HAVE_GSSAPI
+#ifdef USE_SSL
+ curlx_dyn_init(&neg_ctx->channel_binding_data, SSL_CB_MAX_SIZE + 1);
+ if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
result = Curl_ssl_get_channel_binding(
data, FIRSTSOCKET, &neg_ctx->channel_binding_data);
if(result) {
@@ -118,14 +120,17 @@ CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn,
return result;
}
}
-#endif
+#else
+ curlx_dyn_init(&neg_ctx->channel_binding_data, 1);
+#endif /* USE_SSL */
+#endif /* HAVE_GSSAPI */
/* Initialize the security context and decode our challenge */
result = Curl_auth_decode_spnego_message(data, userp, passwdp, service,
host, header, neg_ctx);
-#if defined(USE_SSL) && defined(HAVE_GSSAPI)
- Curl_dyn_free(&neg_ctx->channel_binding_data);
+#ifdef HAVE_GSSAPI
+ curlx_dyn_free(&neg_ctx->channel_binding_data);
#endif
if(result)
@@ -202,12 +207,12 @@ CURLcode Curl_output_negotiate(struct Curl_easy *data,
if(proxy) {
#ifndef CURL_DISABLE_PROXY
- Curl_safefree(data->state.aptr.proxyuserpwd);
+ free(data->state.aptr.proxyuserpwd);
data->state.aptr.proxyuserpwd = userp;
#endif
}
else {
- Curl_safefree(data->state.aptr.userpwd);
+ free(data->state.aptr.userpwd);
data->state.aptr.userpwd = userp;
}
diff --git a/libs/libcurl/src/http_ntlm.c b/libs/libcurl/src/http_ntlm.c
index 03788062ff..21d1845286 100644
--- a/libs/libcurl/src/http_ntlm.c
+++ b/libs/libcurl/src/http_ntlm.c
@@ -33,16 +33,15 @@
* https://www.innovation.ch/java/ntlm.html
*/
-#define DEBUG_ME 0
-
#include "urldata.h"
#include "sendf.h"
#include "strcase.h"
#include "http_ntlm.h"
#include "curl_ntlm_core.h"
-#include "curl_base64.h"
+#include "curlx/base64.h"
#include "vauth/vauth.h"
#include "url.h"
+#include "curlx/strparse.h"
/* SSL backend-specific #if branches in this file must be kept in the order
documented in curl_ntlm_core. */
@@ -55,12 +54,6 @@
#include "curl_memory.h"
#include "memdebug.h"
-#if DEBUG_ME
-# define DEBUG_OUT(x) x
-#else
-# define DEBUG_OUT(x) Curl_nop_stmt
-#endif
-
CURLcode Curl_input_ntlm(struct Curl_easy *data,
bool proxy, /* if proxy or not */
const char *header) /* rest of the www-authenticate:
@@ -78,14 +71,12 @@ CURLcode Curl_input_ntlm(struct Curl_easy *data,
if(checkprefix("NTLM", header)) {
header += strlen("NTLM");
- while(*header && ISSPACE(*header))
- header++;
-
+ curlx_str_passblanks(&header);
if(*header) {
unsigned char *hdr;
size_t hdrlen;
- result = Curl_base64_decode(header, &hdr, &hdrlen);
+ result = curlx_base64_decode(header, &hdr, &hdrlen);
if(!result) {
struct bufref hdrbuf;
@@ -214,7 +205,7 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy)
ntlm, &ntlmmsg);
if(!result) {
DEBUGASSERT(Curl_bufref_len(&ntlmmsg) != 0);
- result = Curl_base64_encode((const char *) Curl_bufref_ptr(&ntlmmsg),
+ result = curlx_base64_encode((const char *) Curl_bufref_ptr(&ntlmmsg),
Curl_bufref_len(&ntlmmsg), &base64, &len);
if(!result) {
free(*allocuserpwd);
@@ -233,8 +224,8 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy)
result = Curl_auth_create_ntlm_type3_message(data, userp, passwdp,
ntlm, &ntlmmsg);
if(!result && Curl_bufref_len(&ntlmmsg)) {
- result = Curl_base64_encode((const char *) Curl_bufref_ptr(&ntlmmsg),
- Curl_bufref_len(&ntlmmsg), &base64, &len);
+ result = curlx_base64_encode((const char *) Curl_bufref_ptr(&ntlmmsg),
+ Curl_bufref_len(&ntlmmsg), &base64, &len);
if(!result) {
free(*allocuserpwd);
*allocuserpwd = aprintf("%sAuthorization: NTLM %s\r\n",
@@ -252,6 +243,12 @@ CURLcode Curl_output_ntlm(struct Curl_easy *data, bool proxy)
break;
case NTLMSTATE_LAST:
+ /* since this is a little artificial in that this is used without any
+ outgoing auth headers being set, we need to set the bit by force */
+ if(proxy)
+ data->info.proxyauthpicked = CURLAUTH_NTLM;
+ else
+ data->info.httpauthpicked = CURLAUTH_NTLM;
Curl_safefree(*allocuserpwd);
authp->done = TRUE;
break;
diff --git a/libs/libcurl/src/http_proxy.c b/libs/libcurl/src/http_proxy.c
index c2c7ef6e37..a2fb414a0c 100644
--- a/libs/libcurl/src/http_proxy.c
+++ b/libs/libcurl/src/http_proxy.c
@@ -29,9 +29,6 @@
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_PROXY)
#include <curl/curl.h>
-#ifdef USE_HYPER
-#include <hyper.h>
-#endif
#include "sendf.h"
#include "http.h"
#include "url.h"
@@ -41,16 +38,158 @@
#include "cf-h1-proxy.h"
#include "cf-h2-proxy.h"
#include "connect.h"
-#include "curlx.h"
+#include "strcase.h"
#include "vtls/vtls.h"
#include "transfer.h"
#include "multiif.h"
+#include "vauth/vauth.h"
+#include "curlx/strparse.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
#include "memdebug.h"
+static bool hd_name_eq(const char *n1, size_t n1len,
+ const char *n2, size_t n2len)
+{
+ return (n1len == n2len) ? strncasecompare(n1, n2, n1len) : FALSE;
+}
+
+static CURLcode dynhds_add_custom(struct Curl_easy *data,
+ bool is_connect, int httpversion,
+ struct dynhds *hds)
+{
+ struct connectdata *conn = data->conn;
+ const char *ptr;
+ struct curl_slist *h[2];
+ struct curl_slist *headers;
+ int numlists = 1; /* by default */
+ int i;
+
+ enum Curl_proxy_use proxy;
+
+ if(is_connect)
+ proxy = HEADER_CONNECT;
+ else
+ proxy = conn->bits.httpproxy && !conn->bits.tunnel_proxy ?
+ HEADER_PROXY : HEADER_SERVER;
+
+ switch(proxy) {
+ case HEADER_SERVER:
+ h[0] = data->set.headers;
+ break;
+ case HEADER_PROXY:
+ h[0] = data->set.headers;
+ if(data->set.sep_headers) {
+ h[1] = data->set.proxyheaders;
+ numlists++;
+ }
+ break;
+ case HEADER_CONNECT:
+ if(data->set.sep_headers)
+ h[0] = data->set.proxyheaders;
+ else
+ h[0] = data->set.headers;
+ break;
+ }
+
+ /* loop through one or two lists */
+ for(i = 0; i < numlists; i++) {
+ for(headers = h[i]; headers; headers = headers->next) {
+ const char *name, *value;
+ size_t namelen, valuelen;
+
+ /* There are 2 quirks in place for custom headers:
+ * 1. setting only 'name:' to suppress a header from being sent
+ * 2. setting only 'name;' to send an empty (illegal) header
+ */
+ ptr = strchr(headers->data, ':');
+ if(ptr) {
+ name = headers->data;
+ namelen = ptr - headers->data;
+ ptr++; /* pass the colon */
+ curlx_str_passblanks(&ptr);
+ if(*ptr) {
+ value = ptr;
+ valuelen = strlen(value);
+ }
+ else {
+ /* quirk #1, suppress this header */
+ continue;
+ }
+ }
+ else {
+ ptr = strchr(headers->data, ';');
+
+ if(!ptr) {
+ /* neither : nor ; in provided header value. We seem
+ * to ignore this silently */
+ continue;
+ }
+
+ name = headers->data;
+ namelen = ptr - headers->data;
+ ptr++; /* pass the semicolon */
+ curlx_str_passblanks(&ptr);
+ if(!*ptr) {
+ /* quirk #2, send an empty header */
+ value = "";
+ valuelen = 0;
+ }
+ else {
+ /* this may be used for something else in the future,
+ * ignore this for now */
+ continue;
+ }
+ }
+
+ DEBUGASSERT(name && value);
+ if(data->state.aptr.host &&
+ /* a Host: header was sent already, do not pass on any custom Host:
+ header as that will produce *two* in the same request! */
+ hd_name_eq(name, namelen, STRCONST("Host:")))
+ ;
+ else if(data->state.httpreq == HTTPREQ_POST_FORM &&
+ /* this header (extended by formdata.c) is sent later */
+ hd_name_eq(name, namelen, STRCONST("Content-Type:")))
+ ;
+ else if(data->state.httpreq == HTTPREQ_POST_MIME &&
+ /* this header is sent later */
+ hd_name_eq(name, namelen, STRCONST("Content-Type:")))
+ ;
+ else if(data->req.authneg &&
+ /* while doing auth neg, do not allow the custom length since
+ we will force length zero then */
+ hd_name_eq(name, namelen, STRCONST("Content-Length:")))
+ ;
+ else if(data->state.aptr.te &&
+ /* when asking for Transfer-Encoding, do not pass on a custom
+ Connection: */
+ hd_name_eq(name, namelen, STRCONST("Connection:")))
+ ;
+ else if((httpversion >= 20) &&
+ hd_name_eq(name, namelen, STRCONST("Transfer-Encoding:")))
+ /* HTTP/2 and HTTP/3 do not support chunked requests */
+ ;
+ else if((hd_name_eq(name, namelen, STRCONST("Authorization:")) ||
+ hd_name_eq(name, namelen, STRCONST("Cookie:"))) &&
+ /* be careful of sending this potentially sensitive header to
+ other hosts */
+ !Curl_auth_allowed_to_host(data))
+ ;
+ else {
+ CURLcode result;
+
+ result = Curl_dynhds_add(hds, name, namelen, value, valuelen);
+ if(result)
+ return result;
+ }
+ }
+ }
+
+ return CURLE_OK;
+}
CURLcode Curl_http_proxy_get_destination(struct Curl_cfilter *cf,
const char **phostname,
@@ -81,11 +220,18 @@ CURLcode Curl_http_proxy_get_destination(struct Curl_cfilter *cf,
return CURLE_OK;
}
+struct cf_proxy_ctx {
+ /* the protocol specific sub-filter we install during connect */
+ struct Curl_cfilter *cf_protocol;
+ int httpversion; /* HTTP version used to CONNECT */
+};
+
CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq,
struct Curl_cfilter *cf,
struct Curl_easy *data,
int http_version_major)
{
+ struct cf_proxy_ctx *ctx = cf->ctx;
const char *hostname = NULL;
char *authority = NULL;
int port;
@@ -146,7 +292,7 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq,
goto out;
}
- result = Curl_dynhds_add_custom(data, TRUE, &req->headers);
+ result = dynhds_add_custom(data, TRUE, ctx->httpversion, &req->headers);
out:
if(result && req) {
@@ -158,15 +304,9 @@ out:
return result;
}
-
-struct cf_proxy_ctx {
- /* the protocol specific sub-filter we install during connect */
- struct Curl_cfilter *cf_protocol;
-};
-
static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool blocking, bool *done)
+ bool *done)
{
struct cf_proxy_ctx *ctx = cf->ctx;
CURLcode result;
@@ -178,13 +318,14 @@ static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf,
CURL_TRC_CF(data, cf, "connect");
connect_sub:
- result = cf->next->cft->do_connect(cf->next, data, blocking, done);
+ result = cf->next->cft->do_connect(cf->next, data, done);
if(result || !*done)
return result;
*done = FALSE;
if(!ctx->cf_protocol) {
struct Curl_cfilter *cf_protocol = NULL;
+ int httpversion = 0;
int alpn = Curl_conn_cf_is_ssl(cf->next) ?
cf->conn->proxy_alpn : CURL_HTTP_VERSION_1_1;
@@ -200,6 +341,7 @@ connect_sub:
if(result)
goto out;
cf_protocol = cf->next;
+ httpversion = (alpn == CURL_HTTP_VERSION_1_0) ? 10 : 11;
break;
#ifdef USE_NGHTTP2
case CURL_HTTP_VERSION_2:
@@ -209,6 +351,7 @@ connect_sub:
if(result)
goto out;
cf_protocol = cf->next;
+ httpversion = 20;
break;
#endif
default:
@@ -218,6 +361,7 @@ connect_sub:
}
ctx->cf_protocol = cf_protocol;
+ ctx->httpversion = httpversion;
/* after we installed the filter "below" us, we call connect
* on out sub-chain again.
*/
diff --git a/libs/libcurl/src/http_proxy.h b/libs/libcurl/src/http_proxy.h
index 5566a7c0bd..95f4c7522c 100644
--- a/libs/libcurl/src/http_proxy.h
+++ b/libs/libcurl/src/http_proxy.h
@@ -30,6 +30,12 @@
#include "urldata.h"
+enum Curl_proxy_use {
+ HEADER_SERVER, /* direct to server */
+ HEADER_PROXY, /* regular request to proxy */
+ HEADER_CONNECT /* sending CONNECT to a proxy */
+};
+
CURLcode Curl_http_proxy_get_destination(struct Curl_cfilter *cf,
const char **phostname,
int *pport, bool *pipv6_ip);
diff --git a/libs/libcurl/src/httpsrr.c b/libs/libcurl/src/httpsrr.c
new file mode 100644
index 0000000000..8111d0fc6e
--- /dev/null
+++ b/libs/libcurl/src/httpsrr.c
@@ -0,0 +1,213 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_HTTPSRR
+
+#include "urldata.h"
+#include "curl_addrinfo.h"
+#include "httpsrr.h"
+#include "connect.h"
+#include "sendf.h"
+#include "strdup.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#define MAX_ALPN_LENGTH 255
+
+static CURLcode httpsrr_decode_alpn(const char *cp, size_t len,
+ unsigned char *alpns)
+{
+ /*
+ * The wire-format value for "alpn" consists of at least one alpn-id
+ * prefixed by its length as a single octet, and these length-value pairs
+ * are concatenated to form the SvcParamValue. These pairs MUST exactly fill
+ * the SvcParamValue; otherwise, the SvcParamValue is malformed.
+ */
+ int idnum = 0;
+
+ while(len > 0) {
+ size_t tlen = (size_t) *cp++;
+ enum alpnid id;
+ len--;
+ if(tlen > len)
+ return CURLE_BAD_CONTENT_ENCODING;
+
+ /* we only store ALPN ids we know about */
+ id = Curl_alpn2alpnid(cp, tlen);
+ if(id != ALPN_none) {
+ if(idnum == MAX_HTTPSRR_ALPNS)
+ break;
+ if(idnum && memchr(alpns, id, idnum))
+ /* this ALPN id is already stored */
+ ;
+ else
+ alpns[idnum++] = (unsigned char)id;
+ }
+ cp += tlen;
+ len -= tlen;
+ }
+ if(idnum < MAX_HTTPSRR_ALPNS)
+ alpns[idnum] = ALPN_none; /* terminate the list */
+ return CURLE_OK;
+}
+
+CURLcode Curl_httpsrr_set(struct Curl_easy *data,
+ struct Curl_https_rrinfo *hi,
+ uint16_t rrkey, const uint8_t *val, size_t vlen)
+{
+ CURLcode result = CURLE_OK;
+ switch(rrkey) {
+ case HTTPS_RR_CODE_MANDATORY:
+ CURL_TRC_DNS(data, "HTTPS RR MANDATORY left to implement");
+ break;
+ case HTTPS_RR_CODE_ALPN: /* str_list */
+ result = httpsrr_decode_alpn((const char *)val, vlen, hi->alpns);
+ CURL_TRC_DNS(data, "HTTPS RR ALPN: %u %u %u %u",
+ hi->alpns[0], hi->alpns[1], hi->alpns[2], hi->alpns[3]);
+ break;
+ case HTTPS_RR_CODE_NO_DEF_ALPN:
+ if(vlen) /* no data */
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ hi->no_def_alpn = TRUE;
+ CURL_TRC_DNS(data, "HTTPS RR no-def-alpn");
+ break;
+ case HTTPS_RR_CODE_IPV4: /* addr4 list */
+ if(!vlen || (vlen & 3)) /* the size must be 4-byte aligned */
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ hi->ipv4hints = Curl_memdup(val, vlen);
+ if(!hi->ipv4hints)
+ return CURLE_OUT_OF_MEMORY;
+ hi->ipv4hints_len = vlen;
+ CURL_TRC_DNS(data, "HTTPS RR IPv4");
+ break;
+ case HTTPS_RR_CODE_ECH:
+ if(!vlen)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ hi->echconfiglist = Curl_memdup(val, vlen);
+ if(!hi->echconfiglist)
+ return CURLE_OUT_OF_MEMORY;
+ hi->echconfiglist_len = vlen;
+ CURL_TRC_DNS(data, "HTTPS RR ECH");
+ break;
+ case HTTPS_RR_CODE_IPV6: /* addr6 list */
+ if(!vlen || (vlen & 15)) /* the size must be 16-byte aligned */
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ hi->ipv6hints = Curl_memdup(val, vlen);
+ if(!hi->ipv6hints)
+ return CURLE_OUT_OF_MEMORY;
+ hi->ipv6hints_len = vlen;
+ CURL_TRC_DNS(data, "HTTPS RR IPv6");
+ break;
+ case HTTPS_RR_CODE_PORT:
+ if(vlen != 2)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ hi->port = (unsigned short)((val[0] << 8) | val[1]);
+ CURL_TRC_DNS(data, "HTTPS RR port %u", hi->port);
+ break;
+ default:
+ CURL_TRC_DNS(data, "HTTPS RR unknown code");
+ break;
+ }
+ return result;
+}
+
+struct Curl_https_rrinfo *
+Curl_httpsrr_dup_move(struct Curl_https_rrinfo *rrinfo)
+{
+ struct Curl_https_rrinfo *dup = Curl_memdup(rrinfo, sizeof(*rrinfo));
+ if(dup)
+ memset(rrinfo, 0, sizeof(*rrinfo));
+ return dup;
+}
+
+void Curl_httpsrr_cleanup(struct Curl_https_rrinfo *rrinfo)
+{
+ Curl_safefree(rrinfo->target);
+ Curl_safefree(rrinfo->echconfiglist);
+ Curl_safefree(rrinfo->ipv4hints);
+ Curl_safefree(rrinfo->ipv6hints);
+}
+
+
+#ifdef USE_ARES
+
+static CURLcode httpsrr_opt(struct Curl_easy *data,
+ const ares_dns_rr_t *rr,
+ ares_dns_rr_key_t key, size_t idx,
+ struct Curl_https_rrinfo *hinfo)
+{
+ const unsigned char *val = NULL;
+ unsigned short code;
+ size_t len = 0;
+
+ code = ares_dns_rr_get_opt(rr, key, idx, &val, &len);
+ return Curl_httpsrr_set(data, hinfo, code, val, len);
+}
+
+CURLcode Curl_httpsrr_from_ares(struct Curl_easy *data,
+ const ares_dns_record_t *dnsrec,
+ struct Curl_https_rrinfo *hinfo)
+{
+ CURLcode result = CURLE_OK;
+ size_t i;
+
+ for(i = 0; i < ares_dns_record_rr_cnt(dnsrec, ARES_SECTION_ANSWER); i++) {
+ const char *target;
+ size_t opt;
+ const ares_dns_rr_t *rr =
+ ares_dns_record_rr_get_const(dnsrec, ARES_SECTION_ANSWER, i);
+ if(ares_dns_rr_get_type(rr) != ARES_REC_TYPE_HTTPS)
+ continue;
+ /* When SvcPriority is 0, the SVCB record is in AliasMode. Otherwise, it
+ is in ServiceMode */
+ target = ares_dns_rr_get_str(rr, ARES_RR_HTTPS_TARGET);
+ if(target && target[0]) {
+ hinfo->target = strdup(target);
+ if(!hinfo->target) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ CURL_TRC_DNS(data, "HTTPS RR target: %s", hinfo->target);
+ }
+ CURL_TRC_DNS(data, "HTTPS RR priority: %u",
+ ares_dns_rr_get_u16(rr, ARES_RR_HTTPS_PRIORITY));
+ for(opt = 0; opt < ares_dns_rr_get_opt_cnt(rr, ARES_RR_HTTPS_PARAMS);
+ opt++) {
+ result = httpsrr_opt(data, rr, ARES_RR_HTTPS_PARAMS, opt, hinfo);
+ if(result)
+ break;
+ }
+ }
+out:
+ return result;
+}
+
+#endif /* USE_ARES */
+
+#endif /* USE_HTTPSRR */
diff --git a/libs/libcurl/src/httpsrr.h b/libs/libcurl/src/httpsrr.h
new file mode 100644
index 0000000000..fdcfdacf07
--- /dev/null
+++ b/libs/libcurl/src/httpsrr.h
@@ -0,0 +1,86 @@
+#ifndef HEADER_CURL_HTTPSRR_H
+#define HEADER_CURL_HTTPSRR_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#ifdef USE_ARES
+#include <ares.h>
+#endif
+
+#ifdef USE_HTTPSRR
+
+#define CURL_MAXLEN_host_name 253
+#define MAX_HTTPSRR_ALPNS 4
+
+struct Curl_easy;
+
+struct Curl_https_rrinfo {
+ /*
+ * Fields from HTTPS RR. The only mandatory fields are priority and target.
+ * See https://datatracker.ietf.org/doc/html/rfc9460#section-14.3.2
+ */
+ char *target;
+ unsigned char *ipv4hints; /* keytag = 4 */
+ size_t ipv4hints_len;
+ unsigned char *echconfiglist; /* keytag = 5 */
+ size_t echconfiglist_len;
+ unsigned char *ipv6hints; /* keytag = 6 */
+ size_t ipv6hints_len;
+ unsigned char alpns[MAX_HTTPSRR_ALPNS]; /* keytag = 1 */
+ /* store parsed alpnid entries in the array, end with ALPN_none */
+ int port; /* -1 means not set */
+ uint16_t priority;
+ BIT(no_def_alpn); /* keytag = 2 */
+};
+
+CURLcode Curl_httpsrr_set(struct Curl_easy *data,
+ struct Curl_https_rrinfo *hi,
+ uint16_t rrkey, const uint8_t *val, size_t vlen);
+
+struct Curl_https_rrinfo *
+Curl_httpsrr_dup_move(struct Curl_https_rrinfo *rrinfo);
+
+void Curl_httpsrr_cleanup(struct Curl_https_rrinfo *rrinfo);
+
+/*
+ * Code points for DNS wire format SvcParams as per RFC 9460
+ */
+#define HTTPS_RR_CODE_MANDATORY 0x00
+#define HTTPS_RR_CODE_ALPN 0x01
+#define HTTPS_RR_CODE_NO_DEF_ALPN 0x02
+#define HTTPS_RR_CODE_PORT 0x03
+#define HTTPS_RR_CODE_IPV4 0x04
+#define HTTPS_RR_CODE_ECH 0x05
+#define HTTPS_RR_CODE_IPV6 0x06
+
+#if defined(USE_ARES)
+CURLcode Curl_httpsrr_from_ares(struct Curl_easy *data,
+ const ares_dns_record_t *dnsrec,
+ struct Curl_https_rrinfo *hinfo);
+#endif /* USE_ARES */
+#endif /* USE_HTTPSRR */
+
+#endif /* HEADER_CURL_HTTPSRR_H */
diff --git a/libs/libcurl/src/idn.c b/libs/libcurl/src/idn.c
index f767906e1d..9708745f99 100644
--- a/libs/libcurl/src/idn.c
+++ b/libs/libcurl/src/idn.c
@@ -30,8 +30,8 @@
#include "urldata.h"
#include "idn.h"
#include "sendf.h"
-#include "curl_multibyte.h"
-#include "warnless.h"
+#include "curlx/multibyte.h"
+#include "curlx/warnless.h"
#ifdef USE_LIBIDN2
#include <idn2.h>
@@ -64,13 +64,14 @@ static CURLcode iconv_to_utf8(const char *in, size_t inlen,
iconv_t cd = iconv_open("UTF-8", nl_langinfo(CODESET));
if(cd != (iconv_t)-1) {
size_t iconv_outlen = *outlen;
- char *iconv_in = (char *)in;
+ char *iconv_in = (char *)CURL_UNCONST(in);
size_t iconv_inlen = inlen;
size_t iconv_result = iconv(cd, &iconv_in, &iconv_inlen,
out, &iconv_outlen);
*outlen -= iconv_outlen;
iconv_close(cd);
if(iconv_result == (size_t)-1) {
+ /* !checksrc! disable ERRNOVAR 1 */
if(errno == ENOMEM)
return CURLE_OUT_OF_MEMORY;
else
@@ -80,6 +81,7 @@ static CURLcode iconv_to_utf8(const char *in, size_t inlen,
return CURLE_OK;
}
else {
+ /* !checksrc! disable ERRNOVAR 1 */
if(errno == ENOMEM)
return CURLE_OUT_OF_MEMORY;
else
diff --git a/libs/libcurl/src/if2ip.c b/libs/libcurl/src/if2ip.c
index a3f07befce..2a4444d3e5 100644
--- a/libs/libcurl/src/if2ip.c
+++ b/libs/libcurl/src/if2ip.c
@@ -67,7 +67,8 @@
unsigned int Curl_ipv6_scope(const struct sockaddr *sa)
{
if(sa->sa_family == AF_INET6) {
- const struct sockaddr_in6 * sa6 = (const struct sockaddr_in6 *)(void *) sa;
+ const struct sockaddr_in6 * sa6 =
+ (const struct sockaddr_in6 *)(const void *) sa;
const unsigned char *b = sa6->sin6_addr.s6_addr;
unsigned short w = (unsigned short) ((b[0] << 8) | b[1]);
@@ -92,7 +93,7 @@ unsigned int Curl_ipv6_scope(const struct sockaddr *sa)
}
#endif
-#ifndef CURL_DISABLE_BINDLOCAL
+#if !defined(CURL_DISABLE_BINDLOCAL) || !defined(CURL_DISABLE_FTP)
#if defined(HAVE_GETIFADDRS)
@@ -265,4 +266,4 @@ if2ip_result_t Curl_if2ip(int af,
#endif
-#endif /* CURL_DISABLE_BINDLOCAL */
+#endif /* CURL_DISABLE_BINDLOCAL && CURL_DISABLE_FTP */
diff --git a/libs/libcurl/src/imap.c b/libs/libcurl/src/imap.c
index c36a1a1974..81f0db12a4 100644
--- a/libs/libcurl/src/imap.c
+++ b/libs/libcurl/src/imap.c
@@ -36,6 +36,7 @@
***************************************************************************/
#include "curl_setup.h"
+#include "curlx/dynbuf.h"
#ifndef CURL_DISABLE_IMAP
@@ -64,7 +65,7 @@
#include "socks.h"
#include "imap.h"
#include "mime.h"
-#include "strtoofft.h"
+#include "curlx/strparse.h"
#include "strcase.h"
#include "vtls/vtls.h"
#include "cfilters.h"
@@ -74,7 +75,7 @@
#include "url.h"
#include "bufref.h"
#include "curl_sasl.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
#include "curl_ctype.h"
/* The last 3 #include files should be in this order */
@@ -82,8 +83,73 @@
#include "curl_memory.h"
#include "memdebug.h"
+
+/* meta key for storing protocol meta at easy handle */
+#define CURL_META_IMAP_EASY "meta:proto:imap:easy"
+/* meta key for storing protocol meta at connection */
+#define CURL_META_IMAP_CONN "meta:proto:imap:conn"
+
+typedef enum {
+ IMAP_STOP, /* do nothing state, stops the state machine */
+ IMAP_SERVERGREET, /* waiting for the initial greeting immediately after
+ a connect */
+ IMAP_CAPABILITY,
+ IMAP_STARTTLS,
+ IMAP_UPGRADETLS, /* asynchronously upgrade the connection to SSL/TLS
+ (multi mode only) */
+ IMAP_AUTHENTICATE,
+ IMAP_LOGIN,
+ IMAP_LIST,
+ IMAP_SELECT,
+ IMAP_FETCH,
+ IMAP_FETCH_FINAL,
+ IMAP_APPEND,
+ IMAP_APPEND_FINAL,
+ IMAP_SEARCH,
+ IMAP_LOGOUT,
+ IMAP_LAST /* never used */
+} imapstate;
+
+/* imap_conn is used for struct connection-oriented data */
+struct imap_conn {
+ struct pingpong pp;
+ struct SASL sasl; /* SASL-related parameters */
+ struct dynbuf dyn; /* for the IMAP commands */
+ char *mailbox; /* The last selected mailbox */
+ char *mailbox_uidvalidity; /* UIDVALIDITY parsed from select response */
+ imapstate state; /* Always use imap.c:state() to change state! */
+ char resptag[5]; /* Response tag to wait for */
+ unsigned char preftype; /* Preferred authentication type */
+ unsigned char cmdid; /* Last used command ID */
+ BIT(ssldone); /* Is connect() over SSL done? */
+ BIT(preauth); /* Is this connection PREAUTH? */
+ BIT(tls_supported); /* StartTLS capability supported by server */
+ BIT(login_disabled); /* LOGIN command disabled by server */
+ BIT(ir_supported); /* Initial response supported by server */
+};
+
+/* This IMAP struct is used in the Curl_easy. All IMAP data that is
+ connection-oriented must be in imap_conn to properly deal with the fact that
+ perhaps the Curl_easy is changed between the times the connection is
+ used. */
+struct IMAP {
+ curl_pp_transfer transfer;
+ char *mailbox; /* Mailbox to select */
+ char *uidvalidity; /* UIDVALIDITY to check in select */
+ char *uid; /* Message UID to fetch */
+ char *mindex; /* Index in mail box of mail to fetch */
+ char *section; /* Message SECTION to fetch */
+ char *partial; /* Message PARTIAL to fetch */
+ char *query; /* Query to search for */
+ char *custom; /* Custom request */
+ char *custom_params; /* Parameters for the custom request */
+};
+
+
/* Local API functions */
-static CURLcode imap_regular_transfer(struct Curl_easy *data, bool *done);
+static CURLcode imap_regular_transfer(struct Curl_easy *data,
+ struct IMAP *imap,
+ bool *done);
static CURLcode imap_do(struct Curl_easy *data, bool *done);
static CURLcode imap_done(struct Curl_easy *data, CURLcode status,
bool premature);
@@ -97,11 +163,15 @@ static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done);
static CURLcode imap_setup_connection(struct Curl_easy *data,
struct connectdata *conn);
static char *imap_atom(const char *str, bool escape_only);
-static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...)
- CURL_PRINTF(2, 3);
-static CURLcode imap_parse_url_options(struct connectdata *conn);
-static CURLcode imap_parse_url_path(struct Curl_easy *data);
-static CURLcode imap_parse_custom_request(struct Curl_easy *data);
+static CURLcode imap_sendf(struct Curl_easy *data,
+ struct imap_conn *imapc,
+ const char *fmt, ...) CURL_PRINTF(3, 4);
+static CURLcode imap_parse_url_options(struct connectdata *conn,
+ struct imap_conn *imapc);
+static CURLcode imap_parse_url_path(struct Curl_easy *data,
+ struct IMAP *imap);
+static CURLcode imap_parse_custom_request(struct Curl_easy *data,
+ struct IMAP *imap);
static CURLcode imap_perform_authenticate(struct Curl_easy *data,
const char *mech,
const struct bufref *initresp);
@@ -111,6 +181,7 @@ static CURLcode imap_continue_authenticate(struct Curl_easy *data,
static CURLcode imap_cancel_authenticate(struct Curl_easy *data,
const char *mech);
static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out);
+static void imap_easy_reset(struct IMAP *imap);
/*
* IMAP protocol handler.
@@ -134,6 +205,7 @@ const struct Curl_handler Curl_handler_imap = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_IMAP, /* defport */
CURLPROTO_IMAP, /* protocol */
CURLPROTO_IMAP, /* family */
@@ -164,6 +236,7 @@ const struct Curl_handler Curl_handler_imaps = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_IMAPS, /* defport */
CURLPROTO_IMAPS, /* protocol */
CURLPROTO_IMAP, /* family */
@@ -190,19 +263,10 @@ static const struct SASLproto saslimap = {
SASL_FLAG_BASE64 /* Configuration flags */
};
-
-#ifdef USE_SSL
-static void imap_to_imaps(struct connectdata *conn)
-{
- /* Change the connection handler */
- conn->handler = &Curl_handler_imaps;
-
- /* Set the connection's upgraded to TLS flag */
- conn->bits.tls_upgraded = TRUE;
-}
-#else
-#define imap_to_imaps(x) Curl_nop_stmt
-#endif
+struct ulbits {
+ int bit;
+ const char *flag;
+};
/***********************************************************************
*
@@ -253,14 +317,21 @@ static bool imap_matchresp(const char *line, size_t len, const char *cmd)
* response which can be processed by the response handler.
*/
static bool imap_endofresp(struct Curl_easy *data, struct connectdata *conn,
- char *line, size_t len, int *resp)
+ const char *line, size_t len, int *resp)
{
- struct IMAP *imap = data->req.p.imap;
- struct imap_conn *imapc = &conn->proto.imapc;
- const char *id = imapc->resptag;
- size_t id_len = strlen(id);
+ struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN);
+ struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY);
+ const char *id;
+ size_t id_len;
+
+ DEBUGASSERT(imapc);
+ DEBUGASSERT(imap);
+ if(!imapc || !imap)
+ return FALSE;
/* Do we have a tagged command response? */
+ id = imapc->resptag;
+ id_len = strlen(id);
if(len >= id_len + 1 && !memcmp(id, line, id_len) && line[id_len] == ' ') {
line += id_len + 1;
len -= id_len + 1;
@@ -286,17 +357,17 @@ static bool imap_endofresp(struct Curl_easy *data, struct connectdata *conn,
case IMAP_LIST:
if((!imap->custom && !imap_matchresp(line, len, "LIST")) ||
- (imap->custom && !imap_matchresp(line, len, imap->custom) &&
- (!strcasecompare(imap->custom, "STORE") ||
- !imap_matchresp(line, len, "FETCH")) &&
- !strcasecompare(imap->custom, "SELECT") &&
- !strcasecompare(imap->custom, "EXAMINE") &&
- !strcasecompare(imap->custom, "SEARCH") &&
- !strcasecompare(imap->custom, "EXPUNGE") &&
- !strcasecompare(imap->custom, "LSUB") &&
- !strcasecompare(imap->custom, "UID") &&
- !strcasecompare(imap->custom, "GETQUOTAROOT") &&
- !strcasecompare(imap->custom, "NOOP")))
+ (imap->custom && !imap_matchresp(line, len, imap->custom) &&
+ (!strcasecompare(imap->custom, "STORE") ||
+ !imap_matchresp(line, len, "FETCH")) &&
+ !strcasecompare(imap->custom, "SELECT") &&
+ !strcasecompare(imap->custom, "EXAMINE") &&
+ !strcasecompare(imap->custom, "SEARCH") &&
+ !strcasecompare(imap->custom, "EXPUNGE") &&
+ !strcasecompare(imap->custom, "LSUB") &&
+ !strcasecompare(imap->custom, "UID") &&
+ !strcasecompare(imap->custom, "GETQUOTAROOT") &&
+ !strcasecompare(imap->custom, "NOOP")))
return FALSE;
break;
@@ -328,8 +399,8 @@ static bool imap_endofresp(struct Curl_easy *data, struct connectdata *conn,
a space and optionally some text as per RFC-3501 for the AUTHENTICATE and
APPEND commands and as outlined in Section 4. Examples of RFC-4959 but
some email servers ignore this and only send a single + instead. */
- if(imap && !imap->custom && ((len == 3 && line[0] == '+') ||
- (len >= 2 && !memcmp("+ ", line, 2)))) {
+ if(!imap->custom && ((len == 3 && line[0] == '+') ||
+ (len >= 2 && !memcmp("+ ", line, 2)))) {
switch(imapc->state) {
/* States which are interested in continuation responses */
case IMAP_AUTHENTICATE:
@@ -357,9 +428,16 @@ static bool imap_endofresp(struct Curl_easy *data, struct connectdata *conn,
*/
static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out)
{
- char *message = Curl_dyn_ptr(&data->conn->proto.imapc.pp.recvbuf);
- size_t len = data->conn->proto.imapc.pp.nfinal;
+ struct imap_conn *imapc =
+ Curl_conn_meta_get(data->conn, CURL_META_IMAP_CONN);
+ char *message;
+ size_t len;
+
+ if(!imapc)
+ return CURLE_FAILED_INIT;
+ message = curlx_dyn_ptr(&imapc->pp.recvbuf);
+ len = imapc->pp.nfinal;
if(len > 2) {
/* Find the start of the message */
len -= 2;
@@ -389,9 +467,10 @@ static CURLcode imap_get_message(struct Curl_easy *data, struct bufref *out)
*
* This is the ONLY way to change IMAP state!
*/
-static void imap_state(struct Curl_easy *data, imapstate newstate)
+static void imap_state(struct Curl_easy *data,
+ struct imap_conn *imapc,
+ imapstate newstate)
{
- struct imap_conn *imapc = &data->conn->proto.imapc;
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
/* for debug purposes */
static const char * const names[]={
@@ -417,7 +496,7 @@ static void imap_state(struct Curl_easy *data, imapstate newstate)
infof(data, "IMAP %p state change from %s to %s",
(void *)imapc, names[imapc->state], names[newstate]);
#endif
-
+ (void)data;
imapc->state = newstate;
}
@@ -429,19 +508,19 @@ static void imap_state(struct Curl_easy *data, imapstate newstate)
* supported capabilities.
*/
static CURLcode imap_perform_capability(struct Curl_easy *data,
- struct connectdata *conn)
+ struct imap_conn *imapc)
{
CURLcode result = CURLE_OK;
- struct imap_conn *imapc = &conn->proto.imapc;
+
imapc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */
imapc->sasl.authused = SASL_AUTH_NONE; /* Clear the auth. mechanism used */
imapc->tls_supported = FALSE; /* Clear the TLS capability */
/* Send the CAPABILITY command */
- result = imap_sendf(data, "CAPABILITY");
+ result = imap_sendf(data, imapc, "CAPABILITY");
if(!result)
- imap_state(data, IMAP_CAPABILITY);
+ imap_state(data, imapc, IMAP_CAPABILITY);
return result;
}
@@ -452,13 +531,14 @@ static CURLcode imap_perform_capability(struct Curl_easy *data,
*
* Sends the STARTTLS command to start the upgrade to TLS.
*/
-static CURLcode imap_perform_starttls(struct Curl_easy *data)
+static CURLcode imap_perform_starttls(struct Curl_easy *data,
+ struct imap_conn *imapc)
{
/* Send the STARTTLS command */
- CURLcode result = imap_sendf(data, "STARTTLS");
+ CURLcode result = imap_sendf(data, imapc, "STARTTLS");
if(!result)
- imap_state(data, IMAP_STARTTLS);
+ imap_state(data, imapc, IMAP_STARTTLS);
return result;
}
@@ -470,10 +550,11 @@ static CURLcode imap_perform_starttls(struct Curl_easy *data)
* Performs the upgrade to TLS.
*/
static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data,
+ struct imap_conn *imapc,
struct connectdata *conn)
{
+#ifdef USE_SSL
/* Start the SSL connection */
- struct imap_conn *imapc = &conn->proto.imapc;
CURLcode result;
bool ssldone = FALSE;
@@ -481,21 +562,27 @@ static CURLcode imap_perform_upgrade_tls(struct Curl_easy *data,
result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
if(result)
goto out;
+ /* Change the connection handler */
+ conn->handler = &Curl_handler_imaps;
}
+ DEBUGASSERT(!imapc->ssldone);
result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
- if(!result) {
+ DEBUGF(infof(data, "imap_perform_upgrade_tls, connect -> %d, %d",
+ result, ssldone));
+ if(!result && ssldone) {
imapc->ssldone = ssldone;
- if(imapc->state != IMAP_UPGRADETLS)
- imap_state(data, IMAP_UPGRADETLS);
-
- if(imapc->ssldone) {
- imap_to_imaps(conn);
- result = imap_perform_capability(data, conn);
- }
+ /* perform CAPA now, changes imapc->state out of IMAP_UPGRADETLS */
+ result = imap_perform_capability(data, imapc);
}
out:
return result;
+#else
+ (void)data;
+ (void)imapc;
+ (void)conn;
+ return CURLE_NOT_BUILT_IN;
+#endif
}
/***********************************************************************
@@ -505,6 +592,7 @@ out:
* Sends a clear text LOGIN command to authenticate with.
*/
static CURLcode imap_perform_login(struct Curl_easy *data,
+ struct imap_conn *imapc,
struct connectdata *conn)
{
CURLcode result = CURLE_OK;
@@ -514,7 +602,7 @@ static CURLcode imap_perform_login(struct Curl_easy *data,
/* Check we have a username and password to authenticate with and end the
connect phase if we do not */
if(!data->state.aptr.user) {
- imap_state(data, IMAP_STOP);
+ imap_state(data, imapc, IMAP_STOP);
return result;
}
@@ -524,14 +612,14 @@ static CURLcode imap_perform_login(struct Curl_easy *data,
passwd = imap_atom(conn->passwd, FALSE);
/* Send the LOGIN command */
- result = imap_sendf(data, "LOGIN %s %s", user ? user : "",
+ result = imap_sendf(data, imapc, "LOGIN %s %s", user ? user : "",
passwd ? passwd : "");
free(user);
free(passwd);
if(!result)
- imap_state(data, IMAP_LOGIN);
+ imap_state(data, imapc, IMAP_LOGIN);
return result;
}
@@ -547,16 +635,20 @@ static CURLcode imap_perform_authenticate(struct Curl_easy *data,
const char *mech,
const struct bufref *initresp)
{
+ struct imap_conn *imapc =
+ Curl_conn_meta_get(data->conn, CURL_META_IMAP_CONN);
CURLcode result = CURLE_OK;
const char *ir = (const char *) Curl_bufref_ptr(initresp);
+ if(!imapc)
+ return CURLE_FAILED_INIT;
if(ir) {
/* Send the AUTHENTICATE command with the initial response */
- result = imap_sendf(data, "AUTHENTICATE %s %s", mech, ir);
+ result = imap_sendf(data, imapc, "AUTHENTICATE %s %s", mech, ir);
}
else {
/* Send the AUTHENTICATE command */
- result = imap_sendf(data, "AUTHENTICATE %s", mech);
+ result = imap_sendf(data, imapc, "AUTHENTICATE %s", mech);
}
return result;
@@ -572,10 +664,12 @@ static CURLcode imap_continue_authenticate(struct Curl_easy *data,
const char *mech,
const struct bufref *resp)
{
- struct imap_conn *imapc = &data->conn->proto.imapc;
+ struct imap_conn *imapc =
+ Curl_conn_meta_get(data->conn, CURL_META_IMAP_CONN);
(void)mech;
-
+ if(!imapc)
+ return CURLE_FAILED_INIT;
return Curl_pp_sendf(data, &imapc->pp,
"%s", (const char *) Curl_bufref_ptr(resp));
}
@@ -589,10 +683,12 @@ static CURLcode imap_continue_authenticate(struct Curl_easy *data,
static CURLcode imap_cancel_authenticate(struct Curl_easy *data,
const char *mech)
{
- struct imap_conn *imapc = &data->conn->proto.imapc;
+ struct imap_conn *imapc =
+ Curl_conn_meta_get(data->conn, CURL_META_IMAP_CONN);
(void)mech;
-
+ if(!imapc)
+ return CURLE_FAILED_INIT;
return Curl_pp_sendf(data, &imapc->pp, "*");
}
@@ -605,17 +701,16 @@ static CURLcode imap_cancel_authenticate(struct Curl_easy *data,
* mechanism not be available between the client and server.
*/
static CURLcode imap_perform_authentication(struct Curl_easy *data,
- struct connectdata *conn)
+ struct imap_conn *imapc)
{
CURLcode result = CURLE_OK;
- struct imap_conn *imapc = &conn->proto.imapc;
saslprogress progress;
/* Check if already authenticated OR if there is enough data to authenticate
with and end the connect phase if we do not */
if(imapc->preauth ||
!Curl_sasl_can_authenticate(&imapc->sasl, data)) {
- imap_state(data, IMAP_STOP);
+ imap_state(data, imapc, IMAP_STOP);
return result;
}
@@ -624,15 +719,12 @@ static CURLcode imap_perform_authentication(struct Curl_easy *data,
if(!result) {
if(progress == SASL_INPROGRESS)
- imap_state(data, IMAP_AUTHENTICATE);
+ imap_state(data, imapc, IMAP_AUTHENTICATE);
else if(!imapc->login_disabled && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
/* Perform clear text authentication */
- result = imap_perform_login(data, conn);
- else {
- /* Other mechanisms not supported */
- infof(data, "No known authentication mechanisms supported");
- result = CURLE_LOGIN_DENIED;
- }
+ result = imap_perform_login(data, imapc, data->conn);
+ else
+ result = Curl_sasl_is_blocked(&imapc->sasl, data);
}
return result;
@@ -644,14 +736,15 @@ static CURLcode imap_perform_authentication(struct Curl_easy *data,
*
* Sends a LIST command or an alternative custom request.
*/
-static CURLcode imap_perform_list(struct Curl_easy *data)
+static CURLcode imap_perform_list(struct Curl_easy *data,
+ struct imap_conn *imapc,
+ struct IMAP *imap)
{
CURLcode result = CURLE_OK;
- struct IMAP *imap = data->req.p.imap;
if(imap->custom)
/* Send the custom request */
- result = imap_sendf(data, "%s%s", imap->custom,
+ result = imap_sendf(data, imapc, "%s%s", imap->custom,
imap->custom_params ? imap->custom_params : "");
else {
/* Make sure the mailbox is in the correct atom format if necessary */
@@ -661,13 +754,13 @@ static CURLcode imap_perform_list(struct Curl_easy *data)
return CURLE_OUT_OF_MEMORY;
/* Send the LIST command */
- result = imap_sendf(data, "LIST \"%s\" *", mailbox);
+ result = imap_sendf(data, imapc, "LIST \"%s\" *", mailbox);
free(mailbox);
}
if(!result)
- imap_state(data, IMAP_LIST);
+ imap_state(data, imapc, IMAP_LIST);
return result;
}
@@ -678,12 +771,11 @@ static CURLcode imap_perform_list(struct Curl_easy *data)
*
* Sends a SELECT command to ask the server to change the selected mailbox.
*/
-static CURLcode imap_perform_select(struct Curl_easy *data)
+static CURLcode imap_perform_select(struct Curl_easy *data,
+ struct imap_conn *imapc,
+ struct IMAP *imap)
{
CURLcode result = CURLE_OK;
- struct connectdata *conn = data->conn;
- struct IMAP *imap = data->req.p.imap;
- struct imap_conn *imapc = &conn->proto.imapc;
char *mailbox;
/* Invalidate old information as we are switching mailboxes */
@@ -702,12 +794,12 @@ static CURLcode imap_perform_select(struct Curl_easy *data)
return CURLE_OUT_OF_MEMORY;
/* Send the SELECT command */
- result = imap_sendf(data, "SELECT %s", mailbox);
+ result = imap_sendf(data, imapc, "SELECT %s", mailbox);
free(mailbox);
if(!result)
- imap_state(data, IMAP_SELECT);
+ imap_state(data, imapc, IMAP_SELECT);
return result;
}
@@ -718,30 +810,31 @@ static CURLcode imap_perform_select(struct Curl_easy *data)
*
* Sends a FETCH command to initiate the download of a message.
*/
-static CURLcode imap_perform_fetch(struct Curl_easy *data)
+static CURLcode imap_perform_fetch(struct Curl_easy *data,
+ struct imap_conn *imapc,
+ struct IMAP *imap)
{
CURLcode result = CURLE_OK;
- struct IMAP *imap = data->req.p.imap;
/* Check we have a UID */
if(imap->uid) {
/* Send the FETCH command */
if(imap->partial)
- result = imap_sendf(data, "UID FETCH %s BODY[%s]<%s>",
+ result = imap_sendf(data, imapc, "UID FETCH %s BODY[%s]<%s>",
imap->uid, imap->section ? imap->section : "",
imap->partial);
else
- result = imap_sendf(data, "UID FETCH %s BODY[%s]",
+ result = imap_sendf(data, imapc, "UID FETCH %s BODY[%s]",
imap->uid, imap->section ? imap->section : "");
}
else if(imap->mindex) {
/* Send the FETCH command */
if(imap->partial)
- result = imap_sendf(data, "FETCH %s BODY[%s]<%s>",
+ result = imap_sendf(data, imapc, "FETCH %s BODY[%s]<%s>",
imap->mindex, imap->section ? imap->section : "",
imap->partial);
else
- result = imap_sendf(data, "FETCH %s BODY[%s]",
+ result = imap_sendf(data, imapc, "FETCH %s BODY[%s]",
imap->mindex, imap->section ? imap->section : "");
}
else {
@@ -749,7 +842,7 @@ static CURLcode imap_perform_fetch(struct Curl_easy *data)
return CURLE_URL_MALFORMAT;
}
if(!result)
- imap_state(data, IMAP_FETCH);
+ imap_state(data, imapc, IMAP_FETCH);
return result;
}
@@ -760,11 +853,13 @@ static CURLcode imap_perform_fetch(struct Curl_easy *data)
*
* Sends an APPEND command to initiate the upload of a message.
*/
-static CURLcode imap_perform_append(struct Curl_easy *data)
+static CURLcode imap_perform_append(struct Curl_easy *data,
+ struct imap_conn *imapc,
+ struct IMAP *imap)
{
CURLcode result = CURLE_OK;
- struct IMAP *imap = data->req.p.imap;
char *mailbox;
+ struct dynbuf flags;
/* Check we have a mailbox */
if(!imap->mailbox) {
@@ -813,14 +908,48 @@ static CURLcode imap_perform_append(struct Curl_easy *data)
if(!mailbox)
return CURLE_OUT_OF_MEMORY;
- /* Send the APPEND command */
- result = imap_sendf(data, "APPEND %s (\\Seen) {%" FMT_OFF_T "}",
- mailbox, data->state.infilesize);
+ /* Generate flags string and send the APPEND command */
+ curlx_dyn_init(&flags, 100);
+ if(data->set.upload_flags) {
+ int i;
+ struct ulbits ulflag[] = {
+ {CURLULFLAG_ANSWERED, "Answered"},
+ {CURLULFLAG_DELETED, "Deleted"},
+ {CURLULFLAG_DRAFT, "Draft"},
+ {CURLULFLAG_FLAGGED, "Flagged"},
+ {CURLULFLAG_SEEN, "Seen"},
+ {0, NULL}
+ };
+
+ result = CURLE_OUT_OF_MEMORY;
+ if(curlx_dyn_add(&flags, " (")) {
+ goto cleanup;
+ }
+
+ for(i = 0; ulflag[i].bit; i++) {
+ if(data->set.upload_flags & ulflag[i].bit) {
+ if((curlx_dyn_len(&flags) > 2 && curlx_dyn_add(&flags, " ")) ||
+ curlx_dyn_add(&flags, "\\") ||
+ curlx_dyn_add(&flags, ulflag[i].flag))
+ goto cleanup;
+ }
+ }
+ if(curlx_dyn_add(&flags, ")"))
+ goto cleanup;
+ }
+ else if(curlx_dyn_add(&flags, ""))
+ goto cleanup;
+
+ result = imap_sendf(data, imapc, "APPEND %s%s {%" FMT_OFF_T "}",
+ mailbox, curlx_dyn_ptr(&flags), data->state.infilesize);
+
+cleanup:
+ curlx_dyn_free(&flags);
free(mailbox);
if(!result)
- imap_state(data, IMAP_APPEND);
+ imap_state(data, imapc, IMAP_APPEND);
return result;
}
@@ -831,10 +960,11 @@ static CURLcode imap_perform_append(struct Curl_easy *data)
*
* Sends a SEARCH command.
*/
-static CURLcode imap_perform_search(struct Curl_easy *data)
+static CURLcode imap_perform_search(struct Curl_easy *data,
+ struct imap_conn *imapc,
+ struct IMAP *imap)
{
CURLcode result = CURLE_OK;
- struct IMAP *imap = data->req.p.imap;
/* Check we have a query string */
if(!imap->query) {
@@ -843,10 +973,10 @@ static CURLcode imap_perform_search(struct Curl_easy *data)
}
/* Send the SEARCH command */
- result = imap_sendf(data, "SEARCH %s", imap->query);
+ result = imap_sendf(data, imapc, "SEARCH %s", imap->query);
if(!result)
- imap_state(data, IMAP_SEARCH);
+ imap_state(data, imapc, IMAP_SEARCH);
return result;
}
@@ -857,28 +987,28 @@ static CURLcode imap_perform_search(struct Curl_easy *data)
*
* Performs the logout action prior to sclose() being called.
*/
-static CURLcode imap_perform_logout(struct Curl_easy *data)
+static CURLcode imap_perform_logout(struct Curl_easy *data,
+ struct imap_conn *imapc)
{
/* Send the LOGOUT command */
- CURLcode result = imap_sendf(data, "LOGOUT");
+ CURLcode result = imap_sendf(data, imapc, "LOGOUT");
if(!result)
- imap_state(data, IMAP_LOGOUT);
+ imap_state(data, imapc, IMAP_LOGOUT);
return result;
}
/* For the initial server greeting */
static CURLcode imap_state_servergreet_resp(struct Curl_easy *data,
+ struct imap_conn *imapc,
int imapcode,
imapstate instate)
{
- struct connectdata *conn = data->conn;
(void)instate; /* no use for this yet */
if(imapcode == IMAP_RESP_PREAUTH) {
/* PREAUTH */
- struct imap_conn *imapc = &conn->proto.imapc;
imapc->preauth = TRUE;
infof(data, "PREAUTH connection, already authenticated");
}
@@ -887,22 +1017,22 @@ static CURLcode imap_state_servergreet_resp(struct Curl_easy *data,
return CURLE_WEIRD_SERVER_REPLY;
}
- return imap_perform_capability(data, conn);
+ return imap_perform_capability(data, imapc);
}
/* For CAPABILITY responses */
static CURLcode imap_state_capability_resp(struct Curl_easy *data,
+ struct imap_conn *imapc,
int imapcode,
imapstate instate)
{
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
- struct imap_conn *imapc = &conn->proto.imapc;
- const char *line = Curl_dyn_ptr(&imapc->pp.recvbuf);
+ const char *line = curlx_dyn_ptr(&imapc->pp.recvbuf);
(void)instate; /* no use for this yet */
- /* Do we have a untagged response? */
+ /* Do we have an untagged response? */
if(imapcode == '*') {
line += 2;
@@ -958,33 +1088,33 @@ static CURLcode imap_state_capability_resp(struct Curl_easy *data,
/* PREAUTH is not compatible with STARTTLS. */
if(imapcode == IMAP_RESP_OK && imapc->tls_supported && !imapc->preauth) {
/* Switch to TLS connection now */
- result = imap_perform_starttls(data);
+ result = imap_perform_starttls(data, imapc);
}
else if(data->set.use_ssl <= CURLUSESSL_TRY)
- result = imap_perform_authentication(data, conn);
+ result = imap_perform_authentication(data, imapc);
else {
failf(data, "STARTTLS not available.");
result = CURLE_USE_SSL_FAILED;
}
}
else
- result = imap_perform_authentication(data, conn);
+ result = imap_perform_authentication(data, imapc);
return result;
}
/* For STARTTLS responses */
static CURLcode imap_state_starttls_resp(struct Curl_easy *data,
+ struct imap_conn *imapc,
int imapcode,
imapstate instate)
{
CURLcode result = CURLE_OK;
- struct connectdata *conn = data->conn;
(void)instate; /* no use for this yet */
/* Pipelining in response is forbidden. */
- if(data->conn->proto.imapc.pp.overflow)
+ if(imapc->pp.overflow)
return CURLE_WEIRD_SERVER_REPLY;
if(imapcode != IMAP_RESP_OK) {
@@ -993,22 +1123,21 @@ static CURLcode imap_state_starttls_resp(struct Curl_easy *data,
result = CURLE_USE_SSL_FAILED;
}
else
- result = imap_perform_authentication(data, conn);
+ result = imap_perform_authentication(data, imapc);
}
else
- result = imap_perform_upgrade_tls(data, conn);
+ imap_state(data, imapc, IMAP_UPGRADETLS);
return result;
}
/* For SASL authentication responses */
static CURLcode imap_state_auth_resp(struct Curl_easy *data,
- struct connectdata *conn,
+ struct imap_conn *imapc,
int imapcode,
imapstate instate)
{
CURLcode result = CURLE_OK;
- struct imap_conn *imapc = &conn->proto.imapc;
saslprogress progress;
(void)instate; /* no use for this yet */
@@ -1017,12 +1146,12 @@ static CURLcode imap_state_auth_resp(struct Curl_easy *data,
if(!result)
switch(progress) {
case SASL_DONE:
- imap_state(data, IMAP_STOP); /* Authenticated */
+ imap_state(data, imapc, IMAP_STOP); /* Authenticated */
break;
case SASL_IDLE: /* No mechanism left after cancellation */
if((!imapc->login_disabled) && (imapc->preftype & IMAP_TYPE_CLEARTEXT))
/* Perform clear text authentication */
- result = imap_perform_login(data, conn);
+ result = imap_perform_login(data, imapc, data->conn);
else {
failf(data, "Authentication cancelled");
result = CURLE_LOGIN_DENIED;
@@ -1037,6 +1166,7 @@ static CURLcode imap_state_auth_resp(struct Curl_easy *data,
/* For LOGIN responses */
static CURLcode imap_state_login_resp(struct Curl_easy *data,
+ struct imap_conn *imapc,
int imapcode,
imapstate instate)
{
@@ -1049,19 +1179,20 @@ static CURLcode imap_state_login_resp(struct Curl_easy *data,
}
else
/* End of connect phase */
- imap_state(data, IMAP_STOP);
+ imap_state(data, imapc, IMAP_STOP);
return result;
}
/* For LIST and SEARCH responses */
static CURLcode imap_state_listsearch_resp(struct Curl_easy *data,
+ struct imap_conn *imapc,
int imapcode,
imapstate instate)
{
CURLcode result = CURLE_OK;
- char *line = Curl_dyn_ptr(&data->conn->proto.imapc.pp.recvbuf);
- size_t len = data->conn->proto.imapc.pp.nfinal;
+ char *line = curlx_dyn_ptr(&imapc->pp.recvbuf);
+ size_t len = imapc->pp.nfinal;
(void)instate; /* No use for this yet */
@@ -1071,20 +1202,20 @@ static CURLcode imap_state_listsearch_resp(struct Curl_easy *data,
result = CURLE_QUOTE_ERROR;
else
/* End of DO phase */
- imap_state(data, IMAP_STOP);
+ imap_state(data, imapc, IMAP_STOP);
return result;
}
/* For SELECT responses */
-static CURLcode imap_state_select_resp(struct Curl_easy *data, int imapcode,
+static CURLcode imap_state_select_resp(struct Curl_easy *data,
+ struct imap_conn *imapc,
+ struct IMAP *imap,
+ int imapcode,
imapstate instate)
{
CURLcode result = CURLE_OK;
- struct connectdata *conn = data->conn;
- struct IMAP *imap = data->req.p.imap;
- struct imap_conn *imapc = &conn->proto.imapc;
- const char *line = Curl_dyn_ptr(&data->conn->proto.imapc.pp.recvbuf);
+ const char *line = curlx_dyn_ptr(&imapc->pp.recvbuf);
(void)instate; /* no use for this yet */
@@ -1097,11 +1228,11 @@ static CURLcode imap_state_select_resp(struct Curl_easy *data, int imapcode,
len++;
if(len && (p[len] == ']')) {
struct dynbuf uid;
- Curl_dyn_init(&uid, 20);
- if(Curl_dyn_addn(&uid, p, len))
+ curlx_dyn_init(&uid, 20);
+ if(curlx_dyn_addn(&uid, p, len))
return CURLE_OUT_OF_MEMORY;
- Curl_safefree(imapc->mailbox_uidvalidity);
- imapc->mailbox_uidvalidity = Curl_dyn_ptr(&uid);
+ free(imapc->mailbox_uidvalidity);
+ imapc->mailbox_uidvalidity = curlx_dyn_ptr(&uid);
}
}
}
@@ -1120,11 +1251,11 @@ static CURLcode imap_state_select_resp(struct Curl_easy *data, int imapcode,
return CURLE_OUT_OF_MEMORY;
if(imap->custom)
- result = imap_perform_list(data);
+ result = imap_perform_list(data, imapc, imap);
else if(imap->query)
- result = imap_perform_search(data);
+ result = imap_perform_search(data, imapc, imap);
else
- result = imap_perform_fetch(data);
+ result = imap_perform_fetch(data, imapc, imap);
}
}
else {
@@ -1137,14 +1268,14 @@ static CURLcode imap_state_select_resp(struct Curl_easy *data, int imapcode,
/* For the (first line of the) FETCH responses */
static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
- struct connectdata *conn, int imapcode,
+ struct imap_conn *imapc,
+ int imapcode,
imapstate instate)
{
CURLcode result = CURLE_OK;
- struct imap_conn *imapc = &conn->proto.imapc;
struct pingpong *pp = &imapc->pp;
- const char *ptr = Curl_dyn_ptr(&data->conn->proto.imapc.pp.recvbuf);
- size_t len = data->conn->proto.imapc.pp.nfinal;
+ const char *ptr = curlx_dyn_ptr(&imapc->pp.recvbuf);
+ size_t len = imapc->pp.nfinal;
bool parsed = FALSE;
curl_off_t size = 0;
@@ -1152,7 +1283,7 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
if(imapcode != '*') {
Curl_pgrsSetDownloadSize(data, -1);
- imap_state(data, IMAP_STOP);
+ imap_state(data, imapc, IMAP_STOP);
return CURLE_REMOTE_FILE_NOT_FOUND;
}
@@ -1160,9 +1291,9 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
the continuation data contained within the curly brackets */
ptr = memchr(ptr, '{', len);
if(ptr) {
- char *endptr;
- if(!curlx_strtoofft(ptr + 1, &endptr, 10, &size) &&
- (endptr - ptr > 1 && *endptr == '}'))
+ ptr++;
+ if(!curlx_str_number(&ptr, &size, CURL_OFF_T_MAX) &&
+ !curlx_str_single(&ptr, '}'))
parsed = TRUE;
}
@@ -1177,7 +1308,7 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
size_t chunk = pp->overflow;
/* keep only the overflow */
- Curl_dyn_tail(&pp->recvbuf, chunk);
+ curlx_dyn_tail(&pp->recvbuf, chunk);
pp->nfinal = 0; /* done */
if(chunk > (size_t)size)
@@ -1186,11 +1317,11 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
if(!chunk) {
/* no size, we are done with the data */
- imap_state(data, IMAP_STOP);
+ imap_state(data, imapc, IMAP_STOP);
return CURLE_OK;
}
result = Curl_client_write(data, CLIENTWRITE_BODY,
- Curl_dyn_ptr(&pp->recvbuf), chunk);
+ curlx_dyn_ptr(&pp->recvbuf), chunk);
if(result)
return result;
@@ -1201,12 +1332,12 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
if(pp->overflow > chunk) {
/* remember the remaining trailing overflow data */
pp->overflow -= chunk;
- Curl_dyn_tail(&pp->recvbuf, pp->overflow);
+ curlx_dyn_tail(&pp->recvbuf, pp->overflow);
}
else {
pp->overflow = 0; /* handled */
/* Free the cache */
- Curl_dyn_reset(&pp->recvbuf);
+ curlx_dyn_reset(&pp->recvbuf);
}
}
@@ -1229,13 +1360,14 @@ static CURLcode imap_state_fetch_resp(struct Curl_easy *data,
}
/* End of DO phase */
- imap_state(data, IMAP_STOP);
+ imap_state(data, imapc, IMAP_STOP);
return result;
}
/* For final FETCH responses performed after the download */
static CURLcode imap_state_fetch_final_resp(struct Curl_easy *data,
+ struct imap_conn *imapc,
int imapcode,
imapstate instate)
{
@@ -1247,13 +1379,15 @@ static CURLcode imap_state_fetch_final_resp(struct Curl_easy *data,
result = CURLE_WEIRD_SERVER_REPLY;
else
/* End of DONE phase */
- imap_state(data, IMAP_STOP);
+ imap_state(data, imapc, IMAP_STOP);
return result;
}
/* For APPEND responses */
-static CURLcode imap_state_append_resp(struct Curl_easy *data, int imapcode,
+static CURLcode imap_state_append_resp(struct Curl_easy *data,
+ struct imap_conn *imapc,
+ int imapcode,
imapstate instate)
{
CURLcode result = CURLE_OK;
@@ -1270,7 +1404,7 @@ static CURLcode imap_state_append_resp(struct Curl_easy *data, int imapcode,
Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
/* End of DO phase */
- imap_state(data, IMAP_STOP);
+ imap_state(data, imapc, IMAP_STOP);
}
return result;
@@ -1278,6 +1412,7 @@ static CURLcode imap_state_append_resp(struct Curl_easy *data, int imapcode,
/* For final APPEND responses performed after the upload */
static CURLcode imap_state_append_final_resp(struct Curl_easy *data,
+ struct imap_conn *imapc,
int imapcode,
imapstate instate)
{
@@ -1289,24 +1424,32 @@ static CURLcode imap_state_append_final_resp(struct Curl_easy *data,
result = CURLE_UPLOAD_FAILED;
else
/* End of DONE phase */
- imap_state(data, IMAP_STOP);
+ imap_state(data, imapc, IMAP_STOP);
return result;
}
-static CURLcode imap_statemachine(struct Curl_easy *data,
- struct connectdata *conn)
+static CURLcode imap_pp_statemachine(struct Curl_easy *data,
+ struct connectdata *conn)
{
CURLcode result = CURLE_OK;
int imapcode;
- struct imap_conn *imapc = &conn->proto.imapc;
- struct pingpong *pp = &imapc->pp;
+ struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN);
+ struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY);
+ struct pingpong *pp;
size_t nread = 0;
- (void)data;
+ (void)data;
+ if(!imapc || !imap)
+ return CURLE_FAILED_INIT;
+ pp = &imapc->pp;
/* Busy upgrading the connection; right now all I/O is SSL/TLS, not IMAP */
- if(imapc->state == IMAP_UPGRADETLS)
- return imap_perform_upgrade_tls(data, conn);
+upgrade_tls:
+ if(imapc->state == IMAP_UPGRADETLS) {
+ result = imap_perform_upgrade_tls(data, imapc, conn);
+ if(result || (imapc->state == IMAP_UPGRADETLS))
+ return result;
+ }
/* Flush any data that needs to be sent */
if(pp->sendleft)
@@ -1328,54 +1471,62 @@ static CURLcode imap_statemachine(struct Curl_easy *data,
/* We have now received a full IMAP server response */
switch(imapc->state) {
case IMAP_SERVERGREET:
- result = imap_state_servergreet_resp(data, imapcode, imapc->state);
+ result = imap_state_servergreet_resp(data, imapc,
+ imapcode, imapc->state);
break;
case IMAP_CAPABILITY:
- result = imap_state_capability_resp(data, imapcode, imapc->state);
+ result = imap_state_capability_resp(data, imapc, imapcode, imapc->state);
break;
case IMAP_STARTTLS:
- result = imap_state_starttls_resp(data, imapcode, imapc->state);
+ result = imap_state_starttls_resp(data, imapc, imapcode, imapc->state);
+ /* During UPGRADETLS, leave the read loop as we need to connect
+ * (e.g. TLS handshake) before we continue sending/receiving. */
+ if(!result && (imapc->state == IMAP_UPGRADETLS))
+ goto upgrade_tls;
break;
case IMAP_AUTHENTICATE:
- result = imap_state_auth_resp(data, conn, imapcode, imapc->state);
+ result = imap_state_auth_resp(data, imapc, imapcode, imapc->state);
break;
case IMAP_LOGIN:
- result = imap_state_login_resp(data, imapcode, imapc->state);
+ result = imap_state_login_resp(data, imapc, imapcode, imapc->state);
break;
case IMAP_LIST:
case IMAP_SEARCH:
- result = imap_state_listsearch_resp(data, imapcode, imapc->state);
+ result = imap_state_listsearch_resp(data, imapc, imapcode, imapc->state);
break;
case IMAP_SELECT:
- result = imap_state_select_resp(data, imapcode, imapc->state);
+ result = imap_state_select_resp(data, imapc, imap,
+ imapcode, imapc->state);
break;
case IMAP_FETCH:
- result = imap_state_fetch_resp(data, conn, imapcode, imapc->state);
+ result = imap_state_fetch_resp(data, imapc, imapcode, imapc->state);
break;
case IMAP_FETCH_FINAL:
- result = imap_state_fetch_final_resp(data, imapcode, imapc->state);
+ result = imap_state_fetch_final_resp(data, imapc,
+ imapcode, imapc->state);
break;
case IMAP_APPEND:
- result = imap_state_append_resp(data, imapcode, imapc->state);
+ result = imap_state_append_resp(data, imapc, imapcode, imapc->state);
break;
case IMAP_APPEND_FINAL:
- result = imap_state_append_final_resp(data, imapcode, imapc->state);
+ result = imap_state_append_final_resp(data, imapc,
+ imapcode, imapc->state);
break;
case IMAP_LOGOUT:
default:
/* internal error */
- imap_state(data, IMAP_STOP);
+ imap_state(data, imapc, IMAP_STOP);
break;
}
} while(!result && imapc->state != IMAP_STOP && Curl_pp_moredata(pp));
@@ -1387,17 +1538,12 @@ static CURLcode imap_statemachine(struct Curl_easy *data,
static CURLcode imap_multi_statemach(struct Curl_easy *data, bool *done)
{
CURLcode result = CURLE_OK;
- struct connectdata *conn = data->conn;
- struct imap_conn *imapc = &conn->proto.imapc;
-
- if((conn->handler->flags & PROTOPT_SSL) && !imapc->ssldone) {
- bool ssldone = FALSE;
- result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
- imapc->ssldone = ssldone;
- if(result || !ssldone)
- return result;
- }
+ struct imap_conn *imapc =
+ Curl_conn_meta_get(data->conn, CURL_META_IMAP_CONN);
+ *done = FALSE;
+ if(!imapc)
+ return CURLE_FAILED_INIT;
result = Curl_pp_statemach(data, &imapc->pp, FALSE, FALSE);
*done = (imapc->state == IMAP_STOP);
@@ -1405,11 +1551,10 @@ static CURLcode imap_multi_statemach(struct Curl_easy *data, bool *done)
}
static CURLcode imap_block_statemach(struct Curl_easy *data,
- struct connectdata *conn,
+ struct imap_conn *imapc,
bool disconnecting)
{
CURLcode result = CURLE_OK;
- struct imap_conn *imapc = &conn->proto.imapc;
while(imapc->state != IMAP_STOP && !result)
result = Curl_pp_statemach(data, &imapc->pp, TRUE, disconnecting);
@@ -1417,26 +1562,14 @@ static CURLcode imap_block_statemach(struct Curl_easy *data,
return result;
}
-/* Allocate and initialize the struct IMAP for the current Curl_easy if
- required */
-static CURLcode imap_init(struct Curl_easy *data)
-{
- CURLcode result = CURLE_OK;
- struct IMAP *imap;
-
- imap = data->req.p.imap = calloc(1, sizeof(struct IMAP));
- if(!imap)
- result = CURLE_OUT_OF_MEMORY;
-
- return result;
-}
-
/* For the IMAP "protocol connect" and "doing" phases only */
static int imap_getsock(struct Curl_easy *data,
struct connectdata *conn,
curl_socket_t *socks)
{
- return Curl_pp_getsock(data, &conn->proto.imapc.pp, socks);
+ struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN);
+ return imapc ?
+ Curl_pp_getsock(data, &imapc->pp, socks) : GETSOCK_BLANK;
}
/***********************************************************************
@@ -1451,32 +1584,24 @@ static int imap_getsock(struct Curl_easy *data,
*/
static CURLcode imap_connect(struct Curl_easy *data, bool *done)
{
+ struct imap_conn *imapc =
+ Curl_conn_meta_get(data->conn, CURL_META_IMAP_CONN);
CURLcode result = CURLE_OK;
- struct connectdata *conn = data->conn;
- struct imap_conn *imapc = &conn->proto.imapc;
- struct pingpong *pp = &imapc->pp;
*done = FALSE; /* default to not done yet */
+ if(!imapc)
+ return CURLE_FAILED_INIT;
/* We always support persistent connections in IMAP */
- connkeep(conn, "IMAP default");
-
- PINGPONG_SETUP(pp, imap_statemachine, imap_endofresp);
-
- /* Set the default preferred authentication type and mechanism */
- imapc->preftype = IMAP_TYPE_ANY;
- Curl_sasl_init(&imapc->sasl, data, &saslimap);
-
- Curl_dyn_init(&imapc->dyn, DYN_IMAP_CMD);
- Curl_pp_init(pp);
+ connkeep(data->conn, "IMAP default");
/* Parse the URL options */
- result = imap_parse_url_options(conn);
+ result = imap_parse_url_options(data->conn, imapc);
if(result)
return result;
/* Start off waiting for the server greeting response */
- imap_state(data, IMAP_SERVERGREET);
+ imap_state(data, imapc, IMAP_SERVERGREET);
/* Start off with an response id of '*' */
strcpy(imapc->resptag, "*");
@@ -1500,10 +1625,13 @@ static CURLcode imap_done(struct Curl_easy *data, CURLcode status,
{
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
- struct IMAP *imap = data->req.p.imap;
+ struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN);
+ struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY);
(void)premature;
+ if(!imapc)
+ return CURLE_FAILED_INIT;
if(!imap)
return CURLE_OK;
@@ -1517,33 +1645,20 @@ static CURLcode imap_done(struct Curl_easy *data, CURLcode status,
/* Handle responses after FETCH or APPEND transfer has finished */
if(!data->state.upload && !IS_MIME_POST(data))
- imap_state(data, IMAP_FETCH_FINAL);
+ imap_state(data, imapc, IMAP_FETCH_FINAL);
else {
/* End the APPEND command first by sending an empty line */
- result = Curl_pp_sendf(data, &conn->proto.imapc.pp, "%s", "");
+ result = Curl_pp_sendf(data, &imapc->pp, "%s", "");
if(!result)
- imap_state(data, IMAP_APPEND_FINAL);
+ imap_state(data, imapc, IMAP_APPEND_FINAL);
}
/* Run the state-machine */
if(!result)
- result = imap_block_statemach(data, conn, FALSE);
+ result = imap_block_statemach(data, imapc, FALSE);
}
- /* Cleanup our per-request based variables */
- Curl_safefree(imap->mailbox);
- Curl_safefree(imap->uidvalidity);
- Curl_safefree(imap->uid);
- Curl_safefree(imap->mindex);
- Curl_safefree(imap->section);
- Curl_safefree(imap->partial);
- Curl_safefree(imap->query);
- Curl_safefree(imap->custom);
- Curl_safefree(imap->custom_params);
-
- /* Clear the transfer mode for the next request */
- imap->transfer = PPTRANSFER_BODY;
-
+ imap_easy_reset(imap);
return result;
}
@@ -1560,11 +1675,13 @@ static CURLcode imap_perform(struct Curl_easy *data, bool *connected,
/* This is IMAP and no proxy */
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
- struct IMAP *imap = data->req.p.imap;
- struct imap_conn *imapc = &conn->proto.imapc;
+ struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN);
+ struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY);
bool selected = FALSE;
DEBUGF(infof(data, "DO phase starts"));
+ if(!imapc || !imap)
+ return CURLE_FAILED_INIT;
if(data->req.no_body) {
/* Requested no body means no transfer */
@@ -1584,23 +1701,23 @@ static CURLcode imap_perform(struct Curl_easy *data, bool *connected,
/* Start the first command in the DO phase */
if(data->state.upload || IS_MIME_POST(data))
/* APPEND can be executed directly */
- result = imap_perform_append(data);
+ result = imap_perform_append(data, imapc, imap);
else if(imap->custom && (selected || !imap->mailbox))
/* Custom command using the same mailbox or no mailbox */
- result = imap_perform_list(data);
+ result = imap_perform_list(data, imapc, imap);
else if(!imap->custom && selected && (imap->uid || imap->mindex))
/* FETCH from the same mailbox */
- result = imap_perform_fetch(data);
+ result = imap_perform_fetch(data, imapc, imap);
else if(!imap->custom && selected && imap->query)
/* SEARCH the current mailbox */
- result = imap_perform_search(data);
+ result = imap_perform_search(data, imapc, imap);
else if(imap->mailbox && !selected &&
(imap->custom || imap->uid || imap->mindex || imap->query))
/* SELECT the mailbox */
- result = imap_perform_select(data);
+ result = imap_perform_select(data, imapc, imap);
else
/* LIST */
- result = imap_perform_list(data);
+ result = imap_perform_list(data, imapc, imap);
if(result)
return result;
@@ -1627,20 +1744,23 @@ static CURLcode imap_perform(struct Curl_easy *data, bool *connected,
*/
static CURLcode imap_do(struct Curl_easy *data, bool *done)
{
+ struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY);
CURLcode result = CURLE_OK;
*done = FALSE; /* default to false */
+ if(!imap)
+ return CURLE_FAILED_INIT;
/* Parse the URL path */
- result = imap_parse_url_path(data);
+ result = imap_parse_url_path(data, imap);
if(result)
return result;
/* Parse the custom request */
- result = imap_parse_custom_request(data);
+ result = imap_parse_custom_request(data, imap);
if(result)
return result;
- result = imap_regular_transfer(data, done);
+ result = imap_regular_transfer(data, imap, done);
return result;
}
@@ -1655,39 +1775,32 @@ static CURLcode imap_do(struct Curl_easy *data, bool *done)
static CURLcode imap_disconnect(struct Curl_easy *data,
struct connectdata *conn, bool dead_connection)
{
- struct imap_conn *imapc = &conn->proto.imapc;
- (void)data;
+ struct imap_conn *imapc = Curl_conn_meta_get(conn, CURL_META_IMAP_CONN);
- /* We cannot send quit unconditionally. If this connection is stale or
- bad in any way, sending quit and waiting around here will make the
- disconnect wait in vain and cause more problems than we need to. */
+ (void)data;
+ if(imapc) {
+ /* We cannot send quit unconditionally. If this connection is stale or
+ bad in any way, sending quit and waiting around here will make the
+ disconnect wait in vain and cause more problems than we need to. */
+
+ /* The IMAP session may or may not have been allocated/setup at this
+ point! */
+ if(!dead_connection && conn->bits.protoconnstart) {
+ if(!imap_perform_logout(data, imapc))
+ (void)imap_block_statemach(data, imapc, TRUE); /* ignore errors */
+ }
- /* The IMAP session may or may not have been allocated/setup at this
- point! */
- if(!dead_connection && conn->bits.protoconnstart) {
- if(!imap_perform_logout(data))
- (void)imap_block_statemach(data, conn, TRUE); /* ignore errors */
+ /* Cleanup the SASL module */
+ Curl_sasl_cleanup(conn, imapc->sasl.authused);
}
-
- /* Disconnect from the server */
- Curl_pp_disconnect(&imapc->pp);
- Curl_dyn_free(&imapc->dyn);
-
- /* Cleanup the SASL module */
- Curl_sasl_cleanup(conn, imapc->sasl.authused);
-
- /* Cleanup our connection based variables */
- Curl_safefree(imapc->mailbox);
- Curl_safefree(imapc->mailbox_uidvalidity);
-
return CURLE_OK;
}
/* Call this when the DO phase has completed */
-static CURLcode imap_dophase_done(struct Curl_easy *data, bool connected)
+static CURLcode imap_dophase_done(struct Curl_easy *data,
+ struct IMAP *imap,
+ bool connected)
{
- struct IMAP *imap = data->req.p.imap;
-
(void)connected;
if(imap->transfer != PPTRANSFER_BODY)
@@ -1700,12 +1813,17 @@ static CURLcode imap_dophase_done(struct Curl_easy *data, bool connected)
/* Called from multi.c while DOing */
static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done)
{
- CURLcode result = imap_multi_statemach(data, dophase_done);
+ struct IMAP *imap = Curl_meta_get(data, CURL_META_IMAP_EASY);
+ CURLcode result;
+ if(!imap)
+ return CURLE_FAILED_INIT;
+
+ result = imap_multi_statemach(data, dophase_done);
if(result)
DEBUGF(infof(data, "DO phase failed"));
else if(*dophase_done) {
- result = imap_dophase_done(data, FALSE /* not connected */);
+ result = imap_dophase_done(data, imap, FALSE /* not connected */);
DEBUGF(infof(data, "DO phase is complete"));
}
@@ -1723,6 +1841,7 @@ static CURLcode imap_doing(struct Curl_easy *data, bool *dophase_done)
* remote host.
*/
static CURLcode imap_regular_transfer(struct Curl_easy *data,
+ struct IMAP *imap,
bool *dophase_done)
{
CURLcode result = CURLE_OK;
@@ -1742,21 +1861,75 @@ static CURLcode imap_regular_transfer(struct Curl_easy *data,
/* Perform post DO phase operations if necessary */
if(!result && *dophase_done)
- result = imap_dophase_done(data, connected);
+ result = imap_dophase_done(data, imap, connected);
return result;
}
+static void imap_easy_reset(struct IMAP *imap)
+{
+ Curl_safefree(imap->mailbox);
+ Curl_safefree(imap->uidvalidity);
+ Curl_safefree(imap->uid);
+ Curl_safefree(imap->mindex);
+ Curl_safefree(imap->section);
+ Curl_safefree(imap->partial);
+ Curl_safefree(imap->query);
+ Curl_safefree(imap->custom);
+ Curl_safefree(imap->custom_params);
+ /* Clear the transfer mode for the next request */
+ imap->transfer = PPTRANSFER_BODY;
+}
+
+static void imap_easy_dtor(void *key, size_t klen, void *entry)
+{
+ struct IMAP *imap = entry;
+ (void)key;
+ (void)klen;
+ imap_easy_reset(imap);
+ free(imap);
+}
+
+static void imap_conn_dtor(void *key, size_t klen, void *entry)
+{
+ struct imap_conn *imapc = entry;
+ (void)key;
+ (void)klen;
+ Curl_pp_disconnect(&imapc->pp);
+ curlx_dyn_free(&imapc->dyn);
+ Curl_safefree(imapc->mailbox);
+ Curl_safefree(imapc->mailbox_uidvalidity);
+ free(imapc);
+}
+
static CURLcode imap_setup_connection(struct Curl_easy *data,
struct connectdata *conn)
{
- /* Initialise the IMAP layer */
- CURLcode result = imap_init(data);
- if(result)
- return result;
+ struct imap_conn *imapc;
+ struct pingpong *pp;
+ struct IMAP *imap;
+
+ imapc = calloc(1, sizeof(*imapc));
+ if(!imapc)
+ return CURLE_OUT_OF_MEMORY;
+
+ pp = &imapc->pp;
+ PINGPONG_SETUP(pp, imap_pp_statemachine, imap_endofresp);
- /* Clear the TLS upgraded flag */
- conn->bits.tls_upgraded = FALSE;
+ /* Set the default preferred authentication type and mechanism */
+ imapc->preftype = IMAP_TYPE_ANY;
+ Curl_sasl_init(&imapc->sasl, data, &saslimap);
+
+ curlx_dyn_init(&imapc->dyn, DYN_IMAP_CMD);
+ Curl_pp_init(pp);
+
+ if(Curl_conn_meta_set(conn, CURL_META_IMAP_CONN, imapc, imap_conn_dtor))
+ return CURLE_OUT_OF_MEMORY;
+
+ imap = calloc(1, sizeof(struct IMAP));
+ if(!imap ||
+ Curl_meta_set(data, CURL_META_IMAP_EASY, imap, imap_easy_dtor))
+ return CURLE_OUT_OF_MEMORY;
return CURLE_OK;
}
@@ -1769,10 +1942,11 @@ static CURLcode imap_setup_connection(struct Curl_easy *data,
*
* Designed to never block.
*/
-static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...)
+static CURLcode imap_sendf(struct Curl_easy *data,
+ struct imap_conn *imapc,
+ const char *fmt, ...)
{
CURLcode result = CURLE_OK;
- struct imap_conn *imapc = &data->conn->proto.imapc;
DEBUGASSERT(fmt);
@@ -1782,10 +1956,10 @@ static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...)
++imapc->cmdid);
/* start with a blank buffer */
- Curl_dyn_reset(&imapc->dyn);
+ curlx_dyn_reset(&imapc->dyn);
/* append tag + space + fmt */
- result = Curl_dyn_addf(&imapc->dyn, "%s %s", imapc->resptag, fmt);
+ result = curlx_dyn_addf(&imapc->dyn, "%s %s", imapc->resptag, fmt);
if(!result) {
va_list ap;
va_start(ap, fmt);
@@ -1793,7 +1967,7 @@ static CURLcode imap_sendf(struct Curl_easy *data, const char *fmt, ...)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wformat-nonliteral"
#endif
- result = Curl_pp_vsendf(data, &imapc->pp, Curl_dyn_ptr(&imapc->dyn), ap);
+ result = Curl_pp_vsendf(data, &imapc->pp, curlx_dyn_ptr(&imapc->dyn), ap);
#ifdef __clang__
#pragma clang diagnostic pop
#endif
@@ -1827,24 +2001,24 @@ static char *imap_atom(const char *str, bool escape_only)
/* nothing to escape, return a strdup */
return strdup(str);
- Curl_dyn_init(&line, 2000);
+ curlx_dyn_init(&line, 2000);
- if(!escape_only && Curl_dyn_addn(&line, "\"", 1))
+ if(!escape_only && curlx_dyn_addn(&line, "\"", 1))
return NULL;
while(*str) {
if((*str == '\\' || *str == '"') &&
- Curl_dyn_addn(&line, "\\", 1))
+ curlx_dyn_addn(&line, "\\", 1))
return NULL;
- if(Curl_dyn_addn(&line, str, 1))
+ if(curlx_dyn_addn(&line, str, 1))
return NULL;
str++;
}
- if(!escape_only && Curl_dyn_addn(&line, "\"", 1))
+ if(!escape_only && curlx_dyn_addn(&line, "\"", 1))
return NULL;
- return Curl_dyn_ptr(&line);
+ return curlx_dyn_ptr(&line);
}
/***********************************************************************
@@ -1886,10 +2060,10 @@ static bool imap_is_bchar(char ch)
*
* Parse the URL login options.
*/
-static CURLcode imap_parse_url_options(struct connectdata *conn)
+static CURLcode imap_parse_url_options(struct connectdata *conn,
+ struct imap_conn *imapc)
{
CURLcode result = CURLE_OK;
- struct imap_conn *imapc = &conn->proto.imapc;
const char *ptr = conn->options;
bool prefer_login = FALSE;
@@ -1950,11 +2124,11 @@ static CURLcode imap_parse_url_options(struct connectdata *conn)
* Parse the URL path into separate path components.
*
*/
-static CURLcode imap_parse_url_path(struct Curl_easy *data)
+static CURLcode imap_parse_url_path(struct Curl_easy *data,
+ struct IMAP *imap)
{
/* The imap struct is already initialised in imap_connect() */
CURLcode result = CURLE_OK;
- struct IMAP *imap = data->req.p.imap;
const char *begin = &data->state.up.path[1]; /* skip leading slash */
const char *ptr = begin;
@@ -2082,10 +2256,10 @@ static CURLcode imap_parse_url_path(struct Curl_easy *data)
*
* Parse the custom request.
*/
-static CURLcode imap_parse_custom_request(struct Curl_easy *data)
+static CURLcode imap_parse_custom_request(struct Curl_easy *data,
+ struct IMAP *imap)
{
CURLcode result = CURLE_OK;
- struct IMAP *imap = data->req.p.imap;
const char *custom = data->set.str[STRING_CUSTOMREQUEST];
if(custom) {
diff --git a/libs/libcurl/src/imap.h b/libs/libcurl/src/imap.h
index 305d22a148..93a0aaa3e4 100644
--- a/libs/libcurl/src/imap.h
+++ b/libs/libcurl/src/imap.h
@@ -27,65 +27,6 @@
#include "pingpong.h"
#include "curl_sasl.h"
-/****************************************************************************
- * IMAP unique setup
- ***************************************************************************/
-typedef enum {
- IMAP_STOP, /* do nothing state, stops the state machine */
- IMAP_SERVERGREET, /* waiting for the initial greeting immediately after
- a connect */
- IMAP_CAPABILITY,
- IMAP_STARTTLS,
- IMAP_UPGRADETLS, /* asynchronously upgrade the connection to SSL/TLS
- (multi mode only) */
- IMAP_AUTHENTICATE,
- IMAP_LOGIN,
- IMAP_LIST,
- IMAP_SELECT,
- IMAP_FETCH,
- IMAP_FETCH_FINAL,
- IMAP_APPEND,
- IMAP_APPEND_FINAL,
- IMAP_SEARCH,
- IMAP_LOGOUT,
- IMAP_LAST /* never used */
-} imapstate;
-
-/* This IMAP struct is used in the Curl_easy. All IMAP data that is
- connection-oriented must be in imap_conn to properly deal with the fact that
- perhaps the Curl_easy is changed between the times the connection is
- used. */
-struct IMAP {
- curl_pp_transfer transfer;
- char *mailbox; /* Mailbox to select */
- char *uidvalidity; /* UIDVALIDITY to check in select */
- char *uid; /* Message UID to fetch */
- char *mindex; /* Index in mail box of mail to fetch */
- char *section; /* Message SECTION to fetch */
- char *partial; /* Message PARTIAL to fetch */
- char *query; /* Query to search for */
- char *custom; /* Custom request */
- char *custom_params; /* Parameters for the custom request */
-};
-
-/* imap_conn is used for struct connection-oriented data in the connectdata
- struct */
-struct imap_conn {
- struct pingpong pp;
- struct SASL sasl; /* SASL-related parameters */
- struct dynbuf dyn; /* for the IMAP commands */
- char *mailbox; /* The last selected mailbox */
- char *mailbox_uidvalidity; /* UIDVALIDITY parsed from select response */
- imapstate state; /* Always use imap.c:state() to change state! */
- char resptag[5]; /* Response tag to wait for */
- unsigned char preftype; /* Preferred authentication type */
- unsigned char cmdid; /* Last used command ID */
- BIT(ssldone); /* Is connect() over SSL done? */
- BIT(preauth); /* Is this connection PREAUTH? */
- BIT(tls_supported); /* StartTLS capability supported by server */
- BIT(login_disabled); /* LOGIN command disabled by server */
- BIT(ir_supported); /* Initial response supported by server */
-};
extern const struct Curl_handler Curl_handler_imap;
extern const struct Curl_handler Curl_handler_imaps;
diff --git a/libs/libcurl/src/inet_ntop.c b/libs/libcurl/src/inet_ntop.c
index ff318d1a25..60b9e4a6c7 100644
--- a/libs/libcurl/src/inet_ntop.c
+++ b/libs/libcurl/src/inet_ntop.c
@@ -38,7 +38,7 @@
#include "curl_printf.h"
#define IN6ADDRSZ 16
-#define INADDRSZ 4
+/* #define INADDRSZ 4 */
#define INT16SZ 2
/*
@@ -56,7 +56,7 @@
* Returns `dst' (as a const)
* Note:
* - uses no statics
- * - takes a unsigned char* not an in_addr as input
+ * - takes an unsigned char* not an in_addr as input
*/
static char *inet_ntop4(const unsigned char *src, char *dst, size_t size)
{
@@ -74,8 +74,12 @@ static char *inet_ntop4(const unsigned char *src, char *dst, size_t size)
len = strlen(tmp);
if(len == 0 || len >= size) {
- errno = ENOSPC;
- return (NULL);
+#ifdef USE_WINSOCK
+ CURL_SETERRNO(WSAEINVAL);
+#else
+ CURL_SETERRNO(ENOSPC);
+#endif
+ return NULL;
}
strcpy(dst, tmp);
return dst;
@@ -153,8 +157,7 @@ static char *inet_ntop6(const unsigned char *src, char *dst, size_t size)
if(i == 6 && best.base == 0 &&
(best.len == 6 || (best.len == 5 && words[5] == 0xffff))) {
if(!inet_ntop4(src + 12, tp, sizeof(tmp) - (tp - tmp))) {
- errno = ENOSPC;
- return (NULL);
+ return NULL;
}
tp += strlen(tp);
break;
@@ -171,8 +174,12 @@ static char *inet_ntop6(const unsigned char *src, char *dst, size_t size)
/* Check for overflow, copy, and we are done.
*/
if((size_t)(tp - tmp) > size) {
- errno = ENOSPC;
- return (NULL);
+#ifdef USE_WINSOCK
+ CURL_SETERRNO(WSAEINVAL);
+#else
+ CURL_SETERRNO(ENOSPC);
+#endif
+ return NULL;
}
strcpy(dst, tmp);
return dst;
@@ -197,7 +204,7 @@ char *Curl_inet_ntop(int af, const void *src, char *buf, size_t size)
case AF_INET6:
return inet_ntop6((const unsigned char *)src, buf, size);
default:
- errno = EAFNOSUPPORT;
+ CURL_SETERRNO(SOCKEAFNOSUPPORT);
return NULL;
}
}
diff --git a/libs/libcurl/src/inet_ntop.h b/libs/libcurl/src/inet_ntop.h
index e2c3b4b5ac..6c02c68e41 100644
--- a/libs/libcurl/src/inet_ntop.h
+++ b/libs/libcurl/src/inet_ntop.h
@@ -29,15 +29,22 @@
char *Curl_inet_ntop(int af, const void *addr, char *buf, size_t size);
#ifdef HAVE_INET_NTOP
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
-#ifdef _WIN32
-#define Curl_inet_ntop(af,addr,buf,size) \
- inet_ntop(af, addr, buf, size)
+#ifdef __AMIGA__
+#define Curl_inet_ntop(af,addr,buf,size) \
+ (char *)inet_ntop(af, CURL_UNCONST(addr), (unsigned char *)buf, \
+ (curl_socklen_t)(size))
#else
-#define Curl_inet_ntop(af,addr,buf,size) \
- inet_ntop(af, addr, buf, (curl_socklen_t)(size))
+#define Curl_inet_ntop(af,addr,buf,size) \
+ inet_ntop(af, addr, buf, (curl_socklen_t)(size))
#endif
#endif
diff --git a/libs/libcurl/src/krb5.c b/libs/libcurl/src/krb5.c
index 22eef17139..35c9e60fc1 100644
--- a/libs/libcurl/src/krb5.c
+++ b/libs/libcurl/src/krb5.c
@@ -46,15 +46,16 @@
#endif
#include "urldata.h"
+#include "url.h"
#include "cfilters.h"
#include "cf-socket.h"
-#include "curl_base64.h"
+#include "curlx/base64.h"
#include "ftp.h"
#include "curl_gssapi.h"
#include "sendf.h"
#include "transfer.h"
#include "curl_krb5.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
#include "strcase.h"
#include "strdup.h"
@@ -63,6 +64,11 @@
#include "curl_memory.h"
#include "memdebug.h"
+#if defined(__GNUC__) && defined(__APPLE__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
static CURLcode ftpsend(struct Curl_easy *data, struct connectdata *conn,
const char *cmd)
{
@@ -168,7 +174,7 @@ krb5_encode(void *app_data, const void *from, int length, int level, void **to)
/* NOTE that the cast is safe, neither of the krb5, gnu gss and heimdal
* libraries modify the input buffer in gss_wrap()
*/
- dec.value = (void *)from;
+ dec.value = CURL_UNCONST(from);
dec.length = (size_t)length;
maj = gss_wrap(&min, *context,
level == PROT_PRIVATE,
@@ -202,15 +208,20 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn)
data->set.str[STRING_SERVICE_NAME] :
"ftp";
const char *srv_host = "host";
- gss_buffer_desc input_buffer, output_buffer, _gssresp, *gssresp;
+ gss_buffer_desc input_buffer, output_buffer, *gssresp;
+ gss_buffer_desc _gssresp = GSS_C_EMPTY_BUFFER;
OM_uint32 maj, min;
gss_name_t gssname;
gss_ctx_id_t *context = app_data;
struct gss_channel_bindings_struct chan;
size_t base64_sz = 0;
struct sockaddr_in *remote_addr =
- (struct sockaddr_in *)(void *)&conn->remote_addr->curl_sa_addr;
+ (struct sockaddr_in *)CURL_UNCONST(&conn->remote_addr->curl_sa_addr);
char *stringp;
+ struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN);
+
+ if(!ftpc)
+ return -2;
if(getsockname(conn->sock[FIRSTSOCKET],
(struct sockaddr *)&conn->local_addr, &l) < 0)
@@ -236,8 +247,7 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn)
if(Curl_GetFTPResponse(data, &nread, NULL))
return -1;
else {
- struct pingpong *pp = &conn->proto.ftpc.pp;
- char *line = Curl_dyn_ptr(&pp->recvbuf);
+ char *line = curlx_dyn_ptr(&ftpc->pp.recvbuf);
if(line[0] != '3')
return -1;
}
@@ -298,7 +308,7 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn)
if(output_buffer.length) {
char *cmd;
- result = Curl_base64_encode((char *)output_buffer.value,
+ result = curlx_base64_encode((char *)output_buffer.value,
output_buffer.length, &p, &base64_sz);
if(result) {
infof(data, "base64-encoding: %s", curl_easy_strerror(result));
@@ -325,9 +335,8 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn)
break;
}
else {
- struct pingpong *pp = &conn->proto.ftpc.pp;
- size_t len = Curl_dyn_len(&pp->recvbuf);
- p = Curl_dyn_ptr(&pp->recvbuf);
+ size_t len = curlx_dyn_len(&ftpc->pp.recvbuf);
+ p = curlx_dyn_ptr(&ftpc->pp.recvbuf);
if((len < 4) || (p[0] != '2' && p[0] != '3')) {
infof(data, "Server did not accept auth data");
ret = AUTH_ERROR;
@@ -342,7 +351,7 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn)
if(p) {
unsigned char *outptr;
size_t outlen;
- result = Curl_base64_decode(p + 5, &outptr, &outlen);
+ result = curlx_base64_decode(p + 5, &outptr, &outlen);
if(result) {
failf(data, "base64-decoding: %s", curl_easy_strerror(result));
ret = AUTH_CONTINUE;
@@ -363,7 +372,7 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn)
free(_gssresp.value);
if(ret == AUTH_OK || service == srv_host)
- return ret;
+ break;
service = srv_host;
}
@@ -372,13 +381,13 @@ krb5_auth(void *app_data, struct Curl_easy *data, struct connectdata *conn)
static void krb5_end(void *app_data)
{
- OM_uint32 min;
- gss_ctx_id_t *context = app_data;
- if(*context != GSS_C_NO_CONTEXT) {
- OM_uint32 maj = gss_delete_sec_context(&min, context, GSS_C_NO_BUFFER);
- (void)maj;
- DEBUGASSERT(maj == GSS_S_COMPLETE);
- }
+ OM_uint32 min;
+ gss_ctx_id_t *context = app_data;
+ if(*context != GSS_C_NO_CONTEXT) {
+ OM_uint32 maj = gss_delete_sec_context(&min, context, GSS_C_NO_BUFFER);
+ (void)maj;
+ DEBUGASSERT(maj == GSS_S_COMPLETE);
+ }
}
static const struct Curl_sec_client_mech Curl_krb5_client_mech = {
@@ -531,7 +540,7 @@ static CURLcode read_data(struct Curl_easy *data, int sockindex,
if(len > CURL_MAX_INPUT_LENGTH)
return CURLE_TOO_LARGE;
- Curl_dyn_reset(&buf->buf);
+ curlx_dyn_reset(&buf->buf);
}
else
return CURLE_RECV_ERROR;
@@ -542,18 +551,18 @@ static CURLcode read_data(struct Curl_easy *data, int sockindex,
result = socket_read(data, sockindex, buffer, (size_t)nread);
if(result)
return result;
- result = Curl_dyn_addn(&buf->buf, buffer, nread);
+ result = curlx_dyn_addn(&buf->buf, buffer, nread);
if(result)
return result;
len -= nread;
} while(len);
/* this decodes the dynbuf *in place* */
nread = conn->mech->decode(conn->app_data,
- Curl_dyn_ptr(&buf->buf),
+ curlx_dyn_ptr(&buf->buf),
len, conn->data_prot, conn);
if(nread < 0)
return CURLE_RECV_ERROR;
- Curl_dyn_setlen(&buf->buf, nread);
+ curlx_dyn_setlen(&buf->buf, nread);
buf->index = 0;
return CURLE_OK;
}
@@ -561,10 +570,10 @@ static CURLcode read_data(struct Curl_easy *data, int sockindex,
static size_t
buffer_read(struct krb5buffer *buf, void *data, size_t len)
{
- size_t size = Curl_dyn_len(&buf->buf);
+ size_t size = curlx_dyn_len(&buf->buf);
if(size - buf->index < len)
len = size - buf->index;
- memcpy(data, Curl_dyn_ptr(&buf->buf) + buf->index, len);
+ memcpy(data, curlx_dyn_ptr(&buf->buf) + buf->index, len);
buf->index += len;
return len;
}
@@ -599,7 +608,7 @@ static ssize_t sec_recv(struct Curl_easy *data, int sockindex,
while(len > 0) {
if(read_data(data, sockindex, &conn->in_buffer))
return -1;
- if(Curl_dyn_len(&conn->in_buffer.buf) == 0) {
+ if(curlx_dyn_len(&conn->in_buffer.buf) == 0) {
if(bytes_read > 0)
conn->in_buffer.eof_flag = 1;
return bytes_read;
@@ -612,10 +621,10 @@ static ssize_t sec_recv(struct Curl_easy *data, int sockindex,
return total_read;
}
-/* Send |length| bytes from |from| to the |fd| socket taking care of encoding
- and negotiating with the server. |from| can be NULL. */
+/* Send |length| bytes from |from| to the |sockindex| socket taking care of
+ encoding and negotiating with the server. |from| can be NULL. */
static void do_sec_send(struct Curl_easy *data, struct connectdata *conn,
- curl_socket_t fd, const char *from, int length)
+ int sockindex, const char *from, int length)
{
int bytes, htonl_bytes; /* 32-bit integers for htonl */
char *buffer = NULL;
@@ -639,7 +648,7 @@ static void do_sec_send(struct Curl_easy *data, struct connectdata *conn,
return; /* error */
if(iscmd) {
- error = Curl_base64_encode(buffer, curlx_sitouz(bytes),
+ error = curlx_base64_encode(buffer, curlx_sitouz(bytes),
&cmd_buffer, &cmd_size);
if(error) {
free(buffer);
@@ -649,12 +658,12 @@ static void do_sec_send(struct Curl_easy *data, struct connectdata *conn,
static const char *enc = "ENC ";
static const char *mic = "MIC ";
if(prot_level == PROT_PRIVATE)
- socket_write(data, fd, enc, 4);
+ socket_write(data, sockindex, enc, 4);
else
- socket_write(data, fd, mic, 4);
+ socket_write(data, sockindex, mic, 4);
- socket_write(data, fd, cmd_buffer, cmd_size);
- socket_write(data, fd, "\r\n", 2);
+ socket_write(data, sockindex, cmd_buffer, cmd_size);
+ socket_write(data, sockindex, "\r\n", 2);
infof(data, "Send: %s%s", prot_level == PROT_PRIVATE ? enc : mic,
cmd_buffer);
free(cmd_buffer);
@@ -662,14 +671,14 @@ static void do_sec_send(struct Curl_easy *data, struct connectdata *conn,
}
else {
htonl_bytes = (int)htonl((OM_uint32)bytes);
- socket_write(data, fd, &htonl_bytes, sizeof(htonl_bytes));
- socket_write(data, fd, buffer, curlx_sitouz(bytes));
+ socket_write(data, sockindex, &htonl_bytes, sizeof(htonl_bytes));
+ socket_write(data, sockindex, buffer, curlx_sitouz(bytes));
}
free(buffer);
}
static ssize_t sec_write(struct Curl_easy *data, struct connectdata *conn,
- curl_socket_t fd, const char *buffer, size_t length)
+ int sockindex, const char *buffer, size_t length)
{
ssize_t tx = 0, len = conn->buffer_size;
@@ -679,7 +688,7 @@ static ssize_t sec_write(struct Curl_easy *data, struct connectdata *conn,
if(length < (size_t)len)
len = length;
- do_sec_send(data, conn, fd, buffer, curlx_sztosi(len));
+ do_sec_send(data, conn, sockindex, buffer, curlx_sztosi(len));
length -= len;
buffer += len;
tx += len;
@@ -693,10 +702,9 @@ static ssize_t sec_send(struct Curl_easy *data, int sockindex,
CURLcode *err)
{
struct connectdata *conn = data->conn;
- curl_socket_t fd = conn->sock[sockindex];
(void)eos; /* unused */
*err = CURLE_OK;
- return sec_write(data, conn, fd, buffer, len);
+ return sec_write(data, conn, sockindex, buffer, len);
}
int Curl_sec_read_msg(struct Curl_easy *data, struct connectdata *conn,
@@ -718,7 +726,7 @@ int Curl_sec_read_msg(struct Curl_easy *data, struct connectdata *conn,
DEBUGASSERT(level > PROT_NONE && level < PROT_LAST);
- error = Curl_base64_decode(buffer + 4, (unsigned char **)&buf, &decoded_sz);
+ error = curlx_base64_decode(buffer + 4, (unsigned char **)&buf, &decoded_sz);
if(error || decoded_sz == 0)
return -1;
@@ -776,9 +784,12 @@ static int sec_set_protection_level(struct Curl_easy *data)
if(level) {
char *pbsz;
unsigned int buffer_size = 1 << 20; /* 1048576 */
- struct pingpong *pp = &conn->proto.ftpc.pp;
+ struct ftp_conn *ftpc = Curl_conn_meta_get(conn, CURL_META_FTP_CONN);
char *line;
+ if(!ftpc)
+ return -2;
+
code = ftp_send_command(data, "PBSZ %u", buffer_size);
if(code < 0)
return -1;
@@ -789,7 +800,7 @@ static int sec_set_protection_level(struct Curl_easy *data)
}
conn->buffer_size = buffer_size;
- line = Curl_dyn_ptr(&pp->recvbuf);
+ line = curlx_dyn_ptr(&ftpc->pp.recvbuf);
pbsz = strstr(line, "PBSZ=");
if(pbsz) {
/* stick to default value if the check fails */
@@ -850,7 +861,6 @@ static CURLcode choose_mech(struct Curl_easy *data, struct connectdata *conn)
mech->name);
return CURLE_FAILED_INIT;
}
- Curl_dyn_init(&conn->in_buffer.buf, CURL_MAX_INPUT_LENGTH);
}
infof(data, "Trying mechanism %s...", mech->name);
@@ -909,14 +919,21 @@ Curl_sec_login(struct Curl_easy *data, struct connectdata *conn)
return choose_mech(data, conn);
}
+void
+Curl_sec_conn_init(struct connectdata *conn)
+{
+ curlx_dyn_init(&conn->in_buffer.buf, CURL_MAX_INPUT_LENGTH);
+ conn->in_buffer.index = 0;
+ conn->in_buffer.eof_flag = 0;
+}
void
-Curl_sec_end(struct connectdata *conn)
+Curl_sec_conn_destroy(struct connectdata *conn)
{
if(conn->mech && conn->mech->end)
conn->mech->end(conn->app_data);
Curl_safefree(conn->app_data);
- Curl_dyn_free(&conn->in_buffer.buf);
+ curlx_dyn_free(&conn->in_buffer.buf);
conn->in_buffer.index = 0;
conn->in_buffer.eof_flag = 0;
conn->sec_complete = 0;
@@ -924,4 +941,8 @@ Curl_sec_end(struct connectdata *conn)
conn->mech = NULL;
}
+#if defined(__GNUC__) && defined(__APPLE__)
+#pragma GCC diagnostic pop
+#endif
+
#endif /* HAVE_GSSAPI && !CURL_DISABLE_FTP */
diff --git a/libs/libcurl/src/ldap.c b/libs/libcurl/src/ldap.c
index 6bc73526d7..f38653e3e0 100644
--- a/libs/libcurl/src/ldap.c
+++ b/libs/libcurl/src/ldap.c
@@ -26,6 +26,11 @@
#if !defined(CURL_DISABLE_LDAP) && !defined(USE_OPENLDAP)
+#if defined(__GNUC__) && defined(__APPLE__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
/*
* Notice that USE_OPENLDAP is only a source code selection switch. When
* libcurl is built with USE_OPENLDAP defined the libcurl source code that
@@ -52,7 +57,7 @@
#ifdef USE_WIN32_LDAP /* Use Windows LDAP implementation. */
# ifdef _MSC_VER
# pragma warning(push)
-# pragma warning(disable: 4201)
+# pragma warning(disable:4201)
# endif
# include <subauth.h> /* for [P]UNICODE_STRING */
# ifdef _MSC_VER
@@ -78,15 +83,16 @@
#include "urldata.h"
#include <curl/curl.h>
+#include "cfilters.h"
#include "sendf.h"
#include "escape.h"
#include "progress.h"
#include "transfer.h"
#include "strcase.h"
-#include "strtok.h"
+#include "curlx/strparse.h"
#include "curl_ldap.h"
-#include "curl_multibyte.h"
-#include "curl_base64.h"
+#include "curlx/multibyte.h"
+#include "curlx/base64.h"
#include "connect.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -148,7 +154,7 @@ static void _ldap_free_urldesc(LDAPURLDesc *ludp);
#define ldap_err2string ldap_err2stringA
#endif
-#if defined(USE_WIN32_LDAP) && defined(_MSC_VER) && (_MSC_VER <= 1600)
+#if defined(USE_WIN32_LDAP) && defined(_MSC_VER) && (_MSC_VER <= 1700)
/* Workaround for warning:
'type cast' : conversion from 'int' to 'void *' of greater size */
#undef LDAP_OPT_ON
@@ -181,6 +187,7 @@ const struct Curl_handler Curl_handler_ldap = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_LDAP, /* defport */
CURLPROTO_LDAP, /* protocol */
CURLPROTO_LDAP, /* family */
@@ -210,6 +217,7 @@ const struct Curl_handler Curl_handler_ldaps = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_LDAPS, /* defport */
CURLPROTO_LDAPS, /* protocol */
CURLPROTO_LDAP, /* family */
@@ -277,8 +285,8 @@ static int ldap_win_bind(struct Curl_easy *data, LDAP *server,
PTCHAR inpass = NULL;
if(user && passwd && (data->set.httpauth & CURLAUTH_BASIC)) {
- inuser = curlx_convert_UTF8_to_tchar((char *) user);
- inpass = curlx_convert_UTF8_to_tchar((char *) passwd);
+ inuser = curlx_convert_UTF8_to_tchar(user);
+ inpass = curlx_convert_UTF8_to_tchar(passwd);
rc = (int)ldap_simple_bind_s(server, inuser, inpass);
@@ -346,7 +354,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
}
/* Get the URL scheme (either ldap or ldaps) */
- if(conn->given->flags & PROTOPT_SSL)
+ if(Curl_conn_is_ssl(conn, FIRSTSOCKET))
ldap_ssl = 1;
infof(data, "LDAP local: trying to establish %s connection",
ldap_ssl ? "encrypted" : "cleartext");
@@ -381,55 +389,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
#else
int ldap_option;
char *ldap_ca = conn->ssl_config.CAfile;
-#if defined(CURL_HAS_NOVELL_LDAPSDK)
- rc = ldapssl_client_init(NULL, NULL);
- if(rc != LDAP_SUCCESS) {
- failf(data, "LDAP local: ldapssl_client_init %s", ldap_err2string(rc));
- result = CURLE_SSL_CERTPROBLEM;
- goto quit;
- }
- if(conn->ssl_config.verifypeer) {
- /* Novell SDK supports DER or BASE64 files. */
- int cert_type = LDAPSSL_CERT_FILETYPE_B64;
- if((data->set.ssl.cert_type) &&
- (strcasecompare(data->set.ssl.cert_type, "DER")))
- cert_type = LDAPSSL_CERT_FILETYPE_DER;
- if(!ldap_ca) {
- failf(data, "LDAP local: ERROR %s CA cert not set",
- (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"));
- result = CURLE_SSL_CERTPROBLEM;
- goto quit;
- }
- infof(data, "LDAP local: using %s CA cert '%s'",
- (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"),
- ldap_ca);
- rc = ldapssl_add_trusted_cert(ldap_ca, cert_type);
- if(rc != LDAP_SUCCESS) {
- failf(data, "LDAP local: ERROR setting %s CA cert: %s",
- (cert_type == LDAPSSL_CERT_FILETYPE_DER ? "DER" : "PEM"),
- ldap_err2string(rc));
- result = CURLE_SSL_CERTPROBLEM;
- goto quit;
- }
- ldap_option = LDAPSSL_VERIFY_SERVER;
- }
- else
- ldap_option = LDAPSSL_VERIFY_NONE;
- rc = ldapssl_set_verify_mode(ldap_option);
- if(rc != LDAP_SUCCESS) {
- failf(data, "LDAP local: ERROR setting cert verify mode: %s",
- ldap_err2string(rc));
- result = CURLE_SSL_CERTPROBLEM;
- goto quit;
- }
- server = ldapssl_init(host, conn->primary.remote_port, 1);
- if(!server) {
- failf(data, "LDAP local: Cannot connect to %s:%u",
- conn->host.dispname, conn->primary.remote_port);
- result = CURLE_COULDNT_CONNECT;
- goto quit;
- }
-#elif defined(LDAP_OPT_X_TLS)
+#ifdef LDAP_OPT_X_TLS
if(conn->ssl_config.verifypeer) {
/* OpenLDAP SDK supports BASE64 files. */
if((data->set.ssl.cert_type) &&
@@ -583,7 +543,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
#endif
name_len = strlen(name);
- result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"DN: ", 4);
+ result = Curl_client_write(data, CLIENTWRITE_BODY, "DN: ", 4);
if(result) {
FREE_ON_WINLDAP(name);
ldap_memfree(dn);
@@ -597,7 +557,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
goto quit;
}
- result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1);
+ result = Curl_client_write(data, CLIENTWRITE_BODY, "\n", 1);
if(result) {
FREE_ON_WINLDAP(name);
ldap_memfree(dn);
@@ -633,7 +593,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
vals = ldap_get_values_len(server, entryIterator, attribute);
if(vals) {
for(i = 0; (vals[i] != NULL); i++) {
- result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\t", 1);
+ result = Curl_client_write(data, CLIENTWRITE_BODY, "\t", 1);
if(result) {
ldap_value_free_len(vals);
FREE_ON_WINLDAP(attr);
@@ -655,7 +615,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
goto quit;
}
- result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)": ", 2);
+ result = Curl_client_write(data, CLIENTWRITE_BODY, ": ", 2);
if(result) {
ldap_value_free_len(vals);
FREE_ON_WINLDAP(attr);
@@ -669,8 +629,8 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
if((attr_len > 7) &&
(strcmp(";binary", attr + (attr_len - 7)) == 0)) {
/* Binary attribute, encode to base64. */
- result = Curl_base64_encode(vals[i]->bv_val, vals[i]->bv_len,
- &val_b64, &val_b64_sz);
+ result = curlx_base64_encode(vals[i]->bv_val, vals[i]->bv_len,
+ &val_b64, &val_b64_sz);
if(result) {
ldap_value_free_len(vals);
FREE_ON_WINLDAP(attr);
@@ -710,7 +670,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
}
}
- result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1);
+ result = Curl_client_write(data, CLIENTWRITE_BODY, "\n", 1);
if(result) {
ldap_value_free_len(vals);
FREE_ON_WINLDAP(attr);
@@ -730,7 +690,7 @@ static CURLcode ldap_do(struct Curl_easy *data, bool *done)
FREE_ON_WINLDAP(attr);
ldap_memfree(attribute);
- result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)"\n", 1);
+ result = Curl_client_write(data, CLIENTWRITE_BODY, "\n", 1);
if(result)
goto quit;
}
@@ -750,10 +710,6 @@ quit:
ldap_free_urldesc(ludp);
if(server)
ldap_unbind_s(server);
-#if defined(HAVE_LDAP_SSL) && defined(CURL_HAS_NOVELL_LDAPSDK)
- if(ldap_ssl)
- ldapssl_client_deinit();
-#endif /* HAVE_LDAP_SSL && CURL_HAS_NOVELL_LDAPSDK */
FREE_ON_WINLDAP(host);
@@ -772,7 +728,9 @@ static void _ldap_trace(const char *fmt, ...)
if(do_trace == -1) {
const char *env = getenv("CURL_TRACE");
- do_trace = (env && strtol(env, NULL, 10) > 0);
+ curl_off_t e = 0;
+ if(!curlx_str_number(&env, &e, INT_MAX))
+ do_trace = e > 0;
}
if(!do_trace)
return;
@@ -800,39 +758,20 @@ static int str2scope(const char *p)
return LDAP_SCOPE_SUBTREE;
if(strcasecompare(p, "subtree"))
return LDAP_SCOPE_SUBTREE;
- return (-1);
+ return -1;
}
-/*
- * Split 'str' into strings separated by commas.
- * Note: out[] points into 'str'.
- */
-static bool split_str(char *str, char ***out, size_t *count)
+/* number of entries in the attributes list */
+static size_t num_entries(const char *s)
{
- char **res;
- char *lasts;
- char *s;
- size_t i;
size_t items = 1;
- s = strchr(str, ',');
+ s = strchr(s, ',');
while(s) {
items++;
- s = strchr(++s, ',');
+ s = strchr(s + 1, ',');
}
-
- res = calloc(items, sizeof(char *));
- if(!res)
- return FALSE;
-
- for(i = 0, s = strtok_r(str, ",", &lasts); s && i < items;
- s = strtok_r(NULL, ",", &lasts), i++)
- res[i] = s;
-
- *out = res;
- *count = items;
-
- return TRUE;
+ return items;
}
/*
@@ -926,15 +865,8 @@ static int _ldap_url_parse2(struct Curl_easy *data,
*q++ = '\0';
if(*p) {
- char **attributes;
- size_t count = 0;
-
- /* Split the string into an array of attributes */
- if(!split_str(p, &attributes, &count)) {
- rc = LDAP_NO_MEMORY;
-
- goto quit;
- }
+ size_t count = num_entries(p); /* at least one */
+ const char *atp = p;
/* Allocate our array (+1 for the NULL entry) */
#if defined(USE_WIN32_LDAP)
@@ -943,27 +875,25 @@ static int _ldap_url_parse2(struct Curl_easy *data,
ludp->lud_attrs = calloc(count + 1, sizeof(char *));
#endif
if(!ludp->lud_attrs) {
- free(attributes);
-
rc = LDAP_NO_MEMORY;
-
goto quit;
}
for(i = 0; i < count; i++) {
char *unescaped;
CURLcode result;
+ struct Curl_str out;
+
+ if(curlx_str_until(&atp, &out, 1024, ','))
+ break;
- LDAP_TRACE(("attr[%zu] '%s'\n", i, attributes[i]));
+ LDAP_TRACE(("attr[%zu] '%.*s'\n", i, (int)out.len, out.str));
/* Unescape the attribute */
- result = Curl_urldecode(attributes[i], 0, &unescaped, NULL,
+ result = Curl_urldecode(out.str, out.len, &unescaped, NULL,
REJECT_ZERO);
if(result) {
- free(attributes);
-
rc = LDAP_NO_MEMORY;
-
goto quit;
}
@@ -975,10 +905,7 @@ static int _ldap_url_parse2(struct Curl_easy *data,
free(unescaped);
if(!ludp->lud_attrs[i]) {
- free(attributes);
-
rc = LDAP_NO_MEMORY;
-
goto quit;
}
#else
@@ -986,9 +913,9 @@ static int _ldap_url_parse2(struct Curl_easy *data,
#endif
ludp->lud_attrs_dups++;
+ if(curlx_str_single(&atp, ','))
+ break;
}
-
- free(attributes);
}
p = q;
@@ -1082,7 +1009,7 @@ static int _ldap_url_parse(struct Curl_easy *data,
ludp = NULL;
}
*ludpp = ludp;
- return (rc);
+ return rc;
}
static void _ldap_free_urldesc(LDAPURLDesc *ludp)
@@ -1113,4 +1040,9 @@ static void _ldap_free_urldesc(LDAPURLDesc *ludp)
free(ludp);
}
#endif /* !HAVE_LDAP_URL_PARSE */
+
+#if defined(__GNUC__) && defined(__APPLE__)
+#pragma GCC diagnostic pop
+#endif
+
#endif /* !CURL_DISABLE_LDAP && !USE_OPENLDAP */
diff --git a/libs/libcurl/src/libcurl.def b/libs/libcurl/src/libcurl.def
index 4f8f9229d2..33789fd185 100644
--- a/libs/libcurl/src/libcurl.def
+++ b/libs/libcurl/src/libcurl.def
@@ -15,6 +15,8 @@ curl_easy_recv
curl_easy_reset
curl_easy_send
curl_easy_setopt
+curl_easy_ssls_export
+curl_easy_ssls_import
curl_easy_strerror
curl_easy_unescape
curl_easy_upkeep
diff --git a/libs/libcurl/src/llist.c b/libs/libcurl/src/llist.c
index 3d57779338..fa9fe1d556 100644
--- a/libs/libcurl/src/llist.c
+++ b/libs/libcurl/src/llist.c
@@ -32,12 +32,11 @@
/* this must be the last include file */
#include "memdebug.h"
+#ifdef DEBUGBUILD
#define LLISTINIT 0x100cc001 /* random pattern */
#define NODEINIT 0x12344321 /* random pattern */
#define NODEREM 0x54321012 /* random pattern */
-
-#ifdef DEBUGBUILD
#define VERIFYNODE(x) verifynode(x)
static struct Curl_llist_node *verifynode(struct Curl_llist_node *n)
{
@@ -86,7 +85,7 @@ Curl_llist_insert_next(struct Curl_llist *list,
#ifdef DEBUGBUILD
ne->_init = NODEINIT;
#endif
- ne->_ptr = (void *) p;
+ ne->_ptr = CURL_UNCONST(p);
ne->_list = list;
if(list->_size == 0) {
list->_head = ne;
@@ -134,40 +133,38 @@ Curl_llist_append(struct Curl_llist *list, const void *p,
Curl_llist_insert_next(list, list->_tail, p, ne);
}
-/*
- * @unittest: 1300
- */
-void
-Curl_node_uremove(struct Curl_llist_node *e, void *user)
+void *Curl_node_take_elem(struct Curl_llist_node *e)
{
void *ptr;
struct Curl_llist *list;
if(!e)
- return;
+ return NULL;
list = e->_list;
DEBUGASSERT(list);
DEBUGASSERT(list->_init == LLISTINIT);
DEBUGASSERT(list->_size);
DEBUGASSERT(e->_init == NODEINIT);
- if(e == list->_head) {
- list->_head = e->_next;
+ if(list) {
+ if(e == list->_head) {
+ list->_head = e->_next;
- if(!list->_head)
- list->_tail = NULL;
- else
- e->_next->_prev = NULL;
- }
- else {
- if(e->_prev)
- e->_prev->_next = e->_next;
+ if(!list->_head)
+ list->_tail = NULL;
+ else
+ e->_next->_prev = NULL;
+ }
+ else {
+ if(e->_prev)
+ e->_prev->_next = e->_next;
- if(!e->_next)
- list->_tail = e->_prev;
- else
- e->_next->_prev = e->_prev;
+ if(!e->_next)
+ list->_tail = e->_prev;
+ else
+ e->_next->_prev = e->_prev;
+ }
+ --list->_size;
}
-
ptr = e->_ptr;
e->_list = NULL;
@@ -178,11 +175,27 @@ Curl_node_uremove(struct Curl_llist_node *e, void *user)
e->_init = NODEREM; /* specific pattern on remove - not zero */
#endif
- --list->_size;
+ return ptr;
+}
+
+/*
+ * @unittest: 1300
+ */
+void
+Curl_node_uremove(struct Curl_llist_node *e, void *user)
+{
+ struct Curl_llist *list;
+ void *ptr;
+ if(!e)
+ return;
- /* call the dtor() last for when it actually frees the 'e' memory itself */
- if(list->_dtor)
- list->_dtor(user, ptr);
+ list = e->_list;
+ DEBUGASSERT(list);
+ if(list) {
+ ptr = Curl_node_take_elem(e);
+ if(list->_dtor)
+ list->_dtor(user, ptr);
+ }
}
void Curl_node_remove(struct Curl_llist_node *e)
diff --git a/libs/libcurl/src/llist.h b/libs/libcurl/src/llist.h
index 3f44090341..776f9cae87 100644
--- a/libs/libcurl/src/llist.h
+++ b/libs/libcurl/src/llist.h
@@ -75,6 +75,10 @@ size_t Curl_llist_count(struct Curl_llist *list);
/* Curl_node_elem() returns the custom data from a Curl_llist_node */
void *Curl_node_elem(struct Curl_llist_node *n);
+/* Remove the node from the list and return the custom data
+ * from a Curl_llist_node. Will NOT incoke a registered `dtor`. */
+void *Curl_node_take_elem(struct Curl_llist_node *);
+
/* Curl_node_next() returns the next element in a list from a given
Curl_llist_node */
struct Curl_llist_node *Curl_node_next(struct Curl_llist_node *n);
diff --git a/libs/libcurl/src/macos.c b/libs/libcurl/src/macos.c
index f9396e0360..02024329bf 100644
--- a/libs/libcurl/src/macos.c
+++ b/libs/libcurl/src/macos.c
@@ -37,7 +37,7 @@ CURLcode Curl_macos_init(void)
/*
* The automagic conversion from IPv4 literals to IPv6 literals only
* works if the SCDynamicStoreCopyProxies system function gets called
- * first. As Curl currently does not support system-wide HTTP proxies, we
+ * first. As curl currently does not support system-wide HTTP proxies, we
* therefore do not use any value this function might return.
*
* This function is only available on macOS and is not needed for
diff --git a/libs/libcurl/src/md4.c b/libs/libcurl/src/md4.c
index cbeaa0ff55..cae64ae332 100644
--- a/libs/libcurl/src/md4.c
+++ b/libs/libcurl/src/md4.c
@@ -30,7 +30,7 @@
#include "strdup.h"
#include "curl_md4.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
#ifdef USE_OPENSSL
#include <openssl/opensslv.h>
@@ -115,6 +115,13 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx)
#elif defined(USE_WOLFSSL) && !defined(WOLFSSL_NO_MD4)
+#ifdef OPENSSL_COEXIST
+ #define MD4_CTX WOLFSSL_MD4_CTX
+ #define MD4_Init wolfSSL_MD4_Init
+ #define MD4_Update wolfSSL_MD4_Update
+ #define MD4_Final wolfSSL_MD4_Final
+#endif
+
#elif defined(USE_OPENSSL) && !defined(OPENSSL_NO_MD4)
#elif defined(AN_APPLE_OS)
@@ -163,7 +170,12 @@ static int MD4_Init(MD4_CTX *ctx)
static void MD4_Update(MD4_CTX *ctx, const void *data, unsigned long size)
{
- CryptHashData(ctx->hHash, (BYTE *)data, (unsigned int) size, 0);
+#ifdef __MINGW32CE__
+ CryptHashData(ctx->hHash, (BYTE *)CURL_UNCONST(data),
+ (unsigned int) size, 0);
+#else
+ CryptHashData(ctx->hHash, (const BYTE *)data, (unsigned int) size, 0);
+#endif
}
static void MD4_Final(unsigned char *result, MD4_CTX *ctx)
@@ -301,16 +313,16 @@ static void MD4_Final(unsigned char *result, MD4_CTX *ctx);
*/
#if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
#define MD4_SET(n) \
- (*(MD4_u32plus *)(void *)&ptr[(n) * 4])
+ (*(const MD4_u32plus *)(const void *)&ptr[(n) * 4])
#define MD4_GET(n) \
MD4_SET(n)
#else
#define MD4_SET(n) \
(ctx->block[(n)] = \
- (MD4_u32plus)ptr[(n) * 4] | \
- ((MD4_u32plus)ptr[(n) * 4 + 1] << 8) | \
- ((MD4_u32plus)ptr[(n) * 4 + 2] << 16) | \
- ((MD4_u32plus)ptr[(n) * 4 + 3] << 24))
+ (MD4_u32plus)ptr[(n) * 4] | \
+ ((MD4_u32plus)ptr[(n) * 4 + 1] << 8) | \
+ ((MD4_u32plus)ptr[(n) * 4 + 2] << 16) | \
+ ((MD4_u32plus)ptr[(n) * 4 + 3] << 24))
#define MD4_GET(n) \
(ctx->block[(n)])
#endif
diff --git a/libs/libcurl/src/md5.c b/libs/libcurl/src/md5.c
index 2f7f913ad6..ba67184d05 100644
--- a/libs/libcurl/src/md5.c
+++ b/libs/libcurl/src/md5.c
@@ -32,7 +32,7 @@
#include "curl_md5.h"
#include "curl_hmac.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
#ifdef USE_MBEDTLS
#include <mbedtls/version.h>
@@ -106,7 +106,8 @@ static void my_md5_final(unsigned char *digest, void *ctx)
md5_digest(ctx, 16, digest);
}
-#elif defined(USE_OPENSSL_MD5) || defined(USE_WOLFSSL_MD5)
+#elif defined(USE_OPENSSL_MD5) || \
+ (defined(USE_WOLFSSL_MD5) && !defined(OPENSSL_COEXIST))
typedef MD5_CTX my_md5_ctx;
@@ -130,6 +131,30 @@ static void my_md5_final(unsigned char *digest, void *ctx)
(void)MD5_Final(digest, ctx);
}
+#elif defined(USE_WOLFSSL_MD5)
+
+typedef WOLFSSL_MD5_CTX my_md5_ctx;
+
+static CURLcode my_md5_init(void *ctx)
+{
+ if(!wolfSSL_MD5_Init(ctx))
+ return CURLE_OUT_OF_MEMORY;
+
+ return CURLE_OK;
+}
+
+static void my_md5_update(void *ctx,
+ const unsigned char *input,
+ unsigned int len)
+{
+ (void)wolfSSL_MD5_Update(ctx, input, len);
+}
+
+static void my_md5_final(unsigned char *digest, void *ctx)
+{
+ (void)wolfSSL_MD5_Final(digest, ctx);
+}
+
#elif defined(USE_MBEDTLS)
typedef mbedtls_md5_context my_md5_ctx;
@@ -227,7 +252,11 @@ static void my_md5_update(void *in,
unsigned int inputLen)
{
my_md5_ctx *ctx = in;
- CryptHashData(ctx->hHash, (unsigned char *)input, inputLen, 0);
+#ifdef __MINGW32CE__
+ CryptHashData(ctx->hHash, (BYTE *)CURL_UNCONST(input), inputLen, 0);
+#else
+ CryptHashData(ctx->hHash, (const BYTE *)input, inputLen, 0);
+#endif
}
static void my_md5_final(unsigned char *digest, void *in)
@@ -331,7 +360,7 @@ static void my_md5_final(unsigned char *result, void *ctx);
*/
#if defined(__i386__) || defined(__x86_64__) || defined(__vax__)
#define MD5_SET(n) \
- (*(MD5_u32plus *)(void *)&ptr[(n) * 4])
+ (*(const MD5_u32plus *)(const void *)&ptr[(n) * 4])
#define MD5_GET(n) \
MD5_SET(n)
#else
diff --git a/libs/libcurl/src/memdebug.c b/libs/libcurl/src/memdebug.c
index d31658c537..0f09683f58 100644
--- a/libs/libcurl/src/memdebug.c
+++ b/libs/libcurl/src/memdebug.c
@@ -117,20 +117,19 @@ static bool countcheck(const char *func, int line, const char *source)
fprintf(stderr, "LIMIT %s:%d %s reached memlimit\n",
source, line, func);
fflush(curl_dbg_logfile); /* because it might crash now */
- errno = ENOMEM;
+ /* !checksrc! disable ERRNOVAR 1 */
+ CURL_SETERRNO(ENOMEM);
return TRUE; /* RETURN ERROR! */
}
else
memsize--; /* countdown */
-
-
}
return FALSE; /* allow this */
}
-ALLOC_FUNC void *curl_dbg_malloc(size_t wantedsize,
- int line, const char *source)
+ALLOC_FUNC
+void *curl_dbg_malloc(size_t wantedsize, int line, const char *source)
{
struct memdebug *mem;
size_t size;
@@ -153,11 +152,12 @@ ALLOC_FUNC void *curl_dbg_malloc(size_t wantedsize,
source, line, wantedsize,
mem ? (void *)mem->mem : (void *)0);
- return (mem ? mem->mem : NULL);
+ return mem ? mem->mem : NULL;
}
-ALLOC_FUNC void *curl_dbg_calloc(size_t wanted_elements, size_t wanted_size,
- int line, const char *source)
+ALLOC_FUNC
+void *curl_dbg_calloc(size_t wanted_elements, size_t wanted_size,
+ int line, const char *source)
{
struct memdebug *mem;
size_t size, user_size;
@@ -181,11 +181,11 @@ ALLOC_FUNC void *curl_dbg_calloc(size_t wanted_elements, size_t wanted_size,
source, line, wanted_elements, wanted_size,
mem ? (void *)mem->mem : (void *)0);
- return (mem ? mem->mem : NULL);
+ return mem ? mem->mem : NULL;
}
-ALLOC_FUNC char *curl_dbg_strdup(const char *str,
- int line, const char *source)
+ALLOC_FUNC
+char *curl_dbg_strdup(const char *str, int line, const char *source)
{
char *mem;
size_t len;
@@ -209,8 +209,8 @@ ALLOC_FUNC char *curl_dbg_strdup(const char *str,
}
#if defined(_WIN32) && defined(UNICODE)
-ALLOC_FUNC wchar_t *curl_dbg_wcsdup(const wchar_t *str,
- int line, const char *source)
+ALLOC_FUNC
+wchar_t *curl_dbg_wcsdup(const wchar_t *str, int line, const char *source)
{
wchar_t *mem;
size_t wsiz, bsiz;
@@ -229,7 +229,7 @@ ALLOC_FUNC wchar_t *curl_dbg_wcsdup(const wchar_t *str,
if(source)
curl_dbg_log("MEM %s:%d wcsdup(%p) (%zu) = %p\n",
- source, line, (void *)str, bsiz, (void *)mem);
+ source, line, (const void *)str, bsiz, (void *)mem);
return mem;
}
@@ -238,7 +238,7 @@ ALLOC_FUNC wchar_t *curl_dbg_wcsdup(const wchar_t *str,
/* We provide a realloc() that accepts a NULL as pointer, which then
performs a malloc(). In order to work with ares. */
void *curl_dbg_realloc(void *ptr, size_t wantedsize,
- int line, const char *source)
+ int line, const char *source)
{
struct memdebug *mem = NULL;
@@ -378,6 +378,24 @@ curl_socket_t curl_dbg_accept(curl_socket_t s, void *saddr, void *saddrlen,
return sockfd;
}
+#ifdef HAVE_ACCEPT4
+curl_socket_t curl_dbg_accept4(curl_socket_t s, void *saddr, void *saddrlen,
+ int flags,
+ int line, const char *source)
+{
+ struct sockaddr *addr = (struct sockaddr *)saddr;
+ curl_socklen_t *addrlen = (curl_socklen_t *)saddrlen;
+
+ curl_socket_t sockfd = accept4(s, addr, addrlen, flags);
+
+ if(source && (sockfd != CURL_SOCKET_BAD))
+ curl_dbg_log("FD %s:%d accept() = %" FMT_SOCKET_T "\n",
+ source, line, sockfd);
+
+ return sockfd;
+}
+#endif
+
/* separate function to allow libcurl to mark a "faked" close */
void curl_dbg_mark_sclose(curl_socket_t sockfd, int line, const char *source)
{
@@ -394,8 +412,9 @@ int curl_dbg_sclose(curl_socket_t sockfd, int line, const char *source)
return res;
}
-ALLOC_FUNC FILE *curl_dbg_fopen(const char *file, const char *mode,
- int line, const char *source)
+ALLOC_FUNC
+FILE *curl_dbg_fopen(const char *file, const char *mode,
+ int line, const char *source)
{
FILE *res = fopen(file, mode);
@@ -406,8 +425,9 @@ ALLOC_FUNC FILE *curl_dbg_fopen(const char *file, const char *mode,
return res;
}
-ALLOC_FUNC FILE *curl_dbg_fdopen(int filedes, const char *mode,
- int line, const char *source)
+ALLOC_FUNC
+FILE *curl_dbg_fdopen(int filedes, const char *mode,
+ int line, const char *source)
{
FILE *res = fdopen(filedes, mode);
if(source)
@@ -431,33 +451,25 @@ int curl_dbg_fclose(FILE *file, int line, const char *source)
return res;
}
-#define LOGLINE_BUFSIZE 1024
-
/* this does the writing to the memory tracking log file */
void curl_dbg_log(const char *format, ...)
{
- char *buf;
+ char buf[1024];
int nchars;
va_list ap;
if(!curl_dbg_logfile)
return;
- buf = (Curl_cmalloc)(LOGLINE_BUFSIZE);
- if(!buf)
- return;
-
va_start(ap, format);
- nchars = mvsnprintf(buf, LOGLINE_BUFSIZE, format, ap);
+ nchars = mvsnprintf(buf, sizeof(buf), format, ap);
va_end(ap);
- if(nchars > LOGLINE_BUFSIZE - 1)
- nchars = LOGLINE_BUFSIZE - 1;
+ if(nchars > (int)sizeof(buf) - 1)
+ nchars = (int)sizeof(buf) - 1;
if(nchars > 0)
fwrite(buf, 1, (size_t)nchars, curl_dbg_logfile);
-
- (Curl_cfree)(buf);
}
#endif /* CURLDEBUG */
diff --git a/libs/libcurl/src/memdebug.h b/libs/libcurl/src/memdebug.h
index f33acfb657..de7fbd0cb0 100644
--- a/libs/libcurl/src/memdebug.h
+++ b/libs/libcurl/src/memdebug.h
@@ -33,12 +33,21 @@
#include <curl/curl.h>
#include "functypes.h"
-#if defined(__GNUC__) && __GNUC__ >= 3
-# define ALLOC_FUNC __attribute__((__malloc__))
-# define ALLOC_SIZE(s) __attribute__((__alloc_size__(s)))
-# define ALLOC_SIZE2(n, s) __attribute__((__alloc_size__(n, s)))
+#ifdef __clang__
+# define ALLOC_FUNC __attribute__((__malloc__))
+# if __clang_major__ >= 4
+# define ALLOC_SIZE(s) __attribute__((__alloc_size__(s)))
+# define ALLOC_SIZE2(n, s) __attribute__((__alloc_size__(n, s)))
+# else
+# define ALLOC_SIZE(s)
+# define ALLOC_SIZE2(n, s)
+# endif
+#elif defined(__GNUC__) && __GNUC__ >= 3
+# define ALLOC_FUNC __attribute__((__malloc__))
+# define ALLOC_SIZE(s) __attribute__((__alloc_size__(s)))
+# define ALLOC_SIZE2(n, s) __attribute__((__alloc_size__(n, s)))
#elif defined(_MSC_VER)
-# define ALLOC_FUNC __declspec(restrict)
+# define ALLOC_FUNC __declspec(restrict)
# define ALLOC_SIZE(s)
# define ALLOC_SIZE2(n, s)
#else
@@ -49,25 +58,25 @@
#define CURL_MT_LOGFNAME_BUFSIZE 512
+/* Avoid redundant redeclaration warnings with modern compilers, when including
+ this header multiple times. */
+#ifndef HEADER_CURL_MEMDEBUG_H_EXTERNS
+#define HEADER_CURL_MEMDEBUG_H_EXTERNS
extern FILE *curl_dbg_logfile;
/* memory functions */
-CURL_EXTERN ALLOC_FUNC ALLOC_SIZE(1) void *curl_dbg_malloc(size_t size,
- int line,
- const char *source);
-CURL_EXTERN ALLOC_FUNC ALLOC_SIZE2(1, 2) void *curl_dbg_calloc(size_t elements,
- size_t size, int line, const char *source);
-CURL_EXTERN ALLOC_SIZE(2) void *curl_dbg_realloc(void *ptr,
- size_t size,
- int line,
- const char *source);
CURL_EXTERN void curl_dbg_free(void *ptr, int line, const char *source);
-CURL_EXTERN ALLOC_FUNC char *curl_dbg_strdup(const char *str, int line,
- const char *src);
+CURL_EXTERN ALLOC_FUNC ALLOC_SIZE(1)
+ void *curl_dbg_malloc(size_t size, int line, const char *source);
+CURL_EXTERN ALLOC_FUNC ALLOC_SIZE2(1, 2)
+ void *curl_dbg_calloc(size_t n, size_t size, int line, const char *source);
+CURL_EXTERN ALLOC_SIZE(2)
+ void *curl_dbg_realloc(void *ptr, size_t size, int line, const char *source);
+CURL_EXTERN ALLOC_FUNC
+ char *curl_dbg_strdup(const char *str, int line, const char *src);
#if defined(_WIN32) && defined(UNICODE)
-CURL_EXTERN ALLOC_FUNC wchar_t *curl_dbg_wcsdup(const wchar_t *str,
- int line,
- const char *source);
+CURL_EXTERN ALLOC_FUNC
+ wchar_t *curl_dbg_wcsdup(const wchar_t *str, int line, const char *source);
#endif
CURL_EXTERN void curl_dbg_memdebug(const char *logname);
@@ -83,6 +92,11 @@ CURL_EXTERN int curl_dbg_sclose(curl_socket_t sockfd,
int line, const char *source);
CURL_EXTERN curl_socket_t curl_dbg_accept(curl_socket_t s, void *a, void *alen,
int line, const char *source);
+#ifdef HAVE_ACCEPT4
+CURL_EXTERN curl_socket_t curl_dbg_accept4(curl_socket_t s, void *saddr,
+ void *saddrlen, int flags,
+ int line, const char *source);
+#endif
#ifdef HAVE_SOCKETPAIR
CURL_EXTERN int curl_dbg_socketpair(int domain, int type, int protocol,
curl_socket_t socket_vector[2],
@@ -102,12 +116,15 @@ CURL_EXTERN RECV_TYPE_RETV curl_dbg_recv(RECV_TYPE_ARG1 sockfd,
const char *source);
/* FILE functions */
-CURL_EXTERN ALLOC_FUNC FILE *curl_dbg_fopen(const char *file, const char *mode,
- int line, const char *source);
-CURL_EXTERN ALLOC_FUNC FILE *curl_dbg_fdopen(int filedes, const char *mode,
- int line, const char *source);
-
CURL_EXTERN int curl_dbg_fclose(FILE *file, int line, const char *source);
+CURL_EXTERN ALLOC_FUNC
+ FILE *curl_dbg_fopen(const char *file, const char *mode,
+ int line, const char *source);
+CURL_EXTERN ALLOC_FUNC
+ FILE *curl_dbg_fdopen(int filedes, const char *mode,
+ int line, const char *source);
+
+#endif /* HEADER_CURL_MEMDEBUG_H_EXTERNS */
#ifndef MEMDEBUG_NODEFINES
@@ -147,34 +164,17 @@ CURL_EXTERN int curl_dbg_fclose(FILE *file, int line, const char *source);
#undef accept /* for those with accept as a macro */
#define accept(sock,addr,len)\
curl_dbg_accept(sock, addr, len, __LINE__, __FILE__)
+#ifdef HAVE_ACCEPT4
+#undef accept4 /* for those with accept4 as a macro */
+#define accept4(sock,addr,len,flags)\
+ curl_dbg_accept4(sock, addr, len, flags, __LINE__, __FILE__)
+#endif
#ifdef HAVE_SOCKETPAIR
#define socketpair(domain,type,protocol,socket_vector)\
curl_dbg_socketpair((int)domain, type, protocol, socket_vector, \
__LINE__, __FILE__)
#endif
-#ifndef CURL_NO_GETADDRINFO_OVERRIDE
-#ifdef HAVE_GETADDRINFO
-#if defined(getaddrinfo) && defined(__osf__)
-/* OSF/1 and Tru64 have getaddrinfo as a define already, so we cannot define
- our macro as for other platforms. Instead, we redefine the new name they
- define getaddrinfo to become! */
-#define ogetaddrinfo(host,serv,hint,res) \
- curl_dbg_getaddrinfo(host, serv, hint, res, __LINE__, __FILE__)
-#else
-#undef getaddrinfo
-#define getaddrinfo(host,serv,hint,res) \
- curl_dbg_getaddrinfo(host, serv, hint, res, __LINE__, __FILE__)
-#endif
-#endif /* HAVE_GETADDRINFO */
-
-#ifdef HAVE_FREEADDRINFO
-#undef freeaddrinfo
-#define freeaddrinfo(data) \
- curl_dbg_freeaddrinfo(data, __LINE__, __FILE__)
-#endif /* HAVE_FREEADDRINFO */
-#endif /* !CURL_NO_GETADDRINFO_OVERRIDE */
-
/* sclose is probably already defined, redefine it! */
#undef sclose
#define sclose(sockfd) curl_dbg_sclose(sockfd,__LINE__,__FILE__)
diff --git a/libs/libcurl/src/mime.c b/libs/libcurl/src/mime.c
index a07449888d..35200a059b 100644
--- a/libs/libcurl/src/mime.c
+++ b/libs/libcurl/src/mime.c
@@ -29,10 +29,11 @@
struct Curl_easy;
#include "mime.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
#include "urldata.h"
#include "sendf.h"
#include "strdup.h"
+#include "curlx/base64.h"
#if !defined(CURL_DISABLE_MIME) && (!defined(CURL_DISABLE_HTTP) || \
!defined(CURL_DISABLE_SMTP) || \
@@ -45,7 +46,7 @@ struct Curl_easy;
#include "rand.h"
#include "slist.h"
#include "strcase.h"
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
@@ -87,10 +88,6 @@ static const struct mime_encoder encoders[] = {
{ZERO_NULL, ZERO_NULL, ZERO_NULL}
};
-/* Base64 encoding table */
-static const char base64enc[] =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-
/* Quoted-printable character class table.
*
* We cannot rely on ctype functions since quoted-printable input data
@@ -318,19 +315,19 @@ static char *escape_string(struct Curl_easy *data,
if(strategy == MIMESTRATEGY_MAIL || (data && (data->set.mime_formescape)))
table = mimetable;
- Curl_dyn_init(&db, CURL_MAX_INPUT_LENGTH);
+ curlx_dyn_init(&db, CURL_MAX_INPUT_LENGTH);
- for(result = Curl_dyn_addn(&db, STRCONST("")); !result && *src; src++) {
+ for(result = curlx_dyn_addn(&db, STRCONST("")); !result && *src; src++) {
for(p = table; *p && **p != *src; p++)
;
if(*p)
- result = Curl_dyn_add(&db, *p + 1);
+ result = curlx_dyn_add(&db, *p + 1);
else
- result = Curl_dyn_addn(&db, src, 1);
+ result = curlx_dyn_addn(&db, src, 1);
}
- return Curl_dyn_ptr(&db);
+ return curlx_dyn_ptr(&db);
}
/* Check if header matches. */
@@ -472,10 +469,10 @@ static size_t encoder_base64_read(char *buffer, size_t size, bool ateof,
i = st->buf[st->bufbeg++] & 0xFF;
i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF);
i = (i << 8) | (st->buf[st->bufbeg++] & 0xFF);
- *ptr++ = base64enc[(i >> 18) & 0x3F];
- *ptr++ = base64enc[(i >> 12) & 0x3F];
- *ptr++ = base64enc[(i >> 6) & 0x3F];
- *ptr++ = base64enc[i & 0x3F];
+ *ptr++ = Curl_base64encdec[(i >> 18) & 0x3F];
+ *ptr++ = Curl_base64encdec[(i >> 12) & 0x3F];
+ *ptr++ = Curl_base64encdec[(i >> 6) & 0x3F];
+ *ptr++ = Curl_base64encdec[i & 0x3F];
cursize += 4;
st->pos += 4;
size -= 4;
@@ -499,10 +496,10 @@ static size_t encoder_base64_read(char *buffer, size_t size, bool ateof,
i = (st->buf[st->bufbeg + 1] & 0xFF) << 8;
i |= (st->buf[st->bufbeg] & 0xFF) << 16;
- ptr[0] = base64enc[(i >> 18) & 0x3F];
- ptr[1] = base64enc[(i >> 12) & 0x3F];
+ ptr[0] = Curl_base64encdec[(i >> 18) & 0x3F];
+ ptr[1] = Curl_base64encdec[(i >> 12) & 0x3F];
if(++st->bufbeg != st->bufend) {
- ptr[2] = base64enc[(i >> 6) & 0x3F];
+ ptr[2] = Curl_base64encdec[(i >> 6) & 0x3F];
st->bufbeg++;
}
cursize += 4;
@@ -1561,6 +1558,14 @@ CURLcode Curl_mime_set_subparts(curl_mimepart *part,
}
}
+ /* If subparts have already been used as a top-level MIMEPOST,
+ they might not be positioned at start. Rewind them now, as
+ a future check while rewinding the parent may cause this
+ content to be skipped. */
+ if(mime_subparts_seek(subparts, (curl_off_t) 0, SEEK_SET) !=
+ CURL_SEEKFUNC_OK)
+ return CURLE_SEND_FAIL_REWIND;
+
subparts->parent = part;
/* Subparts are processed internally: no read callback. */
part->seekfunc = mime_subparts_seek;
@@ -1590,8 +1595,8 @@ size_t Curl_mime_read(char *buffer, size_t size, size_t nitems, void *instream)
(void) size; /* Always 1. */
- /* TODO: this loop is broken. If `nitems` is <= 4, some encoders will
- * return STOP_FILLING without adding any data and this loops infinitely. */
+ /* If `nitems` is <= 4, some encoders will return STOP_FILLING without
+ * adding any data and this loops infinitely. */
do {
hasread = FALSE;
ret = readback_part(part, buffer, nitems, &hasread);
@@ -1734,7 +1739,7 @@ const char *Curl_mime_contenttype(const char *filename)
const char *nameend = filename + len1;
unsigned int i;
- for(i = 0; i < sizeof(ctts) / sizeof(ctts[0]); i++) {
+ for(i = 0; i < CURL_ARRAYSIZE(ctts); i++) {
size_t len2 = strlen(ctts[i].extension);
if(len1 >= len2 && strcasecompare(nameend - len2, ctts[i].extension))
@@ -1926,6 +1931,7 @@ struct cr_mime_ctx {
curl_off_t total_len;
curl_off_t read_len;
CURLcode error_result;
+ struct bufq tmpbuf;
BIT(seen_eos);
BIT(errored);
};
@@ -1937,9 +1943,18 @@ static CURLcode cr_mime_init(struct Curl_easy *data,
(void)data;
ctx->total_len = -1;
ctx->read_len = 0;
+ Curl_bufq_init2(&ctx->tmpbuf, 1024, 1, BUFQ_OPT_NO_SPARES);
return CURLE_OK;
}
+static void cr_mime_close(struct Curl_easy *data,
+ struct Curl_creader *reader)
+{
+ struct cr_mime_ctx *ctx = reader->ctx;
+ (void)data;
+ Curl_bufq_free(&ctx->tmpbuf);
+}
+
/* Real client reader to installed client callbacks. */
static CURLcode cr_mime_read(struct Curl_easy *data,
struct Curl_creader *reader,
@@ -1948,6 +1963,7 @@ static CURLcode cr_mime_read(struct Curl_easy *data,
{
struct cr_mime_ctx *ctx = reader->ctx;
size_t nread;
+ char tmp[256];
/* Once we have errored, we will return the same error forever */
@@ -1973,18 +1989,46 @@ static CURLcode cr_mime_read(struct Curl_easy *data,
blen = (size_t)remain;
}
- if(blen <= 4) {
- /* TODO: Curl_mime_read() may go into an infinite loop when reading
- * such small lengths. Returning 0 bytes read is a fix that only works
- * as request upload buffers will get flushed eventually and larger
- * reads will happen again. */
- CURL_TRC_READ(data, "cr_mime_read(len=%zu), too small, return", blen);
- *pnread = 0;
- *peos = FALSE;
- goto out;
+ if(!Curl_bufq_is_empty(&ctx->tmpbuf)) {
+ CURLcode result = CURLE_OK;
+ ssize_t n = Curl_bufq_read(&ctx->tmpbuf, (unsigned char *)buf, blen,
+ &result);
+ if(n < 0) {
+ ctx->errored = TRUE;
+ ctx->error_result = result;
+ return result;
+ }
+ nread = (size_t)n;
+ }
+ else if(blen <= 4) {
+ /* Curl_mime_read() may go into an infinite loop when reading
+ * via a base64 encoder, as it stalls when the read buffer is too small
+ * to contain a complete 3 byte encoding. Read into a larger buffer
+ * and use that until empty. */
+ CURL_TRC_READ(data, "cr_mime_read(len=%zu), small read, using tmp", blen);
+ nread = Curl_mime_read(tmp, 1, sizeof(tmp), ctx->part);
+ if(nread <= sizeof(tmp)) {
+ CURLcode result = CURLE_OK;
+ ssize_t n = Curl_bufq_write(&ctx->tmpbuf, (unsigned char *)tmp, nread,
+ &result);
+ if(n < 0) {
+ ctx->errored = TRUE;
+ ctx->error_result = result;
+ return result;
+ }
+ /* stored it, read again */
+ n = Curl_bufq_read(&ctx->tmpbuf, (unsigned char *)buf, blen, &result);
+ if(n < 0) {
+ ctx->errored = TRUE;
+ ctx->error_result = result;
+ return result;
+ }
+ nread = (size_t)n;
+ }
}
+ else
+ nread = Curl_mime_read(buf, 1, blen, ctx->part);
- nread = Curl_mime_read(buf, 1, blen, ctx->part);
CURL_TRC_READ(data, "cr_mime_read(len=%zu), mime_read() -> %zd",
blen, nread);
@@ -2044,7 +2088,6 @@ static CURLcode cr_mime_read(struct Curl_easy *data,
break;
}
-out:
CURL_TRC_READ(data, "cr_mime_read(len=%zu, total=%" FMT_OFF_T
", read=%"FMT_OFF_T") -> %d, %zu, %d",
blen, ctx->total_len, ctx->read_len, CURLE_OK, *pnread, *peos);
@@ -2133,14 +2176,14 @@ static bool cr_mime_is_paused(struct Curl_easy *data,
{
struct cr_mime_ctx *ctx = reader->ctx;
(void)data;
- return (ctx->part && ctx->part->lastreadstatus == CURL_READFUNC_PAUSE);
+ return ctx->part && ctx->part->lastreadstatus == CURL_READFUNC_PAUSE;
}
static const struct Curl_crtype cr_mime = {
"cr-mime",
cr_mime_init,
cr_mime_read,
- Curl_creader_def_close,
+ cr_mime_close,
cr_mime_needs_rewind,
cr_mime_total_length,
cr_mime_resume_from,
diff --git a/libs/libcurl/src/mprintf.c b/libs/libcurl/src/mprintf.c
index 2e4a2580a3..2bf9b37571 100644
--- a/libs/libcurl/src/mprintf.c
+++ b/libs/libcurl/src/mprintf.c
@@ -23,28 +23,23 @@
*/
#include "curl_setup.h"
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
#include "curl_printf.h"
+#include "curlx/strparse.h"
#include "curl_memory.h"
/* The last #include file should be: */
#include "memdebug.h"
-/*
- * If SIZEOF_SIZE_T has not been defined, default to the size of long.
- */
-
#ifdef HAVE_LONGLONG
# define LONG_LONG_TYPE long long
# define HAVE_LONG_LONG_TYPE
+#elif defined(_MSC_VER)
+# define LONG_LONG_TYPE __int64
+# define HAVE_LONG_LONG_TYPE
#else
-# if defined(_MSC_VER) && (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64)
-# define LONG_LONG_TYPE __int64
-# define HAVE_LONG_LONG_TYPE
-# else
-# undef LONG_LONG_TYPE
-# undef HAVE_LONG_LONG_TYPE
-# endif
+# undef LONG_LONG_TYPE
+# undef HAVE_LONG_LONG_TYPE
#endif
/*
@@ -69,17 +64,16 @@
#endif
/* Lower-case digits. */
-static const char lower_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+const unsigned char Curl_ldigits[] = "0123456789abcdef";
/* Upper-case digits. */
-static const char upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-
-#define OUTCHAR(x) \
- do { \
- if(!stream((unsigned char)x, userp)) \
- done++; \
- else \
- return done; /* return on failure */ \
+const unsigned char Curl_udigits[] = "0123456789ABCDEF";
+
+#define OUTCHAR(x) \
+ do { \
+ if(stream((unsigned char)x, userp)) \
+ return TRUE; \
+ (*donep)++; \
} while(0)
/* Data type to read from the arglist */
@@ -136,7 +130,7 @@ enum {
struct va_input {
FormatType type; /* FormatType */
union {
- char *str;
+ const char *str;
void *ptr;
mp_intmax_t nums; /* signed */
mp_uintmax_t numu; /* unsigned */
@@ -152,7 +146,7 @@ struct outsegment {
int precision; /* precision OR precision parameter number */
unsigned int flags;
unsigned int input; /* input argument array index */
- char *start; /* format string start to output */
+ const char *start; /* format string start to output */
size_t outlen; /* number of bytes from the format string to output */
};
@@ -171,26 +165,19 @@ struct asprintf {
returns -1 if no valid number was provided.
*/
-static int dollarstring(char *input, char **end)
+static int dollarstring(const char *p, const char **end)
{
- if(ISDIGIT(*input)) {
- int number = 0;
- do {
- if(number < MAX_PARAMETERS) {
- number *= 10;
- number += *input - '0';
- }
- input++;
- } while(ISDIGIT(*input));
-
- if(number && (number <= MAX_PARAMETERS) && ('$' == *input)) {
- *end = ++input;
- return number - 1;
- }
- }
- return -1;
+ curl_off_t num;
+ if(curlx_str_number(&p, &num, MAX_PARAMETERS) ||
+ curlx_str_single(&p, '$') || !num)
+ return -1;
+ *end = p;
+ return (int)num - 1;
}
+#define is_arg_used(x,y) ((x)[(y)/8] & (1 << ((y)&7)))
+#define mark_arg_used(x,y) ((x)[y/8] |= (unsigned char)(1 << ((y)&7)))
+
/*
* Parse the format string.
*
@@ -218,13 +205,8 @@ static int parsefmt(const char *format,
int *opieces,
int *ipieces, va_list arglist)
{
- char *fmt = (char *)format;
+ const char *fmt = format;
int param_num = 0;
- int param;
- int width;
- int precision;
- unsigned int flags;
- FormatType type;
int max_param = -1;
int i;
int ocount = 0;
@@ -232,7 +214,7 @@ static int parsefmt(const char *format,
size_t outlen = 0;
struct outsegment *optr;
int use_dollar = DOLLAR_UNKNOWN;
- char *start = fmt;
+ const char *start = fmt;
/* clear, set a bit for each used input */
memset(usedinput, 0, sizeof(usedinput));
@@ -241,6 +223,11 @@ static int parsefmt(const char *format,
if(*fmt == '%') {
struct va_input *iptr;
bool loopit = TRUE;
+ FormatType type;
+ unsigned int flags = 0;
+ int width = 0;
+ int precision = 0;
+ int param = -1;
fmt++;
outlen = (size_t)(fmt - start - 1);
if(*fmt == '%') {
@@ -260,9 +247,6 @@ static int parsefmt(const char *format,
continue; /* while */
}
- flags = 0;
- width = precision = 0;
-
if(use_dollar != DOLLAR_NOPE) {
param = dollarstring(fmt, &fmt);
if(param < 0) {
@@ -277,8 +261,6 @@ static int parsefmt(const char *format,
else
use_dollar = DOLLAR_USE;
}
- else
- param = -1;
/* Handle the flags */
while(loopit) {
@@ -313,20 +295,15 @@ static int parsefmt(const char *format,
precision = -1;
}
else {
- bool is_neg = FALSE;
+ bool is_neg;
+ curl_off_t num;
flags |= FLAGS_PREC;
- precision = 0;
- if('-' == *fmt) {
- is_neg = TRUE;
- fmt++;
- }
- while(ISDIGIT(*fmt)) {
- if(precision > INT_MAX/10)
- return PFMT_PREC;
- precision *= 10;
- precision += *fmt - '0';
+ is_neg = ('-' == *fmt);
+ if(is_neg)
fmt++;
- }
+ if(curlx_str_number(&fmt, &num, INT_MAX))
+ return PFMT_PREC;
+ precision = (int)num;
if(is_neg)
precision = -precision;
}
@@ -339,7 +316,7 @@ static int parsefmt(const char *format,
case 'h':
flags |= FLAGS_SHORT;
break;
-#if defined(_WIN32) || defined(_WIN32_WCE)
+#ifdef _WIN32
case 'I':
/* Non-ANSI integer extensions I32 I64 */
if((fmt[0] == '3') && (fmt[1] == '2')) {
@@ -358,7 +335,7 @@ static int parsefmt(const char *format,
#endif
}
break;
-#endif /* _WIN32 || _WIN32_WCE */
+#endif /* _WIN32 */
case 'l':
if(flags & FLAGS_LONG)
flags |= FLAGS_LONGLONG;
@@ -392,18 +369,15 @@ static int parsefmt(const char *format,
flags |= FLAGS_PAD_NIL;
FALLTHROUGH();
case '1': case '2': case '3': case '4':
- case '5': case '6': case '7': case '8': case '9':
+ case '5': case '6': case '7': case '8': case '9': {
+ curl_off_t num;
flags |= FLAGS_WIDTH;
- width = 0;
fmt--;
- do {
- if(width > INT_MAX/10)
- return PFMT_WIDTH;
- width *= 10;
- width += *fmt - '0';
- fmt++;
- } while(ISDIGIT(*fmt));
+ if(curlx_str_number(&fmt, &num, INT_MAX))
+ return PFMT_WIDTH;
+ width = (int)num;
break;
+ }
case '*': /* read width from argument list */
flags |= FLAGS_WIDTHPARAM;
if(use_dollar == DOLLAR_USE) {
@@ -513,9 +487,8 @@ static int parsefmt(const char *format,
if(width < 0)
width = param_num++;
else {
- /* if this identifies a parameter already used, this
- is illegal */
- if(usedinput[width/8] & (1 << (width&7)))
+ /* if this identifies a parameter already used, this is illegal */
+ if(is_arg_used(usedinput, width))
return PFMT_WIDTHARG;
}
if(width >= MAX_PARAMETERS)
@@ -525,16 +498,15 @@ static int parsefmt(const char *format,
in[width].type = FORMAT_WIDTH;
/* mark as used */
- usedinput[width/8] |= (unsigned char)(1 << (width&7));
+ mark_arg_used(usedinput, width);
}
if(flags & FLAGS_PRECPARAM) {
if(precision < 0)
precision = param_num++;
else {
- /* if this identifies a parameter already used, this
- is illegal */
- if(usedinput[precision/8] & (1 << (precision&7)))
+ /* if this identifies a parameter already used, this is illegal */
+ if(is_arg_used(usedinput, precision))
return PFMT_PRECARG;
}
if(precision >= MAX_PARAMETERS)
@@ -543,7 +515,7 @@ static int parsefmt(const char *format,
max_param = precision;
in[precision].type = FORMAT_PRECISION;
- usedinput[precision/8] |= (unsigned char)(1 << (precision&7));
+ mark_arg_used(usedinput, precision);
}
/* Handle the specifier */
@@ -558,7 +530,7 @@ static int parsefmt(const char *format,
iptr->type = type;
/* mark this input as used */
- usedinput[param/8] |= (unsigned char)(1 << (param&7));
+ mark_arg_used(usedinput, param);
fmt++;
optr = &out[ocount++];
@@ -591,14 +563,14 @@ static int parsefmt(const char *format,
/* Read the arg list parameters into our data list */
for(i = 0; i < max_param + 1; i++) {
struct va_input *iptr = &in[i];
- if(!(usedinput[i/8] & (1 << (i&7))))
+ if(!is_arg_used(usedinput, i))
/* bad input */
return PFMT_INPUTGAP;
/* based on the type, read the correct argument */
switch(iptr->type) {
case FORMAT_STRING:
- iptr->val.str = va_arg(arglist, char *);
+ iptr->val.str = va_arg(arglist, const char *);
break;
case FORMAT_INTPTR:
@@ -647,6 +619,333 @@ static int parsefmt(const char *format,
return PFMT_OK;
}
+struct mproperty {
+ int width; /* Width of a field. */
+ int prec; /* Precision of a field. */
+ unsigned int flags;
+};
+
+static bool out_double(void *userp,
+ int (*stream)(unsigned char, void *),
+ struct mproperty *p,
+ double dnum,
+ char *work, int *donep)
+{
+ char formatbuf[32]="%";
+ char *fptr = &formatbuf[1];
+ size_t left = sizeof(formatbuf)-strlen(formatbuf);
+ int flags = p->flags;
+ int width = p->width;
+ int prec = p->prec;
+
+ if(flags & FLAGS_LEFT)
+ *fptr++ = '-';
+ if(flags & FLAGS_SHOWSIGN)
+ *fptr++ = '+';
+ if(flags & FLAGS_SPACE)
+ *fptr++ = ' ';
+ if(flags & FLAGS_ALT)
+ *fptr++ = '#';
+
+ *fptr = 0;
+
+ if(width >= 0) {
+ size_t dlen;
+ if(width >= BUFFSIZE)
+ width = BUFFSIZE - 1;
+ /* RECURSIVE USAGE */
+ dlen = (size_t)curl_msnprintf(fptr, left, "%d", width);
+ fptr += dlen;
+ left -= dlen;
+ }
+ if(prec >= 0) {
+ /* for each digit in the integer part, we can have one less
+ precision */
+ int maxprec = BUFFSIZE - 1;
+ double val = dnum;
+ int len;
+ if(prec > maxprec)
+ prec = maxprec - 1;
+ if(width > 0 && prec <= width)
+ maxprec -= width;
+ while(val >= 10.0) {
+ val /= 10;
+ maxprec--;
+ }
+
+ if(prec > maxprec)
+ prec = maxprec - 1;
+ if(prec < 0)
+ prec = 0;
+ /* RECURSIVE USAGE */
+ len = curl_msnprintf(fptr, left, ".%d", prec);
+ fptr += len;
+ }
+ if(flags & FLAGS_LONG)
+ *fptr++ = 'l';
+
+ if(flags & FLAGS_FLOATE)
+ *fptr++ = (char)((flags & FLAGS_UPPER) ? 'E' : 'e');
+ else if(flags & FLAGS_FLOATG)
+ *fptr++ = (char)((flags & FLAGS_UPPER) ? 'G' : 'g');
+ else
+ *fptr++ = 'f';
+
+ *fptr = 0; /* and a final null-termination */
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-nonliteral"
+#endif
+ /* NOTE NOTE NOTE!! Not all sprintf implementations return number of
+ output characters */
+#ifdef HAVE_SNPRINTF
+ /* !checksrc! disable LONGLINE */
+ /* NOLINTNEXTLINE(clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling) */
+ (snprintf)(work, BUFFSIZE, formatbuf, dnum);
+#ifdef _WIN32
+ /* Old versions of the Windows CRT do not terminate the snprintf output
+ buffer if it reaches the max size so we do that here. */
+ work[BUFFSIZE - 1] = 0;
+#endif
+#else
+ (sprintf)(work, formatbuf, dnum);
+#endif
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+ DEBUGASSERT(strlen(work) < BUFFSIZE);
+ while(*work) {
+ if(stream(*work++, userp))
+ return TRUE;
+ (*donep)++;
+ }
+ return 0;
+}
+
+static bool out_number(void *userp,
+ int (*stream)(unsigned char, void *),
+ struct mproperty *p,
+ mp_uintmax_t num,
+ mp_intmax_t nums,
+ char *work, int *donep)
+{
+ const unsigned char *digits = Curl_ldigits;
+ int flags = p->flags;
+ int width = p->width;
+ int prec = p->prec;
+ bool is_alt = flags & FLAGS_ALT;
+ bool is_neg = FALSE;
+ int base = 10;
+
+ /* 'workend' points to the final buffer byte position, but with an extra
+ byte as margin to avoid the (FALSE?) warning Coverity gives us
+ otherwise */
+ char *workend = &work[BUFFSIZE - 2];
+ char *w;
+
+ if(flags & FLAGS_CHAR) {
+ /* Character. */
+ if(!(flags & FLAGS_LEFT))
+ while(--width > 0)
+ OUTCHAR(' ');
+ OUTCHAR((char) num);
+ if(flags & FLAGS_LEFT)
+ while(--width > 0)
+ OUTCHAR(' ');
+ return FALSE;
+ }
+ if(flags & FLAGS_OCTAL)
+ /* Octal unsigned integer */
+ base = 8;
+
+ else if(flags & FLAGS_HEX) {
+ /* Hexadecimal unsigned integer */
+ digits = (flags & FLAGS_UPPER) ? Curl_udigits : Curl_ldigits;
+ base = 16;
+ }
+ else if(flags & FLAGS_UNSIGNED)
+ /* Decimal unsigned integer */
+ ;
+
+ else {
+ /* Decimal integer. */
+ is_neg = (nums < 0);
+ if(is_neg) {
+ /* signed_num might fail to hold absolute negative minimum by 1 */
+ mp_intmax_t signed_num; /* Used to convert negative in positive. */
+ signed_num = nums + (mp_intmax_t)1;
+ signed_num = -signed_num;
+ num = (mp_uintmax_t)signed_num;
+ num += (mp_uintmax_t)1;
+ }
+ }
+
+ /* Supply a default precision if none was given. */
+ if(prec == -1)
+ prec = 1;
+
+ /* Put the number in WORK. */
+ w = workend;
+ DEBUGASSERT(base <= 16);
+ switch(base) {
+ case 10:
+ while(num > 0) {
+ *w-- = (char)('0' + (num % 10));
+ num /= 10;
+ }
+ break;
+ default:
+ while(num > 0) {
+ *w-- = digits[num % base];
+ num /= base;
+ }
+ break;
+ }
+ width -= (int)(workend - w);
+ prec -= (int)(workend - w);
+
+ if(is_alt && base == 8 && prec <= 0) {
+ *w-- = '0';
+ --width;
+ }
+
+ if(prec > 0) {
+ width -= prec;
+ while(prec-- > 0 && w >= work)
+ *w-- = '0';
+ }
+
+ if(is_alt && base == 16)
+ width -= 2;
+
+ if(is_neg || (flags & FLAGS_SHOWSIGN) || (flags & FLAGS_SPACE))
+ --width;
+
+ if(!(flags & FLAGS_LEFT) && !(flags & FLAGS_PAD_NIL))
+ while(width-- > 0)
+ OUTCHAR(' ');
+
+ if(is_neg)
+ OUTCHAR('-');
+ else if(flags & FLAGS_SHOWSIGN)
+ OUTCHAR('+');
+ else if(flags & FLAGS_SPACE)
+ OUTCHAR(' ');
+
+ if(is_alt && base == 16) {
+ OUTCHAR('0');
+ if(flags & FLAGS_UPPER)
+ OUTCHAR('X');
+ else
+ OUTCHAR('x');
+ }
+
+ if(!(flags & FLAGS_LEFT) && (flags & FLAGS_PAD_NIL))
+ while(width-- > 0)
+ OUTCHAR('0');
+
+ /* Write the number. */
+ while(++w <= workend) {
+ OUTCHAR(*w);
+ }
+
+ if(flags & FLAGS_LEFT)
+ while(width-- > 0)
+ OUTCHAR(' ');
+
+ return FALSE;
+}
+
+static const char nilstr[] = "(nil)";
+
+static bool out_string(void *userp,
+ int (*stream)(unsigned char, void *),
+ struct mproperty *p,
+ const char *str,
+ int *donep)
+{
+ int flags = p->flags;
+ int width = p->width;
+ int prec = p->prec;
+ size_t len;
+
+ if(!str) {
+ /* Write null string if there is space. */
+ if(prec == -1 || prec >= (int) sizeof(nilstr) - 1) {
+ str = nilstr;
+ len = sizeof(nilstr) - 1;
+ /* Disable quotes around (nil) */
+ flags &= ~(unsigned int)FLAGS_ALT;
+ }
+ else {
+ str = "";
+ len = 0;
+ }
+ }
+ else if(prec != -1)
+ len = (size_t)prec;
+ else if(*str == '\0')
+ len = 0;
+ else
+ len = strlen(str);
+
+ width -= (len > INT_MAX) ? INT_MAX : (int)len;
+
+ if(flags & FLAGS_ALT)
+ OUTCHAR('"');
+
+ if(!(flags & FLAGS_LEFT))
+ while(width-- > 0)
+ OUTCHAR(' ');
+
+ for(; len && *str; len--)
+ OUTCHAR(*str++);
+ if(flags & FLAGS_LEFT)
+ while(width-- > 0)
+ OUTCHAR(' ');
+
+ if(flags & FLAGS_ALT)
+ OUTCHAR('"');
+
+ return FALSE;
+}
+
+static bool out_pointer(void *userp,
+ int (*stream)(unsigned char, void *),
+ struct mproperty *p,
+ const char *ptr,
+ char *work,
+ int *donep)
+{
+ /* Generic pointer. */
+ if(ptr) {
+ size_t num = (size_t) ptr;
+
+ /* If the pointer is not NULL, write it as a %#x spec. */
+ p->flags |= FLAGS_HEX|FLAGS_ALT;
+ if(out_number(userp, stream, p, num, 0, work, donep))
+ return TRUE;
+ }
+ else {
+ /* Write "(nil)" for a nil pointer. */
+ const char *point;
+ int width = p->width;
+ int flags = p->flags;
+
+ width -= (int)(sizeof(nilstr) - 1);
+ if(flags & FLAGS_LEFT)
+ while(width-- > 0)
+ OUTCHAR(' ');
+ for(point = nilstr; *point; ++point)
+ OUTCHAR(*point);
+ if(!(flags & FLAGS_LEFT))
+ while(width-- > 0)
+ OUTCHAR(' ');
+ }
+ return FALSE;
+}
+
/*
* formatf() - the general printf function.
*
@@ -671,8 +970,6 @@ static int formatf(
const char *format, /* %-formatted string */
va_list ap_save) /* list of parameters */
{
- static const char nilstr[] = "(nil)";
- const char *digits = lower_digits; /* Base-36 digits for numbers. */
int done = 0; /* number of characters written */
int i;
int ocount = 0; /* number of output segments */
@@ -680,12 +977,7 @@ static int formatf(
struct outsegment output[MAX_SEGMENTS];
struct va_input input[MAX_PARAMETERS];
- char work[BUFFSIZE];
-
- /* 'workend' points to the final buffer byte position, but with an extra
- byte as margin to avoid the (FALSE?) warning Coverity gives us
- otherwise */
- char *workend = &work[sizeof(work) - 2];
+ char work[BUFFSIZE + 2];
/* Parse the format string */
if(parsefmt(format, output, input, &ocount, &icount, ap_save))
@@ -693,350 +985,96 @@ static int formatf(
for(i = 0; i < ocount; i++) {
struct outsegment *optr = &output[i];
- struct va_input *iptr;
- bool is_alt; /* Format spec modifiers. */
- int width; /* Width of a field. */
- int prec; /* Precision of a field. */
- bool is_neg; /* Decimal integer is negative. */
- unsigned long base; /* Base of a number to be written. */
- mp_uintmax_t num; /* Integral values to be written. */
- mp_intmax_t signed_num; /* Used to convert negative in positive. */
- char *w;
+ struct va_input *iptr = &input[optr->input];
+ struct mproperty p;
size_t outlen = optr->outlen;
- unsigned int flags = optr->flags;
if(outlen) {
- char *str = optr->start;
- for(; outlen && *str; outlen--)
- OUTCHAR(*str++);
+ const char *str = optr->start;
+ for(; outlen && *str; outlen--) {
+ if(stream(*str++, userp))
+ return done;
+ done++;
+ }
if(optr->flags & FLAGS_SUBSTR)
/* this is just a substring */
continue;
}
+ p.flags = optr->flags;
+
/* pick up the specified width */
- if(flags & FLAGS_WIDTHPARAM) {
- width = (int)input[optr->width].val.nums;
- if(width < 0) {
+ if(p.flags & FLAGS_WIDTHPARAM) {
+ p.width = (int)input[optr->width].val.nums;
+ if(p.width < 0) {
/* "A negative field width is taken as a '-' flag followed by a
positive field width." */
- if(width == INT_MIN)
- width = INT_MAX;
+ if(p.width == INT_MIN)
+ p.width = INT_MAX;
else
- width = -width;
- flags |= FLAGS_LEFT;
- flags &= ~(unsigned int)FLAGS_PAD_NIL;
+ p.width = -p.width;
+ p.flags |= FLAGS_LEFT;
+ p.flags &= ~(unsigned int)FLAGS_PAD_NIL;
}
}
else
- width = optr->width;
+ p.width = optr->width;
/* pick up the specified precision */
- if(flags & FLAGS_PRECPARAM) {
- prec = (int)input[optr->precision].val.nums;
- if(prec < 0)
+ if(p.flags & FLAGS_PRECPARAM) {
+ p.prec = (int)input[optr->precision].val.nums;
+ if(p.prec < 0)
/* "A negative precision is taken as if the precision were
omitted." */
- prec = -1;
+ p.prec = -1;
}
- else if(flags & FLAGS_PREC)
- prec = optr->precision;
+ else if(p.flags & FLAGS_PREC)
+ p.prec = optr->precision;
else
- prec = -1;
-
- is_alt = (flags & FLAGS_ALT) ? 1 : 0;
- iptr = &input[optr->input];
+ p.prec = -1;
switch(iptr->type) {
case FORMAT_INTU:
case FORMAT_LONGU:
case FORMAT_LONGLONGU:
- flags |= FLAGS_UNSIGNED;
- FALLTHROUGH();
+ p.flags |= FLAGS_UNSIGNED;
+ if(out_number(userp, stream, &p, iptr->val.numu, 0, work, &done))
+ return done;
+ break;
+
case FORMAT_INT:
case FORMAT_LONG:
case FORMAT_LONGLONG:
- num = iptr->val.numu;
- if(flags & FLAGS_CHAR) {
- /* Character. */
- if(!(flags & FLAGS_LEFT))
- while(--width > 0)
- OUTCHAR(' ');
- OUTCHAR((char) num);
- if(flags & FLAGS_LEFT)
- while(--width > 0)
- OUTCHAR(' ');
- break;
- }
- if(flags & FLAGS_OCTAL) {
- /* Octal unsigned integer */
- base = 8;
- is_neg = FALSE;
- }
- else if(flags & FLAGS_HEX) {
- /* Hexadecimal unsigned integer */
- digits = (flags & FLAGS_UPPER) ? upper_digits : lower_digits;
- base = 16;
- is_neg = FALSE;
- }
- else if(flags & FLAGS_UNSIGNED) {
- /* Decimal unsigned integer */
- base = 10;
- is_neg = FALSE;
- }
- else {
- /* Decimal integer. */
- base = 10;
-
- is_neg = (iptr->val.nums < (mp_intmax_t)0);
- if(is_neg) {
- /* signed_num might fail to hold absolute negative minimum by 1 */
- signed_num = iptr->val.nums + (mp_intmax_t)1;
- signed_num = -signed_num;
- num = (mp_uintmax_t)signed_num;
- num += (mp_uintmax_t)1;
- }
- }
-number:
- /* Supply a default precision if none was given. */
- if(prec == -1)
- prec = 1;
-
- /* Put the number in WORK. */
- w = workend;
- switch(base) {
- case 10:
- while(num > 0) {
- *w-- = (char)('0' + (num % 10));
- num /= 10;
- }
- break;
- default:
- while(num > 0) {
- *w-- = digits[num % base];
- num /= base;
- }
- break;
- }
- width -= (int)(workend - w);
- prec -= (int)(workend - w);
-
- if(is_alt && base == 8 && prec <= 0) {
- *w-- = '0';
- --width;
- }
-
- if(prec > 0) {
- width -= prec;
- while(prec-- > 0 && w >= work)
- *w-- = '0';
- }
-
- if(is_alt && base == 16)
- width -= 2;
-
- if(is_neg || (flags & FLAGS_SHOWSIGN) || (flags & FLAGS_SPACE))
- --width;
-
- if(!(flags & FLAGS_LEFT) && !(flags & FLAGS_PAD_NIL))
- while(width-- > 0)
- OUTCHAR(' ');
-
- if(is_neg)
- OUTCHAR('-');
- else if(flags & FLAGS_SHOWSIGN)
- OUTCHAR('+');
- else if(flags & FLAGS_SPACE)
- OUTCHAR(' ');
-
- if(is_alt && base == 16) {
- OUTCHAR('0');
- if(flags & FLAGS_UPPER)
- OUTCHAR('X');
- else
- OUTCHAR('x');
- }
-
- if(!(flags & FLAGS_LEFT) && (flags & FLAGS_PAD_NIL))
- while(width-- > 0)
- OUTCHAR('0');
-
- /* Write the number. */
- while(++w <= workend) {
- OUTCHAR(*w);
- }
-
- if(flags & FLAGS_LEFT)
- while(width-- > 0)
- OUTCHAR(' ');
+ if(out_number(userp, stream, &p, iptr->val.numu,
+ iptr->val.nums, work, &done))
+ return done;
break;
- case FORMAT_STRING: {
- const char *str;
- size_t len;
-
- str = (char *)iptr->val.str;
- if(!str) {
- /* Write null string if there is space. */
- if(prec == -1 || prec >= (int) sizeof(nilstr) - 1) {
- str = nilstr;
- len = sizeof(nilstr) - 1;
- /* Disable quotes around (nil) */
- flags &= ~(unsigned int)FLAGS_ALT;
- }
- else {
- str = "";
- len = 0;
- }
- }
- else if(prec != -1)
- len = (size_t)prec;
- else if(*str == '\0')
- len = 0;
- else
- len = strlen(str);
-
- width -= (len > INT_MAX) ? INT_MAX : (int)len;
-
- if(flags & FLAGS_ALT)
- OUTCHAR('"');
-
- if(!(flags & FLAGS_LEFT))
- while(width-- > 0)
- OUTCHAR(' ');
-
- for(; len && *str; len--)
- OUTCHAR(*str++);
- if(flags & FLAGS_LEFT)
- while(width-- > 0)
- OUTCHAR(' ');
-
- if(flags & FLAGS_ALT)
- OUTCHAR('"');
+ case FORMAT_STRING:
+ if(out_string(userp, stream, &p, iptr->val.str, &done))
+ return done;
break;
- }
case FORMAT_PTR:
- /* Generic pointer. */
- if(iptr->val.ptr) {
- /* If the pointer is not NULL, write it as a %#x spec. */
- base = 16;
- digits = (flags & FLAGS_UPPER) ? upper_digits : lower_digits;
- is_alt = TRUE;
- num = (size_t) iptr->val.ptr;
- is_neg = FALSE;
- goto number;
- }
- else {
- /* Write "(nil)" for a nil pointer. */
- const char *point;
-
- width -= (int)(sizeof(nilstr) - 1);
- if(flags & FLAGS_LEFT)
- while(width-- > 0)
- OUTCHAR(' ');
- for(point = nilstr; *point != '\0'; ++point)
- OUTCHAR(*point);
- if(!(flags & FLAGS_LEFT))
- while(width-- > 0)
- OUTCHAR(' ');
- }
+ if(out_pointer(userp, stream, &p, iptr->val.ptr, work, &done))
+ return done;
break;
- case FORMAT_DOUBLE: {
- char formatbuf[32]="%";
- char *fptr = &formatbuf[1];
- size_t left = sizeof(formatbuf)-strlen(formatbuf);
- int len;
-
- if(flags & FLAGS_WIDTH)
- width = optr->width;
-
- if(flags & FLAGS_PREC)
- prec = optr->precision;
-
- if(flags & FLAGS_LEFT)
- *fptr++ = '-';
- if(flags & FLAGS_SHOWSIGN)
- *fptr++ = '+';
- if(flags & FLAGS_SPACE)
- *fptr++ = ' ';
- if(flags & FLAGS_ALT)
- *fptr++ = '#';
-
- *fptr = 0;
-
- if(width >= 0) {
- size_t dlen;
- if(width >= (int)sizeof(work))
- width = sizeof(work)-1;
- /* RECURSIVE USAGE */
- dlen = (size_t)curl_msnprintf(fptr, left, "%d", width);
- fptr += dlen;
- left -= dlen;
- }
- if(prec >= 0) {
- /* for each digit in the integer part, we can have one less
- precision */
- size_t maxprec = sizeof(work) - 2;
- double val = iptr->val.dnum;
- if(width > 0 && prec <= width)
- maxprec -= (size_t)width;
- while(val >= 10.0) {
- val /= 10;
- maxprec--;
- }
-
- if(prec > (int)maxprec)
- prec = (int)maxprec-1;
- if(prec < 0)
- prec = 0;
- /* RECURSIVE USAGE */
- len = curl_msnprintf(fptr, left, ".%d", prec);
- fptr += len;
- }
- if(flags & FLAGS_LONG)
- *fptr++ = 'l';
-
- if(flags & FLAGS_FLOATE)
- *fptr++ = (char)((flags & FLAGS_UPPER) ? 'E' : 'e');
- else if(flags & FLAGS_FLOATG)
- *fptr++ = (char)((flags & FLAGS_UPPER) ? 'G' : 'g');
- else
- *fptr++ = 'f';
-
- *fptr = 0; /* and a final null-termination */
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wformat-nonliteral"
-#endif
- /* NOTE NOTE NOTE!! Not all sprintf implementations return number of
- output characters */
-#ifdef HAVE_SNPRINTF
- (snprintf)(work, sizeof(work), formatbuf, iptr->val.dnum);
-#else
- (sprintf)(work, formatbuf, iptr->val.dnum);
-#endif
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
- DEBUGASSERT(strlen(work) <= sizeof(work));
- for(fptr = work; *fptr; fptr++)
- OUTCHAR(*fptr);
+ case FORMAT_DOUBLE:
+ if(out_double(userp, stream, &p, iptr->val.dnum, work, &done))
+ return done;
break;
- }
case FORMAT_INTPTR:
/* Answer the count of characters written. */
#ifdef HAVE_LONG_LONG_TYPE
- if(flags & FLAGS_LONGLONG)
+ if(p.flags & FLAGS_LONGLONG)
*(LONG_LONG_TYPE *) iptr->val.ptr = (LONG_LONG_TYPE)done;
else
#endif
- if(flags & FLAGS_LONG)
+ if(p.flags & FLAGS_LONG)
*(long *) iptr->val.ptr = (long)done;
- else if(!(flags & FLAGS_SHORT))
+ else if(!(p.flags & FLAGS_SHORT))
*(int *) iptr->val.ptr = (int)done;
else
*(short *) iptr->val.ptr = (short)done;
@@ -1101,7 +1139,7 @@ int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...)
static int alloc_addbyter(unsigned char outc, void *f)
{
struct asprintf *infop = f;
- CURLcode result = Curl_dyn_addn(infop->b, &outc, 1);
+ CURLcode result = curlx_dyn_addn(infop->b, &outc, 1);
if(result) {
infop->merr = result == CURLE_TOO_LARGE ? MERR_TOO_LARGE : MERR_MEM;
return 1 ; /* fail */
@@ -1110,7 +1148,7 @@ static int alloc_addbyter(unsigned char outc, void *f)
}
/* appends the formatted string, returns MERR error code */
-int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save)
+int curlx_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save)
{
struct asprintf info;
info.b = dyn;
@@ -1118,7 +1156,7 @@ int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save)
(void)formatf(&info, alloc_addbyter, format, ap_save);
if(info.merr) {
- Curl_dyn_free(info.b);
+ curlx_dyn_free(info.b);
return info.merr;
}
return 0;
@@ -1129,16 +1167,16 @@ char *curl_mvaprintf(const char *format, va_list ap_save)
struct asprintf info;
struct dynbuf dyn;
info.b = &dyn;
- Curl_dyn_init(info.b, DYN_APRINTF);
+ curlx_dyn_init(info.b, DYN_APRINTF);
info.merr = MERR_OK;
(void)formatf(&info, alloc_addbyter, format, ap_save);
if(info.merr) {
- Curl_dyn_free(info.b);
+ curlx_dyn_free(info.b);
return NULL;
}
- if(Curl_dyn_len(info.b))
- return Curl_dyn_ptr(info.b);
+ if(curlx_dyn_len(info.b))
+ return curlx_dyn_ptr(info.b);
return strdup("");
}
@@ -1184,7 +1222,6 @@ int curl_mprintf(const char *format, ...)
int retcode;
va_list ap_save; /* argument pointer */
va_start(ap_save, format);
-
retcode = formatf(stdout, fputc_wrapper, format, ap_save);
va_end(ap_save);
return retcode;
diff --git a/libs/libcurl/src/mqtt.c b/libs/libcurl/src/mqtt.c
index 05fd45feb9..568ca4ceab 100644
--- a/libs/libcurl/src/mqtt.c
+++ b/libs/libcurl/src/mqtt.c
@@ -37,7 +37,7 @@
#include "strdup.h"
#include "url.h"
#include "escape.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
#include "curl_printf.h"
#include "curl_memory.h"
#include "multiif.h"
@@ -46,17 +46,59 @@
/* The last #include file should be: */
#include "memdebug.h"
-#define MQTT_MSG_CONNECT 0x10
-#define MQTT_MSG_CONNACK 0x20
-#define MQTT_MSG_PUBLISH 0x30
-#define MQTT_MSG_SUBSCRIBE 0x82
-#define MQTT_MSG_SUBACK 0x90
+/* first byte is command.
+ second byte is for flags. */
+#define MQTT_MSG_CONNECT 0x10
+/* #define MQTT_MSG_CONNACK 0x20 */
+#define MQTT_MSG_PUBLISH 0x30
+#define MQTT_MSG_SUBSCRIBE 0x82
+#define MQTT_MSG_SUBACK 0x90
#define MQTT_MSG_DISCONNECT 0xe0
+#define MQTT_MSG_PINGREQ 0xC0
+#define MQTT_MSG_PINGRESP 0xD0
#define MQTT_CONNACK_LEN 2
#define MQTT_SUBACK_LEN 3
#define MQTT_CLIENTID_LEN 12 /* "curl0123abcd" */
+/* meta key for storing protocol meta at easy handle */
+#define CURL_META_MQTT_EASY "meta:proto:mqtt:easy"
+/* meta key for storing protocol meta at connection */
+#define CURL_META_MQTT_CONN "meta:proto:mqtt:conn"
+
+enum mqttstate {
+ MQTT_FIRST, /* 0 */
+ MQTT_REMAINING_LENGTH, /* 1 */
+ MQTT_CONNACK, /* 2 */
+ MQTT_SUBACK, /* 3 */
+ MQTT_SUBACK_COMING, /* 4 - the SUBACK remainder */
+ MQTT_PUBWAIT, /* 5 - wait for publish */
+ MQTT_PUB_REMAIN, /* 6 - wait for the remainder of the publish */
+
+ MQTT_NOSTATE /* 7 - never used an actual state */
+};
+
+struct mqtt_conn {
+ enum mqttstate state;
+ enum mqttstate nextstate; /* switch to this after remaining length is
+ done */
+ unsigned int packetid;
+};
+
+/* protocol-specific transfer-related data */
+struct MQTT {
+ struct dynbuf sendbuf;
+ /* when receiving */
+ struct dynbuf recvbuf;
+ size_t npacket; /* byte counter */
+ size_t remaining_length;
+ unsigned char pkt_hd[4]; /* for decoding the arriving packet length */
+ struct curltime lastTime; /* last time we sent or received data */
+ unsigned char firstbyte;
+ BIT(pingsent); /* 1 while we wait for ping response */
+};
+
+
/*
* Forward declarations.
*/
@@ -92,51 +134,79 @@ const struct Curl_handler Curl_handler_mqtt = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_MQTT, /* defport */
CURLPROTO_MQTT, /* protocol */
CURLPROTO_MQTT, /* family */
PROTOPT_NONE /* flags */
};
+static void mqtt_easy_dtor(void *key, size_t klen, void *entry)
+{
+ struct MQTT *mq = entry;
+ (void)key;
+ (void)klen;
+ curlx_dyn_free(&mq->sendbuf);
+ curlx_dyn_free(&mq->recvbuf);
+ free(mq);
+}
+
+static void mqtt_conn_dtor(void *key, size_t klen, void *entry)
+{
+ (void)key;
+ (void)klen;
+ free(entry);
+}
+
static CURLcode mqtt_setup_conn(struct Curl_easy *data,
struct connectdata *conn)
{
- /* allocate the HTTP-specific struct for the Curl_easy, only to survive
- during this request */
+ /* setup MQTT specific meta data at easy handle and connection */
+ struct mqtt_conn *mqtt;
struct MQTT *mq;
- (void)conn;
- DEBUGASSERT(data->req.p.mqtt == NULL);
+
+ mqtt = calloc(1, sizeof(*mqtt));
+ if(!mqtt ||
+ Curl_conn_meta_set(conn, CURL_META_MQTT_CONN, mqtt, mqtt_conn_dtor))
+ return CURLE_OUT_OF_MEMORY;
mq = calloc(1, sizeof(struct MQTT));
if(!mq)
return CURLE_OUT_OF_MEMORY;
- Curl_dyn_init(&mq->recvbuf, DYN_MQTT_RECV);
- data->req.p.mqtt = mq;
+ curlx_dyn_init(&mq->recvbuf, DYN_MQTT_RECV);
+ curlx_dyn_init(&mq->sendbuf, DYN_MQTT_SEND);
+ if(Curl_meta_set(data, CURL_META_MQTT_EASY, mq, mqtt_easy_dtor))
+ return CURLE_OUT_OF_MEMORY;
return CURLE_OK;
}
static CURLcode mqtt_send(struct Curl_easy *data,
- char *buf, size_t len)
+ const char *buf, size_t len)
{
- CURLcode result = CURLE_OK;
- struct MQTT *mq = data->req.p.mqtt;
size_t n;
+ CURLcode result;
+ struct MQTT *mq = Curl_meta_get(data, CURL_META_MQTT_EASY);
+
+ if(!mq)
+ return CURLE_FAILED_INIT;
+
result = Curl_xfer_send(data, buf, len, FALSE, &n);
if(result)
return result;
+ mq->lastTime = curlx_now();
Curl_debug(data, CURLINFO_HEADER_OUT, buf, (size_t)n);
if(len != n) {
size_t nsend = len - n;
- char *sendleftovers = Curl_memdup(&buf[n], nsend);
- if(!sendleftovers)
- return CURLE_OUT_OF_MEMORY;
- mq->sendleftovers = sendleftovers;
- mq->nsend = nsend;
- }
- else {
- mq->sendleftovers = NULL;
- mq->nsend = 0;
+ if(curlx_dyn_len(&mq->sendbuf)) {
+ DEBUGASSERT(curlx_dyn_len(&mq->sendbuf) >= nsend);
+ result = curlx_dyn_tail(&mq->sendbuf, nsend); /* keep this much */
+ }
+ else {
+ result = curlx_dyn_addn(&mq->sendbuf, &buf[n], nsend);
+ }
}
+ else
+ curlx_dyn_reset(&mq->sendbuf);
return result;
}
@@ -256,7 +326,6 @@ static CURLcode mqtt_connect(struct Curl_easy *data)
int remain_pos = 0;
char remain[4] = {0};
size_t packetlen = 0;
- size_t payloadlen = 0;
size_t start_user = 0;
size_t start_pwd = 0;
char client_id[MQTT_CLIENTID_LEN + 1] = "curl";
@@ -271,14 +340,11 @@ static CURLcode mqtt_connect(struct Curl_easy *data)
const char *passwd = data->state.aptr.passwd ?
data->state.aptr.passwd : "";
const size_t plen = strlen(passwd);
-
- payloadlen = ulen + plen + MQTT_CLIENTID_LEN + 2;
- /* The plus 2 are for the MSB and LSB describing the length of the string to
- * be added on the payload. Refer to spec 1.5.2 and 1.5.4 */
- if(ulen)
- payloadlen += 2;
- if(plen)
- payloadlen += 2;
+ const size_t payloadlen = ulen + plen + MQTT_CLIENTID_LEN + 2 +
+ /* The plus 2s below are for the MSB and LSB describing the length of the
+ string to be added on the payload. Refer to spec 1.5.2 and 1.5.4 */
+ (ulen ? 2 : 0) +
+ (plen ? 2 : 0);
/* getting how much occupy the remain length */
remain_pos = mqtt_encode_len(remain, payloadlen + 10);
@@ -287,12 +353,11 @@ static CURLcode mqtt_connect(struct Curl_easy *data)
packetlen = payloadlen + 10 + remain_pos + 1;
/* allocating packet */
- if(packetlen > 268435455)
+ if(packetlen > 0xFFFFFFF)
return CURLE_WEIRD_SERVER_REPLY;
- packet = malloc(packetlen);
+ packet = calloc(1, packetlen);
if(!packet)
return CURLE_OUT_OF_MEMORY;
- memset(packet, 0, packetlen);
/* set initial values for the CONNECT packet */
pos = init_connpack(packet, remain, remain_pos);
@@ -308,9 +373,9 @@ static CURLcode mqtt_connect(struct Curl_easy *data)
}
infof(data, "Using client id '%s'", client_id);
- /* position where starts the user payload */
+ /* position where the user payload starts */
start_user = pos + 3 + MQTT_CLIENTID_LEN;
- /* position where starts the password payload */
+ /* position where the password payload starts */
start_pwd = start_user + ulen;
/* if username was provided, add it to the packet */
if(ulen) {
@@ -319,7 +384,7 @@ static CURLcode mqtt_connect(struct Curl_easy *data)
rc = add_user(username, ulen,
(unsigned char *)packet, start_user, remain_pos);
if(rc) {
- failf(data, "Username is too large: [%zu]", ulen);
+ failf(data, "Username too long: [%zu]", ulen);
result = CURLE_WEIRD_SERVER_REPLY;
goto end;
}
@@ -329,7 +394,7 @@ static CURLcode mqtt_connect(struct Curl_easy *data)
if(plen) {
rc = add_passwd(passwd, plen, packet, start_pwd, remain_pos);
if(rc) {
- failf(data, "Password is too large: [%zu]", plen);
+ failf(data, "Password too long: [%zu]", plen);
result = CURLE_WEIRD_SERVER_REPLY;
goto end;
}
@@ -348,20 +413,19 @@ end:
static CURLcode mqtt_disconnect(struct Curl_easy *data)
{
- CURLcode result = CURLE_OK;
- struct MQTT *mq = data->req.p.mqtt;
- result = mqtt_send(data, (char *)"\xe0\x00", 2);
- Curl_safefree(mq->sendleftovers);
- Curl_dyn_free(&mq->recvbuf);
- return result;
+ return mqtt_send(data, "\xe0\x00", 2);
}
static CURLcode mqtt_recv_atleast(struct Curl_easy *data, size_t nbytes)
{
- struct MQTT *mq = data->req.p.mqtt;
- size_t rlen = Curl_dyn_len(&mq->recvbuf);
+ struct MQTT *mq = Curl_meta_get(data, CURL_META_MQTT_EASY);
+ size_t rlen;
CURLcode result;
+ if(!mq)
+ return CURLE_FAILED_INIT;
+ rlen = curlx_dyn_len(&mq->recvbuf);
+
if(rlen < nbytes) {
unsigned char readbuf[1024];
ssize_t nread;
@@ -371,42 +435,49 @@ static CURLcode mqtt_recv_atleast(struct Curl_easy *data, size_t nbytes)
if(result)
return result;
DEBUGASSERT(nread >= 0);
- if(Curl_dyn_addn(&mq->recvbuf, readbuf, (size_t)nread))
+ if(curlx_dyn_addn(&mq->recvbuf, readbuf, (size_t)nread))
return CURLE_OUT_OF_MEMORY;
- rlen = Curl_dyn_len(&mq->recvbuf);
+ rlen = curlx_dyn_len(&mq->recvbuf);
}
return (rlen >= nbytes) ? CURLE_OK : CURLE_AGAIN;
}
static void mqtt_recv_consume(struct Curl_easy *data, size_t nbytes)
{
- struct MQTT *mq = data->req.p.mqtt;
- size_t rlen = Curl_dyn_len(&mq->recvbuf);
- if(rlen <= nbytes)
- Curl_dyn_reset(&mq->recvbuf);
- else
- Curl_dyn_tail(&mq->recvbuf, rlen - nbytes);
+ struct MQTT *mq = Curl_meta_get(data, CURL_META_MQTT_EASY);
+ DEBUGASSERT(mq);
+ if(mq) {
+ size_t rlen = curlx_dyn_len(&mq->recvbuf);
+ if(rlen <= nbytes)
+ curlx_dyn_reset(&mq->recvbuf);
+ else
+ curlx_dyn_tail(&mq->recvbuf, rlen - nbytes);
+ }
}
static CURLcode mqtt_verify_connack(struct Curl_easy *data)
{
- struct MQTT *mq = data->req.p.mqtt;
+ struct MQTT *mq = Curl_meta_get(data, CURL_META_MQTT_EASY);
CURLcode result;
char *ptr;
+ DEBUGASSERT(mq);
+ if(!mq)
+ return CURLE_FAILED_INIT;
+
result = mqtt_recv_atleast(data, MQTT_CONNACK_LEN);
if(result)
goto fail;
/* verify CONNACK */
- DEBUGASSERT(Curl_dyn_len(&mq->recvbuf) >= MQTT_CONNACK_LEN);
- ptr = Curl_dyn_ptr(&mq->recvbuf);
+ DEBUGASSERT(curlx_dyn_len(&mq->recvbuf) >= MQTT_CONNACK_LEN);
+ ptr = curlx_dyn_ptr(&mq->recvbuf);
Curl_debug(data, CURLINFO_HEADER_IN, ptr, MQTT_CONNACK_LEN);
if(ptr[0] != 0x00 || ptr[1] != 0x00) {
failf(data, "Expected %02x%02x but got %02x%02x",
0x00, 0x00, ptr[0], ptr[1]);
- Curl_dyn_reset(&mq->recvbuf);
+ curlx_dyn_reset(&mq->recvbuf);
result = CURLE_WEIRD_SERVER_REPLY;
goto fail;
}
@@ -443,12 +514,16 @@ static CURLcode mqtt_subscribe(struct Curl_easy *data)
char encodedsize[4];
size_t n;
struct connectdata *conn = data->conn;
+ struct mqtt_conn *mqtt = Curl_conn_meta_get(conn, CURL_META_MQTT_CONN);
+
+ if(!mqtt)
+ return CURLE_FAILED_INIT;
result = mqtt_get_topic(data, &topic, &topiclen);
if(result)
goto fail;
- conn->proto.mqtt.packetid++;
+ mqtt->packetid++;
packetlen = topiclen + 5; /* packetid + topic (has a two byte length field)
+ 2 bytes topic length + QoS byte */
@@ -463,14 +538,14 @@ static CURLcode mqtt_subscribe(struct Curl_easy *data)
packet[0] = MQTT_MSG_SUBSCRIBE;
memcpy(&packet[1], encodedsize, n);
- packet[1 + n] = (conn->proto.mqtt.packetid >> 8) & 0xff;
- packet[2 + n] = conn->proto.mqtt.packetid & 0xff;
+ packet[1 + n] = (mqtt->packetid >> 8) & 0xff;
+ packet[2 + n] = mqtt->packetid & 0xff;
packet[3 + n] = (topiclen >> 8) & 0xff;
packet[4 + n ] = topiclen & 0xff;
memcpy(&packet[5 + n], topic, topiclen);
packet[5 + n + topiclen] = 0; /* QoS zero */
- result = mqtt_send(data, (char *)packet, packetlen);
+ result = mqtt_send(data, (const char *)packet, packetlen);
fail:
free(topic);
@@ -483,25 +558,28 @@ fail:
*/
static CURLcode mqtt_verify_suback(struct Curl_easy *data)
{
- struct MQTT *mq = data->req.p.mqtt;
+ struct MQTT *mq = Curl_meta_get(data, CURL_META_MQTT_EASY);
struct connectdata *conn = data->conn;
- struct mqtt_conn *mqtt = &conn->proto.mqtt;
+ struct mqtt_conn *mqtt = Curl_conn_meta_get(conn, CURL_META_MQTT_CONN);
CURLcode result;
char *ptr;
+ if(!mqtt || !mq)
+ return CURLE_FAILED_INIT;
+
result = mqtt_recv_atleast(data, MQTT_SUBACK_LEN);
if(result)
goto fail;
/* verify SUBACK */
- DEBUGASSERT(Curl_dyn_len(&mq->recvbuf) >= MQTT_SUBACK_LEN);
- ptr = Curl_dyn_ptr(&mq->recvbuf);
+ DEBUGASSERT(curlx_dyn_len(&mq->recvbuf) >= MQTT_SUBACK_LEN);
+ ptr = curlx_dyn_ptr(&mq->recvbuf);
Curl_debug(data, CURLINFO_HEADER_IN, ptr, MQTT_SUBACK_LEN);
if(((unsigned char)ptr[0]) != ((mqtt->packetid >> 8) & 0xff) ||
((unsigned char)ptr[1]) != (mqtt->packetid & 0xff) ||
ptr[2] != 0x00) {
- Curl_dyn_reset(&mq->recvbuf);
+ curlx_dyn_reset(&mq->recvbuf);
result = CURLE_WEIRD_SERVER_REPLY;
goto fail;
}
@@ -557,7 +635,7 @@ static CURLcode mqtt_publish(struct Curl_easy *data)
i += topiclen;
memcpy(&pkt[i], payload, payloadlen);
i += payloadlen;
- result = mqtt_send(data, (char *)pkt, i);
+ result = mqtt_send(data, (const char *)pkt, i);
fail:
free(pkt);
@@ -605,7 +683,10 @@ static void mqstate(struct Curl_easy *data,
enum mqttstate nextstate) /* used if state == FIRST */
{
struct connectdata *conn = data->conn;
- struct mqtt_conn *mqtt = &conn->proto.mqtt;
+ struct mqtt_conn *mqtt = Curl_conn_meta_get(conn, CURL_META_MQTT_CONN);
+ DEBUGASSERT(mqtt);
+ if(!mqtt)
+ return;
#ifdef DEBUGBUILD
infof(data, "%s (from %s) (next is %s)",
statenames[state],
@@ -624,10 +705,14 @@ static CURLcode mqtt_read_publish(struct Curl_easy *data, bool *done)
struct connectdata *conn = data->conn;
ssize_t nread;
size_t remlen;
- struct mqtt_conn *mqtt = &conn->proto.mqtt;
- struct MQTT *mq = data->req.p.mqtt;
+ struct mqtt_conn *mqtt = Curl_conn_meta_get(conn, CURL_META_MQTT_CONN);
+ struct MQTT *mq = Curl_meta_get(data, CURL_META_MQTT_EASY);
unsigned char packet;
+ DEBUGASSERT(mqtt);
+ if(!mqtt || !mq)
+ return CURLE_FAILED_INIT;
+
switch(mqtt->state) {
MQTT_SUBACK_COMING:
case MQTT_SUBACK_COMING:
@@ -691,6 +776,9 @@ MQTT_SUBACK_COMING:
goto end;
}
+ /* we received something */
+ mq->lastTime = curlx_now();
+
/* if QoS is set, message contains packet id */
result = Curl_client_write(data, CLIENTWRITE_BODY, buffer, nread);
if(result)
@@ -713,9 +801,15 @@ end:
static CURLcode mqtt_do(struct Curl_easy *data, bool *done)
{
+ struct MQTT *mq = Curl_meta_get(data, CURL_META_MQTT_EASY);
CURLcode result = CURLE_OK;
*done = FALSE; /* unconditionally */
+ if(!mq)
+ return CURLE_FAILED_INIT;
+ mq->lastTime = curlx_now();
+ mq->pingsent = FALSE;
+
result = mqtt_connect(data);
if(result) {
failf(data, "Error %d sending MQTT CONNECT request", result);
@@ -728,34 +822,73 @@ static CURLcode mqtt_do(struct Curl_easy *data, bool *done)
static CURLcode mqtt_done(struct Curl_easy *data,
CURLcode status, bool premature)
{
- struct MQTT *mq = data->req.p.mqtt;
+ struct MQTT *mq = Curl_meta_get(data, CURL_META_MQTT_EASY);
(void)status;
(void)premature;
- Curl_safefree(mq->sendleftovers);
- Curl_dyn_free(&mq->recvbuf);
+ if(mq) {
+ curlx_dyn_free(&mq->sendbuf);
+ curlx_dyn_free(&mq->recvbuf);
+ }
return CURLE_OK;
}
-static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
+/* we ping regularly to avoid being disconnected by the server */
+static CURLcode mqtt_ping(struct Curl_easy *data)
{
+ struct MQTT *mq = Curl_meta_get(data, CURL_META_MQTT_EASY);
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
- struct mqtt_conn *mqtt = &conn->proto.mqtt;
- struct MQTT *mq = data->req.p.mqtt;
+ struct mqtt_conn *mqtt = Curl_conn_meta_get(conn, CURL_META_MQTT_CONN);
+
+ if(!mqtt || !mq)
+ return CURLE_FAILED_INIT;
+
+ if(mqtt->state == MQTT_FIRST &&
+ !mq->pingsent &&
+ data->set.upkeep_interval_ms > 0) {
+ struct curltime t = curlx_now();
+ timediff_t diff = curlx_timediff(t, mq->lastTime);
+
+ if(diff > data->set.upkeep_interval_ms) {
+ /* 0xC0 is PINGREQ, and 0x00 is remaining length */
+ unsigned char packet[2] = { 0xC0, 0x00 };
+ size_t packetlen = sizeof(packet);
+
+ result = mqtt_send(data, (char *)packet, packetlen);
+ if(!result) {
+ mq->pingsent = TRUE;
+ }
+ infof(data, "mqtt_ping: sent ping request.");
+ }
+ }
+ return result;
+}
+
+static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
+{
+ struct MQTT *mq = Curl_meta_get(data, CURL_META_MQTT_EASY);
+ CURLcode result = CURLE_OK;
ssize_t nread;
unsigned char recvbyte;
+ struct mqtt_conn *mqtt = Curl_conn_meta_get(data->conn, CURL_META_MQTT_CONN);
+
+ if(!mqtt || !mq)
+ return CURLE_FAILED_INIT;
*done = FALSE;
- if(mq->nsend) {
+ if(curlx_dyn_len(&mq->sendbuf)) {
/* send the remainder of an outgoing packet */
- char *ptr = mq->sendleftovers;
- result = mqtt_send(data, mq->sendleftovers, mq->nsend);
- free(ptr);
+ result = mqtt_send(data, curlx_dyn_ptr(&mq->sendbuf),
+ curlx_dyn_len(&mq->sendbuf));
if(result)
return result;
}
+ result = mqtt_ping(data);
+ if(result)
+ return result;
+
infof(data, "mqtt_doing: state [%d]", (int) mqtt->state);
switch(mqtt->state) {
case MQTT_FIRST:
@@ -769,7 +902,11 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
result = CURLE_RECV_ERROR;
break;
}
- Curl_debug(data, CURLINFO_HEADER_IN, (char *)&mq->firstbyte, 1);
+ Curl_debug(data, CURLINFO_HEADER_IN, (const char *)&mq->firstbyte, 1);
+
+ /* we received something */
+ mq->lastTime = curlx_now();
+
/* remember the first byte */
mq->npacket = 0;
mqstate(data, MQTT_REMAINING_LENGTH, MQTT_NOSTATE);
@@ -779,7 +916,7 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
result = Curl_xfer_recv(data, (char *)&recvbyte, 1, &nread);
if(result || !nread)
break;
- Curl_debug(data, CURLINFO_HEADER_IN, (char *)&recvbyte, 1);
+ Curl_debug(data, CURLINFO_HEADER_IN, (const char *)&recvbyte, 1);
mq->pkt_hd[mq->npacket++] = recvbyte;
} while((recvbyte & 0x80) && (mq->npacket < 4));
if(!result && nread && (recvbyte & 0x80))
@@ -800,6 +937,13 @@ static CURLcode mqtt_doing(struct Curl_easy *data, bool *done)
infof(data, "Got DISCONNECT");
*done = TRUE;
}
+
+ /* ping response */
+ if(mq->firstbyte == MQTT_MSG_PINGRESP) {
+ infof(data, "Received ping response.");
+ mq->pingsent = FALSE;
+ mqstate(data, MQTT_FIRST, MQTT_PUBWAIT);
+ }
break;
case MQTT_CONNACK:
result = mqtt_verify_connack(data);
diff --git a/libs/libcurl/src/mqtt.h b/libs/libcurl/src/mqtt.h
index ea81f40448..53997588ee 100644
--- a/libs/libcurl/src/mqtt.h
+++ b/libs/libcurl/src/mqtt.h
@@ -28,36 +28,4 @@
extern const struct Curl_handler Curl_handler_mqtt;
#endif
-enum mqttstate {
- MQTT_FIRST, /* 0 */
- MQTT_REMAINING_LENGTH, /* 1 */
- MQTT_CONNACK, /* 2 */
- MQTT_SUBACK, /* 3 */
- MQTT_SUBACK_COMING, /* 4 - the SUBACK remainder */
- MQTT_PUBWAIT, /* 5 - wait for publish */
- MQTT_PUB_REMAIN, /* 6 - wait for the remainder of the publish */
-
- MQTT_NOSTATE /* 7 - never used an actual state */
-};
-
-struct mqtt_conn {
- enum mqttstate state;
- enum mqttstate nextstate; /* switch to this after remaining length is
- done */
- unsigned int packetid;
-};
-
-/* protocol-specific transfer-related data */
-struct MQTT {
- char *sendleftovers;
- size_t nsend; /* size of sendleftovers */
-
- /* when receiving */
- size_t npacket; /* byte counter */
- unsigned char firstbyte;
- size_t remaining_length;
- struct dynbuf recvbuf;
- unsigned char pkt_hd[4]; /* for decoding the arriving packet length */
-};
-
#endif /* HEADER_CURL_MQTT_H */
diff --git a/libs/libcurl/src/multi.c b/libs/libcurl/src/multi.c
index d89657e2f3..d325e92f38 100644
--- a/libs/libcurl/src/multi.c
+++ b/libs/libcurl/src/multi.c
@@ -36,16 +36,18 @@
#include "share.h"
#include "psl.h"
#include "multiif.h"
+#include "multi_ev.h"
#include "sendf.h"
-#include "timeval.h"
+#include "curlx/timeval.h"
#include "http.h"
#include "select.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
#include "speedcheck.h"
#include "conncache.h"
#include "multihandle.h"
#include "sigpipe.h"
#include "vtls/vtls.h"
+#include "vtls/vtls_scache.h"
#include "http_proxy.h"
#include "http2.h"
#include "socketpair.h"
@@ -56,10 +58,13 @@
#include "curl_memory.h"
#include "memdebug.h"
+/* initial multi->xfers table size for a full multi */
+#define CURL_XFER_TABLE_SIZE 512
+
/*
CURL_SOCKET_HASH_TABLE_SIZE should be a prime number. Increasing it from 97
to 911 takes on a 32-bit machine 4 x 804 = 3211 more bytes. Still, every
- CURL handle takes 45-50 K memory, therefore this 3K are not significant.
+ curl handle takes 6K memory, therefore this 3K are not significant.
*/
#ifndef CURL_SOCKET_HASH_TABLE_SIZE
#define CURL_SOCKET_HASH_TABLE_SIZE 911
@@ -73,6 +78,10 @@
#define CURL_DNS_HASH_SIZE 71
#endif
+#ifndef CURL_TLS_SESSION_SIZE
+#define CURL_TLS_SESSION_SIZE 25
+#endif
+
#define CURL_MULTI_HANDLE 0x000bab1e
#ifdef DEBUGBUILD
@@ -89,8 +98,6 @@
static void move_pending_to_connect(struct Curl_multi *multi,
struct Curl_easy *data);
-static CURLMcode singlesocket(struct Curl_multi *multi,
- struct Curl_easy *data);
static CURLMcode add_next_timeout(struct curltime now,
struct Curl_multi *multi,
struct Curl_easy *d);
@@ -99,30 +106,8 @@ static CURLMcode multi_timeout(struct Curl_multi *multi,
long *timeout_ms);
static void process_pending_handles(struct Curl_multi *multi);
static void multi_xfer_bufs_free(struct Curl_multi *multi);
-static void expire_ex(struct Curl_easy *data, const struct curltime *nowp,
- timediff_t milli, expire_id id);
-
-#if defined( DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
-static const char * const multi_statename[]={
- "INIT",
- "PENDING",
- "SETUP",
- "CONNECT",
- "RESOLVING",
- "CONNECTING",
- "TUNNELING",
- "PROTOCONNECT",
- "PROTOCONNECTING",
- "DO",
- "DOING",
- "DOING_MORE",
- "DID",
- "PERFORMING",
- "RATELIMITING",
- "DONE",
- "COMPLETED",
- "MSGSENT",
-};
+#ifdef DEBUGBUILD
+static void multi_xfer_tbl_dump(struct Curl_multi *multi);
#endif
/* function pointer called once when switching TO a state */
@@ -174,31 +159,25 @@ static void mstate(struct Curl_easy *data, CURLMstate state
NULL /* MSGSENT */
};
-#if defined(DEBUGBUILD) && defined(CURL_DISABLE_VERBOSE_STRINGS)
- (void) lineno;
-#endif
-
if(oldstate == state)
/* do not bother when the new state is the same as the old state */
return;
- data->mstate = state;
-
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
- if(data->mstate >= MSTATE_PENDING &&
- data->mstate < MSTATE_COMPLETED) {
- infof(data,
- "STATE: %s => %s handle %p; line %d",
- multi_statename[oldstate], multi_statename[data->mstate],
- (void *)data, lineno);
- }
+#ifdef DEBUGBUILD
+ CURL_TRC_M(data, "-> [%s] (line %d)", CURL_MSTATE_NAME(state), lineno);
+#else
+ CURL_TRC_M(data, "-> [%s]", CURL_MSTATE_NAME(state));
#endif
+ data->mstate = state;
+
if(state == MSTATE_COMPLETED) {
- /* changing to COMPLETED means there is one less easy handle 'alive' */
- DEBUGASSERT(data->multi->num_alive > 0);
- data->multi->num_alive--;
- if(!data->multi->num_alive) {
+ /* changing to COMPLETED means it is in process and needs to go */
+ DEBUGASSERT(Curl_uint_bset_contains(&data->multi->process, data->mid));
+ Curl_uint_bset_remove(&data->multi->process, data->mid);
+ Curl_uint_bset_remove(&data->multi->pending, data->mid); /* to be sure */
+
+ if(Curl_uint_bset_empty(&data->multi->process)) {
/* free the transfer buffer when we have no more active transfers */
multi_xfer_bufs_free(data->multi);
}
@@ -215,163 +194,6 @@ static void mstate(struct Curl_easy *data, CURLMstate state
#define multistate(x,y) mstate(x,y, __LINE__)
#endif
-/*
- * We add one of these structs to the sockhash for each socket
- */
-
-struct Curl_sh_entry {
- struct Curl_hash transfers; /* hash of transfers using this socket */
- unsigned int action; /* what combined action READ/WRITE this socket waits
- for */
- unsigned int users; /* number of transfers using this */
- void *socketp; /* settable by users with curl_multi_assign() */
- unsigned int readers; /* this many transfers want to read */
- unsigned int writers; /* this many transfers want to write */
-};
-
-/* look up a given socket in the socket hash, skip invalid sockets */
-static struct Curl_sh_entry *sh_getentry(struct Curl_hash *sh,
- curl_socket_t s)
-{
- if(s != CURL_SOCKET_BAD) {
- /* only look for proper sockets */
- return Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t));
- }
- return NULL;
-}
-
-#define TRHASH_SIZE 13
-
-/* the given key here is a struct Curl_easy pointer */
-static size_t trhash(void *key, size_t key_length, size_t slots_num)
-{
- unsigned char bytes = ((unsigned char *)key)[key_length - 1] ^
- ((unsigned char *)key)[0];
- return (bytes % slots_num);
-}
-
-static size_t trhash_compare(void *k1, size_t k1_len, void *k2, size_t k2_len)
-{
- (void)k2_len;
- return !memcmp(k1, k2, k1_len);
-}
-
-static void trhash_dtor(void *nada)
-{
- (void)nada;
-}
-
-/*
- * The sockhash has its own separate subhash in each entry that need to be
- * safely destroyed first.
- */
-static void sockhash_destroy(struct Curl_hash *h)
-{
- struct Curl_hash_iterator iter;
- struct Curl_hash_element *he;
-
- DEBUGASSERT(h);
- Curl_hash_start_iterate(h, &iter);
- he = Curl_hash_next_element(&iter);
- while(he) {
- struct Curl_sh_entry *sh = (struct Curl_sh_entry *)he->ptr;
- Curl_hash_destroy(&sh->transfers);
- he = Curl_hash_next_element(&iter);
- }
- Curl_hash_destroy(h);
-}
-
-
-/* make sure this socket is present in the hash for this handle */
-static struct Curl_sh_entry *sh_addentry(struct Curl_hash *sh,
- curl_socket_t s)
-{
- struct Curl_sh_entry *there = sh_getentry(sh, s);
- struct Curl_sh_entry *check;
-
- if(there) {
- /* it is present, return fine */
- return there;
- }
-
- /* not present, add it */
- check = calloc(1, sizeof(struct Curl_sh_entry));
- if(!check)
- return NULL; /* major failure */
-
- Curl_hash_init(&check->transfers, TRHASH_SIZE, trhash, trhash_compare,
- trhash_dtor);
-
- /* make/add new hash entry */
- if(!Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) {
- Curl_hash_destroy(&check->transfers);
- free(check);
- return NULL; /* major failure */
- }
-
- return check; /* things are good in sockhash land */
-}
-
-
-/* delete the given socket + handle from the hash */
-static void sh_delentry(struct Curl_sh_entry *entry,
- struct Curl_hash *sh, curl_socket_t s)
-{
- Curl_hash_destroy(&entry->transfers);
-
- /* We remove the hash entry. This will end up in a call to
- sh_freeentry(). */
- Curl_hash_delete(sh, (char *)&s, sizeof(curl_socket_t));
-}
-
-/*
- * free a sockhash entry
- */
-static void sh_freeentry(void *freethis)
-{
- struct Curl_sh_entry *p = (struct Curl_sh_entry *) freethis;
-
- free(p);
-}
-
-static size_t fd_key_compare(void *k1, size_t k1_len, void *k2, size_t k2_len)
-{
- (void) k1_len; (void) k2_len;
-
- return (*((curl_socket_t *) k1)) == (*((curl_socket_t *) k2));
-}
-
-static size_t hash_fd(void *key, size_t key_length, size_t slots_num)
-{
- curl_socket_t fd = *((curl_socket_t *) key);
- (void) key_length;
-
- return (fd % (curl_socket_t)slots_num);
-}
-
-/*
- * sh_init() creates a new socket hash and returns the handle for it.
- *
- * Quote from README.multi_socket:
- *
- * "Some tests at 7000 and 9000 connections showed that the socket hash lookup
- * is somewhat of a bottle neck. Its current implementation may be a bit too
- * limiting. It simply has a fixed-size array, and on each entry in the array
- * it has a linked list with entries. The hash only checks which list to scan
- * through. The code I had used so for used a list with merely 7 slots (as
- * that is what the DNS hash uses) but with 7000 connections that would make
- * an average of 1000 nodes in each list to run through. I upped that to 97
- * slots (I believe a prime is suitable) and noticed a significant speed
- * increase. I need to reconsider the hash implementation or use a rather
- * large default value like this. At 9000 connections I was still below 10us
- * per call."
- *
- */
-static void sh_init(struct Curl_hash *hash, size_t hashsize)
-{
- Curl_hash_init(hash, hashsize, hash_fd, fd_key_compare,
- sh_freeentry);
-}
/* multi->proto_hash destructor. Should never be called as elements
* MUST be added with their own destructor */
@@ -395,9 +217,11 @@ static void multi_addmsg(struct Curl_multi *multi, struct Curl_message *msg)
Curl_llist_append(&multi->msglist, msg, &msg->list);
}
-struct Curl_multi *Curl_multi_handle(size_t hashsize, /* socket hash */
+struct Curl_multi *Curl_multi_handle(unsigned int xfer_table_size,
+ size_t ev_hashsize, /* event hash */
size_t chashsize, /* connection hash */
- size_t dnssize) /* dns hash */
+ size_t dnssize, /* dns hash */
+ size_t sesssize) /* TLS session cache */
{
struct Curl_multi *multi = calloc(1, sizeof(struct Curl_multi));
@@ -406,26 +230,47 @@ struct Curl_multi *Curl_multi_handle(size_t hashsize, /* socket hash */
multi->magic = CURL_MULTI_HANDLE;
- Curl_init_dnscache(&multi->hostcache, dnssize);
-
- sh_init(&multi->sockhash, hashsize);
-
+ Curl_dnscache_init(&multi->dnscache, dnssize);
+ Curl_multi_ev_init(multi, ev_hashsize);
+ Curl_uint_tbl_init(&multi->xfers, NULL);
+ Curl_uint_bset_init(&multi->process);
+ Curl_uint_bset_init(&multi->pending);
+ Curl_uint_bset_init(&multi->msgsent);
Curl_hash_init(&multi->proto_hash, 23,
- Curl_hash_str, Curl_str_key_compare, ph_freeentry);
-
- if(Curl_cpool_init(&multi->cpool, Curl_on_disconnect,
- multi, NULL, chashsize))
- goto error;
-
+ Curl_hash_str, curlx_str_key_compare, ph_freeentry);
Curl_llist_init(&multi->msglist, NULL);
- Curl_llist_init(&multi->process, NULL);
- Curl_llist_init(&multi->pending, NULL);
- Curl_llist_init(&multi->msgsent, NULL);
multi->multiplexing = TRUE;
multi->max_concurrent_streams = 100;
multi->last_timeout_ms = -1;
+ if(Curl_uint_bset_resize(&multi->process, xfer_table_size) ||
+ Curl_uint_bset_resize(&multi->pending, xfer_table_size) ||
+ Curl_uint_bset_resize(&multi->msgsent, xfer_table_size) ||
+ Curl_uint_tbl_resize(&multi->xfers, xfer_table_size))
+ goto error;
+
+ multi->admin = curl_easy_init();
+ if(!multi->admin)
+ goto error;
+ /* Initialize admin handle to operate inside this multi */
+ multi->admin->multi = multi;
+ multi->admin->state.internal = TRUE;
+ Curl_llist_init(&multi->admin->state.timeoutlist, NULL);
+#ifdef DEBUGBUILD
+ if(getenv("CURL_DEBUG"))
+ multi->admin->set.verbose = TRUE;
+#endif
+ Curl_uint_tbl_add(&multi->xfers, multi->admin, &multi->admin->mid);
+
+ if(Curl_cshutdn_init(&multi->cshutdn, multi))
+ goto error;
+
+ Curl_cpool_init(&multi->cpool, multi->admin, NULL, chashsize);
+
+ if(Curl_ssl_scache_create(sesssize, 2, &multi->ssl_scache))
+ goto error;
+
#ifdef USE_WINSOCK
multi->wsa_event = WSACreateEvent();
if(multi->wsa_event == WSA_INVALID_EVENT)
@@ -443,19 +288,33 @@ struct Curl_multi *Curl_multi_handle(size_t hashsize, /* socket hash */
error:
- sockhash_destroy(&multi->sockhash);
+ Curl_multi_ev_cleanup(multi);
Curl_hash_destroy(&multi->proto_hash);
- Curl_hash_destroy(&multi->hostcache);
+ Curl_dnscache_destroy(&multi->dnscache);
Curl_cpool_destroy(&multi->cpool);
+ Curl_cshutdn_destroy(&multi->cshutdn, multi->admin);
+ Curl_ssl_scache_destroy(multi->ssl_scache);
+ if(multi->admin) {
+ multi->admin->multi = NULL;
+ Curl_close(&multi->admin);
+ }
+
+ Curl_uint_bset_destroy(&multi->process);
+ Curl_uint_bset_destroy(&multi->pending);
+ Curl_uint_bset_destroy(&multi->msgsent);
+ Curl_uint_tbl_destroy(&multi->xfers);
+
free(multi);
return NULL;
}
CURLM *curl_multi_init(void)
{
- return Curl_multi_handle(CURL_SOCKET_HASH_TABLE_SIZE,
+ return Curl_multi_handle(CURL_XFER_TABLE_SIZE,
+ CURL_SOCKET_HASH_TABLE_SIZE,
CURL_CONNECTION_HASH_SIZE,
- CURL_DNS_HASH_SIZE);
+ CURL_DNS_HASH_SIZE,
+ CURL_TLS_SESSION_SIZE);
}
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
@@ -472,6 +331,44 @@ static void multi_warn_debug(struct Curl_multi *multi, struct Curl_easy *data)
#define multi_warn_debug(x,y) Curl_nop_stmt
#endif
+
+static CURLMcode multi_xfers_add(struct Curl_multi *multi,
+ struct Curl_easy *data)
+{
+ /* We want `multi->xfers` to have "sufficient" free rows, so that we do
+ * have to reuse the `mid` from a just removed easy right away.
+ * Since uint_tbl and uint_bset is quite memory efficient,
+ * regard less than 25% free as insufficient.
+ * (for low capacities, e.g. multi_easy, 4 or less). */
+ unsigned int capacity = Curl_uint_tbl_capacity(&multi->xfers);
+ unsigned int unused = capacity - Curl_uint_tbl_count(&multi->xfers);
+ unsigned int min_unused = CURLMAX(capacity >> 2, 4);
+
+ if(unused <= min_unused) {
+ /* make it a 64 multiple, since our bitsets frow by that and
+ * small (easy_multi) grows to at least 64 on first resize. */
+ unsigned int newsize = (((capacity + min_unused) + 63) / 64) * 64;
+ DEBUGASSERT(newsize > capacity);
+ /* Grow the bitsets first. Should one fail, we do not need
+ * to downsize the already resized ones. The sets continue
+ * to work properly when larger than the table, but not
+ * the other way around. */
+ if(Curl_uint_bset_resize(&multi->process, newsize) ||
+ Curl_uint_bset_resize(&multi->pending, newsize) ||
+ Curl_uint_bset_resize(&multi->msgsent, newsize) ||
+ Curl_uint_tbl_resize(&multi->xfers, newsize))
+ return CURLM_OUT_OF_MEMORY;
+ CURL_TRC_M(data, "increased xfer table size to %u", newsize);
+ }
+ /* Insert the easy into the table now that MUST have room for it */
+ if(!Curl_uint_tbl_add(&multi->xfers, data, &data->mid)) {
+ DEBUGASSERT(0);
+ return CURLM_OUT_OF_MEMORY;
+ }
+ return CURLM_OK;
+}
+
+
CURLMcode curl_multi_add_handle(CURLM *m, CURL *d)
{
CURLMcode rc;
@@ -496,10 +393,15 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *d)
if(multi->dead) {
/* a "dead" handle cannot get added transfers while any existing easy
handles are still alive - but if there are none alive anymore, it is
- fine to start over and unmark the "deadness" of this handle */
- if(multi->num_alive)
+ fine to start over and unmark the "deadness" of this handle.
+ This means only the admin handle MUST be present. */
+ if((Curl_uint_tbl_count(&multi->xfers) != 1) ||
+ !Curl_uint_tbl_contains(&multi->xfers, 0))
return CURLM_ABORTED_BY_CALLBACK;
multi->dead = FALSE;
+ Curl_uint_bset_clear(&multi->process);
+ Curl_uint_bset_clear(&multi->pending);
+ Curl_uint_bset_clear(&multi->msgsent);
}
if(data->multi_easy) {
@@ -509,6 +411,10 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *d)
data->multi_easy = NULL;
}
+ /* Insert the easy into the multi->xfers table, assigning it a `mid`. */
+ if(multi_xfers_add(multi, data))
+ return CURLM_OUT_OF_MEMORY;
+
/* Initialize timeout list for this handle */
Curl_llist_init(&data->state.timeoutlist, NULL);
@@ -538,20 +444,14 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *d)
rc = Curl_update_timer(multi);
if(rc) {
data->multi = NULL; /* not anymore */
+ Curl_uint_tbl_remove(&multi->xfers, data->mid);
+ data->mid = UINT_MAX;
return rc;
}
/* set the easy handle */
multistate(data, MSTATE_INIT);
- /* for multi interface connections, we share DNS cache automatically if the
- easy handle's one is currently not set. */
- if(!data->dns.hostcache ||
- (data->dns.hostcachetype == HCACHE_NONE)) {
- data->dns.hostcache = &multi->hostcache;
- data->dns.hostcachetype = HCACHE_MULTI;
- }
-
#ifdef USE_LIBPSL
/* Do the same for PSL. */
if(data->share && (data->share->specifier & (1 << CURL_LOCK_DATA_PSL)))
@@ -560,23 +460,25 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *d)
data->psl = &multi->psl;
#endif
- /* add the easy handle to the process list */
- Curl_llist_append(&multi->process, data, &data->multi_queue);
-
- /* increase the node-counter */
- multi->num_easy++;
-
- /* increase the alive-counter */
- multi->num_alive++;
-
- /* the identifier inside the multi instance */
- data->mid = multi->next_easy_mid++;
- if(multi->next_easy_mid <= 0)
- multi->next_easy_mid = 0;
+ /* add the easy handle to the process set */
+ Curl_uint_bset_add(&multi->process, data->mid);
+ ++multi->xfers_alive;
Curl_cpool_xfer_init(data);
multi_warn_debug(multi, data);
+ /* The admin handle only ever has default timeouts set. To improve the
+ state somewhat we clone the timeouts from each added handle so that the
+ admin handle always has the same timeouts as the most recently added
+ easy handle. */
+ multi->admin->set.timeout = data->set.timeout;
+ multi->admin->set.server_response_timeout =
+ data->set.server_response_timeout;
+ multi->admin->set.no_signal = data->set.no_signal;
+
+ CURL_TRC_M(data, "added to multi, mid=%u, running=%u, total=%u",
+ data->mid, Curl_multi_xfers_running(multi),
+ Curl_uint_tbl_count(&multi->xfers));
return CURLM_OK;
}
@@ -608,20 +510,21 @@ static void multi_done_locked(struct connectdata *conn,
Curl_detach_connection(data);
+ CURL_TRC_M(data, "multi_done_locked, in use=%u",
+ Curl_uint_spbset_count(&conn->xfers_attached));
if(CONN_INUSE(conn)) {
/* Stop if still used. */
- DEBUGF(infof(data, "Connection still in use %zu, "
- "no more multi_done now!",
- Curl_llist_count(&conn->easyq)));
+ CURL_TRC_M(data, "Connection still in use %u, no more multi_done now!",
+ Curl_uint_spbset_count(&conn->xfers_attached));
return;
}
data->state.done = TRUE; /* called just now! */
data->state.recent_conn_id = conn->connection_id;
- if(conn->dns_entry)
- Curl_resolv_unlink(data, &conn->dns_entry); /* done with this */
- Curl_hostcache_prune(data);
+ Curl_resolv_unlink(data, &data->state.dns[0]); /* done with this */
+ Curl_resolv_unlink(data, &data->state.dns[1]);
+ Curl_dnscache_prune(data);
/* if data->set.reuse_forbid is TRUE, it means the libcurl client has
forced us to close this connection. This is ignored for requests taking
@@ -649,14 +552,14 @@ static void multi_done_locked(struct connectdata *conn,
#endif
) || conn->bits.close
|| (mdctx->premature && !Curl_conn_is_multiplex(conn, FIRSTSOCKET))) {
- DEBUGF(infof(data, "multi_done, not reusing connection=%"
- FMT_OFF_T ", forbid=%d"
- ", close=%d, premature=%d, conn_multiplex=%d",
- conn->connection_id, data->set.reuse_forbid,
- conn->bits.close, mdctx->premature,
- Curl_conn_is_multiplex(conn, FIRSTSOCKET)));
+ CURL_TRC_M(data, "multi_done, not reusing connection=%"
+ FMT_OFF_T ", forbid=%d"
+ ", close=%d, premature=%d, conn_multiplex=%d",
+ conn->connection_id, data->set.reuse_forbid,
+ conn->bits.close, mdctx->premature,
+ Curl_conn_is_multiplex(conn, FIRSTSOCKET));
connclose(conn, "disconnecting");
- Curl_cpool_disconnect(data, conn, mdctx->premature);
+ Curl_conn_terminate(data, conn, mdctx->premature);
}
else {
/* the connection is no longer in use by any transfer */
@@ -692,21 +595,15 @@ static CURLcode multi_done(struct Curl_easy *data,
memset(&mdctx, 0, sizeof(mdctx));
-#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
- DEBUGF(infof(data, "multi_done[%s]: status: %d prem: %d done: %d",
- multi_statename[data->mstate],
- (int)status, (int)premature, data->state.done));
-#else
- DEBUGF(infof(data, "multi_done: status: %d prem: %d done: %d",
- (int)status, (int)premature, data->state.done));
-#endif
+ CURL_TRC_M(data, "multi_done: status: %d prem: %d done: %d",
+ (int)status, (int)premature, data->state.done);
if(data->state.done)
/* Stop if multi_done() has already been called */
return CURLE_OK;
- /* Stop the resolver and free its own resources (but not dns_entry yet). */
- Curl_resolver_kill(data);
+ /* Shut down any ongoing async resolver operation. */
+ Curl_async_shutdown(data);
/* Cleanup possible redirect junk */
Curl_safefree(data->req.newurl);
@@ -727,7 +624,7 @@ static CURLcode multi_done(struct Curl_easy *data,
}
/* this calls the protocol-specific function pointer previously set */
- if(conn->handler->done)
+ if(conn->handler->done && (data->mstate >= MSTATE_PROTOCONNECT))
result = conn->handler->done(data, status, premature);
else
result = status;
@@ -781,13 +678,14 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d)
struct Curl_llist_node *e;
CURLMcode rc;
bool removed_timer = FALSE;
+ unsigned int mid;
/* First, make some basic checks that the CURLM handle is a good handle */
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
/* Verify that we got a somewhat good easy handle too */
- if(!GOOD_EASY_HANDLE(data) || !multi->num_easy)
+ if(!GOOD_EASY_HANDLE(data))
return CURLM_BAD_EASY_HANDLE;
/* Prevent users from trying to remove same easy handle more than once */
@@ -798,6 +696,15 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d)
if(data->multi != multi)
return CURLM_BAD_EASY_HANDLE;
+ if(data->mid == UINT_MAX) {
+ DEBUGASSERT(0);
+ return CURLM_INTERNAL_ERROR;
+ }
+ if(Curl_uint_tbl_get(&multi->xfers, data->mid) != data) {
+ DEBUGASSERT(0);
+ return CURLM_INTERNAL_ERROR;
+ }
+
if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
@@ -805,12 +712,6 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d)
/* If the 'state' is not INIT or COMPLETED, we might need to do something
nice to put the easy_handle in a good known state when this returns. */
- if(premature) {
- /* this handle is "alive" so we need to count down the total number of
- alive connections when this is removed */
- multi->num_alive--;
- }
-
if(data->conn &&
data->mstate > MSTATE_DO &&
data->mstate < MSTATE_COMPLETED) {
@@ -833,30 +734,20 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d)
called. Do it after multi_done() in case that sets another time! */
removed_timer = Curl_expire_clear(data);
- /* the handle is in a list, remove it from whichever it is */
- Curl_node_remove(&data->multi_queue);
-
- if(data->dns.hostcachetype == HCACHE_MULTI) {
- /* stop using the multi handle's DNS cache, *after* the possible
- multi_done() call above */
- data->dns.hostcache = NULL;
- data->dns.hostcachetype = HCACHE_NONE;
- }
+ /* If in `msgsent`, it was deducted from `multi->xfers_alive` already. */
+ if(!Curl_uint_bset_contains(&multi->msgsent, data->mid))
+ --multi->xfers_alive;
Curl_wildcard_dtor(&data->wildcard);
- /* change state without using multistate(), only to make singlesocket() do
- what we want */
data->mstate = MSTATE_COMPLETED;
- /* This ignores the return code even in case of problems because there is
- nothing more to do about that, here */
- (void)singlesocket(multi, data); /* to let the application know what sockets
- that vanish with this handle */
-
/* Remove the association between the connection and the handle */
Curl_detach_connection(data);
+ /* Tell event handling that this transfer is definitely going away */
+ Curl_multi_ev_xfer_done(multi, data);
+
if(data->set.connect_only && !data->multi_easy) {
/* This removes a handle that was part the multi interface that used
CONNECT_ONLY, that connection is now left alive but since this handle
@@ -870,7 +761,7 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d)
curl_socket_t s;
s = Curl_getconnectinfo(data, &c);
if((s != CURL_SOCKET_BAD) && c) {
- Curl_cpool_disconnect(data, c, TRUE);
+ Curl_conn_terminate(data, c, TRUE);
}
}
@@ -898,12 +789,19 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d)
}
}
- data->multi = NULL; /* clear the association to this multi handle */
- data->mid = -1;
+ /* clear the association to this multi handle */
+ mid = data->mid;
+ DEBUGASSERT(Curl_uint_tbl_contains(&multi->xfers, mid));
+ Curl_uint_tbl_remove(&multi->xfers, mid);
+ Curl_uint_bset_remove(&multi->process, mid);
+ Curl_uint_bset_remove(&multi->pending, mid);
+ Curl_uint_bset_remove(&multi->msgsent, mid);
+ data->multi = NULL;
+ data->mid = UINT_MAX;
+ data->master_mid = UINT_MAX;
/* NOTE NOTE NOTE
We do not touch the easy handle here! */
- multi->num_easy--; /* one less to care about now */
process_pending_handles(multi);
if(removed_timer) {
@@ -911,13 +809,17 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *d)
if(rc)
return rc;
}
+
+ CURL_TRC_M(data, "removed from multi, mid=%u, running=%u, total=%u",
+ mid, Curl_multi_xfers_running(multi),
+ Curl_uint_tbl_count(&multi->xfers));
return CURLM_OK;
}
/* Return TRUE if the application asked for multiplexing */
bool Curl_multiplex_wanted(const struct Curl_multi *multi)
{
- return (multi && (multi->multiplexing));
+ return multi && multi->multiplexing;
}
/*
@@ -930,8 +832,9 @@ void Curl_detach_connection(struct Curl_easy *data)
{
struct connectdata *conn = data->conn;
if(conn) {
- Curl_conn_ev_data_detach(conn, data);
- Curl_node_remove(&data->conn_queue);
+ Curl_uint_spbset_remove(&conn->xfers_attached, data->mid);
+ if(Curl_uint_spbset_empty(&conn->xfers_attached))
+ conn->attached_multi = NULL;
}
data->conn = NULL;
}
@@ -948,10 +851,14 @@ void Curl_attach_connection(struct Curl_easy *data,
DEBUGASSERT(!data->conn);
DEBUGASSERT(conn);
data->conn = conn;
- Curl_llist_append(&conn->easyq, data, &data->conn_queue);
+ Curl_uint_spbset_add(&conn->xfers_attached, data->mid);
+ /* all attached transfers must be from the same multi */
+ if(!conn->attached_multi)
+ conn->attached_multi = data->multi;
+ DEBUGASSERT(conn->attached_multi == data->multi);
+
if(conn->handler && conn->handler->attach)
conn->handler->attach(data, conn);
- Curl_conn_ev_data_attach(conn, data);
}
static int connecting_getsock(struct Curl_easy *data, curl_socket_t *socks)
@@ -1054,13 +961,15 @@ static int perform_getsock(struct Curl_easy *data, curl_socket_t *sock)
/* Initializes `poll_set` with the current socket poll actions needed
* for transfer `data`. */
-static void multi_getsock(struct Curl_easy *data,
- struct easy_pollset *ps)
+void Curl_multi_getsock(struct Curl_easy *data,
+ struct easy_pollset *ps,
+ const char *caller)
{
bool expect_sockets = TRUE;
- /* The no connection case can happen when this is called from
- curl_multi_remove_handle() => singlesocket() => multi_getsock().
- */
+
+ /* If the transfer has no connection, this is fine. Happens when
+ called via curl_multi_remove_handle() => Curl_multi_ev_assess() =>
+ Curl_multi_getsock(). */
Curl_pollset_reset(data, ps);
if(!data->conn)
return;
@@ -1084,30 +993,30 @@ static void multi_getsock(struct Curl_easy *data,
case MSTATE_CONNECTING:
case MSTATE_TUNNELING:
Curl_pollset_add_socks(data, ps, connecting_getsock);
- Curl_conn_adjust_pollset(data, ps);
+ Curl_conn_adjust_pollset(data, data->conn, ps);
break;
case MSTATE_PROTOCONNECT:
case MSTATE_PROTOCONNECTING:
Curl_pollset_add_socks(data, ps, protocol_getsock);
- Curl_conn_adjust_pollset(data, ps);
+ Curl_conn_adjust_pollset(data, data->conn, ps);
break;
case MSTATE_DO:
case MSTATE_DOING:
Curl_pollset_add_socks(data, ps, doing_getsock);
- Curl_conn_adjust_pollset(data, ps);
+ Curl_conn_adjust_pollset(data, data->conn, ps);
break;
case MSTATE_DOING_MORE:
Curl_pollset_add_socks(data, ps, domore_getsock);
- Curl_conn_adjust_pollset(data, ps);
+ Curl_conn_adjust_pollset(data, data->conn, ps);
break;
case MSTATE_DID: /* same as PERFORMING in regard to polling */
case MSTATE_PERFORMING:
Curl_pollset_add_socks(data, ps, perform_getsock);
- Curl_conn_adjust_pollset(data, ps);
+ Curl_conn_adjust_pollset(data, data->conn, ps);
break;
case MSTATE_RATELIMITING:
@@ -1129,6 +1038,35 @@ static void multi_getsock(struct Curl_easy *data,
break;
}
+ switch(ps->num) {
+ case 0:
+ CURL_TRC_M(data, "%s pollset[], timeouts=%zu, paused %d/%d (r/w)",
+ caller, Curl_llist_count(&data->state.timeoutlist),
+ Curl_creader_is_paused(data), Curl_cwriter_is_paused(data));
+ break;
+ case 1:
+ CURL_TRC_M(data, "%s pollset[fd=%" FMT_SOCKET_T " %s%s], timeouts=%zu",
+ caller, ps->sockets[0],
+ (ps->actions[0] & CURL_POLL_IN) ? "IN" : "",
+ (ps->actions[0] & CURL_POLL_OUT) ? "OUT" : "",
+ Curl_llist_count(&data->state.timeoutlist));
+ break;
+ case 2:
+ CURL_TRC_M(data, "%s pollset[fd=%" FMT_SOCKET_T " %s%s, "
+ "fd=%" FMT_SOCKET_T " %s%s], timeouts=%zu",
+ caller, ps->sockets[0],
+ (ps->actions[0] & CURL_POLL_IN) ? "IN" : "",
+ (ps->actions[0] & CURL_POLL_OUT) ? "OUT" : "",
+ ps->sockets[1],
+ (ps->actions[1] & CURL_POLL_IN) ? "IN" : "",
+ (ps->actions[1] & CURL_POLL_OUT) ? "OUT" : "",
+ Curl_llist_count(&data->state.timeoutlist));
+ break;
+ default:
+ CURL_TRC_M(data, "%s pollset[fds=%u], timeouts=%zu",
+ caller, ps->num, Curl_llist_count(&data->state.timeoutlist));
+ break;
+ }
if(expect_sockets && !ps->num &&
!Curl_llist_count(&data->state.timeoutlist) &&
!Curl_cwriter_is_paused(data) && !Curl_creader_is_paused(data) &&
@@ -1150,8 +1088,8 @@ CURLMcode curl_multi_fdset(CURLM *m,
Some easy handles may not have connected to the remote host yet,
and then we must make sure that is done. */
int this_max_fd = -1;
- struct Curl_llist_node *e;
struct Curl_multi *multi = m;
+ unsigned int i, mid;
(void)exc_fd_set; /* not used */
if(!GOOD_MULTI_HANDLE(multi))
@@ -1160,25 +1098,42 @@ CURLMcode curl_multi_fdset(CURLM *m,
if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
- for(e = Curl_llist_head(&multi->process); e; e = Curl_node_next(e)) {
- struct Curl_easy *data = Curl_node_elem(e);
- unsigned int i;
-
- multi_getsock(data, &data->last_poll);
+ if(Curl_uint_bset_first(&multi->process, &mid)) {
+ do {
+ struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
+ struct easy_pollset ps;
- for(i = 0; i < data->last_poll.num; i++) {
- if(!FDSET_SOCK(data->last_poll.sockets[i]))
- /* pretend it does not exist */
+ if(!data) {
+ DEBUGASSERT(0);
continue;
- if(data->last_poll.actions[i] & CURL_POLL_IN)
- FD_SET(data->last_poll.sockets[i], read_fd_set);
- if(data->last_poll.actions[i] & CURL_POLL_OUT)
- FD_SET(data->last_poll.sockets[i], write_fd_set);
- if((int)data->last_poll.sockets[i] > this_max_fd)
- this_max_fd = (int)data->last_poll.sockets[i];
+ }
+
+ Curl_multi_getsock(data, &ps, "curl_multi_fdset");
+ for(i = 0; i < ps.num; i++) {
+ if(!FDSET_SOCK(ps.sockets[i]))
+ /* pretend it does not exist */
+ continue;
+#if defined(__DJGPP__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Warith-conversion"
+#endif
+ if(ps.actions[i] & CURL_POLL_IN)
+ FD_SET(ps.sockets[i], read_fd_set);
+ if(ps.actions[i] & CURL_POLL_OUT)
+ FD_SET(ps.sockets[i], write_fd_set);
+#if defined(__DJGPP__)
+#pragma GCC diagnostic pop
+#endif
+ if((int)ps.sockets[i] > this_max_fd)
+ this_max_fd = (int)ps.sockets[i];
+ }
}
+ while(Curl_uint_bset_next(&multi->process, mid, &mid));
}
+ Curl_cshutdn_setfds(&multi->cshutdn, multi->admin,
+ read_fd_set, write_fd_set, &this_max_fd);
+
*max_fd = this_max_fd;
return CURLM_OK;
@@ -1189,12 +1144,12 @@ CURLMcode curl_multi_waitfds(CURLM *m,
unsigned int size,
unsigned int *fd_count)
{
- struct curl_waitfds cwfds;
+ struct Curl_waitfds cwfds;
CURLMcode result = CURLM_OK;
- struct Curl_llist_node *e;
struct Curl_multi *multi = m;
+ unsigned int need = 0, mid;
- if(!ufds)
+ if(!ufds && (size || !fd_count))
return CURLM_BAD_FUNCTION_ARGUMENT;
if(!GOOD_MULTI_HANDLE(multi))
@@ -1204,23 +1159,29 @@ CURLMcode curl_multi_waitfds(CURLM *m,
return CURLM_RECURSIVE_API_CALL;
Curl_waitfds_init(&cwfds, ufds, size);
- for(e = Curl_llist_head(&multi->process); e; e = Curl_node_next(e)) {
- struct Curl_easy *data = Curl_node_elem(e);
- multi_getsock(data, &data->last_poll);
- if(Curl_waitfds_add_ps(&cwfds, &data->last_poll)) {
- result = CURLM_OUT_OF_MEMORY;
- goto out;
+ if(Curl_uint_bset_first(&multi->process, &mid)) {
+ do {
+ struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
+ struct easy_pollset ps;
+ if(!data) {
+ DEBUGASSERT(0);
+ Curl_uint_bset_remove(&multi->process, mid);
+ continue;
+ }
+ Curl_multi_getsock(data, &ps, "curl_multi_waitfds");
+ need += Curl_waitfds_add_ps(&cwfds, &ps);
}
+ while(Curl_uint_bset_next(&multi->process, mid, &mid));
}
- if(Curl_cpool_add_waitfds(&multi->cpool, &cwfds)) {
+ need += Curl_cshutdn_add_waitfds(&multi->cshutdn, multi->admin, &cwfds);
+
+ if(need != cwfds.n && ufds) {
result = CURLM_OUT_OF_MEMORY;
- goto out;
}
-out:
if(fd_count)
- *fd_count = cwfds.n;
+ *fd_count = need;
return result;
}
@@ -1258,7 +1219,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
struct curl_pollfds cpfds;
unsigned int curl_nfds = 0; /* how many pfds are for curl transfers */
CURLMcode result = CURLM_OK;
- struct Curl_llist_node *e;
+ unsigned int mid;
#ifdef USE_WINSOCK
WSANETWORKEVENTS wsa_events;
@@ -1280,17 +1241,25 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
Curl_pollfds_init(&cpfds, a_few_on_stack, NUM_POLLS_ON_STACK);
/* Add the curl handles to our pollfds first */
- for(e = Curl_llist_head(&multi->process); e; e = Curl_node_next(e)) {
- struct Curl_easy *data = Curl_node_elem(e);
-
- multi_getsock(data, &data->last_poll);
- if(Curl_pollfds_add_ps(&cpfds, &data->last_poll)) {
- result = CURLM_OUT_OF_MEMORY;
- goto out;
+ if(Curl_uint_bset_first(&multi->process, &mid)) {
+ do {
+ struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
+ struct easy_pollset ps;
+ if(!data) {
+ DEBUGASSERT(0);
+ Curl_uint_bset_remove(&multi->process, mid);
+ continue;
+ }
+ Curl_multi_getsock(data, &ps, "multi_wait");
+ if(Curl_pollfds_add_ps(&cpfds, &ps)) {
+ result = CURLM_OUT_OF_MEMORY;
+ goto out;
+ }
}
+ while(Curl_uint_bset_next(&multi->process, mid, &mid));
}
- if(Curl_cpool_add_pollfds(&multi->cpool, &cpfds)) {
+ if(Curl_cshutdn_add_pollfds(&multi->cshutdn, multi->admin, &cpfds)) {
result = CURLM_OUT_OF_MEMORY;
goto out;
}
@@ -1419,23 +1388,14 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
#ifdef USE_WINSOCK
/* Count up all our own sockets that had activity,
and remove them from the event. */
- if(curl_nfds) {
- for(e = Curl_llist_head(&multi->process); e && !result;
- e = Curl_node_next(e)) {
- struct Curl_easy *data = Curl_node_elem(e);
-
- for(i = 0; i < data->last_poll.num; i++) {
- wsa_events.lNetworkEvents = 0;
- if(WSAEnumNetworkEvents(data->last_poll.sockets[i], NULL,
- &wsa_events) == 0) {
- if(ret && !pollrc && wsa_events.lNetworkEvents)
- retcode++;
- }
- WSAEventSelect(data->last_poll.sockets[i], multi->wsa_event, 0);
- }
+ for(i = 0; i < curl_nfds; ++i) {
+ wsa_events.lNetworkEvents = 0;
+ if(WSAEnumNetworkEvents(cpfds.pfds[i].fd, NULL, &wsa_events) == 0) {
+ if(ret && !pollrc && wsa_events.lNetworkEvents)
+ retcode++;
}
+ WSAEventSelect(cpfds.pfds[i].fd, multi->wsa_event, 0);
}
-
WSAResetEvent(multi->wsa_event);
#else
#ifdef ENABLE_WAKEUP
@@ -1450,7 +1410,7 @@ static CURLMcode multi_wait(struct Curl_multi *multi,
when there is no more data, breaking the loop. */
nread = wakeup_read(multi->wakeup_pair[0], buf, sizeof(buf));
if(nread <= 0) {
- if(nread < 0 && EINTR == SOCKERRNO)
+ if(nread < 0 && SOCKEINTR == SOCKERRNO)
continue;
break;
}
@@ -1517,15 +1477,6 @@ CURLMcode curl_multi_wakeup(CURLM *m)
Curl_multi struct that are constant */
struct Curl_multi *multi = m;
-#if defined(ENABLE_WAKEUP) && !defined(USE_WINSOCK)
-#ifdef USE_EVENTFD
- const void *buf;
- const uint64_t val = 1;
-#else
- char buf[1];
-#endif
-#endif
-
/* GOOD_MULTI_HANDLE can be safely called */
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
@@ -1539,12 +1490,14 @@ CURLMcode curl_multi_wakeup(CURLM *m)
making it safe to access from another thread after the init part
and before cleanup */
if(multi->wakeup_pair[1] != CURL_SOCKET_BAD) {
+ while(1) {
#ifdef USE_EVENTFD
- buf = &val;
+ /* eventfd has a stringent rule of requiring the 8-byte buffer when
+ calling write(2) on it */
+ const uint64_t buf[1] = { 1 };
#else
- buf[0] = 1;
+ const char buf[1] = { 1 };
#endif
- while(1) {
/* swrite() is not thread-safe in general, because concurrent calls
can have their messages interleaved, but in this case the content
of the messages does not matter, which makes it ok to call.
@@ -1558,11 +1511,11 @@ CURLMcode curl_multi_wakeup(CURLM *m)
int err = SOCKERRNO;
int return_success;
#ifdef USE_WINSOCK
- return_success = WSAEWOULDBLOCK == err;
+ return_success = SOCKEWOULDBLOCK == err;
#else
- if(EINTR == err)
+ if(SOCKEINTR == err)
continue;
- return_success = EWOULDBLOCK == err || EAGAIN == err;
+ return_success = SOCKEWOULDBLOCK == err || EAGAIN == err;
#endif
if(!return_success)
return CURLM_WAKEUP_FAILURE;
@@ -1615,10 +1568,15 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi,
rc = curl_multi_add_handle(multi, data);
if(!rc) {
struct SingleRequest *k = &data->req;
+ CURLcode result;
/* pass in NULL for 'conn' here since we do not want to init the
connection, only this transfer */
- Curl_init_do(data, NULL);
+ result = Curl_init_do(data, NULL);
+ if(result) {
+ curl_multi_remove_handle(multi, data);
+ return CURLM_INTERNAL_ERROR;
+ }
/* take this handle to the perform state right away */
multistate(data, MSTATE_PERFORMING);
@@ -1683,22 +1641,22 @@ static bool multi_handle_timeout(struct Curl_easy *data,
since = data->progress.t_startop;
if(data->mstate == MSTATE_RESOLVING)
failf(data, "Resolving timed out after %" FMT_TIMEDIFF_T
- " milliseconds", Curl_timediff(*now, since));
+ " milliseconds", curlx_timediff(*now, since));
else if(data->mstate == MSTATE_CONNECTING)
failf(data, "Connection timed out after %" FMT_TIMEDIFF_T
- " milliseconds", Curl_timediff(*now, since));
+ " milliseconds", curlx_timediff(*now, since));
else {
struct SingleRequest *k = &data->req;
if(k->size != -1) {
failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
" milliseconds with %" FMT_OFF_T " out of %"
FMT_OFF_T " bytes received",
- Curl_timediff(*now, since), k->bytecount, k->size);
+ curlx_timediff(*now, since), k->bytecount, k->size);
}
else {
failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
" milliseconds with %" FMT_OFF_T " bytes received",
- Curl_timediff(*now, since), k->bytecount);
+ curlx_timediff(*now, since), k->bytecount);
}
}
*result = CURLE_OPERATION_TIMEDOUT;
@@ -1831,289 +1789,13 @@ static void multi_posttransfer(struct Curl_easy *data)
* This function DOES NOT FREE the given url.
*/
static CURLcode multi_follow(struct Curl_easy *data,
- char *newurl, /* the Location: string */
+ const struct Curl_handler *handler,
+ const char *newurl, /* the Location: string */
followtype type) /* see transfer.h */
{
-#ifdef CURL_DISABLE_HTTP
- (void)data;
- (void)newurl;
- (void)type;
- /* Location: following will not happen when HTTP is disabled */
+ if(handler && handler->follow)
+ return handler->follow(data, newurl, type);
return CURLE_TOO_MANY_REDIRECTS;
-#else
-
- /* Location: redirect */
- bool disallowport = FALSE;
- bool reachedmax = FALSE;
- CURLUcode uc;
-
- DEBUGASSERT(type != FOLLOW_NONE);
-
- if(type != FOLLOW_FAKE)
- data->state.requests++; /* count all real follows */
- if(type == FOLLOW_REDIR) {
- if((data->set.maxredirs != -1) &&
- (data->state.followlocation >= data->set.maxredirs)) {
- reachedmax = TRUE;
- type = FOLLOW_FAKE; /* switch to fake to store the would-be-redirected
- to URL */
- }
- else {
- data->state.followlocation++; /* count redirect-followings, including
- auth reloads */
-
- if(data->set.http_auto_referer) {
- CURLU *u;
- char *referer = NULL;
-
- /* We are asked to automatically set the previous URL as the referer
- when we get the next URL. We pick the ->url field, which may or may
- not be 100% correct */
-
- if(data->state.referer_alloc) {
- Curl_safefree(data->state.referer);
- data->state.referer_alloc = FALSE;
- }
-
- /* Make a copy of the URL without credentials and fragment */
- u = curl_url();
- if(!u)
- return CURLE_OUT_OF_MEMORY;
-
- uc = curl_url_set(u, CURLUPART_URL, data->state.url, 0);
- if(!uc)
- uc = curl_url_set(u, CURLUPART_FRAGMENT, NULL, 0);
- if(!uc)
- uc = curl_url_set(u, CURLUPART_USER, NULL, 0);
- if(!uc)
- uc = curl_url_set(u, CURLUPART_PASSWORD, NULL, 0);
- if(!uc)
- uc = curl_url_get(u, CURLUPART_URL, &referer, 0);
-
- curl_url_cleanup(u);
-
- if(uc || !referer)
- return CURLE_OUT_OF_MEMORY;
-
- data->state.referer = referer;
- data->state.referer_alloc = TRUE; /* yes, free this later */
- }
- }
- }
-
- if((type != FOLLOW_RETRY) &&
- (data->req.httpcode != 401) && (data->req.httpcode != 407) &&
- Curl_is_absolute_url(newurl, NULL, 0, FALSE)) {
- /* If this is not redirect due to a 401 or 407 response and an absolute
- URL: do not allow a custom port number */
- disallowport = TRUE;
- }
-
- DEBUGASSERT(data->state.uh);
- uc = curl_url_set(data->state.uh, CURLUPART_URL, newurl, (unsigned int)
- ((type == FOLLOW_FAKE) ? CURLU_NON_SUPPORT_SCHEME :
- ((type == FOLLOW_REDIR) ? CURLU_URLENCODE : 0) |
- CURLU_ALLOW_SPACE |
- (data->set.path_as_is ? CURLU_PATH_AS_IS : 0)));
- if(uc) {
- if(type != FOLLOW_FAKE) {
- failf(data, "The redirect target URL could not be parsed: %s",
- curl_url_strerror(uc));
- return Curl_uc_to_curlcode(uc);
- }
-
- /* the URL could not be parsed for some reason, but since this is FAKE
- mode, just duplicate the field as-is */
- newurl = strdup(newurl);
- if(!newurl)
- return CURLE_OUT_OF_MEMORY;
- }
- else {
- uc = curl_url_get(data->state.uh, CURLUPART_URL, &newurl, 0);
- if(uc)
- return Curl_uc_to_curlcode(uc);
-
- /* Clear auth if this redirects to a different port number or protocol,
- unless permitted */
- if(!data->set.allow_auth_to_other_hosts && (type != FOLLOW_FAKE)) {
- char *portnum;
- int port;
- bool clear = FALSE;
-
- if(data->set.use_port && data->state.allow_port)
- /* a custom port is used */
- port = (int)data->set.use_port;
- else {
- uc = curl_url_get(data->state.uh, CURLUPART_PORT, &portnum,
- CURLU_DEFAULT_PORT);
- if(uc) {
- free(newurl);
- return Curl_uc_to_curlcode(uc);
- }
- port = atoi(portnum);
- free(portnum);
- }
- if(port != data->info.conn_remote_port) {
- infof(data, "Clear auth, redirects to port from %u to %u",
- data->info.conn_remote_port, port);
- clear = TRUE;
- }
- else {
- char *scheme;
- const struct Curl_handler *p;
- uc = curl_url_get(data->state.uh, CURLUPART_SCHEME, &scheme, 0);
- if(uc) {
- free(newurl);
- return Curl_uc_to_curlcode(uc);
- }
-
- p = Curl_get_scheme_handler(scheme);
- if(p && (p->protocol != data->info.conn_protocol)) {
- infof(data, "Clear auth, redirects scheme from %s to %s",
- data->info.conn_scheme, scheme);
- clear = TRUE;
- }
- free(scheme);
- }
- if(clear) {
- Curl_safefree(data->state.aptr.user);
- Curl_safefree(data->state.aptr.passwd);
- }
- }
- }
-
- if(type == FOLLOW_FAKE) {
- /* we are only figuring out the new URL if we would have followed locations
- but now we are done so we can get out! */
- data->info.wouldredirect = newurl;
-
- if(reachedmax) {
- failf(data, "Maximum (%ld) redirects followed", data->set.maxredirs);
- return CURLE_TOO_MANY_REDIRECTS;
- }
- return CURLE_OK;
- }
-
- if(disallowport)
- data->state.allow_port = FALSE;
-
- if(data->state.url_alloc)
- Curl_safefree(data->state.url);
-
- data->state.url = newurl;
- data->state.url_alloc = TRUE;
- Curl_req_soft_reset(&data->req, data);
- infof(data, "Issue another request to this URL: '%s'", data->state.url);
-
- /*
- * We get here when the HTTP code is 300-399 (and 401). We need to perform
- * differently based on exactly what return code there was.
- *
- * News from 7.10.6: we can also get here on a 401 or 407, in case we act on
- * an HTTP (proxy-) authentication scheme other than Basic.
- */
- switch(data->info.httpcode) {
- /* 401 - Act on a WWW-Authenticate, we keep on moving and do the
- Authorization: XXXX header in the HTTP request code snippet */
- /* 407 - Act on a Proxy-Authenticate, we keep on moving and do the
- Proxy-Authorization: XXXX header in the HTTP request code snippet */
- /* 300 - Multiple Choices */
- /* 306 - Not used */
- /* 307 - Temporary Redirect */
- default: /* for all above (and the unknown ones) */
- /* Some codes are explicitly mentioned since I have checked RFC2616 and
- * they seem to be OK to POST to.
- */
- break;
- case 301: /* Moved Permanently */
- /* (quote from RFC7231, section 6.4.2)
- *
- * Note: For historical reasons, a user agent MAY change the request
- * method from POST to GET for the subsequent request. If this
- * behavior is undesired, the 307 (Temporary Redirect) status code
- * can be used instead.
- *
- * ----
- *
- * Many webservers expect this, so these servers often answers to a POST
- * request with an error page. To be sure that libcurl gets the page that
- * most user agents would get, libcurl has to force GET.
- *
- * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and
- * can be overridden with CURLOPT_POSTREDIR.
- */
- if((data->state.httpreq == HTTPREQ_POST
- || data->state.httpreq == HTTPREQ_POST_FORM
- || data->state.httpreq == HTTPREQ_POST_MIME)
- && !(data->set.keep_post & CURL_REDIR_POST_301)) {
- infof(data, "Switch from POST to GET");
- data->state.httpreq = HTTPREQ_GET;
- Curl_creader_set_rewind(data, FALSE);
- }
- break;
- case 302: /* Found */
- /* (quote from RFC7231, section 6.4.3)
- *
- * Note: For historical reasons, a user agent MAY change the request
- * method from POST to GET for the subsequent request. If this
- * behavior is undesired, the 307 (Temporary Redirect) status code
- * can be used instead.
- *
- * ----
- *
- * Many webservers expect this, so these servers often answers to a POST
- * request with an error page. To be sure that libcurl gets the page that
- * most user agents would get, libcurl has to force GET.
- *
- * This behavior is forbidden by RFC1945 and the obsolete RFC2616, and
- * can be overridden with CURLOPT_POSTREDIR.
- */
- if((data->state.httpreq == HTTPREQ_POST
- || data->state.httpreq == HTTPREQ_POST_FORM
- || data->state.httpreq == HTTPREQ_POST_MIME)
- && !(data->set.keep_post & CURL_REDIR_POST_302)) {
- infof(data, "Switch from POST to GET");
- data->state.httpreq = HTTPREQ_GET;
- Curl_creader_set_rewind(data, FALSE);
- }
- break;
-
- case 303: /* See Other */
- /* 'See Other' location is not the resource but a substitute for the
- * resource. In this case we switch the method to GET/HEAD, unless the
- * method is POST and the user specified to keep it as POST.
- * https://github.com/curl/curl/issues/5237#issuecomment-614641049
- */
- if(data->state.httpreq != HTTPREQ_GET &&
- ((data->state.httpreq != HTTPREQ_POST &&
- data->state.httpreq != HTTPREQ_POST_FORM &&
- data->state.httpreq != HTTPREQ_POST_MIME) ||
- !(data->set.keep_post & CURL_REDIR_POST_303))) {
- data->state.httpreq = HTTPREQ_GET;
- infof(data, "Switch to %s",
- data->req.no_body ? "HEAD" : "GET");
- }
- break;
- case 304: /* Not Modified */
- /* 304 means we did a conditional request and it was "Not modified".
- * We should not get any Location: header in this response!
- */
- break;
- case 305: /* Use Proxy */
- /* (quote from RFC2616, section 10.3.6):
- * "The requested resource MUST be accessed through the proxy given
- * by the Location field. The Location field gives the URI of the
- * proxy. The recipient is expected to repeat this single request
- * via the proxy. 305 responses MUST only be generated by origin
- * servers."
- */
- break;
- }
- Curl_pgrsTime(data, TIMER_REDIRECT);
- Curl_pgrsResetTransferSizes(data);
-
- return CURLE_OK;
-#endif /* CURL_DISABLE_HTTP */
}
static CURLMcode state_performing(struct Curl_easy *data,
@@ -2172,6 +1854,7 @@ static CURLMcode state_performing(struct Curl_easy *data,
data->req.done = TRUE;
}
}
+#ifndef CURL_DISABLE_HTTP
else if((CURLE_HTTP2_STREAM == result) &&
Curl_h2_http_1_1_error(data)) {
CURLcode ret = Curl_retry_request(data, &newurl);
@@ -2179,7 +1862,8 @@ static CURLMcode state_performing(struct Curl_easy *data,
if(!ret) {
infof(data, "Downgrades to HTTP/1.1");
streamclose(data->conn, "Disconnect HTTP/2 for HTTP/1");
- data->state.httpwant = CURL_HTTP_VERSION_1_1;
+ data->state.http_neg.wanted = CURL_HTTP_V1x;
+ data->state.http_neg.allowed = CURL_HTTP_V1x;
/* clear the error message bit too as we ignore the one we got */
data->state.errorbuf = FALSE;
if(!newurl)
@@ -2194,6 +1878,7 @@ static CURLMcode state_performing(struct Curl_easy *data,
else
result = ret;
}
+#endif
if(result) {
/*
@@ -2212,6 +1897,7 @@ static CURLMcode state_performing(struct Curl_easy *data,
multi_done(data, result, TRUE);
}
else if(data->req.done && !Curl_cwriter_is_paused(data)) {
+ const struct Curl_handler *handler = data->conn->handler;
/* call this even if the readwrite function returned error */
multi_posttransfer(data);
@@ -2232,7 +1918,7 @@ static CURLMcode state_performing(struct Curl_easy *data,
follow = FOLLOW_RETRY;
(void)multi_done(data, CURLE_OK, FALSE);
/* multi_done() might return CURLE_GOT_NOTHING */
- result = multi_follow(data, newurl, follow);
+ result = multi_follow(data, handler, newurl, follow);
if(!result) {
multistate(data, MSTATE_SETUP);
rc = CURLM_CALL_MULTI_PERFORM;
@@ -2247,7 +1933,7 @@ static CURLMcode state_performing(struct Curl_easy *data,
free(newurl);
newurl = data->req.location;
data->req.location = NULL;
- result = multi_follow(data, newurl, FOLLOW_FAKE);
+ result = multi_follow(data, handler, newurl, FOLLOW_FAKE);
if(result) {
*stream_errorp = TRUE;
result = multi_done(data, result, TRUE);
@@ -2299,7 +1985,7 @@ static CURLMcode state_do(struct Curl_easy *data,
}
}
- if(data->set.connect_only == 1) {
+ if(data->set.connect_only && !data->set.connect_only_ws) {
/* keep connection open for application to use the socket */
connkeep(data->conn, "CONNECT_ONLY");
multistate(data, MSTATE_DONE);
@@ -2356,6 +2042,7 @@ static CURLMcode state_do(struct Curl_easy *data,
* unexpectedly died. If possible, send the connection back to the
* CONNECT phase so we can try again.
*/
+ const struct Curl_handler *handler = data->conn->handler;
char *newurl = NULL;
followtype follow = FOLLOW_NONE;
CURLcode drc;
@@ -2375,7 +2062,7 @@ static CURLMcode state_do(struct Curl_easy *data,
if(newurl) {
if(!drc || (drc == CURLE_SEND_ERROR)) {
follow = FOLLOW_RETRY;
- drc = multi_follow(data, newurl, follow);
+ drc = multi_follow(data, handler, newurl, follow);
if(!drc) {
multistate(data, MSTATE_SETUP);
rc = CURLM_CALL_MULTI_PERFORM;
@@ -2467,43 +2154,18 @@ static CURLMcode state_resolving(struct Curl_multi *multi,
CURLcode *resultp)
{
struct Curl_dns_entry *dns = NULL;
- struct connectdata *conn = data->conn;
- const char *hostname;
- CURLcode result = CURLE_OK;
+ CURLcode result;
CURLMcode rc = CURLM_OK;
- DEBUGASSERT(conn);
-#ifndef CURL_DISABLE_PROXY
- if(conn->bits.httpproxy)
- hostname = conn->http_proxy.host.name;
- else
-#endif
- if(conn->bits.conn_to_host)
- hostname = conn->conn_to_host.name;
- else
- hostname = conn->host.name;
-
- /* check if we have the name resolved by now */
- dns = Curl_fetch_addr(data, hostname, conn->primary.remote_port);
-
- if(dns) {
-#ifdef CURLRES_ASYNCH
- data->state.async.dns = dns;
- data->state.async.done = TRUE;
-#endif
- result = CURLE_OK;
- infof(data, "Hostname '%s' was found in DNS cache", hostname);
- }
-
- if(!dns)
- result = Curl_resolv_check(data, &dns);
-
+ result = Curl_resolv_check(data, &dns);
+ CURL_TRC_DNS(data, "Curl_resolv_check() -> %d, %s",
+ result, dns ? "found" : "missing");
/* Update sockets here, because the socket(s) may have been closed and the
application thus needs to be told, even if it is likely that the same
socket(s) will again be used further down. If the name has not yet been
resolved, it is likely that new sockets have been opened in an attempt to
contact another resolver. */
- rc = singlesocket(multi, data);
+ rc = Curl_multi_ev_assess_xfer(multi, data);
if(rc)
return rc;
@@ -2511,7 +2173,7 @@ static CURLMcode state_resolving(struct Curl_multi *multi,
bool connected;
/* Perform the next step in the connection phase, and then move on to the
WAITCONNECT state */
- result = Curl_once_resolved(data, &connected);
+ result = Curl_once_resolved(data, dns, &connected);
if(result)
/* if Curl_once_resolved() returns failure, the connection struct is
@@ -2551,10 +2213,9 @@ static CURLMcode state_connect(struct Curl_multi *multi,
/* There was no connection available. We will go to the pending state and
wait for an available connection. */
multistate(data, MSTATE_PENDING);
- /* unlink from process list */
- Curl_node_remove(&data->multi_queue);
- /* add handle to pending list */
- Curl_llist_append(&multi->pending, data, &data->multi_queue);
+ /* move from process to pending set */
+ Curl_uint_bset_remove(&multi->process, data->mid);
+ Curl_uint_bset_add(&multi->pending, data->mid);
*resultp = CURLE_OK;
return rc;
}
@@ -2622,7 +2283,7 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
rc = CURLM_OK;
if(multi_ischanged(multi, TRUE)) {
- DEBUGF(infof(data, "multi changed, check CONNECT_PEND queue"));
+ CURL_TRC_M(data, "multi changed, check CONNECT_PEND queue");
process_pending_handles(multi); /* multiplexed */
}
@@ -2878,7 +2539,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi,
case MSTATE_PENDING:
case MSTATE_MSGSENT:
/* handles in these states should NOT be in this list */
- DEBUGASSERT(0);
break;
default:
@@ -2923,7 +2583,7 @@ statemachine_end:
We do not have to do this in every case block above where a
failure is detected */
Curl_detach_connection(data);
- Curl_cpool_disconnect(data, conn, dead_connection);
+ Curl_conn_terminate(data, conn, dead_connection);
}
}
else if(data->mstate == MSTATE_CONNECT) {
@@ -2950,9 +2610,22 @@ statemachine_end:
}
if(MSTATE_COMPLETED == data->mstate) {
- if(data->set.fmultidone) {
- /* signal via callback instead */
- data->set.fmultidone(data, result);
+ if(data->master_mid != UINT_MAX) {
+ /* A sub transfer, not for msgsent to application */
+ struct Curl_easy *mdata;
+
+ CURL_TRC_M(data, "sub xfer done for master %u", data->master_mid);
+ mdata = Curl_multi_get_easy(multi, data->master_mid);
+ if(mdata) {
+ if(mdata->sub_xfer_done)
+ mdata->sub_xfer_done(mdata, data, result);
+ else
+ CURL_TRC_M(data, "master easy %u without sub_xfer_done callback.",
+ data->master_mid);
+ }
+ else {
+ CURL_TRC_M(data, "master easy %u already gone.", data->master_mid);
+ }
}
else {
/* now fill in the Curl_message with this info */
@@ -2967,10 +2640,11 @@ statemachine_end:
}
multistate(data, MSTATE_MSGSENT);
- /* unlink from the process list */
- Curl_node_remove(&data->multi_queue);
- /* add this handle msgsent list */
- Curl_llist_append(&multi->msgsent, data, &data->multi_queue);
+ /* remove from the other sets, add to msgsent */
+ Curl_uint_bset_remove(&multi->process, data->mid);
+ Curl_uint_bset_remove(&multi->pending, data->mid);
+ Curl_uint_bset_add(&multi->msgsent, data->mid);
+ --multi->xfers_alive;
return CURLM_OK;
}
} while((rc == CURLM_CALL_MULTI_PERFORM) || multi_ischanged(multi, FALSE));
@@ -2984,10 +2658,9 @@ CURLMcode curl_multi_perform(CURLM *m, int *running_handles)
{
CURLMcode returncode = CURLM_OK;
struct Curl_tree *t = NULL;
- struct curltime now = Curl_now();
- struct Curl_llist_node *e;
- struct Curl_llist_node *n = NULL;
+ struct curltime now = curlx_now();
struct Curl_multi *multi = m;
+ unsigned int mid;
SIGPIPE_VARIABLE(pipe_st);
if(!GOOD_MULTI_HANDLE(multi))
@@ -2997,30 +2670,35 @@ CURLMcode curl_multi_perform(CURLM *m, int *running_handles)
return CURLM_RECURSIVE_API_CALL;
sigpipe_init(&pipe_st);
- for(e = Curl_llist_head(&multi->process); e; e = n) {
- struct Curl_easy *data = Curl_node_elem(e);
- CURLMcode result;
- /* Do the loop and only alter the signal ignore state if the next handle
- has a different NO_SIGNAL state than the previous */
-
- /* the current node might be unlinked in multi_runsingle(), get the next
- pointer now */
- n = Curl_node_next(e);
-
- if(data != multi->cpool.idata) {
- /* connection pool handle is processed below */
- sigpipe_apply(data, &pipe_st);
- result = multi_runsingle(multi, &now, data);
- if(result)
- returncode = result;
+ if(Curl_uint_bset_first(&multi->process, &mid)) {
+ CURL_TRC_M(multi->admin, "multi_perform(running=%u)",
+ Curl_multi_xfers_running(multi));
+ do {
+ struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
+ CURLMcode result;
+ if(!data) {
+ DEBUGASSERT(0);
+ Curl_uint_bset_remove(&multi->process, mid);
+ continue;
+ }
+ if(data != multi->admin) {
+ /* admin handle is processed below */
+ sigpipe_apply(data, &pipe_st);
+ result = multi_runsingle(multi, &now, data);
+ if(result)
+ returncode = result;
+ }
}
+ while(Curl_uint_bset_next(&multi->process, mid, &mid));
}
- sigpipe_apply(multi->cpool.idata, &pipe_st);
- Curl_cpool_multi_perform(multi);
-
+ sigpipe_apply(multi->admin, &pipe_st);
+ Curl_cshutdn_perform(&multi->cshutdn, multi->admin, CURL_SOCKET_TIMEOUT);
sigpipe_restore(&pipe_st);
+ if(multi_ischanged(m, TRUE))
+ process_pending_handles(m);
+
/*
* Simply remove all expired timers from the splay since handles are dealt
* with unconditionally by this function and curl_multi_timeout() requires
@@ -3048,8 +2726,10 @@ CURLMcode curl_multi_perform(CURLM *m, int *running_handles)
}
} while(t);
- if(running_handles)
- *running_handles = (int)multi->num_alive;
+ if(running_handles) {
+ unsigned int running = Curl_multi_xfers_running(multi);
+ *running_handles = (running < INT_MAX) ? (int)running : INT_MAX;
+ }
if(CURLM_OK >= returncode)
returncode = Curl_update_timer(multi);
@@ -3057,70 +2737,68 @@ CURLMcode curl_multi_perform(CURLM *m, int *running_handles)
return returncode;
}
-/* unlink_all_msgsent_handles() moves all nodes back from the msgsent list to
- the process list */
-static void unlink_all_msgsent_handles(struct Curl_multi *multi)
-{
- struct Curl_llist_node *e;
- for(e = Curl_llist_head(&multi->msgsent); e; e = Curl_node_next(e)) {
- struct Curl_easy *data = Curl_node_elem(e);
- if(data) {
- DEBUGASSERT(data->mstate == MSTATE_MSGSENT);
- Curl_node_remove(&data->multi_queue);
- /* put it into the process list */
- Curl_llist_append(&multi->process, data, &data->multi_queue);
- }
- }
-}
-
CURLMcode curl_multi_cleanup(CURLM *m)
{
struct Curl_multi *multi = m;
if(GOOD_MULTI_HANDLE(multi)) {
- struct Curl_llist_node *e;
- struct Curl_llist_node *n;
+ void *entry;
+ unsigned int mid;
if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
- /* move the pending and msgsent entries back to process
- so that there is just one list to iterate over */
- unlink_all_msgsent_handles(multi);
- process_pending_handles(multi);
+ /* First remove all remaining easy handles,
+ * close internal ones. admin handle is special */
+ if(Curl_uint_tbl_first(&multi->xfers, &mid, &entry)) {
+ do {
+ struct Curl_easy *data = entry;
+ if(!GOOD_EASY_HANDLE(data))
+ return CURLM_BAD_HANDLE;
- /* First remove all remaining easy handles */
- for(e = Curl_llist_head(&multi->process); e; e = n) {
- struct Curl_easy *data = Curl_node_elem(e);
-
- if(!GOOD_EASY_HANDLE(data))
- return CURLM_BAD_HANDLE;
-
- n = Curl_node_next(e);
- if(!data->state.done && data->conn)
- /* if DONE was never called for this handle */
- (void)multi_done(data, CURLE_OK, TRUE);
- if(data->dns.hostcachetype == HCACHE_MULTI) {
- /* clear out the usage of the shared DNS cache */
- Curl_hostcache_clean(data, data->dns.hostcache);
- data->dns.hostcache = NULL;
- data->dns.hostcachetype = HCACHE_NONE;
- }
+#ifdef DEBUGBUILD
+ if(mid != data->mid) {
+ CURL_TRC_M(data, "multi_cleanup: still present with mid=%u, "
+ "but unexpected data->mid=%u\n", mid, data->mid);
+ DEBUGASSERT(0);
+ }
+#endif
- data->multi = NULL; /* clear the association */
+ if(data == multi->admin)
+ continue;
+
+ if(!data->state.done && data->conn)
+ /* if DONE was never called for this handle */
+ (void)multi_done(data, CURLE_OK, TRUE);
+
+ data->multi = NULL; /* clear the association */
+ Curl_uint_tbl_remove(&multi->xfers, mid);
+ data->mid = UINT_MAX;
#ifdef USE_LIBPSL
- if(data->psl == &multi->psl)
- data->psl = NULL;
+ if(data->psl == &multi->psl)
+ data->psl = NULL;
#endif
+ if(data->state.internal)
+ Curl_close(&data);
+ }
+ while(Curl_uint_tbl_next(&multi->xfers, mid, &mid, &entry));
}
Curl_cpool_destroy(&multi->cpool);
+ Curl_cshutdn_destroy(&multi->cshutdn, multi->admin);
+ if(multi->admin) {
+ CURL_TRC_M(multi->admin, "multi_cleanup, closing admin handle, done");
+ multi->admin->multi = NULL;
+ Curl_uint_tbl_remove(&multi->xfers, multi->admin->mid);
+ Curl_close(&multi->admin);
+ }
multi->magic = 0; /* not good anymore */
- sockhash_destroy(&multi->sockhash);
+ Curl_multi_ev_cleanup(multi);
Curl_hash_destroy(&multi->proto_hash);
- Curl_hash_destroy(&multi->hostcache);
+ Curl_dnscache_destroy(&multi->dnscache);
Curl_psl_destroy(&multi->psl);
+ Curl_ssl_scache_destroy(multi->ssl_scache);
#ifdef USE_WINSOCK
WSACloseEvent(multi->wsa_event);
@@ -3134,6 +2812,16 @@ CURLMcode curl_multi_cleanup(CURLM *m)
#endif
multi_xfer_bufs_free(multi);
+#ifdef DEBUGBUILD
+ if(Curl_uint_tbl_count(&multi->xfers)) {
+ multi_xfer_tbl_dump(multi);
+ DEBUGASSERT(0);
+ }
+#endif
+ Curl_uint_bset_destroy(&multi->process);
+ Curl_uint_bset_destroy(&multi->pending);
+ Curl_uint_bset_destroy(&multi->msgsent);
+ Curl_uint_tbl_destroy(&multi->xfers);
free(multi);
return CURLM_OK;
@@ -3179,233 +2867,14 @@ CURLMsg *curl_multi_info_read(CURLM *m, int *msgs_in_queue)
return NULL;
}
-/*
- * singlesocket() checks what sockets we deal with and their "action state"
- * and if we have a different state in any of those sockets from last time we
- * call the callback accordingly.
- */
-static CURLMcode singlesocket(struct Curl_multi *multi,
- struct Curl_easy *data)
-{
- struct easy_pollset cur_poll;
- CURLMcode mresult;
- /* Fill in the 'current' struct with the state as it is now: what sockets to
- supervise and for what actions */
- multi_getsock(data, &cur_poll);
- mresult = Curl_multi_pollset_ev(multi, data, &cur_poll, &data->last_poll);
-
- if(!mresult) /* Remember for next time */
- memcpy(&data->last_poll, &cur_poll, sizeof(cur_poll));
- return mresult;
-}
-
-CURLMcode Curl_multi_pollset_ev(struct Curl_multi *multi,
- struct Curl_easy *data,
- struct easy_pollset *ps,
- struct easy_pollset *last_ps)
-{
- unsigned int i;
- struct Curl_sh_entry *entry;
- curl_socket_t s;
- int rc;
-
- /* We have 0 .. N sockets already and we get to know about the 0 .. M
- sockets we should have from now on. Detect the differences, remove no
- longer supervised ones and add new ones */
-
- /* walk over the sockets we got right now */
- for(i = 0; i < ps->num; i++) {
- unsigned char cur_action = ps->actions[i];
- unsigned char last_action = 0;
- int comboaction;
-
- s = ps->sockets[i];
-
- /* get it from the hash */
- entry = sh_getentry(&multi->sockhash, s);
- if(entry) {
- /* check if new for this transfer */
- unsigned int j;
- for(j = 0; j < last_ps->num; j++) {
- if(s == last_ps->sockets[j]) {
- last_action = last_ps->actions[j];
- break;
- }
- }
- }
- else {
- /* this is a socket we did not have before, add it to the hash! */
- entry = sh_addentry(&multi->sockhash, s);
- if(!entry)
- /* fatal */
- return CURLM_OUT_OF_MEMORY;
- }
- if(last_action && (last_action != cur_action)) {
- /* Socket was used already, but different action now */
- if(last_action & CURL_POLL_IN) {
- DEBUGASSERT(entry->readers);
- entry->readers--;
- }
- if(last_action & CURL_POLL_OUT) {
- DEBUGASSERT(entry->writers);
- entry->writers--;
- }
- if(cur_action & CURL_POLL_IN) {
- entry->readers++;
- }
- if(cur_action & CURL_POLL_OUT)
- entry->writers++;
- }
- else if(!last_action &&
- !Curl_hash_pick(&entry->transfers, (char *)&data, /* hash key */
- sizeof(struct Curl_easy *))) {
- DEBUGASSERT(entry->users < 100000); /* detect weird values */
- /* a new transfer using this socket */
- entry->users++;
- if(cur_action & CURL_POLL_IN)
- entry->readers++;
- if(cur_action & CURL_POLL_OUT)
- entry->writers++;
- /* add 'data' to the transfer hash on this socket! */
- if(!Curl_hash_add(&entry->transfers, (char *)&data, /* hash key */
- sizeof(struct Curl_easy *), data)) {
- Curl_hash_destroy(&entry->transfers);
- return CURLM_OUT_OF_MEMORY;
- }
- }
-
- comboaction = (entry->writers ? CURL_POLL_OUT : 0) |
- (entry->readers ? CURL_POLL_IN : 0);
-
- /* socket existed before and has the same action set as before */
- if(last_action && ((int)entry->action == comboaction))
- /* same, continue */
- continue;
-
- if(multi->socket_cb) {
- set_in_callback(multi, TRUE);
- rc = multi->socket_cb(data, s, comboaction, multi->socket_userp,
- entry->socketp);
-
- set_in_callback(multi, FALSE);
- if(rc == -1) {
- multi->dead = TRUE;
- return CURLM_ABORTED_BY_CALLBACK;
- }
- }
-
- /* store the current action state */
- entry->action = (unsigned int)comboaction;
- }
-
- /* Check for last_poll.sockets that no longer appear in ps->sockets.
- * Need to remove the easy handle from the multi->sockhash->transfers and
- * remove multi->sockhash entry when this was the last transfer */
- for(i = 0; i < last_ps->num; i++) {
- unsigned int j;
- bool stillused = FALSE;
- s = last_ps->sockets[i];
- for(j = 0; j < ps->num; j++) {
- if(s == ps->sockets[j]) {
- /* this is still supervised */
- stillused = TRUE;
- break;
- }
- }
- if(stillused)
- continue;
-
- entry = sh_getentry(&multi->sockhash, s);
- /* if this is NULL here, the socket has been closed and notified so
- already by Curl_multi_closed() */
- if(entry) {
- unsigned char oldactions = last_ps->actions[i];
- /* this socket has been removed. Decrease user count */
- DEBUGASSERT(entry->users);
- entry->users--;
- if(oldactions & CURL_POLL_OUT)
- entry->writers--;
- if(oldactions & CURL_POLL_IN)
- entry->readers--;
- if(!entry->users) {
- bool dead = FALSE;
- if(multi->socket_cb) {
- set_in_callback(multi, TRUE);
- rc = multi->socket_cb(data, s, CURL_POLL_REMOVE,
- multi->socket_userp, entry->socketp);
- set_in_callback(multi, FALSE);
- if(rc == -1)
- dead = TRUE;
- }
- sh_delentry(entry, &multi->sockhash, s);
- if(dead) {
- multi->dead = TRUE;
- return CURLM_ABORTED_BY_CALLBACK;
- }
- }
- else {
- /* still users, but remove this handle as a user of this socket */
- if(Curl_hash_delete(&entry->transfers, (char *)&data,
- sizeof(struct Curl_easy *))) {
- DEBUGASSERT(NULL);
- }
- }
- }
- } /* for loop over num */
-
- return CURLM_OK;
-}
-
-CURLcode Curl_updatesocket(struct Curl_easy *data)
-{
- if(singlesocket(data->multi, data))
- return CURLE_ABORTED_BY_CALLBACK;
- return CURLE_OK;
-}
-
-
-/*
- * Curl_multi_closed()
- *
- * Used by the connect code to tell the multi_socket code that one of the
- * sockets we were using is about to be closed. This function will then
- * remove it from the sockethash for this handle to make the multi_socket API
- * behave properly, especially for the case when libcurl will create another
- * socket again and it gets the same file descriptor number.
- */
-
-void Curl_multi_closed(struct Curl_easy *data, curl_socket_t s)
+void Curl_multi_will_close(struct Curl_easy *data, curl_socket_t s)
{
if(data) {
- /* if there is still an easy handle associated with this connection */
struct Curl_multi *multi = data->multi;
- DEBUGF(infof(data, "Curl_multi_closed, fd=%" FMT_SOCKET_T
- " multi is %p", s, (void *)multi));
if(multi) {
- /* this is set if this connection is part of a handle that is added to
- a multi handle, and only then this is necessary */
- struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s);
-
- DEBUGF(infof(data, "Curl_multi_closed, fd=%" FMT_SOCKET_T
- " entry is %p", s, (void *)entry));
- if(entry) {
- int rc = 0;
- if(multi->socket_cb) {
- set_in_callback(multi, TRUE);
- rc = multi->socket_cb(data, s, CURL_POLL_REMOVE,
- multi->socket_userp, entry->socketp);
- set_in_callback(multi, FALSE);
- }
-
- /* now remove it from the socket hash */
- sh_delentry(entry, &multi->sockhash, s);
- if(rc == -1)
- /* This just marks the multi handle as "dead" without returning an
- error code primarily because this function is used from many
- places where propagating an error back is tricky. */
- multi->dead = TRUE;
- }
+ CURL_TRC_M(data, "Curl_multi_will_close fd=%" FMT_SOCKET_T, s);
+ Curl_multi_ev_socket_done(multi, data, s);
}
}
}
@@ -3436,7 +2905,7 @@ static CURLMcode add_next_timeout(struct curltime now,
for(e = Curl_llist_head(list); e;) {
struct Curl_llist_node *n = Curl_node_next(e);
struct time_node *node = Curl_node_elem(e);
- timediff_t diff = Curl_timediff_us(node->time, now);
+ timediff_t diff = curlx_timediff_us(node->time, now);
if(diff <= 0)
/* remove outdated entry */
Curl_node_remove(e);
@@ -3497,7 +2966,7 @@ static CURLMcode multi_run_expired(struct multi_run_ctx *mrc)
continue;
(void)add_next_timeout(mrc->now, multi, data);
- if(data == multi->cpool.idata) {
+ if(data == multi->admin) {
mrc->run_cpool = TRUE;
continue;
}
@@ -3507,9 +2976,8 @@ static CURLMcode multi_run_expired(struct multi_run_ctx *mrc)
result = multi_runsingle(multi, &mrc->now, data);
if(CURLM_OK >= result) {
- /* get the socket(s) and check if the state has been changed since
- last */
- result = singlesocket(multi, data);
+ /* reassess event handling of data */
+ result = Curl_multi_ev_assess_xfer(multi, data);
if(result)
goto out;
}
@@ -3525,66 +2993,37 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
int *running_handles)
{
CURLMcode result = CURLM_OK;
- struct Curl_easy *data = NULL;
struct multi_run_ctx mrc;
(void)ev_bitmask;
memset(&mrc, 0, sizeof(mrc));
mrc.multi = multi;
- mrc.now = Curl_now();
+ mrc.now = curlx_now();
sigpipe_init(&mrc.pipe_st);
if(checkall) {
- struct Curl_llist_node *e;
/* *perform() deals with running_handles on its own */
result = curl_multi_perform(multi, running_handles);
- /* walk through each easy handle and do the socket state change magic
- and callbacks */
if(result != CURLM_BAD_HANDLE) {
- for(e = Curl_llist_head(&multi->process); e && !result;
- e = Curl_node_next(e)) {
- result = singlesocket(multi, Curl_node_elem(e));
- }
+ /* Reassess event status of all active transfers */
+ result = Curl_multi_ev_assess_xfer_bset(multi, &multi->process);
}
mrc.run_cpool = TRUE;
goto out;
}
if(s != CURL_SOCKET_TIMEOUT) {
- struct Curl_sh_entry *entry = sh_getentry(&multi->sockhash, s);
-
- if(!entry) {
- /* Unmatched socket, we cannot act on it but we ignore this fact. In
- real-world tests it has been proved that libevent can in fact give
- the application actions even though the socket was just previously
- asked to get removed, so thus we better survive stray socket actions
- and just move on. */
- /* The socket might come from a connection that is being shut down
- * by the multi's connection pool. */
- Curl_cpool_multi_socket(multi, s, ev_bitmask);
- }
- else {
- struct Curl_hash_iterator iter;
- struct Curl_hash_element *he;
-
- /* the socket can be shared by many transfers, iterate */
- Curl_hash_start_iterate(&entry->transfers, &iter);
- for(he = Curl_hash_next_element(&iter); he;
- he = Curl_hash_next_element(&iter)) {
- data = (struct Curl_easy *)he->ptr;
- DEBUGASSERT(data);
- DEBUGASSERT(data->magic == CURLEASY_MAGIC_NUMBER);
-
- if(data == multi->cpool.idata)
- mrc.run_cpool = TRUE;
- else {
- /* Expire with out current now, so we will get it below when
- * asking the splaytree for expired transfers. */
- expire_ex(data, &mrc.now, 0, EXPIRE_RUN_NOW);
- }
- }
- }
+ Curl_multi_ev_expire_xfers(multi, s, &mrc.now, &mrc.run_cpool);
+ }
+ else {
+ /* Asked to run due to time-out. Clear the 'last_expire_ts' variable to
+ force Curl_update_timer() to trigger a callback to the app again even
+ if the same timeout is still the one to run after this call. That
+ handles the case when the application asks libcurl to run the timeout
+ prematurely. */
+ memset(&multi->last_expire_ts, 0, sizeof(multi->last_expire_ts));
+ mrc.run_cpool = TRUE;
}
result = multi_run_expired(&mrc);
@@ -3597,19 +3036,24 @@ static CURLMcode multi_socket(struct Curl_multi *multi,
* to set a 0 timeout and call us again, we run them here.
* Do that only once or it might be unfair to transfers on other
* sockets. */
- mrc.now = Curl_now();
+ mrc.now = curlx_now();
result = multi_run_expired(&mrc);
}
out:
if(mrc.run_cpool) {
- sigpipe_apply(multi->cpool.idata, &mrc.pipe_st);
- Curl_cpool_multi_perform(multi);
+ sigpipe_apply(multi->admin, &mrc.pipe_st);
+ Curl_cshutdn_perform(&multi->cshutdn, multi->admin, s);
}
sigpipe_restore(&mrc.pipe_st);
- if(running_handles)
- *running_handles = (int)multi->num_alive;
+ if(multi_ischanged(multi, TRUE))
+ process_pending_handles(multi);
+
+ if(running_handles) {
+ unsigned int running = Curl_multi_xfers_running(multi);
+ *running_handles = (running < INT_MAX) ? (int)running : INT_MAX;
+ }
if(CURLM_OK >= result)
result = Curl_update_timer(multi);
@@ -3665,9 +3109,6 @@ CURLMcode curl_multi_setopt(CURLM *m,
break;
case CURLMOPT_MAX_TOTAL_CONNECTIONS:
multi->max_total_connections = va_arg(param, long);
- /* for now, let this also decide the max number of connections
- * in shutdown handling */
- multi->max_shutdown_connections = va_arg(param, long);
break;
/* options formerly used for pipelining */
case CURLMOPT_MAX_PIPELINE_LENGTH:
@@ -3737,20 +3178,20 @@ static CURLMcode multi_timeout(struct Curl_multi *multi,
if(multi->timetree) {
/* we have a tree of expire times */
- struct curltime now = Curl_now();
+ struct curltime now = curlx_now();
/* splay the lowest to the bottom */
multi->timetree = Curl_splay(tv_zero, multi->timetree);
- /* this will not return NULL from a non-emtpy tree, but some compilers
+ /* this will not return NULL from a non-empty tree, but some compilers
* are not convinced of that. Analyzers are hard. */
*expire_time = multi->timetree ? multi->timetree->key : tv_zero;
/* 'multi->timetree' will be non-NULL here but the compilers sometimes
yell at us if we assume so */
if(multi->timetree &&
- Curl_timediff_us(multi->timetree->key, now) > 0) {
+ curlx_timediff_us(multi->timetree->key, now) > 0) {
/* some time left before expiration */
- timediff_t diff = Curl_timediff_ceil(multi->timetree->key, now);
+ timediff_t diff = curlx_timediff_ceil(multi->timetree->key, now);
/* this should be safe even on 32-bit archs, as we do not use that
overly long timeouts */
*timeout_ms = (long)diff;
@@ -3823,7 +3264,7 @@ CURLMcode Curl_update_timer(struct Curl_multi *multi)
#endif
set_value = TRUE;
}
- else if(Curl_timediff_us(multi->last_expire_ts, expire_ts)) {
+ else if(curlx_timediff_us(multi->last_expire_ts, expire_ts)) {
/* We had a timeout before and have one now, the absolute timestamp
* differs. The relative timeout_ms may be the same, but the starting
* point differs. Let the application restart its timer. */
@@ -3907,7 +3348,7 @@ multi_addtimeout(struct Curl_easy *data,
/* find the correct spot in the list */
for(e = Curl_llist_head(timeoutlist); e; e = Curl_node_next(e)) {
struct time_node *check = Curl_node_elem(e);
- timediff_t diff = Curl_timediff(check->time, node->time);
+ timediff_t diff = curlx_timediff(check->time, node->time);
if(diff > 0)
break;
prev = e;
@@ -3921,9 +3362,9 @@ multi_addtimeout(struct Curl_easy *data,
return CURLM_OK;
}
-static void expire_ex(struct Curl_easy *data,
- const struct curltime *nowp,
- timediff_t milli, expire_id id)
+void Curl_expire_ex(struct Curl_easy *data,
+ const struct curltime *nowp,
+ timediff_t milli, expire_id id)
{
struct Curl_multi *multi = data->multi;
struct curltime *curr_expire = &data->state.expiretime;
@@ -3956,7 +3397,7 @@ static void expire_ex(struct Curl_easy *data,
/* This means that the struct is added as a node in the splay tree.
Compare if the new time is earlier, and only remove-old/add-new if it
is. */
- timediff_t diff = Curl_timediff(set, *curr_expire);
+ timediff_t diff = curlx_timediff(set, *curr_expire);
int rc;
if(diff > 0) {
@@ -3994,8 +3435,8 @@ static void expire_ex(struct Curl_easy *data,
*/
void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id id)
{
- struct curltime now = Curl_now();
- expire_ex(data, &now, milli, id);
+ struct curltime now = curlx_now();
+ Curl_expire_ex(data, &now, milli, id);
}
/*
@@ -4039,9 +3480,7 @@ bool Curl_expire_clear(struct Curl_easy *data)
/* clear the timeout list too */
Curl_llist_destroy(list, NULL);
-#ifdef DEBUGBUILD
- infof(data, "Expire cleared");
-#endif
+ CURL_TRC_M(data, "Expire cleared");
nowp->tv_sec = 0;
nowp->tv_usec = 0;
return TRUE;
@@ -4052,19 +3491,11 @@ bool Curl_expire_clear(struct Curl_easy *data)
CURLMcode curl_multi_assign(CURLM *m, curl_socket_t s,
void *hashp)
{
- struct Curl_sh_entry *there = NULL;
struct Curl_multi *multi = m;
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
- there = sh_getentry(&multi->sockhash, s);
-
- if(!there)
- return CURLM_BAD_SOCKET;
-
- there->socketp = hashp;
-
- return CURLM_OK;
+ return Curl_multi_ev_assign(multi, s, hashp);
}
static void move_pending_to_connect(struct Curl_multi *multi,
@@ -4072,11 +3503,9 @@ static void move_pending_to_connect(struct Curl_multi *multi,
{
DEBUGASSERT(data->mstate == MSTATE_PENDING);
- /* Remove this node from the pending list */
- Curl_node_remove(&data->multi_queue);
-
- /* put it into the process list */
- Curl_llist_append(&multi->process, data, &data->multi_queue);
+ /* Remove this node from the pending set, add into process set */
+ Curl_uint_bset_remove(&multi->pending, data->mid);
+ Curl_uint_bset_add(&multi->process, data->mid);
multistate(data, MSTATE_CONNECT);
@@ -4100,10 +3529,15 @@ static void move_pending_to_connect(struct Curl_multi *multi,
*/
static void process_pending_handles(struct Curl_multi *multi)
{
- struct Curl_llist_node *e = Curl_llist_head(&multi->pending);
- if(e) {
- struct Curl_easy *data = Curl_node_elem(e);
- move_pending_to_connect(multi, data);
+ unsigned int mid;
+ if(Curl_uint_bset_first(&multi->pending, &mid)) {
+ do {
+ struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
+ DEBUGASSERT(data);
+ if(data)
+ move_pending_to_connect(multi, data);
+ }
+ while(Curl_uint_bset_next(&multi->pending, mid, &mid));
}
}
@@ -4115,7 +3549,7 @@ void Curl_set_in_callback(struct Curl_easy *data, bool value)
bool Curl_is_in_callback(struct Curl_easy *data)
{
- return (data && data->multi && data->multi->in_callback);
+ return data && data->multi && data->multi->in_callback;
}
unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi)
@@ -4127,15 +3561,20 @@ unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi)
CURL **curl_multi_get_handles(CURLM *m)
{
struct Curl_multi *multi = m;
- CURL **a = malloc(sizeof(struct Curl_easy *) * (multi->num_easy + 1));
+ void *entry;
+ unsigned int count = Curl_uint_tbl_count(&multi->xfers);
+ CURL **a = malloc(sizeof(struct Curl_easy *) * (count + 1));
if(a) {
- unsigned int i = 0;
- struct Curl_llist_node *e;
- for(e = Curl_llist_head(&multi->process); e; e = Curl_node_next(e)) {
- struct Curl_easy *data = Curl_node_elem(e);
- DEBUGASSERT(i < multi->num_easy);
- if(!data->state.internal)
- a[i++] = data;
+ unsigned int i = 0, mid;
+
+ if(Curl_uint_tbl_first(&multi->xfers, &mid, &entry)) {
+ do {
+ struct Curl_easy *data = entry;
+ DEBUGASSERT(i < count);
+ if(!data->state.internal)
+ a[i++] = data;
+ }
+ while(Curl_uint_tbl_next(&multi->xfers, mid, &mid, &entry));
}
a[i] = NULL; /* last entry is a NULL */
}
@@ -4307,31 +3746,53 @@ static void multi_xfer_bufs_free(struct Curl_multi *multi)
multi->xfer_sockbuf_borrowed = FALSE;
}
-struct Curl_easy *Curl_multi_get_handle(struct Curl_multi *multi,
- curl_off_t mid)
+struct Curl_easy *Curl_multi_get_easy(struct Curl_multi *multi,
+ unsigned int mid)
{
+ struct Curl_easy *data = mid ? Curl_uint_tbl_get(&multi->xfers, mid) : NULL;
+ if(data && GOOD_EASY_HANDLE(data))
+ return data;
+ CURL_TRC_M(multi->admin, "invalid easy handle in xfer table for mid=%u",
+ mid);
+ Curl_uint_tbl_remove(&multi->xfers, mid);
+ return NULL;
+}
- if(mid >= 0) {
- struct Curl_easy *data;
- struct Curl_llist_node *e;
+unsigned int Curl_multi_xfers_running(struct Curl_multi *multi)
+{
+ return multi->xfers_alive;
+}
- for(e = Curl_llist_head(&multi->process); e; e = Curl_node_next(e)) {
- data = Curl_node_elem(e);
- if(data->mid == mid)
- return data;
- }
- /* may be in msgsent queue */
- for(e = Curl_llist_head(&multi->msgsent); e; e = Curl_node_next(e)) {
- data = Curl_node_elem(e);
- if(data->mid == mid)
- return data;
- }
- /* may be in pending queue */
- for(e = Curl_llist_head(&multi->pending); e; e = Curl_node_next(e)) {
- data = Curl_node_elem(e);
- if(data->mid == mid)
- return data;
- }
+#ifdef DEBUGBUILD
+static void multi_xfer_dump(struct Curl_multi *multi, unsigned int mid,
+ void *entry)
+{
+ struct Curl_easy *data = entry;
+
+ (void)multi;
+ if(!data) {
+ fprintf(stderr, "mid=%u, entry=NULL, bug in xfer table?\n", mid);
}
- return NULL;
+ else {
+ fprintf(stderr, "mid=%u, magic=%s, p=%p, id=%" FMT_OFF_T ", url=%s\n",
+ mid, (data->magic == CURLEASY_MAGIC_NUMBER) ? "GOOD" : "BAD!",
+ (void *)data, data->id, data->state.url);
+ }
+}
+
+static void multi_xfer_tbl_dump(struct Curl_multi *multi)
+{
+ unsigned int mid;
+ void *entry;
+ fprintf(stderr, "=== multi xfer table (count=%u, capacity=%u\n",
+ Curl_uint_tbl_count(&multi->xfers),
+ Curl_uint_tbl_capacity(&multi->xfers));
+ if(Curl_uint_tbl_first(&multi->xfers, &mid, &entry)) {
+ multi_xfer_dump(multi, mid, entry);
+ while(Curl_uint_tbl_next(&multi->xfers, mid, &mid, &entry))
+ multi_xfer_dump(multi, mid, entry);
+ }
+ fprintf(stderr, "===\n");
+ fflush(stderr);
}
+#endif /* DEBUGBUILD */
diff --git a/libs/libcurl/src/multi_ev.c b/libs/libcurl/src/multi_ev.c
new file mode 100644
index 0000000000..dc6556d58f
--- /dev/null
+++ b/libs/libcurl/src/multi_ev.c
@@ -0,0 +1,637 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+
+#include "urldata.h"
+#include "url.h"
+#include "cfilters.h"
+#include "curl_trc.h"
+#include "multiif.h"
+#include "curlx/timeval.h"
+#include "multi_ev.h"
+#include "select.h"
+#include "uint-bset.h"
+#include "uint-spbset.h"
+#include "uint-table.h"
+#include "curlx/warnless.h"
+#include "multihandle.h"
+#include "socks.h"
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+
+static void mev_in_callback(struct Curl_multi *multi, bool value)
+{
+ multi->in_callback = value;
+}
+
+#define CURL_MEV_CONN_HASH_SIZE 3
+
+/* Information about a socket for which we inform the libcurl application
+ * what to supervise (CURL_POLL_IN/CURL_POLL_OUT/CURL_POLL_REMOVE)
+ */
+struct mev_sh_entry {
+ struct uint_spbset xfers; /* bitset of transfers `mid`s on this socket */
+ struct connectdata *conn; /* connection using this socket or NULL */
+ void *user_data; /* libcurl app data via curl_multi_assign() */
+ unsigned int action; /* CURL_POLL_IN/CURL_POLL_OUT we last told the
+ * libcurl application to watch out for */
+ unsigned int readers; /* this many transfers want to read */
+ unsigned int writers; /* this many transfers want to write */
+};
+
+static size_t mev_sh_entry_hash(void *key, size_t key_length, size_t slots_num)
+{
+ curl_socket_t fd = *((curl_socket_t *) key);
+ (void) key_length;
+ return (fd % (curl_socket_t)slots_num);
+}
+
+static size_t mev_sh_entry_compare(void *k1, size_t k1_len,
+ void *k2, size_t k2_len)
+{
+ (void) k1_len; (void) k2_len;
+ return (*((curl_socket_t *) k1)) == (*((curl_socket_t *) k2));
+}
+
+/* sockhash entry destructor callback */
+static void mev_sh_entry_dtor(void *freethis)
+{
+ struct mev_sh_entry *entry = (struct mev_sh_entry *)freethis;
+ Curl_uint_spbset_destroy(&entry->xfers);
+ free(entry);
+}
+
+/* look up a given socket in the socket hash, skip invalid sockets */
+static struct mev_sh_entry *
+mev_sh_entry_get(struct Curl_hash *sh, curl_socket_t s)
+{
+ if(s != CURL_SOCKET_BAD) {
+ /* only look for proper sockets */
+ return Curl_hash_pick(sh, (char *)&s, sizeof(curl_socket_t));
+ }
+ return NULL;
+}
+
+/* make sure this socket is present in the hash for this handle */
+static struct mev_sh_entry *
+mev_sh_entry_add(struct Curl_hash *sh, curl_socket_t s)
+{
+ struct mev_sh_entry *there = mev_sh_entry_get(sh, s);
+ struct mev_sh_entry *check;
+
+ if(there) {
+ /* it is present, return fine */
+ return there;
+ }
+
+ /* not present, add it */
+ check = calloc(1, sizeof(struct mev_sh_entry));
+ if(!check)
+ return NULL; /* major failure */
+
+ Curl_uint_spbset_init(&check->xfers);
+
+ /* make/add new hash entry */
+ if(!Curl_hash_add(sh, (char *)&s, sizeof(curl_socket_t), check)) {
+ mev_sh_entry_dtor(check);
+ return NULL; /* major failure */
+ }
+
+ return check; /* things are good in sockhash land */
+}
+
+/* delete the given socket entry from the hash */
+static void mev_sh_entry_kill(struct Curl_multi *multi, curl_socket_t s)
+{
+ Curl_hash_delete(&multi->ev.sh_entries, (char *)&s, sizeof(curl_socket_t));
+}
+
+static size_t mev_sh_entry_user_count(struct mev_sh_entry *e)
+{
+ return Curl_uint_spbset_count(&e->xfers) + (e->conn ? 1 : 0);
+}
+
+static bool mev_sh_entry_xfer_known(struct mev_sh_entry *e,
+ struct Curl_easy *data)
+{
+ return Curl_uint_spbset_contains(&e->xfers, data->mid);
+}
+
+static bool mev_sh_entry_conn_known(struct mev_sh_entry *e,
+ struct connectdata *conn)
+{
+ return (e->conn == conn);
+}
+
+static bool mev_sh_entry_xfer_add(struct mev_sh_entry *e,
+ struct Curl_easy *data)
+{
+ /* detect weird values */
+ DEBUGASSERT(mev_sh_entry_user_count(e) < 100000);
+ return Curl_uint_spbset_add(&e->xfers, data->mid);
+}
+
+static bool mev_sh_entry_conn_add(struct mev_sh_entry *e,
+ struct connectdata *conn)
+{
+ /* detect weird values */
+ DEBUGASSERT(mev_sh_entry_user_count(e) < 100000);
+ DEBUGASSERT(!e->conn);
+ if(e->conn)
+ return FALSE;
+ e->conn = conn;
+ return TRUE;
+}
+
+
+static bool mev_sh_entry_xfer_remove(struct mev_sh_entry *e,
+ struct Curl_easy *data)
+{
+ bool present = Curl_uint_spbset_contains(&e->xfers, data->mid);
+ if(present)
+ Curl_uint_spbset_remove(&e->xfers, data->mid);
+ return present;
+}
+
+static bool mev_sh_entry_conn_remove(struct mev_sh_entry *e,
+ struct connectdata *conn)
+{
+ DEBUGASSERT(e->conn == conn);
+ if(e->conn == conn) {
+ e->conn = NULL;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/* Purge any information about socket `s`.
+ * Let the socket callback know as well when necessary */
+static CURLMcode mev_forget_socket(struct Curl_multi *multi,
+ struct Curl_easy *data,
+ curl_socket_t s,
+ const char *cause)
+{
+ struct mev_sh_entry *entry = mev_sh_entry_get(&multi->ev.sh_entries, s);
+ int rc = 0;
+
+ if(!entry) /* we never knew or already forgot about this socket */
+ return CURLM_OK;
+
+ /* We managed this socket before, tell the socket callback to forget it. */
+ if(multi->socket_cb) {
+ CURL_TRC_M(data, "ev %s, call(fd=%" FMT_SOCKET_T ", ev=REMOVE)",
+ cause, s);
+ mev_in_callback(multi, TRUE);
+ rc = multi->socket_cb(data, s, CURL_POLL_REMOVE,
+ multi->socket_userp, entry->user_data);
+ mev_in_callback(multi, FALSE);
+ }
+
+ mev_sh_entry_kill(multi, s);
+ if(rc == -1) {
+ multi->dead = TRUE;
+ return CURLM_ABORTED_BY_CALLBACK;
+ }
+ return CURLM_OK;
+}
+
+static CURLMcode mev_sh_entry_update(struct Curl_multi *multi,
+ struct Curl_easy *data,
+ struct mev_sh_entry *entry,
+ curl_socket_t s,
+ unsigned char last_action,
+ unsigned char cur_action)
+{
+ int rc, comboaction;
+
+ /* we should only be called when the callback exists */
+ DEBUGASSERT(multi->socket_cb);
+ if(!multi->socket_cb)
+ return CURLM_OK;
+
+ /* Transfer `data` goes from `last_action` to `cur_action` on socket `s`
+ * with `multi->ev.sh_entries` entry `entry`. Update `entry` and trigger
+ * `multi->socket_cb` on change, if the callback is set. */
+ if(last_action == cur_action) /* nothing from `data` changed */
+ return CURLM_OK;
+
+ if(last_action & CURL_POLL_IN) {
+ DEBUGASSERT(entry->readers);
+ if(!(cur_action & CURL_POLL_IN))
+ entry->readers--;
+ }
+ else if(cur_action & CURL_POLL_IN)
+ entry->readers++;
+
+ if(last_action & CURL_POLL_OUT) {
+ DEBUGASSERT(entry->writers);
+ if(!(cur_action & CURL_POLL_OUT))
+ entry->writers--;
+ }
+ else if(cur_action & CURL_POLL_OUT)
+ entry->writers++;
+
+ DEBUGASSERT(entry->readers <= mev_sh_entry_user_count(entry));
+ DEBUGASSERT(entry->writers <= mev_sh_entry_user_count(entry));
+ DEBUGASSERT(entry->writers + entry->readers);
+
+ CURL_TRC_M(data, "ev update fd=%" FMT_SOCKET_T ", action '%s%s' -> '%s%s'"
+ " (%d/%d r/w)", s,
+ (last_action & CURL_POLL_IN) ? "IN" : "",
+ (last_action & CURL_POLL_OUT) ? "OUT" : "",
+ (cur_action & CURL_POLL_IN) ? "IN" : "",
+ (cur_action & CURL_POLL_OUT) ? "OUT" : "",
+ entry->readers, entry->writers);
+
+ comboaction = (entry->writers ? CURL_POLL_OUT : 0) |
+ (entry->readers ? CURL_POLL_IN : 0);
+ if(((int)entry->action == comboaction)) /* nothing for socket changed */
+ return CURLM_OK;
+
+ CURL_TRC_M(data, "ev update call(fd=%" FMT_SOCKET_T ", ev=%s%s)",
+ s, (comboaction & CURL_POLL_IN) ? "IN" : "",
+ (comboaction & CURL_POLL_OUT) ? "OUT" : "");
+ mev_in_callback(multi, TRUE);
+ rc = multi->socket_cb(data, s, comboaction, multi->socket_userp,
+ entry->user_data);
+
+ mev_in_callback(multi, FALSE);
+ if(rc == -1) {
+ multi->dead = TRUE;
+ return CURLM_ABORTED_BY_CALLBACK;
+ }
+ entry->action = (unsigned int)comboaction;
+ return CURLM_OK;
+}
+
+static CURLMcode mev_pollset_diff(struct Curl_multi *multi,
+ struct Curl_easy *data,
+ struct connectdata *conn,
+ struct easy_pollset *ps,
+ struct easy_pollset *prev_ps)
+{
+ struct mev_sh_entry *entry;
+ curl_socket_t s;
+ unsigned int i, j;
+ CURLMcode mresult;
+
+ /* The transfer `data` reports in `ps` the sockets it is interested
+ * in and which combination of CURL_POLL_IN/CURL_POLL_OUT it wants
+ * to have monitored for events.
+ * There can be more than 1 transfer interested in the same socket
+ * and 1 transfer might be interested in more than 1 socket.
+ * `prev_ps` is the pollset copy from the previous call here. On
+ * the 1st call it will be empty.
+ */
+ DEBUGASSERT(ps);
+ DEBUGASSERT(prev_ps);
+
+ /* Handle changes to sockets the transfer is interested in. */
+ for(i = 0; i < ps->num; i++) {
+ unsigned char last_action;
+ bool first_time = FALSE; /* data/conn appears first time on socket */
+
+ s = ps->sockets[i];
+ /* Have we handled this socket before? */
+ entry = mev_sh_entry_get(&multi->ev.sh_entries, s);
+ if(!entry) {
+ /* new socket, add new entry */
+ first_time = TRUE;
+ entry = mev_sh_entry_add(&multi->ev.sh_entries, s);
+ if(!entry) /* fatal */
+ return CURLM_OUT_OF_MEMORY;
+ CURL_TRC_M(data, "ev new entry fd=%" FMT_SOCKET_T, s);
+ }
+ else if(conn) {
+ first_time = !mev_sh_entry_conn_known(entry, conn);
+ }
+ else {
+ first_time = !mev_sh_entry_xfer_known(entry, data);
+ }
+
+ /* What was the previous action the transfer had regarding this socket?
+ * If the transfer is new to the socket, disregard the information
+ * in `last_poll`, because the socket might have been destroyed and
+ * reopened. We'd have cleared the sh_entry for that, but the socket
+ * might still be mentioned in the hashed pollsets. */
+ last_action = 0;
+ if(first_time) {
+ if(conn) {
+ if(!mev_sh_entry_conn_add(entry, conn))
+ return CURLM_OUT_OF_MEMORY;
+ }
+ else {
+ if(!mev_sh_entry_xfer_add(entry, data))
+ return CURLM_OUT_OF_MEMORY;
+ }
+ CURL_TRC_M(data, "ev entry fd=%" FMT_SOCKET_T ", added %s #%" FMT_OFF_T
+ ", total=%u/%d (xfer/conn)", s,
+ conn ? "connection" : "transfer",
+ conn ? conn->connection_id : data->mid,
+ Curl_uint_spbset_count(&entry->xfers),
+ entry->conn ? 1 : 0);
+ }
+ else {
+ for(j = 0; j < prev_ps->num; j++) {
+ if(s == prev_ps->sockets[j]) {
+ last_action = prev_ps->actions[j];
+ break;
+ }
+ }
+ }
+ /* track readers/writers changes and report to socket callback */
+ mresult = mev_sh_entry_update(multi, data, entry, s,
+ last_action, ps->actions[i]);
+ if(mresult)
+ return mresult;
+ }
+
+ /* Handle changes to sockets the transfer is NO LONGER interested in. */
+ for(i = 0; i < prev_ps->num; i++) {
+ bool stillused = FALSE;
+
+ s = prev_ps->sockets[i];
+ for(j = 0; j < ps->num; j++) {
+ if(s == ps->sockets[j]) {
+ /* socket is still supervised */
+ stillused = TRUE;
+ break;
+ }
+ }
+ if(stillused)
+ continue;
+
+ entry = mev_sh_entry_get(&multi->ev.sh_entries, s);
+ /* if entry does not exist, we were either never told about it or
+ * have already cleaned up this socket via Curl_multi_ev_socket_done().
+ * In other words: this is perfectly normal */
+ if(!entry)
+ continue;
+
+ if(conn && !mev_sh_entry_conn_remove(entry, conn)) {
+ /* `conn` says in `prev_ps` that it had been using a socket,
+ * but `conn` has not been registered for it.
+ * This should not happen if our book-keeping is correct? */
+ CURL_TRC_M(data, "ev entry fd=%" FMT_SOCKET_T ", conn lost "
+ "interest but is not registered", s);
+ DEBUGASSERT(NULL);
+ continue;
+ }
+
+ if(!conn && !mev_sh_entry_xfer_remove(entry, data)) {
+ /* `data` says in `prev_ps` that it had been using a socket,
+ * but `data` has not been registered for it.
+ * This should not happen if our book-keeping is correct? */
+ CURL_TRC_M(data, "ev entry fd=%" FMT_SOCKET_T ", transfer lost "
+ "interest but is not registered", s);
+ DEBUGASSERT(NULL);
+ continue;
+ }
+
+ if(mev_sh_entry_user_count(entry)) {
+ /* track readers/writers changes and report to socket callback */
+ mresult = mev_sh_entry_update(multi, data, entry, s,
+ prev_ps->actions[i], 0);
+ if(mresult)
+ return mresult;
+ CURL_TRC_M(data, "ev entry fd=%" FMT_SOCKET_T ", removed transfer, "
+ "total=%u/%d (xfer/conn)", s,
+ Curl_uint_spbset_count(&entry->xfers),
+ entry->conn ? 1 : 0);
+ }
+ else {
+ mresult = mev_forget_socket(multi, data, s, "last user gone");
+ if(mresult)
+ return mresult;
+ }
+ } /* for loop over num */
+
+ /* Remember for next time */
+ memcpy(prev_ps, ps, sizeof(*prev_ps));
+ return CURLM_OK;
+}
+
+static void mev_pollset_dtor(void *key, size_t klen, void *entry)
+{
+ (void)key;
+ (void)klen;
+ free(entry);
+}
+
+static struct easy_pollset*
+mev_add_new_conn_pollset(struct connectdata *conn)
+{
+ struct easy_pollset *ps;
+
+ ps = calloc(1, sizeof(*ps));
+ if(!ps)
+ return NULL;
+ if(Curl_conn_meta_set(conn, CURL_META_MEV_POLLSET, ps, mev_pollset_dtor))
+ return NULL;
+ return ps;
+}
+
+static struct easy_pollset*
+mev_add_new_xfer_pollset(struct Curl_easy *data)
+{
+ struct easy_pollset *ps;
+
+ ps = calloc(1, sizeof(*ps));
+ if(!ps)
+ return NULL;
+ if(Curl_meta_set(data, CURL_META_MEV_POLLSET, ps, mev_pollset_dtor))
+ return NULL;
+ return ps;
+}
+
+static struct easy_pollset *
+mev_get_last_pollset(struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ if(data) {
+ if(conn)
+ return Curl_conn_meta_get(conn, CURL_META_MEV_POLLSET);
+ return Curl_meta_get(data, CURL_META_MEV_POLLSET);
+ }
+ return NULL;
+}
+
+static void mev_init_cur_pollset(struct easy_pollset *ps,
+ struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ memset(ps, 0, sizeof(*ps));
+ if(conn)
+ Curl_conn_adjust_pollset(data, conn, ps);
+ else if(data)
+ Curl_multi_getsock(data, ps, "ev assess");
+}
+
+static CURLMcode mev_assess(struct Curl_multi *multi,
+ struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ if(multi && multi->socket_cb) {
+ struct easy_pollset ps, *last_ps;
+
+ mev_init_cur_pollset(&ps, data, conn);
+ last_ps = mev_get_last_pollset(data, conn);
+
+ if(!last_ps && ps.num) {
+ if(conn)
+ last_ps = mev_add_new_conn_pollset(conn);
+ else
+ last_ps = mev_add_new_xfer_pollset(data);
+ if(!last_ps)
+ return CURLM_OUT_OF_MEMORY;
+ }
+
+ if(last_ps)
+ return mev_pollset_diff(multi, data, conn, &ps, last_ps);
+ else
+ DEBUGASSERT(!ps.num);
+ }
+ return CURLM_OK;
+}
+
+CURLMcode Curl_multi_ev_assess_xfer(struct Curl_multi *multi,
+ struct Curl_easy *data)
+{
+ return mev_assess(multi, data, NULL);
+}
+
+CURLMcode Curl_multi_ev_assess_conn(struct Curl_multi *multi,
+ struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ return mev_assess(multi, data, conn);
+}
+
+CURLMcode Curl_multi_ev_assess_xfer_bset(struct Curl_multi *multi,
+ struct uint_bset *set)
+{
+ unsigned int mid;
+ CURLMcode result = CURLM_OK;
+
+ if(multi && multi->socket_cb && Curl_uint_bset_first(set, &mid)) {
+ do {
+ struct Curl_easy *data = Curl_multi_get_easy(multi, mid);
+ if(data)
+ result = Curl_multi_ev_assess_xfer(multi, data);
+ }
+ while(!result && Curl_uint_bset_next(set, mid, &mid));
+ }
+ return result;
+}
+
+
+CURLMcode Curl_multi_ev_assign(struct Curl_multi *multi,
+ curl_socket_t s,
+ void *user_data)
+{
+ struct mev_sh_entry *e = mev_sh_entry_get(&multi->ev.sh_entries, s);
+ if(!e)
+ return CURLM_BAD_SOCKET;
+ e->user_data = user_data;
+ return CURLM_OK;
+}
+
+void Curl_multi_ev_expire_xfers(struct Curl_multi *multi,
+ curl_socket_t s,
+ const struct curltime *nowp,
+ bool *run_cpool)
+{
+ struct mev_sh_entry *entry;
+
+ DEBUGASSERT(s != CURL_SOCKET_TIMEOUT);
+ entry = mev_sh_entry_get(&multi->ev.sh_entries, s);
+
+ /* Unmatched socket, we cannot act on it but we ignore this fact. In
+ real-world tests it has been proved that libevent can in fact give
+ the application actions even though the socket was just previously
+ asked to get removed, so thus we better survive stray socket actions
+ and just move on. */
+ if(entry) {
+ struct Curl_easy *data;
+ unsigned int mid;
+
+ if(Curl_uint_spbset_first(&entry->xfers, &mid)) {
+ do {
+ data = Curl_multi_get_easy(multi, mid);
+ if(data) {
+ /* Expire with out current now, so we will get it below when
+ * asking the splaytree for expired transfers. */
+ Curl_expire_ex(data, nowp, 0, EXPIRE_RUN_NOW);
+ }
+ }
+ while(Curl_uint_spbset_next(&entry->xfers, mid, &mid));
+ }
+
+ if(entry->conn)
+ *run_cpool = TRUE;
+ }
+}
+
+void Curl_multi_ev_socket_done(struct Curl_multi *multi,
+ struct Curl_easy *data, curl_socket_t s)
+{
+ mev_forget_socket(multi, data, s, "socket done");
+}
+
+void Curl_multi_ev_xfer_done(struct Curl_multi *multi,
+ struct Curl_easy *data)
+{
+ DEBUGASSERT(!data->conn); /* transfer should have been detached */
+ if(data != multi->admin) {
+ (void)mev_assess(multi, data, NULL);
+ Curl_meta_remove(data, CURL_META_MEV_POLLSET);
+ }
+}
+
+void Curl_multi_ev_conn_done(struct Curl_multi *multi,
+ struct Curl_easy *data,
+ struct connectdata *conn)
+{
+ (void)mev_assess(multi, data, conn);
+ Curl_conn_meta_remove(conn, CURL_META_MEV_POLLSET);
+}
+
+#define CURL_MEV_PS_HASH_SLOTS (991) /* nice prime */
+
+void Curl_multi_ev_init(struct Curl_multi *multi, size_t hashsize)
+{
+ Curl_hash_init(&multi->ev.sh_entries, hashsize, mev_sh_entry_hash,
+ mev_sh_entry_compare, mev_sh_entry_dtor);
+}
+
+void Curl_multi_ev_cleanup(struct Curl_multi *multi)
+{
+ Curl_hash_destroy(&multi->ev.sh_entries);
+}
diff --git a/libs/libcurl/src/multi_ev.h b/libs/libcurl/src/multi_ev.h
new file mode 100644
index 0000000000..73356ec42e
--- /dev/null
+++ b/libs/libcurl/src/multi_ev.h
@@ -0,0 +1,83 @@
+#ifndef HEADER_CURL_MULTI_EV_H
+#define HEADER_CURL_MULTI_EV_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "hash.h"
+
+struct Curl_easy;
+struct Curl_multi;
+struct easy_pollset;
+struct uint_bset;
+
+/* meta key for event pollset at easy handle or connection */
+#define CURL_META_MEV_POLLSET "meta:mev:ps"
+
+struct curl_multi_ev {
+ struct Curl_hash sh_entries;
+};
+
+/* Setup/teardown of multi event book-keeping. */
+void Curl_multi_ev_init(struct Curl_multi *multi, size_t hashsize);
+void Curl_multi_ev_cleanup(struct Curl_multi *multi);
+
+/* Assign a 'user_data' to be passed to the socket callback when
+ * invoked with the given socket. This will fail if this socket
+ * is not active, e.g. the application has not been told to monitor it. */
+CURLMcode Curl_multi_ev_assign(struct Curl_multi *multi, curl_socket_t s,
+ void *user_data);
+
+/* Assess the transfer by getting its current pollset, compute
+ * any changes to the last one and inform the application's socket
+ * callback if things have changed. */
+CURLMcode Curl_multi_ev_assess_xfer(struct Curl_multi *multi,
+ struct Curl_easy *data);
+/* Assess all easy handles on the list */
+CURLMcode Curl_multi_ev_assess_xfer_bset(struct Curl_multi *multi,
+ struct uint_bset *set);
+/* Assess the connection by getting its current pollset */
+CURLMcode Curl_multi_ev_assess_conn(struct Curl_multi *multi,
+ struct Curl_easy *data,
+ struct connectdata *conn);
+
+/* Expire all transfers tied to the given socket */
+void Curl_multi_ev_expire_xfers(struct Curl_multi *multi,
+ curl_socket_t s,
+ const struct curltime *nowp,
+ bool *run_cpool);
+
+/* Socket will be closed, forget anything we know about it. */
+void Curl_multi_ev_socket_done(struct Curl_multi *multi,
+ struct Curl_easy *data, curl_socket_t s);
+
+/* Transfer is removed from the multi */
+void Curl_multi_ev_xfer_done(struct Curl_multi *multi,
+ struct Curl_easy *data);
+
+/* Connection is being destroyed */
+void Curl_multi_ev_conn_done(struct Curl_multi *multi,
+ struct Curl_easy *data,
+ struct connectdata *conn);
+
+#endif /* HEADER_CURL_MULTI_EV_H */
diff --git a/libs/libcurl/src/multihandle.h b/libs/libcurl/src/multihandle.h
index a14f346819..9a5425aef9 100644
--- a/libs/libcurl/src/multihandle.h
+++ b/libs/libcurl/src/multihandle.h
@@ -27,10 +27,17 @@
#include "llist.h"
#include "hash.h"
#include "conncache.h"
+#include "cshutdn.h"
+#include "hostip.h"
+#include "multi_ev.h"
#include "psl.h"
#include "socketpair.h"
+#include "uint-bset.h"
+#include "uint-spbset.h"
+#include "uint-table.h"
struct connectdata;
+struct Curl_easy;
struct Curl_message {
struct Curl_llist_node list;
@@ -38,9 +45,9 @@ struct Curl_message {
struct CURLMsg extmsg;
};
-/* NOTE: if you add a state here, add the name to the statename[] array as
- well!
-*/
+/* NOTE: if you add a state here, add the name to the statenames[] array
+ * in curl_trc.c as well!
+ */
typedef enum {
MSTATE_INIT, /* 0 - start in this state */
MSTATE_PENDING, /* 1 - no connections, waiting for one */
@@ -86,17 +93,18 @@ struct Curl_multi {
this multi handle with an easy handle. Set this to CURL_MULTI_HANDLE. */
unsigned int magic;
- unsigned int num_easy; /* amount of entries in the linked list above. */
- unsigned int num_alive; /* amount of easy handles that are added but have
- not yet reached COMPLETE state */
+ unsigned int xfers_alive; /* amount of added transfers that have
+ not yet reached COMPLETE state */
+ struct uint_tbl xfers; /* transfers added to this multi */
+ /* Each transfer's mid may be present in at most one of these */
+ struct uint_bset process; /* transfer being processed */
+ struct uint_bset pending; /* transfers in waiting (conn limit etc.) */
+ struct uint_bset msgsent; /* transfers done with message for application */
struct Curl_llist msglist; /* a list of messages from completed transfers */
- /* Each added easy handle is added to ONE of these three lists */
- struct Curl_llist process; /* not in PENDING or MSGSENT */
- struct Curl_llist pending; /* in PENDING */
- struct Curl_llist msgsent; /* in MSGSENT */
- curl_off_t next_easy_mid; /* next multi-id for easy handle added */
+ struct Curl_easy *admin; /* internal easy handle for admin operations.
+ gets assigned `mid` 0 on multi init */
/* callback function and user data pointer for the *socket() API */
curl_socket_callback socket_cb;
@@ -106,8 +114,8 @@ struct Curl_multi {
curl_push_callback push_cb;
void *push_userp;
- /* Hostname cache */
- struct Curl_hash hostcache;
+ struct Curl_dnscache dnscache; /* DNS cache */
+ struct Curl_ssl_scache *ssl_scache; /* TLS session pool */
#ifdef USE_LIBPSL
/* PSL cache. */
@@ -128,10 +136,9 @@ struct Curl_multi {
char *xfer_sockbuf; /* the actual buffer */
size_t xfer_sockbuf_len; /* the allocated length */
- /* 'sockhash' is the lookup hash for socket descriptor => easy handles (note
- the pluralis form, there can be more than one easy handle waiting on the
- same actual socket) */
- struct Curl_hash sockhash;
+ /* multi event related things */
+ struct curl_multi_ev ev;
+
/* `proto_hash` is a general key-value store for protocol implementations
* with the lifetime of the multi handle. The number of elements kept here
* should be in the order of supported protocols (and sub-protocols like
@@ -140,16 +147,14 @@ struct Curl_multi {
* the multi handle is cleaned up (see Curl_hash_add2()).*/
struct Curl_hash proto_hash;
- /* Shared connection cache (bundles)*/
- struct cpool cpool;
+ struct cshutdn cshutdn; /* connection shutdown handling */
+ struct cpool cpool; /* connection pool (bundles) */
long max_host_connections; /* if >0, a fixed limit of the maximum number
of connections per host */
long max_total_connections; /* if >0, a fixed limit of the maximum number
of connections in total */
- long max_shutdown_connections; /* if >0, a fixed limit of the maximum number
- of connections in shutdown handling */
/* timer callback and user data pointer for the *socket() API */
curl_multi_timer_callback timer_cb;
diff --git a/libs/libcurl/src/multiif.h b/libs/libcurl/src/multiif.h
index c5cdc24b37..a8701cce62 100644
--- a/libs/libcurl/src/multiif.h
+++ b/libs/libcurl/src/multiif.h
@@ -28,8 +28,10 @@
* Prototypes for library-wide functions provided by multi.c
*/
-CURLcode Curl_updatesocket(struct Curl_easy *data);
void Curl_expire(struct Curl_easy *data, timediff_t milli, expire_id);
+void Curl_expire_ex(struct Curl_easy *data,
+ const struct curltime *nowp,
+ timediff_t milli, expire_id id);
bool Curl_expire_clear(struct Curl_easy *data);
void Curl_expire_done(struct Curl_easy *data, expire_id id);
CURLMcode Curl_update_timer(struct Curl_multi *multi) WARN_UNUSED_RESULT;
@@ -45,9 +47,11 @@ void Curl_multi_connchanged(struct Curl_multi *multi);
/* Internal version of curl_multi_init() accepts size parameters for the
socket, connection and dns hashes */
-struct Curl_multi *Curl_multi_handle(size_t hashsize,
+struct Curl_multi *Curl_multi_handle(unsigned int xfer_table_size,
+ size_t hashsize,
size_t chashsize,
- size_t dnssize);
+ size_t dnssize,
+ size_t sesssize);
/* the write bits start at bit 16 for the *getsock() bitmap */
#define GETSOCK_WRITEBITSTART 16
@@ -63,26 +67,13 @@ struct Curl_multi *Curl_multi_handle(size_t hashsize,
/* mask for checking if read and/or write is set for index x */
#define GETSOCK_MASK_RW(x) (GETSOCK_READSOCK(x)|GETSOCK_WRITESOCK(x))
-/*
- * Curl_multi_closed()
- *
- * Used by the connect code to tell the multi_socket code that one of the
- * sockets we were using is about to be closed. This function will then
- * remove it from the sockethash for this handle to make the multi_socket API
- * behave properly, especially for the case when libcurl will create another
- * socket again and it gets the same file descriptor number.
- */
-
-void Curl_multi_closed(struct Curl_easy *data, curl_socket_t s);
-
-/* Compare the two pollsets to notify the multi_socket API of changes
- * in socket polling, e.g calling multi->socket_cb() with the changes if
- * differences are seen.
+/**
+ * Let the multi handle know that the socket is about to be closed.
+ * The multi will then remove anything it knows about the socket, so
+ * when the OS is using this socket (number) again subsequently,
+ * the internal book keeping will not get confused.
*/
-CURLMcode Curl_multi_pollset_ev(struct Curl_multi *multi,
- struct Curl_easy *data,
- struct easy_pollset *ps,
- struct easy_pollset *last_ps);
+void Curl_multi_will_close(struct Curl_easy *data, curl_socket_t s);
/*
* Add a handle and move it into PERFORM state at once. For pushed streams.
@@ -95,6 +86,10 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi,
/* Return the value of the CURLMOPT_MAX_CONCURRENT_STREAMS option */
unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi);
+void Curl_multi_getsock(struct Curl_easy *data,
+ struct easy_pollset *ps,
+ const char *caller);
+
/**
* Borrow the transfer buffer from the multi, suitable
* for the given transfer `data`. The buffer may only be used in one
@@ -169,9 +164,13 @@ CURLcode Curl_multi_xfer_sockbuf_borrow(struct Curl_easy *data,
void Curl_multi_xfer_sockbuf_release(struct Curl_easy *data, char *buf);
/**
- * Get the transfer handle for the given id. Returns NULL if not found.
+ * Get the easy handle for the given mid.
+ * Returns NULL if not found.
*/
-struct Curl_easy *Curl_multi_get_handle(struct Curl_multi *multi,
- curl_off_t id);
+struct Curl_easy *Curl_multi_get_easy(struct Curl_multi *multi,
+ unsigned int mid);
+
+/* Get the # of transfers current in process/pending. */
+unsigned int Curl_multi_xfers_running(struct Curl_multi *multi);
#endif /* HEADER_CURL_MULTIIF_H */
diff --git a/libs/libcurl/src/netrc.c b/libs/libcurl/src/netrc.c
index 59759fe4a9..41af08009d 100644
--- a/libs/libcurl/src/netrc.c
+++ b/libs/libcurl/src/netrc.c
@@ -26,13 +26,20 @@
#ifndef CURL_DISABLE_NETRC
#ifdef HAVE_PWD_H
+#ifdef __AMIGA__
+#undef __NO_NET_API /* required for AmigaOS to declare getpwuid() */
+#endif
#include <pwd.h>
+#ifdef __AMIGA__
+#define __NO_NET_API
+#endif
#endif
#include <curl/curl.h>
#include "netrc.h"
#include "strcase.h"
#include "curl_get_line.h"
+#include "curlx/strparse.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -54,82 +61,89 @@ enum found_state {
PASSWORD
};
-#define NETRC_FILE_MISSING 1
-#define NETRC_FAILED -1
-#define NETRC_SUCCESS 0
+#define FOUND_LOGIN 1
+#define FOUND_PASSWORD 2
-#define MAX_NETRC_LINE 4096
-#define MAX_NETRC_FILE (64*1024)
-#define MAX_NETRC_TOKEN 128
+#define MAX_NETRC_LINE 16384
+#define MAX_NETRC_FILE (128*1024)
+#define MAX_NETRC_TOKEN 4096
-static CURLcode file2memory(const char *filename, struct dynbuf *filebuf)
+/* convert a dynbuf call CURLcode error to a NETRCcode error */
+#define curl2netrc(result) \
+ (((result) == CURLE_OUT_OF_MEMORY) ? \
+ NETRC_OUT_OF_MEMORY : NETRC_SYNTAX_ERROR)
+
+static NETRCcode file2memory(const char *filename, struct dynbuf *filebuf)
{
- CURLcode result = CURLE_OK;
+ NETRCcode ret = NETRC_FILE_MISSING; /* if it cannot open the file */
FILE *file = fopen(filename, FOPEN_READTEXT);
struct dynbuf linebuf;
- Curl_dyn_init(&linebuf, MAX_NETRC_LINE);
+ curlx_dyn_init(&linebuf, MAX_NETRC_LINE);
if(file) {
+ ret = NETRC_OK;
while(Curl_get_line(&linebuf, file)) {
- const char *line = Curl_dyn_ptr(&linebuf);
+ CURLcode result;
+ const char *line = curlx_dyn_ptr(&linebuf);
/* skip comments on load */
- while(ISBLANK(*line))
- line++;
+ curlx_str_passblanks(&line);
if(*line == '#')
continue;
- result = Curl_dyn_add(filebuf, line);
- if(result)
+ result = curlx_dyn_add(filebuf, line);
+ if(result) {
+ ret = curl2netrc(result);
goto done;
+ }
}
}
done:
- Curl_dyn_free(&linebuf);
+ curlx_dyn_free(&linebuf);
if(file)
fclose(file);
- return result;
+ return ret;
}
/*
* Returns zero on success.
*/
-static int parsenetrc(struct store_netrc *store,
- const char *host,
- char **loginp,
- char **passwordp,
- const char *netrcfile)
+static NETRCcode parsenetrc(struct store_netrc *store,
+ const char *host,
+ char **loginp, /* might point to a username */
+ char **passwordp,
+ const char *netrcfile)
{
- int retcode = NETRC_FILE_MISSING;
+ NETRCcode retcode = NETRC_NO_MATCH;
char *login = *loginp;
- char *password = *passwordp;
- bool specific_login = (login && *login != 0);
- bool login_alloc = FALSE;
- bool password_alloc = FALSE;
+ char *password = NULL;
+ bool specific_login = !!login; /* points to something */
enum host_lookup_state state = NOTHING;
- enum found_state found = NONE;
- bool our_login = TRUE; /* With specific_login, found *our* login name (or
- login-less line) */
+ enum found_state keyword = NONE;
+ unsigned char found = 0; /* login + password found bits, as they can come in
+ any order */
+ bool our_login = FALSE; /* found our login name */
bool done = FALSE;
char *netrcbuffer;
struct dynbuf token;
struct dynbuf *filebuf = &store->filebuf;
- Curl_dyn_init(&token, MAX_NETRC_TOKEN);
+ DEBUGASSERT(!*passwordp);
+ curlx_dyn_init(&token, MAX_NETRC_TOKEN);
if(!store->loaded) {
- if(file2memory(netrcfile, filebuf))
- return NETRC_FAILED;
+ NETRCcode ret = file2memory(netrcfile, filebuf);
+ if(ret)
+ return ret;
store->loaded = TRUE;
}
- netrcbuffer = Curl_dyn_ptr(filebuf);
+ netrcbuffer = curlx_dyn_ptr(filebuf);
while(!done) {
- char *tok = netrcbuffer;
- while(tok) {
- char *tok_end;
+ const char *tok = netrcbuffer;
+ while(tok && !done) {
+ const char *tok_end;
bool quoted;
- Curl_dyn_reset(&token);
- while(ISBLANK(*tok))
- tok++;
+ curlx_dyn_reset(&token);
+ curlx_str_passblanks(&tok);
/* tok is first non-space letter */
if(state == MACDEF) {
if((*tok == '\n') || (*tok == '\r'))
@@ -146,12 +160,18 @@ static int parsenetrc(struct store_netrc *store,
tok_end = tok;
if(!quoted) {
size_t len = 0;
- while(!ISSPACE(*tok_end)) {
+ CURLcode result;
+ while(*tok_end > ' ') {
tok_end++;
len++;
}
- if(!len || Curl_dyn_addn(&token, tok, len)) {
- retcode = NETRC_FAILED;
+ if(!len) {
+ retcode = NETRC_SYNTAX_ERROR;
+ goto out;
+ }
+ result = curlx_dyn_addn(&token, tok, len);
+ if(result) {
+ retcode = curl2netrc(result);
goto out;
}
}
@@ -160,6 +180,7 @@ static int parsenetrc(struct store_netrc *store,
bool endquote = FALSE;
tok_end++; /* pass the leading quote */
while(*tok_end) {
+ CURLcode result;
char s = *tok_end;
if(escape) {
escape = FALSE;
@@ -185,25 +206,26 @@ static int parsenetrc(struct store_netrc *store,
endquote = TRUE;
break;
}
- if(Curl_dyn_addn(&token, &s, 1)) {
- retcode = NETRC_FAILED;
+ result = curlx_dyn_addn(&token, &s, 1);
+ if(result) {
+ retcode = curl2netrc(result);
goto out;
}
tok_end++;
}
if(escape || !endquote) {
/* bad syntax, get out */
- retcode = NETRC_FAILED;
+ retcode = NETRC_SYNTAX_ERROR;
goto out;
}
}
- if((login && *login) && (password && *password)) {
- done = TRUE;
- break;
- }
-
- tok = Curl_dyn_ptr(&token);
+ if(curlx_dyn_len(&token))
+ tok = curlx_dyn_ptr(&token);
+ else
+ /* since tok might actually be NULL for no content, set it to blank
+ to avoid having to deal with it being NULL */
+ tok = "";
switch(state) {
case NOTHING:
@@ -212,14 +234,21 @@ static int parsenetrc(struct store_netrc *store,
contents begin with the next .netrc line and continue until a
null line (consecutive new-line characters) is encountered. */
state = MACDEF;
- else if(strcasecompare("machine", tok))
+ else if(strcasecompare("machine", tok)) {
/* the next tok is the machine name, this is in itself the delimiter
that starts the stuff entered for this machine, after this we
need to search for 'login' and 'password'. */
state = HOSTFOUND;
+ keyword = NONE;
+ found = 0;
+ our_login = FALSE;
+ Curl_safefree(password);
+ if(!specific_login)
+ Curl_safefree(login);
+ }
else if(strcasecompare("default", tok)) {
state = HOSTVALID;
- retcode = NETRC_SUCCESS; /* we did find our host */
+ retcode = NETRC_OK; /* we did find our host */
}
break;
case MACDEF:
@@ -230,7 +259,7 @@ static int parsenetrc(struct store_netrc *store,
if(strcasecompare(host, tok)) {
/* and yes, this is our host! */
state = HOSTVALID;
- retcode = NETRC_SUCCESS; /* we did find our host */
+ retcode = NETRC_OK; /* we did find our host */
}
else
/* not our host */
@@ -238,44 +267,59 @@ static int parsenetrc(struct store_netrc *store,
break;
case HOSTVALID:
/* we are now parsing sub-keywords concerning "our" host */
- if(found == LOGIN) {
- if(specific_login) {
+ if(keyword == LOGIN) {
+ if(specific_login)
our_login = !Curl_timestrcmp(login, tok);
- }
- else if(!login || Curl_timestrcmp(login, tok)) {
- if(login_alloc)
- free(login);
+ else {
+ our_login = TRUE;
+ free(login);
login = strdup(tok);
if(!login) {
- retcode = NETRC_FAILED; /* allocation failed */
+ retcode = NETRC_OUT_OF_MEMORY; /* allocation failed */
goto out;
}
- login_alloc = TRUE;
}
- found = NONE;
+ found |= FOUND_LOGIN;
+ keyword = NONE;
}
- else if(found == PASSWORD) {
- if((our_login || !specific_login) &&
- (!password || Curl_timestrcmp(password, tok))) {
- if(password_alloc)
- free(password);
- password = strdup(tok);
- if(!password) {
- retcode = NETRC_FAILED; /* allocation failed */
- goto out;
- }
- password_alloc = TRUE;
+ else if(keyword == PASSWORD) {
+ free(password);
+ password = strdup(tok);
+ if(!password) {
+ retcode = NETRC_OUT_OF_MEMORY; /* allocation failed */
+ goto out;
}
- found = NONE;
+ if(!specific_login || our_login)
+ found |= FOUND_PASSWORD;
+ keyword = NONE;
}
else if(strcasecompare("login", tok))
- found = LOGIN;
+ keyword = LOGIN;
else if(strcasecompare("password", tok))
- found = PASSWORD;
+ keyword = PASSWORD;
else if(strcasecompare("machine", tok)) {
- /* ok, there is machine here go => */
+ /* a new machine here */
+ if(found & FOUND_PASSWORD) {
+ done = TRUE;
+ break;
+ }
state = HOSTFOUND;
- found = NONE;
+ keyword = NONE;
+ found = 0;
+ Curl_safefree(password);
+ if(!specific_login)
+ Curl_safefree(login);
+ }
+ else if(strcasecompare("default", tok)) {
+ state = HOSTVALID;
+ retcode = NETRC_OK; /* we did find our host */
+ Curl_safefree(password);
+ if(!specific_login)
+ Curl_safefree(login);
+ }
+ if((found == (FOUND_PASSWORD|FOUND_LOGIN)) && our_login) {
+ done = TRUE;
+ break;
}
break;
} /* switch (state) */
@@ -293,40 +337,62 @@ static int parsenetrc(struct store_netrc *store,
} /* while !done */
out:
- Curl_dyn_free(&token);
+ curlx_dyn_free(&token);
+ if(!retcode) {
+ if(!password && our_login) {
+ /* success without a password, set a blank one */
+ password = strdup("");
+ if(!password)
+ retcode = NETRC_OUT_OF_MEMORY; /* out of memory */
+ }
+ else if(!login && !password)
+ /* a default with no credentials */
+ retcode = NETRC_NO_MATCH;
+ }
if(!retcode) {
/* success */
- if(login_alloc) {
- free(*loginp);
+ if(!specific_login)
*loginp = login;
- }
- if(password_alloc) {
- free(*passwordp);
- *passwordp = password;
- }
+ *passwordp = password;
}
else {
- Curl_dyn_free(filebuf);
- if(login_alloc)
+ curlx_dyn_free(filebuf);
+ if(!specific_login)
free(login);
- if(password_alloc)
- free(password);
+ free(password);
}
return retcode;
}
+const char *Curl_netrc_strerror(NETRCcode ret)
+{
+ switch(ret) {
+ default:
+ return ""; /* not a legit error */
+ case NETRC_FILE_MISSING:
+ return "no such file";
+ case NETRC_NO_MATCH:
+ return "no matching entry";
+ case NETRC_OUT_OF_MEMORY:
+ return "out of memory";
+ case NETRC_SYNTAX_ERROR:
+ return "syntax error";
+ }
+ /* never reached */
+}
+
/*
* @unittest: 1304
*
* *loginp and *passwordp MUST be allocated if they are not NULL when passed
* in.
*/
-int Curl_parsenetrc(struct store_netrc *store, const char *host,
- char **loginp, char **passwordp,
- char *netrcfile)
+NETRCcode Curl_parsenetrc(struct store_netrc *store, const char *host,
+ char **loginp, char **passwordp,
+ char *netrcfile)
{
- int retcode = 1;
+ NETRCcode retcode = NETRC_OK;
char *filealloc = NULL;
if(!netrcfile) {
@@ -364,13 +430,13 @@ int Curl_parsenetrc(struct store_netrc *store, const char *host,
}
if(!home)
- return retcode; /* no home directory found (or possibly out of
- memory) */
+ return NETRC_FILE_MISSING; /* no home directory found (or possibly out
+ of memory) */
filealloc = aprintf("%s%s.netrc", home, DIR_CHAR);
if(!filealloc) {
free(homea);
- return -1;
+ return NETRC_OUT_OF_MEMORY;
}
retcode = parsenetrc(store, host, loginp, passwordp, filealloc);
free(filealloc);
@@ -380,7 +446,7 @@ int Curl_parsenetrc(struct store_netrc *store, const char *host,
filealloc = aprintf("%s%s_netrc", home, DIR_CHAR);
if(!filealloc) {
free(homea);
- return -1;
+ return NETRC_OUT_OF_MEMORY;
}
retcode = parsenetrc(store, host, loginp, passwordp, filealloc);
free(filealloc);
@@ -395,12 +461,12 @@ int Curl_parsenetrc(struct store_netrc *store, const char *host,
void Curl_netrc_init(struct store_netrc *s)
{
- Curl_dyn_init(&s->filebuf, MAX_NETRC_FILE);
+ curlx_dyn_init(&s->filebuf, MAX_NETRC_FILE);
s->loaded = FALSE;
}
void Curl_netrc_cleanup(struct store_netrc *s)
{
- Curl_dyn_free(&s->filebuf);
+ curlx_dyn_free(&s->filebuf);
s->loaded = FALSE;
}
#endif
diff --git a/libs/libcurl/src/netrc.h b/libs/libcurl/src/netrc.h
index 6d9f3eddf3..0d44f986a0 100644
--- a/libs/libcurl/src/netrc.h
+++ b/libs/libcurl/src/netrc.h
@@ -26,7 +26,7 @@
#include "curl_setup.h"
#ifndef CURL_DISABLE_NETRC
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
struct store_netrc {
struct dynbuf filebuf;
@@ -34,12 +34,21 @@ struct store_netrc {
BIT(loaded);
};
+typedef enum {
+ NETRC_OK,
+ NETRC_NO_MATCH, /* no matching entry in the file */
+ NETRC_SYNTAX_ERROR, /* in the netrc file */
+ NETRC_FILE_MISSING, /* the netrc file does not exist */
+ NETRC_OUT_OF_MEMORY, /* while parsing netrc */
+ NETRC_LAST /* never used */
+} NETRCcode;
+
+const char *Curl_netrc_strerror(NETRCcode ret);
void Curl_netrc_init(struct store_netrc *s);
void Curl_netrc_cleanup(struct store_netrc *s);
-/* returns -1 on failure, 0 if the host is found, 1 is the host is not found */
-int Curl_parsenetrc(struct store_netrc *s, const char *host, char **loginp,
- char **passwordp, char *filename);
+NETRCcode Curl_parsenetrc(struct store_netrc *s, const char *host,
+ char **loginp, char **passwordp, char *filename);
/* Assume: (*passwordp)[0]=0, host[0] != 0.
* If (*loginp)[0] = 0, search for login and password within a machine
* section in the netrc.
diff --git a/libs/libcurl/src/noproxy.c b/libs/libcurl/src/noproxy.c
index 8580235b56..f1f40aeaa5 100644
--- a/libs/libcurl/src/noproxy.c
+++ b/libs/libcurl/src/noproxy.c
@@ -26,9 +26,10 @@
#ifndef CURL_DISABLE_PROXY
-#include "inet_pton.h"
+#include "curlx/inet_pton.h"
#include "strcase.h"
#include "noproxy.h"
+#include "curlx/strparse.h"
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
@@ -53,9 +54,9 @@ UNITTEST bool Curl_cidr4_match(const char *ipv4, /* 1.2.3.4 address */
/* strange input */
return FALSE;
- if(1 != Curl_inet_pton(AF_INET, ipv4, &address))
+ if(1 != curlx_inet_pton(AF_INET, ipv4, &address))
return FALSE;
- if(1 != Curl_inet_pton(AF_INET, network, &check))
+ if(1 != curlx_inet_pton(AF_INET, network, &check))
return FALSE;
if(bits && (bits != 32)) {
@@ -71,7 +72,7 @@ UNITTEST bool Curl_cidr4_match(const char *ipv4, /* 1.2.3.4 address */
return FALSE;
return TRUE;
}
- return (address == check);
+ return address == check;
}
UNITTEST bool Curl_cidr6_match(const char *ipv6,
@@ -91,9 +92,9 @@ UNITTEST bool Curl_cidr6_match(const char *ipv6,
rest = bits & 0x07;
if((bytes > 16) || ((bytes == 16) && rest))
return FALSE;
- if(1 != Curl_inet_pton(AF_INET6, ipv6, address))
+ if(1 != curlx_inet_pton(AF_INET6, ipv6, address))
return FALSE;
- if(1 != Curl_inet_pton(AF_INET6, network, check))
+ if(1 != curlx_inet_pton(AF_INET6, network, check))
return FALSE;
if(bytes && memcmp(address, check, bytes))
return FALSE;
@@ -162,7 +163,7 @@ bool Curl_check_noproxy(const char *name, const char *no_proxy)
else {
unsigned int address;
namelen = strlen(name);
- if(1 == Curl_inet_pton(AF_INET, name, &address))
+ if(1 == curlx_inet_pton(AF_INET, name, &address))
type = TYPE_IPV4;
else {
/* ignore trailing dots in the hostname */
@@ -177,8 +178,7 @@ bool Curl_check_noproxy(const char *name, const char *no_proxy)
bool match = FALSE;
/* pass blanks */
- while(*p && ISBLANK(*p))
- p++;
+ curlx_str_passblanks(&p);
token = p;
/* pass over the pattern */
@@ -234,7 +234,7 @@ bool Curl_check_noproxy(const char *name, const char *no_proxy)
/* if the bits variable gets a crazy value here, that is fine as
the value will then be rejected in the cidr function */
bits = (unsigned int)atoi(slash + 1);
- *slash = 0; /* null terminate there */
+ *slash = 0; /* null-terminate there */
}
if(type == TYPE_IPV6)
match = Curl_cidr6_match(name, check, bits);
@@ -247,8 +247,7 @@ bool Curl_check_noproxy(const char *name, const char *no_proxy)
return TRUE;
} /* if(tokenlen) */
/* pass blanks after pattern */
- while(ISBLANK(*p))
- p++;
+ curlx_str_passblanks(&p);
/* if not a comma, this ends the loop */
if(*p != ',')
break;
diff --git a/libs/libcurl/src/openldap.c b/libs/libcurl/src/openldap.c
index 8bbf9711bf..3a70cec8e7 100644
--- a/libs/libcurl/src/openldap.c
+++ b/libs/libcurl/src/openldap.c
@@ -41,12 +41,13 @@
#include <ldap.h>
#include "urldata.h"
+#include "url.h"
#include <curl/curl.h>
#include "sendf.h"
#include "vtls/vtls.h"
#include "transfer.h"
#include "curl_ldap.h"
-#include "curl_base64.h"
+#include "curlx/base64.h"
#include "cfilters.h"
#include "connect.h"
#include "curl_sasl.h"
@@ -134,6 +135,7 @@ const struct Curl_handler Curl_handler_ldap = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_LDAP, /* defport */
CURLPROTO_LDAP, /* protocol */
CURLPROTO_LDAP, /* family */
@@ -163,6 +165,7 @@ const struct Curl_handler Curl_handler_ldaps = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_LDAPS, /* defport */
CURLPROTO_LDAPS, /* protocol */
CURLPROTO_LDAP, /* family */
@@ -200,15 +203,20 @@ struct ldapreqinfo {
int nument;
};
+/* meta key for storing ldapconninfo at easy handle */
+#define CURL_META_LDAP_EASY "meta:proto:ldap:easy"
+/* meta key for storing ldapconninfo at connection */
+#define CURL_META_LDAP_CONN "meta:proto:ldap:conn"
+
+
/*
* oldap_state()
*
* This is the ONLY way to change LDAP state!
*/
-static void oldap_state(struct Curl_easy *data, ldapstate newstate)
+static void oldap_state(struct Curl_easy *data, struct ldapconninfo *li,
+ ldapstate newstate)
{
- struct ldapconninfo *ldapc = data->conn->proto.ldapc;
-
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
/* for debug purposes */
static const char * const names[] = {
@@ -223,12 +231,12 @@ static void oldap_state(struct Curl_easy *data, ldapstate newstate)
/* LAST */
};
- if(ldapc->state != newstate)
+ if(li->state != newstate)
infof(data, "LDAP %p state change from %s to %s",
- (void *)ldapc, names[ldapc->state], names[newstate]);
+ (void *)li, names[li->state], names[newstate]);
#endif
-
- ldapc->state = newstate;
+ (void)data;
+ li->state = newstate;
}
/* Map some particular LDAP error codes to CURLcode values. */
@@ -275,7 +283,7 @@ static CURLcode oldap_url_parse(struct Curl_easy *data, LDAPURLDesc **ludp)
result = rc == LDAP_URL_ERR_MEM ? CURLE_OUT_OF_MEMORY :
CURLE_URL_MALFORMAT;
rc -= LDAP_URL_SUCCESS;
- if((size_t) rc < sizeof(url_errs) / sizeof(url_errs[0]))
+ if((size_t) rc < CURL_ARRAYSIZE(url_errs))
msg = url_errs[rc];
failf(data, "LDAP local: %s", msg);
}
@@ -286,9 +294,13 @@ static CURLcode oldap_url_parse(struct Curl_easy *data, LDAPURLDesc **ludp)
static CURLcode oldap_parse_login_options(struct connectdata *conn)
{
CURLcode result = CURLE_OK;
- struct ldapconninfo *li = conn->proto.ldapc;
+ struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
const char *ptr = conn->options;
+ DEBUGASSERT(li);
+ if(!li)
+ return CURLE_FAILED_INIT;
+
while(!result && ptr && *ptr) {
const char *key = ptr;
const char *value;
@@ -332,7 +344,12 @@ static CURLcode oldap_setup_connection(struct Curl_easy *data,
*/
static CURLcode oldap_get_message(struct Curl_easy *data, struct bufref *out)
{
- struct berval *servercred = data->conn->proto.ldapc->servercred;
+ struct ldapconninfo *li =
+ Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN);
+ struct berval *servercred = li ? li->servercred : NULL;
+ DEBUGASSERT(li);
+ if(!li)
+ return CURLE_FAILED_INIT;
if(!servercred || !servercred->bv_val)
return CURLE_WEIRD_SERVER_REPLY;
@@ -347,12 +364,15 @@ static CURLcode oldap_perform_auth(struct Curl_easy *data, const char *mech,
const struct bufref *initresp)
{
struct connectdata *conn = data->conn;
- struct ldapconninfo *li = conn->proto.ldapc;
+ struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
struct berval cred;
struct berval *pcred = &cred;
int rc;
- cred.bv_val = (char *) Curl_bufref_ptr(initresp);
+ DEBUGASSERT(li);
+ if(!li)
+ return CURLE_FAILED_INIT;
+ cred.bv_val = (char *)CURL_UNCONST(Curl_bufref_ptr(initresp));
cred.bv_len = Curl_bufref_len(initresp);
if(!cred.bv_val)
pcred = NULL;
@@ -369,12 +389,14 @@ static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech,
const struct bufref *resp)
{
struct connectdata *conn = data->conn;
- struct ldapconninfo *li = conn->proto.ldapc;
+ struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
struct berval cred;
struct berval *pcred = &cred;
int rc;
- cred.bv_val = (char *) Curl_bufref_ptr(resp);
+ if(!li)
+ return CURLE_FAILED_INIT;
+ cred.bv_val = (char *)CURL_UNCONST(Curl_bufref_ptr(resp));
cred.bv_len = Curl_bufref_len(resp);
if(!cred.bv_val)
pcred = NULL;
@@ -389,11 +411,15 @@ static CURLcode oldap_continue_auth(struct Curl_easy *data, const char *mech,
*/
static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech)
{
- struct ldapconninfo *li = data->conn->proto.ldapc;
- int rc = ldap_sasl_bind(li->ld, NULL, LDAP_SASL_NULL, NULL, NULL, NULL,
- &li->msgid);
+ struct ldapconninfo *li =
+ Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN);
+ int rc;
(void)mech;
+ if(!li)
+ return CURLE_FAILED_INIT;
+ rc = ldap_sasl_bind(li->ld, NULL, LDAP_SASL_NULL, NULL, NULL, NULL,
+ &li->msgid);
if(rc != LDAP_SUCCESS)
return oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
return CURLE_OK;
@@ -403,11 +429,13 @@ static CURLcode oldap_cancel_auth(struct Curl_easy *data, const char *mech)
static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate)
{
struct connectdata *conn = data->conn;
- struct ldapconninfo *li = conn->proto.ldapc;
+ struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
char *binddn = NULL;
struct berval passwd;
int rc;
+ if(!li)
+ return CURLE_FAILED_INIT;
passwd.bv_val = NULL;
passwd.bv_len = 0;
@@ -423,39 +451,47 @@ static CURLcode oldap_perform_bind(struct Curl_easy *data, ldapstate newstate)
return oldap_map_error(rc,
data->state.aptr.user ?
CURLE_LOGIN_DENIED : CURLE_LDAP_CANNOT_BIND);
- oldap_state(data, newstate);
+ oldap_state(data, li, newstate);
return CURLE_OK;
}
/* Query the supported SASL authentication mechanisms. */
static CURLcode oldap_perform_mechs(struct Curl_easy *data)
{
- struct ldapconninfo *li = data->conn->proto.ldapc;
+ struct ldapconninfo *li =
+ Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN);
int rc;
static const char * const supportedSASLMechanisms[] = {
"supportedSASLMechanisms",
NULL
};
+ if(!li)
+ return CURLE_FAILED_INIT;
rc = ldap_search_ext(li->ld, "", LDAP_SCOPE_BASE, "(objectclass=*)",
- (char **) supportedSASLMechanisms, 0,
+ (char **)CURL_UNCONST(supportedSASLMechanisms), 0,
NULL, NULL, NULL, 0, &li->msgid);
if(rc != LDAP_SUCCESS)
return oldap_map_error(rc, CURLE_LOGIN_DENIED);
- oldap_state(data, OLDAP_MECHS);
+ oldap_state(data, li, OLDAP_MECHS);
return CURLE_OK;
}
/* Starts SASL bind. */
static CURLcode oldap_perform_sasl(struct Curl_easy *data)
{
+ struct ldapconninfo *li =
+ Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN);
saslprogress progress = SASL_IDLE;
- struct ldapconninfo *li = data->conn->proto.ldapc;
- CURLcode result = Curl_sasl_start(&li->sasl, data, TRUE, &progress);
+ CURLcode result;
+
+ if(!li)
+ return CURLE_FAILED_INIT;
+ result = Curl_sasl_start(&li->sasl, data, TRUE, &progress);
- oldap_state(data, OLDAP_SASL);
+ oldap_state(data, li, OLDAP_SASL);
if(!result && progress != SASL_INPROGRESS)
- result = CURLE_LOGIN_DENIED;
+ result = Curl_sasl_is_blocked(&li->sasl, data);
return result;
}
@@ -464,17 +500,22 @@ static Sockbuf_IO ldapsb_tls;
static bool ssl_installed(struct connectdata *conn)
{
- return conn->proto.ldapc->recv != NULL;
+ struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
+ return li && li->recv != NULL;
}
static CURLcode oldap_ssl_connect(struct Curl_easy *data, ldapstate newstate)
{
struct connectdata *conn = data->conn;
- struct ldapconninfo *li = conn->proto.ldapc;
+ struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
bool ssldone = FALSE;
- CURLcode result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
+ CURLcode result;
+
+ if(!li)
+ return CURLE_FAILED_INIT;
+ result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
if(!result) {
- oldap_state(data, newstate);
+ oldap_state(data, li, newstate);
if(ssldone) {
Sockbuf *sb;
@@ -493,48 +534,72 @@ static CURLcode oldap_ssl_connect(struct Curl_easy *data, ldapstate newstate)
/* Send the STARTTLS request */
static CURLcode oldap_perform_starttls(struct Curl_easy *data)
{
- struct ldapconninfo *li = data->conn->proto.ldapc;
- int rc = ldap_start_tls(li->ld, NULL, NULL, &li->msgid);
+ struct ldapconninfo *li =
+ Curl_conn_meta_get(data->conn, CURL_META_LDAP_CONN);
+ int rc;
+ if(!li)
+ return CURLE_FAILED_INIT;
+ rc = ldap_start_tls(li->ld, NULL, NULL, &li->msgid);
if(rc != LDAP_SUCCESS)
return oldap_map_error(rc, CURLE_USE_SSL_FAILED);
- oldap_state(data, OLDAP_STARTTLS);
+ oldap_state(data, li, OLDAP_STARTTLS);
return CURLE_OK;
}
#endif
+static void oldap_easy_dtor(void *key, size_t klen, void *entry)
+{
+ struct ldapreqinfo *lr = entry;
+ (void)key;
+ (void)klen;
+ free(lr);
+}
+
+static void oldap_conn_dtor(void *key, size_t klen, void *entry)
+{
+ struct ldapconninfo *li = entry;
+ (void)key;
+ (void)klen;
+ if(li->ld) {
+ ldap_unbind_ext(li->ld, NULL, NULL);
+ li->ld = NULL;
+ }
+ free(li);
+}
+
static CURLcode oldap_connect(struct Curl_easy *data, bool *done)
{
struct connectdata *conn = data->conn;
struct ldapconninfo *li;
static const int version = LDAP_VERSION3;
+ char *hosturl = NULL;
+ CURLcode result;
int rc;
- char *hosturl;
#ifdef CURL_OPENLDAP_DEBUG
static int do_trace = -1;
#endif
(void)done;
- DEBUGASSERT(!conn->proto.ldapc);
li = calloc(1, sizeof(struct ldapconninfo));
- if(!li)
- return CURLE_OUT_OF_MEMORY;
- else {
- CURLcode result;
- li->proto = ldap_pvt_url_scheme2proto(data->state.up.scheme);
- conn->proto.ldapc = li;
+ if(!li) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
- /* Initialize the SASL storage */
- Curl_sasl_init(&li->sasl, data, &saslldap);
+ result = Curl_conn_meta_set(conn, CURL_META_LDAP_CONN, li, oldap_conn_dtor);
+ if(result)
+ goto out;
- /* Clear the TLS upgraded flag */
- conn->bits.tls_upgraded = FALSE;
+ li->proto = ldap_pvt_url_scheme2proto(data->state.up.scheme);
- result = oldap_parse_login_options(conn);
- if(result)
- return result;
- }
+ /* Initialize the SASL storage */
+ Curl_sasl_init(&li->sasl, data, &saslldap);
+
+ result = oldap_parse_login_options(conn);
+ if(result)
+ goto out;
hosturl = aprintf("%s://%s%s%s:%d",
conn->handler->scheme,
@@ -542,23 +607,25 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done)
conn->host.name,
conn->bits.ipv6_ip ? "]" : "",
conn->remote_port);
- if(!hosturl)
- return CURLE_OUT_OF_MEMORY;
+ if(!hosturl) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
rc = ldap_init_fd(conn->sock[FIRSTSOCKET], li->proto, hosturl, &li->ld);
if(rc) {
failf(data, "LDAP local: Cannot connect to %s, %s",
hosturl, ldap_err2string(rc));
- free(hosturl);
- return CURLE_COULDNT_CONNECT;
+ result = CURLE_COULDNT_CONNECT;
+ goto out;
}
- free(hosturl);
-
#ifdef CURL_OPENLDAP_DEBUG
if(do_trace < 0) {
const char *env = getenv("CURL_OPENLDAP_TRACE");
- do_trace = (env && strtol(env, NULL, 10) > 0);
+ curl_off_t e = 0;
+ if(!curlx_str_number(&env, &e, INT_MAX))
+ do_trace = e > 0;
}
if(do_trace)
ldap_set_option(li->ld, LDAP_OPT_DEBUG_LEVEL, &do_trace);
@@ -571,23 +638,30 @@ static CURLcode oldap_connect(struct Curl_easy *data, bool *done)
ldap_set_option(li->ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
#ifdef USE_SSL
- if(conn->handler->flags & PROTOPT_SSL)
- return oldap_ssl_connect(data, OLDAP_SSL);
+ if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
+ result = oldap_ssl_connect(data, OLDAP_SSL);
+ goto out;
+ }
if(data->set.use_ssl) {
- CURLcode result = oldap_perform_starttls(data);
-
+ result = oldap_perform_starttls(data);
if(!result || data->set.use_ssl != CURLUSESSL_TRY)
- return result;
+ goto out;
}
#endif
- if(li->sasl.prefmech != SASL_AUTH_NONE)
- return oldap_perform_mechs(data);
+ if(li->sasl.prefmech != SASL_AUTH_NONE) {
+ result = oldap_perform_mechs(data);
+ goto out;
+ }
/* Force bind even if anonymous bind is not needed in protocol version 3
to detect missing version 3 support. */
- return oldap_perform_bind(data, OLDAP_BIND);
+ result = oldap_perform_bind(data, OLDAP_BIND);
+
+out:
+ free(hosturl);
+ return result;
}
/* Handle the supported SASL mechanisms query response */
@@ -595,12 +669,14 @@ static CURLcode oldap_state_mechs_resp(struct Curl_easy *data,
LDAPMessage *msg, int code)
{
struct connectdata *conn = data->conn;
- struct ldapconninfo *li = conn->proto.ldapc;
+ struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
int rc;
BerElement *ber = NULL;
CURLcode result = CURLE_OK;
struct berval bv, *bvals;
+ if(!li)
+ return CURLE_FAILED_INIT;
switch(ldap_msgtype(msg)) {
case LDAP_RES_SEARCH_ENTRY:
/* Got a list of supported SASL mechanisms. */
@@ -660,11 +736,13 @@ static CURLcode oldap_state_sasl_resp(struct Curl_easy *data,
LDAPMessage *msg, int code)
{
struct connectdata *conn = data->conn;
- struct ldapconninfo *li = conn->proto.ldapc;
+ struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
CURLcode result = CURLE_OK;
saslprogress progress;
int rc;
+ if(!li)
+ return CURLE_FAILED_INIT;
li->servercred = NULL;
rc = ldap_parse_sasl_bind_result(li->ld, msg, &li->servercred, 0);
if(rc != LDAP_SUCCESS) {
@@ -674,7 +752,7 @@ static CURLcode oldap_state_sasl_resp(struct Curl_easy *data,
else {
result = Curl_sasl_continue(&li->sasl, data, code, &progress);
if(!result && progress != SASL_INPROGRESS)
- oldap_state(data, OLDAP_STOP);
+ oldap_state(data, li, OLDAP_STOP);
}
if(li->servercred)
@@ -687,11 +765,14 @@ static CURLcode oldap_state_bind_resp(struct Curl_easy *data, LDAPMessage *msg,
int code)
{
struct connectdata *conn = data->conn;
- struct ldapconninfo *li = conn->proto.ldapc;
+ struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
CURLcode result = CURLE_OK;
struct berval *bv = NULL;
int rc;
+ if(!li)
+ return CURLE_FAILED_INIT;
+
if(code != LDAP_SUCCESS)
return oldap_map_error(code, CURLE_LDAP_CANNOT_BIND);
@@ -702,7 +783,7 @@ static CURLcode oldap_state_bind_resp(struct Curl_easy *data, LDAPMessage *msg,
result = oldap_map_error(rc, CURLE_LDAP_CANNOT_BIND);
}
else
- oldap_state(data, OLDAP_STOP);
+ oldap_state(data, li, OLDAP_STOP);
if(bv)
ber_bvfree(bv);
@@ -713,12 +794,15 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done)
{
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
- struct ldapconninfo *li = conn->proto.ldapc;
+ struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
LDAPMessage *msg = NULL;
struct timeval tv = {0, 0};
int code = LDAP_SUCCESS;
int rc;
+ if(!li)
+ return CURLE_FAILED_INIT;
+
if(li->state != OLDAP_SSL && li->state != OLDAP_TLS) {
/* Get response to last command. */
rc = ldap_result(li->ld, li->msgid, LDAP_MSG_ONE, &tv, &msg);
@@ -793,14 +877,13 @@ static CURLcode oldap_connecting(struct Curl_easy *data, bool *done)
if(result)
result = oldap_map_error(code, CURLE_USE_SSL_FAILED);
else if(ssl_installed(conn)) {
- conn->bits.tls_upgraded = TRUE;
if(li->sasl.prefmech != SASL_AUTH_NONE)
result = oldap_perform_mechs(data);
else if(data->state.aptr.user)
result = oldap_perform_bind(data, OLDAP_BIND);
else {
/* Version 3 supported: no bind required */
- oldap_state(data, OLDAP_STOP);
+ oldap_state(data, li, OLDAP_STOP);
result = CURLE_OK;
}
}
@@ -840,7 +923,7 @@ static CURLcode oldap_disconnect(struct Curl_easy *data,
struct connectdata *conn,
bool dead_connection)
{
- struct ldapconninfo *li = conn->proto.ldapc;
+ struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
(void) dead_connection;
#ifndef USE_SSL
(void)data;
@@ -859,8 +942,6 @@ static CURLcode oldap_disconnect(struct Curl_easy *data,
li->ld = NULL;
}
Curl_sasl_cleanup(conn, li->sasl.authused);
- conn->proto.ldapc = NULL;
- free(li);
}
return CURLE_OK;
}
@@ -868,50 +949,55 @@ static CURLcode oldap_disconnect(struct Curl_easy *data,
static CURLcode oldap_do(struct Curl_easy *data, bool *done)
{
struct connectdata *conn = data->conn;
- struct ldapconninfo *li = conn->proto.ldapc;
+ struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
struct ldapreqinfo *lr;
CURLcode result;
int rc;
LDAPURLDesc *lud;
int msgid;
+ if(!li)
+ return CURLE_FAILED_INIT;
connkeep(conn, "OpenLDAP do");
infof(data, "LDAP local: %s", data->state.url);
result = oldap_url_parse(data, &lud);
- if(!result) {
+ if(result)
+ goto out;
+
#ifdef USE_SSL
- if(ssl_installed(conn)) {
- Sockbuf *sb;
- /* re-install the libcurl SSL handlers into the sockbuf. */
- ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
- ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data);
- }
+ if(ssl_installed(conn)) {
+ Sockbuf *sb;
+ /* re-install the libcurl SSL handlers into the sockbuf. */
+ ldap_get_option(li->ld, LDAP_OPT_SOCKBUF, &sb);
+ ber_sockbuf_add_io(sb, &ldapsb_tls, LBER_SBIOD_LEVEL_TRANSPORT, data);
+ }
#endif
- rc = ldap_search_ext(li->ld, lud->lud_dn, lud->lud_scope,
- lud->lud_filter, lud->lud_attrs, 0,
- NULL, NULL, NULL, 0, &msgid);
- ldap_free_urldesc(lud);
- if(rc != LDAP_SUCCESS) {
- failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc));
- result = CURLE_LDAP_SEARCH_FAILED;
- }
- else {
- lr = calloc(1, sizeof(struct ldapreqinfo));
- if(!lr) {
- ldap_abandon_ext(li->ld, msgid, NULL, NULL);
- result = CURLE_OUT_OF_MEMORY;
- }
- else {
- lr->msgid = msgid;
- data->req.p.ldap = lr;
- Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE);
- *done = TRUE;
- }
- }
+ rc = ldap_search_ext(li->ld, lud->lud_dn, lud->lud_scope,
+ lud->lud_filter, lud->lud_attrs, 0,
+ NULL, NULL, NULL, 0, &msgid);
+ ldap_free_urldesc(lud);
+ if(rc != LDAP_SUCCESS) {
+ failf(data, "LDAP local: ldap_search_ext %s", ldap_err2string(rc));
+ result = CURLE_LDAP_SEARCH_FAILED;
+ goto out;
+ }
+
+ lr = calloc(1, sizeof(struct ldapreqinfo));
+ if(!lr ||
+ Curl_meta_set(data, CURL_META_LDAP_EASY, lr, oldap_easy_dtor)) {
+ ldap_abandon_ext(li->ld, msgid, NULL, NULL);
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
}
+
+ lr->msgid = msgid;
+ Curl_xfer_setup1(data, CURL_XFER_RECV, -1, FALSE);
+ *done = TRUE;
+
+out:
return result;
}
@@ -919,7 +1005,7 @@ static CURLcode oldap_done(struct Curl_easy *data, CURLcode res,
bool premature)
{
struct connectdata *conn = data->conn;
- struct ldapreqinfo *lr = data->req.p.ldap;
+ struct ldapreqinfo *lr = Curl_meta_get(data, CURL_META_LDAP_EASY);
(void)res;
(void)premature;
@@ -927,12 +1013,13 @@ static CURLcode oldap_done(struct Curl_easy *data, CURLcode res,
if(lr) {
/* if there was a search in progress, abandon it */
if(lr->msgid) {
- struct ldapconninfo *li = conn->proto.ldapc;
- ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL);
+ struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
+ if(li && li->ld) {
+ ldap_abandon_ext(li->ld, lr->msgid, NULL, NULL);
+ }
lr->msgid = 0;
}
- data->req.p.ldap = NULL;
- free(lr);
+ Curl_meta_remove(data, CURL_META_LDAP_EASY);
}
return CURLE_OK;
@@ -950,13 +1037,13 @@ static CURLcode client_write(struct Curl_easy *data,
separator, drop the latter. */
if(!len && plen && prefix[plen - 1] == ' ')
plen--;
- result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) prefix, plen);
+ result = Curl_client_write(data, CLIENTWRITE_BODY, prefix, plen);
}
if(!result && value) {
- result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) value, len);
+ result = Curl_client_write(data, CLIENTWRITE_BODY, value, len);
}
if(!result && suffix) {
- result = Curl_client_write(data, CLIENTWRITE_BODY, (char *) suffix, slen);
+ result = Curl_client_write(data, CLIENTWRITE_BODY, suffix, slen);
}
return result;
}
@@ -965,8 +1052,8 @@ static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf,
size_t len, CURLcode *err)
{
struct connectdata *conn = data->conn;
- struct ldapconninfo *li = conn->proto.ldapc;
- struct ldapreqinfo *lr = data->req.p.ldap;
+ struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
+ struct ldapreqinfo *lr = Curl_meta_get(data, CURL_META_LDAP_EASY);
int rc;
LDAPMessage *msg = NULL;
BerElement *ber = NULL;
@@ -980,6 +1067,10 @@ static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf,
(void)len;
(void)buf;
(void)sockindex;
+ if(!li || !lr) {
+ *err = CURLE_FAILED_INIT;
+ return -1;
+ }
rc = ldap_result(li->ld, lr->msgid, LDAP_MSG_ONE, &tv, &msg);
if(rc < 0) {
@@ -1086,8 +1177,8 @@ static ssize_t oldap_recv(struct Curl_easy *data, int sockindex, char *buf,
/* Binary value, encode to base64. */
if(bvals[i].bv_len)
- result = Curl_base64_encode(bvals[i].bv_val, bvals[i].bv_len,
- &val_b64, &val_b64_sz);
+ result = curlx_base64_encode(bvals[i].bv_val, bvals[i].bv_len,
+ &val_b64, &val_b64_sz);
if(!result)
result = client_write(data, STRCONST(": "), val_b64, val_b64_sz,
STRCONST("\n"));
@@ -1165,18 +1256,21 @@ ldapsb_tls_read(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
if(data) {
struct connectdata *conn = data->conn;
if(conn) {
- struct ldapconninfo *li = conn->proto.ldapc;
+ struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
CURLcode err = CURLE_RECV_ERROR;
+ if(!li) {
+ SET_SOCKERRNO(SOCKEINVAL);
+ return -1;
+ }
ret = (li->recv)(data, FIRSTSOCKET, buf, len, &err);
if(ret < 0 && err == CURLE_AGAIN) {
- SET_SOCKERRNO(EWOULDBLOCK);
+ SET_SOCKERRNO(SOCKEWOULDBLOCK);
}
}
}
return ret;
}
-
static ber_slen_t
ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
{
@@ -1185,11 +1279,16 @@ ldapsb_tls_write(Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
if(data) {
struct connectdata *conn = data->conn;
if(conn) {
- struct ldapconninfo *li = conn->proto.ldapc;
+ struct ldapconninfo *li = Curl_conn_meta_get(conn, CURL_META_LDAP_CONN);
CURLcode err = CURLE_SEND_ERROR;
+
+ if(!li) {
+ SET_SOCKERRNO(SOCKEINVAL);
+ return -1;
+ }
ret = (li->send)(data, FIRSTSOCKET, buf, len, FALSE, &err);
if(ret < 0 && err == CURLE_AGAIN) {
- SET_SOCKERRNO(EWOULDBLOCK);
+ SET_SOCKERRNO(SOCKEWOULDBLOCK);
}
}
}
diff --git a/libs/libcurl/src/optiontable.pl b/libs/libcurl/src/optiontable.pl
index ec229b2392..31e3c8c620 100644
--- a/libs/libcurl/src/optiontable.pl
+++ b/libs/libcurl/src/optiontable.pl
@@ -31,7 +31,7 @@ print <<HEAD
#include "easyoptions.h"
/* all easy setopt options listed in alphabetical order */
-struct curl_easyoption Curl_easyopts[] = {
+const struct curl_easyoption Curl_easyopts[] = {
HEAD
;
@@ -109,7 +109,7 @@ while(<STDIN>) {
$o =~ s/^CURLOPT_//;
$n =~ s/^CURLOPT_//;
$alias{$o} = $n;
- push @names, $o,
+ push @names, $o;
}
}
}
@@ -145,7 +145,7 @@ print <<FOOT
*/
int Curl_easyopts_check(void)
{
- return ((CURLOPT_LASTENTRY%10000) != ($lastnum + 1));
+ return (CURLOPT_LASTENTRY % 10000) != ($lastnum + 1);
}
#endif
FOOT
diff --git a/libs/libcurl/src/parsedate.c b/libs/libcurl/src/parsedate.c
index 6438abebea..343fa9dcfe 100644
--- a/libs/libcurl/src/parsedate.c
+++ b/libs/libcurl/src/parsedate.c
@@ -81,8 +81,9 @@
#include <curl/curl.h>
#include "strcase.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
#include "parsedate.h"
+#include "curlx/strparse.h"
/*
* parsedate()
@@ -100,10 +101,12 @@ static int parsedate(const char *date, time_t *output);
#define PARSEDATE_OK 0
#define PARSEDATE_FAIL -1
#define PARSEDATE_LATER 1
+#if defined(HAVE_TIME_T_UNSIGNED) || (SIZEOF_TIME_T < 5)
#define PARSEDATE_SOONER 2
+#endif
#if !defined(CURL_DISABLE_PARSEDATE) || !defined(CURL_DISABLE_FTP) || \
- !defined(CURL_DISABLE_FILE)
+ !defined(CURL_DISABLE_FILE) || defined(USE_GNUTLS)
/* These names are also used by FTP and FILE code */
const char * const Curl_wkday[] =
{"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
@@ -253,7 +256,7 @@ static int checktz(const char *check, size_t len)
if(len > 4) /* longer than any valid timezone */
return -1;
- for(i = 0; i < sizeof(tz)/sizeof(tz[0]); i++) {
+ for(i = 0; i < CURL_ARRAYSIZE(tz); i++) {
size_t ilen = strlen(what->name);
if((ilen == len) &&
strncasecompare(check, what->name, len))
@@ -336,7 +339,7 @@ match:
*h = hh;
*m = mm;
*s = ss;
- *endp = (char *)p;
+ *endp = (char *)CURL_UNCONST(p);
return TRUE;
}
@@ -409,7 +412,7 @@ static int parsedate(const char *date, time_t *output)
}
else if(ISDIGIT(*date)) {
/* a digit */
- int val;
+ unsigned int val;
char *end;
if((secnum == -1) &&
match_time(date, &hournum, &minnum, &secnum, &end)) {
@@ -417,29 +420,18 @@ static int parsedate(const char *date, time_t *output)
date = end;
}
else {
- long lval;
- int error;
- int old_errno;
-
- old_errno = errno;
- errno = 0;
- lval = strtol(date, &end, 10);
- error = errno;
- if(errno != old_errno)
- errno = old_errno;
-
- if(error)
+ curl_off_t lval;
+ int num_digits = 0;
+ const char *p = date;
+ if(curlx_str_number(&p, &lval, 99999999))
return PARSEDATE_FAIL;
-#if LONG_MAX != INT_MAX
- if((lval > (long)INT_MAX) || (lval < (long)INT_MIN))
- return PARSEDATE_FAIL;
-#endif
-
- val = curlx_sltosi(lval);
+ /* we know num_digits cannot be larger than 8 */
+ num_digits = (int)(p - date);
+ val = (unsigned int)lval;
if((tzoff == -1) &&
- ((end - date) == 4) &&
+ (num_digits == 4) &&
(val <= 1400) &&
(indate < date) &&
((date[-1] == '+' || date[-1] == '-'))) {
@@ -459,10 +451,10 @@ static int parsedate(const char *date, time_t *output)
tzoff = date[-1]=='+' ? -tzoff : tzoff;
}
- if(((end - date) == 8) &&
- (yearnum == -1) &&
- (monnum == -1) &&
- (mdaynum == -1)) {
+ else if((num_digits == 8) &&
+ (yearnum == -1) &&
+ (monnum == -1) &&
+ (mdaynum == -1)) {
/* 8 digits, no year, month or day yet. This is YYYYMMDD */
found = TRUE;
yearnum = val/10000;
@@ -494,7 +486,7 @@ static int parsedate(const char *date, time_t *output)
if(!found)
return PARSEDATE_FAIL;
- date = end;
+ date = p;
}
}
@@ -558,7 +550,7 @@ static int parsedate(const char *date, time_t *output)
if(tzoff == -1)
tzoff = 0;
- if((tzoff > 0) && (t > TIME_T_MAX - tzoff)) {
+ if((tzoff > 0) && (t > (time_t)(TIME_T_MAX - tzoff))) {
*output = TIME_T_MAX;
return PARSEDATE_LATER; /* time_t overflow */
}
@@ -586,7 +578,7 @@ time_t curl_getdate(const char *p, const time_t *now)
(void)now; /* legacy argument from the past that we ignore */
if(rc == PARSEDATE_OK) {
- if(parsed == -1)
+ if(parsed == (time_t)-1)
/* avoid returning -1 for a working scenario */
parsed++;
return parsed;
@@ -606,7 +598,7 @@ time_t Curl_getdate_capped(const char *p)
switch(rc) {
case PARSEDATE_OK:
- if(parsed == -1)
+ if(parsed == (time_t)-1)
/* avoid returning -1 for a working scenario */
parsed++;
return parsed;
diff --git a/libs/libcurl/src/pingpong.c b/libs/libcurl/src/pingpong.c
index 6f1e4c3092..4f0171ad3d 100644
--- a/libs/libcurl/src/pingpong.c
+++ b/libs/libcurl/src/pingpong.c
@@ -29,6 +29,7 @@
#include "urldata.h"
#include "cfilters.h"
+#include "connect.h"
#include "sendf.h"
#include "select.h"
#include "progress.h"
@@ -50,10 +51,10 @@
timediff_t Curl_pp_state_timeout(struct Curl_easy *data,
struct pingpong *pp, bool disconnecting)
{
- struct connectdata *conn = data->conn;
timediff_t timeout_ms; /* in milliseconds */
timediff_t response_time = (data->set.server_response_timeout) ?
data->set.server_response_timeout : pp->response_time;
+ struct curltime now = curlx_now();
/* if CURLOPT_SERVER_RESPONSE_TIMEOUT is set, use that to determine
remaining time, or use pp->response because SERVER_RESPONSE_TIMEOUT is
@@ -62,18 +63,20 @@ timediff_t Curl_pp_state_timeout(struct Curl_easy *data,
/* Without a requested timeout, we only wait 'response_time' seconds for the
full response to arrive before we bail out */
- timeout_ms = response_time -
- Curl_timediff(Curl_now(), pp->response); /* spent time */
+ timeout_ms = response_time - curlx_timediff(now, pp->response);
if(data->set.timeout && !disconnecting) {
- /* if timeout is requested, find out how much remaining time we have */
- timediff_t timeout2_ms = data->set.timeout - /* timeout time */
- Curl_timediff(Curl_now(), conn->now); /* spent time */
-
+ /* if timeout is requested, find out how much overall remains */
+ timediff_t timeout2_ms = Curl_timeleft(data, &now, FALSE);
/* pick the lowest number */
timeout_ms = CURLMIN(timeout_ms, timeout2_ms);
}
+ if(disconnecting) {
+ timediff_t total_left_ms = Curl_timeleft(data, NULL, FALSE);
+ timeout_ms = CURLMIN(timeout_ms, CURLMAX(total_left_ms, 0));
+ }
+
return timeout_ms;
}
@@ -96,6 +99,7 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data,
return CURLE_OPERATION_TIMEDOUT; /* already too little time */
}
+ DEBUGF(infof(data, "pp_statematch, timeout=%" FMT_TIMEDIFF_T, timeout_ms));
if(block) {
interval_ms = 1000; /* use 1 second timeout intervals */
if(timeout_ms < interval_ms)
@@ -123,7 +127,7 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data,
if(Curl_pgrsUpdate(data))
result = CURLE_ABORTED_BY_CALLBACK;
else
- result = Curl_speedcheck(data, Curl_now());
+ result = Curl_speedcheck(data, curlx_now());
if(result)
return result;
@@ -135,6 +139,8 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data,
}
else if(rc)
result = pp->statemachine(data, data->conn);
+ else if(disconnecting)
+ return CURLE_OPERATION_TIMEDOUT;
return result;
}
@@ -142,11 +148,13 @@ CURLcode Curl_pp_statemach(struct Curl_easy *data,
/* initialize stuff to prepare for reading a fresh new response */
void Curl_pp_init(struct pingpong *pp)
{
+ DEBUGASSERT(!pp->initialised);
pp->nread_resp = 0;
- pp->response = Curl_now(); /* start response time-out now! */
+ pp->response = curlx_now(); /* start response time-out now! */
pp->pending_resp = TRUE;
- Curl_dyn_init(&pp->sendbuf, DYN_PINGPPONG_CMD);
- Curl_dyn_init(&pp->recvbuf, DYN_PINGPPONG_CMD);
+ curlx_dyn_init(&pp->sendbuf, DYN_PINGPPONG_CMD);
+ curlx_dyn_init(&pp->recvbuf, DYN_PINGPPONG_CMD);
+ pp->initialised = TRUE;
}
/***********************************************************************
@@ -182,19 +190,19 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data,
/* cannot send without a connection! */
return CURLE_SEND_ERROR;
- Curl_dyn_reset(&pp->sendbuf);
- result = Curl_dyn_vaddf(&pp->sendbuf, fmt, args);
+ curlx_dyn_reset(&pp->sendbuf);
+ result = curlx_dyn_vaddf(&pp->sendbuf, fmt, args);
if(result)
return result;
/* append CRLF */
- result = Curl_dyn_addn(&pp->sendbuf, "\r\n", 2);
+ result = curlx_dyn_addn(&pp->sendbuf, "\r\n", 2);
if(result)
return result;
pp->pending_resp = TRUE;
- write_len = Curl_dyn_len(&pp->sendbuf);
- s = Curl_dyn_ptr(&pp->sendbuf);
+ write_len = curlx_dyn_len(&pp->sendbuf);
+ s = curlx_dyn_ptr(&pp->sendbuf);
#ifdef HAVE_GSSAPI
conn->data_prot = PROT_CMD;
@@ -223,7 +231,7 @@ CURLcode Curl_pp_vsendf(struct Curl_easy *data,
else {
pp->sendthis = NULL;
pp->sendleft = pp->sendsize = 0;
- pp->response = Curl_now();
+ pp->response = curlx_now();
}
return CURLE_OK;
@@ -297,10 +305,10 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data,
if(pp->nfinal) {
/* a previous call left this many bytes in the beginning of the buffer as
that was the final line; now ditch that */
- size_t full = Curl_dyn_len(&pp->recvbuf);
+ size_t full = curlx_dyn_len(&pp->recvbuf);
/* trim off the "final" leading part */
- Curl_dyn_tail(&pp->recvbuf, full - pp->nfinal);
+ curlx_dyn_tail(&pp->recvbuf, full - pp->nfinal);
pp->nfinal = 0; /* now gone */
}
@@ -318,7 +326,7 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data,
return CURLE_RECV_ERROR;
}
- result = Curl_dyn_addn(&pp->recvbuf, buffer, gotbytes);
+ result = curlx_dyn_addn(&pp->recvbuf, buffer, gotbytes);
if(result)
return result;
@@ -328,8 +336,8 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data,
}
do {
- char *line = Curl_dyn_ptr(&pp->recvbuf);
- char *nl = memchr(line, '\n', Curl_dyn_len(&pp->recvbuf));
+ char *line = curlx_dyn_ptr(&pp->recvbuf);
+ char *nl = memchr(line, '\n', curlx_dyn_len(&pp->recvbuf));
if(nl) {
/* a newline is CRLF in pp-talk, so the CR is ignored as
the line is not really terminated until the LF comes */
@@ -355,8 +363,8 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data,
parsers). Store the overflow counter to inform about additional
data in this buffer after the endofresp line. */
pp->nfinal = length;
- if(Curl_dyn_len(&pp->recvbuf) > length)
- pp->overflow = Curl_dyn_len(&pp->recvbuf) - length;
+ if(curlx_dyn_len(&pp->recvbuf) > length)
+ pp->overflow = curlx_dyn_len(&pp->recvbuf) - length;
else
pp->overflow = 0;
*size = pp->nread_resp; /* size of the response */
@@ -364,11 +372,11 @@ CURLcode Curl_pp_readresp(struct Curl_easy *data,
gotbytes = 0; /* force break out of outer loop */
break;
}
- if(Curl_dyn_len(&pp->recvbuf) > length)
+ if(curlx_dyn_len(&pp->recvbuf) > length)
/* keep the remaining piece */
- Curl_dyn_tail((&pp->recvbuf), Curl_dyn_len(&pp->recvbuf) - length);
+ curlx_dyn_tail((&pp->recvbuf), curlx_dyn_len(&pp->recvbuf) - length);
else
- Curl_dyn_reset(&pp->recvbuf);
+ curlx_dyn_reset(&pp->recvbuf);
}
else {
/* without a newline, there is no overflow */
@@ -434,21 +442,24 @@ CURLcode Curl_pp_flushsend(struct Curl_easy *data,
else {
pp->sendthis = NULL;
pp->sendleft = pp->sendsize = 0;
- pp->response = Curl_now();
+ pp->response = curlx_now();
}
return CURLE_OK;
}
CURLcode Curl_pp_disconnect(struct pingpong *pp)
{
- Curl_dyn_free(&pp->sendbuf);
- Curl_dyn_free(&pp->recvbuf);
+ if(pp->initialised) {
+ curlx_dyn_free(&pp->sendbuf);
+ curlx_dyn_free(&pp->recvbuf);
+ memset(pp, 0, sizeof(*pp));
+ }
return CURLE_OK;
}
bool Curl_pp_moredata(struct pingpong *pp)
{
- return (!pp->sendleft && Curl_dyn_len(&pp->recvbuf) > pp->nfinal);
+ return !pp->sendleft && curlx_dyn_len(&pp->recvbuf) > pp->nfinal;
}
#endif
diff --git a/libs/libcurl/src/pingpong.h b/libs/libcurl/src/pingpong.h
index ffd1fd7741..46a02d763e 100644
--- a/libs/libcurl/src/pingpong.h
+++ b/libs/libcurl/src/pingpong.h
@@ -48,9 +48,6 @@ typedef enum {
*/
struct pingpong {
size_t nread_resp; /* number of bytes currently read of a server response */
- bool pending_resp; /* set TRUE when a server response is pending or in
- progress, and is cleared once the last response is
- read */
char *sendthis; /* pointer to a buffer that is to be sent to the server */
size_t sendleft; /* number of bytes left to send from the sendthis buffer */
size_t sendsize; /* total size of the sendthis buffer */
@@ -69,14 +66,18 @@ struct pingpong {
CURLcode (*statemachine)(struct Curl_easy *data, struct connectdata *conn);
bool (*endofresp)(struct Curl_easy *data, struct connectdata *conn,
- char *ptr, size_t len, int *code);
+ const char *ptr, size_t len, int *code);
+ BIT(initialised);
+ BIT(pending_resp); /* set TRUE when a server response is pending or in
+ progress, and is cleared once the last response is
+ read */
};
#define PINGPONG_SETUP(pp,s,e) \
do { \
- pp->response_time = RESP_TIMEOUT; \
- pp->statemachine = s; \
- pp->endofresp = e; \
+ (pp)->response_time = RESP_TIMEOUT; \
+ (pp)->statemachine = s; \
+ (pp)->endofresp = e; \
} while(0)
/*
diff --git a/libs/libcurl/src/pop3.c b/libs/libcurl/src/pop3.c
index ca88747ce6..a61973520b 100644
--- a/libs/libcurl/src/pop3.c
+++ b/libs/libcurl/src/pop3.c
@@ -64,8 +64,8 @@
#include "escape.h"
#include "http.h" /* for HTTP proxy tunnel stuff */
#include "socks.h"
+#include "pingpong.h"
#include "pop3.h"
-#include "strtoofft.h"
#include "strcase.h"
#include "vtls/vtls.h"
#include "cfilters.h"
@@ -76,16 +76,74 @@
#include "bufref.h"
#include "curl_sasl.h"
#include "curl_md5.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
#include "strdup.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
#include "memdebug.h"
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
-#endif
+/* Authentication type flags */
+#define POP3_TYPE_CLEARTEXT (1 << 0)
+#define POP3_TYPE_APOP (1 << 1)
+#define POP3_TYPE_SASL (1 << 2)
+
+/* Authentication type values */
+#define POP3_TYPE_NONE 0
+#define POP3_TYPE_ANY (POP3_TYPE_CLEARTEXT|POP3_TYPE_APOP|POP3_TYPE_SASL)
+
+/* This is the 5-bytes End-Of-Body marker for POP3 */
+#define POP3_EOB "\x0d\x0a\x2e\x0d\x0a"
+#define POP3_EOB_LEN 5
+
+/* meta key for storing protocol meta at easy handle */
+#define CURL_META_POP3_EASY "meta:proto:pop3:easy"
+/* meta key for storing protocol meta at connection */
+#define CURL_META_POP3_CONN "meta:proto:pop3:conn"
+
+/*
+ * POP3 easy handle state
+ */
+struct POP3 {
+ curl_pp_transfer transfer;
+ char *id; /* Message ID */
+ char *custom; /* Custom Request */
+};
+
+/*
+ * POP3 connection state
+ */
+typedef enum {
+ POP3_STOP, /* do nothing state, stops the state machine */
+ POP3_SERVERGREET, /* waiting for the initial greeting immediately after
+ a connect */
+ POP3_CAPA,
+ POP3_STARTTLS,
+ POP3_UPGRADETLS, /* asynchronously upgrade the connection to SSL/TLS
+ (multi mode only) */
+ POP3_AUTH,
+ POP3_APOP,
+ POP3_USER,
+ POP3_PASS,
+ POP3_COMMAND,
+ POP3_QUIT,
+ POP3_LAST /* never used */
+} pop3state;
+
+struct pop3_conn {
+ struct pingpong pp;
+ pop3state state; /* Always use pop3.c:state() to change state! */
+ size_t eob; /* Number of bytes of the EOB (End Of Body) that
+ have been received so far */
+ size_t strip; /* Number of bytes from the start to ignore as
+ non-body */
+ struct SASL sasl; /* SASL-related storage */
+ char *apoptimestamp; /* APOP timestamp from the server greeting */
+ unsigned char authtypes; /* Accepted authentication types */
+ unsigned char preftype; /* Preferred authentication type */
+ BIT(ssldone); /* Is connect() over SSL done? */
+ BIT(tls_supported); /* StartTLS capability supported by server */
+};
/* Local API functions */
static CURLcode pop3_regular_transfer(struct Curl_easy *data, bool *done);
@@ -138,6 +196,7 @@ const struct Curl_handler Curl_handler_pop3 = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_POP3, /* defport */
CURLPROTO_POP3, /* protocol */
CURLPROTO_POP3, /* family */
@@ -168,6 +227,7 @@ const struct Curl_handler Curl_handler_pop3s = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_POP3S, /* defport */
CURLPROTO_POP3S, /* protocol */
CURLPROTO_POP3, /* family */
@@ -190,19 +250,6 @@ static const struct SASLproto saslpop3 = {
SASL_FLAG_BASE64 /* Configuration flags */
};
-#ifdef USE_SSL
-static void pop3_to_pop3s(struct connectdata *conn)
-{
- /* Change the connection handler */
- conn->handler = &Curl_handler_pop3s;
-
- /* Set the connection's upgraded to TLS flag */
- conn->bits.tls_upgraded = TRUE;
-}
-#else
-#define pop3_to_pop3s(x) Curl_nop_stmt
-#endif
-
struct pop3_cmd {
const char *name;
unsigned short nlen;
@@ -237,7 +284,7 @@ static const struct pop3_cmd pop3cmds[] = {
static bool pop3_is_multiline(const char *cmdline)
{
size_t i;
- for(i = 0; i < ARRAYSIZE(pop3cmds); ++i) {
+ for(i = 0; i < CURL_ARRAYSIZE(pop3cmds); ++i) {
if(strncasecompare(pop3cmds[i].name, cmdline, pop3cmds[i].nlen)) {
if(!cmdline[pop3cmds[i].nlen])
return pop3cmds[i].multiline;
@@ -260,10 +307,13 @@ static bool pop3_is_multiline(const char *cmdline)
* types and allowed SASL mechanisms.
*/
static bool pop3_endofresp(struct Curl_easy *data, struct connectdata *conn,
- char *line, size_t len, int *resp)
+ const char *line, size_t len, int *resp)
{
- struct pop3_conn *pop3c = &conn->proto.pop3c;
+ struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
(void)data;
+ DEBUGASSERT(pop3c);
+ if(!pop3c) /* internal error */
+ return TRUE;
/* Do we have an error response? */
if(len >= 4 && !memcmp("-ERR", line, 4)) {
@@ -310,9 +360,15 @@ static bool pop3_endofresp(struct Curl_easy *data, struct connectdata *conn,
*/
static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out)
{
- char *message = Curl_dyn_ptr(&data->conn->proto.pop3c.pp.recvbuf);
- size_t len = data->conn->proto.pop3c.pp.nfinal;
-
+ struct pop3_conn *pop3c =
+ Curl_conn_meta_get(data->conn, CURL_META_POP3_CONN);
+ char *message;
+ size_t len;
+
+ if(!pop3c)
+ return CURLE_FAILED_INIT;
+ message = curlx_dyn_ptr(&pop3c->pp.recvbuf);
+ len = pop3c->pp.nfinal;
if(len > 2) {
/* Find the start of the message */
len -= 2;
@@ -344,30 +400,33 @@ static CURLcode pop3_get_message(struct Curl_easy *data, struct bufref *out)
*/
static void pop3_state(struct Curl_easy *data, pop3state newstate)
{
- struct pop3_conn *pop3c = &data->conn->proto.pop3c;
+ struct pop3_conn *pop3c =
+ Curl_conn_meta_get(data->conn, CURL_META_POP3_CONN);
+ if(pop3c) {
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
- /* for debug purposes */
- static const char * const names[] = {
- "STOP",
- "SERVERGREET",
- "CAPA",
- "STARTTLS",
- "UPGRADETLS",
- "AUTH",
- "APOP",
- "USER",
- "PASS",
- "COMMAND",
- "QUIT",
- /* LAST */
- };
-
- if(pop3c->state != newstate)
- infof(data, "POP3 %p state change from %s to %s",
- (void *)pop3c, names[pop3c->state], names[newstate]);
+ /* for debug purposes */
+ static const char * const names[] = {
+ "STOP",
+ "SERVERGREET",
+ "CAPA",
+ "STARTTLS",
+ "UPGRADETLS",
+ "AUTH",
+ "APOP",
+ "USER",
+ "PASS",
+ "COMMAND",
+ "QUIT",
+ /* LAST */
+ };
+
+ if(pop3c->state != newstate)
+ infof(data, "POP3 %p state change from %s to %s",
+ (void *)pop3c, names[pop3c->state], names[newstate]);
#endif
- pop3c->state = newstate;
+ pop3c->state = newstate;
+ }
}
/***********************************************************************
@@ -380,8 +439,11 @@ static void pop3_state(struct Curl_easy *data, pop3state newstate)
static CURLcode pop3_perform_capa(struct Curl_easy *data,
struct connectdata *conn)
{
+ struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
CURLcode result = CURLE_OK;
- struct pop3_conn *pop3c = &conn->proto.pop3c;
+
+ if(!pop3c)
+ return CURLE_FAILED_INIT;
pop3c->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanisms yet */
pop3c->sasl.authused = SASL_AUTH_NONE; /* Clear the auth. mechanism used */
@@ -405,9 +467,14 @@ static CURLcode pop3_perform_capa(struct Curl_easy *data,
static CURLcode pop3_perform_starttls(struct Curl_easy *data,
struct connectdata *conn)
{
- /* Send the STLS command */
- CURLcode result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", "STLS");
+ struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
+ CURLcode result;
+
+ if(!pop3c)
+ return CURLE_FAILED_INIT;
+ /* Send the STLS command */
+ result = Curl_pp_sendf(data, &pop3c->pp, "%s", "STLS");
if(!result)
pop3_state(data, POP3_STARTTLS);
@@ -423,31 +490,39 @@ static CURLcode pop3_perform_starttls(struct Curl_easy *data,
static CURLcode pop3_perform_upgrade_tls(struct Curl_easy *data,
struct connectdata *conn)
{
+#ifdef USE_SSL
/* Start the SSL connection */
- struct pop3_conn *pop3c = &conn->proto.pop3c;
+ struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
CURLcode result;
bool ssldone = FALSE;
+ if(!pop3c)
+ return CURLE_FAILED_INIT;
+
if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
if(result)
goto out;
+ /* Change the connection handler */
+ conn->handler = &Curl_handler_pop3s;
}
+ DEBUGASSERT(!pop3c->ssldone);
result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
-
- if(!result) {
+ DEBUGF(infof(data, "pop3_perform_upgrade_tls, connect -> %d, %d",
+ result, ssldone));
+ if(!result && ssldone) {
pop3c->ssldone = ssldone;
- if(pop3c->state != POP3_UPGRADETLS)
- pop3_state(data, POP3_UPGRADETLS);
-
- if(pop3c->ssldone) {
- pop3_to_pop3s(conn);
- result = pop3_perform_capa(data, conn);
- }
+ /* perform CAPA now, changes pop3c->state out of POP3_UPGRADETLS */
+ result = pop3_perform_capa(data, conn);
}
out:
return result;
+#else
+ (void)data;
+ (void)conn;
+ return CURLE_NOT_BUILT_IN;
+#endif
}
/***********************************************************************
@@ -459,8 +534,12 @@ out:
static CURLcode pop3_perform_user(struct Curl_easy *data,
struct connectdata *conn)
{
+ struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
CURLcode result = CURLE_OK;
+ if(!pop3c)
+ return CURLE_FAILED_INIT;
+
/* Check we have a username and password to authenticate with and end the
connect phase if we do not */
if(!data->state.aptr.user) {
@@ -470,7 +549,7 @@ static CURLcode pop3_perform_user(struct Curl_easy *data,
}
/* Send the USER command */
- result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "USER %s",
+ result = Curl_pp_sendf(data, &pop3c->pp, "USER %s",
conn->user ? conn->user : "");
if(!result)
pop3_state(data, POP3_USER);
@@ -488,13 +567,16 @@ static CURLcode pop3_perform_user(struct Curl_easy *data,
static CURLcode pop3_perform_apop(struct Curl_easy *data,
struct connectdata *conn)
{
+ struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
CURLcode result = CURLE_OK;
- struct pop3_conn *pop3c = &conn->proto.pop3c;
size_t i;
struct MD5_context *ctxt;
unsigned char digest[MD5_DIGEST_LEN];
char secret[2 * MD5_DIGEST_LEN + 1];
+ if(!pop3c)
+ return CURLE_FAILED_INIT;
+
/* Check we have a username and password to authenticate with and end the
connect phase if we do not */
if(!data->state.aptr.user) {
@@ -541,10 +623,14 @@ static CURLcode pop3_perform_auth(struct Curl_easy *data,
const char *mech,
const struct bufref *initresp)
{
+ struct pop3_conn *pop3c =
+ Curl_conn_meta_get(data->conn, CURL_META_POP3_CONN);
CURLcode result = CURLE_OK;
- struct pop3_conn *pop3c = &data->conn->proto.pop3c;
const char *ir = (const char *) Curl_bufref_ptr(initresp);
+ if(!pop3c)
+ return CURLE_FAILED_INIT;
+
if(ir) { /* AUTH <mech> ...<crlf> */
/* Send the AUTH command with the initial response */
result = Curl_pp_sendf(data, &pop3c->pp, "AUTH %s %s", mech, ir);
@@ -567,9 +653,12 @@ static CURLcode pop3_continue_auth(struct Curl_easy *data,
const char *mech,
const struct bufref *resp)
{
- struct pop3_conn *pop3c = &data->conn->proto.pop3c;
+ struct pop3_conn *pop3c =
+ Curl_conn_meta_get(data->conn, CURL_META_POP3_CONN);
(void)mech;
+ if(!pop3c)
+ return CURLE_FAILED_INIT;
return Curl_pp_sendf(data, &pop3c->pp,
"%s", (const char *) Curl_bufref_ptr(resp));
@@ -583,9 +672,12 @@ static CURLcode pop3_continue_auth(struct Curl_easy *data,
*/
static CURLcode pop3_cancel_auth(struct Curl_easy *data, const char *mech)
{
- struct pop3_conn *pop3c = &data->conn->proto.pop3c;
+ struct pop3_conn *pop3c =
+ Curl_conn_meta_get(data->conn, CURL_META_POP3_CONN);
(void)mech;
+ if(!pop3c)
+ return CURLE_FAILED_INIT;
return Curl_pp_sendf(data, &pop3c->pp, "*");
}
@@ -601,10 +693,13 @@ static CURLcode pop3_cancel_auth(struct Curl_easy *data, const char *mech)
static CURLcode pop3_perform_authentication(struct Curl_easy *data,
struct connectdata *conn)
{
+ struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
CURLcode result = CURLE_OK;
- struct pop3_conn *pop3c = &conn->proto.pop3c;
saslprogress progress = SASL_IDLE;
+ if(!pop3c)
+ return CURLE_FAILED_INIT;
+
/* Check we have enough data to authenticate with and end the
connect phase if we do not */
if(!Curl_sasl_can_authenticate(&pop3c->sasl, data)) {
@@ -631,11 +726,8 @@ static CURLcode pop3_perform_authentication(struct Curl_easy *data,
if(pop3c->authtypes & pop3c->preftype & POP3_TYPE_CLEARTEXT)
/* Perform clear text authentication */
result = pop3_perform_user(data, conn);
- else {
- /* Other mechanisms not supported */
- infof(data, "No known authentication mechanisms supported");
- result = CURLE_LOGIN_DENIED;
- }
+ else
+ result = Curl_sasl_is_blocked(&pop3c->sasl, data);
}
return result;
@@ -651,9 +743,13 @@ static CURLcode pop3_perform_command(struct Curl_easy *data)
{
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
- struct POP3 *pop3 = data->req.p.pop3;
+ struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY);
+ struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
const char *command = NULL;
+ if(!pop3 || !pop3c)
+ return CURLE_FAILED_INIT;
+
/* Calculate the default command */
if(pop3->id[0] == '\0' || data->set.list_only) {
command = "LIST";
@@ -670,10 +766,9 @@ static CURLcode pop3_perform_command(struct Curl_easy *data)
/* Send the command */
if(pop3->id[0] != '\0')
- result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s %s",
- command, pop3->id);
+ result = Curl_pp_sendf(data, &pop3c->pp, "%s %s", command, pop3->id);
else
- result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", command);
+ result = Curl_pp_sendf(data, &pop3c->pp, "%s", command);
if(!result) {
pop3_state(data, POP3_COMMAND);
@@ -692,9 +787,14 @@ static CURLcode pop3_perform_command(struct Curl_easy *data)
static CURLcode pop3_perform_quit(struct Curl_easy *data,
struct connectdata *conn)
{
- /* Send the QUIT command */
- CURLcode result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "%s", "QUIT");
+ struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
+ CURLcode result;
+ if(!pop3c)
+ return CURLE_FAILED_INIT;
+
+ /* Send the QUIT command */
+ result = Curl_pp_sendf(data, &pop3c->pp, "%s", "QUIT");
if(!result)
pop3_state(data, POP3_QUIT);
@@ -708,11 +808,16 @@ static CURLcode pop3_state_servergreet_resp(struct Curl_easy *data,
{
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
- struct pop3_conn *pop3c = &conn->proto.pop3c;
- const char *line = Curl_dyn_ptr(&data->conn->proto.pop3c.pp.recvbuf);
- size_t len = data->conn->proto.pop3c.pp.nfinal;
+ struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
+ const char *line;
+ size_t len;
(void)instate; /* no use for this yet */
+ if(!pop3c)
+ return CURLE_FAILED_INIT;
+
+ line = curlx_dyn_ptr(&pop3c->pp.recvbuf);
+ len = pop3c->pp.nfinal;
if(pop3code != '+') {
failf(data, "Got unexpected pop3-server response");
@@ -758,13 +863,18 @@ static CURLcode pop3_state_capa_resp(struct Curl_easy *data, int pop3code,
{
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
- struct pop3_conn *pop3c = &conn->proto.pop3c;
- const char *line = Curl_dyn_ptr(&data->conn->proto.pop3c.pp.recvbuf);
- size_t len = data->conn->proto.pop3c.pp.nfinal;
+ struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
+ const char *line;
+ size_t len;
(void)instate; /* no use for this yet */
+ if(!pop3c)
+ return CURLE_FAILED_INIT;
- /* Do we have a untagged continuation response? */
+ line = curlx_dyn_ptr(&pop3c->pp.recvbuf);
+ len = pop3c->pp.nfinal;
+
+ /* Do we have an untagged continuation response? */
if(pop3code == '*') {
/* Does the server support the STLS capability? */
if(len >= 4 && !memcmp(line, "STLS", 4))
@@ -843,11 +953,15 @@ static CURLcode pop3_state_starttls_resp(struct Curl_easy *data,
int pop3code,
pop3state instate)
{
+ struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
CURLcode result = CURLE_OK;
(void)instate; /* no use for this yet */
+ if(!pop3c)
+ return CURLE_FAILED_INIT;
+
/* Pipelining in response is forbidden. */
- if(data->conn->proto.pop3c.pp.overflow)
+ if(pop3c->pp.overflow)
return CURLE_WEIRD_SERVER_REPLY;
if(pop3code != '+') {
@@ -859,7 +973,7 @@ static CURLcode pop3_state_starttls_resp(struct Curl_easy *data,
result = pop3_perform_authentication(data, conn);
}
else
- result = pop3_perform_upgrade_tls(data, conn);
+ pop3_state(data, POP3_UPGRADETLS);
return result;
}
@@ -871,10 +985,12 @@ static CURLcode pop3_state_auth_resp(struct Curl_easy *data,
{
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
- struct pop3_conn *pop3c = &conn->proto.pop3c;
+ struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
saslprogress progress;
(void)instate; /* no use for this yet */
+ if(!pop3c)
+ return CURLE_FAILED_INIT;
result = Curl_sasl_continue(&pop3c->sasl, data, pop3code, &progress);
if(!result)
@@ -930,15 +1046,19 @@ static CURLcode pop3_state_user_resp(struct Curl_easy *data, int pop3code,
{
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
+ struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
(void)instate; /* no use for this yet */
+ if(!pop3c)
+ return CURLE_FAILED_INIT;
+
if(pop3code != '+') {
failf(data, "Access denied. %c", pop3code);
result = CURLE_LOGIN_DENIED;
}
else
/* Send the PASS command */
- result = Curl_pp_sendf(data, &conn->proto.pop3c.pp, "PASS %s",
+ result = Curl_pp_sendf(data, &pop3c->pp, "PASS %s",
conn->passwd ? conn->passwd : "");
if(!result)
pop3_state(data, POP3_PASS);
@@ -971,12 +1091,15 @@ static CURLcode pop3_state_command_resp(struct Curl_easy *data,
{
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
- struct POP3 *pop3 = data->req.p.pop3;
- struct pop3_conn *pop3c = &conn->proto.pop3c;
- struct pingpong *pp = &pop3c->pp;
+ struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY);
+ struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
+ struct pingpong *pp;
(void)instate; /* no use for this yet */
+ if(!pop3 || !pop3c)
+ return CURLE_FAILED_INIT;
+ pp = &pop3c->pp;
if(pop3code != '+') {
pop3_state(data, POP3_STOP);
return CURLE_WEIRD_SERVER_REPLY;
@@ -1002,18 +1125,18 @@ static CURLcode pop3_state_command_resp(struct Curl_easy *data,
the body */
/* keep only the overflow */
- Curl_dyn_tail(&pp->recvbuf, pp->overflow);
+ curlx_dyn_tail(&pp->recvbuf, pp->overflow);
pp->nfinal = 0; /* done */
if(!data->req.no_body) {
- result = pop3_write(data, Curl_dyn_ptr(&pp->recvbuf),
- Curl_dyn_len(&pp->recvbuf), FALSE);
+ result = pop3_write(data, curlx_dyn_ptr(&pp->recvbuf),
+ curlx_dyn_len(&pp->recvbuf), FALSE);
if(result)
return result;
}
/* reset the buffer */
- Curl_dyn_reset(&pp->recvbuf);
+ curlx_dyn_reset(&pp->recvbuf);
pp->overflow = 0;
}
}
@@ -1029,16 +1152,24 @@ static CURLcode pop3_state_command_resp(struct Curl_easy *data,
static CURLcode pop3_statemachine(struct Curl_easy *data,
struct connectdata *conn)
{
+ struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
CURLcode result = CURLE_OK;
int pop3code;
- struct pop3_conn *pop3c = &conn->proto.pop3c;
- struct pingpong *pp = &pop3c->pp;
+ struct pingpong *pp;
size_t nread = 0;
(void)data;
+ if(!pop3c)
+ return CURLE_FAILED_INIT;
+
+ pp = &pop3c->pp;
/* Busy upgrading the connection; right now all I/O is SSL/TLS, not POP3 */
- if(pop3c->state == POP3_UPGRADETLS)
- return pop3_perform_upgrade_tls(data, conn);
+upgrade_tls:
+ if(pop3c->state == POP3_UPGRADETLS) {
+ result = pop3_perform_upgrade_tls(data, conn);
+ if(result || (pop3c->state == POP3_UPGRADETLS))
+ return result;
+ }
/* Flush any data that needs to be sent */
if(pp->sendleft)
@@ -1065,6 +1196,10 @@ static CURLcode pop3_statemachine(struct Curl_easy *data,
case POP3_STARTTLS:
result = pop3_state_starttls_resp(data, conn, pop3code, pop3c->state);
+ /* During UPGRADETLS, leave the read loop as we need to connect
+ * (e.g. TLS handshake) before we continue sending/receiving. */
+ if(!result && (pop3c->state == POP3_UPGRADETLS))
+ goto upgrade_tls;
break;
case POP3_AUTH:
@@ -1108,16 +1243,10 @@ static CURLcode pop3_multi_statemach(struct Curl_easy *data, bool *done)
{
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
- struct pop3_conn *pop3c = &conn->proto.pop3c;
-
- if((conn->handler->flags & PROTOPT_SSL) && !pop3c->ssldone) {
- bool ssldone = FALSE;
- result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
- pop3c->ssldone = ssldone;
- if(result || !pop3c->ssldone)
- return result;
- }
+ struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
+ if(!pop3c)
+ return CURLE_FAILED_INIT;
result = Curl_pp_statemach(data, &pop3c->pp, FALSE, FALSE);
*done = (pop3c->state == POP3_STOP);
@@ -1129,7 +1258,10 @@ static CURLcode pop3_block_statemach(struct Curl_easy *data,
bool disconnecting)
{
CURLcode result = CURLE_OK;
- struct pop3_conn *pop3c = &conn->proto.pop3c;
+ struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
+
+ if(!pop3c)
+ return CURLE_FAILED_INIT;
while(pop3c->state != POP3_STOP && !result)
result = Curl_pp_statemach(data, &pop3c->pp, TRUE, disconnecting);
@@ -1137,25 +1269,14 @@ static CURLcode pop3_block_statemach(struct Curl_easy *data,
return result;
}
-/* Allocate and initialize the POP3 struct for the current Curl_easy if
- required */
-static CURLcode pop3_init(struct Curl_easy *data)
-{
- CURLcode result = CURLE_OK;
- struct POP3 *pop3;
-
- pop3 = data->req.p.pop3 = calloc(1, sizeof(struct POP3));
- if(!pop3)
- result = CURLE_OUT_OF_MEMORY;
-
- return result;
-}
-
/* For the POP3 "protocol connect" and "doing" phases only */
static int pop3_getsock(struct Curl_easy *data,
struct connectdata *conn, curl_socket_t *socks)
{
- return Curl_pp_getsock(data, &conn->proto.pop3c.pp, socks);
+ struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
+ if(pop3c)
+ return Curl_pp_getsock(data, &pop3c->pp, socks);
+ return GETSOCK_BLANK;
}
/***********************************************************************
@@ -1172,10 +1293,12 @@ static CURLcode pop3_connect(struct Curl_easy *data, bool *done)
{
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
- struct pop3_conn *pop3c = &conn->proto.pop3c;
- struct pingpong *pp = &pop3c->pp;
+ struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
+ struct pingpong *pp = pop3c ? &pop3c->pp : NULL;
*done = FALSE; /* default to not done yet */
+ if(!pop3c)
+ return CURLE_FAILED_INIT;
/* We always support persistent connections in POP3 */
connkeep(conn, "POP3 default");
@@ -1215,7 +1338,7 @@ static CURLcode pop3_done(struct Curl_easy *data, CURLcode status,
bool premature)
{
CURLcode result = CURLE_OK;
- struct POP3 *pop3 = data->req.p.pop3;
+ struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY);
(void)premature;
@@ -1249,7 +1372,10 @@ static CURLcode pop3_perform(struct Curl_easy *data, bool *connected,
{
/* This is POP3 and no proxy */
CURLcode result = CURLE_OK;
- struct POP3 *pop3 = data->req.p.pop3;
+ struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY);
+
+ if(!pop3)
+ return CURLE_FAILED_INIT;
DEBUGF(infof(data, "DO phase starts"));
@@ -1314,9 +1440,12 @@ static CURLcode pop3_do(struct Curl_easy *data, bool *done)
static CURLcode pop3_disconnect(struct Curl_easy *data,
struct connectdata *conn, bool dead_connection)
{
- struct pop3_conn *pop3c = &conn->proto.pop3c;
+ struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
(void)data;
+ if(!pop3c)
+ return CURLE_FAILED_INIT;
+
/* We cannot send quit unconditionally. If this connection is stale or
bad in any way, sending quit and waiting around here will make the
disconnect wait in vain and cause more problems than we need to. */
@@ -1397,16 +1526,42 @@ static CURLcode pop3_regular_transfer(struct Curl_easy *data,
return result;
}
+static void pop3_easy_dtor(void *key, size_t klen, void *entry)
+{
+ struct POP3 *pop3 = entry;
+ (void)key;
+ (void)klen;
+ DEBUGASSERT(pop3);
+ /* Cleanup our per-request based variables */
+ Curl_safefree(pop3->id);
+ Curl_safefree(pop3->custom);
+ free(pop3);
+}
+
+static void pop3_conn_dtor(void *key, size_t klen, void *entry)
+{
+ struct pop3_conn *pop3c = entry;
+ (void)key;
+ (void)klen;
+ DEBUGASSERT(pop3c);
+ Curl_pp_disconnect(&pop3c->pp);
+ Curl_safefree(pop3c->apoptimestamp);
+ free(pop3c);
+}
+
static CURLcode pop3_setup_connection(struct Curl_easy *data,
struct connectdata *conn)
{
- /* Initialise the POP3 layer */
- CURLcode result = pop3_init(data);
- if(result)
- return result;
+ struct pop3_conn *pop3c;
+ struct POP3 *pop3 = calloc(1, sizeof(*pop3));
+ if(!pop3 ||
+ Curl_meta_set(data, CURL_META_POP3_EASY, pop3, pop3_easy_dtor))
+ return CURLE_OUT_OF_MEMORY;
- /* Clear the TLS upgraded flag */
- conn->bits.tls_upgraded = FALSE;
+ pop3c = calloc(1, sizeof(*pop3c));
+ if(!pop3c ||
+ Curl_conn_meta_set(conn, CURL_META_POP3_CONN, pop3c, pop3_conn_dtor))
+ return CURLE_OUT_OF_MEMORY;
return CURLE_OK;
}
@@ -1419,10 +1574,13 @@ static CURLcode pop3_setup_connection(struct Curl_easy *data,
*/
static CURLcode pop3_parse_url_options(struct connectdata *conn)
{
+ struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
CURLcode result = CURLE_OK;
- struct pop3_conn *pop3c = &conn->proto.pop3c;
const char *ptr = conn->options;
+ if(!pop3c)
+ return CURLE_FAILED_INIT;
+
while(!result && ptr && *ptr) {
const char *key = ptr;
const char *value;
@@ -1477,9 +1635,11 @@ static CURLcode pop3_parse_url_options(struct connectdata *conn)
static CURLcode pop3_parse_url_path(struct Curl_easy *data)
{
/* The POP3 struct is already initialised in pop3_connect() */
- struct POP3 *pop3 = data->req.p.pop3;
+ struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY);
const char *path = &data->state.up.path[1]; /* skip leading path */
+ if(!pop3)
+ return CURLE_FAILED_INIT;
/* URL decode the path for the message ID */
return Curl_urldecode(path, 0, &pop3->id, NULL, REJECT_CTRL);
}
@@ -1493,9 +1653,11 @@ static CURLcode pop3_parse_url_path(struct Curl_easy *data)
static CURLcode pop3_parse_custom_request(struct Curl_easy *data)
{
CURLcode result = CURLE_OK;
- struct POP3 *pop3 = data->req.p.pop3;
+ struct POP3 *pop3 = Curl_meta_get(data, CURL_META_POP3_EASY);
const char *custom = data->set.str[STRING_CUSTOMREQUEST];
+ if(!pop3)
+ return CURLE_FAILED_INIT;
/* URL decode the custom request */
if(custom)
result = Curl_urldecode(custom, 0, &pop3->custom, NULL, REJECT_CTRL);
@@ -1517,12 +1679,15 @@ static CURLcode pop3_write(struct Curl_easy *data, const char *str,
CURLcode result = CURLE_OK;
struct SingleRequest *k = &data->req;
struct connectdata *conn = data->conn;
- struct pop3_conn *pop3c = &conn->proto.pop3c;
+ struct pop3_conn *pop3c = Curl_conn_meta_get(conn, CURL_META_POP3_CONN);
bool strip_dot = FALSE;
size_t last = 0;
size_t i;
(void)is_eos;
+ if(!pop3c)
+ return CURLE_FAILED_INIT;
+
/* Search through the buffer looking for the end-of-body marker which is
5 bytes (0d 0a 2e 0d 0a). Note that a line starting with a dot matches
the eob so the server will have prefixed it with an extra dot which we
@@ -1597,11 +1762,11 @@ static CURLcode pop3_write(struct Curl_easy *data, const char *str,
/* If the partial match was the CRLF and dot then only write the CRLF
as the server would have inserted the dot */
if(strip_dot && prev - 1 > 0) {
- result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)POP3_EOB,
+ result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB,
prev - 1);
}
else if(!strip_dot) {
- result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)POP3_EOB,
+ result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB,
prev);
}
else {
@@ -1621,7 +1786,7 @@ static CURLcode pop3_write(struct Curl_easy *data, const char *str,
/* We have a full match so the transfer is done, however we must transfer
the CRLF at the start of the EOB as this is considered to be part of the
message as per RFC-1939, sect. 3 */
- result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)POP3_EOB, 2);
+ result = Curl_client_write(data, CLIENTWRITE_BODY, POP3_EOB, 2);
k->keepon &= ~KEEP_RECV;
pop3c->eob = 0;
diff --git a/libs/libcurl/src/pop3.h b/libs/libcurl/src/pop3.h
index ce0d5f6dc2..be57486d48 100644
--- a/libs/libcurl/src/pop3.h
+++ b/libs/libcurl/src/pop3.h
@@ -24,70 +24,7 @@
*
***************************************************************************/
-#include "pingpong.h"
-#include "curl_sasl.h"
-
-/****************************************************************************
- * POP3 unique setup
- ***************************************************************************/
-typedef enum {
- POP3_STOP, /* do nothing state, stops the state machine */
- POP3_SERVERGREET, /* waiting for the initial greeting immediately after
- a connect */
- POP3_CAPA,
- POP3_STARTTLS,
- POP3_UPGRADETLS, /* asynchronously upgrade the connection to SSL/TLS
- (multi mode only) */
- POP3_AUTH,
- POP3_APOP,
- POP3_USER,
- POP3_PASS,
- POP3_COMMAND,
- POP3_QUIT,
- POP3_LAST /* never used */
-} pop3state;
-
-/* This POP3 struct is used in the Curl_easy. All POP3 data that is
- connection-oriented must be in pop3_conn to properly deal with the fact that
- perhaps the Curl_easy is changed between the times the connection is
- used. */
-struct POP3 {
- curl_pp_transfer transfer;
- char *id; /* Message ID */
- char *custom; /* Custom Request */
-};
-
-/* pop3_conn is used for struct connection-oriented data in the connectdata
- struct */
-struct pop3_conn {
- struct pingpong pp;
- pop3state state; /* Always use pop3.c:state() to change state! */
- size_t eob; /* Number of bytes of the EOB (End Of Body) that
- have been received so far */
- size_t strip; /* Number of bytes from the start to ignore as
- non-body */
- struct SASL sasl; /* SASL-related storage */
- char *apoptimestamp; /* APOP timestamp from the server greeting */
- unsigned char authtypes; /* Accepted authentication types */
- unsigned char preftype; /* Preferred authentication type */
- BIT(ssldone); /* Is connect() over SSL done? */
- BIT(tls_supported); /* StartTLS capability supported by server */
-};
-
extern const struct Curl_handler Curl_handler_pop3;
extern const struct Curl_handler Curl_handler_pop3s;
-/* Authentication type flags */
-#define POP3_TYPE_CLEARTEXT (1 << 0)
-#define POP3_TYPE_APOP (1 << 1)
-#define POP3_TYPE_SASL (1 << 2)
-
-/* Authentication type values */
-#define POP3_TYPE_NONE 0
-#define POP3_TYPE_ANY (POP3_TYPE_CLEARTEXT|POP3_TYPE_APOP|POP3_TYPE_SASL)
-
-/* This is the 5-bytes End-Of-Body marker for POP3 */
-#define POP3_EOB "\x0d\x0a\x2e\x0d\x0a"
-#define POP3_EOB_LEN 5
-
#endif /* HEADER_CURL_POP3_H */
diff --git a/libs/libcurl/src/progress.c b/libs/libcurl/src/progress.c
index dd0cfc1d00..b8887786d8 100644
--- a/libs/libcurl/src/progress.c
+++ b/libs/libcurl/src/progress.c
@@ -28,7 +28,7 @@
#include "sendf.h"
#include "multiif.h"
#include "progress.h"
-#include "timeval.h"
+#include "curlx/timeval.h"
#include "curl_printf.h"
/* check rate limits within this many recent milliseconds, at minimum. */
@@ -136,8 +136,7 @@ int Curl_pgrsDone(struct Curl_easy *data)
if(rc)
return rc;
- if(!(data->progress.flags & PGRS_HIDE) &&
- !data->progress.callback)
+ if(!data->progress.hide && !data->progress.callback)
/* only output if we do not use a progress callback and we are not
* hidden */
fprintf(data->set.err, "\n");
@@ -170,6 +169,8 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer,
case TIMER_STARTOP:
/* This is set at the start of a transfer */
data->progress.t_startop = timestamp;
+ data->progress.t_startqueue = timestamp;
+ data->progress.t_postqueue = 0;
break;
case TIMER_STARTSINGLE:
/* This is set at the start of each single transfer */
@@ -177,12 +178,9 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer,
data->progress.is_t_startransfer_set = FALSE;
break;
case TIMER_POSTQUEUE:
- /* Set when the transfer starts (after potentially having been brought
- back from the waiting queue). It needs to count from t_startop and not
- t_startsingle since the latter is reset when a connection is brought
- back from the pending queue. */
- data->progress.t_postqueue =
- Curl_timediff_us(timestamp, data->progress.t_startop);
+ /* Queue time is accumulative from all involved redirects */
+ data->progress.t_postqueue +=
+ curlx_timediff_us(timestamp, data->progress.t_startqueue);
break;
case TIMER_STARTACCEPT:
data->progress.t_acceptdata = timestamp;
@@ -218,12 +216,13 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer,
delta = &data->progress.t_posttransfer;
break;
case TIMER_REDIRECT:
- data->progress.t_redirect = Curl_timediff_us(timestamp,
+ data->progress.t_redirect = curlx_timediff_us(timestamp,
data->progress.start);
+ data->progress.t_startqueue = timestamp;
break;
}
if(delta) {
- timediff_t us = Curl_timediff_us(timestamp, data->progress.t_startsingle);
+ timediff_t us = curlx_timediff_us(timestamp, data->progress.t_startsingle);
if(us < 1)
us = 1; /* make sure at least one microsecond passed */
*delta += us;
@@ -239,7 +238,7 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer,
*/
struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer)
{
- struct curltime now = Curl_now();
+ struct curltime now = curlx_now();
Curl_pgrsTimeWas(data, timer, now);
return now;
@@ -247,18 +246,20 @@ struct curltime Curl_pgrsTime(struct Curl_easy *data, timerid timer)
void Curl_pgrsStartNow(struct Curl_easy *data)
{
- data->progress.speeder_c = 0; /* reset the progress meter display */
- data->progress.start = Curl_now();
- data->progress.is_t_startransfer_set = FALSE;
- data->progress.ul.limit.start = data->progress.start;
- data->progress.dl.limit.start = data->progress.start;
- data->progress.ul.limit.start_size = 0;
- data->progress.dl.limit.start_size = 0;
- data->progress.dl.cur_size = 0;
- data->progress.ul.cur_size = 0;
- /* clear all bits except HIDE and HEADERS_OUT */
- data->progress.flags &= PGRS_HIDE|PGRS_HEADERS_OUT;
- Curl_ratelimit(data, data->progress.start);
+ struct Progress *p = &data->progress;
+ p->speeder_c = 0; /* reset the progress meter display */
+ p->start = curlx_now();
+ p->is_t_startransfer_set = FALSE;
+ p->ul.limit.start = p->start;
+ p->dl.limit.start = p->start;
+ p->ul.limit.start_size = 0;
+ p->dl.limit.start_size = 0;
+ p->dl.cur_size = 0;
+ p->ul.cur_size = 0;
+ /* the sizes are unknown at start */
+ p->dl_size_known = FALSE;
+ p->ul_size_known = FALSE;
+ Curl_ratelimit(data, p->start);
}
/*
@@ -308,11 +309,11 @@ timediff_t Curl_pgrsLimitWaitTime(struct pgrs_dir *d,
* 'actual' is the time in milliseconds it took to actually download the
* last 'size' bytes.
*/
- actual = Curl_timediff_ceil(now, d->limit.start);
+ actual = curlx_timediff_ceil(now, d->limit.start);
if(actual < minimum) {
/* if it downloaded the data faster than the limit, make it wait the
difference */
- return (minimum - actual);
+ return minimum - actual;
}
return 0;
@@ -334,14 +335,14 @@ void Curl_ratelimit(struct Curl_easy *data, struct curltime now)
{
/* do not set a new stamp unless the time since last update is long enough */
if(data->set.max_recv_speed) {
- if(Curl_timediff(now, data->progress.dl.limit.start) >=
+ if(curlx_timediff(now, data->progress.dl.limit.start) >=
MIN_RATE_LIMIT_PERIOD) {
data->progress.dl.limit.start = now;
data->progress.dl.limit.start_size = data->progress.dl.cur_size;
}
}
if(data->set.max_send_speed) {
- if(Curl_timediff(now, data->progress.ul.limit.start) >=
+ if(curlx_timediff(now, data->progress.ul.limit.start) >=
MIN_RATE_LIMIT_PERIOD) {
data->progress.ul.limit.start = now;
data->progress.ul.limit.start_size = data->progress.ul.cur_size;
@@ -361,11 +362,11 @@ void Curl_pgrsSetDownloadSize(struct Curl_easy *data, curl_off_t size)
{
if(size >= 0) {
data->progress.dl.total_size = size;
- data->progress.flags |= PGRS_DL_SIZE_KNOWN;
+ data->progress.dl_size_known = TRUE;
}
else {
data->progress.dl.total_size = 0;
- data->progress.flags &= ~PGRS_DL_SIZE_KNOWN;
+ data->progress.dl_size_known = FALSE;
}
}
@@ -373,11 +374,11 @@ void Curl_pgrsSetUploadSize(struct Curl_easy *data, curl_off_t size)
{
if(size >= 0) {
data->progress.ul.total_size = size;
- data->progress.flags |= PGRS_UL_SIZE_KNOWN;
+ data->progress.ul_size_known = TRUE;
}
else {
data->progress.ul.total_size = 0;
- data->progress.flags &= ~PGRS_UL_SIZE_KNOWN;
+ data->progress.ul_size_known = FALSE;
}
}
@@ -407,7 +408,7 @@ static bool progress_calc(struct Curl_easy *data, struct curltime now)
struct Progress * const p = &data->progress;
/* The time spent so far (from the start) in microseconds */
- p->timespent = Curl_timediff_us(now, p->start);
+ p->timespent = curlx_timediff_us(now, p->start);
p->dl.speed = trspeed(p->dl.cur_size, p->timespent);
p->ul.speed = trspeed(p->ul.cur_size, p->timespent);
@@ -447,7 +448,7 @@ static bool progress_calc(struct Curl_easy *data, struct curltime now)
checkindex = (p->speeder_c >= CURR_TIME) ? p->speeder_c%CURR_TIME : 0;
/* Figure out the exact time for the time span */
- span_ms = Curl_timediff(now, p->speeder_time[checkindex]);
+ span_ms = curlx_timediff(now, p->speeder_time[checkindex]);
if(0 == span_ms)
span_ms = 1; /* at least one millisecond MUST have passed */
@@ -509,12 +510,13 @@ static void progress_meter(struct Curl_easy *data)
struct pgrs_estimate total_estm;
curl_off_t total_cur_size;
curl_off_t total_expected_size;
+ curl_off_t dl_size;
char time_left[10];
char time_total[10];
char time_spent[10];
curl_off_t cur_secs = (curl_off_t)p->timespent/1000000; /* seconds */
- if(!(p->flags & PGRS_HEADERS_OUT)) {
+ if(!p->headers_out) {
if(data->state.resume_from) {
fprintf(data->set.err,
"** Resuming transfer from byte position %" FMT_OFF_T "\n",
@@ -525,12 +527,12 @@ static void progress_meter(struct Curl_easy *data)
"Time Time Time Current\n"
" Dload Upload "
"Total Spent Left Speed\n");
- p->flags |= PGRS_HEADERS_OUT; /* headers are shown */
+ p->headers_out = TRUE; /* headers are shown */
}
/* Figure out the estimated time of arrival for upload and download */
- pgrs_estimates(&p->ul, (p->flags & PGRS_UL_SIZE_KNOWN), &ul_estm);
- pgrs_estimates(&p->dl, (p->flags & PGRS_DL_SIZE_KNOWN), &dl_estm);
+ pgrs_estimates(&p->ul, (bool)p->ul_size_known, &ul_estm);
+ pgrs_estimates(&p->dl, (bool)p->dl_size_known, &dl_estm);
/* Since both happen at the same time, total expected duration is max. */
total_estm.secs = CURLMAX(ul_estm.secs, dl_estm.secs);
@@ -541,8 +543,16 @@ static void progress_meter(struct Curl_easy *data)
/* Get the total amount of data expected to get transferred */
total_expected_size =
- ((p->flags & PGRS_UL_SIZE_KNOWN) ? p->ul.total_size : p->ul.cur_size) +
- ((p->flags & PGRS_DL_SIZE_KNOWN) ? p->dl.total_size : p->dl.cur_size);
+ p->ul_size_known ? p->ul.total_size : p->ul.cur_size;
+
+ dl_size =
+ p->dl_size_known ? p->dl.total_size : p->dl.cur_size;
+
+ /* integer overflow check */
+ if((CURL_OFF_T_MAX - total_expected_size) < dl_size)
+ total_expected_size = CURL_OFF_T_MAX; /* capped */
+ else
+ total_expected_size += dl_size;
/* We have transferred this much so far */
total_cur_size = p->dl.cur_size + p->ul.cur_size;
@@ -584,7 +594,7 @@ static void progress_meter(struct Curl_easy *data)
*/
static int pgrsupdate(struct Curl_easy *data, bool showprogress)
{
- if(!(data->progress.flags & PGRS_HIDE)) {
+ if(!data->progress.hide) {
if(data->set.fxferinfo) {
int result;
/* There is a callback set, call that */
@@ -627,7 +637,7 @@ static int pgrsupdate(struct Curl_easy *data, bool showprogress)
int Curl_pgrsUpdate(struct Curl_easy *data)
{
- struct curltime now = Curl_now(); /* what time is it */
+ struct curltime now = curlx_now(); /* what time is it */
bool showprogress = progress_calc(data, now);
return pgrsupdate(data, showprogress);
}
@@ -637,6 +647,6 @@ int Curl_pgrsUpdate(struct Curl_easy *data)
*/
void Curl_pgrsUpdate_nometer(struct Curl_easy *data)
{
- struct curltime now = Curl_now(); /* what time is it */
+ struct curltime now = curlx_now(); /* what time is it */
(void)progress_calc(data, now);
}
diff --git a/libs/libcurl/src/progress.h b/libs/libcurl/src/progress.h
index 5386e1bd3f..baad4ee14c 100644
--- a/libs/libcurl/src/progress.h
+++ b/libs/libcurl/src/progress.h
@@ -24,7 +24,7 @@
*
***************************************************************************/
-#include "timeval.h"
+#include "curlx/timeval.h"
typedef enum {
@@ -71,9 +71,4 @@ void Curl_pgrsTimeWas(struct Curl_easy *data, timerid timer,
void Curl_pgrsEarlyData(struct Curl_easy *data, curl_off_t sent);
-#define PGRS_HIDE (1<<4)
-#define PGRS_UL_SIZE_KNOWN (1<<5)
-#define PGRS_DL_SIZE_KNOWN (1<<6)
-#define PGRS_HEADERS_OUT (1<<7) /* set when the headers have been written */
-
#endif /* HEADER_CURL_PROGRESS_H */
diff --git a/libs/libcurl/src/psl.c b/libs/libcurl/src/psl.c
index 4e9ce22f3f..22703ff234 100644
--- a/libs/libcurl/src/psl.c
+++ b/libs/libcurl/src/psl.c
@@ -40,7 +40,7 @@ void Curl_psl_destroy(struct PslCache *pslcache)
{
if(pslcache->psl) {
if(pslcache->dynamic)
- psl_free((psl_ctx_t *) pslcache->psl);
+ psl_free((psl_ctx_t *)CURL_UNCONST(pslcache->psl));
pslcache->psl = NULL;
pslcache->dynamic = FALSE;
}
@@ -48,7 +48,7 @@ void Curl_psl_destroy(struct PslCache *pslcache)
static time_t now_seconds(void)
{
- struct curltime now = Curl_now();
+ struct curltime now = curlx_now();
return now.tv_sec;
}
diff --git a/libs/libcurl/src/psl.h b/libs/libcurl/src/psl.h
index cce64ea740..9aa2ea2a04 100644
--- a/libs/libcurl/src/psl.h
+++ b/libs/libcurl/src/psl.h
@@ -34,7 +34,7 @@ struct Curl_easy;
struct PslCache {
const psl_ctx_t *psl; /* The PSL. */
time_t expires; /* Time this PSL life expires. */
- bool dynamic; /* PSL should be released when no longer needed. */
+ BIT(dynamic); /* PSL should be released when no longer needed. */
};
const psl_ctx_t *Curl_psl_use(struct Curl_easy *easy);
diff --git a/libs/libcurl/src/rand.c b/libs/libcurl/src/rand.c
index 00ba581689..be106e6ad8 100644
--- a/libs/libcurl/src/rand.c
+++ b/libs/libcurl/src/rand.c
@@ -37,7 +37,7 @@
#include "urldata.h"
#include "vtls/vtls.h"
#include "sendf.h"
-#include "timeval.h"
+#include "curlx/timeval.h"
#include "rand.h"
#include "escape.h"
@@ -130,7 +130,7 @@ static CURLcode weak_random(struct Curl_easy *data,
static bool seeded = FALSE;
unsigned int rnd;
if(!seeded) {
- struct curltime now = Curl_now();
+ struct curltime now = curlx_now();
randseed += (unsigned int)now.tv_usec + (unsigned int)now.tv_sec;
randseed = randseed * 1103515245 + 12345;
randseed = randseed * 1103515245 + 12345;
@@ -191,12 +191,11 @@ static CURLcode randit(struct Curl_easy *data, unsigned int *rnd,
* Curl_rand() stores 'num' number of random unsigned characters in the buffer
* 'rnd' points to.
*
- * If libcurl is built without TLS support or with a TLS backend that lacks a
- * proper random API (Rustls or mbedTLS), this function will use "weak"
- * random.
+ * If libcurl is built without TLS support or arc4random, this function will
+ * use "weak" random.
*
- * When built *with* TLS support and a backend that offers strong random, it
- * will return error if it cannot provide strong random values.
+ * When built *with* TLS support, it will return error if it cannot provide
+ * strong random values.
*
* NOTE: 'data' may be passed in as NULL when coming from external API without
* easy handle!
diff --git a/libs/libcurl/src/rename.c b/libs/libcurl/src/rename.c
index a35f6ce76e..f4f5ac13b0 100644
--- a/libs/libcurl/src/rename.c
+++ b/libs/libcurl/src/rename.c
@@ -29,8 +29,8 @@
#if (!defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_COOKIES)) || \
!defined(CURL_DISABLE_ALTSVC)
-#include "curl_multibyte.h"
-#include "timeval.h"
+#include "curlx/multibyte.h"
+#include "curlx/timeval.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -40,14 +40,14 @@
/* return 0 on success, 1 on error */
int Curl_rename(const char *oldpath, const char *newpath)
{
-#ifdef _WIN32
+#if defined(_WIN32) && !defined(UNDER_CE)
/* rename() on Windows does not overwrite, so we cannot use it here.
MoveFileEx() will overwrite and is usually atomic, however it fails
when there are open handles to the file. */
const int max_wait_ms = 1000;
- struct curltime start = Curl_now();
- TCHAR *tchar_oldpath = curlx_convert_UTF8_to_tchar((char *)oldpath);
- TCHAR *tchar_newpath = curlx_convert_UTF8_to_tchar((char *)newpath);
+ struct curltime start = curlx_now();
+ TCHAR *tchar_oldpath = curlx_convert_UTF8_to_tchar(oldpath);
+ TCHAR *tchar_newpath = curlx_convert_UTF8_to_tchar(newpath);
for(;;) {
timediff_t diff;
if(MoveFileEx(tchar_oldpath, tchar_newpath, MOVEFILE_REPLACE_EXISTING)) {
@@ -55,7 +55,7 @@ int Curl_rename(const char *oldpath, const char *newpath)
curlx_unicodefree(tchar_newpath);
break;
}
- diff = Curl_timediff(Curl_now(), start);
+ diff = curlx_timediff(curlx_now(), start);
if(diff < 0 || diff > max_wait_ms) {
curlx_unicodefree(tchar_oldpath);
curlx_unicodefree(tchar_newpath);
diff --git a/libs/libcurl/src/request.c b/libs/libcurl/src/request.c
index 66130d3f1d..9b11fa2aec 100644
--- a/libs/libcurl/src/request.c
+++ b/libs/libcurl/src/request.c
@@ -26,7 +26,7 @@
#include "urldata.h"
#include "cfilters.h"
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
#include "doh.h"
#include "multiif.h"
#include "progress.h"
@@ -34,6 +34,7 @@
#include "sendf.h"
#include "transfer.h"
#include "url.h"
+#include "curlx/strparse.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -66,6 +67,9 @@ CURLcode Curl_req_soft_reset(struct SingleRequest *req,
req->headerbytecount = 0;
req->allheadercount = 0;
req->deductheadercount = 0;
+ req->httpversion_sent = 0;
+ req->httpversion = 0;
+ req->sendbuf_hds_len = 0;
result = Curl_client_start(data);
if(result)
@@ -91,7 +95,7 @@ CURLcode Curl_req_soft_reset(struct SingleRequest *req,
CURLcode Curl_req_start(struct SingleRequest *req,
struct Curl_easy *data)
{
- req->start = Curl_now();
+ req->start = curlx_now();
return Curl_req_soft_reset(req, data);
}
@@ -114,9 +118,6 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data)
{
struct curltime t0 = {0, 0};
- /* This is a bit ugly. `req->p` is a union and we assume we can
- * free this safely without leaks. */
- Curl_safefree(req->p.ftp);
Curl_safefree(req->newurl);
Curl_client_reset(data);
if(req->sendbuf_init)
@@ -139,6 +140,7 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data)
req->httpcode = 0;
req->keepon = 0;
req->upgr101 = UPGR101_INIT;
+ req->sendbuf_hds_len = 0;
req->timeofdoc = 0;
req->location = NULL;
req->newurl = NULL;
@@ -162,24 +164,14 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data)
req->no_body = data->set.opt_no_body;
req->authneg = FALSE;
req->shutdown = FALSE;
-#ifdef USE_HYPER
- req->bodywritten = FALSE;
-#endif
}
void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data)
{
- /* This is a bit ugly. `req->p` is a union and we assume we can
- * free this safely without leaks. */
- Curl_safefree(req->p.ftp);
Curl_safefree(req->newurl);
if(req->sendbuf_init)
Curl_bufq_free(&req->sendbuf);
Curl_client_cleanup(data);
-
-#ifndef CURL_DISABLE_DOH
- Curl_doh_cleanup(data);
-#endif
}
static CURLcode xfer_send(struct Curl_easy *data,
@@ -196,11 +188,13 @@ static CURLcode xfer_send(struct Curl_easy *data,
/* Allow debug builds to override this logic to force short initial
sends */
size_t body_len = blen - hds_len;
- char *p = getenv("CURL_SMALLREQSEND");
- if(p) {
- size_t body_small = (size_t)strtoul(p, NULL, 10);
- if(body_small && body_small < body_len)
- blen = hds_len + body_small;
+ if(body_len) {
+ const char *p = getenv("CURL_SMALLREQSEND");
+ if(p) {
+ curl_off_t body_small;
+ if(!curlx_str_number(&p, &body_small, body_len))
+ blen = hds_len + (size_t)body_small;
+ }
}
}
#endif
@@ -224,11 +218,11 @@ static CURLcode xfer_send(struct Curl_easy *data,
data->req.eos_sent = TRUE;
if(*pnwritten) {
if(hds_len)
- Curl_debug(data, CURLINFO_HEADER_OUT, (char *)buf,
+ Curl_debug(data, CURLINFO_HEADER_OUT, buf,
CURLMIN(hds_len, *pnwritten));
if(*pnwritten > hds_len) {
size_t body_len = *pnwritten - hds_len;
- Curl_debug(data, CURLINFO_DATA_OUT, (char *)buf + hds_len, body_len);
+ Curl_debug(data, CURLINFO_DATA_OUT, buf + hds_len, body_len);
data->req.writebytecount += body_len;
Curl_pgrsSetUploadCounter(data, data->req.writebytecount);
}
@@ -261,7 +255,7 @@ static CURLcode req_send_buffer_flush(struct Curl_easy *data)
return result;
}
-CURLcode Curl_req_set_upload_done(struct Curl_easy *data)
+static CURLcode req_set_upload_done(struct Curl_easy *data)
{
DEBUGASSERT(!data->req.upload_done);
data->req.upload_done = TRUE;
@@ -339,7 +333,7 @@ static CURLcode req_flush(struct Curl_easy *data)
if(!done)
return CURLE_AGAIN;
}
- return Curl_req_set_upload_done(data);
+ return req_set_upload_done(data);
}
return CURLE_OK;
}
@@ -360,8 +354,6 @@ static ssize_t add_from_client(void *reader_ctx,
return (ssize_t)nread;
}
-#ifndef USE_HYPER
-
static CURLcode req_send_buffer_add(struct Curl_easy *data,
const char *buf, size_t blen,
size_t hds_len)
@@ -378,7 +370,8 @@ static CURLcode req_send_buffer_add(struct Curl_easy *data,
return CURLE_OK;
}
-CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req)
+CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req,
+ unsigned char httpversion)
{
CURLcode result;
const char *buf;
@@ -387,8 +380,9 @@ CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req)
if(!data || !data->conn)
return CURLE_FAILED_INIT;
- buf = Curl_dyn_ptr(req);
- blen = Curl_dyn_len(req);
+ data->req.httpversion_sent = httpversion;
+ buf = curlx_dyn_ptr(req);
+ blen = curlx_dyn_len(req);
if(!Curl_creader_total_length(data)) {
/* Request without body. Try to send directly from the buf given. */
data->req.eos_read = TRUE;
@@ -411,7 +405,6 @@ CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req)
}
return CURLE_OK;
}
-#endif /* !USE_HYPER */
bool Curl_req_sendbuf_empty(struct Curl_easy *data)
{
@@ -464,7 +457,7 @@ CURLcode Curl_req_abort_sending(struct Curl_easy *data)
data->req.upload_aborted = TRUE;
/* no longer KEEP_SEND and KEEP_SEND_PAUSE */
data->req.keepon &= ~KEEP_SENDBITS;
- return Curl_req_set_upload_done(data);
+ return req_set_upload_done(data);
}
return CURLE_OK;
}
diff --git a/libs/libcurl/src/request.h b/libs/libcurl/src/request.h
index 12dade3754..8e56f2d246 100644
--- a/libs/libcurl/src/request.h
+++ b/libs/libcurl/src/request.h
@@ -32,9 +32,6 @@
/* forward declarations */
struct UserDefined;
-#ifndef CURL_DISABLE_DOH
-struct doh_probes;
-#endif
enum expect100 {
EXP100_SEND_DATA, /* enough waiting, just send the body now */
@@ -81,10 +78,11 @@ struct SingleRequest {
first one */
curl_off_t offset; /* possible resume offset read from the
Content-Range: header */
- int httpversion; /* Version in response (09, 10, 11, etc.) */
int httpcode; /* error code from the 'HTTP/1.? XXX' or
'RTSP/1.? XXX' line */
int keepon;
+ unsigned char httpversion_sent; /* Version in request (09, 10, 11, etc.) */
+ unsigned char httpversion; /* Version in response (09, 10, 11, etc.) */
enum upgrade101 upgr101; /* 101 upgrade state */
/* Client Writer stack, handles transfer- and content-encodings, protocol
@@ -101,24 +99,6 @@ struct SingleRequest {
char *newurl; /* Set to the new URL to use when a redirect or a retry is
wanted */
- /* Allocated protocol-specific data. Each protocol handler makes sure this
- points to data it needs. */
- union {
- struct FILEPROTO *file;
- struct FTP *ftp;
- struct IMAP *imap;
- struct ldapreqinfo *ldap;
- struct MQTT *mqtt;
- struct POP3 *pop3;
- struct RTSP *rtsp;
- struct smb_request *smb;
- struct SMTP *smtp;
- struct SSHPROTO *ssh;
- struct TELNET *telnet;
- } p;
-#ifndef CURL_DISABLE_DOH
- struct doh_probes *doh; /* DoH specific data for this request */
-#endif
#ifndef CURL_DISABLE_COOKIES
unsigned char setcookies;
#endif
@@ -152,9 +132,6 @@ struct SingleRequest {
BIT(sendbuf_init); /* sendbuf is initialized */
BIT(shutdown); /* request end will shutdown connection */
BIT(shutdown_err_ignore); /* errors in shutdown will not fail request */
-#ifdef USE_HYPER
- BIT(bodywritten);
-#endif
};
/**
@@ -196,18 +173,17 @@ void Curl_req_free(struct SingleRequest *req, struct Curl_easy *data);
*/
void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data);
-#ifndef USE_HYPER
/**
* Send request headers. If not all could be sent
* they will be buffered. Use `Curl_req_flush()` to make sure
* bytes are really send.
* @param data the transfer making the request
* @param buf the complete header bytes, no body
+ * @param httpversion version used in request (09, 10, 11, etc.)
* @return CURLE_OK (on blocking with *pnwritten == 0) or error.
*/
-CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *buf);
-
-#endif /* !USE_HYPER */
+CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *buf,
+ unsigned char httpversion);
/**
* TRUE iff the request has sent all request headers and data.
diff --git a/libs/libcurl/src/rtsp.c b/libs/libcurl/src/rtsp.c
index 0aff2d4bb8..9e3169accc 100644
--- a/libs/libcurl/src/rtsp.c
+++ b/libs/libcurl/src/rtsp.c
@@ -24,7 +24,7 @@
#include "curl_setup.h"
-#if !defined(CURL_DISABLE_RTSP) && !defined(USE_HYPER)
+#if !defined(CURL_DISABLE_RTSP)
#include "urldata.h"
#include <curl/curl.h>
@@ -40,11 +40,42 @@
#include "connect.h"
#include "cfilters.h"
#include "strdup.h"
+#include "curlx/strparse.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
#include "memdebug.h"
+
+/* meta key for storing protocol meta at easy handle */
+#define CURL_META_RTSP_EASY "meta:proto:rtsp:easy"
+/* meta key for storing protocol meta at connection */
+#define CURL_META_RTSP_CONN "meta:proto:rtsp:conn"
+
+typedef enum {
+ RTP_PARSE_SKIP,
+ RTP_PARSE_CHANNEL,
+ RTP_PARSE_LEN,
+ RTP_PARSE_DATA
+} rtp_parse_st;
+
+/* RTSP Connection data
+ * Currently, only used for tracking incomplete RTP data reads */
+struct rtsp_conn {
+ struct dynbuf buf;
+ int rtp_channel;
+ size_t rtp_len;
+ rtp_parse_st state;
+ BIT(in_header);
+};
+
+/* RTSP transfer data */
+struct RTSP {
+ long CSeq_sent; /* CSeq of this request */
+ long CSeq_recv; /* CSeq received */
+};
+
+
#define RTP_PKT_LENGTH(p) ((((unsigned int)((unsigned char)((p)[2]))) << 8) | \
((unsigned int)((unsigned char)((p)[3]))))
@@ -52,8 +83,6 @@
static CURLcode rtsp_do(struct Curl_easy *data, bool *done);
static CURLcode rtsp_done(struct Curl_easy *data, CURLcode, bool premature);
static CURLcode rtsp_connect(struct Curl_easy *data, bool *done);
-static CURLcode rtsp_disconnect(struct Curl_easy *data,
- struct connectdata *conn, bool dead);
static int rtsp_getsock_do(struct Curl_easy *data,
struct connectdata *conn, curl_socket_t *socks);
@@ -112,11 +141,12 @@ const struct Curl_handler Curl_handler_rtsp = {
rtsp_getsock_do, /* doing_getsock */
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
- rtsp_disconnect, /* disconnect */
+ ZERO_NULL, /* disconnect */
rtsp_rtp_write_resp, /* write_resp */
ZERO_NULL, /* write_resp_hd */
rtsp_conncheck, /* connection_check */
ZERO_NULL, /* attach connection */
+ Curl_http_follow, /* follow */
PORT_RTSP, /* defport */
CURLPROTO_RTSP, /* protocol */
CURLPROTO_RTSP, /* family */
@@ -125,17 +155,41 @@ const struct Curl_handler Curl_handler_rtsp = {
#define MAX_RTP_BUFFERSIZE 1000000 /* arbitrary */
+static void rtsp_easy_dtor(void *key, size_t klen, void *entry)
+{
+ struct RTSP *rtsp = entry;
+ (void)key;
+ (void)klen;
+ free(rtsp);
+}
+
+static void rtsp_conn_dtor(void *key, size_t klen, void *entry)
+{
+ struct rtsp_conn *rtspc = entry;
+ (void)key;
+ (void)klen;
+ curlx_dyn_free(&rtspc->buf);
+ free(rtspc);
+}
+
static CURLcode rtsp_setup_connection(struct Curl_easy *data,
struct connectdata *conn)
{
+ struct rtsp_conn *rtspc;
struct RTSP *rtsp;
- (void)conn;
- data->req.p.rtsp = rtsp = calloc(1, sizeof(struct RTSP));
- if(!rtsp)
+ rtspc = calloc(1, sizeof(*rtspc));
+ if(!rtspc)
+ return CURLE_OUT_OF_MEMORY;
+ curlx_dyn_init(&rtspc->buf, MAX_RTP_BUFFERSIZE);
+ if(Curl_conn_meta_set(conn, CURL_META_RTSP_CONN, rtspc, rtsp_conn_dtor))
+ return CURLE_OUT_OF_MEMORY;
+
+ rtsp = calloc(1, sizeof(struct RTSP));
+ if(!rtsp ||
+ Curl_meta_set(data, CURL_META_RTSP_EASY, rtsp, rtsp_easy_dtor))
return CURLE_OUT_OF_MEMORY;
- Curl_dyn_init(&conn->proto.rtspc.buf, MAX_RTP_BUFFERSIZE);
return CURLE_OK;
}
@@ -162,8 +216,13 @@ static unsigned int rtsp_conncheck(struct Curl_easy *data,
static CURLcode rtsp_connect(struct Curl_easy *data, bool *done)
{
+ struct rtsp_conn *rtspc =
+ Curl_conn_meta_get(data->conn, CURL_META_RTSP_CONN);
CURLcode httpStatus;
+ if(!rtspc)
+ return CURLE_FAILED_INIT;
+
httpStatus = Curl_http_connect(data, done);
/* Initialize the CSeq if not already done */
@@ -172,34 +231,29 @@ static CURLcode rtsp_connect(struct Curl_easy *data, bool *done)
if(data->state.rtsp_next_server_CSeq == 0)
data->state.rtsp_next_server_CSeq = 1;
- data->conn->proto.rtspc.rtp_channel = -1;
+ rtspc->rtp_channel = -1;
return httpStatus;
}
-static CURLcode rtsp_disconnect(struct Curl_easy *data,
- struct connectdata *conn, bool dead)
-{
- (void) dead;
- (void) data;
- Curl_dyn_free(&conn->proto.rtspc.buf);
- return CURLE_OK;
-}
-
-
static CURLcode rtsp_done(struct Curl_easy *data,
CURLcode status, bool premature)
{
- struct RTSP *rtsp = data->req.p.rtsp;
+ struct rtsp_conn *rtspc =
+ Curl_conn_meta_get(data->conn, CURL_META_RTSP_CONN);
+ struct RTSP *rtsp = Curl_meta_get(data, CURL_META_RTSP_EASY);
CURLcode httpStatus;
+ if(!rtspc || !rtsp)
+ return CURLE_FAILED_INIT;
+
/* Bypass HTTP empty-reply checks on receive */
if(data->set.rtspreq == RTSPREQ_RECEIVE)
premature = TRUE;
httpStatus = Curl_http_done(data, status, premature);
- if(rtsp && !status && !httpStatus) {
+ if(!status && !httpStatus) {
/* Check the sequence numbers */
long CSeq_sent = rtsp->CSeq_sent;
long CSeq_recv = rtsp->CSeq_recv;
@@ -209,10 +263,14 @@ static CURLcode rtsp_done(struct Curl_easy *data,
CSeq_sent, CSeq_recv);
return CURLE_RTSP_CSEQ_ERROR;
}
- if(data->set.rtspreq == RTSPREQ_RECEIVE &&
- (data->conn->proto.rtspc.rtp_channel == -1)) {
+ if(data->set.rtspreq == RTSPREQ_RECEIVE && (rtspc->rtp_channel == -1)) {
infof(data, "Got an RTP Receive with a CSeq of %ld", CSeq_recv);
}
+ if(data->set.rtspreq == RTSPREQ_RECEIVE &&
+ data->req.eos_written) {
+ failf(data, "Server prematurely closed the RTSP connection.");
+ return CURLE_RECV_ERROR;
+ }
}
return httpStatus;
@@ -223,8 +281,9 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
struct connectdata *conn = data->conn;
CURLcode result = CURLE_OK;
Curl_RtspReq rtspreq = data->set.rtspreq;
- struct RTSP *rtsp = data->req.p.rtsp;
+ struct RTSP *rtsp = Curl_meta_get(data, CURL_META_RTSP_EASY);
struct dynbuf req_buffer;
+ unsigned char httpversion = 11; /* RTSP is close to HTTP/1.1, sort of... */
const char *p_request = NULL;
const char *p_session_id = NULL;
@@ -239,8 +298,11 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
const char *p_userpwd = NULL;
*done = TRUE;
+ if(!rtsp)
+ return CURLE_FAILED_INIT;
+
/* Initialize a dynamic send buffer */
- Curl_dyn_init(&req_buffer, DYN_RTSP_REQ_HEADER);
+ curlx_dyn_init(&req_buffer, DYN_RTSP_REQ_HEADER);
rtsp->CSeq_sent = data->state.rtsp_next_client_CSeq;
rtsp->CSeq_recv = 0;
@@ -338,8 +400,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
if(rtspreq == RTSPREQ_SETUP && !p_transport) {
/* New Transport: setting? */
if(data->set.str[STRING_RTSP_TRANSPORT]) {
- Curl_safefree(data->state.aptr.rtsp_transport);
-
+ free(data->state.aptr.rtsp_transport);
data->state.aptr.rtsp_transport =
aprintf("Transport: %s\r\n",
data->set.str[STRING_RTSP_TRANSPORT]);
@@ -365,7 +426,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
/* Accept-Encoding header */
if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) &&
data->set.str[STRING_ENCODING]) {
- Curl_safefree(data->state.aptr.accept_encoding);
+ free(data->state.aptr.accept_encoding);
data->state.aptr.accept_encoding =
aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
@@ -419,7 +480,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
/* Check to see if there is a range set in the custom headers */
if(!Curl_checkheaders(data, STRCONST("Range")) && data->state.range) {
- Curl_safefree(data->state.aptr.rangeline);
+ free(data->state.aptr.rangeline);
data->state.aptr.rangeline = aprintf("Range: %s\r\n", data->state.range);
p_range = data->state.aptr.rangeline;
}
@@ -440,10 +501,10 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
}
result =
- Curl_dyn_addf(&req_buffer,
- "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */
- "CSeq: %ld\r\n", /* CSeq */
- p_request, p_stream_uri, rtsp->CSeq_sent);
+ curlx_dyn_addf(&req_buffer,
+ "%s %s RTSP/1.0\r\n" /* Request Stream-URI RTSP/1.0 */
+ "CSeq: %ld\r\n", /* CSeq */
+ p_request, p_stream_uri, rtsp->CSeq_sent);
if(result)
goto out;
@@ -452,7 +513,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
* to make comparison easier
*/
if(p_session_id) {
- result = Curl_dyn_addf(&req_buffer, "Session: %s\r\n", p_session_id);
+ result = curlx_dyn_addf(&req_buffer, "Session: %s\r\n", p_session_id);
if(result)
goto out;
}
@@ -460,24 +521,24 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
/*
* Shared HTTP-like options
*/
- result = Curl_dyn_addf(&req_buffer,
- "%s" /* transport */
- "%s" /* accept */
- "%s" /* accept-encoding */
- "%s" /* range */
- "%s" /* referrer */
- "%s" /* user-agent */
- "%s" /* proxyuserpwd */
- "%s" /* userpwd */
- ,
- p_transport ? p_transport : "",
- p_accept ? p_accept : "",
- p_accept_encoding ? p_accept_encoding : "",
- p_range ? p_range : "",
- p_referrer ? p_referrer : "",
- p_uagent ? p_uagent : "",
- p_proxyuserpwd ? p_proxyuserpwd : "",
- p_userpwd ? p_userpwd : "");
+ result = curlx_dyn_addf(&req_buffer,
+ "%s" /* transport */
+ "%s" /* accept */
+ "%s" /* accept-encoding */
+ "%s" /* range */
+ "%s" /* referrer */
+ "%s" /* user-agent */
+ "%s" /* proxyuserpwd */
+ "%s" /* userpwd */
+ ,
+ p_transport ? p_transport : "",
+ p_accept ? p_accept : "",
+ p_accept_encoding ? p_accept_encoding : "",
+ p_range ? p_range : "",
+ p_referrer ? p_referrer : "",
+ p_uagent ? p_uagent : "",
+ p_proxyuserpwd ? p_proxyuserpwd : "",
+ p_userpwd ? p_userpwd : "");
/*
* Free userpwd now --- cannot reuse this for Negotiate and possibly NTLM
@@ -494,7 +555,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
goto out;
}
- result = Curl_add_custom_headers(data, FALSE, &req_buffer);
+ result = Curl_add_custom_headers(data, FALSE, httpversion, &req_buffer);
if(result)
goto out;
@@ -533,8 +594,8 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
* actually set a custom Content-Length in the headers */
if(!Curl_checkheaders(data, STRCONST("Content-Length"))) {
result =
- Curl_dyn_addf(&req_buffer, "Content-Length: %" FMT_OFF_T"\r\n",
- req_clen);
+ curlx_dyn_addf(&req_buffer, "Content-Length: %" FMT_OFF_T"\r\n",
+ req_clen);
if(result)
goto out;
}
@@ -542,9 +603,9 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
if(rtspreq == RTSPREQ_SET_PARAMETER ||
rtspreq == RTSPREQ_GET_PARAMETER) {
if(!Curl_checkheaders(data, STRCONST("Content-Type"))) {
- result = Curl_dyn_addn(&req_buffer,
- STRCONST("Content-Type: "
- "text/parameters\r\n"));
+ result = curlx_dyn_addn(&req_buffer,
+ STRCONST("Content-Type: "
+ "text/parameters\r\n"));
if(result)
goto out;
}
@@ -552,9 +613,9 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
if(rtspreq == RTSPREQ_ANNOUNCE) {
if(!Curl_checkheaders(data, STRCONST("Content-Type"))) {
- result = Curl_dyn_addn(&req_buffer,
- STRCONST("Content-Type: "
- "application/sdp\r\n"));
+ result = curlx_dyn_addn(&req_buffer,
+ STRCONST("Content-Type: "
+ "application/sdp\r\n"));
if(result)
goto out;
}
@@ -573,14 +634,14 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
}
/* Finish the request buffer */
- result = Curl_dyn_addn(&req_buffer, STRCONST("\r\n"));
+ result = curlx_dyn_addn(&req_buffer, STRCONST("\r\n"));
if(result)
goto out;
Curl_xfer_setup1(data, CURL_XFER_SENDRECV, -1, TRUE);
/* issue the request */
- result = Curl_req_send(data, &req_buffer);
+ result = Curl_req_send(data, &req_buffer, httpversion);
if(result) {
failf(data, "Failed sending RTSP request");
goto out;
@@ -597,7 +658,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
result = CURLE_ABORTED_BY_CALLBACK;
}
out:
- Curl_dyn_free(&req_buffer);
+ curlx_dyn_free(&req_buffer);
return result;
}
@@ -605,10 +666,10 @@ out:
* write any BODY bytes missing to the client, ignore the rest.
*/
static CURLcode rtp_write_body_junk(struct Curl_easy *data,
+ struct rtsp_conn *rtspc,
const char *buf,
size_t blen)
{
- struct rtsp_conn *rtspc = &(data->conn->proto.rtspc);
curl_off_t body_remain;
bool in_body;
@@ -620,17 +681,17 @@ static CURLcode rtp_write_body_junk(struct Curl_easy *data,
if(body_remain) {
if((curl_off_t)blen > body_remain)
blen = (size_t)body_remain;
- return Curl_client_write(data, CLIENTWRITE_BODY, (char *)buf, blen);
+ return Curl_client_write(data, CLIENTWRITE_BODY, buf, blen);
}
return CURLE_OK;
}
static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
- const char *buf,
- size_t blen,
- size_t *pconsumed)
+ struct rtsp_conn *rtspc,
+ const char *buf,
+ size_t blen,
+ size_t *pconsumed)
{
- struct rtsp_conn *rtspc = &(data->conn->proto.rtspc);
CURLcode result = CURLE_OK;
size_t skip_len = 0;
@@ -642,7 +703,7 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
switch(rtspc->state) {
case RTP_PARSE_SKIP: {
- DEBUGASSERT(Curl_dyn_len(&rtspc->buf) == 0);
+ DEBUGASSERT(curlx_dyn_len(&rtspc->buf) == 0);
while(blen && buf[0] != '$') {
if(!in_body && buf[0] == 'R' &&
data->set.rtspreq != RTSPREQ_RECEIVE) {
@@ -667,13 +728,12 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
/* possible start of an RTP message, buffer */
if(skip_len) {
/* end of junk/BODY bytes, flush */
- result = rtp_write_body_junk(data,
- (char *)(buf - skip_len), skip_len);
+ result = rtp_write_body_junk(data, rtspc, buf - skip_len, skip_len);
skip_len = 0;
if(result)
goto out;
}
- if(Curl_dyn_addn(&rtspc->buf, buf, 1)) {
+ if(curlx_dyn_addn(&rtspc->buf, buf, 1)) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
@@ -688,7 +748,7 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
case RTP_PARSE_CHANNEL: {
int idx = ((unsigned char)buf[0]) / 8;
int off = ((unsigned char)buf[0]) % 8;
- DEBUGASSERT(Curl_dyn_len(&rtspc->buf) == 1);
+ DEBUGASSERT(curlx_dyn_len(&rtspc->buf) == 1);
if(!(data->state.rtp_channel_mask[idx] & (1 << off))) {
/* invalid channel number, junk or BODY data */
rtspc->state = RTP_PARSE_SKIP;
@@ -699,7 +759,8 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
/* We did not consume the initial '$' in our buffer, but had
* it from an earlier call. We cannot un-consume it and have
* to write it directly as BODY data */
- result = rtp_write_body_junk(data, Curl_dyn_ptr(&rtspc->buf), 1);
+ result = rtp_write_body_junk(data, rtspc,
+ curlx_dyn_ptr(&rtspc->buf), 1);
if(result)
goto out;
}
@@ -707,12 +768,12 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
/* count the '$' as skip and continue */
skip_len = 1;
}
- Curl_dyn_free(&rtspc->buf);
+ curlx_dyn_free(&rtspc->buf);
break;
}
/* a valid channel, so we expect this to be a real RTP message */
rtspc->rtp_channel = (unsigned char)buf[0];
- if(Curl_dyn_addn(&rtspc->buf, buf, 1)) {
+ if(curlx_dyn_addn(&rtspc->buf, buf, 1)) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
@@ -724,10 +785,10 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
}
case RTP_PARSE_LEN: {
- size_t rtp_len = Curl_dyn_len(&rtspc->buf);
+ size_t rtp_len = curlx_dyn_len(&rtspc->buf);
const char *rtp_buf;
DEBUGASSERT(rtp_len >= 2 && rtp_len < 4);
- if(Curl_dyn_addn(&rtspc->buf, buf, 1)) {
+ if(curlx_dyn_addn(&rtspc->buf, buf, 1)) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
@@ -736,19 +797,19 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
--blen;
if(rtp_len == 2)
break;
- rtp_buf = Curl_dyn_ptr(&rtspc->buf);
+ rtp_buf = curlx_dyn_ptr(&rtspc->buf);
rtspc->rtp_len = RTP_PKT_LENGTH(rtp_buf) + 4;
rtspc->state = RTP_PARSE_DATA;
break;
}
case RTP_PARSE_DATA: {
- size_t rtp_len = Curl_dyn_len(&rtspc->buf);
+ size_t rtp_len = curlx_dyn_len(&rtspc->buf);
size_t needed;
DEBUGASSERT(rtp_len < rtspc->rtp_len);
needed = rtspc->rtp_len - rtp_len;
if(needed <= blen) {
- if(Curl_dyn_addn(&rtspc->buf, buf, needed)) {
+ if(curlx_dyn_addn(&rtspc->buf, buf, needed)) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
@@ -758,15 +819,15 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
/* complete RTP message in buffer */
DEBUGF(infof(data, "RTP write channel %d rtp_len %zu",
rtspc->rtp_channel, rtspc->rtp_len));
- result = rtp_client_write(data, Curl_dyn_ptr(&rtspc->buf),
+ result = rtp_client_write(data, curlx_dyn_ptr(&rtspc->buf),
rtspc->rtp_len);
- Curl_dyn_free(&rtspc->buf);
+ curlx_dyn_free(&rtspc->buf);
rtspc->state = RTP_PARSE_SKIP;
if(result)
goto out;
}
else {
- if(Curl_dyn_addn(&rtspc->buf, buf, blen)) {
+ if(curlx_dyn_addn(&rtspc->buf, buf, blen)) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
@@ -784,7 +845,7 @@ static CURLcode rtsp_filter_rtp(struct Curl_easy *data,
}
out:
if(!result && skip_len)
- result = rtp_write_body_junk(data, (char *)(buf - skip_len), skip_len);
+ result = rtp_write_body_junk(data, rtspc, buf - skip_len, skip_len);
return result;
}
@@ -793,10 +854,14 @@ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
size_t blen,
bool is_eos)
{
- struct rtsp_conn *rtspc = &(data->conn->proto.rtspc);
+ struct rtsp_conn *rtspc =
+ Curl_conn_meta_get(data->conn, CURL_META_RTSP_CONN);
CURLcode result = CURLE_OK;
size_t consumed = 0;
+ if(!rtspc)
+ return CURLE_FAILED_INIT;
+
if(!data->req.header)
rtspc->in_header = FALSE;
if(!blen) {
@@ -808,7 +873,7 @@ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
/* If header parsing is not ongoing, extract RTP messages */
if(!rtspc->in_header) {
- result = rtsp_filter_rtp(data, buf, blen, &consumed);
+ result = rtsp_filter_rtp(data, rtspc, buf, blen, &consumed);
if(result)
goto out;
buf += consumed;
@@ -840,7 +905,7 @@ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
data->req.size = 0;
data->req.download_done = TRUE;
}
- result = rtsp_filter_rtp(data, buf, blen, &consumed);
+ result = rtsp_filter_rtp(data, rtspc, buf, blen, &consumed);
if(result)
goto out;
blen -= consumed;
@@ -858,8 +923,7 @@ static CURLcode rtsp_rtp_write_resp(struct Curl_easy *data,
data->req.size));
if(!result && (is_eos || blen)) {
result = Curl_client_write(data, CLIENTWRITE_BODY|
- (is_eos ? CLIENTWRITE_EOS : 0),
- (char *)buf, blen);
+ (is_eos ? CLIENTWRITE_EOS : 0), buf, blen);
}
out:
@@ -899,7 +963,7 @@ CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len)
}
Curl_set_in_callback(data, TRUE);
- wrote = writeit((char *)ptr, 1, len, user_ptr);
+ wrote = writeit((char *)CURL_UNCONST(ptr), 1, len, user_ptr);
Curl_set_in_callback(data, FALSE);
if(CURL_WRITEFUNC_PAUSE == wrote) {
@@ -918,21 +982,18 @@ CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len)
CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header)
{
if(checkprefix("CSeq:", header)) {
- long CSeq = 0;
- char *endp;
+ curl_off_t CSeq = 0;
+ struct RTSP *rtsp = Curl_meta_get(data, CURL_META_RTSP_EASY);
const char *p = &header[5];
- while(ISBLANK(*p))
- p++;
- CSeq = strtol(p, &endp, 10);
- if(p != endp) {
- struct RTSP *rtsp = data->req.p.rtsp;
- rtsp->CSeq_recv = CSeq; /* mark the request */
- data->state.rtsp_CSeq_recv = CSeq; /* update the handle */
- }
- else {
+ if(!rtsp)
+ return CURLE_FAILED_INIT;
+ curlx_str_passblanks(&p);
+ if(curlx_str_number(&p, &CSeq, LONG_MAX)) {
failf(data, "Unable to read the CSeq header: [%s]", header);
return CURLE_RTSP_CSEQ_ERROR;
}
+ rtsp->CSeq_recv = (long)CSeq; /* mark the request */
+ data->state.rtsp_CSeq_recv = (long)CSeq; /* update the handle */
}
else if(checkprefix("Session:", header)) {
const char *start, *end;
@@ -940,8 +1001,7 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header)
/* Find the first non-space letter */
start = header + 8;
- while(*start && ISBLANK(*start))
- start++;
+ curlx_str_passblanks(&start);
if(!*start) {
failf(data, "Got a blank Session ID");
@@ -955,7 +1015,7 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header)
* gstreamer does url-encoded session ID's not covered by the standard.
*/
end = start;
- while(*end && *end != ';' && !ISSPACE(*end))
+ while((*end > ' ') && (*end != ';'))
end++;
idlen = end - start;
@@ -999,29 +1059,24 @@ CURLcode rtsp_parse_transport(struct Curl_easy *data, const char *transport)
const char *start, *end;
start = transport;
while(start && *start) {
- while(*start && ISBLANK(*start) )
- start++;
+ curlx_str_passblanks(&start);
end = strchr(start, ';');
if(checkprefix("interleaved=", start)) {
- long chan1, chan2, chan;
- char *endp;
+ curl_off_t chan1, chan2, chan;
const char *p = start + 12;
- chan1 = strtol(p, &endp, 10);
- if(p != endp && chan1 >= 0 && chan1 <= 255) {
+ if(!curlx_str_number(&p, &chan1, 255)) {
unsigned char *rtp_channel_mask = data->state.rtp_channel_mask;
chan2 = chan1;
- if(*endp == '-') {
- p = endp + 1;
- chan2 = strtol(p, &endp, 10);
- if(p == endp || chan2 < 0 || chan2 > 255) {
+ if(!curlx_str_single(&p, '-')) {
+ if(curlx_str_number(&p, &chan2, 255)) {
infof(data, "Unable to read the interleaved parameter from "
"Transport header: [%s]", transport);
chan2 = chan1;
}
}
for(chan = chan1; chan <= chan2; chan++) {
- long idx = chan / 8;
- long off = chan % 8;
+ int idx = (int)chan / 8;
+ int off = (int)chan % 8;
rtp_channel_mask[idx] |= (unsigned char)(1 << off);
}
}
@@ -1038,4 +1093,4 @@ CURLcode rtsp_parse_transport(struct Curl_easy *data, const char *transport)
}
-#endif /* CURL_DISABLE_RTSP or using Hyper */
+#endif /* CURL_DISABLE_RTSP */
diff --git a/libs/libcurl/src/rtsp.h b/libs/libcurl/src/rtsp.h
index 27e04a0992..3209219d52 100644
--- a/libs/libcurl/src/rtsp.h
+++ b/libs/libcurl/src/rtsp.h
@@ -23,9 +23,6 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#ifdef USE_HYPER
-#define CURL_DISABLE_RTSP 1
-#endif
#ifndef CURL_DISABLE_RTSP
@@ -39,32 +36,4 @@ CURLcode Curl_rtsp_parseheader(struct Curl_easy *data, const char *header);
#endif /* CURL_DISABLE_RTSP */
-typedef enum {
- RTP_PARSE_SKIP,
- RTP_PARSE_CHANNEL,
- RTP_PARSE_LEN,
- RTP_PARSE_DATA
-} rtp_parse_st;
-/*
- * RTSP Connection data
- *
- * Currently, only used for tracking incomplete RTP data reads
- */
-struct rtsp_conn {
- struct dynbuf buf;
- int rtp_channel;
- size_t rtp_len;
- rtp_parse_st state;
- BIT(in_header);
-};
-
-/****************************************************************************
- * RTSP unique setup
- ***************************************************************************/
-struct RTSP {
- long CSeq_sent; /* CSeq of this request */
- long CSeq_recv; /* CSeq received */
-};
-
-
#endif /* HEADER_CURL_RTSP_H */
diff --git a/libs/libcurl/src/select.c b/libs/libcurl/src/select.c
index d01820d966..01bde7c0ee 100644
--- a/libs/libcurl/src/select.c
+++ b/libs/libcurl/src/select.c
@@ -45,8 +45,8 @@
#include "urldata.h"
#include "connect.h"
#include "select.h"
-#include "timediff.h"
-#include "warnless.h"
+#include "curlx/timediff.h"
+#include "curlx/warnless.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
@@ -74,11 +74,11 @@ int Curl_wait_ms(timediff_t timeout_ms)
if(!timeout_ms)
return 0;
if(timeout_ms < 0) {
- SET_SOCKERRNO(EINVAL);
+ SET_SOCKERRNO(SOCKEINVAL);
return -1;
}
#if defined(MSDOS)
- delay(timeout_ms);
+ delay((unsigned int)timeout_ms);
#elif defined(_WIN32)
/* prevent overflow, timeout_ms is typecast to ULONG/DWORD. */
#if TIMEDIFF_T_MAX >= ULONG_MAX
@@ -86,7 +86,7 @@ int Curl_wait_ms(timediff_t timeout_ms)
timeout_ms = ULONG_MAX-1;
/* do not use ULONG_MAX, because that is equal to INFINITE */
#endif
- Sleep((ULONG)timeout_ms);
+ Sleep((DWORD)timeout_ms);
#else
/* avoid using poll() for this since it behaves incorrectly with no sockets
on Apple operating systems */
@@ -96,7 +96,7 @@ int Curl_wait_ms(timediff_t timeout_ms)
}
#endif /* _WIN32 */
if(r) {
- if((r == -1) && (SOCKERRNO == EINTR))
+ if((r == -1) && (SOCKERRNO == SOCKEINTR))
/* make EINTR from select or poll not a "lethal" error */
r = 0;
else
@@ -197,7 +197,7 @@ int Curl_socket_check(curl_socket_t readfd0, /* two sockets to read from */
return Curl_wait_ms(timeout_ms);
}
- /* Avoid initial timestamp, avoid Curl_now() call, when elapsed
+ /* Avoid initial timestamp, avoid curlx_now() call, when elapsed
time in this function does not need to be measured. This happens
when function is called with a zero timeout or a negative timeout
value indicating a blocking call should be performed. */
@@ -292,7 +292,7 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms)
return Curl_wait_ms(timeout_ms);
}
- /* Avoid initial timestamp, avoid Curl_now() call, when elapsed
+ /* Avoid initial timestamp, avoid curlx_now() call, when elapsed
time in this function does not need to be measured. This happens
when function is called with a zero timeout or a negative timeout
value indicating a blocking call should be performed. */
@@ -312,7 +312,7 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms)
pending_ms = 0;
r = poll(ufds, nfds, pending_ms);
if(r <= 0) {
- if((r == -1) && (SOCKERRNO == EINTR))
+ if((r == -1) && (SOCKERRNO == SOCKEINTR))
/* make EINTR from select or poll not a "lethal" error */
r = 0;
return r;
@@ -360,7 +360,7 @@ int Curl_poll(struct pollfd ufds[], unsigned int nfds, timediff_t timeout_ms)
*/
r = our_select(maxfd, &fds_read, &fds_write, &fds_err, timeout_ms);
if(r <= 0) {
- if((r == -1) && (SOCKERRNO == EINTR))
+ if((r == -1) && (SOCKERRNO == SOCKEINTR))
/* make EINTR from select or poll not a "lethal" error */
r = 0;
return r;
@@ -410,6 +410,11 @@ void Curl_pollfds_init(struct curl_pollfds *cpfds,
}
}
+void Curl_pollfds_reset(struct curl_pollfds *cpfds)
+{
+ cpfds->n = 0;
+}
+
void Curl_pollfds_cleanup(struct curl_pollfds *cpfds)
{
DEBUGASSERT(cpfds);
@@ -488,43 +493,47 @@ CURLcode Curl_pollfds_add_ps(struct curl_pollfds *cpfds,
return CURLE_OK;
}
-void Curl_waitfds_init(struct curl_waitfds *cwfds,
+void Curl_waitfds_init(struct Curl_waitfds *cwfds,
struct curl_waitfd *static_wfds,
unsigned int static_count)
{
DEBUGASSERT(cwfds);
- DEBUGASSERT(static_wfds);
+ DEBUGASSERT(static_wfds || !static_count);
memset(cwfds, 0, sizeof(*cwfds));
cwfds->wfds = static_wfds;
cwfds->count = static_count;
}
-static CURLcode cwfds_add_sock(struct curl_waitfds *cwfds,
- curl_socket_t sock, short events)
+static unsigned int cwfds_add_sock(struct Curl_waitfds *cwfds,
+ curl_socket_t sock, short events)
{
int i;
-
+ if(!cwfds->wfds) {
+ DEBUGASSERT(!cwfds->count && !cwfds->n);
+ return 1;
+ }
if(cwfds->n <= INT_MAX) {
for(i = (int)cwfds->n - 1; i >= 0; --i) {
if(sock == cwfds->wfds[i].fd) {
cwfds->wfds[i].events |= events;
- return CURLE_OK;
+ return 0;
}
}
}
/* not folded, add new entry */
- if(cwfds->n >= cwfds->count)
- return CURLE_OUT_OF_MEMORY;
- cwfds->wfds[cwfds->n].fd = sock;
- cwfds->wfds[cwfds->n].events = events;
- ++cwfds->n;
- return CURLE_OK;
+ if(cwfds->n < cwfds->count) {
+ cwfds->wfds[cwfds->n].fd = sock;
+ cwfds->wfds[cwfds->n].events = events;
+ ++cwfds->n;
+ }
+ return 1;
}
-CURLcode Curl_waitfds_add_ps(struct curl_waitfds *cwfds,
- struct easy_pollset *ps)
+unsigned int Curl_waitfds_add_ps(struct Curl_waitfds *cwfds,
+ struct easy_pollset *ps)
{
size_t i;
+ unsigned int need = 0;
DEBUGASSERT(cwfds);
DEBUGASSERT(ps);
@@ -534,10 +543,8 @@ CURLcode Curl_waitfds_add_ps(struct curl_waitfds *cwfds,
events |= CURL_WAIT_POLLIN;
if(ps->actions[i] & CURL_POLL_OUT)
events |= CURL_WAIT_POLLOUT;
- if(events) {
- if(cwfds_add_sock(cwfds, ps->sockets[i], events))
- return CURLE_OUT_OF_MEMORY;
- }
+ if(events)
+ need += cwfds_add_sock(cwfds, ps->sockets[i], events);
}
- return CURLE_OK;
+ return need;
}
diff --git a/libs/libcurl/src/select.h b/libs/libcurl/src/select.h
index 6af855f950..d1d3d8564b 100644
--- a/libs/libcurl/src/select.h
+++ b/libs/libcurl/src/select.h
@@ -93,7 +93,7 @@ int Curl_wait_ms(timediff_t timeout_ms);
#define FDSET_SOCK(x) 1
#define VERIFY_SOCK(x) do { \
if(!VALID_SOCK(x)) { \
- SET_SOCKERRNO(WSAEINVAL); \
+ SET_SOCKERRNO(SOCKEINVAL); \
return -1; \
} \
} while(0)
@@ -105,7 +105,7 @@ int Curl_wait_ms(timediff_t timeout_ms);
#define VERIFY_SOCK(x) do { \
if(!VALID_SOCK(x) || !FDSET_SOCK(x)) { \
- SET_SOCKERRNO(EINVAL); \
+ SET_SOCKERRNO(SOCKEINVAL); \
return -1; \
} \
} while(0)
@@ -122,6 +122,8 @@ void Curl_pollfds_init(struct curl_pollfds *cpfds,
struct pollfd *static_pfds,
unsigned int static_count);
+void Curl_pollfds_reset(struct curl_pollfds *cpfds);
+
void Curl_pollfds_cleanup(struct curl_pollfds *cpfds);
CURLcode Curl_pollfds_add_ps(struct curl_pollfds *cpfds,
@@ -130,18 +132,17 @@ CURLcode Curl_pollfds_add_ps(struct curl_pollfds *cpfds,
CURLcode Curl_pollfds_add_sock(struct curl_pollfds *cpfds,
curl_socket_t sock, short events);
-struct curl_waitfds {
+struct Curl_waitfds {
struct curl_waitfd *wfds;
unsigned int n;
unsigned int count;
};
-void Curl_waitfds_init(struct curl_waitfds *cwfds,
+void Curl_waitfds_init(struct Curl_waitfds *cwfds,
struct curl_waitfd *static_wfds,
unsigned int static_count);
-CURLcode Curl_waitfds_add_ps(struct curl_waitfds *cwfds,
- struct easy_pollset *ps);
-
+unsigned int Curl_waitfds_add_ps(struct Curl_waitfds *cwfds,
+ struct easy_pollset *ps);
#endif /* HEADER_CURL_SELECT_H */
diff --git a/libs/libcurl/src/sendf.c b/libs/libcurl/src/sendf.c
index 723ef1458a..34fd0edce0 100644
--- a/libs/libcurl/src/sendf.c
+++ b/libs/libcurl/src/sendf.c
@@ -42,6 +42,7 @@
#include "connect.h"
#include "content_encoding.h"
#include "cw-out.h"
+#include "cw-pause.h"
#include "vtls/vtls.h"
#include "vssh/ssh.h"
#include "easyif.h"
@@ -51,7 +52,7 @@
#include "strdup.h"
#include "http2.h"
#include "progress.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
#include "ws.h"
/* The last 3 #include files should be in this order */
@@ -316,9 +317,6 @@ static CURLcode cw_download_write(struct Curl_easy *data,
}
/* Update stats, write and report progress */
data->req.bytecount += nwrite;
-#ifdef USE_HYPER
- data->req.bodywritten = TRUE;
-#endif
result = Curl_pgrsSetDownloadCounter(data, data->req.bytecount);
if(result)
return result;
@@ -362,7 +360,7 @@ static CURLcode cw_raw_write(struct Curl_easy *data,
const char *buf, size_t nbytes)
{
if(type & CLIENTWRITE_BODY && data->set.verbose && !data->req.ignorebody) {
- Curl_debug(data, CURLINFO_DATA_IN, (char *)buf, nbytes);
+ Curl_debug(data, CURLINFO_DATA_IN, buf, nbytes);
}
return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
}
@@ -436,21 +434,37 @@ static CURLcode do_init_writer_stack(struct Curl_easy *data)
if(result)
return result;
- result = Curl_cwriter_create(&writer, data, &cw_download, CURL_CW_PROTOCOL);
+ /* This places the "pause" writer behind the "download" writer that
+ * is added below. Meaning the "download" can do checks on content length
+ * and other things *before* write outs are buffered for paused transfers. */
+ result = Curl_cwriter_create(&writer, data, &Curl_cwt_pause,
+ CURL_CW_PROTOCOL);
+ if(!result) {
+ result = Curl_cwriter_add(data, writer);
+ if(result)
+ Curl_cwriter_free(data, writer);
+ }
if(result)
return result;
- result = Curl_cwriter_add(data, writer);
- if(result) {
- Curl_cwriter_free(data, writer);
+
+ result = Curl_cwriter_create(&writer, data, &cw_download, CURL_CW_PROTOCOL);
+ if(!result) {
+ result = Curl_cwriter_add(data, writer);
+ if(result)
+ Curl_cwriter_free(data, writer);
}
+ if(result)
+ return result;
result = Curl_cwriter_create(&writer, data, &cw_raw, CURL_CW_RAW);
+ if(!result) {
+ result = Curl_cwriter_add(data, writer);
+ if(result)
+ Curl_cwriter_free(data, writer);
+ }
if(result)
return result;
- result = Curl_cwriter_add(data, writer);
- if(result) {
- Curl_cwriter_free(data, writer);
- }
+
return result;
}
@@ -497,20 +511,14 @@ struct Curl_cwriter *Curl_cwriter_get_by_type(struct Curl_easy *data,
return NULL;
}
-void Curl_cwriter_remove_by_name(struct Curl_easy *data,
- const char *name)
+bool Curl_cwriter_is_content_decoding(struct Curl_easy *data)
{
- struct Curl_cwriter **anchor = &data->req.writer_stack;
-
- while(*anchor) {
- if(!strcmp(name, (*anchor)->cwt->name)) {
- struct Curl_cwriter *w = (*anchor);
- *anchor = w->next;
- Curl_cwriter_free(data, w);
- continue;
- }
- anchor = &((*anchor)->next);
+ struct Curl_cwriter *writer;
+ for(writer = data->req.writer_stack; writer; writer = writer->next) {
+ if(writer->phase == CURL_CW_CONTENT_DECODE)
+ return TRUE;
}
+ return FALSE;
}
bool Curl_cwriter_is_paused(struct Curl_easy *data)
@@ -1022,13 +1030,6 @@ static CURLcode cr_lc_read(struct Curl_easy *data,
if(result)
return result;
start = i + 1;
- if(!data->set.crlf && (data->state.infilesize != -1)) {
- /* we are here only because FTP is in ASCII mode...
- bump infilesize for the LF we just added */
- data->state.infilesize++;
- /* comment: this might work for FTP, but in HTTP we could not change
- * the content length after having started the request... */
- }
}
if(start < i) { /* leftover */
@@ -1305,6 +1306,15 @@ static bool cr_buf_needs_rewind(struct Curl_easy *data,
return ctx->index > 0;
}
+static CURLcode cr_buf_rewind(struct Curl_easy *data,
+ struct Curl_creader *reader)
+{
+ struct cr_buf_ctx *ctx = reader->ctx;
+ (void)data;
+ ctx->index = 0;
+ return CURLE_OK;
+}
+
static curl_off_t cr_buf_total_length(struct Curl_easy *data,
struct Curl_creader *reader)
{
@@ -1344,7 +1354,7 @@ static const struct Curl_crtype cr_buf = {
cr_buf_needs_rewind,
cr_buf_total_length,
cr_buf_resume_from,
- Curl_creader_def_rewind,
+ cr_buf_rewind,
Curl_creader_def_unpause,
Curl_creader_def_is_paused,
Curl_creader_def_done,
diff --git a/libs/libcurl/src/sendf.h b/libs/libcurl/src/sendf.h
index cda179b920..e4ed646475 100644
--- a/libs/libcurl/src/sendf.h
+++ b/libs/libcurl/src/sendf.h
@@ -166,9 +166,6 @@ CURLcode Curl_cwriter_add(struct Curl_easy *data,
struct Curl_cwriter *Curl_cwriter_get_by_type(struct Curl_easy *data,
const struct Curl_cwtype *cwt);
-void Curl_cwriter_remove_by_name(struct Curl_easy *data,
- const char *name);
-
struct Curl_cwriter *Curl_cwriter_get_by_name(struct Curl_easy *data,
const char *name);
@@ -185,6 +182,8 @@ CURLcode Curl_cwriter_write(struct Curl_easy *data,
*/
bool Curl_cwriter_is_paused(struct Curl_easy *data);
+bool Curl_cwriter_is_content_decoding(struct Curl_easy *data);
+
/**
* Unpause client writer and flush any buffered date to the client.
*/
diff --git a/libs/libcurl/src/setopt.c b/libs/libcurl/src/setopt.c
index d02fca6c8f..349ddf6ae3 100644
--- a/libs/libcurl/src/setopt.c
+++ b/libs/libcurl/src/setopt.c
@@ -43,8 +43,9 @@
#include "strcase.h"
#include "share.h"
#include "vtls/vtls.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
#include "sendf.h"
+#include "hostip.h"
#include "http2.h"
#include "setopt.h"
#include "multiif.h"
@@ -255,6 +256,157 @@ static CURLcode httpauth(struct Curl_easy *data, bool proxy,
return CURLE_OK;
}
+#ifndef CURL_DISABLE_HTTP
+static CURLcode setopt_HTTP_VERSION(struct Curl_easy *data, long arg)
+{
+ /*
+ * This sets a requested HTTP version to be used. The value is one of
+ * the listed enums in curl/curl.h.
+ */
+ switch(arg) {
+ case CURL_HTTP_VERSION_NONE:
+ /* accepted */
+ break;
+ case CURL_HTTP_VERSION_1_0:
+ case CURL_HTTP_VERSION_1_1:
+ /* accepted */
+ break;
+#ifdef USE_HTTP2
+ case CURL_HTTP_VERSION_2_0:
+ case CURL_HTTP_VERSION_2TLS:
+ case CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE:
+ /* accepted */
+ break;
+#endif
+#ifdef USE_HTTP3
+ case CURL_HTTP_VERSION_3:
+ case CURL_HTTP_VERSION_3ONLY:
+ /* accepted */
+ break;
+#endif
+ default:
+ /* not accepted */
+ if(arg < CURL_HTTP_VERSION_NONE)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ return CURLE_UNSUPPORTED_PROTOCOL;
+ }
+ data->set.httpwant = (unsigned char)arg;
+ return CURLE_OK;
+}
+#endif /* ! CURL_DISABLE_HTTP */
+
+#ifdef USE_SSL
+static CURLcode setopt_SSLVERSION(struct Curl_easy *data, CURLoption option,
+ long arg)
+{
+ /*
+ * Set explicit SSL version to try to connect with, as some SSL
+ * implementations are lame.
+ */
+ {
+ long version, version_max;
+ struct ssl_primary_config *primary = &data->set.ssl.primary;
+#ifndef CURL_DISABLE_PROXY
+ if(option != CURLOPT_SSLVERSION)
+ primary = &data->set.proxy_ssl.primary;
+#else
+ if(option) {}
+#endif
+ version = C_SSLVERSION_VALUE(arg);
+ version_max = (long)C_SSLVERSION_MAX_VALUE(arg);
+
+ if(version < CURL_SSLVERSION_DEFAULT ||
+ version == CURL_SSLVERSION_SSLv2 ||
+ version == CURL_SSLVERSION_SSLv3 ||
+ version >= CURL_SSLVERSION_LAST ||
+ version_max < CURL_SSLVERSION_MAX_NONE ||
+ version_max >= CURL_SSLVERSION_MAX_LAST)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ primary->version = (unsigned char)version;
+ primary->version_max = (unsigned int)version_max;
+ }
+ return CURLE_OK;
+}
+#endif /* ! USE_SSL */
+
+#ifndef CURL_DISABLE_RTSP
+static CURLcode setopt_RTSP_REQUEST(struct Curl_easy *data, long arg)
+{
+ /*
+ * Set the RTSP request method (OPTIONS, SETUP, PLAY, etc...)
+ * Would this be better if the RTSPREQ_* were just moved into here?
+ */
+ Curl_RtspReq rtspreq = RTSPREQ_NONE;
+ switch(arg) {
+ case CURL_RTSPREQ_OPTIONS:
+ rtspreq = RTSPREQ_OPTIONS;
+ break;
+
+ case CURL_RTSPREQ_DESCRIBE:
+ rtspreq = RTSPREQ_DESCRIBE;
+ break;
+
+ case CURL_RTSPREQ_ANNOUNCE:
+ rtspreq = RTSPREQ_ANNOUNCE;
+ break;
+
+ case CURL_RTSPREQ_SETUP:
+ rtspreq = RTSPREQ_SETUP;
+ break;
+
+ case CURL_RTSPREQ_PLAY:
+ rtspreq = RTSPREQ_PLAY;
+ break;
+
+ case CURL_RTSPREQ_PAUSE:
+ rtspreq = RTSPREQ_PAUSE;
+ break;
+
+ case CURL_RTSPREQ_TEARDOWN:
+ rtspreq = RTSPREQ_TEARDOWN;
+ break;
+
+ case CURL_RTSPREQ_GET_PARAMETER:
+ rtspreq = RTSPREQ_GET_PARAMETER;
+ break;
+
+ case CURL_RTSPREQ_SET_PARAMETER:
+ rtspreq = RTSPREQ_SET_PARAMETER;
+ break;
+
+ case CURL_RTSPREQ_RECORD:
+ rtspreq = RTSPREQ_RECORD;
+ break;
+
+ case CURL_RTSPREQ_RECEIVE:
+ rtspreq = RTSPREQ_RECEIVE;
+ break;
+ default:
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+
+ data->set.rtspreq = rtspreq;
+ return CURLE_OK;
+}
+#endif /* ! CURL_DISABLE_RTSP */
+
+#ifdef USE_SSL
+static void set_ssl_options(struct ssl_config_data *ssl,
+ struct ssl_primary_config *config,
+ long arg)
+{
+ config->ssl_options = (unsigned char)(arg & 0xff);
+ ssl->enable_beast = !!(arg & CURLSSLOPT_ALLOW_BEAST);
+ ssl->no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE);
+ ssl->no_partialchain = !!(arg & CURLSSLOPT_NO_PARTIALCHAIN);
+ ssl->revoke_best_effort = !!(arg & CURLSSLOPT_REVOKE_BEST_EFFORT);
+ ssl->native_ca_store = !!(arg & CURLSSLOPT_NATIVE_CA);
+ ssl->auto_client_cert = !!(arg & CURLSSLOPT_AUTO_CLIENT_CERT);
+ ssl->earlydata = !!(arg & CURLSSLOPT_EARLYDATA);
+}
+#endif
+
static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
long arg)
{
@@ -290,7 +442,7 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
return CURLE_BAD_FUNCTION_ARGUMENT;
data->set.maxconnects = (unsigned int)uarg;
break;
- case CURLOPT_FORBID_REUSE:
+ case CURLOPT_FORBID_REUSE:
/*
* When this transfer is done, it must not be left to be reused by a
* subsequent transfer but shall be closed immediately.
@@ -321,11 +473,7 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
/*
* Shut off the internal supported progress meter
*/
- data->set.hide_progress = enabled;
- if(data->set.hide_progress)
- data->progress.flags |= PGRS_HIDE;
- else
- data->progress.flags &= ~PGRS_HIDE;
+ data->progress.hide = enabled;
break;
case CURLOPT_NOBODY:
/*
@@ -438,7 +586,7 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
*/
if((arg < CURL_TIMECOND_NONE) || (arg >= CURL_TIMECOND_LAST))
return CURLE_BAD_FUNCTION_ARGUMENT;
- data->set.timecondition = (unsigned char)(curl_TimeCond)arg;
+ data->set.timecondition = (unsigned char)arg;
break;
case CURLOPT_TIMEVALUE:
/*
@@ -451,36 +599,12 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_SSLVERSION:
#endif
- /*
- * Set explicit SSL version to try to connect with, as some SSL
- * implementations are lame.
- */
#ifdef USE_SSL
- {
- long version, version_max;
- struct ssl_primary_config *primary = &data->set.ssl.primary;
-#ifndef CURL_DISABLE_PROXY
- if(option != CURLOPT_SSLVERSION)
- primary = &data->set.proxy_ssl.primary;
-#endif
- version = C_SSLVERSION_VALUE(arg);
- version_max = (long)C_SSLVERSION_MAX_VALUE(arg);
-
- if(version < CURL_SSLVERSION_DEFAULT ||
- version == CURL_SSLVERSION_SSLv2 ||
- version == CURL_SSLVERSION_SSLv3 ||
- version >= CURL_SSLVERSION_LAST ||
- version_max < CURL_SSLVERSION_MAX_NONE ||
- version_max >= CURL_SSLVERSION_MAX_LAST)
- return CURLE_BAD_FUNCTION_ARGUMENT;
-
- primary->version = (unsigned char)version;
- primary->version_max = (unsigned int)version_max;
- }
+ return setopt_SSLVERSION(data, option, arg);
#else
return CURLE_NOT_BUILT_IN;
#endif
- break;
+
case CURLOPT_POSTFIELDSIZE:
/*
* The size of the POSTFIELD data to prevent libcurl to do strlen() to
@@ -525,7 +649,9 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
/*
* Follow Location: header hints on an HTTP-server.
*/
- data->set.http_follow_location = enabled;
+ if(uarg > 3)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ data->set.http_follow_mode = (unsigned char)uarg;
break;
case CURLOPT_UNRESTRICTED_AUTH:
@@ -594,44 +720,7 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
break;
case CURLOPT_HTTP_VERSION:
- /*
- * This sets a requested HTTP version to be used. The value is one of
- * the listed enums in curl/curl.h.
- */
- switch(arg) {
- case CURL_HTTP_VERSION_NONE:
-#ifdef USE_HTTP2
- /* TODO: this seems an undesirable quirk to force a behaviour on
- * lower implementations that they should recognize independently? */
- arg = CURL_HTTP_VERSION_2TLS;
-#endif
- /* accepted */
- break;
- case CURL_HTTP_VERSION_1_0:
- case CURL_HTTP_VERSION_1_1:
- /* accepted */
- break;
-#ifdef USE_HTTP2
- case CURL_HTTP_VERSION_2_0:
- case CURL_HTTP_VERSION_2TLS:
- case CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE:
- /* accepted */
- break;
-#endif
-#ifdef USE_HTTP3
- case CURL_HTTP_VERSION_3:
- case CURL_HTTP_VERSION_3ONLY:
- /* accepted */
- break;
-#endif
- default:
- /* not accepted */
- if(arg < CURL_HTTP_VERSION_NONE)
- return CURLE_BAD_FUNCTION_ARGUMENT;
- return CURLE_UNSUPPORTED_PROTOCOL;
- }
- data->set.httpwant = (unsigned char)arg;
- break;
+ return setopt_HTTP_VERSION(data, arg);
case CURLOPT_EXPECT_100_TIMEOUT_MS:
/*
@@ -644,13 +733,7 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
break;
case CURLOPT_HTTP09_ALLOWED:
-#ifdef USE_HYPER
- /* Hyper does not support HTTP/0.9 */
- if(enabled)
- return CURLE_BAD_FUNCTION_ARGUMENT;
-#else
data->set.http09_allowed = enabled;
-#endif
break;
#endif /* ! CURL_DISABLE_HTTP */
@@ -698,7 +781,7 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
data->set.proxy_transfer_mode = (bool)uarg;
break;
case CURLOPT_SOCKS5_AUTH:
- if(data->set.socks5auth & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI))
+ if(uarg & ~(CURLAUTH_BASIC | CURLAUTH_GSSAPI))
return CURLE_NOT_BUILT_IN;
data->set.socks5auth = (unsigned char)uarg;
break;
@@ -923,6 +1006,7 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
break;
#endif
+#ifdef HAVE_GSSAPI
case CURLOPT_GSSAPI_DELEGATION:
/*
* GSS-API credential delegation bitmask
@@ -930,6 +1014,7 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
data->set.gssapi_delegation = (unsigned char)uarg&
(CURLGSSAPI_DELEGATION_POLICY_FLAG|CURLGSSAPI_DELEGATION_FLAG);
break;
+#endif
case CURLOPT_SSL_VERIFYPEER:
/*
* Enable peer SSL verifying.
@@ -991,7 +1076,7 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
/*
* Enable TLS false start.
*/
- if(!Curl_ssl_false_start(data))
+ if(!Curl_ssl_false_start())
return CURLE_NOT_BUILT_IN;
data->set.ssl.falsestart = enabled;
@@ -1058,29 +1143,12 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
data->set.use_ssl = (unsigned char)arg;
break;
case CURLOPT_SSL_OPTIONS:
- data->set.ssl.primary.ssl_options = (unsigned char)(arg & 0xff);
- data->set.ssl.enable_beast = !!(arg & CURLSSLOPT_ALLOW_BEAST);
- data->set.ssl.no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE);
- data->set.ssl.no_partialchain = !!(arg & CURLSSLOPT_NO_PARTIALCHAIN);
- data->set.ssl.revoke_best_effort = !!(arg & CURLSSLOPT_REVOKE_BEST_EFFORT);
- data->set.ssl.native_ca_store = !!(arg & CURLSSLOPT_NATIVE_CA);
- data->set.ssl.auto_client_cert = !!(arg & CURLSSLOPT_AUTO_CLIENT_CERT);
- data->set.ssl.earlydata = !!(arg & CURLSSLOPT_EARLYDATA);
- /* If a setting is added here it should also be added in dohprobe()
- which sets its own CURLOPT_SSL_OPTIONS based on these settings. */
+ set_ssl_options(&data->set.ssl, &data->set.ssl.primary, arg);
break;
#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_SSL_OPTIONS:
- data->set.proxy_ssl.primary.ssl_options = (unsigned char)(arg & 0xff);
- data->set.proxy_ssl.enable_beast = !!(arg & CURLSSLOPT_ALLOW_BEAST);
- data->set.proxy_ssl.no_revoke = !!(arg & CURLSSLOPT_NO_REVOKE);
- data->set.proxy_ssl.no_partialchain = !!(arg & CURLSSLOPT_NO_PARTIALCHAIN);
- data->set.proxy_ssl.revoke_best_effort =
- !!(arg & CURLSSLOPT_REVOKE_BEST_EFFORT);
- data->set.proxy_ssl.native_ca_store = !!(arg & CURLSSLOPT_NATIVE_CA);
- data->set.proxy_ssl.auto_client_cert =
- !!(arg & CURLSSLOPT_AUTO_CLIENT_CERT);
+ set_ssl_options(&data->set.proxy_ssl, &data->set.proxy_ssl.primary, arg);
break;
#endif
@@ -1110,7 +1178,8 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
*/
if(arg > 2)
return CURLE_BAD_FUNCTION_ARGUMENT;
- data->set.connect_only = (unsigned char)arg;
+ data->set.connect_only = !!arg;
+ data->set.connect_only_ws = (arg == 2);
break;
case CURLOPT_SSL_SESSIONID_CACHE:
@@ -1135,18 +1204,14 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
/*
* disable libcurl transfer encoding is used
*/
-#ifndef USE_HYPER
data->set.http_te_skip = !enabled; /* reversed */
break;
-#else
- return CURLE_NOT_BUILT_IN; /* hyper does not support */
-#endif
case CURLOPT_HTTP_CONTENT_DECODING:
/*
* raw data passed to the application when content encoding is used
*/
- data->set.http_ce_skip = enabled;
+ data->set.http_ce_skip = !enabled; /* reversed */
break;
#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH)
@@ -1210,63 +1275,7 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
break;
#ifndef CURL_DISABLE_RTSP
case CURLOPT_RTSP_REQUEST:
- {
- /*
- * Set the RTSP request method (OPTIONS, SETUP, PLAY, etc...)
- * Would this be better if the RTSPREQ_* were just moved into here?
- */
- Curl_RtspReq rtspreq = RTSPREQ_NONE;
- switch(arg) {
- case CURL_RTSPREQ_OPTIONS:
- rtspreq = RTSPREQ_OPTIONS;
- break;
-
- case CURL_RTSPREQ_DESCRIBE:
- rtspreq = RTSPREQ_DESCRIBE;
- break;
-
- case CURL_RTSPREQ_ANNOUNCE:
- rtspreq = RTSPREQ_ANNOUNCE;
- break;
-
- case CURL_RTSPREQ_SETUP:
- rtspreq = RTSPREQ_SETUP;
- break;
-
- case CURL_RTSPREQ_PLAY:
- rtspreq = RTSPREQ_PLAY;
- break;
-
- case CURL_RTSPREQ_PAUSE:
- rtspreq = RTSPREQ_PAUSE;
- break;
-
- case CURL_RTSPREQ_TEARDOWN:
- rtspreq = RTSPREQ_TEARDOWN;
- break;
-
- case CURL_RTSPREQ_GET_PARAMETER:
- rtspreq = RTSPREQ_GET_PARAMETER;
- break;
-
- case CURL_RTSPREQ_SET_PARAMETER:
- rtspreq = RTSPREQ_SET_PARAMETER;
- break;
-
- case CURL_RTSPREQ_RECORD:
- rtspreq = RTSPREQ_RECORD;
- break;
-
- case CURL_RTSPREQ_RECEIVE:
- rtspreq = RTSPREQ_RECEIVE;
- break;
- default:
- return CURLE_BAD_FUNCTION_ARGUMENT;
- }
-
- data->set.rtspreq = rtspreq;
- break;
- }
+ return setopt_RTSP_REQUEST(data, arg);
case CURLOPT_RTSP_CLIENT_CSEQ:
/*
* Set the CSEQ number to issue for the next RTSP request. Useful if the
@@ -1394,7 +1403,8 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
#endif /* ! CURL_DISABLE_ALTSVC */
#ifndef CURL_DISABLE_WEBSOCKETS
case CURLOPT_WS_OPTIONS:
- data->set.ws_raw_mode = (bool)(arg & CURLWS_RAW_MODE);
+ data->set.ws_raw_mode = (bool)(arg & CURLWS_RAW_MODE);
+ data->set.ws_no_auto_pong = (bool)(arg & CURLWS_NOAUTOPONG);
break;
#endif
case CURLOPT_QUICK_EXIT:
@@ -1409,7 +1419,9 @@ static CURLcode setopt_long(struct Curl_easy *data, CURLoption option,
*/
Curl_safefree(data->set.str[STRING_SSL_ENGINE]);
return Curl_ssl_set_engine_default(data);
-
+ case CURLOPT_UPLOAD_FLAGS:
+ data->set.upload_flags = (unsigned char)arg;
+ break;
default:
/* unknown option */
return CURLE_UNKNOWN_OPTION;
@@ -1537,7 +1549,7 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option,
#if !defined(CURL_DISABLE_HTTP) || !defined(CURL_DISABLE_SMTP) || \
!defined(CURL_DISABLE_IMAP)
# ifndef CURL_DISABLE_MIME
- case CURLOPT_MIMEPOST:
+ case CURLOPT_MIMEPOST:
/*
* Set to make us do MIME POST
*/
@@ -1573,11 +1585,6 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option,
if(data->share) {
Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
- if(data->dns.hostcachetype == HCACHE_SHARED) {
- data->dns.hostcache = NULL;
- data->dns.hostcachetype = HCACHE_NONE;
- }
-
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
if(data->share->cookies == data->cookies)
data->cookies = NULL;
@@ -1587,14 +1594,14 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option,
if(data->share->hsts == data->hsts)
data->hsts = NULL;
#endif
-#ifdef USE_SSL
- if(data->share->sslsession == data->state.session)
- data->state.session = NULL;
-#endif
#ifdef USE_LIBPSL
if(data->psl == &data->share->psl)
data->psl = data->multi ? &data->multi->psl : NULL;
#endif
+ if(data->share->specifier & (1 << CURL_LOCK_DATA_DNS)) {
+ Curl_resolv_unlink(data, &data->state.dns[0]);
+ Curl_resolv_unlink(data, &data->state.dns[1]);
+ }
data->share->dirty--;
@@ -1611,11 +1618,6 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option,
data->share->dirty++;
- if(data->share->specifier & (1 << CURL_LOCK_DATA_DNS)) {
- /* use shared host cache */
- data->dns.hostcache = &data->share->hostcache;
- data->dns.hostcachetype = HCACHE_SHARED;
- }
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
if(data->share->cookies) {
/* use shared cookie list, first free own one if any */
@@ -1631,12 +1633,6 @@ static CURLcode setopt_pointers(struct Curl_easy *data, CURLoption option,
data->hsts = data->share->hsts;
}
#endif
-#ifdef USE_SSL
- if(data->share->sslsession) {
- data->set.general_ssl.max_ssl_sessions = data->share->max_ssl_sessions;
- data->state.session = data->share->sslsession;
- }
-#endif
#ifdef USE_LIBPSL
if(data->share->specifier & (1 << CURL_LOCK_DATA_PSL))
data->psl = &data->share->psl;
@@ -1675,8 +1671,8 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
if(Curl_ssl_supports(data, SSLSUPP_CIPHER_LIST))
/* set a list of cipher we want to use in the SSL connection */
return Curl_setstropt(&data->set.str[STRING_SSL_CIPHER_LIST], ptr);
- return CURLE_NOT_BUILT_IN;
- break;
+ else
+ return CURLE_NOT_BUILT_IN;
#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_SSL_CIPHER_LIST:
if(Curl_ssl_supports(data, SSLSUPP_CIPHER_LIST)) {
@@ -1686,7 +1682,6 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
}
else
return CURLE_NOT_BUILT_IN;
- break;
#endif
case CURLOPT_TLS13_CIPHERS:
if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES)) {
@@ -1695,7 +1690,6 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
}
else
return CURLE_NOT_BUILT_IN;
- break;
#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_TLS13_CIPHERS:
if(Curl_ssl_supports(data, SSLSUPP_TLS13_CIPHERSUITES))
@@ -1704,7 +1698,6 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
ptr);
else
return CURLE_NOT_BUILT_IN;
- break;
#endif
case CURLOPT_RANDOM_FILE:
break;
@@ -1768,6 +1761,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
Curl_safefree(data->set.str[STRING_COPYPOSTFIELDS]);
data->set.method = HTTPREQ_POST;
break;
+#endif /* ! CURL_DISABLE_HTTP || ! CURL_DISABLE_MQTT */
#ifndef CURL_DISABLE_HTTP
case CURLOPT_ACCEPT_ENCODING:
@@ -1956,7 +1950,6 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
* to decide for us (if CURLOPT_SOCKS_PROXY setting it to NULL).
*/
return Curl_setstropt(&data->set.str[STRING_PROXY], ptr);
- break;
case CURLOPT_PRE_PROXY:
/*
@@ -1981,95 +1974,95 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
* Set authentication service name for DIGEST-MD5, Kerberos 5 and SPNEGO
*/
return Curl_setstropt(&data->set.str[STRING_SERVICE_NAME], ptr);
- break;
case CURLOPT_HEADERDATA:
/*
* Custom pointer to pass the header write callback function
*/
- data->set.writeheader = (void *)ptr;
+ data->set.writeheader = ptr;
break;
case CURLOPT_READDATA:
/*
* FILE pointer to read the file to be uploaded from. Or possibly used as
* argument to the read callback.
*/
- data->set.in_set = (void *)ptr;
+ data->set.in_set = ptr;
break;
case CURLOPT_WRITEDATA:
/*
* FILE pointer to write to. Or possibly used as argument to the write
* callback.
*/
- data->set.out = (void *)ptr;
+ data->set.out = ptr;
break;
case CURLOPT_DEBUGDATA:
/*
* Set to a void * that should receive all error writes. This
* defaults to CURLOPT_STDERR for normal operations.
*/
- data->set.debugdata = (void *)ptr;
+ data->set.debugdata = ptr;
break;
case CURLOPT_PROGRESSDATA:
/*
* Custom client data to pass to the progress callback
*/
- data->set.progress_client = (void *)ptr;
+ data->set.progress_client = ptr;
break;
case CURLOPT_SEEKDATA:
/*
* Seek control callback. Might be NULL.
*/
- data->set.seek_client = (void *)ptr;
+ data->set.seek_client = ptr;
break;
case CURLOPT_IOCTLDATA:
/*
* I/O control data pointer. Might be NULL.
*/
- data->set.ioctl_client = (void *)ptr;
+ data->set.ioctl_client = ptr;
break;
case CURLOPT_SSL_CTX_DATA:
/*
- * Set a SSL_CTX callback parameter pointer
+ * Set an SSL_CTX callback parameter pointer
*/
#ifdef USE_SSL
- if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX))
- data->set.ssl.fsslctxp = (void *)ptr;
+ if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX)) {
+ data->set.ssl.fsslctxp = ptr;
+ break;
+ }
else
#endif
return CURLE_NOT_BUILT_IN;
- break;
case CURLOPT_SOCKOPTDATA:
/*
* socket callback data pointer. Might be NULL.
*/
- data->set.sockopt_client = (void *)ptr;
+ data->set.sockopt_client = ptr;
break;
case CURLOPT_OPENSOCKETDATA:
/*
* socket callback data pointer. Might be NULL.
*/
- data->set.opensocket_client = (void *)ptr;
+ data->set.opensocket_client = ptr;
break;
case CURLOPT_RESOLVER_START_DATA:
/*
* resolver start callback data pointer. Might be NULL.
*/
- data->set.resolver_start_client = (void *)ptr;
+ data->set.resolver_start_client = ptr;
break;
case CURLOPT_CLOSESOCKETDATA:
/*
* socket callback data pointer. Might be NULL.
*/
- data->set.closesocket_client = (void *)ptr;
+ data->set.closesocket_client = ptr;
break;
case CURLOPT_TRAILERDATA:
#ifndef CURL_DISABLE_HTTP
- data->set.trailer_data = (void *)ptr;
+ data->set.trailer_data = ptr;
#endif
break;
case CURLOPT_PREREQDATA:
- data->set.prereq_userp = (void *)ptr;
+ data->set.prereq_userp = ptr;
break;
case CURLOPT_ERRORBUFFER:
@@ -2110,7 +2103,6 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
* The URL to fetch.
*/
if(data->state.url_alloc) {
- /* the already set URL is allocated, free it first! */
Curl_safefree(data->state.url);
data->state.url_alloc = FALSE;
}
@@ -2159,12 +2151,16 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
result = setstropt_userpwd(ptr, &u, &p);
/* URL decode the components */
- if(!result && u)
+ if(!result && u) {
+ Curl_safefree(data->set.str[STRING_PROXYUSERNAME]);
result = Curl_urldecode(u, 0, &data->set.str[STRING_PROXYUSERNAME], NULL,
REJECT_ZERO);
- if(!result && p)
+ }
+ if(!result && p) {
+ Curl_safefree(data->set.str[STRING_PROXYPASSWORD]);
result = Curl_urldecode(p, 0, &data->set.str[STRING_PROXYPASSWORD], NULL,
REJECT_ZERO);
+ }
free(u);
free(p);
}
@@ -2186,7 +2182,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
* proxy exception list
*/
return Curl_setstropt(&data->set.str[STRING_NOPROXY], ptr);
-#endif
+#endif /* ! CURL_DISABLE_PROXY */
case CURLOPT_RANGE:
/*
@@ -2194,11 +2190,17 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
*/
return Curl_setstropt(&data->set.str[STRING_SET_RANGE], ptr);
-#endif /* ! CURL_DISABLE_PROXY */
case CURLOPT_CURLU:
/*
* pass CURLU to set URL
*/
+ if(data->state.url_alloc) {
+ Curl_safefree(data->state.url);
+ data->state.url_alloc = FALSE;
+ }
+ else
+ data->state.url = NULL;
+ Curl_safefree(data->set.str[STRING_SET_URL]);
data->set.uh = (CURLU *)ptr;
break;
case CURLOPT_SSLCERT:
@@ -2227,6 +2229,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
* String that holds file type of the SSL certificate to use for proxy
*/
return Curl_setstropt(&data->set.str[STRING_CERT_TYPE_PROXY], ptr);
+
#endif
case CURLOPT_SSLKEY:
/*
@@ -2247,7 +2250,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
* String that holds file type of the SSL key to use
*/
return Curl_setstropt(&data->set.str[STRING_KEY_TYPE], ptr);
- break;
+
#ifndef CURL_DISABLE_PROXY
case CURLOPT_PROXY_SSLKEYTYPE:
/*
@@ -2268,6 +2271,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
* String that holds the SSL private key password for proxy.
*/
return Curl_setstropt(&data->set.str[STRING_KEY_PASSWD_PROXY], ptr);
+
#endif
case CURLOPT_SSLENGINE:
/*
@@ -2290,6 +2294,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
/* enable the HAProxy protocol */
data->set.haproxyprotocol = TRUE;
break;
+
#endif
case CURLOPT_INTERFACE:
/*
@@ -2338,8 +2343,8 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
* CA certificate
*/
return Curl_setstropt(&data->set.str[STRING_SSL_CAFILE_PROXY], ptr);
-#endif
+#endif
case CURLOPT_CAPATH:
/*
* Set CA path info for SSL connection. Specify directory name of the CA
@@ -2378,6 +2383,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
* CRL to check certificates revocation
*/
return Curl_setstropt(&data->set.str[STRING_SSL_CRLFILE_PROXY], ptr);
+
#endif
case CURLOPT_ISSUERCERT:
/*
@@ -2395,12 +2401,11 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
return Curl_setstropt(&data->set.str[STRING_SSL_ISSUERCERT_PROXY], ptr);
#endif
-
case CURLOPT_PRIVATE:
/*
* Set private data pointer.
*/
- data->set.private_data = (void *)ptr;
+ data->set.private_data = ptr;
break;
#ifdef USE_SSL
@@ -2410,6 +2415,16 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
* Specify colon-delimited list of curve algorithm names.
*/
return Curl_setstropt(&data->set.str[STRING_SSL_EC_CURVES], ptr);
+
+ case CURLOPT_SSL_SIGNATURE_ALGORITHMS:
+ /*
+ * Set accepted signature algorithms.
+ * Specify colon-delimited list of signature scheme names.
+ */
+ if(Curl_ssl_supports(data, SSLSUPP_SIGNATURE_ALGORITHMS))
+ return Curl_setstropt(&data->set.str[STRING_SSL_SIGNATURE_ALGORITHMS],
+ ptr);
+ return CURLE_NOT_BUILT_IN;
#endif
#ifdef USE_SSH
case CURLOPT_SSH_PUBLIC_KEYFILE:
@@ -2424,6 +2439,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
*/
return Curl_setstropt(&data->set.str[STRING_SSH_PRIVATE_KEY], ptr);
+#if defined(USE_LIBSSH2) || defined(USE_LIBSSH)
case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5:
/*
* Option to allow for the MD5 of the host public key to be checked
@@ -2436,12 +2452,12 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
* Store the filename to read known hosts from.
*/
return Curl_setstropt(&data->set.str[STRING_SSH_KNOWNHOSTS], ptr);
-
+#endif
case CURLOPT_SSH_KEYDATA:
/*
* Custom client data to pass to the SSH keyfunc callback
*/
- data->set.ssh_keyfunc_userp = (void *)ptr;
+ data->set.ssh_keyfunc_userp = ptr;
break;
#ifdef USE_LIBSSH2
case CURLOPT_SSH_HOST_PUBLIC_KEY_SHA256:
@@ -2456,7 +2472,7 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
/*
* Custom client data to pass to the SSH keyfunc callback
*/
- data->set.ssh_hostkeyfunc_userp = (void *)ptr;
+ data->set.ssh_hostkeyfunc_userp = ptr;
break;
#endif /* USE_LIBSSH2 */
#endif /* USE_SSH */
@@ -2487,7 +2503,6 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
/* Set the SMTP auth originator */
return Curl_setstropt(&data->set.str[STRING_MAIL_AUTH], ptr);
#endif
-
case CURLOPT_SASL_AUTHZID:
/* Authorization identity (identity to act as) */
return Curl_setstropt(&data->set.str[STRING_SASL_AUTHZID], ptr);
@@ -2506,7 +2521,6 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
* for generic server options, the application will need to set this.
*/
return Curl_setstropt(&data->set.str[STRING_RTSP_STREAM_URI], ptr);
- break;
case CURLOPT_RTSP_TRANSPORT:
/*
@@ -2515,15 +2529,15 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
return Curl_setstropt(&data->set.str[STRING_RTSP_TRANSPORT], ptr);
case CURLOPT_INTERLEAVEDATA:
- data->set.rtp_out = (void *)ptr;
+ data->set.rtp_out = ptr;
break;
#endif /* ! CURL_DISABLE_RTSP */
#ifndef CURL_DISABLE_FTP
case CURLOPT_CHUNK_DATA:
- data->set.wildcardptr = (void *)ptr;
+ data->set.wildcardptr = ptr;
break;
case CURLOPT_FNMATCH_DATA:
- data->set.fnmatch_data = (void *)ptr;
+ data->set.fnmatch_data = ptr;
break;
#endif
#ifdef USE_TLS_SRP
@@ -2553,30 +2567,30 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
break;
#endif
#endif
-#ifdef USE_ARES
+#ifdef CURLRES_ARES
case CURLOPT_DNS_SERVERS:
result = Curl_setstropt(&data->set.str[STRING_DNS_SERVERS], ptr);
if(result)
return result;
- return Curl_set_dns_servers(data, data->set.str[STRING_DNS_SERVERS]);
+ return Curl_async_ares_set_dns_servers(data);
case CURLOPT_DNS_INTERFACE:
result = Curl_setstropt(&data->set.str[STRING_DNS_INTERFACE], ptr);
if(result)
return result;
- return Curl_set_dns_interface(data, data->set.str[STRING_DNS_INTERFACE]);
+ return Curl_async_ares_set_dns_interface(data);
case CURLOPT_DNS_LOCAL_IP4:
result = Curl_setstropt(&data->set.str[STRING_DNS_LOCAL_IP4], ptr);
if(result)
return result;
- return Curl_set_dns_local_ip4(data, data->set.str[STRING_DNS_LOCAL_IP4]);
+ return Curl_async_ares_set_dns_local_ip4(data);
case CURLOPT_DNS_LOCAL_IP6:
result = Curl_setstropt(&data->set.str[STRING_DNS_LOCAL_IP6], ptr);
if(result)
return result;
- return Curl_set_dns_local_ip6(data, data->set.str[STRING_DNS_LOCAL_IP6]);
+ return Curl_async_ares_set_dns_local_ip6(data);
#endif
#ifdef USE_UNIX_SOCKETS
@@ -2598,10 +2612,10 @@ static CURLcode setopt_cptr(struct Curl_easy *data, CURLoption option,
#endif
#ifndef CURL_DISABLE_HSTS
case CURLOPT_HSTSREADDATA:
- data->set.hsts_read_userp = (void *)ptr;
+ data->set.hsts_read_userp = ptr;
break;
case CURLOPT_HSTSWRITEDATA:
- data->set.hsts_write_userp = (void *)ptr;
+ data->set.hsts_write_userp = ptr;
break;
case CURLOPT_HSTS: {
struct curl_slist *h;
@@ -2773,15 +2787,16 @@ static CURLcode setopt_func(struct Curl_easy *data, CURLoption option,
break;
case CURLOPT_SSL_CTX_FUNCTION:
/*
- * Set a SSL_CTX callback
+ * Set an SSL_CTX callback
*/
#ifdef USE_SSL
- if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX))
+ if(Curl_ssl_supports(data, SSLSUPP_SSL_CTX)) {
data->set.ssl.fsslctx = va_arg(param, curl_ssl_ctx_callback);
+ break;
+ }
else
#endif
return CURLE_NOT_BUILT_IN;
- break;
case CURLOPT_SOCKOPTFUNCTION:
/*
@@ -2814,7 +2829,6 @@ static CURLcode setopt_func(struct Curl_easy *data, CURLoption option,
data->set.resolver_start = va_arg(param, curl_resolver_start_callback);
break;
-
#ifdef USE_SSH
#ifdef USE_LIBSSH2
case CURLOPT_SSH_HOSTKEYFUNCTION:
@@ -3079,9 +3093,7 @@ CURLcode curl_easy_setopt(CURL *d, CURLoption tag, ...)
result = Curl_vsetopt(data, tag, arg);
va_end(arg);
-#ifdef DEBUGBUILD
if(result == CURLE_BAD_FUNCTION_ARGUMENT)
- infof(data, "setopt arg 0x%x returned CURLE_BAD_FUNCTION_ARGUMENT", tag);
-#endif
+ failf(data, "setopt 0x%x got bad argument", tag);
return result;
}
diff --git a/libs/libcurl/src/setup-vms.h b/libs/libcurl/src/setup-vms.h
index 98cd6eb979..509766ff08 100644
--- a/libs/libcurl/src/setup-vms.h
+++ b/libs/libcurl/src/setup-vms.h
@@ -370,7 +370,7 @@ static struct passwd *vms_getpwuid(uid_t uid)
#define USE_UPPERCASE_KRBAPI 1
-/* AI_NUMERICHOST needed for IP V6 support in Curl */
+/* AI_NUMERICHOST needed for IP V6 support in curl */
#ifdef HAVE_NETDB_H
#include <netdb.h>
#ifndef AI_NUMERICHOST
@@ -394,51 +394,11 @@ static struct passwd *vms_getpwuid(uid_t uid)
/* that way a newer port will also work if some one has one */
#ifdef __VAX
-# if (OPENSSL_VERSION_NUMBER < 0x00907001L)
-# define des_set_odd_parity DES_SET_ODD_PARITY
-# define des_set_key DES_SET_KEY
-# define des_ecb_encrypt DES_ECB_ENCRYPT
-
-# endif
# include <openssl/evp.h>
# ifndef OpenSSL_add_all_algorithms
# define OpenSSL_add_all_algorithms OPENSSL_ADD_ALL_ALGORITHMS
void OPENSSL_ADD_ALL_ALGORITHMS(void);
# endif
-
- /* Curl defines these to lower case and VAX needs them in upper case */
- /* So we need static routines */
-# if (OPENSSL_VERSION_NUMBER < 0x00907001L)
-
-# undef des_set_odd_parity
-# undef DES_set_odd_parity
-# undef des_set_key
-# undef DES_set_key
-# undef des_ecb_encrypt
-# undef DES_ecb_encrypt
-
- static void des_set_odd_parity(des_cblock *key) {
- DES_SET_ODD_PARITY(key);
- }
-
- static int des_set_key(const_des_cblock *key,
- des_key_schedule schedule) {
- return DES_SET_KEY(key, schedule);
- }
-
- static void des_ecb_encrypt(const_des_cblock *input,
- des_cblock *output,
- des_key_schedule ks, int enc) {
- DES_ECB_ENCRYPT(input, output, ks, enc);
- }
-#endif
-/* Need this to stop a macro redefinition error */
-#if OPENSSL_VERSION_NUMBER < 0x00907000L
-# ifdef X509_STORE_set_flags
-# undef X509_STORE_set_flags
-# define X509_STORE_set_flags(x,y) Curl_nop_stmt
-# endif
-#endif
#endif
#endif /* HEADER_CURL_SETUP_VMS_H */
diff --git a/libs/libcurl/src/setup-win32.h b/libs/libcurl/src/setup-win32.h
index 3d16dce8fc..3f8a28ef67 100644
--- a/libs/libcurl/src/setup-win32.h
+++ b/libs/libcurl/src/setup-win32.h
@@ -77,17 +77,6 @@
# if defined(_UNICODE) && !defined(UNICODE)
# error "_UNICODE is defined but UNICODE is not defined"
# endif
-/*
- * Do not include unneeded stuff in Windows headers to avoid compiler
- * warnings and macro clashes.
- * Make sure to define this macro before including any Windows headers.
- */
-# ifndef WIN32_LEAN_AND_MEAN
-# define WIN32_LEAN_AND_MEAN
-# endif
-# ifndef NOGDI
-# define NOGDI
-# endif
# include <winsock2.h>
# include <ws2tcpip.h>
# include <windows.h>
@@ -104,12 +93,6 @@
* newer symbols.
*/
-#ifndef _WIN32_WINNT_NT4
-#define _WIN32_WINNT_NT4 0x0400 /* Windows NT 4.0 */
-#endif
-#ifndef _WIN32_WINNT_WIN2K
-#define _WIN32_WINNT_WIN2K 0x0500 /* Windows 2000 */
-#endif
#ifndef _WIN32_WINNT_WINXP
#define _WIN32_WINNT_WINXP 0x0501 /* Windows XP */
#endif
diff --git a/libs/libcurl/src/sha256.c b/libs/libcurl/src/sha256.c
index 6074adb644..f762c195d8 100644
--- a/libs/libcurl/src/sha256.c
+++ b/libs/libcurl/src/sha256.c
@@ -26,47 +26,22 @@
#include "curl_setup.h"
#if !defined(CURL_DISABLE_AWS) || !defined(CURL_DISABLE_DIGEST_AUTH) \
- || defined(USE_LIBSSH2)
+ || defined(USE_LIBSSH2) || defined(USE_SSL)
-#include "warnless.h"
+#include "curlx/warnless.h"
#include "curl_sha256.h"
#include "curl_hmac.h"
-#ifdef USE_WOLFSSL
-#include <wolfssl/options.h>
-#endif
-
-#if defined(USE_OPENSSL)
-
-#include <openssl/opensslv.h>
-
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL)
-#define USE_OPENSSL_SHA256
-#endif
-
-#endif /* USE_OPENSSL */
-
-#ifdef USE_MBEDTLS
+#ifdef USE_OPENSSL
+#include <openssl/evp.h>
+#elif defined(USE_GNUTLS)
+#include <nettle/sha.h>
+#elif defined(USE_MBEDTLS)
#include <mbedtls/version.h>
-
#if(MBEDTLS_VERSION_NUMBER >= 0x02070000) && \
(MBEDTLS_VERSION_NUMBER < 0x03000000)
#define HAS_MBEDTLS_RESULT_CODE_BASED_FUNCTIONS
#endif
-#endif /* USE_MBEDTLS */
-
-#if defined(USE_OPENSSL_SHA256)
-
-/* When OpenSSL or wolfSSL is available we use their SHA256-functions. */
-#if defined(USE_OPENSSL)
-#include <openssl/evp.h>
-#elif defined(USE_WOLFSSL)
-#include <wolfssl/openssl/evp.h>
-#endif
-
-#elif defined(USE_GNUTLS)
-#include <nettle/sha.h>
-#elif defined(USE_MBEDTLS)
#include <mbedtls/sha256.h>
#elif (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && \
(__MAC_OS_X_VERSION_MAX_ALLOWED >= 1040)) || \
@@ -95,7 +70,7 @@
* file even if multiple backends are enabled at the same time.
*/
-#if defined(USE_OPENSSL_SHA256)
+#ifdef USE_OPENSSL
struct ossl_sha256_ctx {
EVP_MD_CTX *openssl_ctx;
@@ -241,7 +216,11 @@ static void my_sha256_update(void *in,
unsigned int length)
{
my_sha256_ctx *ctx = (my_sha256_ctx *)in;
- CryptHashData(ctx->hHash, (unsigned char *) data, length, 0);
+#ifdef __MINGW32CE__
+ CryptHashData(ctx->hHash, (BYTE *)CURL_UNCONST(data), length, 0);
+#else
+ CryptHashData(ctx->hHash, (const BYTE *)data, length, 0);
+#endif
}
static void my_sha256_final(unsigned char *digest, void *in)
@@ -348,7 +327,7 @@ static const unsigned long K[64] = {
/* Compress 512-bits */
static int sha256_compress(struct sha256_state *md,
- unsigned char *buf)
+ const unsigned char *buf)
{
unsigned long S[8], W[64];
int i;
@@ -426,7 +405,7 @@ static void my_sha256_update(void *ctx,
return;
while(inlen > 0) {
if(md->curlen == 0 && inlen >= CURL_SHA256_BLOCK_SIZE) {
- if(sha256_compress(md, (unsigned char *)in) < 0)
+ if(sha256_compress(md, in) < 0)
return;
md->length += CURL_SHA256_BLOCK_SIZE * 8;
in += CURL_SHA256_BLOCK_SIZE;
@@ -533,5 +512,4 @@ const struct HMAC_params Curl_HMAC_SHA256 = {
32 /* Result size. */
};
-
#endif /* AWS, DIGEST, or libssh2 */
diff --git a/libs/libcurl/src/share.c b/libs/libcurl/src/share.c
index 6caed2f7a6..d31b23f512 100644
--- a/libs/libcurl/src/share.c
+++ b/libs/libcurl/src/share.c
@@ -30,6 +30,7 @@
#include "share.h"
#include "psl.h"
#include "vtls/vtls.h"
+#include "vtls/vtls_scache.h"
#include "hsts.h"
#include "url.h"
@@ -45,7 +46,19 @@ curl_share_init(void)
if(share) {
share->magic = CURL_GOOD_SHARE;
share->specifier |= (1 << CURL_LOCK_DATA_SHARE);
- Curl_init_dnscache(&share->hostcache, 23);
+ Curl_dnscache_init(&share->dnscache, 23);
+ share->admin = curl_easy_init();
+ if(!share->admin) {
+ free(share);
+ return NULL;
+ }
+ /* admin handles have mid 0 */
+ share->admin->mid = 0;
+ share->admin->state.internal = TRUE;
+#ifdef DEBUGBUILD
+ if(getenv("CURL_DEBUG"))
+ share->admin->set.verbose = TRUE;
+#endif
}
return share;
@@ -108,12 +121,13 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...)
case CURL_LOCK_DATA_SSL_SESSION:
#ifdef USE_SSL
- if(!share->sslsession) {
- share->max_ssl_sessions = 8;
- share->sslsession = calloc(share->max_ssl_sessions,
- sizeof(struct Curl_ssl_session));
- share->sessionage = 0;
- if(!share->sslsession)
+ if(!share->ssl_scache) {
+ /* There is no way (yet) for the application to configure the
+ * session cache size, shared between many transfers. As for curl
+ * itself, a high session count will impact startup time. Also, the
+ * scache is not optimized for several hundreds of peers. So,
+ * keep it at a reasonable level. */
+ if(Curl_ssl_scache_create(25, 2, &share->ssl_scache))
res = CURLSHE_NOMEM;
}
#else
@@ -123,10 +137,8 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...)
case CURL_LOCK_DATA_CONNECT:
/* It is safe to set this option several times on a share. */
- if(!share->cpool.idata) {
- if(Curl_cpool_init(&share->cpool, Curl_on_disconnect,
- NULL, share, 103))
- res = CURLSHE_NOMEM;
+ if(!share->cpool.initialised) {
+ Curl_cpool_init(&share->cpool, share->admin, share, 103);
}
break;
@@ -174,7 +186,10 @@ curl_share_setopt(CURLSH *sh, CURLSHoption option, ...)
case CURL_LOCK_DATA_SSL_SESSION:
#ifdef USE_SSL
- Curl_safefree(share->sslsession);
+ if(share->ssl_scache) {
+ Curl_ssl_scache_destroy(share->ssl_scache);
+ share->ssl_scache = NULL;
+ }
#else
res = CURLSHE_NOT_BUILT_IN;
#endif
@@ -234,7 +249,8 @@ curl_share_cleanup(CURLSH *sh)
if(share->specifier & (1 << CURL_LOCK_DATA_CONNECT)) {
Curl_cpool_destroy(&share->cpool);
}
- Curl_hash_destroy(&share->hostcache);
+
+ Curl_dnscache_destroy(&share->dnscache);
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
Curl_cookie_cleanup(share->cookies);
@@ -245,15 +261,14 @@ curl_share_cleanup(CURLSH *sh)
#endif
#ifdef USE_SSL
- if(share->sslsession) {
- size_t i;
- for(i = 0; i < share->max_ssl_sessions; i++)
- Curl_ssl_kill_session(&(share->sslsession[i]));
- free(share->sslsession);
+ if(share->ssl_scache) {
+ Curl_ssl_scache_destroy(share->ssl_scache);
+ share->ssl_scache = NULL;
}
#endif
Curl_psl_destroy(&share->psl);
+ Curl_close(&share->admin);
if(share->unlockfunc)
share->unlockfunc(NULL, CURL_LOCK_DATA_SHARE, share->clientdata);
diff --git a/libs/libcurl/src/share.h b/libs/libcurl/src/share.h
index 05cd2b0be2..6cc9db3ee3 100644
--- a/libs/libcurl/src/share.h
+++ b/libs/libcurl/src/share.h
@@ -31,6 +31,9 @@
#include "urldata.h"
#include "conncache.h"
+struct Curl_easy;
+struct Curl_ssl_scache;
+
#define CURL_GOOD_SHARE 0x7e117a1e
#define GOOD_SHARE_HANDLE(x) ((x) && (x)->magic == CURL_GOOD_SHARE)
@@ -46,8 +49,9 @@ struct Curl_share {
curl_lock_function lockfunc;
curl_unlock_function unlockfunc;
void *clientdata;
+ struct Curl_easy *admin;
struct cpool cpool;
- struct Curl_hash hostcache;
+ struct Curl_dnscache dnscache; /* DNS cache */
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
struct CookieInfo *cookies;
#endif
@@ -58,9 +62,7 @@ struct Curl_share {
struct hsts *hsts;
#endif
#ifdef USE_SSL
- struct Curl_ssl_session *sslsession;
- size_t max_ssl_sessions;
- long sessionage;
+ struct Curl_ssl_scache *ssl_scache;
#endif
};
@@ -68,4 +70,9 @@ CURLSHcode Curl_share_lock(struct Curl_easy *, curl_lock_data,
curl_lock_access);
CURLSHcode Curl_share_unlock(struct Curl_easy *, curl_lock_data);
+/* convenience macro to check if this handle is using a shared SSL spool */
+#define CURL_SHARE_ssl_scache(data) (data->share && \
+ (data->share->specifier & \
+ (1<<CURL_LOCK_DATA_SSL_SESSION)))
+
#endif /* HEADER_CURL_SHARE_H */
diff --git a/libs/libcurl/src/sigpipe.h b/libs/libcurl/src/sigpipe.h
index c68a076b99..6ab1faab77 100644
--- a/libs/libcurl/src/sigpipe.h
+++ b/libs/libcurl/src/sigpipe.h
@@ -31,7 +31,7 @@
struct sigpipe_ignore {
struct sigaction old_pipe_act;
- bool no_signal;
+ BIT(no_signal);
};
#define SIGPIPE_VARIABLE(x) struct sigpipe_ignore x
diff --git a/libs/libcurl/src/smb.c b/libs/libcurl/src/smb.c
index b99064820b..bf8394eb08 100644
--- a/libs/libcurl/src/smb.c
+++ b/libs/libcurl/src/smb.c
@@ -27,14 +27,9 @@
#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE)
-#ifdef _WIN32
-#define Curl_getpid() ((unsigned int)GetCurrentProcessId())
-#else
-#define Curl_getpid() ((unsigned int)getpid())
-#endif
-
#include "smb.h"
#include "urldata.h"
+#include "url.h"
#include "sendf.h"
#include "multiif.h"
#include "cfilters.h"
@@ -51,6 +46,58 @@
#include "curl_memory.h"
#include "memdebug.h"
+
+/* meta key for storing protocol meta at easy handle */
+#define CURL_META_SMB_EASY "meta:proto:smb:easy"
+/* meta key for storing protocol meta at connection */
+#define CURL_META_SMB_CONN "meta:proto:smb:conn"
+
+enum smb_conn_state {
+ SMB_NOT_CONNECTED = 0,
+ SMB_CONNECTING,
+ SMB_NEGOTIATE,
+ SMB_SETUP,
+ SMB_CONNECTED
+};
+
+/* SMB connection data, kept at connection */
+struct smb_conn {
+ enum smb_conn_state state;
+ char *user;
+ char *domain;
+ char *share;
+ unsigned char challenge[8];
+ unsigned int session_key;
+ unsigned short uid;
+ char *recv_buf;
+ char *send_buf;
+ size_t upload_size;
+ size_t send_size;
+ size_t sent;
+ size_t got;
+};
+
+/* SMB request state */
+enum smb_req_state {
+ SMB_REQUESTING,
+ SMB_TREE_CONNECT,
+ SMB_OPEN,
+ SMB_DOWNLOAD,
+ SMB_UPLOAD,
+ SMB_CLOSE,
+ SMB_TREE_DISCONNECT,
+ SMB_DONE
+};
+
+/* SMB request data, kept at easy handle */
+struct smb_request {
+ enum smb_req_state state;
+ char *path;
+ unsigned short tid; /* Even if we connect to the same tree as another */
+ unsigned short fid; /* request, the tid will be different */
+ CURLcode result;
+};
+
/*
* Definitions for SMB protocol data structures
*/
@@ -83,7 +130,7 @@
#define SMB_FLAGS_CANONICAL_PATHNAMES 0x10
#define SMB_FLAGS_CASELESS_PATHNAMES 0x08
-#define SMB_FLAGS2_UNICODE_STRINGS 0x8000
+/* #define SMB_FLAGS2_UNICODE_STRINGS 0x8000 */
#define SMB_FLAGS2_IS_LONG_NAME 0x0040
#define SMB_FLAGS2_KNOWS_LONG_NAME 0x0001
@@ -251,12 +298,11 @@ static CURLcode smb_connect(struct Curl_easy *data, bool *done);
static CURLcode smb_connection_state(struct Curl_easy *data, bool *done);
static CURLcode smb_do(struct Curl_easy *data, bool *done);
static CURLcode smb_request_state(struct Curl_easy *data, bool *done);
-static CURLcode smb_disconnect(struct Curl_easy *data,
- struct connectdata *conn, bool dead);
static int smb_getsock(struct Curl_easy *data, struct connectdata *conn,
curl_socket_t *socks);
static CURLcode smb_parse_url_path(struct Curl_easy *data,
- struct connectdata *conn);
+ struct smb_conn *smbc,
+ struct smb_request *req);
/*
* SMB handler interface
@@ -274,11 +320,12 @@ const struct Curl_handler Curl_handler_smb = {
smb_getsock, /* doing_getsock */
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
- smb_disconnect, /* disconnect */
+ ZERO_NULL, /* disconnect */
ZERO_NULL, /* write_resp */
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_SMB, /* defport */
CURLPROTO_SMB, /* protocol */
CURLPROTO_SMB, /* family */
@@ -302,11 +349,12 @@ const struct Curl_handler Curl_handler_smbs = {
smb_getsock, /* doing_getsock */
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
- smb_disconnect, /* disconnect */
+ ZERO_NULL, /* disconnect */
ZERO_NULL, /* write_resp */
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_SMBS, /* defport */
CURLPROTO_SMBS, /* protocol */
CURLPROTO_SMB, /* family */
@@ -345,30 +393,9 @@ static curl_off_t smb_swap64(curl_off_t x)
# define smb_swap64(x) (x)
#endif
-/* SMB request state */
-enum smb_req_state {
- SMB_REQUESTING,
- SMB_TREE_CONNECT,
- SMB_OPEN,
- SMB_DOWNLOAD,
- SMB_UPLOAD,
- SMB_CLOSE,
- SMB_TREE_DISCONNECT,
- SMB_DONE
-};
-
-/* SMB request data */
-struct smb_request {
- enum smb_req_state state;
- char *path;
- unsigned short tid; /* Even if we connect to the same tree as another */
- unsigned short fid; /* request, the tid will be different */
- CURLcode result;
-};
-
-static void conn_state(struct Curl_easy *data, enum smb_conn_state newstate)
+static void conn_state(struct Curl_easy *data, struct smb_conn *smbc,
+ enum smb_conn_state newstate)
{
- struct smb_conn *smbc = &data->conn->proto.smbc;
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
/* For debug purposes */
static const char * const names[] = {
@@ -384,34 +411,59 @@ static void conn_state(struct Curl_easy *data, enum smb_conn_state newstate)
infof(data, "SMB conn %p state change from %s to %s",
(void *)smbc, names[smbc->state], names[newstate]);
#endif
-
+ (void)data;
smbc->state = newstate;
}
static void request_state(struct Curl_easy *data,
enum smb_req_state newstate)
{
- struct smb_request *req = data->req.p.smb;
+ struct smb_request *req = Curl_meta_get(data, CURL_META_SMB_EASY);
+ if(req) {
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
- /* For debug purposes */
- static const char * const names[] = {
- "SMB_REQUESTING",
- "SMB_TREE_CONNECT",
- "SMB_OPEN",
- "SMB_DOWNLOAD",
- "SMB_UPLOAD",
- "SMB_CLOSE",
- "SMB_TREE_DISCONNECT",
- "SMB_DONE",
- /* LAST */
- };
-
- if(req->state != newstate)
- infof(data, "SMB request %p state change from %s to %s",
- (void *)req, names[req->state], names[newstate]);
+ /* For debug purposes */
+ static const char * const names[] = {
+ "SMB_REQUESTING",
+ "SMB_TREE_CONNECT",
+ "SMB_OPEN",
+ "SMB_DOWNLOAD",
+ "SMB_UPLOAD",
+ "SMB_CLOSE",
+ "SMB_TREE_DISCONNECT",
+ "SMB_DONE",
+ /* LAST */
+ };
+
+ if(req->state != newstate)
+ infof(data, "SMB request %p state change from %s to %s",
+ (void *)req, names[req->state], names[newstate]);
#endif
- req->state = newstate;
+ req->state = newstate;
+ }
+}
+
+static void smb_easy_dtor(void *key, size_t klen, void *entry)
+{
+ struct smb_request *req = entry;
+ (void)key;
+ (void)klen;
+ /* `req->path` points to somewhere in `struct smb_conn` which is
+ * kept at the connection meta. If the connection is destroyed first,
+ * req->path points to free'd memory. */
+ free(req);
+}
+
+static void smb_conn_dtor(void *key, size_t klen, void *entry)
+{
+ struct smb_conn *smbc = entry;
+ (void)key;
+ (void)klen;
+ Curl_safefree(smbc->share);
+ Curl_safefree(smbc->domain);
+ Curl_safefree(smbc->recv_buf);
+ Curl_safefree(smbc->send_buf);
+ free(smbc);
}
/* this should setup things in the connection, not in the easy
@@ -419,24 +471,34 @@ static void request_state(struct Curl_easy *data,
static CURLcode smb_setup_connection(struct Curl_easy *data,
struct connectdata *conn)
{
+ struct smb_conn *smbc;
struct smb_request *req;
+ /* Initialize the connection state */
+ smbc = calloc(1, sizeof(*smbc));
+ if(!smbc ||
+ Curl_conn_meta_set(conn, CURL_META_SMB_CONN, smbc, smb_conn_dtor))
+ return CURLE_OUT_OF_MEMORY;
+
/* Initialize the request state */
- data->req.p.smb = req = calloc(1, sizeof(struct smb_request));
- if(!req)
+ req = calloc(1, sizeof(*req));
+ if(!req ||
+ Curl_meta_set(data, CURL_META_SMB_EASY, req, smb_easy_dtor))
return CURLE_OUT_OF_MEMORY;
/* Parse the URL path */
- return smb_parse_url_path(data, conn);
+ return smb_parse_url_path(data, smbc, req);
}
static CURLcode smb_connect(struct Curl_easy *data, bool *done)
{
struct connectdata *conn = data->conn;
- struct smb_conn *smbc = &conn->proto.smbc;
+ struct smb_conn *smbc = Curl_conn_meta_get(conn, CURL_META_SMB_CONN);
char *slash;
(void) done;
+ if(!smbc)
+ return CURLE_FAILED_INIT;
/* Check we have a username and password to authenticate with */
if(!data->state.aptr.user)
@@ -476,10 +538,10 @@ static CURLcode smb_connect(struct Curl_easy *data, bool *done)
return CURLE_OK;
}
-static CURLcode smb_recv_message(struct Curl_easy *data, void **msg)
+static CURLcode smb_recv_message(struct Curl_easy *data,
+ struct smb_conn *smbc,
+ void **msg)
{
- struct connectdata *conn = data->conn;
- struct smb_conn *smbc = &conn->proto.smbc;
char *buf = smbc->recv_buf;
ssize_t bytes_read;
size_t nbt_size;
@@ -524,20 +586,17 @@ static CURLcode smb_recv_message(struct Curl_easy *data, void **msg)
return CURLE_OK;
}
-static void smb_pop_message(struct connectdata *conn)
+static void smb_pop_message(struct smb_conn *smbc)
{
- struct smb_conn *smbc = &conn->proto.smbc;
-
smbc->got = 0;
}
-static void smb_format_message(struct Curl_easy *data, struct smb_header *h,
+static void smb_format_message(struct smb_conn *smbc,
+ struct smb_request *req,
+ struct smb_header *h,
unsigned char cmd, size_t len)
{
- struct connectdata *conn = data->conn;
- struct smb_conn *smbc = &conn->proto.smbc;
- struct smb_request *req = data->req.p.smb;
- unsigned int pid;
+ const unsigned int pid = 0xbad71d; /* made up */
memset(h, 0, sizeof(*h));
h->nbt_length = htons((unsigned short) (sizeof(*h) - sizeof(unsigned int) +
@@ -548,16 +607,13 @@ static void smb_format_message(struct Curl_easy *data, struct smb_header *h,
h->flags2 = smb_swap16(SMB_FLAGS2_IS_LONG_NAME | SMB_FLAGS2_KNOWS_LONG_NAME);
h->uid = smb_swap16(smbc->uid);
h->tid = smb_swap16(req->tid);
- pid = Curl_getpid();
h->pid_high = smb_swap16((unsigned short)(pid >> 16));
h->pid = smb_swap16((unsigned short) pid);
}
-static CURLcode smb_send(struct Curl_easy *data, size_t len,
- size_t upload_size)
+static CURLcode smb_send(struct Curl_easy *data, struct smb_conn *smbc,
+ size_t len, size_t upload_size)
{
- struct connectdata *conn = data->conn;
- struct smb_conn *smbc = &conn->proto.smbc;
size_t bytes_written;
CURLcode result;
@@ -575,10 +631,8 @@ static CURLcode smb_send(struct Curl_easy *data, size_t len,
return CURLE_OK;
}
-static CURLcode smb_flush(struct Curl_easy *data)
+static CURLcode smb_flush(struct Curl_easy *data, struct smb_conn *smbc)
{
- struct connectdata *conn = data->conn;
- struct smb_conn *smbc = &conn->proto.smbc;
size_t bytes_written;
size_t len = smbc->send_size - smbc->sent;
CURLcode result;
@@ -599,39 +653,46 @@ static CURLcode smb_flush(struct Curl_easy *data)
return CURLE_OK;
}
-static CURLcode smb_send_message(struct Curl_easy *data, unsigned char cmd,
+static CURLcode smb_send_message(struct Curl_easy *data,
+ struct smb_conn *smbc,
+ struct smb_request *req,
+ unsigned char cmd,
const void *msg, size_t msg_len)
{
- struct connectdata *conn = data->conn;
- struct smb_conn *smbc = &conn->proto.smbc;
-
- smb_format_message(data, (struct smb_header *)smbc->send_buf,
+ smb_format_message(smbc, req, (struct smb_header *)smbc->send_buf,
cmd, msg_len);
DEBUGASSERT((sizeof(struct smb_header) + msg_len) <= MAX_MESSAGE_SIZE);
memcpy(smbc->send_buf + sizeof(struct smb_header), msg, msg_len);
- return smb_send(data, sizeof(struct smb_header) + msg_len, 0);
+ return smb_send(data, smbc, sizeof(struct smb_header) + msg_len, 0);
}
-static CURLcode smb_send_negotiate(struct Curl_easy *data)
+static CURLcode smb_send_negotiate(struct Curl_easy *data,
+ struct smb_conn *smbc,
+ struct smb_request *req)
{
const char *msg = "\x00\x0c\x00\x02NT LM 0.12";
- return smb_send_message(data, SMB_COM_NEGOTIATE, msg, 15);
+ return smb_send_message(data, smbc, req, SMB_COM_NEGOTIATE, msg, 15);
}
static CURLcode smb_send_setup(struct Curl_easy *data)
{
struct connectdata *conn = data->conn;
- struct smb_conn *smbc = &conn->proto.smbc;
+ struct smb_conn *smbc = Curl_conn_meta_get(conn, CURL_META_SMB_CONN);
+ struct smb_request *req = Curl_meta_get(data, CURL_META_SMB_EASY);
struct smb_setup msg;
char *p = msg.bytes;
unsigned char lm_hash[21];
unsigned char lm[24];
unsigned char nt_hash[21];
unsigned char nt[24];
+ size_t byte_count;
+
+ if(!smbc || !req)
+ return CURLE_FAILED_INIT;
- const size_t byte_count = sizeof(lm) + sizeof(nt) +
+ byte_count = sizeof(lm) + sizeof(nt) +
strlen(smbc->user) + strlen(smbc->domain) +
strlen(CURL_OS) + strlen(CLIENTNAME) + 4; /* 4 null chars */
if(byte_count > sizeof(msg.bytes))
@@ -662,23 +723,24 @@ static CURLcode smb_send_setup(struct Curl_easy *data)
"%s%c" /* OS */
"%s", /* client name */
smbc->user, 0, smbc->domain, 0, CURL_OS, 0, CLIENTNAME);
- p++; /* count the final null termination */
+ p++; /* count the final null-termination */
DEBUGASSERT(byte_count == (size_t)(p - msg.bytes));
msg.byte_count = smb_swap16((unsigned short)byte_count);
- return smb_send_message(data, SMB_COM_SETUP_ANDX, &msg,
+ return smb_send_message(data, smbc, req, SMB_COM_SETUP_ANDX, &msg,
sizeof(msg) - sizeof(msg.bytes) + byte_count);
}
-static CURLcode smb_send_tree_connect(struct Curl_easy *data)
+static CURLcode smb_send_tree_connect(struct Curl_easy *data,
+ struct smb_conn *smbc,
+ struct smb_request *req)
{
struct smb_tree_connect msg;
struct connectdata *conn = data->conn;
- struct smb_conn *smbc = &conn->proto.smbc;
char *p = msg.bytes;
-
const size_t byte_count = strlen(conn->host.name) + strlen(smbc->share) +
strlen(SERVICENAME) + 5; /* 2 nulls and 3 backslashes */
+
if(byte_count > sizeof(msg.bytes))
return CURLE_FILESIZE_EXCEEDED;
@@ -692,17 +754,18 @@ static CURLcode smb_send_tree_connect(struct Curl_easy *data)
"%s%c" /* share */
"%s", /* service */
conn->host.name, smbc->share, 0, SERVICENAME);
- p++; /* count the final null termination */
+ p++; /* count the final null-termination */
DEBUGASSERT(byte_count == (size_t)(p - msg.bytes));
msg.byte_count = smb_swap16((unsigned short)byte_count);
- return smb_send_message(data, SMB_COM_TREE_CONNECT_ANDX, &msg,
+ return smb_send_message(data, smbc, req, SMB_COM_TREE_CONNECT_ANDX, &msg,
sizeof(msg) - sizeof(msg.bytes) + byte_count);
}
-static CURLcode smb_send_open(struct Curl_easy *data)
+static CURLcode smb_send_open(struct Curl_easy *data,
+ struct smb_conn *smbc,
+ struct smb_request *req)
{
- struct smb_request *req = data->req.p.smb;
struct smb_nt_create msg;
const size_t byte_count = strlen(req->path) + 1;
@@ -725,34 +788,37 @@ static CURLcode smb_send_open(struct Curl_easy *data)
msg.byte_count = smb_swap16((unsigned short) byte_count);
strcpy(msg.bytes, req->path);
- return smb_send_message(data, SMB_COM_NT_CREATE_ANDX, &msg,
+ return smb_send_message(data, smbc, req, SMB_COM_NT_CREATE_ANDX, &msg,
sizeof(msg) - sizeof(msg.bytes) + byte_count);
}
-static CURLcode smb_send_close(struct Curl_easy *data)
+static CURLcode smb_send_close(struct Curl_easy *data,
+ struct smb_conn *smbc,
+ struct smb_request *req)
{
- struct smb_request *req = data->req.p.smb;
struct smb_close msg;
memset(&msg, 0, sizeof(msg));
msg.word_count = SMB_WC_CLOSE;
msg.fid = smb_swap16(req->fid);
- return smb_send_message(data, SMB_COM_CLOSE, &msg, sizeof(msg));
+ return smb_send_message(data, smbc, req, SMB_COM_CLOSE, &msg, sizeof(msg));
}
-static CURLcode smb_send_tree_disconnect(struct Curl_easy *data)
+static CURLcode smb_send_tree_disconnect(struct Curl_easy *data,
+ struct smb_conn *smbc,
+ struct smb_request *req)
{
struct smb_tree_disconnect msg;
-
memset(&msg, 0, sizeof(msg));
-
- return smb_send_message(data, SMB_COM_TREE_DISCONNECT, &msg, sizeof(msg));
+ return smb_send_message(data, smbc, req, SMB_COM_TREE_DISCONNECT,
+ &msg, sizeof(msg));
}
-static CURLcode smb_send_read(struct Curl_easy *data)
+static CURLcode smb_send_read(struct Curl_easy *data,
+ struct smb_conn *smbc,
+ struct smb_request *req)
{
- struct smb_request *req = data->req.p.smb;
curl_off_t offset = data->req.offset;
struct smb_read msg;
@@ -765,15 +831,15 @@ static CURLcode smb_send_read(struct Curl_easy *data)
msg.min_bytes = smb_swap16(MAX_PAYLOAD_SIZE);
msg.max_bytes = smb_swap16(MAX_PAYLOAD_SIZE);
- return smb_send_message(data, SMB_COM_READ_ANDX, &msg, sizeof(msg));
+ return smb_send_message(data, smbc, req, SMB_COM_READ_ANDX,
+ &msg, sizeof(msg));
}
-static CURLcode smb_send_write(struct Curl_easy *data)
+static CURLcode smb_send_write(struct Curl_easy *data,
+ struct smb_conn *smbc,
+ struct smb_request *req)
{
- struct connectdata *conn = data->conn;
- struct smb_conn *smbc = &conn->proto.smbc;
struct smb_write *msg;
- struct smb_request *req = data->req.p.smb;
curl_off_t offset = data->req.offset;
curl_off_t upload_size = data->req.size - data->req.bytecount;
@@ -791,16 +857,15 @@ static CURLcode smb_send_write(struct Curl_easy *data)
msg->data_offset = smb_swap16(sizeof(*msg) - sizeof(unsigned int));
msg->byte_count = smb_swap16((unsigned short) (upload_size + 1));
- smb_format_message(data, &msg->h, SMB_COM_WRITE_ANDX,
+ smb_format_message(smbc, req, &msg->h, SMB_COM_WRITE_ANDX,
sizeof(*msg) - sizeof(msg->h) + (size_t) upload_size);
- return smb_send(data, sizeof(*msg), (size_t) upload_size);
+ return smb_send(data, smbc, sizeof(*msg), (size_t) upload_size);
}
-static CURLcode smb_send_and_recv(struct Curl_easy *data, void **msg)
+static CURLcode smb_send_and_recv(struct Curl_easy *data,
+ struct smb_conn *smbc, void **msg)
{
- struct connectdata *conn = data->conn;
- struct smb_conn *smbc = &conn->proto.smbc;
CURLcode result;
*msg = NULL; /* if it returns early */
@@ -823,7 +888,7 @@ static CURLcode smb_send_and_recv(struct Curl_easy *data, void **msg)
/* Check if there is data to send */
if(smbc->send_size) {
- result = smb_flush(data);
+ result = smb_flush(data, smbc);
if(result)
return result;
}
@@ -832,21 +897,25 @@ static CURLcode smb_send_and_recv(struct Curl_easy *data, void **msg)
if(smbc->send_size || smbc->upload_size)
return CURLE_AGAIN;
- return smb_recv_message(data, msg);
+ return smb_recv_message(data, smbc, msg);
}
static CURLcode smb_connection_state(struct Curl_easy *data, bool *done)
{
struct connectdata *conn = data->conn;
- struct smb_conn *smbc = &conn->proto.smbc;
+ struct smb_conn *smbc = Curl_conn_meta_get(conn, CURL_META_SMB_CONN);
+ struct smb_request *req = Curl_meta_get(data, CURL_META_SMB_EASY);
struct smb_negotiate_response *nrsp;
struct smb_header *h;
CURLcode result;
void *msg = NULL;
+ if(!smbc || !req)
+ return CURLE_FAILED_INIT;
+
if(smbc->state == SMB_CONNECTING) {
#ifdef USE_SSL
- if((conn->handler->flags & PROTOPT_SSL)) {
+ if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
bool ssl_done = FALSE;
result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssl_done);
if(result && result != CURLE_AGAIN)
@@ -856,17 +925,17 @@ static CURLcode smb_connection_state(struct Curl_easy *data, bool *done)
}
#endif
- result = smb_send_negotiate(data);
+ result = smb_send_negotiate(data, smbc, req);
if(result) {
connclose(conn, "SMB: failed to send negotiate message");
return result;
}
- conn_state(data, SMB_NEGOTIATE);
+ conn_state(data, smbc, SMB_NEGOTIATE);
}
/* Send the previous message and check for a response */
- result = smb_send_and_recv(data, &msg);
+ result = smb_send_and_recv(data, smbc, &msg);
if(result && result != CURLE_AGAIN) {
connclose(conn, "SMB: failed to communicate");
return result;
@@ -885,14 +954,23 @@ static CURLcode smb_connection_state(struct Curl_easy *data, bool *done)
return CURLE_COULDNT_CONNECT;
}
nrsp = msg;
+#if defined(__GNUC__) && __GNUC__ >= 13
+#pragma GCC diagnostic push
+/* error: 'memcpy' offset [74, 80] from the object at '<unknown>' is out of
+ the bounds of referenced subobject 'bytes' with type 'char[1]' */
+#pragma GCC diagnostic ignored "-Warray-bounds"
+#endif
memcpy(smbc->challenge, nrsp->bytes, sizeof(smbc->challenge));
+#if defined(__GNUC__) && __GNUC__ >= 13
+#pragma GCC diagnostic pop
+#endif
smbc->session_key = smb_swap32(nrsp->session_key);
result = smb_send_setup(data);
if(result) {
connclose(conn, "SMB: failed to send setup message");
return result;
}
- conn_state(data, SMB_SETUP);
+ conn_state(data, smbc, SMB_SETUP);
break;
case SMB_SETUP:
@@ -901,16 +979,16 @@ static CURLcode smb_connection_state(struct Curl_easy *data, bool *done)
return CURLE_LOGIN_DENIED;
}
smbc->uid = smb_swap16(h->uid);
- conn_state(data, SMB_CONNECTED);
+ conn_state(data, smbc, SMB_CONNECTED);
*done = TRUE;
break;
default:
- smb_pop_message(conn);
+ smb_pop_message(smbc);
return CURLE_OK; /* ignore */
}
- smb_pop_message(conn);
+ smb_pop_message(smbc);
return CURLE_OK;
}
@@ -921,24 +999,28 @@ static CURLcode smb_connection_state(struct Curl_easy *data, bool *done)
*/
static void get_posix_time(time_t *out, curl_off_t timestamp)
{
- timestamp -= 116444736000000000;
- timestamp /= 10000000;
+ if(timestamp >= CURL_OFF_T_C(116444736000000000)) {
+ timestamp -= CURL_OFF_T_C(116444736000000000);
+ timestamp /= 10000000;
#if SIZEOF_TIME_T < SIZEOF_CURL_OFF_T
- if(timestamp > TIME_T_MAX)
- *out = TIME_T_MAX;
- else if(timestamp < TIME_T_MIN)
- *out = TIME_T_MIN;
- else
+ if(timestamp > TIME_T_MAX)
+ *out = TIME_T_MAX;
+ else if(timestamp < TIME_T_MIN)
+ *out = TIME_T_MIN;
+ else
#endif
- *out = (time_t) timestamp;
+ *out = (time_t) timestamp;
+ }
+ else
+ *out = 0;
}
static CURLcode smb_request_state(struct Curl_easy *data, bool *done)
{
struct connectdata *conn = data->conn;
- struct smb_request *req = data->req.p.smb;
+ struct smb_conn *smbc = Curl_conn_meta_get(conn, CURL_META_SMB_CONN);
+ struct smb_request *req = Curl_meta_get(data, CURL_META_SMB_EASY);
struct smb_header *h;
- struct smb_conn *smbc = &conn->proto.smbc;
enum smb_req_state next_state = SMB_DONE;
unsigned short len;
unsigned short off;
@@ -946,6 +1028,9 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done)
void *msg = NULL;
const struct smb_nt_create_response *smb_m;
+ if(!smbc || !req)
+ return CURLE_FAILED_INIT;
+
if(data->state.upload && (data->state.infilesize < 0)) {
failf(data, "SMB upload needs to know the size up front");
return CURLE_SEND_ERROR;
@@ -953,7 +1038,7 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done)
/* Start the request */
if(req->state == SMB_REQUESTING) {
- result = smb_send_tree_connect(data);
+ result = smb_send_tree_connect(data, smbc, req);
if(result) {
connclose(conn, "SMB: failed to send tree connect message");
return result;
@@ -963,7 +1048,7 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done)
}
/* Send the previous message and check for a response */
- result = smb_send_and_recv(data, &msg);
+ result = smb_send_and_recv(data, smbc, &msg);
if(result && result != CURLE_AGAIN) {
connclose(conn, "SMB: failed to communicate");
return result;
@@ -1073,31 +1158,31 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done)
break;
default:
- smb_pop_message(conn);
+ smb_pop_message(smbc);
return CURLE_OK; /* ignore */
}
- smb_pop_message(conn);
+ smb_pop_message(smbc);
switch(next_state) {
case SMB_OPEN:
- result = smb_send_open(data);
+ result = smb_send_open(data, smbc, req);
break;
case SMB_DOWNLOAD:
- result = smb_send_read(data);
+ result = smb_send_read(data, smbc, req);
break;
case SMB_UPLOAD:
- result = smb_send_write(data);
+ result = smb_send_write(data, smbc, req);
break;
case SMB_CLOSE:
- result = smb_send_close(data);
+ result = smb_send_close(data, smbc, req);
break;
case SMB_TREE_DISCONNECT:
- result = smb_send_tree_disconnect(data);
+ result = smb_send_tree_disconnect(data, smbc, req);
break;
case SMB_DONE:
@@ -1119,19 +1204,6 @@ static CURLcode smb_request_state(struct Curl_easy *data, bool *done)
return CURLE_OK;
}
-static CURLcode smb_disconnect(struct Curl_easy *data,
- struct connectdata *conn, bool dead)
-{
- struct smb_conn *smbc = &conn->proto.smbc;
- (void) dead;
- (void) data;
- Curl_safefree(smbc->share);
- Curl_safefree(smbc->domain);
- Curl_safefree(smbc->recv_buf);
- Curl_safefree(smbc->send_buf);
- return CURLE_OK;
-}
-
static int smb_getsock(struct Curl_easy *data,
struct connectdata *conn, curl_socket_t *socks)
{
@@ -1143,26 +1215,26 @@ static int smb_getsock(struct Curl_easy *data,
static CURLcode smb_do(struct Curl_easy *data, bool *done)
{
struct connectdata *conn = data->conn;
- struct smb_conn *smbc = &conn->proto.smbc;
+ struct smb_conn *smbc = Curl_conn_meta_get(conn, CURL_META_SMB_CONN);
*done = FALSE;
- if(smbc->share) {
+ if(!smbc)
+ return CURLE_FAILED_INIT;
+ if(smbc->share)
return CURLE_OK;
- }
return CURLE_URL_MALFORMAT;
}
static CURLcode smb_parse_url_path(struct Curl_easy *data,
- struct connectdata *conn)
+ struct smb_conn *smbc,
+ struct smb_request *req)
{
- struct smb_request *req = data->req.p.smb;
- struct smb_conn *smbc = &conn->proto.smbc;
char *path;
char *slash;
+ CURLcode result;
/* URL decode the path */
- CURLcode result = Curl_urldecode(data->state.up.path, 0, &path, NULL,
- REJECT_CTRL);
+ result = Curl_urldecode(data->state.up.path, 0, &path, NULL, REJECT_CTRL);
if(result)
return result;
diff --git a/libs/libcurl/src/smb.h b/libs/libcurl/src/smb.h
index 09281a3adb..6fbdf01066 100644
--- a/libs/libcurl/src/smb.h
+++ b/libs/libcurl/src/smb.h
@@ -25,30 +25,6 @@
*
***************************************************************************/
-enum smb_conn_state {
- SMB_NOT_CONNECTED = 0,
- SMB_CONNECTING,
- SMB_NEGOTIATE,
- SMB_SETUP,
- SMB_CONNECTED
-};
-
-struct smb_conn {
- enum smb_conn_state state;
- char *user;
- char *domain;
- char *share;
- unsigned char challenge[8];
- unsigned int session_key;
- unsigned short uid;
- char *recv_buf;
- char *send_buf;
- size_t upload_size;
- size_t send_size;
- size_t sent;
- size_t got;
-};
-
#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \
(SIZEOF_CURL_OFF_T > 4)
diff --git a/libs/libcurl/src/smtp.c b/libs/libcurl/src/smtp.c
index 4809987224..0392c98561 100644
--- a/libs/libcurl/src/smtp.c
+++ b/libs/libcurl/src/smtp.c
@@ -68,7 +68,6 @@
#include "mime.h"
#include "socks.h"
#include "smtp.h"
-#include "strtoofft.h"
#include "strcase.h"
#include "vtls/vtls.h"
#include "cfilters.h"
@@ -79,15 +78,79 @@
#include "curl_gethostname.h"
#include "bufref.h"
#include "curl_sasl.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
#include "idn.h"
+#include "curlx/strparse.h"
+
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
#include "memdebug.h"
+/* meta key for storing protocol meta at easy handle */
+#define CURL_META_SMTP_EASY "meta:proto:smtp:easy"
+/* meta key for storing protocol meta at connection */
+#define CURL_META_SMTP_CONN "meta:proto:smtp:conn"
+
+/****************************************************************************
+ * SMTP unique setup
+ ***************************************************************************/
+typedef enum {
+ SMTP_STOP, /* do nothing state, stops the state machine */
+ SMTP_SERVERGREET, /* waiting for the initial greeting immediately after
+ a connect */
+ SMTP_EHLO,
+ SMTP_HELO,
+ SMTP_STARTTLS,
+ SMTP_UPGRADETLS, /* asynchronously upgrade the connection to SSL/TLS
+ (multi mode only) */
+ SMTP_AUTH,
+ SMTP_COMMAND, /* VRFY, EXPN, NOOP, RSET and HELP */
+ SMTP_MAIL, /* MAIL FROM */
+ SMTP_RCPT, /* RCPT TO */
+ SMTP_DATA,
+ SMTP_POSTDATA,
+ SMTP_QUIT,
+ SMTP_LAST /* never used */
+} smtpstate;
+
+/* smtp_conn is used for struct connection-oriented data in the connectdata
+ struct */
+struct smtp_conn {
+ struct pingpong pp;
+ struct SASL sasl; /* SASL-related storage */
+ smtpstate state; /* Always use smtp.c:state() to change state! */
+ char *domain; /* Client address/name to send in the EHLO */
+ BIT(ssldone); /* Is connect() over SSL done? */
+ BIT(tls_supported); /* StartTLS capability supported by server */
+ BIT(size_supported); /* If server supports SIZE extension according to
+ RFC 1870 */
+ BIT(utf8_supported); /* If server supports SMTPUTF8 extension according
+ to RFC 6531 */
+ BIT(auth_supported); /* AUTH capability supported by server */
+};
+
+/* This SMTP struct is used in the Curl_easy. All SMTP data that is
+ connection-oriented must be in smtp_conn to properly deal with the fact that
+ perhaps the Curl_easy is changed between the times the connection is
+ used. */
+struct SMTP {
+ curl_pp_transfer transfer;
+ char *custom; /* Custom Request */
+ struct curl_slist *rcpt; /* Recipient list */
+ int rcpt_last_error; /* The last error received for RCPT TO command */
+ size_t eob; /* Number of bytes of the EOB (End Of Body) that
+ have been received so far */
+ BIT(rcpt_had_ok); /* Whether any of RCPT TO commands (depends on
+ total number of recipients) succeeded so far */
+ BIT(trailing_crlf); /* Specifies if the trailing CRLF is present */
+};
+
/* Local API functions */
-static CURLcode smtp_regular_transfer(struct Curl_easy *data, bool *done);
+static CURLcode smtp_regular_transfer(struct Curl_easy *data,
+ struct smtp_conn *smtpc,
+ struct SMTP *smtp,
+ bool *done);
static CURLcode smtp_do(struct Curl_easy *data, bool *done);
static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
bool premature);
@@ -100,9 +163,12 @@ static int smtp_getsock(struct Curl_easy *data,
static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done);
static CURLcode smtp_setup_connection(struct Curl_easy *data,
struct connectdata *conn);
-static CURLcode smtp_parse_url_options(struct connectdata *conn);
-static CURLcode smtp_parse_url_path(struct Curl_easy *data);
-static CURLcode smtp_parse_custom_request(struct Curl_easy *data);
+static CURLcode smtp_parse_url_options(struct connectdata *conn,
+ struct smtp_conn *smtpc);
+static CURLcode smtp_parse_url_path(struct Curl_easy *data,
+ struct smtp_conn *smtpc);
+static CURLcode smtp_parse_custom_request(struct Curl_easy *data,
+ struct SMTP *smtp);
static CURLcode smtp_parse_address(const char *fqma,
char **address, struct hostname *host);
static CURLcode smtp_perform_auth(struct Curl_easy *data, const char *mech,
@@ -135,6 +201,7 @@ const struct Curl_handler Curl_handler_smtp = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_SMTP, /* defport */
CURLPROTO_SMTP, /* protocol */
CURLPROTO_SMTP, /* family */
@@ -165,6 +232,7 @@ const struct Curl_handler Curl_handler_smtps = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_SMTPS, /* defport */
CURLPROTO_SMTPS, /* protocol */
CURLPROTO_SMTP, /* family */
@@ -187,19 +255,6 @@ static const struct SASLproto saslsmtp = {
SASL_FLAG_BASE64 /* Configuration flags */
};
-#ifdef USE_SSL
-static void smtp_to_smtps(struct connectdata *conn)
-{
- /* Change the connection handler */
- conn->handler = &Curl_handler_smtps;
-
- /* Set the connection's upgraded to TLS flag */
- conn->bits.tls_upgraded = TRUE;
-}
-#else
-#define smtp_to_smtps(x) Curl_nop_stmt
-#endif
-
/***********************************************************************
*
* smtp_endofresp()
@@ -209,12 +264,16 @@ static void smtp_to_smtps(struct connectdata *conn)
* supported authentication mechanisms.
*/
static bool smtp_endofresp(struct Curl_easy *data, struct connectdata *conn,
- char *line, size_t len, int *resp)
+ const char *line, size_t len, int *resp)
{
- struct smtp_conn *smtpc = &conn->proto.smtpc;
+ struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN);
bool result = FALSE;
(void)data;
+ DEBUGASSERT(smtpc);
+ if(!smtpc)
+ return FALSE;
+
/* Nothing for us */
if(len < 4 || !ISDIGIT(line[0]) || !ISDIGIT(line[1]) || !ISDIGIT(line[2]))
return FALSE;
@@ -225,11 +284,14 @@ static bool smtp_endofresp(struct Curl_easy *data, struct connectdata *conn,
only send the response code instead as per Section 4.2. */
if(line[3] == ' ' || len == 5) {
char tmpline[6];
-
+ curl_off_t code;
+ const char *p = tmpline;
result = TRUE;
- memset(tmpline, '\0', sizeof(tmpline));
memcpy(tmpline, line, (len == 5 ? 5 : 3));
- *resp = curlx_sltosi(strtol(tmpline, NULL, 10));
+ tmpline[len == 5 ? 5 : 3 ] = 0;
+ if(curlx_str_number(&p, &code, len == 5 ? 99999 : 999))
+ return FALSE;
+ *resp = (int) code;
/* Make sure real server never sends internal value */
if(*resp == 1)
@@ -253,9 +315,16 @@ static bool smtp_endofresp(struct Curl_easy *data, struct connectdata *conn,
*/
static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out)
{
- char *message = Curl_dyn_ptr(&data->conn->proto.smtpc.pp.recvbuf);
- size_t len = data->conn->proto.smtpc.pp.nfinal;
+ struct smtp_conn *smtpc =
+ Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
+ char *message;
+ size_t len;
+ if(!smtpc)
+ return CURLE_FAILED_INIT;
+
+ message = curlx_dyn_ptr(&smtpc->pp.recvbuf);
+ len = smtpc->pp.nfinal;
if(len > 4) {
/* Find the start of the message */
len -= 4;
@@ -285,9 +354,10 @@ static CURLcode smtp_get_message(struct Curl_easy *data, struct bufref *out)
*
* This is the ONLY way to change SMTP state!
*/
-static void smtp_state(struct Curl_easy *data, smtpstate newstate)
+static void smtp_state(struct Curl_easy *data,
+ struct smtp_conn *smtpc,
+ smtpstate newstate)
{
- struct smtp_conn *smtpc = &data->conn->proto.smtpc;
#if !defined(CURL_DISABLE_VERBOSE_STRINGS)
/* for debug purposes */
static const char * const names[] = {
@@ -322,11 +392,10 @@ static void smtp_state(struct Curl_easy *data, smtpstate newstate)
* Sends the EHLO command to not only initialise communication with the ESMTP
* server but to also obtain a list of server side supported capabilities.
*/
-static CURLcode smtp_perform_ehlo(struct Curl_easy *data)
+static CURLcode smtp_perform_ehlo(struct Curl_easy *data,
+ struct smtp_conn *smtpc)
{
CURLcode result = CURLE_OK;
- struct connectdata *conn = data->conn;
- struct smtp_conn *smtpc = &conn->proto.smtpc;
smtpc->sasl.authmechs = SASL_AUTH_NONE; /* No known auth. mechanism yet */
smtpc->sasl.authused = SASL_AUTH_NONE; /* Clear the authentication mechanism
@@ -338,7 +407,7 @@ static CURLcode smtp_perform_ehlo(struct Curl_easy *data)
result = Curl_pp_sendf(data, &smtpc->pp, "EHLO %s", smtpc->domain);
if(!result)
- smtp_state(data, SMTP_EHLO);
+ smtp_state(data, smtpc, SMTP_EHLO);
return result;
}
@@ -350,10 +419,9 @@ static CURLcode smtp_perform_ehlo(struct Curl_easy *data)
* Sends the HELO command to initialise communication with the SMTP server.
*/
static CURLcode smtp_perform_helo(struct Curl_easy *data,
- struct connectdata *conn)
+ struct smtp_conn *smtpc)
{
CURLcode result = CURLE_OK;
- struct smtp_conn *smtpc = &conn->proto.smtpc;
smtpc->sasl.authused = SASL_AUTH_NONE; /* No authentication mechanism used
in smtp connections */
@@ -362,7 +430,7 @@ static CURLcode smtp_perform_helo(struct Curl_easy *data,
result = Curl_pp_sendf(data, &smtpc->pp, "HELO %s", smtpc->domain);
if(!result)
- smtp_state(data, SMTP_HELO);
+ smtp_state(data, smtpc, SMTP_HELO);
return result;
}
@@ -374,14 +442,13 @@ static CURLcode smtp_perform_helo(struct Curl_easy *data,
* Sends the STLS command to start the upgrade to TLS.
*/
static CURLcode smtp_perform_starttls(struct Curl_easy *data,
- struct connectdata *conn)
+ struct smtp_conn *smtpc)
{
/* Send the STARTTLS command */
- CURLcode result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
- "%s", "STARTTLS");
+ CURLcode result = Curl_pp_sendf(data, &smtpc->pp, "%s", "STARTTLS");
if(!result)
- smtp_state(data, SMTP_STARTTLS);
+ smtp_state(data, smtpc, SMTP_STARTTLS);
return result;
}
@@ -392,33 +459,40 @@ static CURLcode smtp_perform_starttls(struct Curl_easy *data,
*
* Performs the upgrade to TLS.
*/
-static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data)
+static CURLcode smtp_perform_upgrade_tls(struct Curl_easy *data,
+ struct smtp_conn *smtpc)
{
+#ifdef USE_SSL
/* Start the SSL connection */
struct connectdata *conn = data->conn;
- struct smtp_conn *smtpc = &conn->proto.smtpc;
CURLcode result;
bool ssldone = FALSE;
+ DEBUGASSERT(smtpc->state == SMTP_UPGRADETLS);
if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
if(result)
goto out;
+ /* Change the connection handler and SMTP state */
+ conn->handler = &Curl_handler_smtps;
}
+ DEBUGASSERT(!smtpc->ssldone);
result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
- if(!result) {
+ DEBUGF(infof(data, "smtp_perform_upgrade_tls, connect -> %d, %d",
+ result, ssldone));
+ if(!result && ssldone) {
smtpc->ssldone = ssldone;
- if(smtpc->state != SMTP_UPGRADETLS)
- smtp_state(data, SMTP_UPGRADETLS);
-
- if(smtpc->ssldone) {
- smtp_to_smtps(conn);
- result = smtp_perform_ehlo(data);
- }
+ /* perform EHLO now, changes smtp->state out of SMTP_UPGRADETLS */
+ result = smtp_perform_ehlo(data, smtpc);
}
out:
return result;
+#else
+ (void)data;
+ (void)smtpc;
+ return CURLE_NOT_BUILT_IN;
+#endif
}
/***********************************************************************
@@ -433,9 +507,13 @@ static CURLcode smtp_perform_auth(struct Curl_easy *data,
const struct bufref *initresp)
{
CURLcode result = CURLE_OK;
- struct smtp_conn *smtpc = &data->conn->proto.smtpc;
+ struct smtp_conn *smtpc =
+ Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
const char *ir = (const char *) Curl_bufref_ptr(initresp);
+ if(!smtpc)
+ return CURLE_FAILED_INIT;
+
if(ir) { /* AUTH <mech> ...<crlf> */
/* Send the AUTH command with the initial response */
result = Curl_pp_sendf(data, &smtpc->pp, "AUTH %s %s", mech, ir);
@@ -458,10 +536,12 @@ static CURLcode smtp_continue_auth(struct Curl_easy *data,
const char *mech,
const struct bufref *resp)
{
- struct smtp_conn *smtpc = &data->conn->proto.smtpc;
+ struct smtp_conn *smtpc =
+ Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
(void)mech;
-
+ if(!smtpc)
+ return CURLE_FAILED_INIT;
return Curl_pp_sendf(data, &smtpc->pp,
"%s", (const char *) Curl_bufref_ptr(resp));
}
@@ -474,10 +554,12 @@ static CURLcode smtp_continue_auth(struct Curl_easy *data,
*/
static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech)
{
- struct smtp_conn *smtpc = &data->conn->proto.smtpc;
+ struct smtp_conn *smtpc =
+ Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
(void)mech;
-
+ if(!smtpc)
+ return CURLE_FAILED_INIT;
return Curl_pp_sendf(data, &smtpc->pp, "*");
}
@@ -488,18 +570,17 @@ static CURLcode smtp_cancel_auth(struct Curl_easy *data, const char *mech)
* Initiates the authentication sequence, with the appropriate SASL
* authentication mechanism.
*/
-static CURLcode smtp_perform_authentication(struct Curl_easy *data)
+static CURLcode smtp_perform_authentication(struct Curl_easy *data,
+ struct smtp_conn *smtpc)
{
CURLcode result = CURLE_OK;
- struct connectdata *conn = data->conn;
- struct smtp_conn *smtpc = &conn->proto.smtpc;
saslprogress progress;
/* Check we have enough data to authenticate with, and the
server supports authentication, and end the connect phase if not */
if(!smtpc->auth_supported ||
!Curl_sasl_can_authenticate(&smtpc->sasl, data)) {
- smtp_state(data, SMTP_STOP);
+ smtp_state(data, smtpc, SMTP_STOP);
return result;
}
@@ -508,12 +589,9 @@ static CURLcode smtp_perform_authentication(struct Curl_easy *data)
if(!result) {
if(progress == SASL_INPROGRESS)
- smtp_state(data, SMTP_AUTH);
- else {
- /* Other mechanisms not supported */
- infof(data, "No known authentication mechanisms supported");
- result = CURLE_LOGIN_DENIED;
- }
+ smtp_state(data, smtpc, SMTP_AUTH);
+ else
+ result = Curl_sasl_is_blocked(&smtpc->sasl, data);
}
return result;
@@ -523,13 +601,13 @@ static CURLcode smtp_perform_authentication(struct Curl_easy *data)
*
* smtp_perform_command()
*
- * Sends a SMTP based command.
+ * Sends an SMTP based command.
*/
-static CURLcode smtp_perform_command(struct Curl_easy *data)
+static CURLcode smtp_perform_command(struct Curl_easy *data,
+ struct smtp_conn *smtpc,
+ struct SMTP *smtp)
{
CURLcode result = CURLE_OK;
- struct connectdata *conn = data->conn;
- struct SMTP *smtp = data->req.p.smtp;
if(smtp->rcpt) {
/* We notify the server we are sending UTF-8 data if a) it supports the
@@ -551,13 +629,13 @@ static CURLcode smtp_perform_command(struct Curl_easy *data)
/* Establish whether we should report SMTPUTF8 to the server for this
mailbox as per RFC-6531 sect. 3.1 point 6 */
- utf8 = (conn->proto.smtpc.utf8_supported) &&
+ utf8 = (smtpc->utf8_supported) &&
((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
(!Curl_is_ASCII_name(host.name)));
/* Send the VRFY command (Note: The hostname part may be absent when the
host is a local system) */
- result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "VRFY %s%s%s%s",
+ result = Curl_pp_sendf(data, &smtpc->pp, "VRFY %s%s%s%s",
address,
host.name ? "@" : "",
host.name ? host.name : "",
@@ -569,11 +647,10 @@ static CURLcode smtp_perform_command(struct Curl_easy *data)
else {
/* Establish whether we should report that we support SMTPUTF8 for EXPN
commands to the server as per RFC-6531 sect. 3.1 point 6 */
- utf8 = (conn->proto.smtpc.utf8_supported) &&
- (!strcmp(smtp->custom, "EXPN"));
+ utf8 = (smtpc->utf8_supported) && (!strcmp(smtp->custom, "EXPN"));
/* Send the custom recipient based command such as the EXPN command */
- result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
+ result = Curl_pp_sendf(data, &smtpc->pp,
"%s %s%s", smtp->custom,
smtp->rcpt->data,
utf8 ? " SMTPUTF8" : "");
@@ -581,12 +658,12 @@ static CURLcode smtp_perform_command(struct Curl_easy *data)
}
else
/* Send the non-recipient based command such as HELP */
- result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s",
+ result = Curl_pp_sendf(data, &smtpc->pp, "%s",
smtp->custom && smtp->custom[0] != '\0' ?
smtp->custom : "HELP");
if(!result)
- smtp_state(data, SMTP_COMMAND);
+ smtp_state(data, smtpc, SMTP_COMMAND);
return result;
}
@@ -597,13 +674,14 @@ static CURLcode smtp_perform_command(struct Curl_easy *data)
*
* Sends an MAIL command to initiate the upload of a message.
*/
-static CURLcode smtp_perform_mail(struct Curl_easy *data)
+static CURLcode smtp_perform_mail(struct Curl_easy *data,
+ struct smtp_conn *smtpc,
+ struct SMTP *smtp)
{
char *from = NULL;
char *auth = NULL;
char *size = NULL;
CURLcode result = CURLE_OK;
- struct connectdata *conn = data->conn;
/* We notify the server we are sending UTF-8 data if a) it supports the
SMTPUTF8 extension and b) The mailbox contains UTF-8 characters, in
@@ -625,7 +703,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
/* Establish whether we should report SMTPUTF8 to the server for this
mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
- utf8 = (conn->proto.smtpc.utf8_supported) &&
+ utf8 = (smtpc->utf8_supported) &&
((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
(!Curl_is_ASCII_name(host.name)));
@@ -651,7 +729,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
}
/* Calculate the optional AUTH parameter */
- if(data->set.str[STRING_MAIL_AUTH] && conn->proto.smtpc.sasl.authused) {
+ if(data->set.str[STRING_MAIL_AUTH] && smtpc->sasl.authused) {
if(data->set.str[STRING_MAIL_AUTH][0] != '\0') {
char *address = NULL;
struct hostname host = { NULL, NULL, NULL, NULL };
@@ -665,7 +743,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
/* Establish whether we should report SMTPUTF8 to the server for this
mailbox as per RFC-6531 sect. 3.1 point 4 and sect. 3.4 */
- if((!utf8) && (conn->proto.smtpc.utf8_supported) &&
+ if((!utf8) && (smtpc->utf8_supported) &&
((host.encalloc) || (!Curl_is_ASCII_name(address)) ||
(!Curl_is_ASCII_name(host.name))))
utf8 = TRUE;
@@ -722,7 +800,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
}
/* Calculate the optional SIZE parameter */
- if(conn->proto.smtpc.size_supported && data->state.infilesize > 0) {
+ if(smtpc->size_supported && data->state.infilesize > 0) {
size = aprintf("%" FMT_OFF_T, data->state.infilesize);
if(!size) {
@@ -735,8 +813,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
based address then quickly scan through the recipient list and check if
any there do, as we need to correctly identify our support for SMTPUTF8
in the envelope, as per RFC-6531 sect. 3.4 */
- if(conn->proto.smtpc.utf8_supported && !utf8) {
- struct SMTP *smtp = data->req.p.smtp;
+ if(smtpc->utf8_supported && !utf8) {
struct curl_slist *rcpt = smtp->rcpt;
while(rcpt && !utf8) {
@@ -754,7 +831,7 @@ static CURLcode smtp_perform_mail(struct Curl_easy *data)
goto out;
/* Send the MAIL command */
- result = Curl_pp_sendf(data, &conn->proto.smtpc.pp,
+ result = Curl_pp_sendf(data, &smtpc->pp,
"MAIL FROM:%s%s%s%s%s%s",
from, /* Mandatory */
auth ? " AUTH=" : "", /* Optional on AUTH support */
@@ -770,7 +847,7 @@ out:
free(size);
if(!result)
- smtp_state(data, SMTP_MAIL);
+ smtp_state(data, smtpc, SMTP_MAIL);
return result;
}
@@ -782,11 +859,11 @@ out:
* Sends a RCPT TO command for a given recipient as part of the message upload
* process.
*/
-static CURLcode smtp_perform_rcpt_to(struct Curl_easy *data)
+static CURLcode smtp_perform_rcpt_to(struct Curl_easy *data,
+ struct smtp_conn *smtpc,
+ struct SMTP *smtp)
{
CURLcode result = CURLE_OK;
- struct connectdata *conn = data->conn;
- struct SMTP *smtp = data->req.p.smtp;
char *address = NULL;
struct hostname host = { NULL, NULL, NULL, NULL };
@@ -799,19 +876,18 @@ static CURLcode smtp_perform_rcpt_to(struct Curl_easy *data)
/* Send the RCPT TO command */
if(host.name)
- result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "RCPT TO:<%s@%s>",
+ result = Curl_pp_sendf(data, &smtpc->pp, "RCPT TO:<%s@%s>",
address, host.name);
else
/* An invalid mailbox was provided but we will simply let the server worry
about that and reply with a 501 error */
- result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "RCPT TO:<%s>",
- address);
+ result = Curl_pp_sendf(data, &smtpc->pp, "RCPT TO:<%s>", address);
Curl_free_idnconverted_hostname(&host);
free(address);
if(!result)
- smtp_state(data, SMTP_RCPT);
+ smtp_state(data, smtpc, SMTP_RCPT);
return result;
}
@@ -823,19 +899,20 @@ static CURLcode smtp_perform_rcpt_to(struct Curl_easy *data)
* Performs the quit action prior to sclose() being called.
*/
static CURLcode smtp_perform_quit(struct Curl_easy *data,
- struct connectdata *conn)
+ struct smtp_conn *smtpc)
{
/* Send the QUIT command */
- CURLcode result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", "QUIT");
+ CURLcode result = Curl_pp_sendf(data, &smtpc->pp, "%s", "QUIT");
if(!result)
- smtp_state(data, SMTP_QUIT);
+ smtp_state(data, smtpc, SMTP_QUIT);
return result;
}
/* For the initial server greeting */
static CURLcode smtp_state_servergreet_resp(struct Curl_easy *data,
+ struct smtp_conn *smtpc,
int smtpcode,
smtpstate instate)
{
@@ -847,13 +924,14 @@ static CURLcode smtp_state_servergreet_resp(struct Curl_easy *data,
result = CURLE_WEIRD_SERVER_REPLY;
}
else
- result = smtp_perform_ehlo(data);
+ result = smtp_perform_ehlo(data, smtpc);
return result;
}
/* For STARTTLS responses */
static CURLcode smtp_state_starttls_resp(struct Curl_easy *data,
+ struct smtp_conn *smtpc,
int smtpcode,
smtpstate instate)
{
@@ -861,7 +939,7 @@ static CURLcode smtp_state_starttls_resp(struct Curl_easy *data,
(void)instate; /* no use for this yet */
/* Pipelining in response is forbidden. */
- if(data->conn->proto.smtpc.pp.overflow)
+ if(smtpc->pp.overflow)
return CURLE_WEIRD_SERVER_REPLY;
if(smtpcode != 220) {
@@ -870,30 +948,30 @@ static CURLcode smtp_state_starttls_resp(struct Curl_easy *data,
result = CURLE_USE_SSL_FAILED;
}
else
- result = smtp_perform_authentication(data);
+ result = smtp_perform_authentication(data, smtpc);
}
else
- result = smtp_perform_upgrade_tls(data);
+ smtp_state(data, smtpc, SMTP_UPGRADETLS);
return result;
}
/* For EHLO responses */
static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data,
- struct connectdata *conn, int smtpcode,
+ struct smtp_conn *smtpc,
+ int smtpcode,
smtpstate instate)
{
CURLcode result = CURLE_OK;
- struct smtp_conn *smtpc = &conn->proto.smtpc;
- const char *line = Curl_dyn_ptr(&smtpc->pp.recvbuf);
+ const char *line = curlx_dyn_ptr(&smtpc->pp.recvbuf);
size_t len = smtpc->pp.nfinal;
(void)instate; /* no use for this yet */
if(smtpcode/100 != 2 && smtpcode != 1) {
if(data->set.use_ssl <= CURLUSESSL_TRY
- || Curl_conn_is_ssl(conn, FIRSTSOCKET))
- result = smtp_perform_helo(data, conn);
+ || Curl_conn_is_ssl(data->conn, FIRSTSOCKET))
+ result = smtp_perform_helo(data, smtpc);
else {
failf(data, "Remote access denied: %d", smtpcode);
result = CURLE_REMOTE_ACCESS_DENIED;
@@ -957,21 +1035,21 @@ static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data,
}
if(smtpcode != 1) {
- if(data->set.use_ssl && !Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
- /* We do not have a SSL/TLS connection yet, but SSL is requested */
+ if(data->set.use_ssl && !Curl_conn_is_ssl(data->conn, FIRSTSOCKET)) {
+ /* We do not have an SSL/TLS connection yet, but SSL is requested */
if(smtpc->tls_supported)
/* Switch to TLS connection now */
- result = smtp_perform_starttls(data, conn);
+ result = smtp_perform_starttls(data, smtpc);
else if(data->set.use_ssl == CURLUSESSL_TRY)
/* Fallback and carry on with authentication */
- result = smtp_perform_authentication(data);
+ result = smtp_perform_authentication(data, smtpc);
else {
failf(data, "STARTTLS not supported.");
result = CURLE_USE_SSL_FAILED;
}
}
else
- result = smtp_perform_authentication(data);
+ result = smtp_perform_authentication(data, smtpc);
}
}
else {
@@ -983,7 +1061,9 @@ static CURLcode smtp_state_ehlo_resp(struct Curl_easy *data,
}
/* For HELO responses */
-static CURLcode smtp_state_helo_resp(struct Curl_easy *data, int smtpcode,
+static CURLcode smtp_state_helo_resp(struct Curl_easy *data,
+ struct smtp_conn *smtpc,
+ int smtpcode,
smtpstate instate)
{
CURLcode result = CURLE_OK;
@@ -995,19 +1075,18 @@ static CURLcode smtp_state_helo_resp(struct Curl_easy *data, int smtpcode,
}
else
/* End of connect phase */
- smtp_state(data, SMTP_STOP);
+ smtp_state(data, smtpc, SMTP_STOP);
return result;
}
/* For SASL authentication responses */
static CURLcode smtp_state_auth_resp(struct Curl_easy *data,
+ struct smtp_conn *smtpc,
int smtpcode,
smtpstate instate)
{
CURLcode result = CURLE_OK;
- struct connectdata *conn = data->conn;
- struct smtp_conn *smtpc = &conn->proto.smtpc;
saslprogress progress;
(void)instate; /* no use for this yet */
@@ -1016,7 +1095,7 @@ static CURLcode smtp_state_auth_resp(struct Curl_easy *data,
if(!result)
switch(progress) {
case SASL_DONE:
- smtp_state(data, SMTP_STOP); /* Authenticated */
+ smtp_state(data, smtpc, SMTP_STOP); /* Authenticated */
break;
case SASL_IDLE: /* No mechanism left after cancellation */
failf(data, "Authentication cancelled");
@@ -1030,13 +1109,15 @@ static CURLcode smtp_state_auth_resp(struct Curl_easy *data,
}
/* For command responses */
-static CURLcode smtp_state_command_resp(struct Curl_easy *data, int smtpcode,
+static CURLcode smtp_state_command_resp(struct Curl_easy *data,
+ struct smtp_conn *smtpc,
+ struct SMTP *smtp,
+ int smtpcode,
smtpstate instate)
{
CURLcode result = CURLE_OK;
- struct SMTP *smtp = data->req.p.smtp;
- char *line = Curl_dyn_ptr(&data->conn->proto.smtpc.pp.recvbuf);
- size_t len = data->conn->proto.smtpc.pp.nfinal;
+ char *line = curlx_dyn_ptr(&smtpc->pp.recvbuf);
+ size_t len = smtpc->pp.nfinal;
(void)instate; /* no use for this yet */
@@ -1055,15 +1136,15 @@ static CURLcode smtp_state_command_resp(struct Curl_easy *data, int smtpcode,
if(smtp->rcpt) {
/* Send the next command */
- result = smtp_perform_command(data);
+ result = smtp_perform_command(data, smtpc, smtp);
}
else
/* End of DO phase */
- smtp_state(data, SMTP_STOP);
+ smtp_state(data, smtpc, SMTP_STOP);
}
else
/* End of DO phase */
- smtp_state(data, SMTP_STOP);
+ smtp_state(data, smtpc, SMTP_STOP);
}
}
@@ -1071,7 +1152,10 @@ static CURLcode smtp_state_command_resp(struct Curl_easy *data, int smtpcode,
}
/* For MAIL responses */
-static CURLcode smtp_state_mail_resp(struct Curl_easy *data, int smtpcode,
+static CURLcode smtp_state_mail_resp(struct Curl_easy *data,
+ struct smtp_conn *smtpc,
+ struct SMTP *smtp,
+ int smtpcode,
smtpstate instate)
{
CURLcode result = CURLE_OK;
@@ -1083,18 +1167,19 @@ static CURLcode smtp_state_mail_resp(struct Curl_easy *data, int smtpcode,
}
else
/* Start the RCPT TO command */
- result = smtp_perform_rcpt_to(data);
+ result = smtp_perform_rcpt_to(data, smtpc, smtp);
return result;
}
/* For RCPT responses */
static CURLcode smtp_state_rcpt_resp(struct Curl_easy *data,
- struct connectdata *conn, int smtpcode,
+ struct smtp_conn *smtpc,
+ struct SMTP *smtp,
+ int smtpcode,
smtpstate instate)
{
CURLcode result = CURLE_OK;
- struct SMTP *smtp = data->req.p.smtp;
bool is_smtp_err = FALSE;
bool is_smtp_blocking_err = FALSE;
@@ -1126,7 +1211,7 @@ static CURLcode smtp_state_rcpt_resp(struct Curl_easy *data,
if(smtp->rcpt)
/* Send the next RCPT TO command */
- result = smtp_perform_rcpt_to(data);
+ result = smtp_perform_rcpt_to(data, smtpc, smtp);
else {
/* We were not able to issue a successful RCPT TO command while going
over recipients (potentially multiple). Sending back last error. */
@@ -1136,10 +1221,10 @@ static CURLcode smtp_state_rcpt_resp(struct Curl_easy *data,
}
else {
/* Send the DATA command */
- result = Curl_pp_sendf(data, &conn->proto.smtpc.pp, "%s", "DATA");
+ result = Curl_pp_sendf(data, &smtpc->pp, "%s", "DATA");
if(!result)
- smtp_state(data, SMTP_DATA);
+ smtp_state(data, smtpc, SMTP_DATA);
}
}
}
@@ -1148,7 +1233,9 @@ static CURLcode smtp_state_rcpt_resp(struct Curl_easy *data,
}
/* For DATA response */
-static CURLcode smtp_state_data_resp(struct Curl_easy *data, int smtpcode,
+static CURLcode smtp_state_data_resp(struct Curl_easy *data,
+ struct smtp_conn *smtpc,
+ int smtpcode,
smtpstate instate)
{
CURLcode result = CURLE_OK;
@@ -1166,7 +1253,7 @@ static CURLcode smtp_state_data_resp(struct Curl_easy *data, int smtpcode,
Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
/* End of DO phase */
- smtp_state(data, SMTP_STOP);
+ smtp_state(data, smtpc, SMTP_STOP);
}
return result;
@@ -1175,6 +1262,7 @@ static CURLcode smtp_state_data_resp(struct Curl_easy *data, int smtpcode,
/* For POSTDATA responses, which are received after the entire DATA
part has been sent to the server */
static CURLcode smtp_state_postdata_resp(struct Curl_easy *data,
+ struct smtp_conn *smtpc,
int smtpcode,
smtpstate instate)
{
@@ -1186,32 +1274,39 @@ static CURLcode smtp_state_postdata_resp(struct Curl_easy *data,
result = CURLE_WEIRD_SERVER_REPLY;
/* End of DONE phase */
- smtp_state(data, SMTP_STOP);
+ smtp_state(data, smtpc, SMTP_STOP);
return result;
}
-static CURLcode smtp_statemachine(struct Curl_easy *data,
- struct connectdata *conn)
+static CURLcode smtp_pp_statemachine(struct Curl_easy *data,
+ struct connectdata *conn)
{
CURLcode result = CURLE_OK;
int smtpcode;
- struct smtp_conn *smtpc = &conn->proto.smtpc;
- struct pingpong *pp = &smtpc->pp;
+ struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN);
+ struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY);
size_t nread = 0;
+ if(!smtpc || !smtp)
+ return CURLE_FAILED_INIT;
+
/* Busy upgrading the connection; right now all I/O is SSL/TLS, not SMTP */
upgrade_tls:
- if(smtpc->state == SMTP_UPGRADETLS)
- return smtp_perform_upgrade_tls(data);
+ if(smtpc->state == SMTP_UPGRADETLS) {
+ result = smtp_perform_upgrade_tls(data, smtpc);
+ if(result || (smtpc->state == SMTP_UPGRADETLS))
+ return result;
+ }
/* Flush any data that needs to be sent */
- if(pp->sendleft)
- return Curl_pp_flushsend(data, pp);
+ if(smtpc->pp.sendleft)
+ return Curl_pp_flushsend(data, &smtpc->pp);
do {
/* Read the response from the server */
- result = Curl_pp_readresp(data, FIRSTSOCKET, pp, &smtpcode, &nread);
+ result = Curl_pp_readresp(data, FIRSTSOCKET, &smtpc->pp,
+ &smtpcode, &nread);
if(result)
return result;
@@ -1225,19 +1320,20 @@ upgrade_tls:
/* We have now received a full SMTP server response */
switch(smtpc->state) {
case SMTP_SERVERGREET:
- result = smtp_state_servergreet_resp(data, smtpcode, smtpc->state);
+ result = smtp_state_servergreet_resp(data, smtpc,
+ smtpcode, smtpc->state);
break;
case SMTP_EHLO:
- result = smtp_state_ehlo_resp(data, conn, smtpcode, smtpc->state);
+ result = smtp_state_ehlo_resp(data, smtpc, smtpcode, smtpc->state);
break;
case SMTP_HELO:
- result = smtp_state_helo_resp(data, smtpcode, smtpc->state);
+ result = smtp_state_helo_resp(data, smtpc, smtpcode, smtpc->state);
break;
case SMTP_STARTTLS:
- result = smtp_state_starttls_resp(data, smtpcode, smtpc->state);
+ result = smtp_state_starttls_resp(data, smtpc, smtpcode, smtpc->state);
/* During UPGRADETLS, leave the read loop as we need to connect
* (e.g. TLS handshake) before we continue sending/receiving. */
if(!result && (smtpc->state == SMTP_UPGRADETLS))
@@ -1245,36 +1341,38 @@ upgrade_tls:
break;
case SMTP_AUTH:
- result = smtp_state_auth_resp(data, smtpcode, smtpc->state);
+ result = smtp_state_auth_resp(data, smtpc, smtpcode, smtpc->state);
break;
case SMTP_COMMAND:
- result = smtp_state_command_resp(data, smtpcode, smtpc->state);
+ result = smtp_state_command_resp(data, smtpc, smtp,
+ smtpcode, smtpc->state);
break;
case SMTP_MAIL:
- result = smtp_state_mail_resp(data, smtpcode, smtpc->state);
+ result = smtp_state_mail_resp(data, smtpc, smtp, smtpcode, smtpc->state);
break;
case SMTP_RCPT:
- result = smtp_state_rcpt_resp(data, conn, smtpcode, smtpc->state);
+ result = smtp_state_rcpt_resp(data, smtpc, smtp, smtpcode, smtpc->state);
break;
case SMTP_DATA:
- result = smtp_state_data_resp(data, smtpcode, smtpc->state);
+ result = smtp_state_data_resp(data, smtpc, smtpcode, smtpc->state);
break;
case SMTP_POSTDATA:
- result = smtp_state_postdata_resp(data, smtpcode, smtpc->state);
+ result = smtp_state_postdata_resp(data, smtpc, smtpcode, smtpc->state);
break;
case SMTP_QUIT:
default:
/* internal error */
- smtp_state(data, SMTP_STOP);
+ smtp_state(data, smtpc, SMTP_STOP);
break;
}
- } while(!result && smtpc->state != SMTP_STOP && Curl_pp_moredata(pp));
+ } while(!result && smtpc->state != SMTP_STOP &&
+ Curl_pp_moredata(&smtpc->pp));
return result;
}
@@ -1283,29 +1381,23 @@ upgrade_tls:
static CURLcode smtp_multi_statemach(struct Curl_easy *data, bool *done)
{
CURLcode result = CURLE_OK;
- struct connectdata *conn = data->conn;
- struct smtp_conn *smtpc = &conn->proto.smtpc;
+ struct smtp_conn *smtpc =
+ Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
- if((conn->handler->flags & PROTOPT_SSL) && !smtpc->ssldone) {
- bool ssldone = FALSE;
- result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssldone);
- smtpc->ssldone = ssldone;
- if(result || !smtpc->ssldone)
- return result;
- }
+ *done = FALSE;
+ if(!smtpc)
+ return CURLE_FAILED_INIT;
result = Curl_pp_statemach(data, &smtpc->pp, FALSE, FALSE);
*done = (smtpc->state == SMTP_STOP);
-
return result;
}
static CURLcode smtp_block_statemach(struct Curl_easy *data,
- struct connectdata *conn,
+ struct smtp_conn *smtpc,
bool disconnecting)
{
CURLcode result = CURLE_OK;
- struct smtp_conn *smtpc = &conn->proto.smtpc;
while(smtpc->state != SMTP_STOP && !result)
result = Curl_pp_statemach(data, &smtpc->pp, TRUE, disconnecting);
@@ -1313,25 +1405,13 @@ static CURLcode smtp_block_statemach(struct Curl_easy *data,
return result;
}
-/* Allocate and initialize the SMTP struct for the current Curl_easy if
- required */
-static CURLcode smtp_init(struct Curl_easy *data)
-{
- CURLcode result = CURLE_OK;
- struct SMTP *smtp;
-
- smtp = data->req.p.smtp = calloc(1, sizeof(struct SMTP));
- if(!smtp)
- result = CURLE_OUT_OF_MEMORY;
-
- return result;
-}
-
/* For the SMTP "protocol connect" and "doing" phases only */
static int smtp_getsock(struct Curl_easy *data,
struct connectdata *conn, curl_socket_t *socks)
{
- return Curl_pp_getsock(data, &conn->proto.smtpc.pp, socks);
+ struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN);
+ return smtpc ?
+ Curl_pp_getsock(data, &smtpc->pp, socks) : GETSOCK_BLANK;
}
/***********************************************************************
@@ -1346,36 +1426,37 @@ static int smtp_getsock(struct Curl_easy *data,
*/
static CURLcode smtp_connect(struct Curl_easy *data, bool *done)
{
+ struct smtp_conn *smtpc =
+ Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
CURLcode result = CURLE_OK;
- struct connectdata *conn = data->conn;
- struct smtp_conn *smtpc = &conn->proto.smtpc;
- struct pingpong *pp = &smtpc->pp;
*done = FALSE; /* default to not done yet */
+ if(!smtpc)
+ return CURLE_FAILED_INIT;
/* We always support persistent connections in SMTP */
- connkeep(conn, "SMTP default");
+ connkeep(data->conn, "SMTP default");
- PINGPONG_SETUP(pp, smtp_statemachine, smtp_endofresp);
+ PINGPONG_SETUP(&smtpc->pp, smtp_pp_statemachine, smtp_endofresp);
/* Initialize the SASL storage */
Curl_sasl_init(&smtpc->sasl, data, &saslsmtp);
/* Initialise the pingpong layer */
- Curl_pp_init(pp);
+ Curl_pp_init(&smtpc->pp);
/* Parse the URL options */
- result = smtp_parse_url_options(conn);
+ result = smtp_parse_url_options(data->conn, smtpc);
if(result)
return result;
/* Parse the URL path */
- result = smtp_parse_url_path(data);
+ result = smtp_parse_url_path(data, smtpc);
if(result)
return result;
/* Start off waiting for the server greeting response */
- smtp_state(data, SMTP_SERVERGREET);
+ smtp_state(data, smtpc, SMTP_SERVERGREET);
result = smtp_multi_statemach(data, done);
@@ -1394,12 +1475,16 @@ static CURLcode smtp_connect(struct Curl_easy *data, bool *done)
static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
bool premature)
{
+ struct smtp_conn *smtpc =
+ Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
- struct SMTP *smtp = data->req.p.smtp;
+ struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY);
(void)premature;
+ if(!smtpc)
+ return CURLE_FAILED_INIT;
if(!smtp)
return CURLE_OK;
@@ -1413,10 +1498,10 @@ static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
else if(!data->set.connect_only && data->set.mail_rcpt &&
(data->state.upload || IS_MIME_POST(data))) {
- smtp_state(data, SMTP_POSTDATA);
+ smtp_state(data, smtpc, SMTP_POSTDATA);
/* Run the state-machine */
- result = smtp_block_statemach(data, conn, FALSE);
+ result = smtp_block_statemach(data, smtpc, FALSE);
}
/* Clear the transfer mode for the next request */
@@ -1433,12 +1518,14 @@ static CURLcode smtp_done(struct Curl_easy *data, CURLcode status,
* This is the actual DO function for SMTP. Transfer a mail, send a command
* or get some data according to the options previously setup.
*/
-static CURLcode smtp_perform(struct Curl_easy *data, bool *connected,
+static CURLcode smtp_perform(struct Curl_easy *data,
+ struct smtp_conn *smtpc,
+ struct SMTP *smtp,
+ bool *connected,
bool *dophase_done)
{
/* This is SMTP and no proxy */
CURLcode result = CURLE_OK;
- struct SMTP *smtp = data->req.p.smtp;
CURL_TRC_SMTP(data, "smtp_perform(), start");
@@ -1466,10 +1553,10 @@ static CURLcode smtp_perform(struct Curl_easy *data, bool *connected,
/* Start the first command in the DO phase */
if((data->state.upload || IS_MIME_POST(data)) && data->set.mail_rcpt)
/* MAIL transfer */
- result = smtp_perform_mail(data);
+ result = smtp_perform_mail(data, smtpc, smtp);
else
/* SMTP based command (VRFY, EXPN, NOOP, RSET or HELP) */
- result = smtp_perform_command(data);
+ result = smtp_perform_command(data, smtpc, smtp);
if(result)
goto out;
@@ -1496,17 +1583,23 @@ out:
*/
static CURLcode smtp_do(struct Curl_easy *data, bool *done)
{
+ struct smtp_conn *smtpc =
+ Curl_conn_meta_get(data->conn, CURL_META_SMTP_CONN);
+ struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY);
CURLcode result = CURLE_OK;
+
DEBUGASSERT(data);
DEBUGASSERT(data->conn);
*done = FALSE; /* default to false */
+ if(!smtpc || !smtp)
+ return CURLE_FAILED_INIT;
/* Parse the custom request */
- result = smtp_parse_custom_request(data);
+ result = smtp_parse_custom_request(data, smtp);
if(result)
return result;
- result = smtp_regular_transfer(data, done);
+ result = smtp_regular_transfer(data, smtpc, smtp, done);
CURL_TRC_SMTP(data, "smtp_do() -> %d, done=%d", result, *done);
return result;
}
@@ -1522,36 +1615,32 @@ static CURLcode smtp_disconnect(struct Curl_easy *data,
struct connectdata *conn,
bool dead_connection)
{
- struct smtp_conn *smtpc = &conn->proto.smtpc;
+ struct smtp_conn *smtpc = Curl_conn_meta_get(conn, CURL_META_SMTP_CONN);
+
(void)data;
+ if(!smtpc)
+ return CURLE_FAILED_INIT;
/* We cannot send quit unconditionally. If this connection is stale or
bad in any way, sending quit and waiting around here will make the
disconnect wait in vain and cause more problems than we need to. */
if(!dead_connection && conn->bits.protoconnstart) {
- if(!smtp_perform_quit(data, conn))
- (void)smtp_block_statemach(data, conn, TRUE); /* ignore errors on QUIT */
+ if(!smtp_perform_quit(data, smtpc))
+ (void)smtp_block_statemach(data, smtpc, TRUE); /* ignore on QUIT */
}
- /* Disconnect from the server */
- Curl_pp_disconnect(&smtpc->pp);
-
/* Cleanup the SASL module */
Curl_sasl_cleanup(conn, smtpc->sasl.authused);
-
- /* Cleanup our connection based variables */
- Curl_safefree(smtpc->domain);
CURL_TRC_SMTP(data, "smtp_disconnect(), finished");
-
return CURLE_OK;
}
/* Call this when the DO phase has completed */
-static CURLcode smtp_dophase_done(struct Curl_easy *data, bool connected)
+static CURLcode smtp_dophase_done(struct Curl_easy *data,
+ struct SMTP *smtp,
+ bool connected)
{
- struct SMTP *smtp = data->req.p.smtp;
-
(void)connected;
if(smtp->transfer != PPTRANSFER_BODY)
@@ -1564,12 +1653,16 @@ static CURLcode smtp_dophase_done(struct Curl_easy *data, bool connected)
/* Called from multi.c while DOing */
static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done)
{
- CURLcode result = smtp_multi_statemach(data, dophase_done);
+ struct SMTP *smtp = Curl_meta_get(data, CURL_META_SMTP_EASY);
+ CURLcode result;
+ if(!smtp)
+ return CURLE_FAILED_INIT;
+ result = smtp_multi_statemach(data, dophase_done);
if(result)
DEBUGF(infof(data, "DO phase failed"));
else if(*dophase_done) {
- result = smtp_dophase_done(data, FALSE /* not connected */);
+ result = smtp_dophase_done(data, smtp, FALSE /* not connected */);
DEBUGF(infof(data, "DO phase is complete"));
}
@@ -1588,6 +1681,8 @@ static CURLcode smtp_doing(struct Curl_easy *data, bool *dophase_done)
* remote host.
*/
static CURLcode smtp_regular_transfer(struct Curl_easy *data,
+ struct smtp_conn *smtpc,
+ struct SMTP *smtp,
bool *dophase_done)
{
CURLcode result = CURLE_OK;
@@ -1603,27 +1698,56 @@ static CURLcode smtp_regular_transfer(struct Curl_easy *data,
Curl_pgrsSetDownloadSize(data, -1);
/* Carry out the perform */
- result = smtp_perform(data, &connected, dophase_done);
+ result = smtp_perform(data, smtpc, smtp, &connected, dophase_done);
/* Perform post DO phase operations if necessary */
if(!result && *dophase_done)
- result = smtp_dophase_done(data, connected);
+ result = smtp_dophase_done(data, smtp, connected);
CURL_TRC_SMTP(data, "smtp_regular_transfer() -> %d, done=%d",
result, *dophase_done);
return result;
}
+
+static void smtp_easy_dtor(void *key, size_t klen, void *entry)
+{
+ struct SMTP *smtp = entry;
+ (void)key;
+ (void)klen;
+ free(smtp);
+}
+
+static void smtp_conn_dtor(void *key, size_t klen, void *entry)
+{
+ struct smtp_conn *smtpc = entry;
+ (void)key;
+ (void)klen;
+ Curl_pp_disconnect(&smtpc->pp);
+ Curl_safefree(smtpc->domain);
+ free(smtpc);
+}
+
static CURLcode smtp_setup_connection(struct Curl_easy *data,
struct connectdata *conn)
{
- CURLcode result;
+ struct smtp_conn *smtpc;
+ struct SMTP *smtp;
+ CURLcode result = CURLE_OK;
- /* Clear the TLS upgraded flag */
- conn->bits.tls_upgraded = FALSE;
+ smtpc = calloc(1, sizeof(*smtpc));
+ if(!smtpc ||
+ Curl_conn_meta_set(conn, CURL_META_SMTP_CONN, smtpc, smtp_conn_dtor)) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ smtp = calloc(1, sizeof(*smtp));
+ if(!smtp ||
+ Curl_meta_set(data, CURL_META_SMTP_EASY, smtp, smtp_easy_dtor))
+ result = CURLE_OUT_OF_MEMORY;
- /* Initialise the SMTP layer */
- result = smtp_init(data);
+out:
CURL_TRC_SMTP(data, "smtp_setup_connection() -> %d", result);
return result;
}
@@ -1634,10 +1758,10 @@ static CURLcode smtp_setup_connection(struct Curl_easy *data,
*
* Parse the URL login options.
*/
-static CURLcode smtp_parse_url_options(struct connectdata *conn)
+static CURLcode smtp_parse_url_options(struct connectdata *conn,
+ struct smtp_conn *smtpc)
{
CURLcode result = CURLE_OK;
- struct smtp_conn *smtpc = &conn->proto.smtpc;
const char *ptr = conn->options;
while(!result && ptr && *ptr) {
@@ -1671,11 +1795,10 @@ static CURLcode smtp_parse_url_options(struct connectdata *conn)
*
* Parse the URL path into separate path components.
*/
-static CURLcode smtp_parse_url_path(struct Curl_easy *data)
+static CURLcode smtp_parse_url_path(struct Curl_easy *data,
+ struct smtp_conn *smtpc)
{
/* The SMTP struct is already initialised in smtp_connect() */
- struct connectdata *conn = data->conn;
- struct smtp_conn *smtpc = &conn->proto.smtpc;
const char *path = &data->state.up.path[1]; /* skip leading path */
char localhost[HOSTNAME_MAX + 1];
@@ -1697,10 +1820,10 @@ static CURLcode smtp_parse_url_path(struct Curl_easy *data)
*
* Parse the custom request.
*/
-static CURLcode smtp_parse_custom_request(struct Curl_easy *data)
+static CURLcode smtp_parse_custom_request(struct Curl_easy *data,
+ struct SMTP *smtp)
{
CURLcode result = CURLE_OK;
- struct SMTP *smtp = data->req.p.smtp;
const char *custom = data->set.str[STRING_CUSTOMREQUEST];
/* URL decode the custom request */
diff --git a/libs/libcurl/src/smtp.h b/libs/libcurl/src/smtp.h
index c6b33515b7..ef0f0ed8a5 100644
--- a/libs/libcurl/src/smtp.h
+++ b/libs/libcurl/src/smtp.h
@@ -27,60 +27,6 @@
#include "pingpong.h"
#include "curl_sasl.h"
-/****************************************************************************
- * SMTP unique setup
- ***************************************************************************/
-typedef enum {
- SMTP_STOP, /* do nothing state, stops the state machine */
- SMTP_SERVERGREET, /* waiting for the initial greeting immediately after
- a connect */
- SMTP_EHLO,
- SMTP_HELO,
- SMTP_STARTTLS,
- SMTP_UPGRADETLS, /* asynchronously upgrade the connection to SSL/TLS
- (multi mode only) */
- SMTP_AUTH,
- SMTP_COMMAND, /* VRFY, EXPN, NOOP, RSET and HELP */
- SMTP_MAIL, /* MAIL FROM */
- SMTP_RCPT, /* RCPT TO */
- SMTP_DATA,
- SMTP_POSTDATA,
- SMTP_QUIT,
- SMTP_LAST /* never used */
-} smtpstate;
-
-/* This SMTP struct is used in the Curl_easy. All SMTP data that is
- connection-oriented must be in smtp_conn to properly deal with the fact that
- perhaps the Curl_easy is changed between the times the connection is
- used. */
-struct SMTP {
- curl_pp_transfer transfer;
- char *custom; /* Custom Request */
- struct curl_slist *rcpt; /* Recipient list */
- int rcpt_last_error; /* The last error received for RCPT TO command */
- size_t eob; /* Number of bytes of the EOB (End Of Body) that
- have been received so far */
- BIT(rcpt_had_ok); /* Whether any of RCPT TO commands (depends on
- total number of recipients) succeeded so far */
- BIT(trailing_crlf); /* Specifies if the trailing CRLF is present */
-};
-
-/* smtp_conn is used for struct connection-oriented data in the connectdata
- struct */
-struct smtp_conn {
- struct pingpong pp;
- struct SASL sasl; /* SASL-related storage */
- smtpstate state; /* Always use smtp.c:state() to change state! */
- char *domain; /* Client address/name to send in the EHLO */
- BIT(ssldone); /* Is connect() over SSL done? */
- BIT(tls_supported); /* StartTLS capability supported by server */
- BIT(size_supported); /* If server supports SIZE extension according to
- RFC 1870 */
- BIT(utf8_supported); /* If server supports SMTPUTF8 extension according
- to RFC 6531 */
- BIT(auth_supported); /* AUTH capability supported by server */
-};
-
extern const struct Curl_handler Curl_handler_smtp;
extern const struct Curl_handler Curl_handler_smtps;
diff --git a/libs/libcurl/src/socketpair.c b/libs/libcurl/src/socketpair.c
index 22b067335a..b194831312 100644
--- a/libs/libcurl/src/socketpair.c
+++ b/libs/libcurl/src/socketpair.c
@@ -27,10 +27,9 @@
#include "urldata.h"
#include "rand.h"
-#if defined(USE_EVENTFD)
-#ifdef HAVE_SYS_EVENTFD_H
+#ifdef USE_EVENTFD
+
#include <sys/eventfd.h>
-#endif
int Curl_eventfd(curl_socket_t socks[2], bool nonblocking)
{
@@ -42,18 +41,25 @@ int Curl_eventfd(curl_socket_t socks[2], bool nonblocking)
socks[0] = socks[1] = efd;
return 0;
}
+
#elif defined(HAVE_PIPE)
+
#ifdef HAVE_FCNTL
#include <fcntl.h>
#endif
int Curl_pipe(curl_socket_t socks[2], bool nonblocking)
{
+#ifdef HAVE_PIPE2
+ int flags = nonblocking ? O_NONBLOCK | O_CLOEXEC : O_CLOEXEC;
+ if(pipe2(socks, flags))
+ return -1;
+#else
if(pipe(socks))
return -1;
#ifdef HAVE_FCNTL
if(fcntl(socks[0], F_SETFD, FD_CLOEXEC) ||
- fcntl(socks[1], F_SETFD, FD_CLOEXEC) ) {
+ fcntl(socks[1], F_SETFD, FD_CLOEXEC)) {
close(socks[0]);
close(socks[1]);
socks[0] = socks[1] = CURL_SOCKET_BAD;
@@ -69,11 +75,12 @@ int Curl_pipe(curl_socket_t socks[2], bool nonblocking)
return -1;
}
}
+#endif
return 0;
}
-#endif
+#endif /* USE_EVENTFD */
#ifndef CURL_DISABLE_SOCKETPAIR
#ifdef HAVE_SOCKETPAIR
@@ -103,7 +110,9 @@ int Curl_socketpair(int domain, int type, int protocol,
* This is a socketpair() implementation for Windows.
*/
#include <string.h>
+#ifdef HAVE_IO_H
#include <io.h>
+#endif
#else
#ifdef HAVE_NETDB_H
#include <netdb.h>
@@ -119,8 +128,8 @@ int Curl_socketpair(int domain, int type, int protocol,
#endif /* !INADDR_LOOPBACK */
#endif /* !_WIN32 */
-#include "nonblock.h" /* for curlx_nonblock */
-#include "timeval.h" /* needed before select.h */
+#include "curlx/nonblock.h" /* for curlx_nonblock */
+#include "curlx/timeval.h" /* needed before select.h */
#include "select.h" /* for Curl_poll */
/* The last 3 #include files should be in this order */
@@ -194,7 +203,7 @@ int Curl_socketpair(int domain, int type, int protocol,
if(socks[1] == CURL_SOCKET_BAD)
goto error;
else {
- struct curltime start = Curl_now();
+ struct curltime start = curlx_now();
char rnd[9];
char check[sizeof(rnd)];
char *p = &check[0];
@@ -218,18 +227,18 @@ int Curl_socketpair(int domain, int type, int protocol,
if(nread == -1) {
int sockerr = SOCKERRNO;
/* Do not block forever */
- if(Curl_timediff(Curl_now(), start) > (60 * 1000))
+ if(curlx_timediff(curlx_now(), start) > (60 * 1000))
goto error;
if(
-#ifdef WSAEWOULDBLOCK
+#ifdef USE_WINSOCK
/* This is how Windows does it */
- (WSAEWOULDBLOCK == sockerr)
+ (SOCKEWOULDBLOCK == sockerr)
#else
/* errno may be EWOULDBLOCK or on some systems EAGAIN when it
returned due to its inability to send off data without
blocking. We therefore treat both error codes the same here */
- (EWOULDBLOCK == sockerr) || (EAGAIN == sockerr) ||
- (EINTR == sockerr) || (EINPROGRESS == sockerr)
+ (SOCKEWOULDBLOCK == sockerr) || (EAGAIN == sockerr) ||
+ (SOCKEINTR == sockerr) || (SOCKEINPROGRESS == sockerr)
#endif
) {
continue;
diff --git a/libs/libcurl/src/socketpair.h b/libs/libcurl/src/socketpair.h
index d3fa60c135..a68a52f3fe 100644
--- a/libs/libcurl/src/socketpair.h
+++ b/libs/libcurl/src/socketpair.h
@@ -26,24 +26,7 @@
#include "curl_setup.h"
-#if defined(HAVE_EVENTFD) && \
- defined(__x86_64__) && \
- defined(__aarch64__) && \
- defined(__ia64__) && \
- defined(__ppc64__) && \
- defined(__mips64) && \
- defined(__sparc64__) && \
- defined(__riscv_64e) && \
- defined(__s390x__)
-
-/* Use eventfd only with 64-bit CPU architectures because eventfd has a
- * stringent rule of requiring the 8-byte buffer when calling read(2) and
- * write(2) on it. In some rare cases, the C standard library implementation
- * on a 32-bit system might choose to define uint64_t as a 32-bit type for
- * various reasons (memory limitations, compatibility with older code),
- * which makes eventfd broken.
- */
-#define USE_EVENTFD 1
+#ifdef USE_EVENTFD
#define wakeup_write write
#define wakeup_read read
diff --git a/libs/libcurl/src/socks.c b/libs/libcurl/src/socks.c
index 57ad6e1363..cf99ab1f53 100644
--- a/libs/libcurl/src/socks.c
+++ b/libs/libcurl/src/socks.c
@@ -38,10 +38,10 @@
#include "select.h"
#include "cfilters.h"
#include "connect.h"
-#include "timeval.h"
+#include "curlx/timeval.h"
#include "socks.h"
#include "multiif.h" /* for getsock macros */
-#include "inet_pton.h"
+#include "curlx/inet_pton.h"
#include "url.h"
/* The last 3 #include files should be in this order */
@@ -321,16 +321,16 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf,
/* DNS resolve only for SOCKS4, not SOCKS4a */
if(!protocol4a) {
- enum resolve_t rc =
- Curl_resolv(data, sx->hostname, sx->remote_port, TRUE, &dns);
+ result = Curl_resolv(data, sx->hostname, sx->remote_port,
+ cf->conn->ip_version, TRUE, &dns);
- if(rc == CURLRESOLV_ERROR)
- return CURLPX_RESOLVE_HOST;
- else if(rc == CURLRESOLV_PENDING) {
+ if(result == CURLE_AGAIN) {
sxstate(sx, data, CONNECT_RESOLVING);
infof(data, "SOCKS4 non-blocking resolve of %s", sx->hostname);
return CURLPX_OK;
}
+ else if(result)
+ return CURLPX_RESOLVE_HOST;
sxstate(sx, data, CONNECT_RESOLVED);
goto CONNECT_RESOLVED;
}
@@ -341,23 +341,11 @@ static CURLproxycode do_SOCKS4(struct Curl_cfilter *cf,
case CONNECT_RESOLVING:
/* check if we have the name resolved by now */
- dns = Curl_fetch_addr(data, sx->hostname, conn->primary.remote_port);
-
- if(dns) {
-#ifdef CURLRES_ASYNCH
- data->state.async.dns = dns;
- data->state.async.done = TRUE;
-#endif
- infof(data, "Hostname '%s' was found", sx->hostname);
- sxstate(sx, data, CONNECT_RESOLVED);
- }
- else {
- result = Curl_resolv_check(data, &dns);
- if(!dns) {
- if(result)
- return CURLPX_RESOLVE_HOST;
- return CURLPX_OK;
- }
+ result = Curl_resolv_check(data, &dns);
+ if(!dns) {
+ if(result)
+ return CURLPX_RESOLVE_HOST;
+ return CURLPX_OK;
}
FALLTHROUGH();
case CONNECT_RESOLVED:
@@ -406,7 +394,7 @@ CONNECT_REQ_INIT:
/*
* This is currently not supporting "Identification Protocol (RFC1413)".
*/
- socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
+ socksreq[8] = 0; /* ensure empty userid is null-terminated */
if(sx->proxy_user) {
size_t plen = strlen(sx->proxy_user);
if(plen > 255) {
@@ -590,7 +578,6 @@ static CURLproxycode do_SOCKS5(struct Curl_cfilter *cf,
bool allow_gssapi = FALSE;
struct Curl_dns_entry *dns = NULL;
- DEBUGASSERT(auth & (CURLAUTH_BASIC | CURLAUTH_GSSAPI));
switch(sx->state) {
case CONNECT_SOCKS_INIT:
if(conn->bits.httpproxy)
@@ -795,16 +782,15 @@ CONNECT_AUTH_INIT:
case CONNECT_REQ_INIT:
CONNECT_REQ_INIT:
if(socks5_resolve_local) {
- enum resolve_t rc = Curl_resolv(data, sx->hostname, sx->remote_port,
- TRUE, &dns);
+ result = Curl_resolv(data, sx->hostname, sx->remote_port,
+ cf->conn->ip_version, TRUE, &dns);
- if(rc == CURLRESOLV_ERROR)
- return CURLPX_RESOLVE_HOST;
-
- if(rc == CURLRESOLV_PENDING) {
+ if(result == CURLE_AGAIN) {
sxstate(sx, data, CONNECT_RESOLVING);
return CURLPX_OK;
}
+ else if(result)
+ return CURLPX_RESOLVE_HOST;
sxstate(sx, data, CONNECT_RESOLVED);
goto CONNECT_RESOLVED;
}
@@ -812,23 +798,11 @@ CONNECT_REQ_INIT:
case CONNECT_RESOLVING:
/* check if we have the name resolved by now */
- dns = Curl_fetch_addr(data, sx->hostname, sx->remote_port);
-
- if(dns) {
-#ifdef CURLRES_ASYNCH
- data->state.async.dns = dns;
- data->state.async.done = TRUE;
-#endif
- infof(data, "SOCKS5: hostname '%s' found", sx->hostname);
- }
-
+ result = Curl_resolv_check(data, &dns);
if(!dns) {
- result = Curl_resolv_check(data, &dns);
- if(!dns) {
- if(result)
- return CURLPX_RESOLVE_HOST;
- return CURLPX_OK;
- }
+ if(result)
+ return CURLPX_RESOLVE_HOST;
+ return CURLPX_OK;
}
FALLTHROUGH();
case CONNECT_RESOLVED:
@@ -912,7 +886,7 @@ CONNECT_RESOLVE_REMOTE:
#ifdef USE_IPV6
if(conn->bits.ipv6_ip) {
char ip6[16];
- if(1 != Curl_inet_pton(AF_INET6, sx->hostname, ip6))
+ if(1 != curlx_inet_pton(AF_INET6, sx->hostname, ip6))
return CURLPX_BAD_ADDRESS_TYPE;
socksreq[len++] = 4;
memcpy(&socksreq[len], ip6, sizeof(ip6));
@@ -920,7 +894,7 @@ CONNECT_RESOLVE_REMOTE:
}
else
#endif
- if(1 == Curl_inet_pton(AF_INET, sx->hostname, ip4)) {
+ if(1 == curlx_inet_pton(AF_INET, sx->hostname, ip4)) {
socksreq[len++] = 1;
memcpy(&socksreq[len], ip4, sizeof(ip4));
len += sizeof(ip4);
@@ -1128,7 +1102,7 @@ static void socks_proxy_cf_free(struct Curl_cfilter *cf)
*/
static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool blocking, bool *done)
+ bool *done)
{
CURLcode result;
struct connectdata *conn = cf->conn;
@@ -1140,7 +1114,7 @@ static CURLcode socks_proxy_cf_connect(struct Curl_cfilter *cf,
return CURLE_OK;
}
- result = cf->next->cft->do_connect(cf->next, data, blocking, done);
+ result = cf->next->cft->do_connect(cf->next, data, done);
if(result || !*done)
return result;
diff --git a/libs/libcurl/src/socks_gssapi.c b/libs/libcurl/src/socks_gssapi.c
index a71fc433bd..e12ebfc03a 100644
--- a/libs/libcurl/src/socks_gssapi.c
+++ b/libs/libcurl/src/socks_gssapi.c
@@ -32,9 +32,9 @@
#include "sendf.h"
#include "cfilters.h"
#include "connect.h"
-#include "timeval.h"
+#include "curlx/timeval.h"
#include "socks.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
#include "strdup.h"
/* The last 3 #include files should be in this order */
@@ -42,6 +42,11 @@
#include "curl_memory.h"
#include "memdebug.h"
+#if defined(__GNUC__) && defined(__APPLE__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
#define MAX_GSS_LEN 1024
static gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT;
@@ -60,7 +65,7 @@ static int check_gss_err(struct Curl_easy *data,
gss_buffer_desc status_string = GSS_C_EMPTY_BUFFER;
struct dynbuf dbuf;
- Curl_dyn_init(&dbuf, MAX_GSS_LEN);
+ curlx_dyn_init(&dbuf, MAX_GSS_LEN);
msg_ctx = 0;
while(!msg_ctx) {
/* convert major status code (GSS-API error) to text */
@@ -69,7 +74,7 @@ static int check_gss_err(struct Curl_easy *data,
GSS_C_NULL_OID,
&msg_ctx, &status_string);
if(maj_stat == GSS_S_COMPLETE) {
- if(Curl_dyn_addn(&dbuf, status_string.value,
+ if(curlx_dyn_addn(&dbuf, status_string.value,
status_string.length))
return 1; /* error */
gss_release_buffer(&min_stat, &status_string);
@@ -77,7 +82,7 @@ static int check_gss_err(struct Curl_easy *data,
}
gss_release_buffer(&min_stat, &status_string);
}
- if(Curl_dyn_addn(&dbuf, ".\n", 2))
+ if(curlx_dyn_addn(&dbuf, ".\n", 2))
return 1; /* error */
msg_ctx = 0;
while(!msg_ctx) {
@@ -87,7 +92,7 @@ static int check_gss_err(struct Curl_easy *data,
GSS_C_NULL_OID,
&msg_ctx, &status_string);
if(maj_stat == GSS_S_COMPLETE) {
- if(Curl_dyn_addn(&dbuf, status_string.value,
+ if(curlx_dyn_addn(&dbuf, status_string.value,
status_string.length))
return 1; /* error */
gss_release_buffer(&min_stat, &status_string);
@@ -95,8 +100,9 @@ static int check_gss_err(struct Curl_easy *data,
}
gss_release_buffer(&min_stat, &status_string);
}
- failf(data, "GSS-API error: %s failed: %s", function, Curl_dyn_ptr(&dbuf));
- Curl_dyn_free(&dbuf);
+ failf(data, "GSS-API error: %s failed: %s", function,
+ curlx_dyn_ptr(&dbuf));
+ curlx_dyn_free(&dbuf);
return 1;
}
@@ -537,4 +543,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
return CURLE_OK;
}
+#if defined(__GNUC__) && defined(__APPLE__)
+#pragma GCC diagnostic pop
+#endif
+
#endif /* HAVE_GSSAPI && !CURL_DISABLE_PROXY */
diff --git a/libs/libcurl/src/socks_sspi.c b/libs/libcurl/src/socks_sspi.c
index cd140c4858..d417ba5188 100644
--- a/libs/libcurl/src/socks_sspi.c
+++ b/libs/libcurl/src/socks_sspi.c
@@ -32,11 +32,11 @@
#include "cfilters.h"
#include "connect.h"
#include "strerror.h"
-#include "timeval.h"
+#include "curlx/timeval.h"
#include "socks.h"
#include "curl_sspi.h"
-#include "curl_multibyte.h"
-#include "warnless.h"
+#include "curlx/multibyte.h"
+#include "curlx/warnless.h"
#include "strdup.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -140,14 +140,14 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
cred_handle.dwUpper = 0;
status = Curl_pSecFn->AcquireCredentialsHandle(NULL,
- (TCHAR *) TEXT("Kerberos"),
- SECPKG_CRED_OUTBOUND,
- NULL,
- NULL,
- NULL,
- NULL,
- &cred_handle,
- &expiry);
+ (TCHAR *)CURL_UNCONST(TEXT("Kerberos")),
+ SECPKG_CRED_OUTBOUND,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &cred_handle,
+ &expiry);
if(check_sspi_err(data, status, "AcquireCredentialsHandle")) {
failf(data, "Failed to acquire credentials.");
diff --git a/libs/libcurl/src/speedcheck.c b/libs/libcurl/src/speedcheck.c
index fd1eaa73dc..6c9bdf2d50 100644
--- a/libs/libcurl/src/speedcheck.c
+++ b/libs/libcurl/src/speedcheck.c
@@ -52,7 +52,7 @@ CURLcode Curl_speedcheck(struct Curl_easy *data,
data->state.keeps_speed = now;
else {
/* how long has it been under the limit */
- timediff_t howlong = Curl_timediff(now, data->state.keeps_speed);
+ timediff_t howlong = curlx_timediff(now, data->state.keeps_speed);
if(howlong >= data->set.low_speed_time * 1000) {
/* too long */
diff --git a/libs/libcurl/src/speedcheck.h b/libs/libcurl/src/speedcheck.h
index d09ec743b5..ea39e3db4b 100644
--- a/libs/libcurl/src/speedcheck.h
+++ b/libs/libcurl/src/speedcheck.h
@@ -26,7 +26,7 @@
#include "curl_setup.h"
-#include "timeval.h"
+#include "curlx/timeval.h"
struct Curl_easy;
void Curl_speedinit(struct Curl_easy *data);
CURLcode Curl_speedcheck(struct Curl_easy *data,
diff --git a/libs/libcurl/src/splay.c b/libs/libcurl/src/splay.c
index e5031a70ef..4cd049e51d 100644
--- a/libs/libcurl/src/splay.c
+++ b/libs/libcurl/src/splay.c
@@ -24,7 +24,7 @@
#include "curl_setup.h"
-#include "timeval.h"
+#include "curlx/timeval.h"
#include "splay.h"
/*
@@ -34,7 +34,7 @@
* zero : when i is equal to j
* positive when : when i is larger than j
*/
-#define compare(i,j) Curl_timediff_us(i,j)
+#define compare(i,j) curlx_timediff_us(i,j)
/*
* Splay using the key i (which may or may not be in the tree.) The starting
diff --git a/libs/libcurl/src/splay.h b/libs/libcurl/src/splay.h
index 4816ac7a66..84d4b93967 100644
--- a/libs/libcurl/src/splay.h
+++ b/libs/libcurl/src/splay.h
@@ -24,7 +24,7 @@
*
***************************************************************************/
#include "curl_setup.h"
-#include "timeval.h"
+#include "curlx/timeval.h"
/* only use function calls to access this struct */
struct Curl_tree {
diff --git a/libs/libcurl/src/strcase.c b/libs/libcurl/src/strcase.c
index 86e81295de..f21e76f094 100644
--- a/libs/libcurl/src/strcase.c
+++ b/libs/libcurl/src/strcase.c
@@ -82,69 +82,10 @@ char Curl_raw_tolower(char in)
return (char)tolowermap[(unsigned char) in];
}
-/*
- * curl_strequal() is for doing "raw" case insensitive strings. This is meant
- * to be locale independent and only compare strings we know are safe for
- * this. See https://daniel.haxx.se/blog/2008/10/15/strcasecmp-in-turkish/ for
- * further explanations as to why this function is necessary.
- */
-
-static int casecompare(const char *first, const char *second)
-{
- while(*first && *second) {
- if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second))
- /* get out of the loop as soon as they do not match */
- return 0;
- first++;
- second++;
- }
- /* If we are here either the strings are the same or the length is different.
- We can just test if the "current" character is non-zero for one and zero
- for the other. Note that the characters may not be exactly the same even
- if they match, we only want to compare zero-ness. */
- return !*first == !*second;
-}
-
-/* --- public function --- */
-int curl_strequal(const char *first, const char *second)
-{
- if(first && second)
- /* both pointers point to something then compare them */
- return casecompare(first, second);
-
- /* if both pointers are NULL then treat them as equal */
- return (NULL == first && NULL == second);
-}
-
-static int ncasecompare(const char *first, const char *second, size_t max)
-{
- while(*first && *second && max) {
- if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second))
- return 0;
- max--;
- first++;
- second++;
- }
- if(0 == max)
- return 1; /* they are equal this far */
-
- return Curl_raw_toupper(*first) == Curl_raw_toupper(*second);
-}
-
-/* --- public function --- */
-int curl_strnequal(const char *first, const char *second, size_t max)
-{
- if(first && second)
- /* both pointers point to something then compare them */
- return ncasecompare(first, second, max);
-
- /* if both pointers are NULL then treat them as equal if max is non-zero */
- return (NULL == first && NULL == second && max);
-}
/* Copy an upper case version of the string from src to dest. The
* strings may overlap. No more than n characters of the string are copied
* (including any NUL) and the destination string will NOT be
- * NUL-terminated if that limit is reached.
+ * null-terminated if that limit is reached.
*/
void Curl_strntoupper(char *dest, const char *src, size_t n)
{
@@ -159,7 +100,7 @@ void Curl_strntoupper(char *dest, const char *src, size_t 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.
+ * null-terminated if that limit is reached.
*/
void Curl_strntolower(char *dest, const char *src, size_t n)
{
@@ -171,7 +112,7 @@ void Curl_strntolower(char *dest, const char *src, size_t n)
} while(*src++ && --n);
}
-/* Compare case-sensitive NUL-terminated strings, taking care of possible
+/* Compare case-sensitive null-terminated strings, taking care of possible
* null pointers. Return true if arguments match.
*/
bool Curl_safecmp(char *a, char *b)
diff --git a/libs/libcurl/src/strdup.c b/libs/libcurl/src/strdup.c
index f3d8ed3846..94caa05fb8 100644
--- a/libs/libcurl/src/strdup.c
+++ b/libs/libcurl/src/strdup.c
@@ -104,7 +104,7 @@ void *Curl_memdup(const void *src, size_t length)
* Curl_memdup0(source, length)
*
* Copies the 'source' string to a newly allocated buffer (that is returned).
- * Copies 'length' bytes then adds a null terminator.
+ * Copies 'length' bytes then adds a null-terminator.
*
* Returns the new pointer or NULL on failure.
*
@@ -114,7 +114,10 @@ void *Curl_memdup0(const char *src, size_t length)
char *buf = malloc(length + 1);
if(!buf)
return NULL;
- memcpy(buf, src, length);
+ if(length) {
+ DEBUGASSERT(src); /* must never be NULL */
+ memcpy(buf, src, length);
+ }
buf[length] = 0;
return buf;
}
diff --git a/libs/libcurl/src/strequal.c b/libs/libcurl/src/strequal.c
new file mode 100644
index 0000000000..fb8e718e4c
--- /dev/null
+++ b/libs/libcurl/src/strequal.c
@@ -0,0 +1,88 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+#include "strcase.h"
+
+/*
+ * curl_strequal() is for doing "raw" case insensitive strings. This is meant
+ * to be locale independent and only compare strings we know are safe for
+ * this. See https://daniel.haxx.se/blog/2008/10/15/strcasecmp-in-turkish/ for
+ * further explanations as to why this function is necessary.
+ */
+
+static int casecompare(const char *first, const char *second)
+{
+ while(*first) {
+ if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second))
+ /* get out of the loop as soon as they do not match */
+ return 0;
+ first++;
+ second++;
+ }
+ /* If we are here either the strings are the same or the length is different.
+ We can just test if the "current" character is non-zero for one and zero
+ for the other. Note that the characters may not be exactly the same even
+ if they match, we only want to compare zero-ness. */
+ return !*first == !*second;
+}
+
+static int ncasecompare(const char *first, const char *second, size_t max)
+{
+ while(*first && max) {
+ if(Curl_raw_toupper(*first) != Curl_raw_toupper(*second))
+ return 0;
+ max--;
+ first++;
+ second++;
+ }
+ if(0 == max)
+ return 1; /* they are equal this far */
+
+ return Curl_raw_toupper(*first) == Curl_raw_toupper(*second);
+}
+
+/* --- public function --- */
+int curl_strequal(const char *first, const char *second)
+{
+ if(first && second)
+ /* both pointers point to something then compare them */
+ return casecompare(first, second);
+
+ /* if both pointers are NULL then treat them as equal */
+ return NULL == first && NULL == second;
+}
+
+/* --- public function --- */
+int curl_strnequal(const char *first, const char *second, size_t max)
+{
+ if(first && second)
+ /* both pointers point to something then compare them */
+ return ncasecompare(first, second, max);
+
+ /* if both pointers are NULL then treat them as equal if max is non-zero */
+ return NULL == first && NULL == second && max;
+}
diff --git a/libs/libcurl/src/strerror.c b/libs/libcurl/src/strerror.c
index 7b8d4876c8..a48ab988aa 100644
--- a/libs/libcurl/src/strerror.c
+++ b/libs/libcurl/src/strerror.c
@@ -42,16 +42,13 @@
#include "curl_sspi.h"
#endif
+#include "curlx/winapi.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"
-#if defined(_WIN32) || defined(_WIN32_WCE)
-#define PRESERVE_WINDOWS_ERROR_CODE
-#endif
-
const char *
curl_easy_strerror(CURLcode error)
{
@@ -151,9 +148,6 @@ curl_easy_strerror(CURLcode error)
case CURLE_RANGE_ERROR:
return "Requested range was not delivered by the server";
- case CURLE_HTTP_POST_ERROR:
- return "Internal problem setting up the POST";
-
case CURLE_SSL_CONNECT_ERROR:
return "SSL connect error";
@@ -169,9 +163,6 @@ curl_easy_strerror(CURLcode error)
case CURLE_LDAP_SEARCH_FAILED:
return "LDAP: search failed";
- case CURLE_FUNCTION_NOT_FOUND:
- return "A required function in the library was not found";
-
case CURLE_ABORTED_BY_CALLBACK:
return "Operation was aborted by an application callback";
@@ -330,7 +321,9 @@ curl_easy_strerror(CURLcode error)
case CURLE_OBSOLETE24:
case CURLE_OBSOLETE29:
case CURLE_OBSOLETE32:
+ case CURLE_OBSOLETE34:
case CURLE_OBSOLETE40:
+ case CURLE_OBSOLETE41:
case CURLE_OBSOLETE44:
case CURLE_OBSOLETE46:
case CURLE_OBSOLETE50:
@@ -339,6 +332,20 @@ curl_easy_strerror(CURLcode error)
case CURLE_OBSOLETE62:
case CURLE_OBSOLETE75:
case CURLE_OBSOLETE76:
+
+ /* error codes used by curl tests */
+ case CURLE_RESERVED115:
+ case CURLE_RESERVED116:
+ case CURLE_RESERVED117:
+ case CURLE_RESERVED118:
+ case CURLE_RESERVED119:
+ case CURLE_RESERVED120:
+ case CURLE_RESERVED121:
+ case CURLE_RESERVED122:
+ case CURLE_RESERVED123:
+ case CURLE_RESERVED124:
+ case CURLE_RESERVED125:
+ case CURLE_RESERVED126:
case CURL_LAST:
break;
}
@@ -772,50 +779,6 @@ get_winsock_error(int err, char *buf, size_t len)
}
#endif /* USE_WINSOCK */
-#if defined(_WIN32) || defined(_WIN32_WCE)
-/* This is a helper function for Curl_strerror that converts Windows API error
- * codes (GetLastError) to error messages.
- * Returns NULL if no error message was found for error code.
- */
-static const char *
-get_winapi_error(int err, char *buf, size_t buflen)
-{
- char *p;
- wchar_t wbuf[256];
-
- if(!buflen)
- return NULL;
-
- *buf = '\0';
- *wbuf = L'\0';
-
- /* We return the local codepage version of the error string because if it is
- output to the user's terminal it will likely be with functions which
- expect the local codepage (eg fprintf, failf, infof).
- FormatMessageW -> wcstombs is used for Windows CE compatibility. */
- if(FormatMessageW((FORMAT_MESSAGE_FROM_SYSTEM |
- FORMAT_MESSAGE_IGNORE_INSERTS), NULL, (DWORD)err,
- LANG_NEUTRAL, wbuf, sizeof(wbuf)/sizeof(wchar_t), NULL)) {
- size_t written = wcstombs(buf, wbuf, buflen - 1);
- if(written != (size_t)-1)
- buf[written] = '\0';
- else
- *buf = '\0';
- }
-
- /* Truncate multiple lines */
- p = strchr(buf, '\n');
- if(p) {
- if(p > buf && *(p-1) == '\r')
- *(p-1) = '\0';
- else
- *p = '\0';
- }
-
- return (*buf ? buf : NULL);
-}
-#endif /* _WIN32 || _WIN32_WCE */
-
/*
* Our thread-safe and smart strerror() replacement.
*
@@ -833,11 +796,11 @@ get_winapi_error(int err, char *buf, size_t buflen)
*
* It may be more correct to call one of the variant functions instead:
* Call Curl_sspi_strerror if the error code is definitely Windows SSPI.
- * Call Curl_winapi_strerror if the error code is definitely Windows API.
+ * Call curlx_winapi_strerror if the error code is definitely Windows API.
*/
const char *Curl_strerror(int err, char *buf, size_t buflen)
{
-#ifdef PRESERVE_WINDOWS_ERROR_CODE
+#ifdef _WIN32
DWORD old_win_err = GetLastError();
#endif
int old_errno = errno;
@@ -852,20 +815,20 @@ const char *Curl_strerror(int err, char *buf, size_t buflen)
*buf = '\0';
-#if defined(_WIN32) || defined(_WIN32_WCE)
-#if defined(_WIN32)
+#ifdef _WIN32
+#ifndef UNDER_CE
/* 'sys_nerr' is the maximum errno number, it is not widely portable */
if(err >= 0 && err < sys_nerr)
- msnprintf(buf, buflen, "%s", sys_errlist[err]);
+ curl_msnprintf(buf, buflen, "%s", sys_errlist[err]);
else
#endif
{
if(
#ifdef USE_WINSOCK
- !get_winsock_error(err, buf, buflen) &&
+ !get_winsock_error(err, buf, buflen) &&
#endif
- !get_winapi_error(err, buf, buflen))
- msnprintf(buf, buflen, "Unknown error %d (%#x)", err, err);
+ !curlx_get_winapi_error(err, buf, buflen))
+ curl_msnprintf(buf, buflen, "Unknown error %d (%#x)", err, err);
}
#else /* not Windows coming up */
@@ -877,7 +840,7 @@ const char *Curl_strerror(int err, char *buf, size_t buflen)
*/
if(0 != strerror_r(err, buf, buflen)) {
if('\0' == buf[0])
- msnprintf(buf, buflen, "Unknown error %d", err);
+ curl_msnprintf(buf, buflen, "Unknown error %d", err);
}
#elif defined(HAVE_STRERROR_R) && defined(HAVE_GLIBC_STRERROR_R)
/*
@@ -889,18 +852,18 @@ const char *Curl_strerror(int err, char *buf, size_t buflen)
char buffer[256];
char *msg = strerror_r(err, buffer, sizeof(buffer));
if(msg)
- msnprintf(buf, buflen, "%s", msg);
+ curl_msnprintf(buf, buflen, "%s", msg);
else
- msnprintf(buf, buflen, "Unknown error %d", err);
+ curl_msnprintf(buf, buflen, "Unknown error %d", err);
}
#else
{
- /* !checksrc! disable STRERROR 1 */
+ /* !checksrc! disable BANNEDFUNC 1 */
const char *msg = strerror(err);
if(msg)
- msnprintf(buf, buflen, "%s", msg);
+ curl_msnprintf(buf, buflen, "%s", msg);
else
- msnprintf(buf, buflen, "Unknown error %d", err);
+ curl_msnprintf(buf, buflen, "Unknown error %d", err);
}
#endif
@@ -915,56 +878,15 @@ const char *Curl_strerror(int err, char *buf, size_t buflen)
*p = '\0';
if(errno != old_errno)
- errno = old_errno;
-
-#ifdef PRESERVE_WINDOWS_ERROR_CODE
- if(old_win_err != GetLastError())
- SetLastError(old_win_err);
-#endif
-
- return buf;
-}
-
-/*
- * Curl_winapi_strerror:
- * Variant of Curl_strerror if the error code is definitely Windows API.
- */
-#if defined(_WIN32) || defined(_WIN32_WCE)
-const char *Curl_winapi_strerror(DWORD err, char *buf, size_t buflen)
-{
-#ifdef PRESERVE_WINDOWS_ERROR_CODE
- DWORD old_win_err = GetLastError();
-#endif
- int old_errno = errno;
-
- if(!buflen)
- return NULL;
-
- *buf = '\0';
-
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
- if(!get_winapi_error((int)err, buf, buflen)) {
- msnprintf(buf, buflen, "Unknown error %lu (0x%08lX)", err, err);
- }
-#else
- {
- const char *txt = (err == ERROR_SUCCESS) ? "No error" : "Error";
- if(strlen(txt) < buflen)
- strcpy(buf, txt);
- }
-#endif
-
- if(errno != old_errno)
- errno = old_errno;
+ CURL_SETERRNO(old_errno);
-#ifdef PRESERVE_WINDOWS_ERROR_CODE
+#ifdef _WIN32
if(old_win_err != GetLastError())
SetLastError(old_win_err);
#endif
return buf;
}
-#endif /* _WIN32 || _WIN32_WCE */
#ifdef USE_WINDOWS_SSPI
/*
@@ -973,7 +895,7 @@ const char *Curl_winapi_strerror(DWORD err, char *buf, size_t buflen)
*/
const char *Curl_sspi_strerror(int err, char *buf, size_t buflen)
{
-#ifdef PRESERVE_WINDOWS_ERROR_CODE
+#ifdef _WIN32
DWORD old_win_err = GetLastError();
#endif
int old_errno = errno;
@@ -1080,18 +1002,18 @@ const char *Curl_sspi_strerror(int err, char *buf, size_t buflen)
}
if(err == SEC_E_ILLEGAL_MESSAGE) {
- msnprintf(buf, buflen,
- "SEC_E_ILLEGAL_MESSAGE (0x%08X) - This error usually occurs "
- "when a fatal SSL/TLS alert is received (e.g. handshake failed)."
- " More detail may be available in the Windows System event log.",
- err);
+ curl_msnprintf(buf, buflen,
+ "SEC_E_ILLEGAL_MESSAGE (0x%08X) - This error usually "
+ "occurs when a fatal SSL/TLS alert is received (e.g. "
+ "handshake failed). More detail may be available in "
+ "the Windows System event log.", err);
}
else {
char msgbuf[256];
- if(get_winapi_error(err, msgbuf, sizeof(msgbuf)))
- msnprintf(buf, buflen, "%s (0x%08X) - %s", txt, err, msgbuf);
+ if(curlx_get_winapi_error(err, msgbuf, sizeof(msgbuf)))
+ curl_msnprintf(buf, buflen, "%s (0x%08X) - %s", txt, err, msgbuf);
else
- msnprintf(buf, buflen, "%s (0x%08X)", txt, err);
+ curl_msnprintf(buf, buflen, "%s (0x%08X)", txt, err);
}
#else
@@ -1104,9 +1026,9 @@ const char *Curl_sspi_strerror(int err, char *buf, size_t buflen)
#endif
if(errno != old_errno)
- errno = old_errno;
+ CURL_SETERRNO(old_errno);
-#ifdef PRESERVE_WINDOWS_ERROR_CODE
+#ifdef _WIN32
if(old_win_err != GetLastError())
SetLastError(old_win_err);
#endif
diff --git a/libs/libcurl/src/strerror.h b/libs/libcurl/src/strerror.h
index 1fd423fc17..130c4647fe 100644
--- a/libs/libcurl/src/strerror.h
+++ b/libs/libcurl/src/strerror.h
@@ -29,9 +29,6 @@
#define STRERROR_LEN 256 /* a suitable length */
const char *Curl_strerror(int err, char *buf, size_t buflen);
-#if defined(_WIN32) || defined(_WIN32_WCE)
-const char *Curl_winapi_strerror(DWORD err, char *buf, size_t buflen);
-#endif
#ifdef USE_WINDOWS_SSPI
const char *Curl_sspi_strerror(int err, char *buf, size_t buflen);
#endif
diff --git a/libs/libcurl/src/strtok.c b/libs/libcurl/src/strtok.c
deleted file mode 100644
index ffd546170c..0000000000
--- a/libs/libcurl/src/strtok.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifndef HAVE_STRTOK_R
-#include <stddef.h>
-
-#include "strtok.h"
-
-char *
-Curl_strtok_r(char *ptr, const char *sep, char **end)
-{
- if(!ptr)
- /* we got NULL input so then we get our last position instead */
- ptr = *end;
-
- /* pass all letters that are including in the separator string */
- while(*ptr && strchr(sep, *ptr))
- ++ptr;
-
- if(*ptr) {
- /* so this is where the next piece of string starts */
- char *start = ptr;
-
- /* set the end pointer to the first byte after the start */
- *end = start + 1;
-
- /* scan through the string to find where it ends, it ends on a
- null byte or a character that exists in the separator string */
- while(**end && !strchr(sep, **end))
- ++*end;
-
- if(**end) {
- /* the end is not a null byte */
- **end = '\0'; /* null-terminate it! */
- ++*end; /* advance the last pointer to beyond the null byte */
- }
-
- return start; /* return the position where the string starts */
- }
-
- /* we ended up on a null byte, there are no more strings to find! */
- return NULL;
-}
-
-#endif /* this was only compiled if strtok_r was not present */
diff --git a/libs/libcurl/src/strtoofft.c b/libs/libcurl/src/strtoofft.c
deleted file mode 100644
index 141d485331..0000000000
--- a/libs/libcurl/src/strtoofft.c
+++ /dev/null
@@ -1,240 +0,0 @@
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include <errno.h>
-#include "curl_setup.h"
-
-#include "strtoofft.h"
-
-/*
- * NOTE:
- *
- * In the ISO C standard (IEEE Std 1003.1), there is a strtoimax() function we
- * could use in case strtoll() does not exist... See
- * https://www.opengroup.org/onlinepubs/009695399/functions/strtoimax.html
- */
-
-#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG)
-# ifdef HAVE_STRTOLL
-# define strtooff strtoll
-# else
-# if defined(_MSC_VER) && (_MSC_VER >= 1300) && (_INTEGRAL_MAX_BITS >= 64)
-# if defined(_SAL_VERSION)
- _Check_return_ _CRTIMP __int64 __cdecl _strtoi64(
- _In_z_ const char *_String,
- _Out_opt_ _Deref_post_z_ char **_EndPtr, _In_ int _Radix);
-# else
- _CRTIMP __int64 __cdecl _strtoi64(const char *_String,
- char **_EndPtr, int _Radix);
-# endif
-# define strtooff _strtoi64
-# else
-# define PRIVATE_STRTOOFF 1
-# endif
-# endif
-#else
-# define strtooff strtol
-#endif
-
-#ifdef PRIVATE_STRTOOFF
-
-/* Range tests can be used for alphanum decoding if characters are consecutive,
- like in ASCII. Else an array is scanned. Determine this condition now. */
-
-#if('9' - '0') != 9 || ('Z' - 'A') != 25 || ('z' - 'a') != 25
-
-#define NO_RANGE_TEST
-
-static const char valchars[] =
- "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
-#endif
-
-static int get_char(char c, int base);
-
-/**
- * Custom version of the strtooff function. This extracts a curl_off_t
- * value from the given input string and returns it.
- */
-static curl_off_t strtooff(const char *nptr, char **endptr, int base)
-{
- char *end;
- bool is_negative = FALSE;
- bool overflow = FALSE;
- int i;
- curl_off_t value = 0;
-
- /* Skip leading whitespace. */
- end = (char *)nptr;
- while(ISBLANK(end[0])) {
- end++;
- }
-
- /* Handle the sign, if any. */
- if(end[0] == '-') {
- is_negative = TRUE;
- end++;
- }
- else if(end[0] == '+') {
- end++;
- }
- else if(end[0] == '\0') {
- /* We had nothing but perhaps some whitespace -- there was no number. */
- if(endptr) {
- *endptr = end;
- }
- return 0;
- }
-
- /* Handle special beginnings, if present and allowed. */
- if(end[0] == '0' && end[1] == 'x') {
- if(base == 16 || base == 0) {
- end += 2;
- base = 16;
- }
- }
- else if(end[0] == '0') {
- if(base == 8 || base == 0) {
- end++;
- base = 8;
- }
- }
-
- /* Matching strtol, if the base is 0 and it does not look like
- * the number is octal or hex, we assume it is base 10.
- */
- if(base == 0) {
- base = 10;
- }
-
- /* Loop handling digits. */
- for(i = get_char(end[0], base);
- i != -1;
- end++, i = get_char(end[0], base)) {
-
- if(value > (CURL_OFF_T_MAX - i) / base) {
- overflow = TRUE;
- break;
- }
- value = base * value + i;
- }
-
- if(!overflow) {
- if(is_negative) {
- /* Fix the sign. */
- value *= -1;
- }
- }
- else {
- if(is_negative)
- value = CURL_OFF_T_MIN;
- else
- value = CURL_OFF_T_MAX;
-
- errno = ERANGE;
- }
-
- if(endptr)
- *endptr = end;
-
- return value;
-}
-
-/**
- * Returns the value of c in the given base, or -1 if c cannot
- * be interpreted properly in that base (i.e., is out of range,
- * is a null, etc.).
- *
- * @param c the character to interpret according to base
- * @param base the base in which to interpret c
- *
- * @return the value of c in base, or -1 if c is not in range
- */
-static int get_char(char c, int base)
-{
-#ifndef NO_RANGE_TEST
- int value = -1;
- if(c <= '9' && c >= '0') {
- value = c - '0';
- }
- else if(c <= 'Z' && c >= 'A') {
- value = c - 'A' + 10;
- }
- else if(c <= 'z' && c >= 'a') {
- value = c - 'a' + 10;
- }
-#else
- const char *cp;
- int value;
-
- cp = memchr(valchars, c, 10 + 26 + 26);
-
- if(!cp)
- return -1;
-
- value = cp - valchars;
-
- if(value >= 10 + 26)
- value -= 26; /* Lowercase. */
-#endif
-
- if(value >= base) {
- value = -1;
- }
-
- return value;
-}
-#endif /* Only present if we need strtoll, but do not have it. */
-
-/*
- * Parse a *positive* up to 64-bit number written in ASCII.
- */
-CURLofft curlx_strtoofft(const char *str, char **endp, int base,
- curl_off_t *num)
-{
- char *end = NULL;
- curl_off_t number;
- errno = 0;
- *num = 0; /* clear by default */
- DEBUGASSERT(base); /* starting now, avoid base zero */
-
- while(*str && ISBLANK(*str))
- str++;
- if(('-' == *str) || (ISSPACE(*str))) {
- if(endp)
- *endp = (char *)str; /* did not actually move */
- return CURL_OFFT_INVAL; /* nothing parsed */
- }
- number = strtooff(str, &end, base);
- if(endp)
- *endp = end;
- if(errno == ERANGE)
- /* overflow/underflow */
- return CURL_OFFT_FLOW;
- else if(str == end)
- /* nothing parsed */
- return CURL_OFFT_INVAL;
-
- *num = number;
- return CURL_OFFT_OK;
-}
diff --git a/libs/libcurl/src/strtoofft.h b/libs/libcurl/src/strtoofft.h
deleted file mode 100644
index e6ef31a870..0000000000
--- a/libs/libcurl/src/strtoofft.h
+++ /dev/null
@@ -1,54 +0,0 @@
-#ifndef HEADER_CURL_STRTOOFFT_H
-#define HEADER_CURL_STRTOOFFT_H
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-/*
- * Determine which string to integral data type conversion function we use
- * to implement string conversion to our curl_off_t integral data type.
- *
- * Notice that curl_off_t might be 64 or 32 bits wide, and that it might use
- * an underlying data type which might be 'long', 'int64_t', 'long long' or
- * '__int64' and more remotely other data types.
- *
- * On systems where the size of curl_off_t is greater than the size of 'long'
- * the conversion function to use is strtoll() if it is available, otherwise,
- * we emulate its functionality with our own clone.
- *
- * On systems where the size of curl_off_t is smaller or equal than the size
- * of 'long' the conversion function to use is strtol().
- */
-
-typedef enum {
- CURL_OFFT_OK, /* parsed fine */
- CURL_OFFT_FLOW, /* over or underflow */
- CURL_OFFT_INVAL /* nothing was parsed */
-} CURLofft;
-
-CURLofft curlx_strtoofft(const char *str, char **endp, int base,
- curl_off_t *num);
-
-#endif /* HEADER_CURL_STRTOOFFT_H */
diff --git a/libs/libcurl/src/system_win32.c b/libs/libcurl/src/system_win32.c
index c5599ce20f..836e1b1d57 100644
--- a/libs/libcurl/src/system_win32.c
+++ b/libs/libcurl/src/system_win32.c
@@ -24,21 +24,18 @@
#include "curl_setup.h"
-#if defined(_WIN32)
+#ifdef _WIN32
#include <curl/curl.h>
#include "system_win32.h"
-#include "version_win32.h"
+#include "curlx/version_win32.h"
#include "curl_sspi.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
/* The last #include files should be: */
#include "curl_memory.h"
#include "memdebug.h"
-LARGE_INTEGER Curl_freq;
-bool Curl_isVistaOrGreater;
-
/* Handle of iphlpapp.dll */
static HMODULE s_hIpHlpApiDll = NULL;
@@ -96,9 +93,15 @@ CURLcode Curl_win32_init(long flags)
s_hIpHlpApiDll = Curl_load_library(TEXT("iphlpapi.dll"));
if(s_hIpHlpApiDll) {
/* Get the address of the if_nametoindex function */
+#ifdef UNDER_CE
+ #define CURL_TEXT(n) TEXT(n)
+#else
+ #define CURL_TEXT(n) (n)
+#endif
IF_NAMETOINDEX_FN pIfNameToIndex =
CURLX_FUNCTION_CAST(IF_NAMETOINDEX_FN,
- (GetProcAddress(s_hIpHlpApiDll, "if_nametoindex")));
+ (GetProcAddress(s_hIpHlpApiDll,
+ CURL_TEXT("if_nametoindex"))));
if(pIfNameToIndex)
Curl_if_nametoindex = pIfNameToIndex;
@@ -150,7 +153,7 @@ typedef HMODULE (APIENTRY *LOADLIBRARYEX_FN)(LPCTSTR, HANDLE, DWORD);
/* See function definitions in winbase.h */
#ifdef UNICODE
-# ifdef _WIN32_WCE
+# ifdef UNDER_CE
# define LOADLIBARYEX L"LoadLibraryExW"
# else
# define LOADLIBARYEX "LoadLibraryExW"
@@ -175,7 +178,7 @@ typedef HMODULE (APIENTRY *LOADLIBRARYEX_FN)(LPCTSTR, HANDLE, DWORD);
*/
HMODULE Curl_load_library(LPCTSTR filename)
{
-#ifndef CURL_WINDOWS_UWP
+#if !defined(CURL_WINDOWS_UWP) && !defined(UNDER_CE)
HMODULE hModule = NULL;
LOADLIBRARYEX_FN pLoadLibraryEx = NULL;
@@ -211,7 +214,7 @@ HMODULE Curl_load_library(LPCTSTR filename)
/* Attempt to get the Windows system path */
UINT systemdirlen = GetSystemDirectory(NULL, 0);
if(systemdirlen) {
- /* Allocate space for the full DLL path (Room for the null terminator
+ /* Allocate space for the full DLL path (Room for the null-terminator
is included in systemdirlen) */
size_t filenamelen = _tcslen(filename);
TCHAR *path = malloc(sizeof(TCHAR) * (systemdirlen + 1 + filenamelen));
diff --git a/libs/libcurl/src/system_win32.h b/libs/libcurl/src/system_win32.h
index 4ba748d381..0568450713 100644
--- a/libs/libcurl/src/system_win32.h
+++ b/libs/libcurl/src/system_win32.h
@@ -26,7 +26,7 @@
#include "curl_setup.h"
-#if defined(_WIN32)
+#ifdef _WIN32
#include <curl/curl.h>
diff --git a/libs/libcurl/src/telnet.c b/libs/libcurl/src/telnet.c
index 622850f052..b5d4380263 100644
--- a/libs/libcurl/src/telnet.c
+++ b/libs/libcurl/src/telnet.c
@@ -47,6 +47,7 @@
#endif
#include "urldata.h"
+#include "url.h"
#include <curl/curl.h>
#include "transfer.h"
#include "sendf.h"
@@ -57,7 +58,8 @@
#include "arpa_telnet.h"
#include "select.h"
#include "strcase.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
+#include "curlx/strparse.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -89,36 +91,6 @@
#define printoption(a,b,c,d) Curl_nop_stmt
#endif
-static
-CURLcode telrcv(struct Curl_easy *data,
- const unsigned char *inbuf, /* Data received from socket */
- ssize_t count); /* Number of bytes received */
-
-#ifndef CURL_DISABLE_VERBOSE_STRINGS
-static void printoption(struct Curl_easy *data,
- const char *direction,
- int cmd, int option);
-#endif
-
-static void negotiate(struct Curl_easy *data);
-static void send_negotiation(struct Curl_easy *data, int cmd, int option);
-static void set_local_option(struct Curl_easy *data,
- int option, int newstate);
-static void set_remote_option(struct Curl_easy *data,
- int option, int newstate);
-
-static void printsub(struct Curl_easy *data,
- int direction, unsigned char *pointer,
- size_t length);
-static void suboption(struct Curl_easy *data);
-static void sendsuboption(struct Curl_easy *data, int option);
-
-static CURLcode telnet_do(struct Curl_easy *data, bool *done);
-static CURLcode telnet_done(struct Curl_easy *data,
- CURLcode, bool premature);
-static CURLcode send_telnet_data(struct Curl_easy *data,
- char *buffer, ssize_t nread);
-
/* For negotiation compliant to RFC 1143 */
#define CURL_NO 0
#define CURL_YES 1
@@ -128,6 +100,10 @@ static CURLcode send_telnet_data(struct Curl_easy *data,
#define CURL_EMPTY 0
#define CURL_OPPOSITE 1
+
+/* meta key for storing protocol meta at easy handle */
+#define CURL_META_TELNET_EASY "meta:proto:telnet:easy"
+
/*
* Telnet receiver states for fsm
*/
@@ -168,6 +144,38 @@ struct TELNET {
};
+static
+CURLcode telrcv(struct Curl_easy *data,
+ struct TELNET *tn,
+ const unsigned char *inbuf, /* Data received from socket */
+ ssize_t count); /* Number of bytes received */
+
+#ifndef CURL_DISABLE_VERBOSE_STRINGS
+static void printoption(struct Curl_easy *data,
+ const char *direction,
+ int cmd, int option);
+#endif
+
+static void send_negotiation(struct Curl_easy *data, int cmd, int option);
+static void set_local_option(struct Curl_easy *data, struct TELNET *tn,
+ int option, int newstate);
+static void set_remote_option(struct Curl_easy *data, struct TELNET *tn,
+ int option, int newstate);
+
+static void printsub(struct Curl_easy *data,
+ int direction, unsigned char *pointer,
+ size_t length);
+static void suboption(struct Curl_easy *data, struct TELNET *tn);
+static void sendsuboption(struct Curl_easy *data,
+ struct TELNET *tn, int option);
+
+static CURLcode telnet_do(struct Curl_easy *data, bool *done);
+static CURLcode telnet_done(struct Curl_easy *data,
+ CURLcode, bool premature);
+static CURLcode send_telnet_data(struct Curl_easy *data,
+ struct TELNET *tn,
+ char *buffer, ssize_t nread);
+
/*
* TELNET protocol handler.
*/
@@ -190,6 +198,7 @@ const struct Curl_handler Curl_handler_telnet = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_TELNET, /* defport */
CURLPROTO_TELNET, /* protocol */
CURLPROTO_TELNET, /* family */
@@ -197,6 +206,16 @@ const struct Curl_handler Curl_handler_telnet = {
};
+static void telnet_easy_dtor(void *key, size_t klen, void *entry)
+{
+ struct TELNET *tn = entry;
+ (void)key;
+ (void)klen;
+ curl_slist_free_all(tn->telnet_vars);
+ curlx_dyn_free(&tn->out);
+ free(tn);
+}
+
static
CURLcode init_telnet(struct Curl_easy *data)
{
@@ -206,8 +225,7 @@ CURLcode init_telnet(struct Curl_easy *data)
if(!tn)
return CURLE_OUT_OF_MEMORY;
- Curl_dyn_init(&tn->out, 0xffff);
- data->req.p.telnet = tn; /* make us known */
+ curlx_dyn_init(&tn->out, 0xffff);
tn->telrcv_state = CURL_TS_DATA;
@@ -246,23 +264,23 @@ CURLcode init_telnet(struct Curl_easy *data)
based upon the terminal type information that may have been sent
using the TERMINAL TYPE Telnet option). */
tn->subnegotiation[CURL_TELOPT_NAWS] = CURL_YES;
- return CURLE_OK;
+
+ return Curl_meta_set(data, CURL_META_TELNET_EASY, tn, telnet_easy_dtor);
}
-static void negotiate(struct Curl_easy *data)
+static void telnet_negotiate(struct Curl_easy *data, struct TELNET *tn)
{
int i;
- struct TELNET *tn = data->req.p.telnet;
for(i = 0; i < CURL_NTELOPTS; i++) {
if(i == CURL_TELOPT_ECHO)
continue;
if(tn->us_preferred[i] == CURL_YES)
- set_local_option(data, i, CURL_YES);
+ set_local_option(data, tn, i, CURL_YES);
if(tn->him_preferred[i] == CURL_YES)
- set_remote_option(data, i, CURL_YES);
+ set_remote_option(data, tn, i, CURL_YES);
}
}
@@ -323,9 +341,9 @@ static void send_negotiation(struct Curl_easy *data, int cmd, int option)
}
static
-void set_remote_option(struct Curl_easy *data, int option, int newstate)
+void set_remote_option(struct Curl_easy *data, struct TELNET *tn,
+ int option, int newstate)
{
- struct TELNET *tn = data->req.p.telnet;
if(newstate == CURL_YES) {
switch(tn->him[option]) {
case CURL_NO:
@@ -397,9 +415,8 @@ void set_remote_option(struct Curl_easy *data, int option, int newstate)
}
static
-void rec_will(struct Curl_easy *data, int option)
+void rec_will(struct Curl_easy *data, struct TELNET *tn, int option)
{
- struct TELNET *tn = data->req.p.telnet;
switch(tn->him[option]) {
case CURL_NO:
if(tn->him_preferred[option] == CURL_YES) {
@@ -445,9 +462,8 @@ void rec_will(struct Curl_easy *data, int option)
}
static
-void rec_wont(struct Curl_easy *data, int option)
+void rec_wont(struct Curl_easy *data, struct TELNET *tn, int option)
{
- struct TELNET *tn = data->req.p.telnet;
switch(tn->him[option]) {
case CURL_NO:
/* Already disabled */
@@ -487,9 +503,9 @@ void rec_wont(struct Curl_easy *data, int option)
}
static void
-set_local_option(struct Curl_easy *data, int option, int newstate)
+set_local_option(struct Curl_easy *data, struct TELNET *tn,
+ int option, int newstate)
{
- struct TELNET *tn = data->req.p.telnet;
if(newstate == CURL_YES) {
switch(tn->us[option]) {
case CURL_NO:
@@ -561,9 +577,8 @@ set_local_option(struct Curl_easy *data, int option, int newstate)
}
static
-void rec_do(struct Curl_easy *data, int option)
+void rec_do(struct Curl_easy *data, struct TELNET *tn, int option)
{
- struct TELNET *tn = data->req.p.telnet;
switch(tn->us[option]) {
case CURL_NO:
if(tn->us_preferred[option] == CURL_YES) {
@@ -571,13 +586,13 @@ void rec_do(struct Curl_easy *data, int option)
send_negotiation(data, CURL_WILL, option);
if(tn->subnegotiation[option] == CURL_YES)
/* transmission of data option */
- sendsuboption(data, option);
+ sendsuboption(data, tn, option);
}
else if(tn->subnegotiation[option] == CURL_YES) {
/* send information to achieve this option */
tn->us[option] = CURL_YES;
send_negotiation(data, CURL_WILL, option);
- sendsuboption(data, option);
+ sendsuboption(data, tn, option);
}
else
send_negotiation(data, CURL_WONT, option);
@@ -607,7 +622,7 @@ void rec_do(struct Curl_easy *data, int option)
tn->us[option] = CURL_YES;
if(tn->subnegotiation[option] == CURL_YES) {
/* transmission of data option */
- sendsuboption(data, option);
+ sendsuboption(data, tn, option);
}
break;
case CURL_OPPOSITE:
@@ -621,9 +636,8 @@ void rec_do(struct Curl_easy *data, int option)
}
static
-void rec_dont(struct Curl_easy *data, int option)
+void rec_dont(struct Curl_easy *data, struct TELNET *tn, int option)
{
- struct TELNET *tn = data->req.p.telnet;
switch(tn->us[option]) {
case CURL_NO:
/* Already disabled */
@@ -695,7 +709,10 @@ static void printsub(struct Curl_easy *data,
infof(data, ", not IAC SE) ");
}
}
- length -= 2;
+ if(length >= 2)
+ length -= 2;
+ else /* bad input */
+ return;
}
if(length < 1) {
infof(data, "(Empty suboption?)");
@@ -773,29 +790,21 @@ static void printsub(struct Curl_easy *data,
}
}
-#ifdef _MSC_VER
-#pragma warning(push)
-/* warning C4706: assignment within conditional expression */
-#pragma warning(disable:4706)
-#endif
static bool str_is_nonascii(const char *str)
{
char c;
- while((c = *str++))
+ while((c = *str++) != 0)
if(c & 0x80)
return TRUE;
return FALSE;
}
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
-static CURLcode check_telnet_options(struct Curl_easy *data)
+static CURLcode check_telnet_options(struct Curl_easy *data,
+ struct TELNET *tn)
{
struct curl_slist *head;
struct curl_slist *beg;
- struct TELNET *tn = data->req.p.telnet;
CURLcode result = CURLE_OK;
/* Add the username as an environment variable if it
@@ -866,22 +875,20 @@ static CURLcode check_telnet_options(struct Curl_easy *data)
case 2:
/* Window Size */
if(strncasecompare(option, "WS", 2)) {
- char *p;
- unsigned long x = strtoul(arg, &p, 10);
- unsigned long y = 0;
- if(x && (x <= 0xffff) && Curl_raw_tolower(*p) == 'x') {
- p++;
- y = strtoul(p, NULL, 10);
- if(y && (y <= 0xffff)) {
- tn->subopt_wsx = (unsigned short)x;
- tn->subopt_wsy = (unsigned short)y;
- tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES;
- }
- }
- if(!y) {
+ const char *p = arg;
+ curl_off_t x = 0;
+ curl_off_t y = 0;
+ if(curlx_str_number(&p, &x, 0xffff) ||
+ curlx_str_single(&p, 'x') ||
+ curlx_str_number(&p, &y, 0xffff)) {
failf(data, "Syntax error in telnet option: %s", head->data);
result = CURLE_SETOPT_OPTION_SYNTAX;
}
+ else {
+ tn->subopt_wsx = (unsigned short)x;
+ tn->subopt_wsy = (unsigned short)y;
+ tn->us_preferred[CURL_TELOPT_NAWS] = CURL_YES;
+ }
}
else
result = CURLE_UNKNOWN_OPTION;
@@ -926,14 +933,13 @@ static CURLcode check_telnet_options(struct Curl_easy *data)
* side.
*/
-static void suboption(struct Curl_easy *data)
+static void suboption(struct Curl_easy *data, struct TELNET *tn)
{
struct curl_slist *v;
unsigned char temp[2048];
ssize_t bytes_written;
size_t len;
int err;
- struct TELNET *tn = data->req.p.telnet;
struct connectdata *conn = data->conn;
printsub(data, '<', (unsigned char *)tn->subbuffer, CURL_SB_LEN(tn) + 2);
@@ -1005,13 +1011,13 @@ static void suboption(struct Curl_easy *data)
* Send suboption information to the server side.
*/
-static void sendsuboption(struct Curl_easy *data, int option)
+static void sendsuboption(struct Curl_easy *data,
+ struct TELNET *tn, int option)
{
ssize_t bytes_written;
int err;
unsigned short x, y;
unsigned char *uc1, *uc2;
- struct TELNET *tn = data->req.p.telnet;
struct connectdata *conn = data->conn;
switch(option) {
@@ -1048,7 +1054,7 @@ static void sendsuboption(struct Curl_easy *data, int option)
}
/* ... then the window size with the send_telnet_data() function
to deal with 0xFF cases ... */
- send_telnet_data(data, (char *)tn->subbuffer + 3, 4);
+ send_telnet_data(data, tn, (char *)tn->subbuffer + 3, 4);
/* ... and the footer */
bytes_written = swrite(conn->sock[FIRSTSOCKET], tn->subbuffer + 7, 2);
if(bytes_written < 0) {
@@ -1062,6 +1068,7 @@ static void sendsuboption(struct Curl_easy *data, int option)
static
CURLcode telrcv(struct Curl_easy *data,
+ struct TELNET *tn,
const unsigned char *inbuf, /* Data received from socket */
ssize_t count) /* Number of bytes received */
{
@@ -1069,17 +1076,16 @@ CURLcode telrcv(struct Curl_easy *data,
CURLcode result;
int in = 0;
int startwrite = -1;
- struct TELNET *tn = data->req.p.telnet;
-
-#define startskipping() \
- if(startwrite >= 0) { \
- result = Curl_client_write(data, \
- CLIENTWRITE_BODY, \
- (char *)&inbuf[startwrite], \
- in-startwrite); \
- if(result) \
- return result; \
- } \
+
+#define startskipping() \
+ if(startwrite >= 0) { \
+ result = Curl_client_write(data, \
+ CLIENTWRITE_BODY, \
+ (const char *)&inbuf[startwrite], \
+ in-startwrite); \
+ if(result) \
+ return result; \
+ } \
startwrite = -1
#define writebyte() \
@@ -1149,28 +1155,28 @@ process_iac:
case CURL_TS_WILL:
printoption(data, "RCVD", CURL_WILL, c);
tn->please_negotiate = 1;
- rec_will(data, c);
+ rec_will(data, tn, c);
tn->telrcv_state = CURL_TS_DATA;
break;
case CURL_TS_WONT:
printoption(data, "RCVD", CURL_WONT, c);
tn->please_negotiate = 1;
- rec_wont(data, c);
+ rec_wont(data, tn, c);
tn->telrcv_state = CURL_TS_DATA;
break;
case CURL_TS_DO:
printoption(data, "RCVD", CURL_DO, c);
tn->please_negotiate = 1;
- rec_do(data, c);
+ rec_do(data, tn, c);
tn->telrcv_state = CURL_TS_DATA;
break;
case CURL_TS_DONT:
printoption(data, "RCVD", CURL_DONT, c);
tn->please_negotiate = 1;
- rec_dont(data, c);
+ rec_dont(data, tn, c);
tn->telrcv_state = CURL_TS_DATA;
break;
@@ -1199,7 +1205,7 @@ process_iac:
CURL_SB_TERM(tn);
printoption(data, "In SUBOPTION processing, RCVD", CURL_IAC, c);
- suboption(data); /* handle sub-option */
+ suboption(data, tn); /* handle sub-option */
tn->telrcv_state = CURL_TS_IAC;
goto process_iac;
}
@@ -1211,7 +1217,7 @@ process_iac:
CURL_SB_ACCUM(tn, CURL_SE);
tn->subpointer -= 2;
CURL_SB_TERM(tn);
- suboption(data); /* handle sub-option */
+ suboption(data, tn); /* handle sub-option */
tn->telrcv_state = CURL_TS_DATA;
}
break;
@@ -1224,6 +1230,7 @@ process_iac:
/* Escape and send a telnet data block */
static CURLcode send_telnet_data(struct Curl_easy *data,
+ struct TELNET *tn,
char *buffer, ssize_t nread)
{
size_t i, outlen;
@@ -1232,7 +1239,6 @@ static CURLcode send_telnet_data(struct Curl_easy *data,
size_t bytes_written;
size_t total_written = 0;
struct connectdata *conn = data->conn;
- struct TELNET *tn = data->req.p.telnet;
DEBUGASSERT(tn);
DEBUGASSERT(nread > 0);
@@ -1241,17 +1247,17 @@ static CURLcode send_telnet_data(struct Curl_easy *data,
if(memchr(buffer, CURL_IAC, nread)) {
/* only use the escape buffer when necessary */
- Curl_dyn_reset(&tn->out);
+ curlx_dyn_reset(&tn->out);
for(i = 0; i < (size_t)nread && !result; i++) {
- result = Curl_dyn_addn(&tn->out, &buffer[i], 1);
+ result = curlx_dyn_addn(&tn->out, &buffer[i], 1);
if(!result && ((unsigned char)buffer[i] == CURL_IAC))
/* IAC is FF in hex */
- result = Curl_dyn_addn(&tn->out, "\xff", 1);
+ result = curlx_dyn_addn(&tn->out, "\xff", 1);
}
- outlen = Curl_dyn_len(&tn->out);
- outbuf = Curl_dyn_uptr(&tn->out);
+ outlen = curlx_dyn_len(&tn->out);
+ outbuf = curlx_dyn_uptr(&tn->out);
}
else {
outlen = (size_t)nread;
@@ -1282,16 +1288,9 @@ static CURLcode send_telnet_data(struct Curl_easy *data,
static CURLcode telnet_done(struct Curl_easy *data,
CURLcode status, bool premature)
{
- struct TELNET *tn = data->req.p.telnet;
(void)status; /* unused */
(void)premature; /* not used */
-
- if(!tn)
- return CURLE_OK;
-
- curl_slist_free_all(tn->telnet_vars);
- tn->telnet_vars = NULL;
- Curl_dyn_free(&tn->out);
+ Curl_meta_remove(data, CURL_META_TELNET_EASY);
return CURLE_OK;
}
@@ -1328,9 +1327,11 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
if(result)
return result;
- tn = data->req.p.telnet;
+ tn = Curl_meta_get(data, CURL_META_TELNET_EASY);
+ if(!tn)
+ return CURLE_FAILED_INIT;
- result = check_telnet_options(data);
+ result = check_telnet_options(data, tn);
if(result)
return result;
@@ -1423,7 +1424,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
}
}
- result = send_telnet_data(data, buffer, readfile_read);
+ result = send_telnet_data(data, tn, buffer, readfile_read);
if(result) {
keepon = FALSE;
break;
@@ -1441,7 +1442,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
break;
}
- result = send_telnet_data(data, buffer, readfile_read);
+ result = send_telnet_data(data, tn, buffer, readfile_read);
if(result) {
keepon = FALSE;
break;
@@ -1454,7 +1455,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
events.lNetworkEvents = 0;
if(WSAEnumNetworkEvents(sockfd, event_handle, &events) == SOCKET_ERROR) {
err = SOCKERRNO;
- if(err != EINPROGRESS) {
+ if(err != SOCKEINPROGRESS) {
infof(data, "WSAEnumNetworkEvents failed (%d)", err);
keepon = FALSE;
result = CURLE_READ_ERROR;
@@ -1479,7 +1480,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
break;
}
- result = telrcv(data, (unsigned char *) buffer, nread);
+ result = telrcv(data, tn, (unsigned char *) buffer, nread);
if(result) {
keepon = FALSE;
break;
@@ -1489,7 +1490,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
otherwise do not. We do not want to speak telnet with
non-telnet servers, like POP or SMTP. */
if(tn->please_negotiate && !tn->already_negotiated) {
- negotiate(data);
+ telnet_negotiate(data, tn);
tn->already_negotiated = 1;
}
}
@@ -1502,8 +1503,8 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
}
if(data->set.timeout) {
- now = Curl_now();
- if(Curl_timediff(now, conn->created) >= data->set.timeout) {
+ now = curlx_now();
+ if(curlx_timediff(now, conn->created) >= data->set.timeout) {
failf(data, "Time-out");
result = CURLE_OPERATION_TIMEDOUT;
keepon = FALSE;
@@ -1556,11 +1557,10 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
/* returned not-zero, this an error */
if(result) {
keepon = FALSE;
- /* TODO: in test 1452, macOS sees a ECONNRESET sometimes?
- * Is this the telnet test server not shutting down the socket
- * in a clean way? Seems to be timing related, happens more
- * on slow debug build */
- if(data->state.os_errno == ECONNRESET) {
+ /* In test 1452, macOS sees a ECONNRESET sometimes? Is this the
+ * telnet test server not shutting down the socket in a clean way?
+ * Seems to be timing related, happens more on slow debug build */
+ if(data->state.os_errno == SOCKECONNRESET) {
DEBUGF(infof(data, "telnet_do, unexpected ECONNRESET on recv"));
}
break;
@@ -1575,7 +1575,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
total_dl += nread;
result = Curl_pgrsSetDownloadCounter(data, total_dl);
if(!result)
- result = telrcv(data, (unsigned char *)buffer, nread);
+ result = telrcv(data, tn, (unsigned char *)buffer, nread);
if(result) {
keepon = FALSE;
break;
@@ -1585,7 +1585,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
otherwise do not. We do not want to speak telnet with
non-telnet servers, like POP or SMTP. */
if(tn->please_negotiate && !tn->already_negotiated) {
- negotiate(data);
+ telnet_negotiate(data, tn);
tn->already_negotiated = 1;
}
}
@@ -1609,7 +1609,7 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
}
if(nread > 0) {
- result = send_telnet_data(data, buffer, nread);
+ result = send_telnet_data(data, tn, buffer, nread);
if(result) {
keepon = FALSE;
break;
@@ -1624,8 +1624,8 @@ static CURLcode telnet_do(struct Curl_easy *data, bool *done)
} /* poll switch statement */
if(data->set.timeout) {
- now = Curl_now();
- if(Curl_timediff(now, conn->created) >= data->set.timeout) {
+ now = curlx_now();
+ if(curlx_timediff(now, conn->created) >= data->set.timeout) {
failf(data, "Time-out");
result = CURLE_OPERATION_TIMEDOUT;
keepon = FALSE;
diff --git a/libs/libcurl/src/tftp.c b/libs/libcurl/src/tftp.c
index 13d5e068d1..f6ba609797 100644
--- a/libs/libcurl/src/tftp.c
+++ b/libs/libcurl/src/tftp.c
@@ -62,6 +62,7 @@
#include "speedcheck.h"
#include "select.h"
#include "escape.h"
+#include "curlx/strparse.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -120,7 +121,10 @@ struct tftp_packet {
unsigned char *data;
};
-struct tftp_state_data {
+/* meta key for storing protocol meta at connection */
+#define CURL_META_TFTP_CONN "meta:proto:tftp:conn"
+
+struct tftp_conn {
tftp_state_t state;
tftp_mode_t mode;
tftp_error_t error;
@@ -135,9 +139,9 @@ struct tftp_state_data {
struct Curl_sockaddr_storage remote_addr;
curl_socklen_t remote_addrlen;
int rbytes;
- int sbytes;
- int blksize;
- int requested_blksize;
+ size_t sbytes;
+ unsigned int blksize;
+ unsigned int requested_blksize;
unsigned short block;
struct tftp_packet rpacket;
struct tftp_packet spacket;
@@ -145,12 +149,9 @@ struct tftp_state_data {
/* Forward declarations */
-static CURLcode tftp_rx(struct tftp_state_data *state, tftp_event_t event);
-static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event);
+static CURLcode tftp_rx(struct tftp_conn *state, tftp_event_t event);
+static CURLcode tftp_tx(struct tftp_conn *state, tftp_event_t event);
static CURLcode tftp_connect(struct Curl_easy *data, bool *done);
-static CURLcode tftp_disconnect(struct Curl_easy *data,
- struct connectdata *conn,
- bool dead_connection);
static CURLcode tftp_do(struct Curl_easy *data, bool *done);
static CURLcode tftp_done(struct Curl_easy *data,
CURLcode, bool premature);
@@ -180,11 +181,12 @@ const struct Curl_handler Curl_handler_tftp = {
tftp_getsock, /* doing_getsock */
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
- tftp_disconnect, /* disconnect */
+ ZERO_NULL, /* disconnect */
ZERO_NULL, /* write_resp */
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_TFTP, /* defport */
CURLPROTO_TFTP, /* protocol */
CURLPROTO_TFTP, /* family */
@@ -201,7 +203,7 @@ const struct Curl_handler Curl_handler_tftp = {
*
*
**********************************************************/
-static CURLcode tftp_set_timeouts(struct tftp_state_data *state)
+static CURLcode tftp_set_timeouts(struct tftp_conn *state)
{
time_t maxtime, timeout;
timediff_t timeout_ms;
@@ -308,7 +310,7 @@ static const char *tftp_option_get(const char *buf, size_t len,
return &buf[loc];
}
-static CURLcode tftp_parse_option_ack(struct tftp_state_data *state,
+static CURLcode tftp_parse_option_ack(struct tftp_conn *state,
const char *ptr, int len)
{
const char *tmp = ptr;
@@ -329,19 +331,16 @@ static CURLcode tftp_parse_option_ack(struct tftp_state_data *state,
infof(data, "got option=(%s) value=(%s)", option, value);
if(checkprefix(TFTP_OPTION_BLKSIZE, option)) {
- long blksize;
-
- blksize = strtol(value, NULL, 10);
-
- if(!blksize) {
- failf(data, "invalid blocksize value in OACK packet");
- return CURLE_TFTP_ILLEGAL;
- }
- if(blksize > TFTP_BLKSIZE_MAX) {
+ curl_off_t blksize;
+ if(curlx_str_number(&value, &blksize, TFTP_BLKSIZE_MAX)) {
failf(data, "%s (%d)", "blksize is larger than max supported",
TFTP_BLKSIZE_MAX);
return CURLE_TFTP_ILLEGAL;
}
+ if(!blksize) {
+ failf(data, "invalid blocksize value in OACK packet");
+ return CURLE_TFTP_ILLEGAL;
+ }
else if(blksize < TFTP_BLKSIZE_MIN) {
failf(data, "%s (%d)", "blksize is smaller than min supported",
TFTP_BLKSIZE_MIN);
@@ -351,28 +350,27 @@ static CURLcode tftp_parse_option_ack(struct tftp_state_data *state,
/* could realloc pkt buffers here, but the spec does not call out
* support for the server requesting a bigger blksize than the client
* requests */
- failf(data, "%s (%ld)",
- "server requested blksize larger than allocated", blksize);
+ failf(data, "server requested blksize larger than allocated (%"
+ CURL_FORMAT_CURL_OFF_T ")", blksize);
return CURLE_TFTP_ILLEGAL;
}
state->blksize = (int)blksize;
- infof(data, "%s (%d) %s (%d)", "blksize parsed from OACK",
- state->blksize, "requested", state->requested_blksize);
+ infof(data, "blksize parsed from OACK (%d) requested (%d)",
+ state->blksize, state->requested_blksize);
}
else if(checkprefix(TFTP_OPTION_TSIZE, option)) {
- long tsize = 0;
-
- tsize = strtol(value, NULL, 10);
- infof(data, "%s (%ld)", "tsize parsed from OACK", tsize);
-
+ curl_off_t tsize = 0;
/* tsize should be ignored on upload: Who cares about the size of the
remote file? */
- if(!data->state.upload) {
+ if(!data->state.upload &&
+ !curlx_str_number(&value, &tsize, CURL_OFF_T_MAX)) {
if(!tsize) {
failf(data, "invalid tsize -:%s:- value in OACK packet", value);
return CURLE_TFTP_ILLEGAL;
}
+ infof(data, "tsize parsed from OACK (%" CURL_FORMAT_CURL_OFF_T ")",
+ tsize);
Curl_pgrsSetDownloadSize(data, tsize);
}
}
@@ -381,7 +379,7 @@ static CURLcode tftp_parse_option_ack(struct tftp_state_data *state,
return CURLE_OK;
}
-static CURLcode tftp_option_add(struct tftp_state_data *state, size_t *csize,
+static CURLcode tftp_option_add(struct tftp_conn *state, size_t *csize,
char *buf, const char *option)
{
if(( strlen(option) + *csize + 1) > (size_t)state->blksize)
@@ -391,7 +389,7 @@ static CURLcode tftp_option_add(struct tftp_state_data *state, size_t *csize,
return CURLE_OK;
}
-static CURLcode tftp_connect_for_tx(struct tftp_state_data *state,
+static CURLcode tftp_connect_for_tx(struct tftp_conn *state,
tftp_event_t event)
{
CURLcode result;
@@ -407,7 +405,7 @@ static CURLcode tftp_connect_for_tx(struct tftp_state_data *state,
return tftp_tx(state, event);
}
-static CURLcode tftp_connect_for_rx(struct tftp_state_data *state,
+static CURLcode tftp_connect_for_rx(struct tftp_conn *state,
tftp_event_t event)
{
CURLcode result;
@@ -423,7 +421,7 @@ static CURLcode tftp_connect_for_rx(struct tftp_state_data *state,
return tftp_rx(state, event);
}
-static CURLcode tftp_send_first(struct tftp_state_data *state,
+static CURLcode tftp_send_first(struct tftp_conn *state,
tftp_event_t event)
{
size_t sbytes;
@@ -522,9 +520,14 @@ static CURLcode tftp_send_first(struct tftp_state_data *state,
/* the typecase for the 3rd argument is mostly for systems that do
not have a size_t argument, like older unixes that want an 'int' */
+#ifdef __AMIGA__
+#define CURL_SENDTO_ARG5(x) CURL_UNCONST(x)
+#else
+#define CURL_SENDTO_ARG5(x) (x)
+#endif
senddata = sendto(state->sockfd, (void *)state->spacket.data,
(SEND_TYPE_ARG3)sbytes, 0,
- &data->conn->remote_addr->curl_sa_addr,
+ CURL_SENDTO_ARG5(&data->conn->remote_addr->curl_sa_addr),
(curl_socklen_t)data->conn->remote_addr->addrlen);
if(senddata != (ssize_t)sbytes) {
char buffer[STRERROR_LEN];
@@ -573,8 +576,7 @@ static CURLcode tftp_send_first(struct tftp_state_data *state,
* Event handler for the RX state
*
**********************************************************/
-static CURLcode tftp_rx(struct tftp_state_data *state,
- tftp_event_t event)
+static CURLcode tftp_rx(struct tftp_conn *state, tftp_event_t event)
{
ssize_t sbytes;
int rblock;
@@ -696,7 +698,7 @@ static CURLcode tftp_rx(struct tftp_state_data *state,
* Event handler for the TX state
*
**********************************************************/
-static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event)
+static CURLcode tftp_tx(struct tftp_conn *state, tftp_event_t event)
{
struct Curl_easy *data = state->data;
ssize_t sbytes;
@@ -775,7 +777,7 @@ static CURLcode tftp_tx(struct tftp_state_data *state, tftp_event_t event)
&cb, &eos);
if(result)
return result;
- state->sbytes += (int)cb;
+ state->sbytes += cb;
bufptr += cb;
} while(state->sbytes < state->blksize && cb);
@@ -898,7 +900,7 @@ static CURLcode tftp_translate_code(tftp_error_t error)
* The tftp state machine event dispatcher
*
**********************************************************/
-static CURLcode tftp_state_machine(struct tftp_state_data *state,
+static CURLcode tftp_state_machine(struct tftp_conn *state,
tftp_event_t event)
{
CURLcode result = CURLE_OK;
@@ -930,28 +932,14 @@ static CURLcode tftp_state_machine(struct tftp_state_data *state,
return result;
}
-/**********************************************************
- *
- * tftp_disconnect
- *
- * The disconnect callback
- *
- **********************************************************/
-static CURLcode tftp_disconnect(struct Curl_easy *data,
- struct connectdata *conn, bool dead_connection)
+static void tftp_conn_dtor(void *key, size_t klen, void *entry)
{
- struct tftp_state_data *state = conn->proto.tftpc;
- (void) data;
- (void) dead_connection;
-
- /* done, free dynamically allocated pkt buffers */
- if(state) {
- Curl_safefree(state->rpacket.data);
- Curl_safefree(state->spacket.data);
- free(state);
- }
-
- return CURLE_OK;
+ struct tftp_conn *state = entry;
+ (void)key;
+ (void)klen;
+ Curl_safefree(state->rpacket.data);
+ Curl_safefree(state->spacket.data);
+ free(state);
}
/**********************************************************
@@ -963,15 +951,16 @@ static CURLcode tftp_disconnect(struct Curl_easy *data,
**********************************************************/
static CURLcode tftp_connect(struct Curl_easy *data, bool *done)
{
- struct tftp_state_data *state;
+ struct tftp_conn *state;
int blksize;
int need_blksize;
struct connectdata *conn = data->conn;
blksize = TFTP_BLKSIZE_DEFAULT;
- state = conn->proto.tftpc = calloc(1, sizeof(struct tftp_state_data));
- if(!state)
+ state = calloc(1, sizeof(*state));
+ if(!state ||
+ Curl_conn_meta_set(conn, CURL_META_TFTP_CONN, state, tftp_conn_dtor))
return CURLE_OUT_OF_MEMORY;
/* alloc pkt buffers based on specified blksize */
@@ -1058,7 +1047,7 @@ static CURLcode tftp_done(struct Curl_easy *data, CURLcode status,
{
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
- struct tftp_state_data *state = conn->proto.tftpc;
+ struct tftp_conn *state = Curl_conn_meta_get(conn, CURL_META_TFTP_CONN);
(void)status; /* unused */
(void)premature; /* not used */
@@ -1095,12 +1084,11 @@ static int tftp_getsock(struct Curl_easy *data,
* Called once select fires and data is ready on the socket
*
**********************************************************/
-static CURLcode tftp_receive_packet(struct Curl_easy *data)
+static CURLcode tftp_receive_packet(struct Curl_easy *data,
+ struct tftp_conn *state)
{
curl_socklen_t fromlen;
CURLcode result = CURLE_OK;
- struct connectdata *conn = data->conn;
- struct tftp_state_data *state = conn->proto.tftpc;
/* Receive the packet */
fromlen = sizeof(state->remote_addr);
@@ -1179,12 +1167,10 @@ static CURLcode tftp_receive_packet(struct Curl_easy *data)
* Check if timeouts have been reached
*
**********************************************************/
-static timediff_t tftp_state_timeout(struct Curl_easy *data,
+static timediff_t tftp_state_timeout(struct tftp_conn *state,
tftp_event_t *event)
{
time_t current;
- struct connectdata *conn = data->conn;
- struct tftp_state_data *state = conn->proto.tftpc;
timediff_t timeout_ms;
if(event)
@@ -1219,11 +1205,14 @@ static CURLcode tftp_multi_statemach(struct Curl_easy *data, bool *done)
tftp_event_t event;
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
- struct tftp_state_data *state = conn->proto.tftpc;
- timediff_t timeout_ms = tftp_state_timeout(data, &event);
+ struct tftp_conn *state = Curl_conn_meta_get(conn, CURL_META_TFTP_CONN);
+ timediff_t timeout_ms;
*done = FALSE;
+ if(!state)
+ return CURLE_FAILED_INIT;
+ timeout_ms = tftp_state_timeout(state, &event);
if(timeout_ms < 0) {
failf(data, "TFTP response timeout");
return CURLE_OPERATION_TIMEDOUT;
@@ -1249,7 +1238,7 @@ static CURLcode tftp_multi_statemach(struct Curl_easy *data, bool *done)
state->event = TFTP_EVENT_ERROR;
}
else if(rc) {
- result = tftp_receive_packet(data);
+ result = tftp_receive_packet(data, state);
if(result)
return result;
result = tftp_state_machine(state, state->event);
@@ -1288,7 +1277,7 @@ static CURLcode tftp_doing(struct Curl_easy *data, bool *dophase_done)
if(Curl_pgrsUpdate(data))
result = CURLE_ABORTED_BY_CALLBACK;
else
- result = Curl_speedcheck(data, Curl_now());
+ result = Curl_speedcheck(data, curlx_now());
}
return result;
}
@@ -1304,9 +1293,11 @@ static CURLcode tftp_perform(struct Curl_easy *data, bool *dophase_done)
{
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
- struct tftp_state_data *state = conn->proto.tftpc;
+ struct tftp_conn *state = Curl_conn_meta_get(conn, CURL_META_TFTP_CONN);
*dophase_done = FALSE;
+ if(!state)
+ return CURLE_FAILED_INIT;
result = tftp_state_machine(state, TFTP_EVENT_INIT);
@@ -1334,21 +1325,21 @@ static CURLcode tftp_perform(struct Curl_easy *data, bool *dophase_done)
static CURLcode tftp_do(struct Curl_easy *data, bool *done)
{
- struct tftp_state_data *state;
- CURLcode result;
struct connectdata *conn = data->conn;
+ struct tftp_conn *state = Curl_conn_meta_get(conn, CURL_META_TFTP_CONN);
+ CURLcode result;
*done = FALSE;
- if(!conn->proto.tftpc) {
+ if(!state) {
result = tftp_connect(data, done);
if(result)
return result;
- }
- state = conn->proto.tftpc;
- if(!state)
- return CURLE_TFTP_ILLEGAL;
+ state = Curl_conn_meta_get(conn, CURL_META_TFTP_CONN);
+ if(!state)
+ return CURLE_TFTP_ILLEGAL;
+ }
result = tftp_perform(data, done);
diff --git a/libs/libcurl/src/transfer.c b/libs/libcurl/src/transfer.c
index 53b7b84496..e39fd04d30 100644
--- a/libs/libcurl/src/transfer.c
+++ b/libs/libcurl/src/transfer.c
@@ -23,7 +23,6 @@
***************************************************************************/
#include "curl_setup.h"
-#include "strtoofft.h"
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
@@ -40,7 +39,9 @@
#ifdef HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif
+#ifndef UNDER_CE
#include <signal.h>
+#endif
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
@@ -176,9 +177,9 @@ static bool xfer_recv_shutdown_started(struct Curl_easy *data)
int sockindex;
if(!data || !data->conn)
- return CURLE_FAILED_INIT;
+ return FALSE;
if(data->conn->sockfd == CURL_SOCKET_BAD)
- return CURLE_FAILED_INIT;
+ return FALSE;
sockindex = (data->conn->sockfd == data->conn->sock[SECONDARYSOCKET]);
return Curl_shutdown_started(data, sockindex);
}
@@ -434,15 +435,6 @@ CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp)
data->state.select_bits = 0;
}
-#ifdef USE_HYPER
- if(data->conn->datastream) {
- result = data->conn->datastream(data, data->conn, &didwhat,
- CURL_CSELECT_OUT|CURL_CSELECT_IN);
- if(result || data->req.done)
- goto out;
- }
- else {
-#endif
/* We go ahead and do a read if we have a readable socket or if the stream
was rewound (in which case we have data in a buffer) */
if(k->keepon & KEEP_RECV) {
@@ -457,9 +449,6 @@ CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp)
if(result)
goto out;
}
-#ifdef USE_HYPER
- }
-#endif
if(!didwhat) {
/* Transfer wanted to send/recv, but nothing was possible. */
@@ -481,13 +470,13 @@ CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp)
failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
" milliseconds with %" FMT_OFF_T " out of %"
FMT_OFF_T " bytes received",
- Curl_timediff(*nowp, data->progress.t_startsingle),
+ curlx_timediff(*nowp, data->progress.t_startsingle),
k->bytecount, k->size);
}
else {
failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
" milliseconds with %" FMT_OFF_T " bytes received",
- Curl_timediff(*nowp, data->progress.t_startsingle),
+ curlx_timediff(*nowp, data->progress.t_startsingle),
k->bytecount);
}
result = CURLE_OPERATION_TIMEDOUT;
@@ -538,22 +527,17 @@ void Curl_init_CONNECT(struct Curl_easy *data)
*/
CURLcode Curl_pretransfer(struct Curl_easy *data)
{
- CURLcode result;
+ CURLcode result = CURLE_OK;
- if(!data->state.url && !data->set.uh) {
+ if(!data->set.str[STRING_SET_URL] && !data->set.uh) {
/* we cannot do anything without URL */
failf(data, "No URL set");
return CURLE_URL_MALFORMAT;
}
- /* since the URL may have been redirected in a previous use of this handle */
- if(data->state.url_alloc) {
- /* the already set URL is allocated, free it first! */
- Curl_safefree(data->state.url);
- data->state.url_alloc = FALSE;
- }
-
- if(!data->state.url && data->set.uh) {
+ /* CURLOPT_CURLU overrides CURLOPT_URL and the contents of the CURLU handle
+ is allowed to be changed by the user between transfers */
+ if(data->set.uh) {
CURLUcode uc;
free(data->set.str[STRING_SET_URL]);
uc = curl_url_get(data->set.uh,
@@ -564,6 +548,14 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
}
}
+ /* since the URL may have been redirected in a previous use of this handle */
+ if(data->state.url_alloc) {
+ Curl_safefree(data->state.url);
+ data->state.url_alloc = FALSE;
+ }
+
+ data->state.url = data->set.str[STRING_SET_URL];
+
if(data->set.postfields && data->set.set_resume_from) {
/* we cannot */
failf(data, "cannot mix POSTFIELDS with RESUME_FROM");
@@ -575,21 +567,14 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
data->state.list_only = data->set.list_only;
#endif
data->state.httpreq = data->set.method;
- data->state.url = data->set.str[STRING_SET_URL];
-
- /* Init the SSL session ID cache here. We do it here since we want to do it
- after the *_setopt() calls (that could specify the size of the cache) but
- before any transfer takes place. */
- result = Curl_ssl_initsessions(data, data->set.general_ssl.max_ssl_sessions);
- if(result)
- return result;
data->state.requests = 0;
data->state.followlocation = 0; /* reset the location-follow counter */
data->state.this_is_a_follow = FALSE; /* reset this */
data->state.errorbuf = FALSE; /* no error has occurred */
- data->state.httpwant = data->set.httpwant;
- data->state.httpversion = 0;
+#ifndef CURL_DISABLE_HTTP
+ Curl_http_neg_init(data, &data->state.http_neg);
+#endif
data->state.authproblem = FALSE;
data->state.authhost.want = data->set.httpauth;
data->state.authproxy.want = data->set.proxyauth;
@@ -671,7 +656,7 @@ CURLcode Curl_pretransfer(struct Curl_easy *data)
* protocol.
*/
if(data->set.str[STRING_USERAGENT]) {
- Curl_safefree(data->state.aptr.uagent);
+ free(data->state.aptr.uagent);
data->state.aptr.uagent =
aprintf("User-Agent: %s\r\n", data->set.str[STRING_USERAGENT]);
if(!data->state.aptr.uagent)
@@ -793,7 +778,7 @@ static void xfer_setup(
DEBUGASSERT((writesockindex <= 1) && (writesockindex >= -1));
DEBUGASSERT(!shutdown || (sockindex == -1) || (writesockindex == -1));
- if(conn->bits.multiplex || conn->httpversion >= 20 || want_send) {
+ if(Curl_conn_is_multiplex(conn, FIRSTSOCKET) || want_send) {
/* when multiplexing, the read/write sockets need to be the same! */
conn->sockfd = sockindex == -1 ?
((writesockindex == -1 ? CURL_SOCKET_BAD : conn->sock[writesockindex])) :
@@ -897,6 +882,11 @@ CURLcode Curl_xfer_write_resp(struct Curl_easy *data,
return result;
}
+bool Curl_xfer_write_is_paused(struct Curl_easy *data)
+{
+ return Curl_cwriter_is_paused(data);
+}
+
CURLcode Curl_xfer_write_resp_hd(struct Curl_easy *data,
const char *hd0, size_t hdlen, bool is_eos)
{
@@ -984,9 +974,9 @@ bool Curl_xfer_is_blocked(struct Curl_easy *data)
bool want_send = ((data)->req.keepon & KEEP_SEND);
bool want_recv = ((data)->req.keepon & KEEP_RECV);
if(!want_send)
- return (want_recv && Curl_cwriter_is_paused(data));
+ return want_recv && Curl_cwriter_is_paused(data);
else if(!want_recv)
- return (want_send && Curl_creader_is_paused(data));
+ return want_send && Curl_creader_is_paused(data);
else
return Curl_creader_is_paused(data) && Curl_cwriter_is_paused(data);
}
diff --git a/libs/libcurl/src/transfer.h b/libs/libcurl/src/transfer.h
index 26a17568bb..025cb448f8 100644
--- a/libs/libcurl/src/transfer.h
+++ b/libs/libcurl/src/transfer.h
@@ -33,15 +33,6 @@ void Curl_init_CONNECT(struct Curl_easy *data);
CURLcode Curl_pretransfer(struct Curl_easy *data);
-typedef enum {
- FOLLOW_NONE, /* not used within the function, just a placeholder to
- allow initing to this */
- FOLLOW_FAKE, /* only records stuff, not actually following */
- FOLLOW_RETRY, /* set if this is a request retry as opposed to a real
- redirect following */
- FOLLOW_REDIR /* a full true redirect */
-} followtype;
-
CURLcode Curl_sendrecv(struct Curl_easy *data, struct curltime *nowp);
int Curl_single_getsock(struct Curl_easy *data,
struct connectdata *conn, curl_socket_t *socks);
@@ -64,9 +55,11 @@ CURLcode Curl_xfer_write_resp(struct Curl_easy *data,
const char *buf, size_t blen,
bool is_eos);
+bool Curl_xfer_write_is_paused(struct Curl_easy *data);
+
/**
* Write a single "header" line from a server response.
- * @param hd0 the 0-terminated, single header line
+ * @param hd0 the null-terminated, single header line
* @param hdlen the length of the header line
* @param is_eos TRUE iff this is the end of the response
*/
diff --git a/libs/libcurl/src/uint-bset.c b/libs/libcurl/src/uint-bset.c
new file mode 100644
index 0000000000..5103aab010
--- /dev/null
+++ b/libs/libcurl/src/uint-bset.c
@@ -0,0 +1,238 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+#include "uint-bset.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#ifdef DEBUGBUILD
+#define CURL_UINT_BSET_MAGIC 0x62757473
+#endif
+
+void Curl_uint_bset_init(struct uint_bset *bset)
+{
+ memset(bset, 0, sizeof(*bset));
+#ifdef DEBUGBUILD
+ bset->init = CURL_UINT_BSET_MAGIC;
+#endif
+}
+
+
+CURLcode Curl_uint_bset_resize(struct uint_bset *bset, unsigned int nmax)
+{
+ unsigned int nslots = (nmax + 63) / 64;
+
+ DEBUGASSERT(bset->init == CURL_UINT_BSET_MAGIC);
+ if(nslots != bset->nslots) {
+ curl_uint64_t *slots = calloc(nslots, sizeof(curl_uint64_t));
+ if(!slots)
+ return CURLE_OUT_OF_MEMORY;
+
+ if(bset->slots) {
+ memcpy(slots, bset->slots,
+ (CURLMIN(nslots, bset->nslots) * sizeof(curl_uint64_t)));
+ free(bset->slots);
+ }
+ bset->slots = slots;
+ bset->nslots = nslots;
+ }
+ return CURLE_OK;
+}
+
+
+void Curl_uint_bset_destroy(struct uint_bset *bset)
+{
+ DEBUGASSERT(bset->init == CURL_UINT_BSET_MAGIC);
+ free(bset->slots);
+ memset(bset, 0, sizeof(*bset));
+}
+
+
+unsigned int Curl_uint_bset_capacity(struct uint_bset *bset)
+{
+ return bset->nslots * 64;
+}
+
+
+unsigned int Curl_uint_bset_count(struct uint_bset *bset)
+{
+ unsigned int i;
+ unsigned int n = 0;
+ for(i = 0; i < bset->nslots; ++i) {
+ if(bset->slots[i])
+ n += CURL_POPCOUNT64(bset->slots[i]);
+ }
+ return n;
+}
+
+
+bool Curl_uint_bset_empty(struct uint_bset *bset)
+{
+ unsigned int i;
+ for(i = 0; i < bset->nslots; ++i) {
+ if(bset->slots[i])
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+void Curl_uint_bset_clear(struct uint_bset *bset)
+{
+ if(bset->nslots)
+ memset(bset->slots, 0, bset->nslots * sizeof(curl_uint64_t));
+}
+
+
+bool Curl_uint_bset_add(struct uint_bset *bset, unsigned int i)
+{
+ unsigned int islot = i / 64;
+ if(islot >= bset->nslots)
+ return FALSE;
+ bset->slots[islot] |= ((curl_uint64_t)1 << (i % 64));
+ return TRUE;
+}
+
+
+void Curl_uint_bset_remove(struct uint_bset *bset, unsigned int i)
+{
+ size_t islot = i / 64;
+ if(islot < bset->nslots)
+ bset->slots[islot] &= ~((curl_uint64_t)1 << (i % 64));
+}
+
+
+bool Curl_uint_bset_contains(struct uint_bset *bset, unsigned int i)
+{
+ unsigned int islot = i / 64;
+ if(islot >= bset->nslots)
+ return FALSE;
+ return (bset->slots[islot] & ((curl_uint64_t)1 << (i % 64))) != 0;
+}
+
+
+bool Curl_uint_bset_first(struct uint_bset *bset, unsigned int *pfirst)
+{
+ unsigned int i;
+ for(i = 0; i < bset->nslots; ++i) {
+ if(bset->slots[i]) {
+ *pfirst = (i * 64) + CURL_CTZ64(bset->slots[i]);
+ return TRUE;
+ }
+ }
+ *pfirst = UINT_MAX; /* a value we cannot store */
+ return FALSE;
+}
+
+bool Curl_uint_bset_next(struct uint_bset *bset, unsigned int last,
+ unsigned int *pnext)
+{
+ unsigned int islot;
+ curl_uint64_t x;
+
+ ++last; /* look for number one higher than last */
+ islot = last / 64; /* the slot this would be in */
+ if(islot < bset->nslots) {
+ /* shift away the bits we already iterated in this slot */
+ x = (bset->slots[islot] >> (last % 64));
+ if(x) {
+ /* more bits set, next is `last` + trailing0s of the shifted slot */
+ *pnext = last + CURL_CTZ64(x);
+ return TRUE;
+ }
+ /* no more bits set in the last slot, scan forward */
+ for(islot = islot + 1; islot < bset->nslots; ++islot) {
+ if(bset->slots[islot]) {
+ *pnext = (islot * 64) + CURL_CTZ64(bset->slots[islot]);
+ return TRUE;
+ }
+ }
+ }
+ *pnext = UINT_MAX; /* a value we cannot store */
+ return FALSE;
+}
+
+#ifdef CURL_POPCOUNT64_IMPLEMENT
+unsigned int Curl_popcount64(curl_uint64_t x)
+{
+ /* Compute the "Hamming Distance" between 'x' and 0,
+ * which is the number of set bits in 'x'.
+ * See: https://en.wikipedia.org/wiki/Hamming_weight */
+ const curl_uint64_t m1 = CURL_OFF_TU_C(0x5555555555555555); /* 0101+ */
+ const curl_uint64_t m2 = CURL_OFF_TU_C(0x3333333333333333); /* 00110011+ */
+ const curl_uint64_t m4 = CURL_OFF_TU_C(0x0f0f0f0f0f0f0f0f); /* 00001111+ */
+ /* 1 + 256^1 + 256^2 + 256^3 + ... + 256^7 */
+ const curl_uint64_t h01 = CURL_OFF_TU_C(0x0101010101010101);
+ x -= (x >> 1) & m1; /* replace every 2 bits with bits present */
+ x = (x & m2) + ((x >> 2) & m2); /* replace every nibble with bits present */
+ x = (x + (x >> 4)) & m4; /* replace every byte with bits present */
+ /* top 8 bits of x + (x<<8) + (x<<16) + (x<<24) + ... which makes the
+ * top byte the sum of all individual 8 bytes, throw away the rest */
+ return (unsigned int)((x * h01) >> 56);
+}
+#endif /* CURL_POPCOUNT64_IMPLEMENT */
+
+
+#ifdef CURL_CTZ64_IMPLEMENT
+unsigned int Curl_ctz64(curl_uint64_t x)
+{
+ /* count trailing zeros in a curl_uint64_t.
+ * divide and conquer to find the number of lower 0 bits */
+ const curl_uint64_t ml32 = CURL_OFF_TU_C(0xFFFFFFFF); /* lower 32 bits */
+ const curl_uint64_t ml16 = CURL_OFF_TU_C(0x0000FFFF); /* lower 16 bits */
+ const curl_uint64_t ml8 = CURL_OFF_TU_C(0x000000FF); /* lower 8 bits */
+ const curl_uint64_t ml4 = CURL_OFF_TU_C(0x0000000F); /* lower 4 bits */
+ const curl_uint64_t ml2 = CURL_OFF_TU_C(0x00000003); /* lower 2 bits */
+ unsigned int n;
+
+ if(!x)
+ return 64;
+ n = 1;
+ if(!(x & ml32)) {
+ n = n + 32;
+ x = x >> 32;
+ }
+ if(!(x & ml16)) {
+ n = n + 16;
+ x = x >> 16;
+ }
+ if(!(x & ml8)) {
+ n = n + 8;
+ x = x >> 8;
+ }
+ if(!(x & ml4)) {
+ n = n + 4;
+ x = x >> 4;
+ }
+ if(!(x & ml2)) {
+ n = n + 2;
+ x = x >> 2;
+ }
+ return n - (unsigned int)(x & 1);
+}
+#endif /* CURL_CTZ64_IMPLEMENT */
diff --git a/libs/libcurl/src/uint-bset.h b/libs/libcurl/src/uint-bset.h
new file mode 100644
index 0000000000..7eab4f44bf
--- /dev/null
+++ b/libs/libcurl/src/uint-bset.h
@@ -0,0 +1,114 @@
+#ifndef HEADER_CURL_UINT_BSET_H
+#define HEADER_CURL_UINT_BSET_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+
+/* A bitset for unsigned int values.
+ * It can hold the numbers from 0 - (nmax - 1),
+ * rounded to the next 64 multiple.
+ *
+ * Optimized for high efficiency in adding/removing numbers.
+ * Efficient storage when the set is (often) relatively full.
+ *
+ * If the set's cardinality is only expected to be a fraction of nmax,
+ * uint_spbset offers a "sparse" variant with more memory efficiency at
+ * the price of slightly slower operations.
+ */
+
+struct uint_bset {
+ curl_uint64_t *slots;
+ unsigned int nslots;
+#ifdef DEBUGBUILD
+ int init;
+#endif
+};
+
+/* Initialize the bitset with capacity 0. */
+void Curl_uint_bset_init(struct uint_bset *bset);
+
+/* Resize the bitset capacity to hold numbers from 0 to `nmax`,
+ * which rounds up `nmax` to the next multiple of 64. */
+CURLcode Curl_uint_bset_resize(struct uint_bset *bset, unsigned int nmax);
+
+/* Destroy the bitset, freeing all resources. */
+void Curl_uint_bset_destroy(struct uint_bset *bset);
+
+/* Get the bitset capacity, e.g. can hold numbers from 0 to capacity - 1. */
+unsigned int Curl_uint_bset_capacity(struct uint_bset *bset);
+
+/* Get the cardinality of the bitset, e.g. numbers present in the set. */
+unsigned int Curl_uint_bset_count(struct uint_bset *bset);
+
+/* TRUE of bitset is empty */
+bool Curl_uint_bset_empty(struct uint_bset *bset);
+
+/* Clear the bitset, making it empty. */
+void Curl_uint_bset_clear(struct uint_bset *bset);
+
+/* Add the number `i` to the bitset. Return FALSE if the number is
+ * outside the set's capacity.
+ * Numbers can be added more than once, without making a difference. */
+bool Curl_uint_bset_add(struct uint_bset *bset, unsigned int i);
+
+/* Remove the number `i` from the bitset. */
+void Curl_uint_bset_remove(struct uint_bset *bset, unsigned int i);
+
+/* Return TRUE if the bitset contains number `i`. */
+bool Curl_uint_bset_contains(struct uint_bset *bset, unsigned int i);
+
+/* Get the first number in the bitset, e.g. the smallest.
+ * Returns FALSE when the bitset is empty. */
+bool Curl_uint_bset_first(struct uint_bset *bset, unsigned int *pfirst);
+
+/* Get the next number in the bitset, following `last` in natural order.
+ * Put another way, this is the smallest number greater than `last` in
+ * the bitset. `last` does not have to be present in the set.
+ *
+ * Returns FALSE when no such number is in the set.
+ *
+ * This allows to iterate the set while being modified:
+ * - added numbers higher than 'last' will be picked up by the iteration.
+ * - added numbers lower than 'last' will not show up.
+ * - removed numbers lower or equal to 'last' will not show up.
+ * - removed numbers higher than 'last' will not be visited. */
+bool Curl_uint_bset_next(struct uint_bset *bset, unsigned int last,
+ unsigned int *pnext);
+
+
+#ifndef CURL_POPCOUNT64
+#define CURL_POPCOUNT64(x) Curl_popcount64(x)
+#define CURL_POPCOUNT64_IMPLEMENT
+unsigned int Curl_popcount64(curl_uint64_t x);
+#endif /* !CURL_POPCOUNT64 */
+
+#ifndef CURL_CTZ64
+#define CURL_CTZ64(x) Curl_ctz64(x)
+#define CURL_CTZ64_IMPLEMENT
+unsigned int Curl_ctz64(curl_uint64_t x);
+#endif /* !CURL_CTZ64 */
+
+#endif /* HEADER_CURL_UINT_BSET_H */
diff --git a/libs/libcurl/src/uint-hash.c b/libs/libcurl/src/uint-hash.c
new file mode 100644
index 0000000000..8257813bd2
--- /dev/null
+++ b/libs/libcurl/src/uint-hash.c
@@ -0,0 +1,246 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+
+#include "uint-hash.h"
+#include "curl_memory.h"
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/* random patterns for API verification */
+#ifdef DEBUGBUILD
+#define CURL_UINTHASHINIT 0x7117e779
+#endif
+
+static unsigned int uint_hash_hash(unsigned int id, unsigned int slots)
+{
+ return (id % slots);
+}
+
+
+struct uint_hash_entry {
+ struct uint_hash_entry *next;
+ void *value;
+ unsigned int id;
+};
+
+void Curl_uint_hash_init(struct uint_hash *h,
+ unsigned int slots,
+ Curl_uint_hash_dtor *dtor)
+{
+ DEBUGASSERT(h);
+ DEBUGASSERT(slots);
+
+ h->table = NULL;
+ h->dtor = dtor;
+ h->size = 0;
+ h->slots = slots;
+#ifdef DEBUGBUILD
+ h->init = CURL_UINTHASHINIT;
+#endif
+}
+
+static struct uint_hash_entry *uint_hash_mk_entry(unsigned int id, void *value)
+{
+ struct uint_hash_entry *e;
+
+ /* allocate the struct for the hash entry */
+ e = malloc(sizeof(*e));
+ if(e) {
+ e->id = id;
+ e->next = NULL;
+ e->value = value;
+ }
+ return e;
+}
+
+static void uint_hash_entry_clear(struct uint_hash *h,
+ struct uint_hash_entry *e)
+{
+ DEBUGASSERT(h);
+ DEBUGASSERT(e);
+ if(e->value) {
+ if(h->dtor)
+ h->dtor(e->id, e->value);
+ e->value = NULL;
+ }
+}
+
+static void uint_hash_entry_destroy(struct uint_hash *h,
+ struct uint_hash_entry *e)
+{
+ uint_hash_entry_clear(h, e);
+ free(e);
+}
+
+static void uint_hash_entry_unlink(struct uint_hash *h,
+ struct uint_hash_entry **he_anchor,
+ struct uint_hash_entry *he)
+{
+ *he_anchor = he->next;
+ --h->size;
+}
+
+static void uint_hash_elem_link(struct uint_hash *h,
+ struct uint_hash_entry **he_anchor,
+ struct uint_hash_entry *he)
+{
+ he->next = *he_anchor;
+ *he_anchor = he;
+ ++h->size;
+}
+
+#define CURL_UINT_HASH_SLOT(h,id) h->table[uint_hash_hash(id, h->slots)]
+#define CURL_UINT_HASH_SLOT_ADDR(h,id) &CURL_UINT_HASH_SLOT(h,id)
+
+bool Curl_uint_hash_set(struct uint_hash *h, unsigned int id, void *value)
+{
+ struct uint_hash_entry *he, **slot;
+
+ DEBUGASSERT(h);
+ DEBUGASSERT(h->slots);
+ DEBUGASSERT(h->init == CURL_UINTHASHINIT);
+ if(!h->table) {
+ h->table = calloc(h->slots, sizeof(*he));
+ if(!h->table)
+ return FALSE; /* OOM */
+ }
+
+ slot = CURL_UINT_HASH_SLOT_ADDR(h, id);
+ for(he = *slot; he; he = he->next) {
+ if(he->id == id) {
+ /* existing key entry, overwrite by clearing old pointer */
+ uint_hash_entry_clear(h, he);
+ he->value = value;
+ return TRUE;
+ }
+ }
+
+ he = uint_hash_mk_entry(id, value);
+ if(!he)
+ return FALSE; /* OOM */
+
+ uint_hash_elem_link(h, slot, he);
+ return TRUE;
+}
+
+bool Curl_uint_hash_remove(struct uint_hash *h, unsigned int id)
+{
+ DEBUGASSERT(h);
+ DEBUGASSERT(h->slots);
+ DEBUGASSERT(h->init == CURL_UINTHASHINIT);
+ if(h->table) {
+ struct uint_hash_entry *he, **he_anchor;
+
+ he_anchor = CURL_UINT_HASH_SLOT_ADDR(h, id);
+ while(*he_anchor) {
+ he = *he_anchor;
+ if(id == he->id) {
+ uint_hash_entry_unlink(h, he_anchor, he);
+ uint_hash_entry_destroy(h, he);
+ return TRUE;
+ }
+ he_anchor = &he->next;
+ }
+ }
+ return FALSE;
+}
+
+void *Curl_uint_hash_get(struct uint_hash *h, unsigned int id)
+{
+ DEBUGASSERT(h);
+ DEBUGASSERT(h->init == CURL_UINTHASHINIT);
+ if(h->table) {
+ struct uint_hash_entry *he;
+ DEBUGASSERT(h->slots);
+ he = CURL_UINT_HASH_SLOT(h, id);
+ while(he) {
+ if(id == he->id) {
+ return he->value;
+ }
+ he = he->next;
+ }
+ }
+ return NULL;
+}
+
+static void uint_hash_clear(struct uint_hash *h)
+{
+ if(h && h->table) {
+ struct uint_hash_entry *he, **he_anchor;
+ size_t i;
+ DEBUGASSERT(h->init == CURL_UINTHASHINIT);
+ for(i = 0; i < h->slots; ++i) {
+ he_anchor = &h->table[i];
+ while(*he_anchor) {
+ he = *he_anchor;
+ uint_hash_entry_unlink(h, he_anchor, he);
+ uint_hash_entry_destroy(h, he);
+ }
+ }
+ }
+}
+
+void Curl_uint_hash_clear(struct uint_hash *h)
+{
+ uint_hash_clear(h);
+}
+
+void Curl_uint_hash_destroy(struct uint_hash *h)
+{
+ DEBUGASSERT(h->init == CURL_UINTHASHINIT);
+ if(h->table) {
+ uint_hash_clear(h);
+ Curl_safefree(h->table);
+ }
+ DEBUGASSERT(h->size == 0);
+ h->slots = 0;
+}
+
+unsigned int Curl_uint_hash_count(struct uint_hash *h)
+{
+ DEBUGASSERT(h->init == CURL_UINTHASHINIT);
+ return h->size;
+}
+
+void Curl_uint_hash_visit(struct uint_hash *h,
+ Curl_uint_hash_visit_cb *cb,
+ void *user_data)
+{
+ if(h && h->table && cb) {
+ struct uint_hash_entry *he;
+ size_t i;
+ DEBUGASSERT(h->init == CURL_UINTHASHINIT);
+ for(i = 0; i < h->slots; ++i) {
+ for(he = h->table[i]; he; he = he->next) {
+ if(!cb(he->id, he->value, user_data))
+ return;
+ }
+ }
+ }
+}
diff --git a/libs/libcurl/src/uint-hash.h b/libs/libcurl/src/uint-hash.h
new file mode 100644
index 0000000000..d2e60c5faf
--- /dev/null
+++ b/libs/libcurl/src/uint-hash.h
@@ -0,0 +1,68 @@
+#ifndef HEADER_CURL_UINT_HASH_H
+#define HEADER_CURL_UINT_HASH_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+
+#include <stddef.h>
+
+#include "llist.h"
+
+/* A version with unsigned int as key */
+typedef void Curl_uint_hash_dtor(unsigned int id, void *value);
+struct uint_hash_entry;
+
+/* Hash for `unsigned int` as key */
+struct uint_hash {
+ struct uint_hash_entry **table;
+ Curl_uint_hash_dtor *dtor;
+ unsigned int slots;
+ unsigned int size;
+#ifdef DEBUGBUILD
+ int init;
+#endif
+};
+
+
+void Curl_uint_hash_init(struct uint_hash *h,
+ unsigned int slots,
+ Curl_uint_hash_dtor *dtor);
+void Curl_uint_hash_destroy(struct uint_hash *h);
+void Curl_uint_hash_clear(struct uint_hash *h);
+
+bool Curl_uint_hash_set(struct uint_hash *h, unsigned int id, void *value);
+bool Curl_uint_hash_remove(struct uint_hash *h, unsigned int id);
+void *Curl_uint_hash_get(struct uint_hash *h, unsigned int id);
+unsigned int Curl_uint_hash_count(struct uint_hash *h);
+
+
+typedef bool Curl_uint_hash_visit_cb(unsigned int id, void *value,
+ void *user_data);
+
+void Curl_uint_hash_visit(struct uint_hash *h,
+ Curl_uint_hash_visit_cb *cb,
+ void *user_data);
+
+#endif /* HEADER_CURL_UINT_HASH_H */
diff --git a/libs/libcurl/src/uint-spbset.c b/libs/libcurl/src/uint-spbset.c
new file mode 100644
index 0000000000..cd367f2799
--- /dev/null
+++ b/libs/libcurl/src/uint-spbset.c
@@ -0,0 +1,273 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+#include "uint-bset.h"
+#include "uint-spbset.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#ifdef DEBUGBUILD
+#define CURL_UINT_SPBSET_MAGIC 0x70737362
+#endif
+
+void Curl_uint_spbset_init(struct uint_spbset *bset)
+{
+ memset(bset, 0, sizeof(*bset));
+#ifdef DEBUGBUILD
+ bset->init = CURL_UINT_SPBSET_MAGIC;
+#endif
+}
+
+void Curl_uint_spbset_destroy(struct uint_spbset *bset)
+{
+ DEBUGASSERT(bset->init == CURL_UINT_SPBSET_MAGIC);
+ Curl_uint_spbset_clear(bset);
+}
+
+unsigned int Curl_uint_spbset_count(struct uint_spbset *bset)
+{
+ struct uint_spbset_chunk *chunk;
+ unsigned int i, n = 0;
+
+ for(chunk = &bset->head; chunk; chunk = chunk->next) {
+ for(i = 0; i < CURL_UINT_SPBSET_CH_SLOTS; ++i) {
+ if(chunk->slots[i])
+ n += CURL_POPCOUNT64(chunk->slots[i]);
+ }
+ }
+ return n;
+}
+
+bool Curl_uint_spbset_empty(struct uint_spbset *bset)
+{
+ struct uint_spbset_chunk *chunk;
+ unsigned int i;
+
+ for(chunk = &bset->head; chunk; chunk = chunk->next) {
+ for(i = 0; i < CURL_UINT_SPBSET_CH_SLOTS; ++i) {
+ if(chunk->slots[i])
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+void Curl_uint_spbset_clear(struct uint_spbset *bset)
+{
+ struct uint_spbset_chunk *next, *chunk;
+
+ for(chunk = bset->head.next; chunk; chunk = next) {
+ next = chunk->next;
+ free(chunk);
+ }
+ memset(&bset->head, 0, sizeof(bset->head));
+}
+
+
+static struct uint_spbset_chunk *
+uint_spbset_get_chunk(struct uint_spbset *bset, unsigned int i, bool grow)
+{
+ struct uint_spbset_chunk *chunk, **panchor = NULL;
+ unsigned int i_offset = (i & ~CURL_UINT_SPBSET_CH_MASK);
+
+ if(!bset)
+ return NULL;
+
+ for(chunk = &bset->head; chunk;
+ panchor = &chunk->next, chunk = chunk->next) {
+ if(chunk->offset == i_offset) {
+ return chunk;
+ }
+ else if(chunk->offset > i_offset) {
+ /* need new chunk here */
+ chunk = NULL;
+ break;
+ }
+ }
+
+ if(!grow)
+ return NULL;
+
+ /* need a new one */
+ chunk = calloc(1, sizeof(*chunk));
+ if(!chunk)
+ return NULL;
+
+ if(panchor) { /* insert between panchor and *panchor */
+ chunk->next = *panchor;
+ *panchor = chunk;
+ }
+ else { /* prepend to head, switching places */
+ memcpy(chunk, &bset->head, sizeof(*chunk));
+ memset(&bset->head, 0, sizeof(bset->head));
+ bset->head.next = chunk;
+ }
+ chunk->offset = i_offset;
+ return chunk;
+}
+
+
+bool Curl_uint_spbset_add(struct uint_spbset *bset, unsigned int i)
+{
+ struct uint_spbset_chunk *chunk;
+ unsigned int i_chunk;
+
+ chunk = uint_spbset_get_chunk(bset, i, TRUE);
+ if(!chunk)
+ return FALSE;
+
+ DEBUGASSERT(i >= chunk->offset);
+ i_chunk = (i - chunk->offset);
+ DEBUGASSERT((i_chunk / 64) < CURL_UINT_SPBSET_CH_SLOTS);
+ chunk->slots[(i_chunk / 64)] |= ((curl_uint64_t)1 << (i_chunk % 64));
+ return TRUE;
+}
+
+
+void Curl_uint_spbset_remove(struct uint_spbset *bset, unsigned int i)
+{
+ struct uint_spbset_chunk *chunk;
+ unsigned int i_chunk;
+
+ chunk = uint_spbset_get_chunk(bset, i, FALSE);
+ if(chunk) {
+ DEBUGASSERT(i >= chunk->offset);
+ i_chunk = (i - chunk->offset);
+ DEBUGASSERT((i_chunk / 64) < CURL_UINT_SPBSET_CH_SLOTS);
+ chunk->slots[(i_chunk / 64)] &= ~((curl_uint64_t)1 << (i_chunk % 64));
+ }
+}
+
+
+bool Curl_uint_spbset_contains(struct uint_spbset *bset, unsigned int i)
+{
+ struct uint_spbset_chunk *chunk;
+ unsigned int i_chunk;
+
+ chunk = uint_spbset_get_chunk(bset, i, FALSE);
+ if(chunk) {
+ DEBUGASSERT(i >= chunk->offset);
+ i_chunk = (i - chunk->offset);
+ DEBUGASSERT((i_chunk / 64) < CURL_UINT_SPBSET_CH_SLOTS);
+ return (chunk->slots[i_chunk / 64] &
+ ((curl_uint64_t)1 << (i_chunk % 64))) != 0;
+ }
+ return FALSE;
+}
+
+bool Curl_uint_spbset_first(struct uint_spbset *bset, unsigned int *pfirst)
+{
+ struct uint_spbset_chunk *chunk;
+ unsigned int i;
+
+ for(chunk = &bset->head; chunk; chunk = chunk->next) {
+ for(i = 0; i < CURL_UINT_SPBSET_CH_SLOTS; ++i) {
+ if(chunk->slots[i]) {
+ *pfirst = chunk->offset + ((i * 64) + CURL_CTZ64(chunk->slots[i]));
+ return TRUE;
+ }
+ }
+ }
+ *pfirst = 0; /* give it a defined value even if it should not be used */
+ return FALSE;
+}
+
+
+static bool uint_spbset_chunk_first(struct uint_spbset_chunk *chunk,
+ unsigned int *pfirst)
+{
+ unsigned int i;
+ for(i = 0; i < CURL_UINT_SPBSET_CH_SLOTS; ++i) {
+ if(chunk->slots[i]) {
+ *pfirst = chunk->offset + ((i * 64) + CURL_CTZ64(chunk->slots[i]));
+ return TRUE;
+ }
+ }
+ *pfirst = UINT_MAX; /* a value we cannot store */
+ return FALSE;
+}
+
+
+static bool uint_spbset_chunk_next(struct uint_spbset_chunk *chunk,
+ unsigned int last,
+ unsigned int *pnext)
+{
+ if(chunk->offset <= last) {
+ curl_uint64_t x;
+ unsigned int i = ((last - chunk->offset) / 64);
+ if(i < CURL_UINT_SPBSET_CH_SLOTS) {
+ x = (chunk->slots[i] >> (last % 64));
+ if(x) {
+ /* more bits set, next is `last` + trailing0s of the shifted slot */
+ *pnext = last + CURL_CTZ64(x);
+ return TRUE;
+ }
+ /* no more bits set in the last slot, scan forward */
+ for(i = i + 1; i < CURL_UINT_SPBSET_CH_SLOTS; ++i) {
+ if(chunk->slots[i]) {
+ *pnext = chunk->offset + ((i * 64) + CURL_CTZ64(chunk->slots[i]));
+ return TRUE;
+ }
+ }
+ }
+ }
+ *pnext = UINT_MAX;
+ return FALSE;
+}
+
+bool Curl_uint_spbset_next(struct uint_spbset *bset, unsigned int last,
+ unsigned int *pnext)
+{
+ struct uint_spbset_chunk *chunk;
+ unsigned int last_offset;
+
+ ++last; /* look for the next higher number */
+ last_offset = (last & ~CURL_UINT_SPBSET_CH_MASK);
+
+ for(chunk = &bset->head; chunk; chunk = chunk->next) {
+ if(chunk->offset >= last_offset) {
+ break;
+ }
+ }
+
+ if(chunk && (chunk->offset == last_offset)) {
+ /* is there a number higher than last in this chunk? */
+ if(uint_spbset_chunk_next(chunk, last, pnext))
+ return TRUE;
+ /* not in this chunk */
+ chunk = chunk->next;
+ }
+ /* look for the first in the "higher" chunks, if there are any. */
+ while(chunk) {
+ if(uint_spbset_chunk_first(chunk, pnext))
+ return TRUE;
+ chunk = chunk->next;
+ }
+ *pnext = UINT_MAX;
+ return FALSE;
+}
diff --git a/libs/libcurl/src/uint-spbset.h b/libs/libcurl/src/uint-spbset.h
new file mode 100644
index 0000000000..060a0e6c0f
--- /dev/null
+++ b/libs/libcurl/src/uint-spbset.h
@@ -0,0 +1,99 @@
+#ifndef HEADER_CURL_UINT_SPBSET_H
+#define HEADER_CURL_UINT_SPBSET_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+
+/* A "sparse" bitset for unsigned int values.
+ * It can hold any unsigned int value.
+ *
+ * Optimized for the case where only a small set of numbers need
+ * to be kept, especially when "close" together. Then storage space
+ * is most efficient, deteriorating when many number are far apart.
+ */
+
+/* 4 slots = 256 bits, keep this a 2^n value. */
+#define CURL_UINT_SPBSET_CH_SLOTS 4
+#define CURL_UINT_SPBSET_CH_MASK ((CURL_UINT_SPBSET_CH_SLOTS * 64) - 1)
+
+/* store the uint value from offset to
+ * (offset + (CURL_UINT_SPBSET_CHUNK_SLOTS * 64) - 1 */
+struct uint_spbset_chunk {
+ struct uint_spbset_chunk *next;
+ curl_uint64_t slots[CURL_UINT_SPBSET_CH_SLOTS];
+ unsigned int offset;
+};
+
+struct uint_spbset {
+ struct uint_spbset_chunk head;
+#ifdef DEBUGBUILD
+ int init;
+#endif
+};
+
+void Curl_uint_spbset_init(struct uint_spbset *bset);
+
+void Curl_uint_spbset_destroy(struct uint_spbset *bset);
+
+/* Get the cardinality of the bitset, e.g. numbers present in the set. */
+unsigned int Curl_uint_spbset_count(struct uint_spbset *bset);
+
+/* TRUE of bitset is empty */
+bool Curl_uint_spbset_empty(struct uint_spbset *bset);
+
+/* Clear the bitset, making it empty. */
+void Curl_uint_spbset_clear(struct uint_spbset *bset);
+
+/* Add the number `i` to the bitset.
+ * Numbers can be added more than once, without making a difference.
+ * Returns FALSE if allocations failed. */
+bool Curl_uint_spbset_add(struct uint_spbset *bset, unsigned int i);
+
+/* Remove the number `i` from the bitset. */
+void Curl_uint_spbset_remove(struct uint_spbset *bset, unsigned int i);
+
+/* Return TRUE if the bitset contains number `i`. */
+bool Curl_uint_spbset_contains(struct uint_spbset *bset, unsigned int i);
+
+/* Get the first number in the bitset, e.g. the smallest.
+ * Returns FALSE when the bitset is empty. */
+bool Curl_uint_spbset_first(struct uint_spbset *bset, unsigned int *pfirst);
+
+/* Get the next number in the bitset, following `last` in natural order.
+ * Put another way, this is the smallest number greater than `last` in
+ * the bitset. `last` does not have to be present in the set.
+ *
+ * Returns FALSE when no such number is in the set.
+ *
+ * This allows to iterate the set while being modified:
+ * - added numbers higher than 'last' will be picked up by the iteration.
+ * - added numbers lower than 'last' will not show up.
+ * - removed numbers lower or equal to 'last' will not show up.
+ * - removed numbers higher than 'last' will not be visited. */
+bool Curl_uint_spbset_next(struct uint_spbset *bset, unsigned int last,
+ unsigned int *pnext);
+
+#endif /* HEADER_CURL_UINT_SPBSET_H */
diff --git a/libs/libcurl/src/uint-table.c b/libs/libcurl/src/uint-table.c
new file mode 100644
index 0000000000..c40d538348
--- /dev/null
+++ b/libs/libcurl/src/uint-table.c
@@ -0,0 +1,214 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "curl_setup.h"
+#include "uint-table.h"
+
+/* The last 3 #include files should be in this order */
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "memdebug.h"
+
+#ifdef DEBUGBUILD
+#define CURL_UINT_TBL_MAGIC 0x62757473
+#endif
+
+void Curl_uint_tbl_init(struct uint_tbl *tbl,
+ Curl_uint_tbl_entry_dtor *entry_dtor)
+{
+ memset(tbl, 0, sizeof(*tbl));
+ tbl->entry_dtor = entry_dtor;
+ tbl->last_key_added = UINT_MAX;
+#ifdef DEBUGBUILD
+ tbl->init = CURL_UINT_TBL_MAGIC;
+#endif
+}
+
+
+static void uint_tbl_clear_rows(struct uint_tbl *tbl,
+ unsigned int from,
+ unsigned int upto_excluding)
+{
+ unsigned int i, end;
+
+ end = CURLMIN(upto_excluding, tbl->nrows);
+ for(i = from; i < end; ++i) {
+ if(tbl->rows[i]) {
+ if(tbl->entry_dtor)
+ tbl->entry_dtor(i, tbl->rows[i]);
+ tbl->rows[i] = NULL;
+ tbl->nentries--;
+ }
+ }
+}
+
+
+CURLcode Curl_uint_tbl_resize(struct uint_tbl *tbl, unsigned int nrows)
+{
+ /* we use `tbl->nrows + 1` during iteration, want that to work */
+ DEBUGASSERT(tbl->init == CURL_UINT_TBL_MAGIC);
+ if(!nrows || (nrows == UINT_MAX))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ if(nrows != tbl->nrows) {
+ void **rows = calloc(nrows, sizeof(void *));
+ if(!rows)
+ return CURLE_OUT_OF_MEMORY;
+ if(tbl->rows) {
+ memcpy(rows, tbl->rows, (CURLMIN(nrows, tbl->nrows) * sizeof(void *)));
+ if(nrows < tbl->nrows)
+ uint_tbl_clear_rows(tbl, nrows, tbl->nrows);
+ free(tbl->rows);
+ }
+ tbl->rows = rows;
+ tbl->nrows = nrows;
+ }
+ return CURLE_OK;
+}
+
+
+void Curl_uint_tbl_destroy(struct uint_tbl *tbl)
+{
+ DEBUGASSERT(tbl->init == CURL_UINT_TBL_MAGIC);
+ Curl_uint_tbl_clear(tbl);
+ free(tbl->rows);
+ memset(tbl, 0, sizeof(*tbl));
+}
+
+
+void Curl_uint_tbl_clear(struct uint_tbl *tbl)
+{
+ DEBUGASSERT(tbl->init == CURL_UINT_TBL_MAGIC);
+ uint_tbl_clear_rows(tbl, 0, tbl->nrows);
+ DEBUGASSERT(!tbl->nentries);
+ tbl->last_key_added = UINT_MAX;
+}
+
+
+unsigned int Curl_uint_tbl_capacity(struct uint_tbl *tbl)
+{
+ return tbl->nrows;
+}
+
+
+unsigned int Curl_uint_tbl_count(struct uint_tbl *tbl)
+{
+ return tbl->nentries;
+}
+
+
+void *Curl_uint_tbl_get(struct uint_tbl *tbl, unsigned int key)
+{
+ return (key < tbl->nrows) ? tbl->rows[key] : NULL;
+}
+
+
+bool Curl_uint_tbl_add(struct uint_tbl *tbl, void *entry, unsigned int *pkey)
+{
+ unsigned int key, start_pos;
+
+ DEBUGASSERT(tbl->init == CURL_UINT_TBL_MAGIC);
+ if(!entry || !pkey)
+ return FALSE;
+ *pkey = UINT_MAX; /* always invalid */
+ if(tbl->nentries == tbl->nrows) /* full */
+ return FALSE;
+
+ start_pos = CURLMIN(tbl->last_key_added, tbl->nrows) + 1;
+ for(key = start_pos; key < tbl->nrows; ++key) {
+ if(!tbl->rows[key]) {
+ tbl->rows[key] = entry;
+ tbl->nentries++;
+ tbl->last_key_added = key;
+ *pkey = key;
+ return TRUE;
+ }
+ }
+ /* no free entry at or above tbl->maybe_next_key, wrap around */
+ for(key = 0; key < start_pos; ++key) {
+ if(!tbl->rows[key]) {
+ tbl->rows[key] = entry;
+ tbl->nentries++;
+ tbl->last_key_added = key;
+ *pkey = key;
+ return TRUE;
+ }
+ }
+ /* Did not find any free row? Should not happen */
+ DEBUGASSERT(0);
+ return FALSE;
+}
+
+
+void Curl_uint_tbl_remove(struct uint_tbl *tbl, unsigned int key)
+{
+ uint_tbl_clear_rows(tbl, key, key + 1);
+}
+
+
+bool Curl_uint_tbl_contains(struct uint_tbl *tbl, unsigned int key)
+{
+ return (key < tbl->nrows) ? !!tbl->rows[key] : FALSE;
+}
+
+
+static bool uint_tbl_next_at(struct uint_tbl *tbl, unsigned int key,
+ unsigned int *pkey, void **pentry)
+{
+ for(; key < tbl->nrows; ++key) {
+ if(tbl->rows[key]) {
+ *pkey = key;
+ *pentry = tbl->rows[key];
+ return TRUE;
+ }
+ }
+ *pkey = UINT_MAX; /* always invalid */
+ *pentry = NULL;
+ return FALSE;
+}
+
+bool Curl_uint_tbl_first(struct uint_tbl *tbl,
+ unsigned int *pkey, void **pentry)
+{
+ if(!pkey || !pentry)
+ return FALSE;
+ if(tbl->nentries && uint_tbl_next_at(tbl, 0, pkey, pentry))
+ return TRUE;
+ DEBUGASSERT(!tbl->nentries);
+ *pkey = UINT_MAX; /* always invalid */
+ *pentry = NULL;
+ return FALSE;
+}
+
+
+bool Curl_uint_tbl_next(struct uint_tbl *tbl, unsigned int last_key,
+ unsigned int *pkey, void **pentry)
+{
+ if(!pkey || !pentry)
+ return FALSE;
+ if(uint_tbl_next_at(tbl, last_key + 1, pkey, pentry))
+ return TRUE;
+ *pkey = UINT_MAX; /* always invalid */
+ *pentry = NULL;
+ return FALSE;
+}
diff --git a/libs/libcurl/src/uint-table.h b/libs/libcurl/src/uint-table.h
new file mode 100644
index 0000000000..ab610d9890
--- /dev/null
+++ b/libs/libcurl/src/uint-table.h
@@ -0,0 +1,101 @@
+#ifndef HEADER_CURL_UINT_TABLE_H
+#define HEADER_CURL_UINT_TABLE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "curl_setup.h"
+
+#include <curl/curl.h>
+
+/* Destructor for a single table entry */
+typedef void Curl_uint_tbl_entry_dtor(unsigned int key, void *entry);
+
+struct uint_tbl {
+ void **rows; /* array of void* holding entries */
+ Curl_uint_tbl_entry_dtor *entry_dtor;
+ unsigned int nrows; /* length of `rows` array */
+ unsigned int nentries; /* entries in table */
+ unsigned int last_key_added; /* UINT_MAX or last key added */
+#ifdef DEBUGBUILD
+ int init;
+#endif
+};
+
+/* Initialize the table with 0 capacity.
+ * The optional `entry_dtor` is called when a table entry is removed,
+ * Passing NULL means no action is taken on removal. */
+void Curl_uint_tbl_init(struct uint_tbl *tbl,
+ Curl_uint_tbl_entry_dtor *entry_dtor);
+
+/* Resize the table to change capacity `nmax`. When `nmax` is reduced,
+ * all present entries with key equal or larger to `nmax` are removed. */
+CURLcode Curl_uint_tbl_resize(struct uint_tbl *tbl, unsigned int nmax);
+
+/* Destroy the table, freeing all entries. */
+void Curl_uint_tbl_destroy(struct uint_tbl *tbl);
+
+/* Get the table capacity. */
+unsigned int Curl_uint_tbl_capacity(struct uint_tbl *tbl);
+
+/* Get the number of entries in the table. */
+unsigned int Curl_uint_tbl_count(struct uint_tbl *tbl);
+
+/* Clear the table, making it empty. */
+void Curl_uint_tbl_clear(struct uint_tbl *tbl);
+
+/* Get the entry for key or NULL if not present */
+void *Curl_uint_tbl_get(struct uint_tbl *tbl, unsigned int key);
+
+/* Add a new entry to the table and assign it a free key.
+ * Returns FALSE if the table is full.
+ *
+ * Keys are assigned in a round-robin manner.
+ * No matter the capacity, UINT_MAX is never assigned. */
+bool Curl_uint_tbl_add(struct uint_tbl *tbl, void *entry, unsigned int *pkey);
+
+/* Remove the entry with `key`. */
+void Curl_uint_tbl_remove(struct uint_tbl *tbl, unsigned int key);
+
+/* Return TRUE if the table contains an tryn with that keys. */
+bool Curl_uint_tbl_contains(struct uint_tbl *tbl, unsigned int key);
+
+/* Get the first entry in the table (with the smallest `key`).
+ * Returns FALSE if the table is empty. */
+bool Curl_uint_tbl_first(struct uint_tbl *tbl,
+ unsigned int *pkey, void **pentry);
+
+/* Get the next key in the table, following `last_key` in natural order.
+ * Put another way, this is the smallest key greater than `last_key` in
+ * the table. `last_key` does not have to be present in the table.
+ *
+ * Returns FALSE when no such entry is in the table.
+ *
+ * This allows to iterate the table while being modified:
+ * - added keys higher than 'last_key' will be picked up by the iteration.
+ * - added keys lower than 'last_key' will not show up.
+ * - removed keys lower or equal to 'last_key' will not show up.
+ * - removed keys higher than 'last_key' will not be visited. */
+bool Curl_uint_tbl_next(struct uint_tbl *tbl, unsigned int last_key,
+ unsigned int *pkey, void **pentry);
+
+#endif /* HEADER_CURL_UINT_TABLE_H */
diff --git a/libs/libcurl/src/url.c b/libs/libcurl/src/url.c
index 28c37019ea..8329acef58 100644
--- a/libs/libcurl/src/url.c
+++ b/libs/libcurl/src/url.c
@@ -75,7 +75,6 @@
#include "strcase.h"
#include "strerror.h"
#include "escape.h"
-#include "strtok.h"
#include "share.h"
#include "content_encoding.h"
#include "http_digest.h"
@@ -84,13 +83,15 @@
#include "multiif.h"
#include "easyif.h"
#include "speedcheck.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
#include "getinfo.h"
+#include "pop3.h"
#include "urlapi-int.h"
#include "system_win32.h"
#include "hsts.h"
#include "noproxy.h"
#include "cfilters.h"
+#include "curl_krb5.h"
#include "idn.h"
/* And now for the protocols */
@@ -117,18 +118,14 @@
#include "strdup.h"
#include "setopt.h"
#include "altsvc.h"
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
#include "headers.h"
-
+#include "curlx/strparse.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
#include "memdebug.h"
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
-#endif
-
#ifdef USE_NGHTTP2
static void data_priority_cleanup(struct Curl_easy *data);
#else
@@ -275,7 +272,7 @@ CURLcode Curl_close(struct Curl_easy **datap)
data->state.referer = NULL;
up_free(data);
- Curl_dyn_free(&data->state.headerb);
+ curlx_dyn_free(&data->state.headerb);
Curl_flush_cookies(data, TRUE);
#ifndef CURL_DISABLE_ALTSVC
Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]);
@@ -290,12 +287,14 @@ CURLcode Curl_close(struct Curl_easy **datap)
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH)
Curl_http_auth_cleanup_digest(data);
#endif
+ Curl_safefree(data->state.most_recent_ftp_entrypath);
Curl_safefree(data->info.contenttype);
Curl_safefree(data->info.wouldredirect);
- /* this destroys the channel and we cannot use it anymore after this */
- Curl_resolver_cancel(data);
- Curl_resolver_cleanup(data->state.async.resolver);
+ /* release any resolve information this transfer kept */
+ Curl_async_destroy(data);
+ Curl_resolv_unlink(data, &data->state.dns[0]); /* done with this */
+ Curl_resolv_unlink(data, &data->state.dns[1]);
data_priority_cleanup(data);
@@ -306,6 +305,7 @@ CURLcode Curl_close(struct Curl_easy **datap)
Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
}
+ Curl_hash_destroy(&data->meta_hash);
#ifndef CURL_DISABLE_PROXY
Curl_safefree(data->state.aptr.proxyuserpwd);
#endif
@@ -382,8 +382,6 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
#endif
set->dns_cache_timeout = 60; /* Timeout every 60 seconds by default */
- /* Set the default size of the SSL session ID cache */
- set->general_ssl.max_ssl_sessions = 5;
/* Timeout every 24 hours by default */
set->general_ssl.ca_cache_timeout = 24 * 60 * 60;
@@ -397,9 +395,6 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
set->socks5auth = CURLAUTH_BASIC | CURLAUTH_GSSAPI;
#endif
- /* make libcurl quiet by default: */
- set->hide_progress = TRUE; /* CURLOPT_NOPROGRESS changes these */
-
Curl_mime_initpart(&set->mimepost);
Curl_ssl_easy_config_init(data);
@@ -434,7 +429,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
*/
if(Curl_ssl_backend() != CURLSSLBACKEND_SCHANNEL &&
Curl_ssl_backend() != CURLSSLBACKEND_SECURETRANSPORT) {
-#if defined(CURL_CA_BUNDLE)
+#ifdef CURL_CA_BUNDLE
result = Curl_setstropt(&set->str[STRING_SSL_CAFILE], CURL_CA_BUNDLE);
if(result)
return result;
@@ -445,7 +440,7 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
return result;
#endif
#endif
-#if defined(CURL_CA_PATH)
+#ifdef CURL_CA_PATH
result = Curl_setstropt(&set->str[STRING_SSL_CAPATH], CURL_CA_PATH);
if(result)
return result;
@@ -480,19 +475,31 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
set->maxage_conn = 118;
set->maxlifetime_conn = 0;
set->http09_allowed = FALSE;
-#ifdef USE_HTTP2
- set->httpwant = CURL_HTTP_VERSION_2TLS
-#else
- set->httpwant = CURL_HTTP_VERSION_1_1
-#endif
+ set->httpwant = CURL_HTTP_VERSION_NONE
;
#if defined(USE_HTTP2) || defined(USE_HTTP3)
memset(&set->priority, 0, sizeof(set->priority));
#endif
set->quick_exit = 0L;
+#ifndef CURL_DISABLE_WEBSOCKETS
+ set->ws_raw_mode = FALSE;
+ set->ws_no_auto_pong = FALSE;
+#endif
+
return result;
}
+/* easy->meta_hash destructor. Should never be called as elements
+ * MUST be added with their own destructor */
+static void easy_meta_freeentry(void *p)
+{
+ (void)p;
+ /* Will always be FALSE. Cannot use a 0 assert here since compilers
+ * are not in agreement if they then want a NORETURN attribute or
+ * not. *sigh* */
+ DEBUGASSERT(p == NULL);
+}
+
/**
* Curl_open()
*
@@ -515,45 +522,34 @@ CURLcode Curl_open(struct Curl_easy **curl)
}
data->magic = CURLEASY_MAGIC_NUMBER;
-
+ /* most recent connection is not yet defined */
+ data->state.lastconnect_id = -1;
+ data->state.recent_conn_id = -1;
+ /* and not assigned an id yet */
+ data->id = -1;
+ data->mid = UINT_MAX;
+ data->master_mid = UINT_MAX;
+ data->progress.hide = TRUE;
+ data->state.current_speed = -1; /* init to negative == impossible */
+
+ Curl_hash_init(&data->meta_hash, 23,
+ Curl_hash_str, curlx_str_key_compare, easy_meta_freeentry);
+ curlx_dyn_init(&data->state.headerb, CURL_MAX_HTTP_HEADER);
Curl_req_init(&data->req);
-
- result = Curl_resolver_init(data, &data->state.async.resolver);
- if(result) {
- DEBUGF(fprintf(stderr, "Error: resolver_init failed\n"));
- Curl_req_free(&data->req, data);
- free(data);
- return result;
- }
-
- result = Curl_init_userdefined(data);
- if(!result) {
- Curl_dyn_init(&data->state.headerb, CURL_MAX_HTTP_HEADER);
- Curl_initinfo(data);
-
- /* most recent connection is not yet defined */
- data->state.lastconnect_id = -1;
- data->state.recent_conn_id = -1;
- /* and not assigned an id yet */
- data->id = -1;
- data->mid = -1;
-#ifndef CURL_DISABLE_DOH
- data->set.dohfor_mid = -1;
-#endif
-
- data->progress.flags |= PGRS_HIDE;
- data->state.current_speed = -1; /* init to negative == impossible */
+ Curl_initinfo(data);
#ifndef CURL_DISABLE_HTTP
- Curl_llist_init(&data->state.httphdrs, NULL);
+ Curl_llist_init(&data->state.httphdrs, NULL);
#endif
- Curl_netrc_init(&data->state.netrc);
- }
+ Curl_netrc_init(&data->state.netrc);
+
+ result = Curl_init_userdefined(data);
if(result) {
- Curl_resolver_cleanup(data->state.async.resolver);
- Curl_dyn_free(&data->state.headerb);
+ curlx_dyn_free(&data->state.headerb);
Curl_freeset(data);
Curl_req_free(&data->req, data);
+ Curl_hash_destroy(&data->meta_hash);
+ data->magic = 0;
free(data);
data = NULL;
}
@@ -568,7 +564,11 @@ void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn)
DEBUGASSERT(conn);
- for(i = 0; i < ARRAYSIZE(conn->cfilter); ++i) {
+ if(conn->handler && conn->handler->disconnect &&
+ !conn->bits.shutdown_handler)
+ conn->handler->disconnect(data, conn, TRUE);
+
+ for(i = 0; i < CURL_ARRAYSIZE(conn->cfilter); ++i) {
Curl_conn_cf_discard_all(data, conn, (int)i);
}
@@ -584,6 +584,7 @@ void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn)
Curl_safefree(conn->http_proxy.host.rawalloc); /* http proxy name buffer */
Curl_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name buffer */
#endif
+ Curl_sec_conn_destroy(conn);
Curl_safefree(conn->user);
Curl_safefree(conn->passwd);
Curl_safefree(conn->sasl_authzid);
@@ -600,57 +601,13 @@ void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn)
Curl_safefree(conn->unix_domain_socket);
#endif
Curl_safefree(conn->destination);
+ Curl_uint_spbset_destroy(&conn->xfers_attached);
+ Curl_hash_destroy(&conn->meta_hash);
free(conn); /* free all the connection oriented data */
}
/*
- * Disconnects the given connection. Note the connection may not be the
- * primary connection, like when freeing room in the connection pool or
- * killing of a dead old connection.
- *
- * A connection needs an easy handle when closing down. We support this passed
- * in separately since the connection to get closed here is often already
- * disassociated from an easy handle.
- *
- * This function MUST NOT reset state in the Curl_easy struct if that
- * is not strictly bound to the life-time of *this* particular connection.
- */
-bool Curl_on_disconnect(struct Curl_easy *data,
- struct connectdata *conn, bool aborted)
-{
- /* there must be a connection to close */
- DEBUGASSERT(conn);
-
- /* it must be removed from the connection pool */
- DEBUGASSERT(!conn->bits.in_cpool);
-
- /* there must be an associated transfer */
- DEBUGASSERT(data);
-
- /* the transfer must be detached from the connection */
- DEBUGASSERT(!data->conn);
-
- DEBUGF(infof(data, "Curl_disconnect(conn #%" FMT_OFF_T ", aborted=%d)",
- conn->connection_id, aborted));
-
- if(conn->dns_entry)
- Curl_resolv_unlink(data, &conn->dns_entry);
-
- /* Cleanup NTLM connection-related data */
- Curl_http_auth_cleanup_ntlm(conn);
-
- /* Cleanup NEGOTIATE connection-related data */
- Curl_http_auth_cleanup_negotiate(conn);
-
- if(conn->connect_only)
- /* treat the connection as aborted in CONNECT_ONLY situations */
- aborted = TRUE;
-
- return aborted;
-}
-
-/*
* xfer_may_multiplex()
*
* Return a TRUE, iff the transfer can be done over an (appropriate)
@@ -659,15 +616,20 @@ bool Curl_on_disconnect(struct Curl_easy *data,
static bool xfer_may_multiplex(const struct Curl_easy *data,
const struct connectdata *conn)
{
+#ifndef CURL_DISABLE_HTTP
/* If an HTTP protocol and multiplexing is enabled */
if((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
(!conn->bits.protoconnstart || !conn->bits.close)) {
if(Curl_multiplex_wanted(data->multi) &&
- (data->state.httpwant >= CURL_HTTP_VERSION_2))
+ (data->state.http_neg.allowed & (CURL_HTTP_V2x|CURL_HTTP_V3x)))
/* allows HTTP/2 or newer */
return TRUE;
}
+#else
+ (void)data;
+ (void)conn;
+#endif
return FALSE;
}
@@ -718,7 +680,7 @@ static bool conn_maxage(struct Curl_easy *data,
{
timediff_t idletime, lifetime;
- idletime = Curl_timediff(now, conn->lastused);
+ idletime = curlx_timediff(now, conn->lastused);
idletime /= 1000; /* integer seconds is fine */
if(idletime > data->set.maxage_conn) {
@@ -727,7 +689,7 @@ static bool conn_maxage(struct Curl_easy *data,
return TRUE;
}
- lifetime = Curl_timediff(now, conn->created);
+ lifetime = curlx_timediff(now, conn->created);
lifetime /= 1000; /* integer seconds is fine */
if(data->set.maxlifetime_conn && lifetime > data->set.maxlifetime_conn) {
@@ -755,7 +717,7 @@ bool Curl_conn_seems_dead(struct connectdata *conn,
bool dead;
struct curltime now;
if(!pnow) {
- now = Curl_now();
+ now = curlx_now();
pnow = &now;
}
@@ -813,7 +775,7 @@ CURLcode Curl_conn_upkeep(struct Curl_easy *data,
struct curltime *now)
{
CURLcode result = CURLE_OK;
- if(Curl_timediff(*now, conn->keepalive) <= data->set.upkeep_interval_ms)
+ if(curlx_timediff(*now, conn->keepalive) <= data->set.upkeep_interval_ms)
return result;
/* briefly attach for action */
@@ -839,11 +801,13 @@ CURLcode Curl_conn_upkeep(struct Curl_easy *data,
static bool ssh_config_matches(struct connectdata *one,
struct connectdata *two)
{
- return (Curl_safecmp(one->proto.sshc.rsa, two->proto.sshc.rsa) &&
- Curl_safecmp(one->proto.sshc.rsa_pub, two->proto.sshc.rsa_pub));
+ struct ssh_conn *sshc1, *sshc2;
+
+ sshc1 = Curl_conn_meta_get(one, CURL_META_SSH_CONN);
+ sshc2 = Curl_conn_meta_get(two, CURL_META_SSH_CONN);
+ return (sshc1 && sshc2 && Curl_safecmp(sshc1->rsa, sshc2->rsa) &&
+ Curl_safecmp(sshc1->rsa_pub, sshc2->rsa_pub));
}
-#else
-#define ssh_config_matches(x,y) FALSE
#endif
struct url_conn_match {
@@ -861,25 +825,19 @@ struct url_conn_match {
BIT(seen_multiplex_conn);
};
-static bool url_match_conn(struct connectdata *conn, void *userdata)
+static bool url_match_connect_config(struct connectdata *conn,
+ struct url_conn_match *m)
{
- struct url_conn_match *match = userdata;
- struct Curl_easy *data = match->data;
- struct connectdata *needle = match->needle;
-
- /* Check if `conn` can be used for transfer `data` */
-
+ /* connect-only or to-be-closed connections will not be reused */
if(conn->connect_only || conn->bits.close)
- /* connect-only or to-be-closed connections will not be reused */
return FALSE;
- if(data->set.ipver != CURL_IPRESOLVE_WHATEVER
- && data->set.ipver != conn->ip_version) {
- /* skip because the connection is not via the requested IP version */
+ /* ip_version must match */
+ if(m->data->set.ipver != CURL_IPRESOLVE_WHATEVER
+ && m->data->set.ipver != conn->ip_version)
return FALSE;
- }
- if(needle->localdev || needle->localport) {
+ if(m->needle->localdev || m->needle->localport) {
/* If we are bound to a specific local end (IP+port), we must not reuse a
random other one, although if we did not ask for a particular one we
can reuse one that was bound.
@@ -891,105 +849,155 @@ static bool url_match_conn(struct connectdata *conn, void *userdata)
likely also reuse the exact same binding parameters and missing out a
few edge cases should not hurt anyone much.
*/
- if((conn->localport != needle->localport) ||
- (conn->localportrange != needle->localportrange) ||
- (needle->localdev &&
- (!conn->localdev || strcmp(conn->localdev, needle->localdev))))
+ if((conn->localport != m->needle->localport) ||
+ (conn->localportrange != m->needle->localportrange) ||
+ (m->needle->localdev &&
+ (!conn->localdev || strcmp(conn->localdev, m->needle->localdev))))
return FALSE;
}
- if(needle->bits.conn_to_host != conn->bits.conn_to_host)
+ if(m->needle->bits.conn_to_host != conn->bits.conn_to_host)
/* do not mix connections that use the "connect to host" feature and
* connections that do not use this feature */
return FALSE;
- if(needle->bits.conn_to_port != conn->bits.conn_to_port)
+ if(m->needle->bits.conn_to_port != conn->bits.conn_to_port)
/* do not mix connections that use the "connect to port" feature and
* connections that do not use this feature */
return FALSE;
+ /* Does `conn` use the correct protocol? */
+#ifdef USE_UNIX_SOCKETS
+ if(m->needle->unix_domain_socket) {
+ if(!conn->unix_domain_socket)
+ return FALSE;
+ if(strcmp(m->needle->unix_domain_socket, conn->unix_domain_socket))
+ return FALSE;
+ if(m->needle->bits.abstract_unix_socket != conn->bits.abstract_unix_socket)
+ return FALSE;
+ }
+ else if(conn->unix_domain_socket)
+ return FALSE;
+#endif
+
+ return TRUE;
+}
+
+static bool url_match_fully_connected(struct connectdata *conn,
+ struct url_conn_match *m)
+{
if(!Curl_conn_is_connected(conn, FIRSTSOCKET) ||
conn->bits.asks_multiplex) {
/* Not yet connected, or not yet decided if it multiplexes. The later
* happens for HTTP/2 Upgrade: requests that need a response. */
- if(match->may_multiplex) {
- match->seen_pending_conn = TRUE;
+ if(m->may_multiplex) {
+ m->seen_pending_conn = TRUE;
/* Do not pick a connection that has not connected yet */
- infof(data, "Connection #%" FMT_OFF_T
+ infof(m->data, "Connection #%" FMT_OFF_T
" is not open enough, cannot reuse", conn->connection_id);
}
/* Do not pick a connection that has not connected yet */
return FALSE;
}
- /* `conn` is connected. If it has transfers, can we add ours to it? */
+ return TRUE;
+}
+
+static bool url_match_multi(struct connectdata *conn,
+ struct url_conn_match *m)
+{
+ if(CONN_INUSE(conn)) {
+ DEBUGASSERT(conn->attached_multi);
+ if(conn->attached_multi != m->data->multi)
+ return FALSE;
+ }
+ return TRUE;
+}
+static bool url_match_multiplex_needs(struct connectdata *conn,
+ struct url_conn_match *m)
+{
if(CONN_INUSE(conn)) {
if(!conn->bits.multiplex) {
/* conn busy and conn cannot take more transfers */
- match->seen_single_use_conn = TRUE;
+ m->seen_single_use_conn = TRUE;
return FALSE;
}
- match->seen_multiplex_conn = TRUE;
- if(!match->may_multiplex)
+ m->seen_multiplex_conn = TRUE;
+ if(!m->may_multiplex || !url_match_multi(conn, m))
/* conn busy and transfer cannot be multiplexed */
return FALSE;
- else {
- /* transfer and conn multiplex. Are they on the same multi? */
- struct Curl_llist_node *e = Curl_llist_head(&conn->easyq);
- struct Curl_easy *entry = Curl_node_elem(e);
- if(entry->multi != data->multi)
- return FALSE;
- }
}
- /* `conn` is connected and we could add the transfer to it, if
- * all the other criteria do match. */
+ return TRUE;
+}
- /* Does `conn` use the correct protocol? */
-#ifdef USE_UNIX_SOCKETS
- if(needle->unix_domain_socket) {
- if(!conn->unix_domain_socket)
- return FALSE;
- if(strcmp(needle->unix_domain_socket, conn->unix_domain_socket))
+static bool url_match_multiplex_limits(struct connectdata *conn,
+ struct url_conn_match *m)
+{
+ if(CONN_INUSE(conn) && m->may_multiplex) {
+ DEBUGASSERT(conn->bits.multiplex);
+ /* If multiplexed, make sure we do not go over concurrency limit */
+ if(CONN_ATTACHED(conn) >=
+ Curl_multi_max_concurrent_streams(m->data->multi)) {
+ infof(m->data, "client side MAX_CONCURRENT_STREAMS reached"
+ ", skip (%u)", CONN_ATTACHED(conn));
return FALSE;
- if(needle->bits.abstract_unix_socket != conn->bits.abstract_unix_socket)
+ }
+ if(CONN_ATTACHED(conn) >=
+ Curl_conn_get_max_concurrent(m->data, conn, FIRSTSOCKET)) {
+ infof(m->data, "MAX_CONCURRENT_STREAMS reached, skip (%u)",
+ CONN_ATTACHED(conn));
return FALSE;
+ }
+ /* When not multiplexed, we have a match here! */
+ infof(m->data, "Multiplexed connection found");
}
- else if(conn->unix_domain_socket)
- return FALSE;
-#endif
+ return TRUE;
+}
- if((needle->handler->flags&PROTOPT_SSL) !=
- (conn->handler->flags&PROTOPT_SSL))
- /* do not do mixed SSL and non-SSL connections */
- if(get_protocol_family(conn->handler) !=
- needle->handler->protocol || !conn->bits.tls_upgraded)
- /* except protocols that have been upgraded via TLS */
+static bool url_match_ssl_use(struct connectdata *conn,
+ struct url_conn_match *m)
+{
+ if(m->needle->handler->flags&PROTOPT_SSL) {
+ /* We are looking for SSL, if `conn` does not do it, not a match. */
+ if(!Curl_conn_is_ssl(conn, FIRSTSOCKET))
return FALSE;
+ }
+ else if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
+ /* We are not *requiring* SSL, however `conn` has it. If the
+ * protocol *family* is not the same, not a match. */
+ if(get_protocol_family(conn->handler) != m->needle->handler->protocol)
+ return FALSE;
+ }
+ return TRUE;
+}
#ifndef CURL_DISABLE_PROXY
- if(needle->bits.httpproxy != conn->bits.httpproxy ||
- needle->bits.socksproxy != conn->bits.socksproxy)
+static bool url_match_proxy_use(struct connectdata *conn,
+ struct url_conn_match *m)
+{
+ if(m->needle->bits.httpproxy != conn->bits.httpproxy ||
+ m->needle->bits.socksproxy != conn->bits.socksproxy)
return FALSE;
- if(needle->bits.socksproxy &&
- !socks_proxy_info_matches(&needle->socks_proxy,
+ if(m->needle->bits.socksproxy &&
+ !socks_proxy_info_matches(&m->needle->socks_proxy,
&conn->socks_proxy))
return FALSE;
- if(needle->bits.httpproxy) {
- if(needle->bits.tunnel_proxy != conn->bits.tunnel_proxy)
+ if(m->needle->bits.httpproxy) {
+ if(m->needle->bits.tunnel_proxy != conn->bits.tunnel_proxy)
return FALSE;
- if(!proxy_info_matches(&needle->http_proxy, &conn->http_proxy))
+ if(!proxy_info_matches(&m->needle->http_proxy, &conn->http_proxy))
return FALSE;
- if(IS_HTTPS_PROXY(needle->http_proxy.proxytype)) {
+ if(IS_HTTPS_PROXY(m->needle->http_proxy.proxytype)) {
/* https proxies come in different types, http/1.1, h2, ... */
- if(needle->http_proxy.proxytype != conn->http_proxy.proxytype)
+ if(m->needle->http_proxy.proxytype != conn->http_proxy.proxytype)
return FALSE;
/* match SSL config to proxy */
- if(!Curl_ssl_conn_config_match(data, conn, TRUE)) {
- DEBUGF(infof(data,
+ if(!Curl_ssl_conn_config_match(m->data, conn, TRUE)) {
+ DEBUGF(infof(m->data,
"Connection #%" FMT_OFF_T
" has different SSL proxy parameters, cannot reuse",
conn->connection_id));
@@ -999,129 +1007,187 @@ static bool url_match_conn(struct connectdata *conn, void *userdata)
* further below */
}
}
+ return TRUE;
+}
+#else
+#define url_match_proxy_use(c,m) ((void)c, (void)m, TRUE)
#endif
- if(match->may_multiplex &&
- (data->state.httpwant == CURL_HTTP_VERSION_2_0) &&
- (needle->handler->protocol & CURLPROTO_HTTP) &&
- !conn->httpversion) {
- if(data->set.pipewait) {
- infof(data, "Server upgrade does not support multiplex yet, wait");
- match->found = NULL;
- match->wait_pipe = TRUE;
+#ifndef CURL_DISABLE_HTTP
+static bool url_match_http_multiplex(struct connectdata *conn,
+ struct url_conn_match *m)
+{
+ if(m->may_multiplex &&
+ (m->data->state.http_neg.allowed & (CURL_HTTP_V2x|CURL_HTTP_V3x)) &&
+ (m->needle->handler->protocol & CURLPROTO_HTTP) &&
+ !conn->httpversion_seen) {
+ if(m->data->set.pipewait) {
+ infof(m->data, "Server upgrade does not support multiplex yet, wait");
+ m->found = NULL;
+ m->wait_pipe = TRUE;
return TRUE; /* stop searching, we want to wait */
}
- infof(data, "Server upgrade cannot be used");
+ infof(m->data, "Server upgrade cannot be used");
return FALSE;
}
+ return TRUE;
+}
- if(!(needle->handler->flags & PROTOPT_CREDSPERREQUEST)) {
- /* This protocol requires credentials per connection,
- so verify that we are using the same name and password as well */
- if(Curl_timestrcmp(needle->user, conn->user) ||
- Curl_timestrcmp(needle->passwd, conn->passwd) ||
- Curl_timestrcmp(needle->sasl_authzid, conn->sasl_authzid) ||
- Curl_timestrcmp(needle->oauth_bearer, conn->oauth_bearer)) {
- /* one of them was different */
- return FALSE;
+static bool url_match_http_version(struct connectdata *conn,
+ struct url_conn_match *m)
+{
+ /* If looking for HTTP and the HTTP versions allowed do not include
+ * the HTTP version of conn, continue looking. */
+ if((m->needle->handler->protocol & PROTO_FAMILY_HTTP)) {
+ switch(Curl_conn_http_version(m->data, conn)) {
+ case 30:
+ if(!(m->data->state.http_neg.allowed & CURL_HTTP_V3x)) {
+ DEBUGF(infof(m->data, "not reusing conn #%" CURL_FORMAT_CURL_OFF_T
+ ", we do not want h3", conn->connection_id));
+ return FALSE;
+ }
+ break;
+ case 20:
+ if(!(m->data->state.http_neg.allowed & CURL_HTTP_V2x)) {
+ DEBUGF(infof(m->data, "not reusing conn #%" CURL_FORMAT_CURL_OFF_T
+ ", we do not want h2", conn->connection_id));
+ return FALSE;
+ }
+ break;
+ default:
+ if(!(m->data->state.http_neg.allowed & CURL_HTTP_V1x)) {
+ DEBUGF(infof(m->data, "not reusing conn #%" CURL_FORMAT_CURL_OFF_T
+ ", we do not want h1", conn->connection_id));
+ return FALSE;
+ }
+ break;
}
}
+ return TRUE;
+}
+#else
+#define url_match_http_multiplex(c,m) ((void)c, (void)m, TRUE)
+#define url_match_http_version(c,m) ((void)c, (void)m, TRUE)
+#endif
- /* GSS delegation differences do not actually affect every connection
- and auth method, but this check takes precaution before efficiency */
- if(needle->gssapi_delegation != conn->gssapi_delegation)
+static bool url_match_proto_config(struct connectdata *conn,
+ struct url_conn_match *m)
+{
+ if(!url_match_http_version(conn, m))
return FALSE;
- /* If looking for HTTP and the HTTP version we want is less
- * than the HTTP version of conn, continue looking.
- * CURL_HTTP_VERSION_2TLS is default which indicates no preference,
- * so we take any existing connection. */
- if((needle->handler->protocol & PROTO_FAMILY_HTTP) &&
- (data->state.httpwant != CURL_HTTP_VERSION_2TLS)) {
- if((conn->httpversion >= 20) &&
- (data->state.httpwant < CURL_HTTP_VERSION_2_0)) {
- DEBUGF(infof(data, "nor reusing conn #%" CURL_FORMAT_CURL_OFF_T
- " with httpversion=%d, we want a version less than h2",
- conn->connection_id, conn->httpversion));
- }
- if((conn->httpversion >= 30) &&
- (data->state.httpwant < CURL_HTTP_VERSION_3)) {
- DEBUGF(infof(data, "nor reusing conn #%" CURL_FORMAT_CURL_OFF_T
- " with httpversion=%d, we want a version less than h3",
- conn->connection_id, conn->httpversion));
- return FALSE;
- }
- }
#ifdef USE_SSH
- else if(get_protocol_family(needle->handler) & PROTO_FAMILY_SSH) {
- if(!ssh_config_matches(needle, conn))
+ if(get_protocol_family(m->needle->handler) & PROTO_FAMILY_SSH) {
+ if(!ssh_config_matches(m->needle, conn))
return FALSE;
}
#endif
#ifndef CURL_DISABLE_FTP
- else if(get_protocol_family(needle->handler) & PROTO_FAMILY_FTP) {
- /* Also match ACCOUNT, ALTERNATIVE-TO-USER, USE_SSL and CCC options */
- if(Curl_timestrcmp(needle->proto.ftpc.account,
- conn->proto.ftpc.account) ||
- Curl_timestrcmp(needle->proto.ftpc.alternative_to_user,
- conn->proto.ftpc.alternative_to_user) ||
- (needle->proto.ftpc.use_ssl != conn->proto.ftpc.use_ssl) ||
- (needle->proto.ftpc.ccc != conn->proto.ftpc.ccc))
+ else if(get_protocol_family(m->needle->handler) & PROTO_FAMILY_FTP) {
+ if(!ftp_conns_match(m->needle, conn))
+ return FALSE;
+ }
+#endif
+ return TRUE;
+}
+
+static bool url_match_auth(struct connectdata *conn,
+ struct url_conn_match *m)
+{
+ if(!(m->needle->handler->flags & PROTOPT_CREDSPERREQUEST)) {
+ /* This protocol requires credentials per connection,
+ so verify that we are using the same name and password as well */
+ if(Curl_timestrcmp(m->needle->user, conn->user) ||
+ Curl_timestrcmp(m->needle->passwd, conn->passwd) ||
+ Curl_timestrcmp(m->needle->sasl_authzid, conn->sasl_authzid) ||
+ Curl_timestrcmp(m->needle->oauth_bearer, conn->oauth_bearer)) {
+ /* one of them was different */
return FALSE;
+ }
}
+#ifdef HAVE_GSSAPI
+ /* GSS delegation differences do not actually affect every connection
+ and auth method, but this check takes precaution before efficiency */
+ if(m->needle->gssapi_delegation != conn->gssapi_delegation)
+ return FALSE;
#endif
+ return TRUE;
+}
+
+static bool url_match_destination(struct connectdata *conn,
+ struct url_conn_match *m)
+{
/* Additional match requirements if talking TLS OR
* not talking to an HTTP proxy OR using a tunnel through a proxy */
- if((needle->handler->flags&PROTOPT_SSL)
+ if((m->needle->handler->flags&PROTOPT_SSL)
#ifndef CURL_DISABLE_PROXY
- || !needle->bits.httpproxy || needle->bits.tunnel_proxy
+ || !m->needle->bits.httpproxy || m->needle->bits.tunnel_proxy
#endif
) {
- /* Talking the same protocol scheme or a TLS upgraded protocol in the
- * same protocol family? */
- if(!strcasecompare(needle->handler->scheme, conn->handler->scheme) &&
- (get_protocol_family(conn->handler) !=
- needle->handler->protocol || !conn->bits.tls_upgraded))
- return FALSE;
+ if(!strcasecompare(m->needle->handler->scheme, conn->handler->scheme)) {
+ /* `needle` and `conn` do not have the same scheme... */
+ if(get_protocol_family(conn->handler) != m->needle->handler->protocol) {
+ /* and `conn`s protocol family is not the protocol `needle` wants.
+ * IMAPS would work for IMAP, but no vice versa. */
+ return FALSE;
+ }
+ /* We are in an IMAPS vs IMAP like case. We expect `conn` to have SSL */
+ if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
+ DEBUGF(infof(m->data,
+ "Connection #%" FMT_OFF_T " has compatible protocol family, "
+ "but no SSL, no match", conn->connection_id));
+ return FALSE;
+ }
+ }
/* If needle has "conn_to_*" set, conn must match this */
- if((needle->bits.conn_to_host && !strcasecompare(
- needle->conn_to_host.name, conn->conn_to_host.name)) ||
- (needle->bits.conn_to_port &&
- needle->conn_to_port != conn->conn_to_port))
+ if((m->needle->bits.conn_to_host && !strcasecompare(
+ m->needle->conn_to_host.name, conn->conn_to_host.name)) ||
+ (m->needle->bits.conn_to_port &&
+ m->needle->conn_to_port != conn->conn_to_port))
return FALSE;
/* hostname and port must match */
- if(!strcasecompare(needle->host.name, conn->host.name) ||
- needle->remote_port != conn->remote_port)
+ if(!strcasecompare(m->needle->host.name, conn->host.name) ||
+ m->needle->remote_port != conn->remote_port)
return FALSE;
+ }
+ return TRUE;
+}
- /* If talking TLS, conn needs to use the same SSL options. */
- if((needle->handler->flags & PROTOPT_SSL) &&
- !Curl_ssl_conn_config_match(data, conn, FALSE)) {
- DEBUGF(infof(data,
- "Connection #%" FMT_OFF_T
- " has different SSL parameters, cannot reuse",
- conn->connection_id));
- return FALSE;
- }
+static bool url_match_ssl_config(struct connectdata *conn,
+ struct url_conn_match *m)
+{
+ /* If talking TLS, conn needs to use the same SSL options. */
+ if((m->needle->handler->flags & PROTOPT_SSL) &&
+ !Curl_ssl_conn_config_match(m->data, conn, FALSE)) {
+ DEBUGF(infof(m->data,
+ "Connection #%" FMT_OFF_T
+ " has different SSL parameters, cannot reuse",
+ conn->connection_id));
+ return FALSE;
}
+ return TRUE;
+}
-#if defined(USE_NTLM)
+#ifdef USE_NTLM
+static bool url_match_auth_ntlm(struct connectdata *conn,
+ struct url_conn_match *m)
+{
/* If we are looking for an HTTP+NTLM connection, check if this is
already authenticating with the right credentials. If not, keep
looking so that we can reuse NTLM connections if
possible. (Especially we must not reuse the same connection if
partway through a handshake!) */
- if(match->want_ntlm_http) {
- if(Curl_timestrcmp(needle->user, conn->user) ||
- Curl_timestrcmp(needle->passwd, conn->passwd)) {
+ if(m->want_ntlm_http) {
+ if(Curl_timestrcmp(m->needle->user, conn->user) ||
+ Curl_timestrcmp(m->needle->passwd, conn->passwd)) {
/* we prefer a credential match, but this is at least a connection
that can be reused and "upgraded" to NTLM */
if(conn->http_ntlm_state == NTLMSTATE_NONE)
- match->found = conn;
+ m->found = conn;
return FALSE;
}
}
@@ -1132,15 +1198,15 @@ static bool url_match_conn(struct connectdata *conn, void *userdata)
#ifndef CURL_DISABLE_PROXY
/* Same for Proxy NTLM authentication */
- if(match->want_proxy_ntlm_http) {
+ if(m->want_proxy_ntlm_http) {
/* Both conn->http_proxy.user and conn->http_proxy.passwd can be
* NULL */
if(!conn->http_proxy.user || !conn->http_proxy.passwd)
return FALSE;
- if(Curl_timestrcmp(needle->http_proxy.user,
+ if(Curl_timestrcmp(m->needle->http_proxy.user,
conn->http_proxy.user) ||
- Curl_timestrcmp(needle->http_proxy.passwd,
+ Curl_timestrcmp(m->needle->http_proxy.passwd,
conn->http_proxy.passwd))
return FALSE;
}
@@ -1149,53 +1215,83 @@ static bool url_match_conn(struct connectdata *conn, void *userdata)
return FALSE;
}
#endif
- if(match->want_ntlm_http || match->want_proxy_ntlm_http) {
+ if(m->want_ntlm_http || m->want_proxy_ntlm_http) {
/* Credentials are already checked, we may use this connection.
* With NTLM being weird as it is, we MUST use a
* connection where it has already been fully negotiated.
* If it has not, we keep on looking for a better one. */
- match->found = conn;
+ m->found = conn;
- if((match->want_ntlm_http &&
+ if((m->want_ntlm_http &&
(conn->http_ntlm_state != NTLMSTATE_NONE)) ||
- (match->want_proxy_ntlm_http &&
+ (m->want_proxy_ntlm_http &&
(conn->proxy_ntlm_state != NTLMSTATE_NONE))) {
/* We must use this connection, no other */
- match->force_reuse = TRUE;
+ m->force_reuse = TRUE;
return TRUE;
}
/* Continue look up for a better connection */
return FALSE;
}
+ return TRUE;
+}
+#else
+#define url_match_auth_ntlm(c,m) ((void)c, (void)m, TRUE)
#endif
- if(CONN_INUSE(conn)) {
- DEBUGASSERT(match->may_multiplex);
- DEBUGASSERT(conn->bits.multiplex);
- /* If multiplexed, make sure we do not go over concurrency limit */
- if(CONN_INUSE(conn) >=
- Curl_multi_max_concurrent_streams(data->multi)) {
- infof(data, "client side MAX_CONCURRENT_STREAMS reached"
- ", skip (%zu)", CONN_INUSE(conn));
- return FALSE;
- }
- if(CONN_INUSE(conn) >=
- Curl_conn_get_max_concurrent(data, conn, FIRSTSOCKET)) {
- infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)",
- CONN_INUSE(conn));
- return FALSE;
- }
- /* When not multiplexed, we have a match here! */
- infof(data, "Multiplexed connection found");
- }
- else if(Curl_conn_seems_dead(conn, data, NULL)) {
- /* removed and disconnect. Do not treat as aborted. */
- Curl_cpool_disconnect(data, conn, FALSE);
+static bool url_match_conn(struct connectdata *conn, void *userdata)
+{
+ struct url_conn_match *m = userdata;
+ /* Check if `conn` can be used for transfer `m->data` */
+
+ /* general connect config setting match? */
+ if(!url_match_connect_config(conn, m))
+ return FALSE;
+
+ if(!url_match_destination(conn, m))
+ return FALSE;
+
+ if(!url_match_fully_connected(conn, m))
+ return FALSE;
+
+ if(!url_match_multiplex_needs(conn, m))
+ return FALSE;
+
+ if(!url_match_ssl_use(conn, m))
+ return FALSE;
+ if(!url_match_proxy_use(conn, m))
+ return FALSE;
+ if(!url_match_ssl_config(conn, m))
+ return FALSE;
+
+ if(!url_match_http_multiplex(conn, m))
+ return FALSE;
+ else if(m->wait_pipe)
+ /* we decided to wait on PIPELINING */
+ return TRUE;
+
+ if(!url_match_auth(conn, m))
+ return FALSE;
+
+ if(!url_match_proto_config(conn, m))
+ return FALSE;
+
+ if(!url_match_auth_ntlm(conn, m))
+ return FALSE;
+ else if(m->force_reuse)
+ return TRUE;
+
+ if(!url_match_multiplex_limits(conn, m))
+ return FALSE;
+
+ if(!CONN_INUSE(conn) && Curl_conn_seems_dead(conn, m->data, NULL)) {
+ /* remove and disconnect. */
+ Curl_conn_terminate(m->data, conn, FALSE);
return FALSE;
}
- /* We have found a connection. Let's stop searching. */
- match->found = conn;
+ /* conn matches our needs. */
+ m->found = conn;
return TRUE;
}
@@ -1263,7 +1359,7 @@ ConnectionExists(struct Curl_easy *data,
/* Find a connection in the pool that matches what "data + needle"
* requires. If a suitable candidate is found, it is attached to "data". */
- result = Curl_cpool_find(data, needle->destination, needle->destination_len,
+ result = Curl_cpool_find(data, needle->destination,
url_match_conn, url_match_result, &match);
/* wait_pipe is TRUE if we encounter a bundle that is undecided. There
@@ -1288,7 +1384,7 @@ void Curl_verboseconnect(struct Curl_easy *data,
infof(data, "Connected to %s (%s) port %u",
CURL_CONN_HOST_DISPNAME(conn), conn->primary.remote_ip,
conn->primary.remote_port);
-#if !defined(CURL_DISABLE_HTTP)
+#ifndef CURL_DISABLE_HTTP
if(conn->handler->protocol & PROTO_FAMILY_HTTP) {
switch(conn->alpn) {
case CURL_HTTP_VERSION_3:
@@ -1331,7 +1427,7 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
connclose(conn, "Default to force-close");
/* Store creation time to help future close decision making */
- conn->created = Curl_now();
+ conn->created = curlx_now();
/* Store current time to give a baseline to keepalive connection times. */
conn->keepalive = conn->created;
@@ -1367,8 +1463,8 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
conn->connect_only = data->set.connect_only;
conn->transport = TRNSPRT_TCP; /* most of them are TCP streams */
- /* Initialize the easy handle list */
- Curl_llist_init(&conn->easyq, NULL);
+ /* Initialize the attached xfers bitset */
+ Curl_uint_spbset_init(&conn->xfers_attached);
#ifdef HAVE_GSSAPI
conn->data_prot = PROT_CLEAR;
@@ -1390,8 +1486,9 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
conn->fclosesocket = data->set.fclosesocket;
conn->closesocket_client = data->set.closesocket_client;
conn->lastused = conn->created;
+#ifdef HAVE_GSSAPI
conn->gssapi_delegation = data->set.gssapi_delegation;
-
+#endif
return conn;
error:
@@ -1442,7 +1539,7 @@ const struct Curl_handler *Curl_getn_scheme_handler(const char *scheme,
#else
NULL,
#endif
-#if defined(USE_SSH)
+#ifdef USE_SSH
&Curl_handler_sftp,
#else
NULL,
@@ -1684,12 +1781,12 @@ static void zonefrom_url(CURLU *uh, struct Curl_easy *data,
#endif
if(!uc && zoneid) {
- char *endp;
- unsigned long scope = strtoul(zoneid, &endp, 10);
- if(!*endp && (scope < UINT_MAX))
+ const char *p = zoneid;
+ curl_off_t scope;
+ if(!curlx_str_number(&p, &scope, UINT_MAX))
/* A plain number, use it directly as a scope id. */
conn->scope_id = (unsigned int)scope;
-#if defined(HAVE_IF_NAMETOINDEX)
+#ifdef HAVE_IF_NAMETOINDEX
else {
#elif defined(_WIN32)
else if(Curl_if_nametoindex) {
@@ -1698,7 +1795,7 @@ static void zonefrom_url(CURLU *uh, struct Curl_easy *data,
#if defined(HAVE_IF_NAMETOINDEX) || defined(_WIN32)
/* Zone identifier is not numeric */
unsigned int scopeidx = 0;
-#if defined(_WIN32)
+#ifdef _WIN32
scopeidx = Curl_if_nametoindex(zoneid);
#else
scopeidx = if_nametoindex(zoneid);
@@ -1827,7 +1924,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
/* HSTS upgrade */
if(data->hsts && strcasecompare("http", data->state.up.scheme)) {
/* This MUST use the IDN decoded name */
- if(Curl_hsts(data->hsts, conn->host.name, TRUE)) {
+ if(Curl_hsts(data->hsts, conn->host.name, strlen(conn->host.name), TRUE)) {
char *url;
Curl_safefree(data->state.up.scheme);
uc = curl_url_set(uh, CURLUPART_SCHEME, "https", 0);
@@ -1923,10 +2020,17 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data,
return CURLE_OUT_OF_MEMORY;
}
else {
- unsigned long port = strtoul(data->state.up.port, NULL, 10);
- conn->primary.remote_port = conn->remote_port =
- (data->set.use_port && data->state.allow_port) ?
- data->set.use_port : curlx_ultous(port);
+ curl_off_t port;
+ bool valid = TRUE;
+ if(data->set.use_port && data->state.allow_port)
+ port = data->set.use_port;
+ else {
+ const char *p = data->state.up.port;
+ if(curlx_str_number(&p, &port, 0xffff))
+ valid = FALSE;
+ }
+ if(valid)
+ conn->primary.remote_port = conn->remote_port = (unsigned short)port;
}
(void)curl_url_get(uh, CURLUPART_QUERY, &data->state.up.query, 0);
@@ -2031,9 +2135,8 @@ static CURLcode setup_connection_internals(struct Curl_easy *data,
if(!conn->destination)
return CURLE_OUT_OF_MEMORY;
- conn->destination_len = strlen(conn->destination) + 1;
Curl_strntolower(conn->destination, conn->destination,
- conn->destination_len - 1);
+ strlen(conn->destination));
return CURLE_OK;
}
@@ -2070,7 +2173,7 @@ static char *detect_proxy(struct Curl_easy *data,
* checked if the lowercase versions do not exist.
*/
char proxy_env[20];
- char *envp = proxy_env;
+ const char *envp = proxy_env;
#ifdef CURL_DISABLE_VERBOSE_STRINGS
(void)data;
#endif
@@ -2111,10 +2214,10 @@ static char *detect_proxy(struct Curl_easy *data,
}
if(!proxy) {
#endif
- envp = (char *)"all_proxy";
+ envp = "all_proxy";
proxy = curl_getenv(envp); /* default proxy to use */
if(!proxy) {
- envp = (char *)"ALL_PROXY";
+ envp = "ALL_PROXY";
proxy = curl_getenv(envp);
}
#ifndef CURL_DISABLE_WEBSOCKETS
@@ -2230,7 +2333,7 @@ static CURLcode parse_proxy(struct Curl_easy *data,
goto error;
if(proxyuser || proxypasswd) {
- Curl_safefree(proxyinfo->user);
+ free(proxyinfo->user);
proxyinfo->user = proxyuser;
result = Curl_setstropt(&data->state.aptr.proxyuser, proxyuser);
proxyuser = NULL;
@@ -2255,7 +2358,10 @@ static CURLcode parse_proxy(struct Curl_easy *data,
(void)curl_url_get(uhp, CURLUPART_PORT, &portptr, 0);
if(portptr) {
- port = (int)strtol(portptr, NULL, 10);
+ curl_off_t num;
+ const char *p = portptr;
+ if(!curlx_str_number(&p, &num, 0xffff))
+ port = (int)num;
free(portptr);
}
else {
@@ -2299,7 +2405,7 @@ static CURLcode parse_proxy(struct Curl_easy *data,
result = CURLE_OUT_OF_MEMORY;
goto error;
}
- Curl_safefree(proxyinfo->host.rawalloc);
+ free(proxyinfo->host.rawalloc);
proxyinfo->host.rawalloc = host;
proxyinfo->host.name = host;
host = NULL;
@@ -2308,7 +2414,7 @@ static CURLcode parse_proxy(struct Curl_easy *data,
if(!is_unix_proxy) {
#endif
- Curl_safefree(proxyinfo->host.rawalloc);
+ free(proxyinfo->host.rawalloc);
proxyinfo->host.rawalloc = host;
if(host[0] == '[') {
/* this is a numerical IPv6, strip off the brackets */
@@ -2495,7 +2601,7 @@ static CURLcode create_conn_helper_init_proxy(struct Curl_easy *data,
if(!conn->socks_proxy.user) {
conn->socks_proxy.user = conn->http_proxy.user;
conn->http_proxy.user = NULL;
- Curl_safefree(conn->socks_proxy.passwd);
+ free(conn->socks_proxy.passwd);
conn->socks_proxy.passwd = conn->http_proxy.passwd;
conn->http_proxy.passwd = NULL;
}
@@ -2651,6 +2757,17 @@ static CURLcode parse_remote_port(struct Curl_easy *data,
return CURLE_OK;
}
+static bool str_has_ctrl(const char *input)
+{
+ const unsigned char *str = (const unsigned char *)input;
+ while(*str) {
+ if(*str < 0x20)
+ return TRUE;
+ str++;
+ }
+ return FALSE;
+}
+
/*
* Override the login details from the URL with that in the CURLOPT_USERPWD
* option or a .netrc file, if applicable.
@@ -2677,37 +2794,49 @@ static CURLcode override_login(struct Curl_easy *data,
}
conn->bits.netrc = FALSE;
if(data->set.use_netrc && !data->set.str[STRING_USERNAME]) {
- int ret;
bool url_provided = FALSE;
if(data->state.aptr.user &&
(data->state.creds_from != CREDS_NETRC)) {
- /* there was a username in the URL. Use the URL decoded version */
+ /* there was a username with a length in the URL. Use the URL decoded
+ version */
userp = &data->state.aptr.user;
url_provided = TRUE;
}
- ret = Curl_parsenetrc(&data->state.netrc, conn->host.name,
- userp, passwdp,
- data->set.str[STRING_NETRC_FILE]);
- if(ret > 0) {
- infof(data, "Couldn't find host %s in the %s file; using defaults",
- conn->host.name,
- (data->set.str[STRING_NETRC_FILE] ?
- data->set.str[STRING_NETRC_FILE] : ".netrc"));
- }
- else if(ret < 0) {
- failf(data, ".netrc parser error");
- return CURLE_READ_ERROR;
- }
- else {
- /* set bits.netrc TRUE to remember that we got the name from a .netrc
- file, so that it is safe to use even if we followed a Location: to a
- different host or similar. */
- conn->bits.netrc = TRUE;
+ if(!*passwdp) {
+ NETRCcode ret = Curl_parsenetrc(&data->state.netrc, conn->host.name,
+ userp, passwdp,
+ data->set.str[STRING_NETRC_FILE]);
+ if(ret && ((ret == NETRC_NO_MATCH) ||
+ (data->set.use_netrc == CURL_NETRC_OPTIONAL))) {
+ infof(data, "Couldn't find host %s in the %s file; using defaults",
+ conn->host.name,
+ (data->set.str[STRING_NETRC_FILE] ?
+ data->set.str[STRING_NETRC_FILE] : ".netrc"));
+ }
+ else if(ret) {
+ const char *m = Curl_netrc_strerror(ret);
+ failf(data, ".netrc error: %s", m);
+ return CURLE_READ_ERROR;
+ }
+ else {
+ if(!(conn->handler->flags&PROTOPT_USERPWDCTRL)) {
+ /* if the protocol can't handle control codes in credentials, make
+ sure there are none */
+ if(str_has_ctrl(*userp) || str_has_ctrl(*passwdp)) {
+ failf(data, "control code detected in .netrc credentials");
+ return CURLE_READ_ERROR;
+ }
+ }
+ /* set bits.netrc TRUE to remember that we got the name from a .netrc
+ file, so that it is safe to use even if we followed a Location: to a
+ different host or similar. */
+ conn->bits.netrc = TRUE;
+ }
}
if(url_provided) {
- Curl_safefree(conn->user);
+ free(conn->user);
conn->user = strdup(*userp);
if(!conn->user)
return CURLE_OUT_OF_MEMORY;
@@ -2815,7 +2944,7 @@ static CURLcode parse_connect_to_host_port(struct Curl_easy *data,
int port = -1;
CURLcode result = CURLE_OK;
-#if defined(CURL_DISABLE_VERBOSE_STRINGS)
+#ifdef CURL_DISABLE_VERBOSE_STRINGS
(void) data;
#endif
@@ -2870,19 +2999,18 @@ static CURLcode parse_connect_to_host_port(struct Curl_easy *data,
/* Get port number off server.com:1080 */
host_portno = strchr(portptr, ':');
if(host_portno) {
- char *endp = NULL;
*host_portno = '\0'; /* cut off number from hostname */
host_portno++;
if(*host_portno) {
- long portparse = strtol(host_portno, &endp, 10);
- if((endp && *endp) || (portparse < 0) || (portparse > 65535)) {
+ curl_off_t portparse;
+ const char *p = host_portno;
+ if(curlx_str_number(&p, &portparse, 0xffff)) {
failf(data, "No valid port number in connect to host string (%s)",
host_portno);
result = CURLE_SETOPT_OPTION_SYNTAX;
goto error;
}
- else
- port = (int)portparse; /* we know it will fit */
+ port = (int)portparse; /* we know it will fit */
}
}
@@ -2953,12 +3081,11 @@ static CURLcode parse_connect_to_string(struct Curl_easy *data,
/* check whether the URL's port matches */
char *ptr_next = strchr(ptr, ':');
if(ptr_next) {
- char *endp = NULL;
- long port_to_match = strtol(ptr, &endp, 10);
- if((endp == ptr_next) && (port_to_match == conn->remote_port)) {
+ curl_off_t port_to_match;
+ if(!curlx_str_number(&ptr, &port_to_match, 0xffff) &&
+ (port_to_match == (curl_off_t)conn->remote_port))
port_match = TRUE;
- ptr = ptr_next + 1;
- }
+ ptr = ptr_next + 1;
}
}
}
@@ -3028,75 +3155,54 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data,
)) {
/* no connect_to match, try alt-svc! */
enum alpnid srcalpnid = ALPN_none;
- bool use_alt_svc = FALSE;
bool hit = FALSE;
struct altsvc *as = NULL;
- const int allowed_versions = ( ALPN_h1
-#ifdef USE_HTTP2
- | ALPN_h2
-#endif
-#ifdef USE_HTTP3
- | ALPN_h3
-#endif
- ) & data->asi->flags;
- static enum alpnid alpn_ids[] = {
+ int allowed_alpns = ALPN_none;
+ struct http_negotiation *neg = &data->state.http_neg;
+
+ DEBUGF(infof(data, "Alt-svc check wanted=%x, allowed=%x",
+ neg->wanted, neg->allowed));
#ifdef USE_HTTP3
- ALPN_h3,
+ if(neg->allowed & CURL_HTTP_V3x)
+ allowed_alpns |= ALPN_h3;
#endif
#ifdef USE_HTTP2
- ALPN_h2,
+ if(neg->allowed & CURL_HTTP_V2x)
+ allowed_alpns |= ALPN_h2;
#endif
- ALPN_h1,
- };
- size_t i;
-
- switch(data->state.httpwant) {
- case CURL_HTTP_VERSION_1_0:
- break;
- case CURL_HTTP_VERSION_1_1:
- use_alt_svc = TRUE;
- srcalpnid = ALPN_h1; /* only regard alt-svc advice for http/1.1 */
- break;
- case CURL_HTTP_VERSION_2_0:
- use_alt_svc = TRUE;
- srcalpnid = ALPN_h2; /* only regard alt-svc advice for h2 */
- break;
- case CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE:
- break;
- case CURL_HTTP_VERSION_3:
- use_alt_svc = TRUE;
- srcalpnid = ALPN_h3; /* only regard alt-svc advice for h3 */
- break;
- case CURL_HTTP_VERSION_3ONLY:
- break;
- default: /* no specific HTTP version wanted, look at all of alt-svc */
- use_alt_svc = TRUE;
- srcalpnid = ALPN_none;
- break;
- }
- if(!use_alt_svc)
- return CURLE_OK;
+ if(neg->allowed & CURL_HTTP_V1x)
+ allowed_alpns |= ALPN_h1;
+ allowed_alpns &= (int)data->asi->flags;
host = conn->host.rawalloc;
DEBUGF(infof(data, "check Alt-Svc for host %s", host));
- if(srcalpnid == ALPN_none) {
- /* scan all alt-svc protocol ids in order or relevance */
- for(i = 0; !hit && (i < ARRAYSIZE(alpn_ids)); ++i) {
- srcalpnid = alpn_ids[i];
- hit = Curl_altsvc_lookup(data->asi,
- srcalpnid, host, conn->remote_port, /* from */
- &as /* to */,
- allowed_versions);
- }
+#ifdef USE_HTTP3
+ if(!hit && (neg->wanted & CURL_HTTP_V3x)) {
+ srcalpnid = ALPN_h3;
+ hit = Curl_altsvc_lookup(data->asi,
+ ALPN_h3, host, conn->remote_port, /* from */
+ &as /* to */,
+ allowed_alpns);
}
- else {
- /* look for a specific alt-svc protocol id */
+ #endif
+ #ifdef USE_HTTP2
+ if(!hit && (neg->wanted & CURL_HTTP_V2x) &&
+ !neg->h2_prior_knowledge) {
+ srcalpnid = ALPN_h2;
hit = Curl_altsvc_lookup(data->asi,
- srcalpnid, host, conn->remote_port, /* from */
+ ALPN_h2, host, conn->remote_port, /* from */
&as /* to */,
- allowed_versions);
+ allowed_alpns);
+ }
+ #endif
+ if(!hit && (neg->wanted & CURL_HTTP_V1x) &&
+ !neg->only_10) {
+ srcalpnid = ALPN_h1;
+ hit = Curl_altsvc_lookup(data->asi,
+ ALPN_h1, host, conn->remote_port, /* from */
+ &as /* to */,
+ allowed_alpns);
}
-
if(hit) {
char *hostd = strdup((char *)as->dst.host);
@@ -3115,14 +3221,15 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data,
/* protocol version switch */
switch(as->dst.alpnid) {
case ALPN_h1:
- conn->httpversion = 11;
+ neg->wanted = neg->allowed = CURL_HTTP_V1x;
+ neg->only_10 = FALSE;
break;
case ALPN_h2:
- conn->httpversion = 20;
+ neg->wanted = neg->allowed = CURL_HTTP_V2x;
break;
case ALPN_h3:
conn->transport = TRNSPRT_QUIC;
- conn->httpversion = 30;
+ neg->wanted = neg->allowed = CURL_HTTP_V3x;
break;
default: /* should not be possible */
break;
@@ -3138,13 +3245,14 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data,
#ifdef USE_UNIX_SOCKETS
static CURLcode resolve_unix(struct Curl_easy *data,
struct connectdata *conn,
- char *unix_path)
+ char *unix_path,
+ struct Curl_dns_entry **pdns)
{
- struct Curl_dns_entry *hostaddr = NULL;
+ struct Curl_dns_entry *hostaddr;
bool longpath = FALSE;
DEBUGASSERT(unix_path);
- DEBUGASSERT(conn->dns_entry == NULL);
+ *pdns = NULL;
/* Unix domain sockets are local. The host gets ignored, just use the
* specified domain socket address. Do not cache "DNS entries". There is
@@ -3164,7 +3272,7 @@ static CURLcode resolve_unix(struct Curl_easy *data,
}
hostaddr->refcount = 1; /* connection is the only one holding this */
- conn->dns_entry = hostaddr;
+ *pdns = hostaddr;
return CURLE_OK;
}
#endif
@@ -3174,31 +3282,35 @@ static CURLcode resolve_unix(struct Curl_easy *data,
*************************************************************/
static CURLcode resolve_server(struct Curl_easy *data,
struct connectdata *conn,
- bool *async)
+ bool *async,
+ struct Curl_dns_entry **pdns)
{
struct hostname *ehost;
timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
const char *peertype = "host";
- int rc;
+ CURLcode result;
+
+ *pdns = NULL;
+
#ifdef USE_UNIX_SOCKETS
- char *unix_path = conn->unix_domain_socket;
+ {
+ char *unix_path = conn->unix_domain_socket;
#ifndef CURL_DISABLE_PROXY
- if(!unix_path && CONN_IS_PROXIED(conn) && conn->socks_proxy.host.name &&
- !strncmp(UNIX_SOCKET_PREFIX"/",
- conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX)))
- unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1;
+ if(!unix_path && CONN_IS_PROXIED(conn) && conn->socks_proxy.host.name &&
+ !strncmp(UNIX_SOCKET_PREFIX"/",
+ conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX)))
+ unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1;
#endif
- if(unix_path) {
- /* TODO, this only works if previous transport is TRNSPRT_TCP. Check it? */
- conn->transport = TRNSPRT_UNIX;
- return resolve_unix(data, conn, unix_path);
+ if(unix_path) {
+ /* This only works if previous transport is TRNSPRT_TCP. Check it? */
+ conn->transport = TRNSPRT_UNIX;
+ return resolve_unix(data, conn, unix_path, pdns);
+ }
}
#endif
- DEBUGASSERT(conn->dns_entry == NULL);
-
#ifndef CURL_DISABLE_PROXY
if(CONN_IS_PROXIED(conn)) {
ehost = conn->bits.socksproxy ? &conn->socks_proxy.host :
@@ -3220,22 +3332,25 @@ static CURLcode resolve_server(struct Curl_easy *data,
if(!conn->hostname_resolve)
return CURLE_OUT_OF_MEMORY;
- rc = Curl_resolv_timeout(data, conn->hostname_resolve,
- conn->primary.remote_port,
- &conn->dns_entry, timeout_ms);
- if(rc == CURLRESOLV_PENDING)
+ result = Curl_resolv_timeout(data, conn->hostname_resolve,
+ conn->primary.remote_port, conn->ip_version,
+ pdns, timeout_ms);
+ DEBUGASSERT(!result || !*pdns);
+ if(result == CURLE_AGAIN) {
*async = TRUE;
- else if(rc == CURLRESOLV_TIMEDOUT) {
+ return CURLE_OK;
+ }
+ else if(result == CURLE_OPERATION_TIMEDOUT) {
failf(data, "Failed to resolve %s '%s' with timeout after %"
FMT_TIMEDIFF_T " ms", peertype, ehost->dispname,
- Curl_timediff(Curl_now(), data->progress.t_startsingle));
+ curlx_timediff(curlx_now(), data->progress.t_startsingle));
return CURLE_OPERATION_TIMEDOUT;
}
- else if(!conn->dns_entry) {
+ else if(result) {
failf(data, "Could not resolve %s: %s", peertype, ehost->dispname);
- return CURLE_COULDNT_RESOLVE_HOST;
+ return result;
}
-
+ DEBUGASSERT(*pdns);
return CURLE_OK;
}
@@ -3252,8 +3367,8 @@ static void reuse_conn(struct Curl_easy *data,
* be new for this request even when we reuse an existing connection */
if(temp->user) {
/* use the new username and password though */
- Curl_safefree(existing->user);
- Curl_safefree(existing->passwd);
+ free(existing->user);
+ free(existing->passwd);
existing->user = temp->user;
existing->passwd = temp->passwd;
temp->user = NULL;
@@ -3264,10 +3379,10 @@ static void reuse_conn(struct Curl_easy *data,
existing->bits.proxy_user_passwd = temp->bits.proxy_user_passwd;
if(existing->bits.proxy_user_passwd) {
/* use the new proxy username and proxy password though */
- Curl_safefree(existing->http_proxy.user);
- Curl_safefree(existing->socks_proxy.user);
- Curl_safefree(existing->http_proxy.passwd);
- Curl_safefree(existing->socks_proxy.passwd);
+ free(existing->http_proxy.user);
+ free(existing->socks_proxy.user);
+ free(existing->http_proxy.passwd);
+ free(existing->socks_proxy.passwd);
existing->http_proxy.user = temp->http_proxy.user;
existing->socks_proxy.user = temp->socks_proxy.user;
existing->http_proxy.passwd = temp->http_proxy.passwd;
@@ -3289,7 +3404,7 @@ static void reuse_conn(struct Curl_easy *data,
* We want to reuse an existing conn to the remote endpoint.
* Since connection reuse does not match on conn->host necessarily, we
* switch `existing` conn to `temp` conn's host settings.
- * TODO: is this correct in the case of TLS connections that have
+ * Is this correct in the case of TLS connections that have
* used the original hostname in SNI to negotiate? Do we send
* requests for another host through the different SNI?
*/
@@ -3304,8 +3419,7 @@ static void reuse_conn(struct Curl_easy *data,
temp->conn_to_host.rawalloc = NULL;
existing->conn_to_port = temp->conn_to_port;
existing->remote_port = temp->remote_port;
- Curl_safefree(existing->hostname_resolve);
-
+ free(existing->hostname_resolve);
existing->hostname_resolve = temp->hostname_resolve;
temp->hostname_resolve = NULL;
@@ -3315,6 +3429,15 @@ static void reuse_conn(struct Curl_easy *data,
Curl_conn_free(data, temp);
}
+static void conn_meta_freeentry(void *p)
+{
+ (void)p;
+ /* Will always be FALSE. Cannot use a 0 assert here since compilers
+ * are not in agreement if they then want a NORETURN attribute or
+ * not. *sigh* */
+ DEBUGASSERT(p == NULL);
+}
+
/**
* create_conn() sets up a new connectdata struct, or reuses an already
* existing one, and resolves hostname.
@@ -3325,14 +3448,14 @@ static void reuse_conn(struct Curl_easy *data,
*
* @param data The sessionhandle pointer
* @param in_connect is set to the next connection data pointer
- * @param async is set TRUE when an async DNS resolution is pending
+ * @param reusedp is set to to TRUE if connection was reused
* @see Curl_setup_conn()
*
*/
static CURLcode create_conn(struct Curl_easy *data,
struct connectdata **in_connect,
- bool *async)
+ bool *reusedp)
{
CURLcode result = CURLE_OK;
struct connectdata *conn;
@@ -3342,7 +3465,7 @@ static CURLcode create_conn(struct Curl_easy *data,
bool force_reuse = FALSE;
bool waitpipe = FALSE;
- *async = FALSE;
+ *reusedp = FALSE;
*in_connect = NULL;
/*************************************************************
@@ -3369,6 +3492,13 @@ static CURLcode create_conn(struct Curl_easy *data,
any failure */
*in_connect = conn;
+ /* Do the unfailable inits first, before checks that may early return */
+ Curl_hash_init(&conn->meta_hash, 23,
+ Curl_hash_str, curlx_str_key_compare, conn_meta_freeentry);
+
+ /* GSSAPI related inits */
+ Curl_sec_conn_init(conn);
+
result = parseurlandfillconn(data, conn);
if(result)
goto out;
@@ -3509,30 +3639,26 @@ static CURLcode create_conn(struct Curl_easy *data,
/* conn_protocol can only provide "old" protocols */
data->info.conn_protocol = (conn->handler->protocol) & CURLPROTO_MASK;
result = conn->handler->connect_it(data, &done);
+ if(result)
+ goto out;
/* Setup a "faked" transfer that will do nothing */
+ Curl_attach_connection(data, conn);
+ result = Curl_cpool_add(data, conn);
if(!result) {
- Curl_attach_connection(data, conn);
- result = Curl_cpool_add_conn(data, conn);
- if(result)
- goto out;
-
- /*
- * Setup whatever necessary for a resumed transfer
- */
+ /* Setup whatever necessary for a resumed transfer */
result = setup_range(data);
- if(result) {
- DEBUGASSERT(conn->handler->done);
- /* we ignore the return code for the protocol-specific DONE */
- (void)conn->handler->done(data, result, FALSE);
- goto out;
+ if(!result) {
+ Curl_xfer_setup_nop(data);
+ result = Curl_init_do(data, conn);
}
- Curl_xfer_setup_nop(data);
}
- /* since we skip do_init() */
- Curl_init_do(data, conn);
-
+ if(result) {
+ DEBUGASSERT(conn->handler->done);
+ /* we ignore the return code for the protocol-specific DONE */
+ (void)conn->handler->done(data, result, FALSE);
+ }
goto out;
}
#endif
@@ -3549,7 +3675,6 @@ static CURLcode create_conn(struct Curl_easy *data,
if(result)
goto out;
- /* FIXME: do we really want to run this every time we add a transfer? */
Curl_cpool_prune_dead(data);
/*************************************************************
@@ -3577,18 +3702,25 @@ static CURLcode create_conn(struct Curl_easy *data,
* `existing` and thus we need to cleanup the one we just
* allocated before we can move along and use `existing`.
*/
+ bool tls_upgraded = (!(conn->given->flags & PROTOPT_SSL) &&
+ Curl_conn_is_ssl(conn, FIRSTSOCKET));
+
reuse_conn(data, conn, existing);
conn = existing;
*in_connect = conn;
#ifndef CURL_DISABLE_PROXY
- infof(data, "Re-using existing connection with %s %s",
+ infof(data, "Re-using existing %s: connection%s with %s %s",
+ conn->given->scheme,
+ tls_upgraded ? " (upgraded to SSL)" : "",
conn->bits.proxy ? "proxy" : "host",
conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname :
conn->http_proxy.host.name ? conn->http_proxy.host.dispname :
conn->host.dispname);
#else
- infof(data, "Re-using existing connection with host %s",
+ infof(data, "Re-using existing %s: connection%s with host %s",
+ conn->given->scheme,
+ tls_upgraded ? " (upgraded to SSL)" : "",
conn->host.dispname);
#endif
}
@@ -3604,10 +3736,12 @@ static CURLcode create_conn(struct Curl_easy *data,
conn->bits.tls_enable_alpn = TRUE;
}
- if(waitpipe)
+ if(waitpipe) {
/* There is a connection that *might* become usable for multiplexing
"soon", and we wait for that */
+ infof(data, "Waiting on connection to negotiate possible multiplexing.");
connections_available = FALSE;
+ }
else {
switch(Curl_cpool_check_limits(data, conn)) {
case CPOOL_LIMIT_DEST:
@@ -3615,13 +3749,12 @@ static CURLcode create_conn(struct Curl_easy *data,
connections_available = FALSE;
break;
case CPOOL_LIMIT_TOTAL:
-#ifndef CURL_DISABLE_DOH
- if(data->set.dohfor_mid >= 0)
- infof(data, "Allowing DoH to override max connection limit");
- else
-#endif
- {
- infof(data, "No connections available in cache");
+ if(data->master_mid != UINT_MAX)
+ CURL_TRC_M(data, "Allowing sub-requests (like DoH) to override "
+ "max connection limit");
+ else {
+ infof(data, "No connections available, total of %ld reached.",
+ data->multi->max_total_connections);
connections_available = FALSE;
}
break;
@@ -3631,8 +3764,6 @@ static CURLcode create_conn(struct Curl_easy *data,
}
if(!connections_available) {
- infof(data, "No connections available.");
-
Curl_conn_free(data, conn);
*in_connect = NULL;
@@ -3651,12 +3782,12 @@ static CURLcode create_conn(struct Curl_easy *data,
}
Curl_attach_connection(data, conn);
- result = Curl_cpool_add_conn(data, conn);
+ result = Curl_cpool_add(data, conn);
if(result)
goto out;
}
-#if defined(USE_NTLM)
+#ifdef USE_NTLM
/* If NTLM is requested in a part of this connection, make sure we do not
assume the state is fine as this is a fresh connection and NTLM is
connection based. */
@@ -3677,7 +3808,9 @@ static CURLcode create_conn(struct Curl_easy *data,
}
/* Setup and init stuff before DO starts, in preparing for the transfer. */
- Curl_init_do(data, conn);
+ result = Curl_init_do(data, conn);
+ if(result)
+ goto out;
/*
* Setup whatever necessary for a resumed transfer
@@ -3692,15 +3825,7 @@ static CURLcode create_conn(struct Curl_easy *data,
/* We are reusing the connection - no need to resolve anything, and
idnconvert_hostname() was called already in create_conn() for the reuse
case. */
- *async = FALSE;
- }
- else {
- /*************************************************************
- * Resolve the address of the server or proxy
- *************************************************************/
- result = resolve_server(data, conn, async);
- if(result)
- goto out;
+ *reusedp = TRUE;
}
/* persist the scheme and handler the transfer is using */
@@ -3729,24 +3854,17 @@ out:
* Curl_setup_conn() also handles reused connections
*/
CURLcode Curl_setup_conn(struct Curl_easy *data,
+ struct Curl_dns_entry *dns,
bool *protocol_done)
{
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
+ DEBUGASSERT(dns);
Curl_pgrsTime(data, TIMER_NAMELOOKUP);
- if(conn->handler->flags & PROTOPT_NONETWORK) {
- /* nothing to setup when not using a network */
- *protocol_done = TRUE;
- return result;
- }
-
- /* set start time here for timeout purposes in the connect procedure, it
- is later set again for the progress meter purpose */
- conn->now = Curl_now();
if(!conn->bits.reuse)
- result = Curl_conn_setup(data, conn, FIRSTSOCKET, conn->dns_entry,
+ result = Curl_conn_setup(data, conn, FIRSTSOCKET, dns,
CURL_CF_SSL_DEFAULT);
if(!result)
result = Curl_headers_init(data);
@@ -3762,35 +3880,56 @@ CURLcode Curl_connect(struct Curl_easy *data,
{
CURLcode result;
struct connectdata *conn;
+ bool reused = FALSE;
*asyncp = FALSE; /* assume synchronous resolves by default */
+ *protocol_done = FALSE;
/* Set the request to virgin state based on transfer settings */
Curl_req_hard_reset(&data->req, data);
/* call the stuff that needs to be called */
- result = create_conn(data, &conn, asyncp);
+ result = create_conn(data, &conn, &reused);
+
+ if(result == CURLE_NO_CONNECTION_AVAILABLE) {
+ DEBUGASSERT(!conn);
+ return result;
+ }
if(!result) {
- if(CONN_INUSE(conn) > 1)
- /* multiplexed */
+ DEBUGASSERT(conn);
+ if(reused) {
+ if(CONN_ATTACHED(conn) > 1)
+ /* multiplexed */
+ *protocol_done = TRUE;
+ }
+ else if(conn->handler->flags & PROTOPT_NONETWORK) {
+ *asyncp = FALSE;
+ Curl_pgrsTime(data, TIMER_NAMELOOKUP);
*protocol_done = TRUE;
- else if(!*asyncp) {
- /* DNS resolution is done: that is either because this is a reused
- connection, in which case DNS was unnecessary, or because DNS
- really did finish already (synch resolver/fast async resolve) */
- result = Curl_setup_conn(data, protocol_done);
+ }
+ else {
+ /*************************************************************
+ * Resolve the address of the server or proxy
+ *************************************************************/
+ struct Curl_dns_entry *dns;
+ result = resolve_server(data, conn, asyncp, &dns);
+ if(!result) {
+ *asyncp = !dns;
+ if(dns)
+ /* DNS resolution is done: that is either because this is a reused
+ connection, in which case DNS was unnecessary, or because DNS
+ really did finish already (synch resolver/fast async resolve) */
+ result = Curl_setup_conn(data, dns, protocol_done);
+ }
}
}
- if(result == CURLE_NO_CONNECTION_AVAILABLE) {
- return result;
- }
- else if(result && conn) {
+ if(result && conn) {
/* We are not allowed to return failure with memory left allocated in the
connectdata struct, free those here */
Curl_detach_connection(data);
- Curl_cpool_disconnect(data, conn, TRUE);
+ Curl_conn_terminate(data, conn, TRUE);
}
return result;
@@ -3933,3 +4072,25 @@ void Curl_data_priority_clear_state(struct Curl_easy *data)
}
#endif /* defined(USE_HTTP2) || defined(USE_HTTP3) */
+
+
+CURLcode Curl_conn_meta_set(struct connectdata *conn, const char *key,
+ void *meta_data, Curl_meta_dtor *meta_dtor)
+{
+ if(!Curl_hash_add2(&conn->meta_hash, CURL_UNCONST(key), strlen(key) + 1,
+ meta_data, meta_dtor)) {
+ meta_dtor(CURL_UNCONST(key), strlen(key) + 1, meta_data);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ return CURLE_OK;
+}
+
+void Curl_conn_meta_remove(struct connectdata *conn, const char *key)
+{
+ Curl_hash_delete(&conn->meta_hash, CURL_UNCONST(key), strlen(key) + 1);
+}
+
+void *Curl_conn_meta_get(struct connectdata *conn, const char *key)
+{
+ return Curl_hash_pick(&conn->meta_hash, CURL_UNCONST(key), strlen(key) + 1);
+}
diff --git a/libs/libcurl/src/url.h b/libs/libcurl/src/url.h
index d09bf14137..3ab21dd8b5 100644
--- a/libs/libcurl/src/url.h
+++ b/libs/libcurl/src/url.h
@@ -37,15 +37,36 @@ void Curl_freeset(struct Curl_easy *data);
CURLcode Curl_uc_to_curlcode(CURLUcode uc);
CURLcode Curl_close(struct Curl_easy **datap); /* opposite of curl_open() */
CURLcode Curl_connect(struct Curl_easy *, bool *async, bool *protocol_connect);
-bool Curl_on_disconnect(struct Curl_easy *data,
- struct connectdata *, bool aborted);
CURLcode Curl_setup_conn(struct Curl_easy *data,
+ struct Curl_dns_entry *dns,
bool *protocol_done);
void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn);
CURLcode Curl_parse_login_details(const char *login, const size_t len,
char **userptr, char **passwdptr,
char **optionsptr);
+/* Attach/Clear/Get meta data for an easy handle. Needs to provide
+ * a destructor, will be automatically called when the easy handle
+ * is reset or closed. */
+typedef void Curl_meta_dtor(void *key, size_t key_len, void *meta_data);
+
+/* Set the transfer meta data for the key. Any existing entry for that
+ * key will be destroyed.
+ * Takes ownership of `meta_data` and destroys it when the call fails. */
+CURLcode Curl_meta_set(struct Curl_easy *data, const char *key,
+ void *meta_data, Curl_meta_dtor *meta_dtor);
+void Curl_meta_remove(struct Curl_easy *data, const char *key);
+void *Curl_meta_get(struct Curl_easy *data, const char *key);
+void Curl_meta_reset(struct Curl_easy *data);
+
+/* Set connection meta data for the key. Any existing entry for that
+ * key will be destroyed.
+ * Takes ownership of `meta_data` and destroys it when the call fails. */
+CURLcode Curl_conn_meta_set(struct connectdata *conn, const char *key,
+ void *meta_data, Curl_meta_dtor *meta_dtor);
+void Curl_conn_meta_remove(struct connectdata *conn, const char *key);
+void *Curl_conn_meta_get(struct connectdata *conn, const char *key);
+
/* Get protocol handler for a URI scheme
* @param scheme URI scheme, case-insensitive
* @return NULL of handler not found
diff --git a/libs/libcurl/src/urlapi-int.h b/libs/libcurl/src/urlapi-int.h
index 890e3285d9..a6d2b16be1 100644
--- a/libs/libcurl/src/urlapi-int.h
+++ b/libs/libcurl/src/urlapi-int.h
@@ -30,6 +30,8 @@ size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen,
CURLUcode Curl_url_set_authority(CURLU *u, const char *authority);
+CURLUcode Curl_junkscan(const char *url, size_t *urllen, bool allowspace);
+
#ifdef UNITTESTS
UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host,
bool has_scheme);
diff --git a/libs/libcurl/src/urlapi.c b/libs/libcurl/src/urlapi.c
index 9b137193dd..f0ea831c29 100644
--- a/libs/libcurl/src/urlapi.c
+++ b/libs/libcurl/src/urlapi.c
@@ -30,10 +30,12 @@
#include "url.h"
#include "escape.h"
#include "curl_ctype.h"
-#include "inet_pton.h"
+#include "curlx/inet_pton.h"
#include "inet_ntop.h"
#include "strdup.h"
#include "idn.h"
+#include "curlx/strparse.h"
+#include "curl_memrchr.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -86,6 +88,9 @@ struct Curl_URL {
#define DEFAULT_SCHEME "https"
+static CURLUcode parseurl_and_replace(const char *url, CURLU *u,
+ unsigned int flags);
+
static void free_urlhandle(struct Curl_URL *u)
{
free(u->scheme);
@@ -106,37 +111,24 @@ static void free_urlhandle(struct Curl_URL *u)
*/
static const char *find_host_sep(const char *url)
{
- const char *sep;
- const char *query;
-
/* Find the start of the hostname */
- sep = strstr(url, "//");
+ const char *sep = strstr(url, "//");
if(!sep)
sep = url;
else
sep += 2;
- query = strchr(sep, '?');
- sep = strchr(sep, '/');
-
- if(!sep)
- sep = url + strlen(url);
-
- if(!query)
- query = url + strlen(url);
+ /* Find first / or ? */
+ while(*sep && *sep != '/' && *sep != '?')
+ sep++;
- return sep < query ? sep : query;
+ return sep;
}
/* convert CURLcode to CURLUcode */
#define cc2cu(x) ((x) == CURLE_TOO_LARGE ? CURLUE_TOO_LARGE : \
CURLUE_OUT_OF_MEMORY)
-/*
- * Decide whether a character in a URL must be escaped.
- */
-#define urlchar_needs_escaping(c) (!(ISCNTRL(c) || ISSPACE(c) || ISGRAPH(c)))
-static const char hexdigits[] = "0123456789abcdef";
/* urlencode_str() writes data into an output dynbuf and URL-encodes the
* spaces in the source URL accordingly.
*
@@ -151,46 +143,39 @@ static CURLUcode urlencode_str(struct dynbuf *o, const char *url,
bool left = !query;
const unsigned char *iptr;
const unsigned char *host_sep = (const unsigned char *) url;
- CURLcode result;
+ CURLcode result = CURLE_OK;
- if(!relative)
+ if(!relative) {
+ size_t n;
host_sep = (const unsigned char *) find_host_sep(url);
- for(iptr = (unsigned char *)url; /* read from here */
- len; iptr++, len--) {
-
- if(iptr < host_sep) {
- result = Curl_dyn_addn(o, iptr, 1);
- if(result)
- return cc2cu(result);
- continue;
- }
+ /* output the first piece as-is */
+ n = (const char *)host_sep - url;
+ result = curlx_dyn_addn(o, url, n);
+ len -= n;
+ }
+ for(iptr = host_sep; len && !result; iptr++, len--) {
if(*iptr == ' ') {
if(left)
- result = Curl_dyn_addn(o, "%20", 3);
+ result = curlx_dyn_addn(o, "%20", 3);
else
- result = Curl_dyn_addn(o, "+", 1);
- if(result)
- return cc2cu(result);
- continue;
+ result = curlx_dyn_addn(o, "+", 1);
}
-
- if(*iptr == '?')
- left = FALSE;
-
- if(urlchar_needs_escaping(*iptr)) {
- char out[3]={'%'};
- out[1] = hexdigits[*iptr >> 4];
- out[2] = hexdigits[*iptr & 0xf];
- result = Curl_dyn_addn(o, out, 3);
+ else if((*iptr < ' ') || (*iptr >= 0x7f)) {
+ unsigned char out[3]={'%'};
+ Curl_hexbyte(&out[1], *iptr, TRUE);
+ result = curlx_dyn_addn(o, out, 3);
+ }
+ else {
+ result = curlx_dyn_addn(o, iptr, 1);
+ if(*iptr == '?')
+ left = FALSE;
}
- else
- result = Curl_dyn_addn(o, iptr, 1);
- if(result)
- return cc2cu(result);
}
+ if(result)
+ return cc2cu(result);
return CURLUE_OK;
}
@@ -243,176 +228,95 @@ size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen,
}
/*
- * Concatenate a relative URL to a base URL making it absolute.
- * URL-encodes any spaces.
- * The returned pointer must be freed by the caller unless NULL
- * (returns NULL on out of memory).
- *
- * Note that this function destroys the 'base' string.
+ * Concatenate a relative URL onto a base URL making it absolute.
*/
-static CURLcode concat_url(char *base, const char *relurl, char **newurl)
+static CURLUcode redirect_url(const char *base, const char *relurl,
+ CURLU *u, unsigned int flags)
{
- /***
- TRY to append this new path to the old URL
- to the right of the host part. Oh crap, this is doomed to cause
- problems in the future...
- */
- struct dynbuf newest;
- char *protsep;
- char *pathsep;
+ struct dynbuf urlbuf;
bool host_changed = FALSE;
const char *useurl = relurl;
- CURLcode result = CURLE_OK;
+ const char *cutoff = NULL;
+ size_t prelen;
CURLUcode uc;
- bool skip_slash = FALSE;
- *newurl = NULL;
-
- /* protsep points to the start of the hostname */
- protsep = strstr(base, "//");
- if(!protsep)
- protsep = base;
- else
- protsep += 2; /* pass the slashes */
-
- if('/' != relurl[0]) {
- int level = 0;
-
- /* First we need to find out if there is a ?-letter in the URL,
- and cut it and the right-side of that off */
- pathsep = strchr(protsep, '?');
- if(pathsep)
- *pathsep = 0;
-
- /* we have a relative path to append to the last slash if there is one
- available, or the new URL is just a query string (starts with a '?') or
- a fragment (starts with '#') we append the new one at the end of the
- current URL */
- if((useurl[0] != '?') && (useurl[0] != '#')) {
- pathsep = strrchr(protsep, '/');
- if(pathsep)
- *pathsep = 0;
-
- /* Check if there is any slash after the hostname, and if so, remember
- that position instead */
- pathsep = strchr(protsep, '/');
- if(pathsep)
- protsep = pathsep + 1;
- else
- protsep = NULL;
- /* now deal with one "./" or any amount of "../" in the newurl
- and act accordingly */
-
- if((useurl[0] == '.') && (useurl[1] == '/'))
- useurl += 2; /* just skip the "./" */
-
- while((useurl[0] == '.') &&
- (useurl[1] == '.') &&
- (useurl[2] == '/')) {
- level++;
- useurl += 3; /* pass the "../" */
- }
-
- if(protsep) {
- while(level--) {
- /* cut off one more level from the right of the original URL */
- pathsep = strrchr(protsep, '/');
- if(pathsep)
- *pathsep = 0;
- else {
- *protsep = 0;
- break;
- }
- }
- }
- }
- else
- skip_slash = TRUE;
- }
- else {
- /* We got a new absolute path for this server */
+ /* protsep points to the start of the hostname, after [scheme]:// */
+ const char *protsep = base + strlen(u->scheme) + 3;
+ DEBUGASSERT(base && relurl && u); /* all set here */
+ if(!base)
+ return CURLUE_MALFORMED_INPUT; /* should never happen */
+ /* handle different relative URL types */
+ switch(relurl[0]) {
+ case '/':
if(relurl[1] == '/') {
- /* the new URL starts with //, just keep the protocol part from the
- original one */
- *protsep = 0;
- useurl = &relurl[2]; /* we keep the slashes from the original, so we
- skip the new ones */
+ /* protocol-relative URL: //example.com/path */
+ cutoff = protsep;
+ useurl = &relurl[2];
host_changed = TRUE;
}
- else {
- /* cut off the original URL from the first slash, or deal with URLs
- without slash */
- pathsep = strchr(protsep, '/');
- if(pathsep) {
- /* When people use badly formatted URLs, such as
- "http://www.example.com?dir=/home/daniel" we must not use the first
- slash, if there is a ?-letter before it! */
- char *sep = strchr(protsep, '?');
- if(sep && (sep < pathsep))
- pathsep = sep;
- *pathsep = 0;
- }
- else {
- /* There was no slash. Now, since we might be operating on a badly
- formatted URL, such as "http://www.example.com?id=2380" which does
- not use a slash separator as it is supposed to, we need to check
- for a ?-letter as well! */
- pathsep = strchr(protsep, '?');
- if(pathsep)
- *pathsep = 0;
- }
+ else
+ /* absolute /path */
+ cutoff = strchr(protsep, '/');
+ break;
+
+ case '#':
+ /* fragment-only change */
+ if(u->fragment)
+ cutoff = strchr(protsep, '#');
+ break;
+
+ default:
+ /* path or query-only change */
+ if(u->query && u->query[0])
+ /* remove existing query */
+ cutoff = strchr(protsep, '?');
+ else if(u->fragment && u->fragment[0])
+ /* Remove existing fragment */
+ cutoff = strchr(protsep, '#');
+
+ if(relurl[0] != '?') {
+ /* append a relative path after the last slash */
+ cutoff = memrchr(protsep, '/',
+ cutoff ? (size_t)(cutoff - protsep) : strlen(protsep));
+ if(cutoff)
+ cutoff++; /* truncate after last slash */
}
+ break;
}
- Curl_dyn_init(&newest, CURL_MAX_INPUT_LENGTH);
+ prelen = cutoff ? (size_t)(cutoff - base) : strlen(base);
- /* copy over the root URL part */
- result = Curl_dyn_add(&newest, base);
- if(result)
- return result;
+ /* build new URL */
+ curlx_dyn_init(&urlbuf, CURL_MAX_INPUT_LENGTH);
- /* check if we need to append a slash */
- if(('/' == useurl[0]) || (protsep && !*protsep) || skip_slash)
- ;
- else {
- result = Curl_dyn_addn(&newest, "/", 1);
- if(result)
- return result;
+ if(!curlx_dyn_addn(&urlbuf, base, prelen) &&
+ !urlencode_str(&urlbuf, useurl, strlen(useurl), !host_changed, FALSE)) {
+ uc = parseurl_and_replace(curlx_dyn_ptr(&urlbuf), u,
+ flags & ~CURLU_PATH_AS_IS);
}
+ else
+ uc = CURLUE_OUT_OF_MEMORY;
- /* then append the new piece on the right side */
- uc = urlencode_str(&newest, useurl, strlen(useurl), !host_changed,
- FALSE);
- if(uc)
- return (uc == CURLUE_TOO_LARGE) ? CURLE_TOO_LARGE : CURLE_OUT_OF_MEMORY;
-
- *newurl = Curl_dyn_ptr(&newest);
- return CURLE_OK;
+ curlx_dyn_free(&urlbuf);
+ return uc;
}
/* scan for byte values <= 31, 127 and sometimes space */
-static CURLUcode junkscan(const char *url, size_t *urllen, unsigned int flags)
+CURLUcode Curl_junkscan(const char *url, size_t *urllen, bool allowspace)
{
- static const char badbytes[]={
- /* */ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
- 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
- 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
- 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
- 0x7f, 0x00 /* null-terminate */
- };
size_t n = strlen(url);
- size_t nfine;
-
+ size_t i;
+ unsigned char control;
+ const unsigned char *p = (const unsigned char *)url;
if(n > CURL_MAX_INPUT_LENGTH)
- /* excessive input length */
- return CURLUE_MALFORMED_INPUT;
-
- nfine = strcspn(url, badbytes);
- if((nfine != n) ||
- (!(flags & CURLU_ALLOW_SPACE) && strchr(url, ' ')))
return CURLUE_MALFORMED_INPUT;
+ control = allowspace ? 0x1f : 0x20;
+ for(i = 0; i < n; i++) {
+ if(p[i] <= control || p[i] == 127)
+ return CURLUE_MALFORMED_INPUT;
+ }
*urllen = n;
return CURLUE_OK;
}
@@ -455,7 +359,7 @@ static CURLUcode parse_hostname_login(struct Curl_URL *u,
/* We will now try to extract the
* possible login information in a string like:
- * ftp://user:password@ftp.my.site:8021/README */
+ * ftp://user:password@ftp.site.example:8021/README */
ptr++;
/* if this is a known scheme, get some details */
@@ -512,8 +416,8 @@ out:
UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host,
bool has_scheme)
{
- char *portptr;
- char *hostname = Curl_dyn_ptr(host);
+ const char *portptr;
+ char *hostname = curlx_dyn_ptr(host);
/*
* Find the end of an IPv6 address on the ']' ending bracket.
*/
@@ -534,8 +438,7 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host,
portptr = strchr(hostname, ':');
if(portptr) {
- char *rest = NULL;
- unsigned long port;
+ curl_off_t port;
size_t keep = portptr - hostname;
/* Browser behavior adaptation. If there is a colon with no digits after,
@@ -545,24 +448,18 @@ UNITTEST CURLUcode Curl_parse_port(struct Curl_URL *u, struct dynbuf *host,
Do not do it if the URL has no scheme, to make something that looks like
a scheme not work!
*/
- Curl_dyn_setlen(host, keep);
+ curlx_dyn_setlen(host, keep);
portptr++;
if(!*portptr)
return has_scheme ? CURLUE_OK : CURLUE_BAD_PORT_NUMBER;
- if(!ISDIGIT(*portptr))
- return CURLUE_BAD_PORT_NUMBER;
-
- errno = 0;
- port = strtoul(portptr, &rest, 10); /* Port number must be decimal */
-
- if(errno || (port > 0xffff) || *rest)
+ if(curlx_str_number(&portptr, &port, 0xffff) || *portptr)
return CURLUE_BAD_PORT_NUMBER;
u->portnum = (unsigned short) port;
/* generate a new port number string to get rid of leading zeroes etc */
free(u->port);
- u->port = aprintf("%ld", port);
+ u->port = aprintf("%" CURL_FORMAT_CURL_OFF_T, port);
if(!u->port)
return CURLUE_OUT_OF_MEMORY;
}
@@ -614,7 +511,7 @@ static CURLUcode ipv6_parse(struct Curl_URL *u, char *hostname,
{
char dest[16]; /* fits a binary IPv6 address */
hostname[hlen] = 0; /* end the address there */
- if(1 != Curl_inet_pton(AF_INET6, hostname, dest))
+ if(1 != curlx_inet_pton(AF_INET6, hostname, dest))
return CURLUE_BAD_IPV6;
if(Curl_inet_ntop(AF_INET6, dest, hostname, hlen)) {
hlen = strlen(hostname); /* might be shorter now */
@@ -668,31 +565,31 @@ static int ipv4_normalize(struct dynbuf *host)
{
bool done = FALSE;
int n = 0;
- const char *c = Curl_dyn_ptr(host);
- unsigned long parts[4] = {0, 0, 0, 0};
+ const char *c = curlx_dyn_ptr(host);
+ unsigned int parts[4] = {0, 0, 0, 0};
CURLcode result = CURLE_OK;
if(*c == '[')
return HOST_IPV6;
- errno = 0; /* for strtoul */
while(!done) {
- char *endp = NULL;
- unsigned long l;
- if(!ISDIGIT(*c))
- /* most importantly this does not allow a leading plus or minus */
- return HOST_NAME;
- l = strtoul(c, &endp, 0);
- if(errno)
- return HOST_NAME;
-#if SIZEOF_LONG > 4
- /* a value larger than 32 bits */
- if(l > UINT_MAX)
+ int rc;
+ curl_off_t l;
+ if(*c == '0') {
+ if(c[1] == 'x') {
+ c += 2; /* skip the prefix */
+ rc = curlx_str_hex(&c, &l, UINT_MAX);
+ }
+ else
+ rc = curlx_str_octal(&c, &l, UINT_MAX);
+ }
+ else
+ rc = curlx_str_number(&c, &l, UINT_MAX);
+
+ if(rc)
return HOST_NAME;
-#endif
- parts[n] = l;
- c = endp;
+ parts[n] = (unsigned int)l;
switch(*c) {
case '.':
@@ -713,44 +610,44 @@ static int ipv4_normalize(struct dynbuf *host)
switch(n) {
case 0: /* a -- 32 bits */
- Curl_dyn_reset(host);
+ curlx_dyn_reset(host);
- result = Curl_dyn_addf(host, "%u.%u.%u.%u",
- (unsigned int)(parts[0] >> 24),
- (unsigned int)((parts[0] >> 16) & 0xff),
- (unsigned int)((parts[0] >> 8) & 0xff),
- (unsigned int)(parts[0] & 0xff));
+ result = curlx_dyn_addf(host, "%u.%u.%u.%u",
+ (parts[0] >> 24),
+ ((parts[0] >> 16) & 0xff),
+ ((parts[0] >> 8) & 0xff),
+ (parts[0] & 0xff));
break;
case 1: /* a.b -- 8.24 bits */
if((parts[0] > 0xff) || (parts[1] > 0xffffff))
return HOST_NAME;
- Curl_dyn_reset(host);
- result = Curl_dyn_addf(host, "%u.%u.%u.%u",
- (unsigned int)(parts[0]),
- (unsigned int)((parts[1] >> 16) & 0xff),
- (unsigned int)((parts[1] >> 8) & 0xff),
- (unsigned int)(parts[1] & 0xff));
+ curlx_dyn_reset(host);
+ result = curlx_dyn_addf(host, "%u.%u.%u.%u",
+ (parts[0]),
+ ((parts[1] >> 16) & 0xff),
+ ((parts[1] >> 8) & 0xff),
+ (parts[1] & 0xff));
break;
case 2: /* a.b.c -- 8.8.16 bits */
if((parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xffff))
return HOST_NAME;
- Curl_dyn_reset(host);
- result = Curl_dyn_addf(host, "%u.%u.%u.%u",
- (unsigned int)(parts[0]),
- (unsigned int)(parts[1]),
- (unsigned int)((parts[2] >> 8) & 0xff),
- (unsigned int)(parts[2] & 0xff));
+ curlx_dyn_reset(host);
+ result = curlx_dyn_addf(host, "%u.%u.%u.%u",
+ (parts[0]),
+ (parts[1]),
+ ((parts[2] >> 8) & 0xff),
+ (parts[2] & 0xff));
break;
case 3: /* a.b.c.d -- 8.8.8.8 bits */
if((parts[0] > 0xff) || (parts[1] > 0xff) || (parts[2] > 0xff) ||
(parts[3] > 0xff))
return HOST_NAME;
- Curl_dyn_reset(host);
- result = Curl_dyn_addf(host, "%u.%u.%u.%u",
- (unsigned int)(parts[0]),
- (unsigned int)(parts[1]),
- (unsigned int)(parts[2]),
- (unsigned int)(parts[3]));
+ curlx_dyn_reset(host);
+ result = curlx_dyn_addf(host, "%u.%u.%u.%u",
+ (parts[0]),
+ (parts[1]),
+ (parts[2]),
+ (parts[3]));
break;
}
if(result)
@@ -762,7 +659,7 @@ static int ipv4_normalize(struct dynbuf *host)
static CURLUcode urldecode_host(struct dynbuf *host)
{
char *per = NULL;
- const char *hostname = Curl_dyn_ptr(host);
+ const char *hostname = curlx_dyn_ptr(host);
per = strchr(hostname, '%');
if(!per)
/* nothing to decode */
@@ -775,8 +672,8 @@ static CURLUcode urldecode_host(struct dynbuf *host)
REJECT_CTRL);
if(result)
return CURLUE_BAD_HOSTNAME;
- Curl_dyn_reset(host);
- result = Curl_dyn_addn(host, decoded, dlen);
+ curlx_dyn_reset(host);
+ result = curlx_dyn_addn(host, decoded, dlen);
free(decoded);
if(result)
return cc2cu(result);
@@ -802,7 +699,7 @@ static CURLUcode parse_authority(struct Curl_URL *u,
if(uc)
goto out;
- result = Curl_dyn_addn(host, auth + offset, authlen - offset);
+ result = curlx_dyn_addn(host, auth + offset, authlen - offset);
if(result) {
uc = cc2cu(result);
goto out;
@@ -812,19 +709,19 @@ static CURLUcode parse_authority(struct Curl_URL *u,
if(uc)
goto out;
- if(!Curl_dyn_len(host))
+ if(!curlx_dyn_len(host))
return CURLUE_NO_HOST;
switch(ipv4_normalize(host)) {
case HOST_IPV4:
break;
case HOST_IPV6:
- uc = ipv6_parse(u, Curl_dyn_ptr(host), Curl_dyn_len(host));
+ uc = ipv6_parse(u, curlx_dyn_ptr(host), curlx_dyn_len(host));
break;
case HOST_NAME:
uc = urldecode_host(host);
if(!uc)
- uc = hostname_check(u, Curl_dyn_ptr(host), Curl_dyn_len(host));
+ uc = hostname_check(u, curlx_dyn_ptr(host), curlx_dyn_len(host));
break;
case HOST_ERROR:
uc = CURLUE_OUT_OF_MEMORY;
@@ -845,15 +742,15 @@ CURLUcode Curl_url_set_authority(CURLU *u, const char *authority)
struct dynbuf host;
DEBUGASSERT(authority);
- Curl_dyn_init(&host, CURL_MAX_INPUT_LENGTH);
+ curlx_dyn_init(&host, CURL_MAX_INPUT_LENGTH);
result = parse_authority(u, authority, strlen(authority),
CURLU_DISALLOW_USER, &host, !!u->scheme);
if(result)
- Curl_dyn_free(&host);
+ curlx_dyn_free(&host);
else {
free(u->host);
- u->host = Curl_dyn_ptr(&host);
+ u->host = curlx_dyn_ptr(&host);
}
return result;
}
@@ -863,6 +760,25 @@ CURLUcode Curl_url_set_authority(CURLU *u, const char *authority)
* https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4
*/
+static bool is_dot(const char **str, size_t *clen)
+{
+ const char *p = *str;
+ if(*p == '.') {
+ (*str)++;
+ (*clen)--;
+ return TRUE;
+ }
+ else if((*clen >= 3) &&
+ (p[0] == '%') && (p[1] == '2') && ((p[2] | 0x20) == 'e')) {
+ *str += 3;
+ *clen -= 3;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+#define ISSLASH(x) ((x) == '/')
+
/*
* dedotdotify()
* @unittest: 1395
@@ -871,8 +787,7 @@ CURLUcode Curl_url_set_authority(CURLU *u, const char *authority)
* passed in and strips them off according to the rules in RFC 3986 section
* 5.2.4.
*
- * The function handles a query part ('?' + stuff) appended but it expects
- * that fragments ('#' + stuff) have already been cut off.
+ * The function handles a path. It should not contain the query nor fragment.
*
* RETURNS
*
@@ -881,112 +796,109 @@ CURLUcode Curl_url_set_authority(CURLU *u, const char *authority)
UNITTEST int dedotdotify(const char *input, size_t clen, char **outp);
UNITTEST int dedotdotify(const char *input, size_t clen, char **outp)
{
- char *outptr;
- const char *endp = &input[clen];
- char *out;
+ struct dynbuf out;
+ CURLcode result = CURLE_OK;
*outp = NULL;
/* the path always starts with a slash, and a slash has not dot */
- if((clen < 2) || !memchr(input, '.', clen))
+ if(clen < 2)
return 0;
- out = malloc(clen + 1);
- if(!out)
- return 1; /* out of memory */
-
- *out = 0; /* null-terminates, for inputs like "./" */
- outptr = out;
-
- do {
- bool dotdot = TRUE;
- if(*input == '.') {
- /* A. If the input buffer begins with a prefix of "../" or "./", then
- remove that prefix from the input buffer; otherwise, */
+ curlx_dyn_init(&out, clen + 1);
+
+ /* A. If the input buffer begins with a prefix of "../" or "./", then
+ remove that prefix from the input buffer; otherwise, */
+ if(is_dot(&input, &clen)) {
+ const char *p = input;
+ size_t blen = clen;
+
+ if(!clen)
+ /* . [end] */
+ goto end;
+ else if(ISSLASH(*p)) {
+ /* one dot followed by a slash */
+ input = p + 1;
+ clen--;
+ }
- if(!strncmp("./", input, 2)) {
- input += 2;
- clen -= 2;
+ /* D. if the input buffer consists only of "." or "..", then remove
+ that from the input buffer; otherwise, */
+ else if(is_dot(&p, &blen)) {
+ if(!blen)
+ /* .. [end] */
+ goto end;
+ else if(ISSLASH(*p)) {
+ /* ../ */
+ input = p + 1;
+ clen = blen - 1;
}
- else if(!strncmp("../", input, 3)) {
- input += 3;
- clen -= 3;
- }
- /* D. if the input buffer consists only of "." or "..", then remove
- that from the input buffer; otherwise, */
-
- else if(!strcmp(".", input) || !strcmp("..", input) ||
- !strncmp(".?", input, 2) || !strncmp("..?", input, 3)) {
- *out = 0;
- break;
- }
- else
- dotdot = FALSE;
}
- else if(*input == '/') {
+ }
+
+ while(clen && !result) { /* until end of path content */
+ if(ISSLASH(*input)) {
+ const char *p = &input[1];
+ size_t blen = clen - 1;
/* B. if the input buffer begins with a prefix of "/./" or "/.", where
"." is a complete path segment, then replace that prefix with "/" in
the input buffer; otherwise, */
- if(!strncmp("/./", input, 3)) {
- input += 2;
- clen -= 2;
- }
- else if(!strcmp("/.", input) || !strncmp("/.?", input, 3)) {
- *outptr++ = '/';
- *outptr = 0;
- break;
- }
-
- /* C. if the input buffer begins with a prefix of "/../" or "/..",
- where ".." is a complete path segment, then replace that prefix with
- "/" in the input buffer and remove the last segment and its
- preceding "/" (if any) from the output buffer; otherwise, */
-
- else if(!strncmp("/../", input, 4)) {
- input += 3;
- clen -= 3;
- /* remove the last segment from the output buffer */
- while(outptr > out) {
- outptr--;
- if(*outptr == '/')
- break;
+ if(is_dot(&p, &blen)) {
+ if(!blen) { /* /. */
+ result = curlx_dyn_addn(&out, "/", 1);
+ break;
}
- *outptr = 0; /* null-terminate where it stops */
- }
- else if(!strcmp("/..", input) || !strncmp("/..?", input, 4)) {
- /* remove the last segment from the output buffer */
- while(outptr > out) {
- outptr--;
- if(*outptr == '/')
- break;
+ else if(ISSLASH(*p)) { /* /./ */
+ input = p;
+ clen = blen;
+ continue;
+ }
+
+ /* C. if the input buffer begins with a prefix of "/../" or "/..",
+ where ".." is a complete path segment, then replace that prefix
+ with "/" in the input buffer and remove the last segment and its
+ preceding "/" (if any) from the output buffer; otherwise, */
+ else if(is_dot(&p, &blen) && (ISSLASH(*p) || !blen)) {
+ /* remove the last segment from the output buffer */
+ size_t len = curlx_dyn_len(&out);
+ if(len) {
+ char *ptr = curlx_dyn_ptr(&out);
+ char *last = memrchr(ptr, '/', len);
+ if(last)
+ /* trim the output at the slash */
+ curlx_dyn_setlen(&out, last - ptr);
+ }
+
+ if(blen) { /* /../ */
+ input = p;
+ clen = blen;
+ continue;
+ }
+ result = curlx_dyn_addn(&out, "/", 1);
+ break;
}
- *outptr++ = '/';
- *outptr = 0; /* null-terminate where it stops */
- break;
}
- else
- dotdot = FALSE;
- }
- else
- dotdot = FALSE;
-
- if(!dotdot) {
- /* E. move the first path segment in the input buffer to the end of
- the output buffer, including the initial "/" character (if any) and
- any subsequent characters up to, but not including, the next "/"
- character or the end of the input buffer. */
-
- do {
- *outptr++ = *input++;
- clen--;
- } while(*input && (*input != '/') && (*input != '?'));
- *outptr = 0;
}
- /* continue until end of path */
- } while(input < endp);
+ /* E. move the first path segment in the input buffer to the end of
+ the output buffer, including the initial "/" character (if any) and
+ any subsequent characters up to, but not including, the next "/"
+ character or the end of the input buffer. */
- *outp = out;
- return 0; /* success */
+ result = curlx_dyn_addn(&out, input, 1);
+ input++;
+ clen--;
+ }
+end:
+ if(!result) {
+ if(curlx_dyn_len(&out))
+ *outp = curlx_dyn_ptr(&out);
+ else {
+ *outp = strdup("");
+ if(!*outp)
+ return 1;
+ }
+ }
+ return result ? 1 : 0; /* success */
}
static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
@@ -1004,9 +916,9 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
DEBUGASSERT(url);
- Curl_dyn_init(&host, CURL_MAX_INPUT_LENGTH);
+ curlx_dyn_init(&host, CURL_MAX_INPUT_LENGTH);
- result = junkscan(url, &urllen, flags);
+ result = Curl_junkscan(url, &urllen, !!(flags & CURLU_ALLOW_SPACE));
if(result)
goto fail;
@@ -1024,7 +936,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
}
/* path has been allocated large enough to hold this */
- path = (char *)&url[5];
+ path = &url[5];
pathlen = urllen - 5;
u->scheme = strdup("file");
@@ -1070,7 +982,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
ptr += 9; /* now points to the slash after the host */
}
else {
-#if defined(_WIN32)
+#ifdef _WIN32
size_t len;
/* the hostname, NetBIOS computer name, can not contain disallowed
@@ -1084,7 +996,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
len = path - ptr;
if(len) {
- CURLcode code = Curl_dyn_addn(&host, ptr, len);
+ CURLcode code = curlx_dyn_addn(&host, ptr, len);
if(code) {
result = cc2cu(code);
goto fail;
@@ -1108,7 +1020,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
if(!uncpath)
/* no host for file: URLs by default */
- Curl_dyn_reset(&host);
+ curlx_dyn_reset(&host);
#if !defined(_WIN32) && !defined(MSDOS) && !defined(__CYGWIN__)
/* Do not allow Windows drive letters when not in Windows.
@@ -1194,7 +1106,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
goto fail;
if((flags & CURLU_GUESS_SCHEME) && !schemep) {
- const char *hostname = Curl_dyn_ptr(&host);
+ const char *hostname = curlx_dyn_ptr(&host);
/* legacy curl-style guess based on hostname */
if(checkprefix("ftp.", hostname))
schemep = "ftp";
@@ -1221,7 +1133,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
}
else if(flags & CURLU_NO_AUTHORITY) {
/* allowed to be empty. */
- if(Curl_dyn_add(&host, "")) {
+ if(curlx_dyn_add(&host, "")) {
result = CURLUE_OUT_OF_MEMORY;
goto fail;
}
@@ -1240,11 +1152,11 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
/* skip the leading '#' in the copy but include the terminating null */
if(flags & CURLU_URLENCODE) {
struct dynbuf enc;
- Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
+ curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
result = urlencode_str(&enc, fragment + 1, fraglen - 1, TRUE, FALSE);
if(result)
goto fail;
- u->fragment = Curl_dyn_ptr(&enc);
+ u->fragment = curlx_dyn_ptr(&enc);
}
else {
u->fragment = Curl_memdup0(fragment + 1, fraglen - 1);
@@ -1267,12 +1179,12 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
if(qlen > 1) {
if(flags & CURLU_URLENCODE) {
struct dynbuf enc;
- Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
+ curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
/* skip the leading question mark */
result = urlencode_str(&enc, query + 1, qlen - 1, TRUE, TRUE);
if(result)
goto fail;
- u->query = Curl_dyn_ptr(&enc);
+ u->query = curlx_dyn_ptr(&enc);
}
else {
u->query = Curl_memdup0(query + 1, qlen - 1);
@@ -1294,12 +1206,12 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
if(pathlen && (flags & CURLU_URLENCODE)) {
struct dynbuf enc;
- Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
+ curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
result = urlencode_str(&enc, path, pathlen, TRUE, FALSE);
if(result)
goto fail;
- pathlen = Curl_dyn_len(&enc);
- path = u->path = Curl_dyn_ptr(&enc);
+ pathlen = curlx_dyn_len(&enc);
+ path = u->path = curlx_dyn_ptr(&enc);
}
if(pathlen <= 1) {
@@ -1322,7 +1234,7 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
if(!(flags & CURLU_PATH_AS_IS)) {
/* remove ../ and ./ sequences according to RFC3986 */
char *dedot;
- int err = dedotdotify((char *)path, pathlen, &dedot);
+ int err = dedotdotify(path, pathlen, &dedot);
if(err) {
result = CURLUE_OUT_OF_MEMORY;
goto fail;
@@ -1334,11 +1246,11 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags)
}
}
- u->host = Curl_dyn_ptr(&host);
+ u->host = curlx_dyn_ptr(&host);
return result;
fail:
- Curl_dyn_free(&host);
+ curlx_dyn_free(&host);
free_urlhandle(u);
return result;
}
@@ -1500,7 +1412,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what,
break;
case CURLUPART_URL: {
char *url;
- char *scheme;
+ const char *scheme;
char *options = u->options;
char *port = u->port;
char *allochost = NULL;
@@ -1512,8 +1424,10 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what,
punycode = (flags & CURLU_PUNYCODE) ? 1 : 0;
depunyfy = (flags & CURLU_PUNY2IDN) ? 1 : 0;
if(u->scheme && strcasecompare("file", u->scheme)) {
- url = aprintf("file://%s%s%s",
+ url = aprintf("file://%s%s%s%s%s",
u->path,
+ show_query ? "?": "",
+ u->query ? u->query : "",
show_fragment ? "#": "",
u->fragment ? u->fragment : "");
}
@@ -1525,7 +1439,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what,
if(u->scheme)
scheme = u->scheme;
else if(flags & CURLU_DEFAULT_SCHEME)
- scheme = (char *) DEFAULT_SCHEME;
+ scheme = DEFAULT_SCHEME;
else
return CURLUE_NO_SCHEME;
@@ -1554,11 +1468,11 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what,
/* make it '[ host %25 zoneid ]' */
struct dynbuf enc;
size_t hostlen = strlen(u->host);
- Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
- if(Curl_dyn_addf(&enc, "%.*s%%25%s]", (int)hostlen - 1, u->host,
- u->zoneid))
+ curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
+ if(curlx_dyn_addf(&enc, "%.*s%%25%s]", (int)hostlen - 1, u->host,
+ u->zoneid))
return CURLUE_OUT_OF_MEMORY;
- allochost = Curl_dyn_ptr(&enc);
+ allochost = curlx_dyn_ptr(&enc);
}
}
else if(urlencode) {
@@ -1579,7 +1493,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what,
}
}
else if(depunyfy) {
- if(Curl_is_ASCII_name(u->host) && !strncmp("xn--", u->host, 4)) {
+ if(Curl_is_ASCII_name(u->host)) {
#ifndef USE_IDN
return CURLUE_LACKS_IDN;
#else
@@ -1655,12 +1569,12 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what,
if(urlencode) {
struct dynbuf enc;
CURLUcode uc;
- Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
+ curlx_dyn_init(&enc, CURL_MAX_INPUT_LENGTH);
uc = urlencode_str(&enc, *part, partlen, TRUE, what == CURLUPART_QUERY);
if(uc)
return uc;
free(*part);
- *part = Curl_dyn_ptr(&enc);
+ *part = curlx_dyn_ptr(&enc);
}
else if(punycode) {
if(!Curl_is_ASCII_name(u->host)) {
@@ -1678,7 +1592,7 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what,
}
}
else if(depunyfy) {
- if(Curl_is_ASCII_name(u->host) && !strncmp("xn--", u->host, 4)) {
+ if(Curl_is_ASCII_name(u->host)) {
#ifndef USE_IDN
return CURLUE_LACKS_IDN;
#else
@@ -1699,6 +1613,89 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what,
return ifmissing;
}
+static CURLUcode set_url_scheme(CURLU *u, const char *scheme,
+ unsigned int flags)
+{
+ size_t plen = strlen(scheme);
+ const char *s = scheme;
+ if((plen > MAX_SCHEME_LEN) || (plen < 1))
+ /* too long or too short */
+ return CURLUE_BAD_SCHEME;
+ /* verify that it is a fine scheme */
+ if(!(flags & CURLU_NON_SUPPORT_SCHEME) && !Curl_get_scheme_handler(scheme))
+ return CURLUE_UNSUPPORTED_SCHEME;
+ if(ISALPHA(*s)) {
+ /* ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */
+ while(--plen) {
+ if(ISALNUM(*s) || (*s == '+') || (*s == '-') || (*s == '.'))
+ s++; /* fine */
+ else
+ return CURLUE_BAD_SCHEME;
+ }
+ }
+ else
+ return CURLUE_BAD_SCHEME;
+ u->guessed_scheme = FALSE;
+ return CURLUE_OK;
+}
+
+static CURLUcode set_url_port(CURLU *u, const char *provided_port)
+{
+ char *tmp;
+ curl_off_t port;
+ if(!ISDIGIT(provided_port[0]))
+ /* not a number */
+ return CURLUE_BAD_PORT_NUMBER;
+ if(curlx_str_number(&provided_port, &port, 0xffff) || *provided_port)
+ /* weirdly provided number, not good! */
+ return CURLUE_BAD_PORT_NUMBER;
+ tmp = aprintf("%" CURL_FORMAT_CURL_OFF_T, port);
+ if(!tmp)
+ return CURLUE_OUT_OF_MEMORY;
+ free(u->port);
+ u->port = tmp;
+ u->portnum = (unsigned short)port;
+ return CURLUE_OK;
+}
+
+static CURLUcode set_url(CURLU *u, const char *url, size_t part_size,
+ unsigned int flags)
+{
+ /*
+ * Allow a new URL to replace the existing (if any) contents.
+ *
+ * If the existing contents is enough for a URL, allow a relative URL to
+ * replace it.
+ */
+ CURLUcode uc;
+ char *oldurl = NULL;
+
+ if(!part_size) {
+ /* a blank URL is not a valid URL unless we already have a complete one
+ and this is a redirect */
+ if(!curl_url_get(u, CURLUPART_URL, &oldurl, flags)) {
+ /* success, meaning the "" is a fine relative URL, but nothing
+ changes */
+ free(oldurl);
+ return CURLUE_OK;
+ }
+ return CURLUE_MALFORMED_INPUT;
+ }
+
+ /* if the new thing is absolute or the old one is not (we could not get an
+ * absolute URL in 'oldurl'), then replace the existing with the new. */
+ if(Curl_is_absolute_url(url, NULL, 0,
+ flags & (CURLU_GUESS_SCHEME|CURLU_DEFAULT_SCHEME))
+ || curl_url_get(u, CURLUPART_URL, &oldurl, flags)) {
+ return parseurl_and_replace(url, u, flags);
+ }
+ DEBUGASSERT(oldurl); /* it is set here */
+ /* apply the relative part to create a new URL */
+ uc = redirect_url(oldurl, url, u, flags);
+ free(oldurl);
+ return uc;
+}
+
CURLUcode curl_url_set(CURLU *u, CURLUPart what,
const char *part, unsigned int flags)
{
@@ -1772,28 +1769,11 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
switch(what) {
case CURLUPART_SCHEME: {
- size_t plen = strlen(part);
- const char *s = part;
- if((plen > MAX_SCHEME_LEN) || (plen < 1))
- /* too long or too short */
- return CURLUE_BAD_SCHEME;
- /* verify that it is a fine scheme */
- if(!(flags & CURLU_NON_SUPPORT_SCHEME) && !Curl_get_scheme_handler(part))
- return CURLUE_UNSUPPORTED_SCHEME;
+ CURLUcode status = set_url_scheme(u, part, flags);
+ if(status)
+ return status;
storep = &u->scheme;
urlencode = FALSE; /* never */
- if(ISALPHA(*s)) {
- /* ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */
- while(--plen) {
- if(ISALNUM(*s) || (*s == '+') || (*s == '-') || (*s == '.'))
- s++; /* fine */
- else
- return CURLUE_BAD_SCHEME;
- }
- }
- else
- return CURLUE_BAD_SCHEME;
- u->guessed_scheme = FALSE;
break;
}
case CURLUPART_USER:
@@ -1813,26 +1793,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
storep = &u->zoneid;
break;
case CURLUPART_PORT:
- if(!ISDIGIT(part[0]))
- /* not a number */
- return CURLUE_BAD_PORT_NUMBER;
- else {
- char *tmp;
- char *endp;
- unsigned long port;
- errno = 0;
- port = strtoul(part, &endp, 10); /* must be decimal */
- if(errno || (port > 0xffff) || *endp)
- /* weirdly provided number, not good! */
- return CURLUE_BAD_PORT_NUMBER;
- tmp = strdup(part);
- if(!tmp)
- return CURLUE_OUT_OF_MEMORY;
- free(u->port);
- u->port = tmp;
- u->portnum = (unsigned short)port;
- return CURLUE_OK;
- }
+ return set_url_port(u, part);
case CURLUPART_PATH:
urlskipslash = TRUE;
leadingslash = TRUE; /* enforce */
@@ -1850,41 +1811,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
u->fragment_present = TRUE;
break;
case CURLUPART_URL: {
- /*
- * Allow a new URL to replace the existing (if any) contents.
- *
- * If the existing contents is enough for a URL, allow a relative URL to
- * replace it.
- */
- CURLcode result;
- CURLUcode uc;
- char *oldurl;
- char *redired_url;
-
- if(!nalloc)
- /* a blank URL is not a valid URL */
- return CURLUE_MALFORMED_INPUT;
-
- /* if the new thing is absolute or the old one is not
- * (we could not get an absolute URL in 'oldurl'),
- * then replace the existing with the new. */
- if(Curl_is_absolute_url(part, NULL, 0,
- flags & (CURLU_GUESS_SCHEME|
- CURLU_DEFAULT_SCHEME))
- || curl_url_get(u, CURLUPART_URL, &oldurl, flags)) {
- return parseurl_and_replace(part, u, flags);
- }
-
- /* apply the relative part to create a new URL
- * and replace the existing one with it. */
- result = concat_url(oldurl, part, &redired_url);
- free(oldurl);
- if(result)
- return cc2cu(result);
-
- uc = parseurl_and_replace(redired_url, u, flags);
- free(redired_url);
- return uc;
+ return set_url(u, part, nalloc, flags);
}
default:
return CURLUE_UNKNOWN_PART;
@@ -1893,10 +1820,10 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
{
const char *newp;
struct dynbuf enc;
- Curl_dyn_init(&enc, nalloc * 3 + 1 + leadingslash);
+ curlx_dyn_init(&enc, nalloc * 3 + 1 + leadingslash);
if(leadingslash && (part[0] != '/')) {
- CURLcode result = Curl_dyn_addn(&enc, "/", 1);
+ CURLcode result = curlx_dyn_addn(&enc, "/", 1);
if(result)
return cc2cu(result);
}
@@ -1906,7 +1833,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
for(i = (const unsigned char *)part; *i; i++) {
CURLcode result;
if((*i == ' ') && plusencode) {
- result = Curl_dyn_addn(&enc, "+", 1);
+ result = curlx_dyn_addn(&enc, "+", 1);
if(result)
return CURLUE_OUT_OF_MEMORY;
}
@@ -1916,15 +1843,14 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
if((*i == '=') && equalsencode)
/* only skip the first equals sign */
equalsencode = FALSE;
- result = Curl_dyn_addn(&enc, i, 1);
+ result = curlx_dyn_addn(&enc, i, 1);
if(result)
return cc2cu(result);
}
else {
- char out[3]={'%'};
- out[1] = hexdigits[*i >> 4];
- out[2] = hexdigits[*i & 0xf];
- result = Curl_dyn_addn(&enc, out, 3);
+ unsigned char out[3]={'%'};
+ Curl_hexbyte(&out[1], *i, TRUE);
+ result = curlx_dyn_addn(&enc, out, 3);
if(result)
return cc2cu(result);
}
@@ -1932,10 +1858,10 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
}
else {
char *p;
- CURLcode result = Curl_dyn_add(&enc, part);
+ CURLcode result = curlx_dyn_add(&enc, part);
if(result)
return cc2cu(result);
- p = Curl_dyn_ptr(&enc);
+ p = curlx_dyn_ptr(&enc);
while(*p) {
/* make sure percent encoded are lower case */
if((*p == '%') && ISXDIGIT(p[1]) && ISXDIGIT(p[2]) &&
@@ -1948,7 +1874,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
p++;
}
}
- newp = Curl_dyn_ptr(&enc);
+ newp = curlx_dyn_ptr(&enc);
if(appendquery && newp) {
/* Append the 'newp' string onto the old query. Add a '&' separator if
@@ -1958,29 +1884,29 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what,
bool addamperand = querylen && (u->query[querylen -1] != '&');
if(querylen) {
struct dynbuf qbuf;
- Curl_dyn_init(&qbuf, CURL_MAX_INPUT_LENGTH);
+ curlx_dyn_init(&qbuf, CURL_MAX_INPUT_LENGTH);
- if(Curl_dyn_addn(&qbuf, u->query, querylen)) /* add original query */
+ if(curlx_dyn_addn(&qbuf, u->query, querylen)) /* add original query */
goto nomem;
if(addamperand) {
- if(Curl_dyn_addn(&qbuf, "&", 1))
+ if(curlx_dyn_addn(&qbuf, "&", 1))
goto nomem;
}
- if(Curl_dyn_add(&qbuf, newp))
+ if(curlx_dyn_add(&qbuf, newp))
goto nomem;
- Curl_dyn_free(&enc);
+ curlx_dyn_free(&enc);
free(*storep);
- *storep = Curl_dyn_ptr(&qbuf);
+ *storep = curlx_dyn_ptr(&qbuf);
return CURLUE_OK;
nomem:
- Curl_dyn_free(&enc);
+ curlx_dyn_free(&enc);
return CURLUE_OUT_OF_MEMORY;
}
}
else if(what == CURLUPART_HOST) {
- size_t n = Curl_dyn_len(&enc);
+ size_t n = curlx_dyn_len(&enc);
if(!n && (flags & CURLU_NO_AUTHORITY)) {
/* Skip hostname check, it is allowed to be empty. */
}
@@ -1999,17 +1925,17 @@ nomem:
bad = TRUE;
free(decoded);
}
- else if(hostname_check(u, (char *)newp, n))
+ else if(hostname_check(u, (char *)CURL_UNCONST(newp), n))
bad = TRUE;
if(bad) {
- Curl_dyn_free(&enc);
+ curlx_dyn_free(&enc);
return CURLUE_BAD_HOSTNAME;
}
}
}
free(*storep);
- *storep = (char *)newp;
+ *storep = (char *)CURL_UNCONST(newp);
}
return CURLUE_OK;
}
diff --git a/libs/libcurl/src/urldata.h b/libs/libcurl/src/urldata.h
index 7833211422..f30e97774f 100644
--- a/libs/libcurl/src/urldata.h
+++ b/libs/libcurl/src/urldata.h
@@ -95,17 +95,10 @@ typedef unsigned int curl_prot_t;
in the API */
#define CURLPROTO_MASK (0x3ffffff)
-#define DICT_MATCH "/MATCH:"
-#define DICT_MATCH2 "/M:"
-#define DICT_MATCH3 "/FIND:"
-#define DICT_DEFINE "/DEFINE:"
-#define DICT_DEFINE2 "/D:"
-#define DICT_DEFINE3 "/LOOKUP:"
-
#define CURL_DEFAULT_USER "anonymous"
#define CURL_DEFAULT_PASSWORD "ftp@example.com"
-#if !defined(_WIN32) && !defined(MSDOS) && !defined(__EMX__)
+#if !defined(_WIN32) && !defined(MSDOS)
/* do FTP line-end CRLF => LF conversions on platforms that prefer LF-only. It
also means: keep CRLF line endings on the CRLF platforms */
#define CURL_PREFER_LF_LINEENDS
@@ -152,7 +145,7 @@ typedef unsigned int curl_prot_t;
#include <netinet/in6.h>
#endif
-#include "timeval.h"
+#include "curlx/timeval.h"
#include <curl/curl.h>
@@ -160,7 +153,7 @@ typedef unsigned int curl_prot_t;
#include "hostip.h"
#include "hash.h"
#include "splay.h"
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
#include "dynhds.h"
#include "request.h"
#include "netrc.h"
@@ -180,16 +173,8 @@ typedef ssize_t (Curl_recv)(struct Curl_easy *data, /* transfer */
size_t len, /* max amount to read */
CURLcode *err); /* error to return */
-#ifdef USE_HYPER
-typedef CURLcode (*Curl_datastream)(struct Curl_easy *data,
- struct connectdata *conn,
- int *didwhat,
- int select_res);
-#endif
-
#include "mime.h"
#include "imap.h"
-#include "pop3.h"
#include "smtp.h"
#include "ftp.h"
#include "file.h"
@@ -200,7 +185,6 @@ typedef CURLcode (*Curl_datastream)(struct Curl_easy *data,
#include "mqtt.h"
#include "ftplistparser.h"
#include "multihandle.h"
-#include "c-hyper.h"
#include "cf-socket.h"
#ifdef HAVE_GSSAPI
@@ -271,21 +255,7 @@ enum protection_level {
/* SSL backend-specific data; declared differently by each SSL backend */
struct ssl_backend_data;
-
-typedef enum {
- CURL_SSL_PEER_DNS,
- CURL_SSL_PEER_IPV4,
- CURL_SSL_PEER_IPV6
-} ssl_peer_type;
-
-struct ssl_peer {
- char *hostname; /* hostname for verification */
- char *dispname; /* display version of hostname */
- char *sni; /* SNI version of hostname or NULL if not usable */
- ssl_peer_type type; /* type of the peer information */
- int port; /* port we are talking to */
- int transport; /* one of TRNSPRT_* defines */
-};
+struct Curl_ssl_scache_entry;
struct ssl_primary_config {
char *CApath; /* certificate dir (does not work on Windows) */
@@ -294,6 +264,7 @@ struct ssl_primary_config {
char *clientcert;
char *cipher_list; /* list of ciphers to use */
char *cipher_list13; /* list of TLS 1.3 cipher suites to use */
+ char *signature_algorithms; /* list of signature algorithms to use */
char *pinned_key;
char *CRLfile; /* CRL to check certificate revocation */
struct curl_blob *cert_blob;
@@ -304,8 +275,8 @@ struct ssl_primary_config {
char *password; /* TLS password (for, e.g., SRP) */
#endif
char *curves; /* list of curves to use */
- unsigned char ssl_options; /* the CURLOPT_SSL_OPTIONS bitmask */
unsigned int version_max; /* max supported version the client wants to use */
+ unsigned char ssl_options; /* the CURLOPT_SSL_OPTIONS bitmask */
unsigned char version; /* what version the client wants to use */
BIT(verifypeer); /* set TRUE if this is desired */
BIT(verifyhost); /* set TRUE if CN/SAN must match hostname */
@@ -337,28 +308,9 @@ struct ssl_config_data {
};
struct ssl_general_config {
- size_t max_ssl_sessions; /* SSL session id cache size */
int ca_cache_timeout; /* Certificate store cache timeout (seconds) */
};
-typedef void Curl_ssl_sessionid_dtor(void *sessionid, size_t idsize);
-
-/* information stored about one single SSL session */
-struct Curl_ssl_session {
- char *name; /* hostname for which this ID was used */
- char *conn_to_host; /* hostname for the connection (may be NULL) */
- const char *scheme; /* protocol scheme used */
- char *alpn; /* APLN TLS negotiated protocol string */
- void *sessionid; /* as returned from the SSL layer */
- size_t idsize; /* if known, otherwise 0 */
- Curl_ssl_sessionid_dtor *sessionid_free; /* free `sessionid` callback */
- long age; /* just a number, the higher the more recent */
- int remote_port; /* remote port */
- int conn_to_port; /* remote port for the connection (may be -1) */
- int transport; /* TCP or QUIC */
- struct ssl_primary_config ssl_config; /* setup for this session */
-};
-
#ifdef USE_WINDOWS_SSPI
#include "curl_sspi.h"
#endif
@@ -554,7 +506,6 @@ struct ConnectBits {
#ifdef USE_UNIX_SOCKETS
BIT(abstract_unix_socket);
#endif
- BIT(tls_upgraded);
BIT(sock_accepted); /* TRUE if the SECONDARYSOCKET was created with
accept() */
BIT(parallel_connect); /* set TRUE when a parallel connect attempt has
@@ -604,21 +555,6 @@ struct hostname {
#define CURL_WANT_RECV(data) \
(((data)->req.keepon & KEEP_RECVBITS) == KEEP_RECV)
-#if defined(CURLRES_ASYNCH) || !defined(CURL_DISABLE_DOH)
-#define USE_CURL_ASYNC
-struct Curl_async {
- char *hostname;
- struct Curl_dns_entry *dns;
- struct thread_data *tdata;
- void *resolver; /* resolver state, if it is used in the URL state -
- ares_channel e.g. */
- int port;
- int status; /* if done is TRUE, this is the status from the callback */
- BIT(done); /* set TRUE when the lookup is complete */
-};
-
-#endif
-
#define FIRSTSOCKET 0
#define SECONDARYSOCKET 1
@@ -717,6 +653,12 @@ struct Curl_handler {
/* attach() attaches this transfer to this connection */
void (*attach)(struct Curl_easy *data, struct connectdata *conn);
+ /* return CURLE_OK if a redirect to `newurl` should be followed,
+ CURLE_TOO_MANY_REDIRECTS otherwise. May alter `data` to change
+ the way the follow request is performed. */
+ CURLcode (*follow)(struct Curl_easy *data, const char *newurl,
+ followtype type);
+
int defport; /* Default port. */
curl_prot_t protocol; /* See CURLPROTO_* - this needs to be the single
specific protocol bit */
@@ -777,8 +719,6 @@ struct proxy_info {
char *passwd; /* proxy password string, allocated */
};
-struct ldapconninfo;
-
#define TRNSPRT_TCP 3
#define TRNSPRT_UDP 4
#define TRNSPRT_QUIC 5
@@ -790,6 +730,7 @@ struct ldapconninfo;
*/
struct connectdata {
struct Curl_llist_node cpool_node; /* conncache lists */
+ struct Curl_llist_node cshutdn_node; /* cshutdn list */
curl_closesocket_callback fclosesocket; /* function closing the socket(s) */
void *closesocket_client;
@@ -798,19 +739,19 @@ struct connectdata {
handle is still used by one or more easy handles and can only used by any
other easy handle without careful consideration (== only for
multiplexing) and it cannot be used by another multi handle! */
-#define CONN_INUSE(c) Curl_llist_count(&(c)->easyq)
+#define CONN_INUSE(c) (!Curl_uint_spbset_empty(&(c)->xfers_attached))
+#define CONN_ATTACHED(c) Curl_uint_spbset_count(&(c)->xfers_attached)
/**** Fields set when inited and not modified again */
curl_off_t connection_id; /* Contains a unique number to make it easier to
track the connections in the log output */
char *destination; /* string carrying normalized hostname+port+scope */
- size_t destination_len; /* strlen(destination) + 1 */
- /* 'dns_entry' is the particular host we use. This points to an entry in the
- DNS cache and it will not get pruned while locked. It gets unlocked in
- multi_done(). This entry will be NULL if the connection is reused as then
- there is no name resolve done. */
- struct Curl_dns_entry *dns_entry;
+ /* `meta_hash` is a general key-value store for implementations
+ * with the lifetime of the connection.
+ * Elements need to be added with their own destructor to be invoked when
+ * the connection is cleaned up (see Curl_hash_add2()).*/
+ struct Curl_hash meta_hash;
/* 'remote_addr' is the particular IP we connected to. it is owned, set
* and NULLed by the connected socket filter (if there is one). */
@@ -838,7 +779,6 @@ struct connectdata {
char *options; /* options string, allocated */
char *sasl_authzid; /* authorization identity string, allocated */
char *oauth_bearer; /* OAUTH2 bearer, allocated */
- struct curltime now; /* "current" time */
struct curltime created; /* creation time */
struct curltime lastused; /* when returned to the connection poolas idle */
curl_socket_t sock[2]; /* two sockets, the second is used for the data
@@ -850,9 +790,6 @@ struct connectdata {
struct curltime start[2]; /* when filter shutdown started */
unsigned int timeout_ms; /* 0 means no timeout */
} shutdown;
- /* Last pollset used in connection shutdown. Used to detect changes
- * for multi_socket API. */
- struct easy_pollset shutdown_poll;
struct ssl_primary_config ssl_config;
#ifndef CURL_DISABLE_PROXY
@@ -890,7 +827,14 @@ struct connectdata {
struct kerberos5data krb5; /* variables into the structure definition, */
#endif /* however, some of them are ftp specific. */
- struct Curl_llist easyq; /* List of easy handles using this connection */
+ struct uint_spbset xfers_attached; /* mids of attached transfers */
+ /* A connection cache from a SHARE might be used in several multi handles.
+ * We MUST not reuse connections that are running in another multi,
+ * for concurrency reasons. That multi might run in another thread.
+ * `attached_multi` is set by the first transfer attached and cleared
+ * when the last one is detached.
+ * NEVER call anything on this multi, just check for equality. */
+ struct Curl_multi *attached_multi;
/*************** Request - specific items ************/
#if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS)
@@ -919,53 +863,10 @@ struct connectdata {
struct negotiatedata proxyneg; /* state data for proxy Negotiate auth */
#endif
- union {
-#ifndef CURL_DISABLE_FTP
- struct ftp_conn ftpc;
-#endif
-#ifdef USE_SSH
- struct ssh_conn sshc;
-#endif
-#ifndef CURL_DISABLE_TFTP
- struct tftp_state_data *tftpc;
-#endif
-#ifndef CURL_DISABLE_IMAP
- struct imap_conn imapc;
-#endif
-#ifndef CURL_DISABLE_POP3
- struct pop3_conn pop3c;
-#endif
-#ifndef CURL_DISABLE_SMTP
- struct smtp_conn smtpc;
-#endif
-#ifndef CURL_DISABLE_RTSP
- struct rtsp_conn rtspc;
-#endif
-#ifndef CURL_DISABLE_SMB
- struct smb_conn smbc;
-#endif
-#ifdef USE_LIBRTMP
- void *rtmp;
-#endif
-#ifdef USE_OPENLDAP
- struct ldapconninfo *ldapc;
-#endif
-#ifndef CURL_DISABLE_MQTT
- struct mqtt_conn mqtt;
-#endif
-#ifndef CURL_DISABLE_WEBSOCKETS
- struct websocket *ws;
-#endif
- unsigned int unused:1; /* avoids empty union */
- } proto;
-
#ifdef USE_UNIX_SOCKETS
char *unix_domain_socket;
#endif
-#ifdef USE_HYPER
- /* if set, an alternative data transfer function */
- Curl_datastream datastream;
-#endif
+
/* When this connection is created, store the conditions for the local end
bind. This is stored before the actual bind and before any connection is
made and will serve the purpose of being used for comparison reasons so
@@ -994,7 +895,9 @@ struct connectdata {
#endif
unsigned char transport; /* one of the TRNSPRT_* defines */
unsigned char ip_version; /* copied from the Curl_easy at creation time */
- unsigned char httpversion; /* the HTTP version*10 reported by the server */
+ /* HTTP version last responded with by the server.
+ * 0 at start, then one of 09, 10, 11, etc. */
+ unsigned char httpversion_seen;
unsigned char connect_only;
unsigned char gssapi_delegation; /* inherited from set.gssapi_delegation */
};
@@ -1026,6 +929,8 @@ struct PureInfo {
curl_off_t request_size; /* the amount of bytes sent in the request(s) */
unsigned long proxyauthavail; /* what proxy auth types were announced */
unsigned long httpauthavail; /* what host auth types were announced */
+ unsigned long proxyauthpicked; /* selected proxy auth type */
+ unsigned long httpauthpicked; /* selected host auth type */
long numconnects; /* how many new connection did libcurl created */
char *contenttype; /* the content type of the object */
char *wouldredirect; /* URL this would have been redirected to if asked to */
@@ -1073,9 +978,6 @@ struct Progress {
curl_off_t current_speed; /* uses the currently fastest transfer */
curl_off_t earlydata_sent;
- int width; /* screen width at download start */
- int flags; /* see progress.h */
-
timediff_t timespent;
timediff_t t_postqueue;
@@ -1090,13 +992,18 @@ struct Progress {
struct curltime start;
struct curltime t_startsingle;
struct curltime t_startop;
+ struct curltime t_startqueue;
struct curltime t_acceptdata;
#define CURR_TIME (5 + 1) /* 6 entries for 5 seconds */
curl_off_t speeder[ CURR_TIME ];
struct curltime speeder_time[ CURR_TIME ];
- int speeder_c;
+ unsigned char speeder_c;
+ BIT(hide);
+ BIT(ul_size_known);
+ BIT(dl_size_known);
+ BIT(headers_out); /* when the headers have been written */
BIT(callback); /* set when progress callback is used */
BIT(is_t_startransfer_set);
};
@@ -1141,7 +1048,6 @@ struct Curl_data_prio_node {
/**
* Priority information for an easy handle in relation to others
* on the same connection.
- * TODO: we need to adapt it to the new priority scheme as defined in RFC 9218
*/
struct Curl_data_priority {
#ifdef USE_NGHTTP2
@@ -1172,6 +1078,7 @@ typedef enum {
EXPIRE_QUIC,
EXPIRE_FTP_ACCEPT,
EXPIRE_ALPN_EYEBALLS,
+ EXPIRE_SHUTDOWN,
EXPIRE_LAST /* not an actual timer, used as a marker only */
} expire_id;
@@ -1232,8 +1139,6 @@ struct UrlState {
curl_prot_t first_remote_protocol;
int retrycount; /* number of retries on a new connection */
- struct Curl_ssl_session *session; /* array of 'max_ssl_sessions' size */
- long sessionage; /* number of the most recent session */
int os_errno; /* filled in with errno whenever an error occurs */
long followlocation; /* redirect counter */
int requests; /* request counter: redirects + authentication retakes */
@@ -1247,6 +1152,8 @@ struct UrlState {
#endif
struct auth authhost; /* auth details for host */
struct auth authproxy; /* auth details for proxy */
+
+ struct Curl_dns_entry *dns[2]; /* DNS to connect FIRST/SECONDARY */
#ifdef USE_CURL_ASYNC
struct Curl_async async; /* asynchronous name resolver data */
#endif
@@ -1254,6 +1161,13 @@ struct UrlState {
#if defined(USE_OPENSSL)
/* void instead of ENGINE to avoid bleeding OpenSSL into this header */
void *engine;
+ /* void instead of OSSL_PROVIDER */
+ void *provider;
+ void *baseprov;
+ void *libctx;
+ char *propq; /* for a provider */
+
+ BIT(provider_loaded);
#endif /* USE_OPENSSL */
struct curltime expiretime; /* set this with Curl_expire() only */
struct Curl_tree timenode; /* for the splay stuff */
@@ -1309,10 +1223,6 @@ struct UrlState {
struct curl_slist *cookielist; /* list of cookie files set by
curl_easy_setopt(COOKIEFILE) calls */
#endif
-#ifdef USE_HYPER
- bool hconnect; /* set if a CONNECT request */
- CURLcode hresult; /* used to pass return codes back from hyper callbacks */
-#endif
#ifndef CURL_DISABLE_VERBOSE_STRINGS
struct curl_trc_feat *feat; /* opt. trace feature transfer is part of */
@@ -1348,10 +1258,9 @@ struct UrlState {
char *proxypasswd;
#endif
} aptr;
- unsigned char httpwant; /* when non-zero, a specific HTTP version requested
- to be used in the library's request(s) */
- unsigned char httpversion; /* the lowest HTTP version*10 reported by any
- server involved in this request */
+#ifndef CURL_DISABLE_HTTP
+ struct http_negotiation http_neg;
+#endif
unsigned char httpreq; /* Curl_HttpReq; what kind of HTTP request (if any)
is this */
unsigned char select_bits; /* != 0 -> bitmask of socket events for this
@@ -1396,6 +1305,7 @@ struct UrlState {
BIT(internal); /* internal: true if this easy handle was created for
internal use and the user does not have ownership of the
handle. */
+ BIT(http_ignorecustom); /* ignore custom method from now */
};
/*
@@ -1532,6 +1442,7 @@ enum dupstring {
#endif
STRING_ECH_CONFIG, /* CURLOPT_ECH_CONFIG */
STRING_ECH_PUBLIC, /* CURLOPT_ECH_PUBLIC */
+ STRING_SSL_SIGNATURE_ALGORITHMS, /* CURLOPT_SSL_SIGNATURE_ALGORITHMS */
/* -- end of null-terminated strings -- */
@@ -1558,10 +1469,6 @@ enum dupblob {
BLOB_LAST
};
-/* callback that gets called when this easy handle is completed within a multi
- handle. Only used for internally created transfers, like for example
- DoH. */
-typedef int (*multidone_func)(struct Curl_easy *easy, CURLcode result);
struct UserDefined {
FILE *err; /* the stderr user data goes here */
@@ -1570,23 +1477,15 @@ struct UserDefined {
void *out; /* CURLOPT_WRITEDATA */
void *in_set; /* CURLOPT_READDATA */
void *writeheader; /* write the header to this if non-NULL */
- unsigned short use_port; /* which port to use (when not using default) */
unsigned long httpauth; /* kind of HTTP authentication to use (bitmask) */
unsigned long proxyauth; /* kind of proxy authentication to use (bitmask) */
long maxredirs; /* maximum no. of http(s) redirects to follow, set to -1
for infinity */
-
void *postfields; /* if POST, set the fields' values here */
curl_seek_callback seek_func; /* function that seeks the input */
curl_off_t postfieldsize; /* if POST, this might have a size to use instead
of strlen(), and then the data *may* be binary
(contain zero bytes) */
-#ifndef CURL_DISABLE_BINDLOCAL
- unsigned short localport; /* local port number to bind to */
- unsigned short localportrange; /* number of additional port numbers to test
- in case the 'localport' one cannot be
- bind()ed */
-#endif
curl_write_callback fwrite_func; /* function that stores the output */
curl_write_callback fwrite_header; /* function that stores headers */
curl_write_callback fwrite_rtp; /* function that stores interleaved RTP */
@@ -1616,11 +1515,6 @@ struct UserDefined {
#endif
void *progress_client; /* pointer to pass to the progress callback */
void *ioctl_client; /* pointer to pass to the ioctl callback */
- unsigned int timeout; /* ms, 0 means no timeout */
- unsigned int connecttimeout; /* ms, 0 means default timeout */
- unsigned int happy_eyeballs_timeout; /* ms, 0 is a valid value */
- unsigned int server_response_timeout; /* ms, 0 means no timeout */
- unsigned int shutdowntimeout; /* ms, 0 means default timeout */
long maxage_conn; /* in seconds, max idle time to allow a connection that
is to be reused */
long maxlifetime_conn; /* in seconds, max time since creation to allow a
@@ -1648,10 +1542,6 @@ struct UserDefined {
struct curl_slist *connect_to; /* list of host:port mappings to override
the hostname and port to connect to */
time_t timevalue; /* what time to compare with */
- unsigned char timecondition; /* kind of time comparison: curl_TimeCond */
- unsigned char method; /* what kind of HTTP request: Curl_HttpReq */
- unsigned char httpwant; /* when non-zero, a specific HTTP version requested
- to be used in the library's request(s) */
struct ssl_config_data ssl; /* user defined SSL stuff */
#ifndef CURL_DISABLE_PROXY
struct ssl_config_data proxy_ssl; /* user defined SSL stuff for proxy */
@@ -1671,24 +1561,17 @@ struct UserDefined {
#ifndef CURL_DISABLE_HTTP
struct curl_slist *http200aliases; /* linked list of aliases for http200 */
#endif
- unsigned char ipver; /* the CURL_IPRESOLVE_* defines in the public header
- file 0 - whatever, 1 - v2, 2 - v6 */
curl_off_t max_filesize; /* Maximum file size to download */
#ifndef CURL_DISABLE_FTP
+ unsigned int accepttimeout; /* in milliseconds, 0 means no timeout */
unsigned char ftp_filemethod; /* how to get to a file: curl_ftpfile */
unsigned char ftpsslauth; /* what AUTH XXX to try: curl_ftpauth */
unsigned char ftp_ccc; /* FTP CCC options: curl_ftpccc */
- unsigned int accepttimeout; /* in milliseconds, 0 means no timeout */
#endif
#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH)
struct curl_slist *quote; /* after connection is established */
struct curl_slist *postquote; /* after the transfer */
struct curl_slist *prequote; /* before the transfer, after type */
- /* Despite the name, ftp_create_missing_dirs is for FTP(S) and SFTP
- 1 - create directories that do not exist
- 2 - the same but also allow MKD to fail once
- */
- unsigned char ftp_create_missing_dirs;
#endif
#ifdef USE_LIBSSH2
curl_sshhostkeycallback ssh_hostkeyfunc; /* hostkey check callback */
@@ -1700,9 +1583,6 @@ struct UserDefined {
int ssh_auth_types; /* allowed SSH auth types */
unsigned int new_directory_perms; /* when creating remote dirs */
#endif
-#ifndef CURL_DISABLE_NETRC
- unsigned char use_netrc; /* enum CURL_NETRC_OPTION values */
-#endif
unsigned int new_file_perms; /* when creating remote files */
char *str[STRING_LAST]; /* array of strings, pointing to allocated memory */
struct curl_blob *blobs[BLOB_LAST];
@@ -1726,10 +1606,12 @@ struct UserDefined {
void *fnmatch_data;
void *wildcardptr;
#endif
- /* GSS-API credential delegation, see the documentation of
- CURLOPT_GSSAPI_DELEGATION */
- unsigned char gssapi_delegation;
+ unsigned int timeout; /* ms, 0 means no timeout */
+ unsigned int connecttimeout; /* ms, 0 means default timeout */
+ unsigned int happy_eyeballs_timeout; /* ms, 0 is a valid value */
+ unsigned int server_response_timeout; /* ms, 0 means no timeout */
+ unsigned int shutdowntimeout; /* ms, 0 means default timeout */
int tcp_keepidle; /* seconds in idle before sending keepalive probe */
int tcp_keepintvl; /* seconds between TCP keepalive probes */
int tcp_keepcnt; /* maximum number of keepalive probes */
@@ -1742,27 +1624,59 @@ struct UserDefined {
before resolver start */
void *resolver_start_client; /* pointer to pass to resolver start callback */
long upkeep_interval_ms; /* Time between calls for connection upkeep. */
- multidone_func fmultidone;
-#ifndef CURL_DISABLE_DOH
- curl_off_t dohfor_mid; /* this is a DoH request for that transfer */
-#endif
CURLU *uh; /* URL handle for the current parsed URL */
#ifndef CURL_DISABLE_HTTP
void *trailer_data; /* pointer to pass to trailer data callback */
curl_trailer_callback trailer_callback; /* trailing data callback */
#endif
- char keep_post; /* keep POSTs as POSTs after a 30x request; each
- bit represents a request, from 301 to 303 */
#ifndef CURL_DISABLE_SMTP
struct curl_slist *mail_rcpt; /* linked list of mail recipients */
- BIT(mail_rcpt_allowfails); /* allow RCPT TO command to fail for some
- recipients */
#endif
unsigned int maxconnects; /* Max idle connections in the connection cache */
+#ifdef USE_ECH
+ int tls_ech; /* TLS ECH configuration */
+#endif
+ unsigned short use_port; /* which port to use (when not using default) */
+#ifndef CURL_DISABLE_BINDLOCAL
+ unsigned short localport; /* local port number to bind to */
+ unsigned short localportrange; /* number of additional port numbers to test
+ in case the 'localport' one cannot be
+ bind()ed */
+#endif
+#ifndef CURL_DISABLE_NETRC
+ unsigned char use_netrc; /* enum CURL_NETRC_OPTION values */
+#endif
+#if !defined(CURL_DISABLE_FTP) || defined(USE_SSH)
+ /* Despite the name, ftp_create_missing_dirs is for FTP(S) and SFTP
+ 1 - create directories that do not exist
+ 2 - the same but also allow MKD to fail once
+ */
+ unsigned char ftp_create_missing_dirs;
+#endif
unsigned char use_ssl; /* if AUTH TLS is to be attempted etc, for FTP or
IMAP or POP3 or others! (type: curl_usessl)*/
- unsigned char connect_only; /* make connection/request, then let
- application use the socket */
+ char keep_post; /* keep POSTs as POSTs after a 30x request; each
+ bit represents a request, from 301 to 303 */
+ unsigned char timecondition; /* kind of time comparison: curl_TimeCond */
+ unsigned char method; /* what kind of HTTP request: Curl_HttpReq */
+ unsigned char httpwant; /* when non-zero, a specific HTTP version requested
+ to be used in the library's request(s) */
+ unsigned char ipver; /* the CURL_IPRESOLVE_* defines in the public header
+ file 0 - whatever, 1 - v2, 2 - v6 */
+ unsigned char upload_flags; /* flags set by CURLOPT_UPLOAD_FLAGS */
+#ifdef HAVE_GSSAPI
+ /* GSS-API credential delegation, see the documentation of
+ CURLOPT_GSSAPI_DELEGATION */
+ unsigned char gssapi_delegation;
+#endif
+ unsigned char http_follow_mode; /* follow HTTP redirects */
+ BIT(connect_only); /* make connection/request, then let application use the
+ socket */
+ BIT(connect_only_ws); /* special websocket connect-only level */
+#ifndef CURL_DISABLE_SMTP
+ BIT(mail_rcpt_allowfails); /* allow RCPT TO command to fail for some
+ recipients */
+#endif
#ifndef CURL_DISABLE_MIME
BIT(mime_formescape);
#endif
@@ -1804,10 +1718,8 @@ struct UserDefined {
us */
BIT(wildcard_enabled); /* enable wildcard matching */
#endif
- BIT(hide_progress); /* do not use the progress meter */
BIT(http_fail_on_error); /* fail on HTTP error codes >= 400 */
BIT(http_keep_sending_on_error); /* for HTTP status codes >= 300 */
- BIT(http_follow_location); /* follow HTTP redirects */
BIT(http_transfer_encoding); /* request compressed HTTP transfer-encoding */
BIT(allow_auth_to_other_hosts);
BIT(include_header); /* include received protocol headers in data output */
@@ -1860,9 +1772,7 @@ struct UserDefined {
BIT(http09_allowed); /* allow HTTP/0.9 responses */
#ifndef CURL_DISABLE_WEBSOCKETS
BIT(ws_raw_mode);
-#endif
-#ifdef USE_ECH
- int tls_ech; /* TLS ECH configuration */
+ BIT(ws_no_auto_pong);
#endif
};
@@ -1872,14 +1782,11 @@ struct UserDefined {
#define IS_MIME_POST(a) FALSE
#endif
-struct Names {
- struct Curl_hash *hostcache;
- enum {
- HCACHE_NONE, /* not pointing to anything */
- HCACHE_MULTI, /* points to a shared one in the multi handle */
- HCACHE_SHARED /* points to a shared one in a shared object */
- } hostcachetype;
-};
+/* callback that gets called when a sub easy (data->master_mid set) is
+ DONE. Called on the master easy. */
+typedef void multi_sub_xfer_done_cb(struct Curl_easy *master_easy,
+ struct Curl_easy *sub_easy,
+ CURLcode result);
/*
* The 'connectdata' struct MUST have all the connection oriented stuff as we
@@ -1905,24 +1812,17 @@ struct Curl_easy {
/* once an easy handle is added to a multi, either explicitly by the
* libcurl application or implicitly during `curl_easy_perform()`,
* a unique identifier inside this one multi instance. */
- curl_off_t mid;
+ unsigned int mid;
+ unsigned int master_mid; /* if set, this transfer belongs to a master */
+ multi_sub_xfer_done_cb *sub_xfer_done;
struct connectdata *conn;
- struct Curl_llist_node multi_queue; /* for multihandle list management */
- struct Curl_llist_node conn_queue; /* list per connectdata */
CURLMstate mstate; /* the handle's state */
CURLcode result; /* previous result */
struct Curl_message msg; /* A single posted message. */
- /* Array with the plain socket numbers this handle takes care of, in no
- particular order. Note that all sockets are added to the sockhash, where
- the state etc are also kept. This array is mostly used to detect when a
- socket is to be removed from the hash. See singlesocket(). */
- struct easy_pollset last_poll;
-
- struct Names dns;
struct Curl_multi *multi; /* if non-NULL, points to the multi handle
struct to which this "belongs" when used by
the multi interface */
@@ -1930,6 +1830,13 @@ struct Curl_easy {
struct to which this "belongs" when used
by the easy interface */
struct Curl_share *share; /* Share, handles global variable mutexing */
+
+ /* `meta_hash` is a general key-value store for implementations
+ * with the lifetime of the easy handle.
+ * Elements need to be added with their own destructor to be invoked when
+ * the easy handle is cleaned up (see Curl_hash_add2()).*/
+ struct Curl_hash meta_hash;
+
#ifdef USE_LIBPSL
struct PslCache *psl; /* The associated PSL cache. */
#endif
@@ -1956,9 +1863,6 @@ struct Curl_easy {
struct PureInfo info; /* stats, reports and info data */
struct curl_tlssessioninfo tsi; /* Information about the TLS session, only
valid after a client has asked for it */
-#ifdef USE_HYPER
- struct hyptransfer hyp;
-#endif
};
#define LIBCURL_NAME "libcurl"
diff --git a/libs/libcurl/src/vauth/cleartext.c b/libs/libcurl/src/vauth/cleartext.c
index 198a452962..5833316aaa 100644
--- a/libs/libcurl/src/vauth/cleartext.c
+++ b/libs/libcurl/src/vauth/cleartext.c
@@ -25,24 +25,23 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \
!defined(CURL_DISABLE_POP3) || \
(!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP))
#include <curl/curl.h>
-#include "urldata.h"
+#include "../urldata.h"
-#include "vauth/vauth.h"
-#include "warnless.h"
-#include "strtok.h"
-#include "sendf.h"
-#include "curl_printf.h"
+#include "vauth.h"
+#include "../curlx/warnless.h"
+#include "../sendf.h"
+#include "../curl_printf.h"
/* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
/*
* Curl_auth_create_plain_message()
diff --git a/libs/libcurl/src/vauth/cram.c b/libs/libcurl/src/vauth/cram.c
index 5457135d42..4dea9c48bc 100644
--- a/libs/libcurl/src/vauth/cram.c
+++ b/libs/libcurl/src/vauth/cram.c
@@ -24,22 +24,22 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifndef CURL_DISABLE_DIGEST_AUTH
#include <curl/curl.h>
-#include "urldata.h"
+#include "../urldata.h"
-#include "vauth/vauth.h"
-#include "curl_hmac.h"
-#include "curl_md5.h"
-#include "warnless.h"
-#include "curl_printf.h"
+#include "vauth.h"
+#include "../curl_hmac.h"
+#include "../curl_md5.h"
+#include "../curlx/warnless.h"
+#include "../curl_printf.h"
/* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
/*
diff --git a/libs/libcurl/src/vauth/digest.c b/libs/libcurl/src/vauth/digest.c
index 84a706afc5..be0f7c099f 100644
--- a/libs/libcurl/src/vauth/digest.c
+++ b/libs/libcurl/src/vauth/digest.c
@@ -25,31 +25,32 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifndef CURL_DISABLE_DIGEST_AUTH
#include <curl/curl.h>
-#include "vauth/vauth.h"
-#include "vauth/digest.h"
-#include "urldata.h"
-#include "curl_base64.h"
-#include "curl_hmac.h"
-#include "curl_md5.h"
-#include "curl_sha256.h"
-#include "curl_sha512_256.h"
-#include "vtls/vtls.h"
-#include "warnless.h"
-#include "strtok.h"
-#include "strcase.h"
-#include "curl_printf.h"
-#include "rand.h"
+#include "vauth.h"
+#include "digest.h"
+#include "../urldata.h"
+#include "../curlx/base64.h"
+#include "../curl_hmac.h"
+#include "../curl_md5.h"
+#include "../curl_sha256.h"
+#include "../curl_sha512_256.h"
+#include "../vtls/vtls.h"
+#include "../curlx/warnless.h"
+#include "../curlx/strparse.h"
+#include "../strcase.h"
+#include "../curl_printf.h"
+#include "../rand.h"
/* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
+#ifndef USE_WINDOWS_SSPI
#define SESSION_ALGO 1 /* for algos with this bit set */
#define ALGO_MD5 0
@@ -59,7 +60,6 @@
#define ALGO_SHA512_256 4
#define ALGO_SHA512_256SESS (ALGO_SHA512_256 | SESSION_ALGO)
-#if !defined(USE_WINDOWS_SSPI)
#define DIGEST_QOP_VALUE_AUTH (1 << 0)
#define DIGEST_QOP_VALUE_AUTH_INT (1 << 1)
#define DIGEST_QOP_VALUE_AUTH_CONF (1 << 2)
@@ -141,8 +141,8 @@ bool Curl_auth_digest_get_pair(const char *str, char *value, char *content,
return TRUE;
}
-#if !defined(USE_WINDOWS_SSPI)
-/* Convert md5 chunk to RFC2617 (section 3.1.3) -suitable ASCII string */
+#ifndef USE_WINDOWS_SSPI
+/* Convert MD5 chunk to RFC2617 (section 3.1.3) -suitable ASCII string */
static void auth_digest_md5_to_ascii(unsigned char *source, /* 16 bytes */
unsigned char *dest) /* 33 bytes */
{
@@ -165,7 +165,7 @@ static char *auth_digest_string_quoted(const char *source)
{
char *dest;
const char *s = source;
- size_t n = 1; /* null terminator */
+ size_t n = 1; /* null-terminator */
/* Calculate size needed */
while(*s) {
@@ -219,33 +219,21 @@ static bool auth_digest_get_key_value(const char *chlg,
static CURLcode auth_digest_get_qop_values(const char *options, int *value)
{
- char *tmp;
- char *token;
- char *tok_buf = NULL;
-
+ struct Curl_str out;
/* Initialise the output */
*value = 0;
- /* Tokenise the list of qop values. Use a temporary clone of the buffer since
- strtok_r() ruins it. */
- tmp = strdup(options);
- if(!tmp)
- return CURLE_OUT_OF_MEMORY;
-
- token = strtok_r(tmp, ",", &tok_buf);
- while(token) {
- if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH))
+ while(!curlx_str_until(&options, &out, 32, ',')) {
+ if(curlx_str_casecompare(&out, DIGEST_QOP_VALUE_STRING_AUTH))
*value |= DIGEST_QOP_VALUE_AUTH;
- else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT))
+ else if(curlx_str_casecompare(&out, DIGEST_QOP_VALUE_STRING_AUTH_INT))
*value |= DIGEST_QOP_VALUE_AUTH_INT;
- else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_CONF))
+ else if(curlx_str_casecompare(&out, DIGEST_QOP_VALUE_STRING_AUTH_CONF))
*value |= DIGEST_QOP_VALUE_AUTH_CONF;
-
- token = strtok_r(NULL, ",", &tok_buf);
+ if(curlx_str_single(&options, ','))
+ break;
}
- free(tmp);
-
return CURLE_OK;
}
@@ -504,10 +492,6 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
struct digestdata *digest)
{
bool before = FALSE; /* got a nonce before */
- bool foundAuth = FALSE;
- bool foundAuthInt = FALSE;
- char *token = NULL;
- char *tmp = NULL;
/* If we already have received a nonce, keep that in mind */
if(digest->nonce)
@@ -551,29 +535,25 @@ CURLcode Curl_auth_decode_digest_http_message(const char *chlg,
return CURLE_OUT_OF_MEMORY;
}
else if(strcasecompare(value, "qop")) {
- char *tok_buf = NULL;
- /* Tokenize the list and choose auth if possible, use a temporary
- clone of the buffer since strtok_r() ruins it */
- tmp = strdup(content);
- if(!tmp)
- return CURLE_OUT_OF_MEMORY;
-
- token = strtok_r(tmp, ",", &tok_buf);
- while(token) {
- /* Pass additional spaces here */
- while(*token && ISBLANK(*token))
- token++;
- if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH)) {
+ const char *token = content;
+ struct Curl_str out;
+ bool foundAuth = FALSE;
+ bool foundAuthInt = FALSE;
+ /* Pass leading spaces */
+ while(*token && ISBLANK(*token))
+ token++;
+ while(!curlx_str_until(&token, &out, 32, ',')) {
+ if(curlx_str_casecompare(&out, DIGEST_QOP_VALUE_STRING_AUTH))
foundAuth = TRUE;
- }
- else if(strcasecompare(token, DIGEST_QOP_VALUE_STRING_AUTH_INT)) {
+ else if(curlx_str_casecompare(&out,
+ DIGEST_QOP_VALUE_STRING_AUTH_INT))
foundAuthInt = TRUE;
- }
- token = strtok_r(NULL, ",", &tok_buf);
+ if(curlx_str_single(&token, ','))
+ break;
+ while(*token && ISBLANK(*token))
+ token++;
}
- free(tmp);
-
/* Select only auth or auth-int. Otherwise, ignore */
if(foundAuth) {
free(digest->qop);
@@ -709,14 +689,18 @@ static CURLcode auth_create_digest_http_message(
digest->nc = 1;
if(!digest->cnonce) {
- char cnoncebuf[33];
- result = Curl_rand_hex(data, (unsigned char *)cnoncebuf,
- sizeof(cnoncebuf));
+ char cnoncebuf[12];
+ result = Curl_rand_bytes(data,
+#ifdef DEBUGBUILD
+ TRUE,
+#endif
+ (unsigned char *)cnoncebuf,
+ sizeof(cnoncebuf));
if(result)
return result;
- result = Curl_base64_encode(cnoncebuf, strlen(cnoncebuf),
- &cnonce, &cnonce_sz);
+ result = curlx_base64_encode(cnoncebuf, sizeof(cnoncebuf),
+ &cnonce, &cnonce_sz);
if(result)
return result;
diff --git a/libs/libcurl/src/vauth/digest_sspi.c b/libs/libcurl/src/vauth/digest_sspi.c
index 9c6b1d8667..b950f8f6b8 100644
--- a/libs/libcurl/src/vauth/digest_sspi.c
+++ b/libs/libcurl/src/vauth/digest_sspi.c
@@ -25,25 +25,25 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_DIGEST_AUTH)
#include <curl/curl.h>
-#include "vauth/vauth.h"
-#include "vauth/digest.h"
-#include "urldata.h"
-#include "warnless.h"
-#include "curl_multibyte.h"
-#include "sendf.h"
-#include "strdup.h"
-#include "strcase.h"
-#include "strerror.h"
+#include "vauth.h"
+#include "digest.h"
+#include "../urldata.h"
+#include "../curlx/warnless.h"
+#include "../curlx/multibyte.h"
+#include "../sendf.h"
+#include "../strdup.h"
+#include "../strcase.h"
+#include "../strerror.h"
/* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
/*
* Curl_auth_is_digest_supported()
@@ -61,15 +61,16 @@ bool Curl_auth_is_digest_supported(void)
/* Query the security package for Digest */
status =
- Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
- &SecurityPackage);
+ Curl_pSecFn->QuerySecurityPackageInfo(
+ (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_DIGEST)),
+ &SecurityPackage);
/* Release the package buffer as it is not required anymore */
if(status == SEC_E_OK) {
Curl_pSecFn->FreeContextBuffer(SecurityPackage);
}
- return (status == SEC_E_OK);
+ return status == SEC_E_OK;
}
/*
@@ -121,8 +122,9 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
/* Query the security package for DigestSSP */
status =
- Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
- &SecurityPackage);
+ Curl_pSecFn->QuerySecurityPackageInfo(
+ (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_DIGEST)),
+ &SecurityPackage);
if(status != SEC_E_OK) {
failf(data, "SSPI: could not get auth info");
return CURLE_AUTH_ERROR;
@@ -163,10 +165,10 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
/* Acquire our credentials handle */
status = Curl_pSecFn->AcquireCredentialsHandle(NULL,
- (TCHAR *) TEXT(SP_NAME_DIGEST),
- SECPKG_CRED_OUTBOUND, NULL,
- p_identity, NULL, NULL,
- &credentials, &expiry);
+ (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_DIGEST)),
+ SECPKG_CRED_OUTBOUND, NULL,
+ p_identity, NULL, NULL,
+ &credentials, &expiry);
if(status != SEC_E_OK) {
Curl_sspi_free_identity(p_identity);
@@ -180,7 +182,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
chlg_desc.cBuffers = 1;
chlg_desc.pBuffers = &chlg_buf;
chlg_buf.BufferType = SECBUFFER_TOKEN;
- chlg_buf.pvBuffer = (void *) Curl_bufref_ptr(chlg);
+ chlg_buf.pvBuffer = CURL_UNCONST(Curl_bufref_ptr(chlg));
chlg_buf.cbBuffer = curlx_uztoul(Curl_bufref_len(chlg));
/* Setup the response "output" security buffer */
@@ -240,7 +242,7 @@ CURLcode Curl_auth_create_digest_md5_message(struct Curl_easy *data,
/*
* Curl_override_sspi_http_realm()
*
- * This is used to populate the domain in a SSPI identity structure
+ * This is used to populate the domain in an SSPI identity structure
* The realm is extracted from the challenge message and used as the
* domain if it is not already explicitly set.
*
@@ -271,7 +273,7 @@ CURLcode Curl_override_sspi_http_realm(const char *chlg,
if(strcasecompare(value, "realm")) {
/* Setup identity's domain and length */
- domain.tchar_ptr = curlx_convert_UTF8_to_tchar((char *) content);
+ domain.tchar_ptr = curlx_convert_UTF8_to_tchar(content);
if(!domain.tchar_ptr)
return CURLE_OUT_OF_MEMORY;
@@ -413,8 +415,9 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
/* Query the security package for DigestSSP */
status =
- Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST),
- &SecurityPackage);
+ Curl_pSecFn->QuerySecurityPackageInfo(
+ (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_DIGEST)),
+ &SecurityPackage);
if(status != SEC_E_OK) {
failf(data, "SSPI: could not get auth info");
return CURLE_AUTH_ERROR;
@@ -454,10 +457,10 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
chlg_buf[0].pvBuffer = NULL;
chlg_buf[0].cbBuffer = 0;
chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS;
- chlg_buf[1].pvBuffer = (void *) request;
+ chlg_buf[1].pvBuffer = CURL_UNCONST(request);
chlg_buf[1].cbBuffer = curlx_uztoul(strlen((const char *) request));
chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS;
- chlg_buf[2].pvBuffer = (void *) uripath;
+ chlg_buf[2].pvBuffer = CURL_UNCONST(uripath);
chlg_buf[2].cbBuffer = curlx_uztoul(strlen((const char *) uripath));
chlg_buf[3].BufferType = SECBUFFER_PKG_PARAMS;
chlg_buf[3].pvBuffer = NULL;
@@ -534,10 +537,10 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
/* Acquire our credentials handle */
status = Curl_pSecFn->AcquireCredentialsHandle(NULL,
- (TCHAR *) TEXT(SP_NAME_DIGEST),
- SECPKG_CRED_OUTBOUND, NULL,
- p_identity, NULL, NULL,
- &credentials, &expiry);
+ (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_DIGEST)),
+ SECPKG_CRED_OUTBOUND, NULL,
+ p_identity, NULL, NULL,
+ &credentials, &expiry);
if(status != SEC_E_OK) {
Curl_sspi_free_identity(p_identity);
free(output_token);
@@ -553,7 +556,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
chlg_buf[0].pvBuffer = digest->input_token;
chlg_buf[0].cbBuffer = curlx_uztoul(digest->input_token_len);
chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS;
- chlg_buf[1].pvBuffer = (void *) request;
+ chlg_buf[1].pvBuffer = CURL_UNCONST(request);
chlg_buf[1].cbBuffer = curlx_uztoul(strlen((const char *) request));
chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS;
chlg_buf[2].pvBuffer = NULL;
@@ -567,7 +570,7 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
resp_buf.pvBuffer = output_token;
resp_buf.cbBuffer = curlx_uztoul(token_max);
- spn = curlx_convert_UTF8_to_tchar((char *) uripath);
+ spn = curlx_convert_UTF8_to_tchar((const char *) uripath);
if(!spn) {
Curl_pSecFn->FreeCredentialsHandle(&credentials);
@@ -579,8 +582,12 @@ CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data,
/* Allocate our new context handle */
digest->http_context = calloc(1, sizeof(CtxtHandle));
- if(!digest->http_context)
+ if(!digest->http_context) {
+ curlx_unicodefree(spn);
+ Curl_sspi_free_identity(p_identity);
+ free(output_token);
return CURLE_OUT_OF_MEMORY;
+ }
/* Generate our response message */
status = Curl_pSecFn->InitializeSecurityContext(&credentials, NULL,
diff --git a/libs/libcurl/src/vauth/gsasl.c b/libs/libcurl/src/vauth/gsasl.c
index d88f9ea77a..8897c84f92 100644
--- a/libs/libcurl/src/vauth/gsasl.c
+++ b/libs/libcurl/src/vauth/gsasl.c
@@ -24,22 +24,22 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_GSASL
#include <curl/curl.h>
-#include "vauth/vauth.h"
-#include "urldata.h"
-#include "sendf.h"
+#include "vauth.h"
+#include "../urldata.h"
+#include "../sendf.h"
#include <gsasl.h>
/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
bool Curl_auth_gsasl_is_supported(struct Curl_easy *data,
const char *mech,
diff --git a/libs/libcurl/src/vauth/krb5_gssapi.c b/libs/libcurl/src/vauth/krb5_gssapi.c
index fa799354fb..96d717fbaa 100644
--- a/libs/libcurl/src/vauth/krb5_gssapi.c
+++ b/libs/libcurl/src/vauth/krb5_gssapi.c
@@ -25,22 +25,27 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(HAVE_GSSAPI) && defined(USE_KERBEROS5)
#include <curl/curl.h>
-#include "vauth/vauth.h"
-#include "curl_sasl.h"
-#include "urldata.h"
-#include "curl_gssapi.h"
-#include "sendf.h"
-#include "curl_printf.h"
+#include "vauth.h"
+#include "../curl_sasl.h"
+#include "../urldata.h"
+#include "../curl_gssapi.h"
+#include "../sendf.h"
+#include "../curl_printf.h"
/* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
+
+#if defined(__GNUC__) && defined(__APPLE__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
/*
* Curl_auth_is_gssapi_supported()
@@ -128,7 +133,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
infof(data, "GSSAPI handshake failure (empty challenge message)");
return CURLE_BAD_CONTENT_ENCODING;
}
- input_token.value = (void *) Curl_bufref_ptr(chlg);
+ input_token.value = CURL_UNCONST(Curl_bufref_ptr(chlg));
input_token.length = Curl_bufref_len(chlg);
}
@@ -205,7 +210,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
}
/* Setup the challenge "input" security buffer */
- input_token.value = (void *) Curl_bufref_ptr(chlg);
+ input_token.value = CURL_UNCONST(Curl_bufref_ptr(chlg));
input_token.length = Curl_bufref_len(chlg);
/* Decrypt the inbound challenge and obtain the qop */
@@ -321,4 +326,8 @@ void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5)
}
}
+#if defined(__GNUC__) && defined(__APPLE__)
+#pragma GCC diagnostic pop
+#endif
+
#endif /* HAVE_GSSAPI && USE_KERBEROS5 */
diff --git a/libs/libcurl/src/vauth/krb5_sspi.c b/libs/libcurl/src/vauth/krb5_sspi.c
index a5393b202d..6cac791bdc 100644
--- a/libs/libcurl/src/vauth/krb5_sspi.c
+++ b/libs/libcurl/src/vauth/krb5_sspi.c
@@ -24,21 +24,21 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_WINDOWS_SSPI) && defined(USE_KERBEROS5)
#include <curl/curl.h>
-#include "vauth/vauth.h"
-#include "urldata.h"
-#include "warnless.h"
-#include "curl_multibyte.h"
-#include "sendf.h"
+#include "vauth.h"
+#include "../urldata.h"
+#include "../curlx/warnless.h"
+#include "../curlx/multibyte.h"
+#include "../sendf.h"
/* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
/*
* Curl_auth_is_gssapi_supported()
@@ -55,16 +55,16 @@ bool Curl_auth_is_gssapi_supported(void)
SECURITY_STATUS status;
/* Query the security package for Kerberos */
- status = Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *)
- TEXT(SP_NAME_KERBEROS),
- &SecurityPackage);
+ status = Curl_pSecFn->QuerySecurityPackageInfo(
+ (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_KERBEROS)),
+ &SecurityPackage);
/* Release the package buffer as it is not required anymore */
if(status == SEC_E_OK) {
Curl_pSecFn->FreeContextBuffer(SecurityPackage);
}
- return (status == SEC_E_OK);
+ return status == SEC_E_OK;
}
/*
@@ -118,9 +118,9 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
if(!krb5->output_token) {
/* Query the security package for Kerberos */
- status = Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *)
- TEXT(SP_NAME_KERBEROS),
- &SecurityPackage);
+ status = Curl_pSecFn->QuerySecurityPackageInfo(
+ (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_KERBEROS)),
+ &SecurityPackage);
if(status != SEC_E_OK) {
failf(data, "SSPI: could not get auth info");
return CURLE_AUTH_ERROR;
@@ -159,11 +159,10 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
/* Acquire our credentials handle */
status = Curl_pSecFn->AcquireCredentialsHandle(NULL,
- (TCHAR *)
- TEXT(SP_NAME_KERBEROS),
- SECPKG_CRED_OUTBOUND, NULL,
- krb5->p_identity, NULL, NULL,
- krb5->credentials, &expiry);
+ (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_KERBEROS)),
+ SECPKG_CRED_OUTBOUND, NULL,
+ krb5->p_identity, NULL, NULL,
+ krb5->credentials, &expiry);
if(status != SEC_E_OK)
return CURLE_LOGIN_DENIED;
@@ -184,7 +183,7 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
chlg_desc.cBuffers = 1;
chlg_desc.pBuffers = &chlg_buf;
chlg_buf.BufferType = SECBUFFER_TOKEN;
- chlg_buf.pvBuffer = (void *) Curl_bufref_ptr(chlg);
+ chlg_buf.pvBuffer = CURL_UNCONST(Curl_bufref_ptr(chlg));
chlg_buf.cbBuffer = curlx_uztoul(Curl_bufref_len(chlg));
}
@@ -297,7 +296,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
input_desc.cBuffers = 2;
input_desc.pBuffers = input_buf;
input_buf[0].BufferType = SECBUFFER_STREAM;
- input_buf[0].pvBuffer = (void *) Curl_bufref_ptr(chlg);
+ input_buf[0].pvBuffer = CURL_UNCONST(Curl_bufref_ptr(chlg));
input_buf[0].cbBuffer = curlx_uztoul(Curl_bufref_len(chlg));
input_buf[1].BufferType = SECBUFFER_DATA;
input_buf[1].pvBuffer = NULL;
diff --git a/libs/libcurl/src/vauth/ntlm.c b/libs/libcurl/src/vauth/ntlm.c
index e835500ebf..1d474689d3 100644
--- a/libs/libcurl/src/vauth/ntlm.c
+++ b/libs/libcurl/src/vauth/ntlm.c
@@ -22,7 +22,7 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_NTLM) && !defined(USE_WINDOWS_SSPI)
@@ -35,26 +35,130 @@
#define DEBUG_ME 0
-#include "urldata.h"
-#include "sendf.h"
-#include "curl_ntlm_core.h"
-#include "curl_gethostname.h"
-#include "curl_multibyte.h"
-#include "curl_md5.h"
-#include "warnless.h"
-#include "rand.h"
-#include "vtls/vtls.h"
-#include "strdup.h"
-
-#define BUILDING_CURL_NTLM_MSGS_C
-#include "vauth/vauth.h"
-#include "vauth/ntlm.h"
-#include "curl_endian.h"
-#include "curl_printf.h"
+#include "../urldata.h"
+#include "../sendf.h"
+#include "../curl_ntlm_core.h"
+#include "../curl_gethostname.h"
+#include "../curlx/multibyte.h"
+#include "../curl_md5.h"
+#include "../curlx/warnless.h"
+#include "../rand.h"
+#include "../vtls/vtls.h"
+#include "../strdup.h"
+
+#include "vauth.h"
+#include "../curl_endian.h"
+#include "../curl_printf.h"
/* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
+
+
+/* NTLM buffer fixed size, large enough for long user + host + domain */
+#define NTLM_BUFSIZE 1024
+
+/* Flag bits definitions based on
+ https://davenport.sourceforge.net/ntlm.html */
+
+#define NTLMFLAG_NEGOTIATE_UNICODE (1<<0)
+/* Indicates that Unicode strings are supported for use in security buffer
+ data. */
+
+#define NTLMFLAG_NEGOTIATE_OEM (1<<1)
+/* Indicates that OEM strings are supported for use in security buffer data. */
+
+#define NTLMFLAG_REQUEST_TARGET (1<<2)
+/* Requests that the server's authentication realm be included in the Type 2
+ message. */
+
+/* unknown (1<<3) */
+#define NTLMFLAG_NEGOTIATE_SIGN (1<<4)
+/* Specifies that authenticated communication between the client and server
+ should carry a digital signature (message integrity). */
+
+#define NTLMFLAG_NEGOTIATE_SEAL (1<<5)
+/* Specifies that authenticated communication between the client and server
+ should be encrypted (message confidentiality). */
+
+#define NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE (1<<6)
+/* Indicates that datagram authentication is being used. */
+
+#define NTLMFLAG_NEGOTIATE_LM_KEY (1<<7)
+/* Indicates that the LAN Manager session key should be used for signing and
+ sealing authenticated communications. */
+
+#define NTLMFLAG_NEGOTIATE_NTLM_KEY (1<<9)
+/* Indicates that NTLM authentication is being used. */
+
+/* unknown (1<<10) */
+
+#define NTLMFLAG_NEGOTIATE_ANONYMOUS (1<<11)
+/* Sent by the client in the Type 3 message to indicate that an anonymous
+ context has been established. This also affects the response fields. */
+
+#define NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED (1<<12)
+/* Sent by the client in the Type 1 message to indicate that a desired
+ authentication realm is included in the message. */
+
+#define NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED (1<<13)
+/* Sent by the client in the Type 1 message to indicate that the client
+ workstation's name is included in the message. */
+
+#define NTLMFLAG_NEGOTIATE_LOCAL_CALL (1<<14)
+/* Sent by the server to indicate that the server and client are on the same
+ machine. Implies that the client may use a pre-established local security
+ context rather than responding to the challenge. */
+
+#define NTLMFLAG_NEGOTIATE_ALWAYS_SIGN (1<<15)
+/* Indicates that authenticated communication between the client and server
+ should be signed with a "dummy" signature. */
+
+#define NTLMFLAG_TARGET_TYPE_DOMAIN (1<<16)
+/* Sent by the server in the Type 2 message to indicate that the target
+ authentication realm is a domain. */
+
+#define NTLMFLAG_TARGET_TYPE_SERVER (1<<17)
+/* Sent by the server in the Type 2 message to indicate that the target
+ authentication realm is a server. */
+
+#define NTLMFLAG_TARGET_TYPE_SHARE (1<<18)
+/* Sent by the server in the Type 2 message to indicate that the target
+ authentication realm is a share. Presumably, this is for share-level
+ authentication. Usage is unclear. */
+
+#define NTLMFLAG_NEGOTIATE_NTLM2_KEY (1<<19)
+/* Indicates that the NTLM2 signing and sealing scheme should be used for
+ protecting authenticated communications. */
+
+#define NTLMFLAG_REQUEST_INIT_RESPONSE (1<<20)
+/* unknown purpose */
+
+#define NTLMFLAG_REQUEST_ACCEPT_RESPONSE (1<<21)
+/* unknown purpose */
+
+#define NTLMFLAG_REQUEST_NONNT_SESSION_KEY (1<<22)
+/* unknown purpose */
+
+#define NTLMFLAG_NEGOTIATE_TARGET_INFO (1<<23)
+/* Sent by the server in the Type 2 message to indicate that it is including a
+ Target Information block in the message. */
+
+/* unknown (1<24) */
+/* unknown (1<25) */
+/* unknown (1<26) */
+/* unknown (1<27) */
+/* unknown (1<28) */
+
+#define NTLMFLAG_NEGOTIATE_128 (1<<29)
+/* Indicates that 128-bit encryption is supported. */
+
+#define NTLMFLAG_NEGOTIATE_KEY_EXCHANGE (1<<30)
+/* Indicates that the client will provide an encrypted master key in
+ the "Session Key" field of the Type 3 message. */
+
+#define NTLMFLAG_NEGOTIATE_56 (1<<31)
+/* Indicates that 56-bit encryption is supported. */
/* "NTLMSSP" signature is always in ASCII regardless of the platform */
#define NTLMSSP_SIGNATURE "\x4e\x54\x4c\x4d\x53\x53\x50"
diff --git a/libs/libcurl/src/vauth/ntlm.h b/libs/libcurl/src/vauth/ntlm.h
deleted file mode 100644
index 4c8f5edd36..0000000000
--- a/libs/libcurl/src/vauth/ntlm.h
+++ /dev/null
@@ -1,143 +0,0 @@
-#ifndef HEADER_VAUTH_NTLM_H
-#define HEADER_VAUTH_NTLM_H
-/***************************************************************************
- * _ _ ____ _
- * Project ___| | | | _ \| |
- * / __| | | | |_) | |
- * | (__| |_| | _ <| |___
- * \___|\___/|_| \_\_____|
- *
- * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
- *
- * This software is licensed as described in the file COPYING, which
- * you should have received as part of this distribution. The terms
- * are also available at https://curl.se/docs/copyright.html.
- *
- * You may opt to use, copy, modify, merge, publish, distribute and/or sell
- * copies of the Software, and permit persons to whom the Software is
- * furnished to do so, under the terms of the COPYING file.
- *
- * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
- * KIND, either express or implied.
- *
- * SPDX-License-Identifier: curl
- *
- ***************************************************************************/
-
-#include "curl_setup.h"
-
-#ifdef USE_NTLM
-
-/* NTLM buffer fixed size, large enough for long user + host + domain */
-#define NTLM_BUFSIZE 1024
-
-/* Stuff only required for curl_ntlm_msgs.c */
-#ifdef BUILDING_CURL_NTLM_MSGS_C
-
-/* Flag bits definitions based on
- https://davenport.sourceforge.net/ntlm.html */
-
-#define NTLMFLAG_NEGOTIATE_UNICODE (1<<0)
-/* Indicates that Unicode strings are supported for use in security buffer
- data. */
-
-#define NTLMFLAG_NEGOTIATE_OEM (1<<1)
-/* Indicates that OEM strings are supported for use in security buffer data. */
-
-#define NTLMFLAG_REQUEST_TARGET (1<<2)
-/* Requests that the server's authentication realm be included in the Type 2
- message. */
-
-/* unknown (1<<3) */
-#define NTLMFLAG_NEGOTIATE_SIGN (1<<4)
-/* Specifies that authenticated communication between the client and server
- should carry a digital signature (message integrity). */
-
-#define NTLMFLAG_NEGOTIATE_SEAL (1<<5)
-/* Specifies that authenticated communication between the client and server
- should be encrypted (message confidentiality). */
-
-#define NTLMFLAG_NEGOTIATE_DATAGRAM_STYLE (1<<6)
-/* Indicates that datagram authentication is being used. */
-
-#define NTLMFLAG_NEGOTIATE_LM_KEY (1<<7)
-/* Indicates that the LAN Manager session key should be used for signing and
- sealing authenticated communications. */
-
-#define NTLMFLAG_NEGOTIATE_NTLM_KEY (1<<9)
-/* Indicates that NTLM authentication is being used. */
-
-/* unknown (1<<10) */
-
-#define NTLMFLAG_NEGOTIATE_ANONYMOUS (1<<11)
-/* Sent by the client in the Type 3 message to indicate that an anonymous
- context has been established. This also affects the response fields. */
-
-#define NTLMFLAG_NEGOTIATE_DOMAIN_SUPPLIED (1<<12)
-/* Sent by the client in the Type 1 message to indicate that a desired
- authentication realm is included in the message. */
-
-#define NTLMFLAG_NEGOTIATE_WORKSTATION_SUPPLIED (1<<13)
-/* Sent by the client in the Type 1 message to indicate that the client
- workstation's name is included in the message. */
-
-#define NTLMFLAG_NEGOTIATE_LOCAL_CALL (1<<14)
-/* Sent by the server to indicate that the server and client are on the same
- machine. Implies that the client may use a pre-established local security
- context rather than responding to the challenge. */
-
-#define NTLMFLAG_NEGOTIATE_ALWAYS_SIGN (1<<15)
-/* Indicates that authenticated communication between the client and server
- should be signed with a "dummy" signature. */
-
-#define NTLMFLAG_TARGET_TYPE_DOMAIN (1<<16)
-/* Sent by the server in the Type 2 message to indicate that the target
- authentication realm is a domain. */
-
-#define NTLMFLAG_TARGET_TYPE_SERVER (1<<17)
-/* Sent by the server in the Type 2 message to indicate that the target
- authentication realm is a server. */
-
-#define NTLMFLAG_TARGET_TYPE_SHARE (1<<18)
-/* Sent by the server in the Type 2 message to indicate that the target
- authentication realm is a share. Presumably, this is for share-level
- authentication. Usage is unclear. */
-
-#define NTLMFLAG_NEGOTIATE_NTLM2_KEY (1<<19)
-/* Indicates that the NTLM2 signing and sealing scheme should be used for
- protecting authenticated communications. */
-
-#define NTLMFLAG_REQUEST_INIT_RESPONSE (1<<20)
-/* unknown purpose */
-
-#define NTLMFLAG_REQUEST_ACCEPT_RESPONSE (1<<21)
-/* unknown purpose */
-
-#define NTLMFLAG_REQUEST_NONNT_SESSION_KEY (1<<22)
-/* unknown purpose */
-
-#define NTLMFLAG_NEGOTIATE_TARGET_INFO (1<<23)
-/* Sent by the server in the Type 2 message to indicate that it is including a
- Target Information block in the message. */
-
-/* unknown (1<24) */
-/* unknown (1<25) */
-/* unknown (1<26) */
-/* unknown (1<27) */
-/* unknown (1<28) */
-
-#define NTLMFLAG_NEGOTIATE_128 (1<<29)
-/* Indicates that 128-bit encryption is supported. */
-
-#define NTLMFLAG_NEGOTIATE_KEY_EXCHANGE (1<<30)
-/* Indicates that the client will provide an encrypted master key in
- the "Session Key" field of the Type 3 message. */
-
-#define NTLMFLAG_NEGOTIATE_56 (1<<31)
-/* Indicates that 56-bit encryption is supported. */
-
-#endif /* BUILDING_CURL_NTLM_MSGS_C */
-
-#endif /* USE_NTLM */
-
-#endif /* HEADER_VAUTH_NTLM_H */
diff --git a/libs/libcurl/src/vauth/ntlm_sspi.c b/libs/libcurl/src/vauth/ntlm_sspi.c
index 6778b70e79..7922177158 100644
--- a/libs/libcurl/src/vauth/ntlm_sspi.c
+++ b/libs/libcurl/src/vauth/ntlm_sspi.c
@@ -22,23 +22,23 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_WINDOWS_SSPI) && defined(USE_NTLM)
#include <curl/curl.h>
-#include "vauth/vauth.h"
-#include "urldata.h"
-#include "curl_ntlm_core.h"
-#include "warnless.h"
-#include "curl_multibyte.h"
-#include "sendf.h"
-#include "strdup.h"
+#include "vauth.h"
+#include "../urldata.h"
+#include "../curl_ntlm_core.h"
+#include "../curlx/warnless.h"
+#include "../curlx/multibyte.h"
+#include "../sendf.h"
+#include "../strdup.h"
/* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
/*
* Curl_auth_is_ntlm_supported()
@@ -55,15 +55,16 @@ bool Curl_auth_is_ntlm_supported(void)
SECURITY_STATUS status;
/* Query the security package for NTLM */
- status = Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM),
- &SecurityPackage);
+ status = Curl_pSecFn->QuerySecurityPackageInfo(
+ (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_NTLM)),
+ &SecurityPackage);
/* Release the package buffer as it is not required anymore */
if(status == SEC_E_OK) {
Curl_pSecFn->FreeContextBuffer(SecurityPackage);
}
- return (status == SEC_E_OK);
+ return status == SEC_E_OK;
}
/*
@@ -103,8 +104,9 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data,
Curl_auth_cleanup_ntlm(ntlm);
/* Query the security package for NTLM */
- status = Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM),
- &SecurityPackage);
+ status = Curl_pSecFn->QuerySecurityPackageInfo(
+ (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_NTLM)),
+ &SecurityPackage);
if(status != SEC_E_OK) {
failf(data, "SSPI: could not get auth info");
return CURLE_AUTH_ERROR;
@@ -142,10 +144,10 @@ CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data,
/* Acquire our credentials handle */
status = Curl_pSecFn->AcquireCredentialsHandle(NULL,
- (TCHAR *) TEXT(SP_NAME_NTLM),
- SECPKG_CRED_OUTBOUND, NULL,
- ntlm->p_identity, NULL, NULL,
- ntlm->credentials, &expiry);
+ (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_NTLM)),
+ SECPKG_CRED_OUTBOUND, NULL,
+ ntlm->p_identity, NULL, NULL,
+ ntlm->credentials, &expiry);
if(status != SEC_E_OK)
return CURLE_LOGIN_DENIED;
diff --git a/libs/libcurl/src/vauth/oauth2.c b/libs/libcurl/src/vauth/oauth2.c
index 00bb9867ec..d8fb109386 100644
--- a/libs/libcurl/src/vauth/oauth2.c
+++ b/libs/libcurl/src/vauth/oauth2.c
@@ -24,22 +24,22 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if !defined(CURL_DISABLE_IMAP) || !defined(CURL_DISABLE_SMTP) || \
!defined(CURL_DISABLE_POP3) || \
(!defined(CURL_DISABLE_LDAP) && defined(USE_OPENLDAP))
#include <curl/curl.h>
-#include "urldata.h"
+#include "../urldata.h"
-#include "vauth/vauth.h"
-#include "warnless.h"
-#include "curl_printf.h"
+#include "vauth.h"
+#include "../curlx/warnless.h"
+#include "../curl_printf.h"
/* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
/*
* Curl_auth_create_oauth_bearer_message()
diff --git a/libs/libcurl/src/vauth/spnego_gssapi.c b/libs/libcurl/src/vauth/spnego_gssapi.c
index 74d4fa3362..05169a81f8 100644
--- a/libs/libcurl/src/vauth/spnego_gssapi.c
+++ b/libs/libcurl/src/vauth/spnego_gssapi.c
@@ -24,23 +24,28 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(HAVE_GSSAPI) && defined(USE_SPNEGO)
#include <curl/curl.h>
-#include "vauth/vauth.h"
-#include "urldata.h"
-#include "curl_base64.h"
-#include "curl_gssapi.h"
-#include "warnless.h"
-#include "curl_multibyte.h"
-#include "sendf.h"
+#include "vauth.h"
+#include "../urldata.h"
+#include "../curlx/base64.h"
+#include "../curl_gssapi.h"
+#include "../curlx/warnless.h"
+#include "../curlx/multibyte.h"
+#include "../sendf.h"
/* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
+
+#if defined(__GNUC__) && defined(__APPLE__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
/*
* Curl_auth_is_spnego_supported()
@@ -134,7 +139,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
if(chlg64 && *chlg64) {
/* Decode the base-64 encoded challenge message */
if(*chlg64 != '=') {
- result = Curl_base64_decode(chlg64, &chlg, &chlglen);
+ result = curlx_base64_decode(chlg64, &chlg, &chlglen);
if(result)
return result;
}
@@ -151,10 +156,10 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
}
/* Set channel binding data if available */
- if(nego->channel_binding_data.leng > 0) {
+ if(curlx_dyn_len(&nego->channel_binding_data)) {
memset(&chan, 0, sizeof(struct gss_channel_bindings_struct));
- chan.application_data.length = nego->channel_binding_data.leng;
- chan.application_data.value = nego->channel_binding_data.bufr;
+ chan.application_data.length = curlx_dyn_len(&nego->channel_binding_data);
+ chan.application_data.value = curlx_dyn_ptr(&nego->channel_binding_data);
chan_bindings = &chan;
}
@@ -223,9 +228,9 @@ CURLcode Curl_auth_create_spnego_message(struct negotiatedata *nego,
OM_uint32 minor_status;
/* Base64 encode the already generated response */
- result = Curl_base64_encode(nego->output_token.value,
- nego->output_token.length,
- outptr, outlen);
+ result = curlx_base64_encode(nego->output_token.value,
+ nego->output_token.length,
+ outptr, outlen);
if(result) {
gss_release_buffer(&minor_status, &nego->output_token);
@@ -288,4 +293,8 @@ void Curl_auth_cleanup_spnego(struct negotiatedata *nego)
nego->havemultiplerequests = FALSE;
}
+#if defined(__GNUC__) && defined(__APPLE__)
+#pragma GCC diagnostic pop
+#endif
+
#endif /* HAVE_GSSAPI && USE_SPNEGO */
diff --git a/libs/libcurl/src/vauth/spnego_sspi.c b/libs/libcurl/src/vauth/spnego_sspi.c
index 5f82e9a51e..dead8c4f36 100644
--- a/libs/libcurl/src/vauth/spnego_sspi.c
+++ b/libs/libcurl/src/vauth/spnego_sspi.c
@@ -24,23 +24,23 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_WINDOWS_SSPI) && defined(USE_SPNEGO)
#include <curl/curl.h>
-#include "vauth/vauth.h"
-#include "urldata.h"
-#include "curl_base64.h"
-#include "warnless.h"
-#include "curl_multibyte.h"
-#include "sendf.h"
-#include "strerror.h"
+#include "vauth.h"
+#include "../urldata.h"
+#include "../curlx/base64.h"
+#include "../curlx/warnless.h"
+#include "../curlx/multibyte.h"
+#include "../sendf.h"
+#include "../strerror.h"
/* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
/*
* Curl_auth_is_spnego_supported()
@@ -57,9 +57,9 @@ bool Curl_auth_is_spnego_supported(void)
SECURITY_STATUS status;
/* Query the security package for Negotiate */
- status = Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *)
- TEXT(SP_NAME_NEGOTIATE),
- &SecurityPackage);
+ status = Curl_pSecFn->QuerySecurityPackageInfo(
+ (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_NEGOTIATE)),
+ &SecurityPackage);
/* Release the package buffer as it is not required anymore */
if(status == SEC_E_OK) {
@@ -67,7 +67,7 @@ bool Curl_auth_is_spnego_supported(void)
}
- return (status == SEC_E_OK);
+ return status == SEC_E_OK;
}
/*
@@ -128,9 +128,9 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
if(!nego->output_token) {
/* Query the security package for Negotiate */
- nego->status = (DWORD)Curl_pSecFn->QuerySecurityPackageInfo((TCHAR *)
- TEXT(SP_NAME_NEGOTIATE),
- &SecurityPackage);
+ nego->status = (DWORD)Curl_pSecFn->QuerySecurityPackageInfo(
+ (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_NEGOTIATE)),
+ &SecurityPackage);
if(nego->status != SEC_E_OK) {
failf(data, "SSPI: could not get auth info");
return CURLE_AUTH_ERROR;
@@ -170,10 +170,10 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
/* Acquire our credentials handle */
nego->status = (DWORD)
Curl_pSecFn->AcquireCredentialsHandle(NULL,
- (TCHAR *)TEXT(SP_NAME_NEGOTIATE),
- SECPKG_CRED_OUTBOUND, NULL,
- nego->p_identity, NULL, NULL,
- nego->credentials, &expiry);
+ (TCHAR *)CURL_UNCONST(TEXT(SP_NAME_NEGOTIATE)),
+ SECPKG_CRED_OUTBOUND, NULL,
+ nego->p_identity, NULL, NULL,
+ nego->credentials, &expiry);
if(nego->status != SEC_E_OK)
return CURLE_AUTH_ERROR;
@@ -186,7 +186,7 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
if(chlg64 && *chlg64) {
/* Decode the base-64 encoded challenge message */
if(*chlg64 != '=') {
- result = Curl_base64_decode(chlg64, &chlg, &chlglen);
+ result = curlx_base64_decode(chlg64, &chlg, &chlglen);
if(result)
return result;
}
@@ -308,9 +308,9 @@ CURLcode Curl_auth_create_spnego_message(struct negotiatedata *nego,
char **outptr, size_t *outlen)
{
/* Base64 encode the already generated response */
- CURLcode result = Curl_base64_encode((const char *) nego->output_token,
- nego->output_token_length, outptr,
- outlen);
+ CURLcode result = curlx_base64_encode((const char *) nego->output_token,
+ nego->output_token_length, outptr,
+ outlen);
if(!result && (!*outptr || !*outlen)) {
free(*outptr);
result = CURLE_REMOTE_ACCESS_DENIED;
diff --git a/libs/libcurl/src/vauth/vauth.c b/libs/libcurl/src/vauth/vauth.c
index 98f5e05d65..0a3b9acfed 100644
--- a/libs/libcurl/src/vauth/vauth.c
+++ b/libs/libcurl/src/vauth/vauth.c
@@ -22,24 +22,24 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#include <curl/curl.h>
#include "vauth.h"
-#include "urldata.h"
-#include "strcase.h"
-#include "curl_multibyte.h"
-#include "curl_printf.h"
+#include "../urldata.h"
+#include "../strcase.h"
+#include "../curlx/multibyte.h"
+#include "../curl_printf.h"
/* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
/*
* Curl_auth_build_spn()
*
- * This is used to build a SPN string in the following formats:
+ * This is used to build an SPN string in the following formats:
*
* service/host@realm (Not currently used)
* service/host (Not used by GSS-API)
@@ -153,10 +153,10 @@ bool Curl_auth_user_contains_domain(const char *user)
bool Curl_auth_allowed_to_host(struct Curl_easy *data)
{
struct connectdata *conn = data->conn;
- return (!data->state.this_is_a_follow ||
- data->set.allow_auth_to_other_hosts ||
- (data->state.first_host &&
- strcasecompare(data->state.first_host, conn->host.name) &&
- (data->state.first_remote_port == conn->remote_port) &&
- (data->state.first_remote_protocol == conn->handler->protocol)));
+ return !data->state.this_is_a_follow ||
+ data->set.allow_auth_to_other_hosts ||
+ (data->state.first_host &&
+ strcasecompare(data->state.first_host, conn->host.name) &&
+ (data->state.first_remote_port == conn->remote_port) &&
+ (data->state.first_remote_protocol == conn->handler->protocol));
}
diff --git a/libs/libcurl/src/vauth/vauth.h b/libs/libcurl/src/vauth/vauth.h
index fb43bc5e02..0960f4a45a 100644
--- a/libs/libcurl/src/vauth/vauth.h
+++ b/libs/libcurl/src/vauth/vauth.h
@@ -26,7 +26,7 @@
#include <curl/curl.h>
-#include "bufref.h"
+#include "../bufref.h"
struct Curl_easy;
@@ -60,7 +60,7 @@ struct gsasldata;
*/
bool Curl_auth_allowed_to_host(struct Curl_easy *data);
-/* This is used to build a SPN string */
+/* This is used to build an SPN string */
#if !defined(USE_WINDOWS_SSPI)
char *Curl_auth_build_spn(const char *service, const char *host,
const char *realm);
@@ -167,6 +167,8 @@ CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data,
/* This is used to clean up the NTLM specific data */
void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm);
+#else
+#define Curl_auth_is_ntlm_supported() FALSE
#endif /* USE_NTLM */
/* This is used to generate a base64 encoded OAuth 2.0 message */
@@ -207,6 +209,8 @@ CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
/* This is used to clean up the GSSAPI specific data */
void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5);
+#else
+#define Curl_auth_is_gssapi_supported() FALSE
#endif /* USE_KERBEROS5 */
#if defined(USE_SPNEGO)
diff --git a/libs/libcurl/src/version.c b/libs/libcurl/src/version.c
index 34eaa314f2..283a9d30b4 100644
--- a/libs/libcurl/src/version.c
+++ b/libs/libcurl/src/version.c
@@ -38,10 +38,6 @@
#include "easy_lock.h"
#ifdef USE_ARES
-# if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \
- defined(_WIN32)
-# define CARES_STATICLIB
-# endif
# include <ares.h>
#endif
@@ -210,9 +206,6 @@ char *curl_version(void)
#ifdef USE_LIBRTMP
char rtmp_version[30];
#endif
-#ifdef USE_HYPER
- char hyper_buf[30];
-#endif
#ifdef USE_GSASL
char gsasl_buf[30];
#endif
@@ -277,10 +270,6 @@ char *curl_version(void)
Curl_rtmp_version(rtmp_version, sizeof(rtmp_version));
src[i++] = rtmp_version;
#endif
-#ifdef USE_HYPER
- msnprintf(hyper_buf, sizeof(hyper_buf), "Hyper/%s", hyper_version());
- src[i++] = hyper_buf;
-#endif
#ifdef USE_GSASL
msnprintf(gsasl_buf, sizeof(gsasl_buf), "libgsasl/%s",
gsasl_check_version(NULL));
@@ -423,13 +412,16 @@ static const char * const supported_protocols[] = {
* curl_global_init() and curl_global_cleanup() calls.
*/
-#if defined(USE_LIBIDN2)
+#if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN)
static int idn_present(curl_version_info_data *info)
{
+#if defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN)
+ (void)info;
+ return TRUE;
+#else
return info->libidn != NULL;
+#endif
}
-#else
-#define idn_present NULL
#endif
#if defined(USE_SSL) && !defined(CURL_DISABLE_PROXY) && \
@@ -468,6 +460,9 @@ static const struct feat features_table[] = {
#ifndef CURL_DISABLE_ALTSVC
FEATURE("alt-svc", NULL, CURL_VERSION_ALTSVC),
#endif
+#if defined(USE_ARES) && defined(CURLRES_THREADED) && defined(USE_HTTPSRR)
+ FEATURE("asyn-rr", NULL, 0),
+#endif
#ifdef CURLRES_ASYNCH
FEATURE("AsynchDNS", NULL, CURL_VERSION_ASYNCHDNS),
#endif
@@ -479,6 +474,10 @@ static const struct feat features_table[] = {
#endif
#if defined(USE_SSL) && defined(USE_ECH)
FEATURE("ECH", ech_present, 0),
+
+#ifndef USE_HTTPSRR
+#error "ECH enabled but not HTTPSRR, must be a config error"
+#endif
#endif
#ifdef USE_GSASL
FEATURE("gsasl", NULL, CURL_VERSION_GSASL),
@@ -499,6 +498,9 @@ static const struct feat features_table[] = {
!defined(CURL_DISABLE_HTTP)
FEATURE("HTTPS-proxy", https_proxy_present, CURL_VERSION_HTTPS_PROXY),
#endif
+#if defined(USE_HTTPSRR)
+ FEATURE("HTTPSRR", NULL, 0),
+#endif
#if defined(USE_LIBIDN2) || defined(USE_WIN32_IDN) || defined(USE_APPLE_IDN)
FEATURE("IDN", idn_present, CURL_VERSION_IDN),
#endif
@@ -530,6 +532,9 @@ static const struct feat features_table[] = {
#ifdef USE_SSL
FEATURE("SSL", NULL, CURL_VERSION_SSL),
#endif
+#if defined(USE_SSLS_EXPORT)
+ FEATURE("SSLS-EXPORT", NULL, 0),
+#endif
#ifdef USE_WINDOWS_SSPI
FEATURE("SSPI", NULL, CURL_VERSION_SSPI),
#endif
@@ -551,7 +556,7 @@ static const struct feat features_table[] = {
#ifdef HAVE_ZSTD
FEATURE("zstd", NULL, CURL_VERSION_ZSTD),
#endif
- {NULL, NULL, 0}
+ {NULL, NULL, 0}
};
static const char *feature_names[sizeof(features_table) /
@@ -676,14 +681,6 @@ curl_version_info_data *curl_version_info(CURLversion stamp)
}
#endif
-#ifdef USE_HYPER
- {
- static char hyper_buffer[30];
- msnprintf(hyper_buffer, sizeof(hyper_buffer), "Hyper/%s", hyper_version());
- version_info.hyper_version = hyper_buffer;
- }
-#endif
-
#ifdef USE_GSASL
{
version_info.gsasl_version = gsasl_check_version(NULL);
diff --git a/libs/libcurl/src/vquic/curl_msh3.c b/libs/libcurl/src/vquic/curl_msh3.c
index 0ce9fe4cac..8f9590391f 100644
--- a/libs/libcurl/src/vquic/curl_msh3.c
+++ b/libs/libcurl/src/vquic/curl_msh3.c
@@ -22,30 +22,32 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_MSH3
-#include "urldata.h"
-#include "hash.h"
-#include "timeval.h"
-#include "multiif.h"
-#include "sendf.h"
-#include "curl_trc.h"
-#include "cfilters.h"
-#include "cf-socket.h"
-#include "connect.h"
-#include "progress.h"
-#include "http1.h"
+#include "../urldata.h"
+#include "../hash.h"
+#include "../uint-hash.h"
+#include "../curlx/timeval.h"
+#include "../multiif.h"
+#include "../sendf.h"
+#include "../curl_trc.h"
+#include "../cfilters.h"
+#include "../cf-socket.h"
+#include "../connect.h"
+#include "../progress.h"
+#include "../http1.h"
#include "curl_msh3.h"
-#include "socketpair.h"
-#include "vtls/vtls.h"
-#include "vquic/vquic.h"
+#include "../socketpair.h"
+#include "../vtls/vtls.h"
+#include "vquic.h"
+#include "vquic_int.h"
/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
#ifdef CURL_DISABLE_SOCKETPAIR
#error "MSH3 cannot be build with CURL_DISABLE_SOCKETPAIR set"
@@ -119,34 +121,42 @@ struct cf_msh3_ctx {
struct cf_call_data call_data;
struct curltime connect_started; /* time the current attempt started */
struct curltime handshake_at; /* time connect handshake finished */
- struct Curl_hash streams; /* hash `data->mid` to `stream_ctx` */
+ struct uint_hash streams; /* hash `data->mid` to `stream_ctx` */
/* Flags written by msh3/msquic thread */
- bool handshake_complete;
- bool handshake_succeeded;
- bool connected;
+ BIT(handshake_complete);
+ BIT(handshake_succeeded);
+ BIT(connected);
BIT(initialized);
/* Flags written by curl thread */
BIT(verbose);
BIT(active);
};
-static void h3_stream_hash_free(void *stream);
+static void h3_stream_hash_free(unsigned int id, void *stream);
-static void cf_msh3_ctx_init(struct cf_msh3_ctx *ctx,
- const struct Curl_addrinfo *ai)
+static CURLcode cf_msh3_ctx_init(struct cf_msh3_ctx *ctx,
+ const struct Curl_addrinfo *ai)
{
+ CURLcode result;
+
DEBUGASSERT(!ctx->initialized);
- Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free);
- Curl_sock_assign_addr(&ctx->addr, ai, TRNSPRT_QUIC);
+ Curl_uint_hash_init(&ctx->streams, 63, h3_stream_hash_free);
+
+ result = Curl_sock_assign_addr(&ctx->addr, ai, TRNSPRT_QUIC);
+ if(result)
+ return result;
+
ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
ctx->initialized = TRUE;
+
+ return result;
}
static void cf_msh3_ctx_free(struct cf_msh3_ctx *ctx)
{
if(ctx && ctx->initialized) {
- Curl_hash_destroy(&ctx->streams);
+ Curl_uint_hash_destroy(&ctx->streams);
}
free(ctx);
}
@@ -161,7 +171,7 @@ static struct cf_msh3_ctx *h3_get_msh3_ctx(struct Curl_easy *data);
/**
* All about the H3 internals of a stream
*/
-struct stream_ctx {
+struct h3_stream_ctx {
struct MSH3_REQUEST *req;
struct bufq recvbuf; /* h3 response */
#ifdef _WIN32
@@ -172,33 +182,31 @@ struct stream_ctx {
uint64_t error3; /* HTTP/3 stream error code */
int status_code; /* HTTP status code */
CURLcode recv_error;
- bool closed;
- bool reset;
- bool upload_done;
- bool firstheader; /* FALSE until headers arrive */
- bool recv_header_complete;
+ BIT(closed);
+ BIT(reset);
+ BIT(upload_done);
+ BIT(firstheader); /* FALSE until headers arrive */
+ BIT(recv_header_complete);
};
-#define H3_STREAM_CTX(ctx,data) ((struct stream_ctx *)((data && ctx)? \
- Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL))
-
-static void h3_stream_ctx_free(struct stream_ctx *stream)
+static void h3_stream_ctx_free(struct h3_stream_ctx *stream)
{
Curl_bufq_free(&stream->recvbuf);
free(stream);
}
-static void h3_stream_hash_free(void *stream)
+static void h3_stream_hash_free(unsigned int id, void *stream)
{
+ (void)id;
DEBUGASSERT(stream);
- h3_stream_ctx_free((struct stream_ctx *)stream);
+ h3_stream_ctx_free((struct h3_stream_ctx *)stream);
}
static CURLcode h3_data_setup(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_msh3_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
if(stream)
return CURLE_OK;
@@ -213,7 +221,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf,
H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
CURL_TRC_CF(data, cf, "data setup");
- if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) {
+ if(!Curl_uint_hash_set(&ctx->streams, data->mid, stream)) {
h3_stream_ctx_free(stream);
return CURLE_OUT_OF_MEMORY;
}
@@ -224,17 +232,17 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf,
static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct cf_msh3_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
(void)cf;
if(stream) {
CURL_TRC_CF(data, cf, "easy handle is done");
- Curl_hash_offt_remove(&ctx->streams, data->mid);
+ Curl_uint_hash_remove(&ctx->streams, data->mid);
}
}
static void drain_stream_from_other_thread(struct Curl_easy *data,
- struct stream_ctx *stream)
+ struct h3_stream_ctx *stream)
{
unsigned char bits;
@@ -252,7 +260,7 @@ static void h3_drain_stream(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_msh3_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
unsigned char bits;
(void)cf;
@@ -351,7 +359,7 @@ static CURLcode write_resp_raw(struct Curl_easy *data,
const void *mem, size_t memlen)
{
struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data);
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
CURLcode result = CURLE_OK;
ssize_t nwritten;
@@ -378,7 +386,7 @@ static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
{
struct Curl_easy *data = userp;
struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data);
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
CURLcode result;
(void)Request;
@@ -390,7 +398,7 @@ static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
msh3_lock_acquire(&stream->recv_lock);
if((hd->NameLength == 7) &&
- !strncmp(HTTP_PSEUDO_STATUS, (char *)hd->Name, 7)) {
+ !strncmp(HTTP_PSEUDO_STATUS, (const char *)hd->Name, 7)) {
char line[14]; /* status line is always 13 characters long */
size_t ncopy;
@@ -429,14 +437,14 @@ static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
{
struct Curl_easy *data = IfContext;
struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data);
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
CURLcode result;
bool rv = FALSE;
- /* TODO: we would like to limit the amount of data we are buffer here.
- * There seems to be no mechanism in msh3 to adjust flow control and
- * it is undocumented what happens if we return FALSE here or less
- * length (buflen is an inout parameter).
+ /* We would like to limit the amount of data we are buffer here. There seems
+ * to be no mechanism in msh3 to adjust flow control and it is undocumented
+ * what happens if we return FALSE here or less length (buflen is an inout
+ * parameter).
*/
(void)Request;
if(!stream)
@@ -469,7 +477,7 @@ static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
{
struct Curl_easy *data = IfContext;
struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data);
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
(void)Request;
if(!stream)
@@ -489,7 +497,7 @@ static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request,
{
struct Curl_easy *data = IfContext;
struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data);
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
if(!stream)
return;
@@ -502,7 +510,7 @@ static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request,
{
struct Curl_easy *data = IfContext;
struct cf_msh3_ctx *ctx = h3_get_msh3_ctx(data);
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
if(!stream)
return;
(void)Request;
@@ -515,7 +523,7 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
CURLcode *err)
{
struct cf_msh3_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
ssize_t nread = -1;
if(!stream) {
@@ -549,7 +557,7 @@ out:
static void set_quic_expire(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct cf_msh3_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
/* we have no indication from msh3 when it would be a good time
* to juggle the connection again. So, we compromise by calling
@@ -567,7 +575,7 @@ static ssize_t cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
char *buf, size_t len, CURLcode *err)
{
struct cf_msh3_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
ssize_t nread = -1;
struct cf_call_data save;
@@ -619,7 +627,7 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data,
CURLcode *err)
{
struct cf_msh3_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
struct h1_req_parser h1;
struct dynhds h2_headers;
MSH3_HEADER *nva = NULL;
@@ -695,8 +703,8 @@ static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data,
goto out;
}
- /* TODO - msh3/msquic will hold onto this memory until the send complete
- event. How do we make sure curl does not free it until then? */
+ /* msh3/msquic will hold onto this memory until the send complete event.
+ How do we make sure curl does not free it until then? */
*err = CURLE_OK;
nwritten = len;
}
@@ -715,7 +723,7 @@ static void cf_msh3_adjust_pollset(struct Curl_cfilter *cf,
struct easy_pollset *ps)
{
struct cf_msh3_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
struct cf_call_data save;
CF_DATA_SAVE(save, cf, data);
@@ -735,7 +743,7 @@ static bool cf_msh3_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data)
{
struct cf_msh3_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
struct cf_call_data save;
bool pending = FALSE;
@@ -744,12 +752,13 @@ static bool cf_msh3_data_pending(struct Curl_cfilter *cf,
(void)cf;
if(stream && stream->req) {
msh3_lock_acquire(&stream->recv_lock);
- CURL_TRC_CF((struct Curl_easy *)data, cf, "data pending = %zu",
+ CURL_TRC_CF((struct Curl_easy *)CURL_UNCONST(data), cf,
+ "data pending = %zu",
Curl_bufq_len(&stream->recvbuf));
pending = !Curl_bufq_is_empty(&stream->recvbuf);
msh3_lock_release(&stream->recv_lock);
if(pending)
- h3_drain_stream(cf, (struct Curl_easy *)data);
+ h3_drain_stream(cf, (struct Curl_easy *)CURL_UNCONST(data));
}
CF_DATA_RESTORE(cf, save);
@@ -772,7 +781,7 @@ static CURLcode cf_msh3_data_event(struct Curl_cfilter *cf,
int event, int arg1, void *arg2)
{
struct cf_msh3_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
struct cf_call_data save;
CURLcode result = CURLE_OK;
@@ -830,16 +839,10 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
MSH3_SET_PORT(&addr, (uint16_t)cf->conn->remote_port);
if(verify && (conn_config->CAfile || conn_config->CApath)) {
- /* TODO: need a way to provide trust anchors to MSH3 */
-#ifdef DEBUGBUILD
- /* we need this for our test cases to run */
- CURL_TRC_CF(data, cf, "non-standard CA not supported, "
- "switching off verifypeer in DEBUG mode");
- verify = 0;
-#else
+ /* Note there's currently no way to provide trust anchors to MSH3 and
+ that causes tests to fail. */
CURL_TRC_CF(data, cf, "non-standard CA not supported, "
"attempting with built-in verification");
-#endif
}
CURL_TRC_CF(data, cf, "connecting to %s:%d (verify=%d)",
@@ -875,13 +878,12 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
static CURLcode cf_msh3_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool blocking, bool *done)
+ bool *done)
{
struct cf_msh3_ctx *ctx = cf->ctx;
struct cf_call_data save;
CURLcode result = CURLE_OK;
- (void)blocking;
if(cf->connected) {
*done = TRUE;
return CURLE_OK;
@@ -899,18 +901,17 @@ static CURLcode cf_msh3_connect(struct Curl_cfilter *cf,
*done = FALSE;
if(!ctx->qconn) {
- ctx->connect_started = Curl_now();
+ ctx->connect_started = curlx_now();
result = cf_connect_start(cf, data);
if(result)
goto out;
}
if(ctx->handshake_complete) {
- ctx->handshake_at = Curl_now();
+ ctx->handshake_at = curlx_now();
if(ctx->handshake_succeeded) {
CURL_TRC_CF(data, cf, "handshake succeeded");
cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
- cf->conn->httpversion = 30;
cf->connected = TRUE;
cf->conn->alpn = CURL_HTTP_VERSION_3;
*done = TRUE;
@@ -999,7 +1000,7 @@ static CURLcode cf_msh3_query(struct Curl_cfilter *cf,
switch(query) {
case CF_QUERY_MAX_CONCURRENT: {
- /* TODO: we do not have access to this so far, fake it */
+ /* We do not have access to this so far, fake it */
(void)ctx;
*pres1 = 100;
return CURLE_OK;
@@ -1017,6 +1018,9 @@ static CURLcode cf_msh3_query(struct Curl_cfilter *cf,
*when = ctx->handshake_at;
return CURLE_OK;
}
+ case CF_QUERY_HTTP_VERSION:
+ *pres1 = 30;
+ return CURLE_OK;
default:
break;
}
@@ -1039,7 +1043,7 @@ static bool cf_msh3_conn_is_alive(struct Curl_cfilter *cf,
struct Curl_cftype Curl_cft_http3 = {
"HTTP/3",
- CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
+ CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX | CF_TYPE_HTTP,
0,
cf_msh3_destroy,
cf_msh3_connect,
@@ -1081,13 +1085,16 @@ CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf,
(void)data;
(void)conn;
- (void)ai; /* TODO: msh3 resolves itself? */
+ (void)ai; /* msh3 resolves itself? */
ctx = calloc(1, sizeof(*ctx));
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
}
- cf_msh3_ctx_init(ctx, ai);
+
+ result = cf_msh3_ctx_init(ctx, ai);
+ if(result)
+ goto out;
result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
diff --git a/libs/libcurl/src/vquic/curl_msh3.h b/libs/libcurl/src/vquic/curl_msh3.h
index e3f40c2ab9..259011499b 100644
--- a/libs/libcurl/src/vquic/curl_msh3.h
+++ b/libs/libcurl/src/vquic/curl_msh3.h
@@ -24,7 +24,7 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_MSH3
diff --git a/libs/libcurl/src/vquic/curl_ngtcp2.c b/libs/libcurl/src/vquic/curl_ngtcp2.c
index b017b49ace..d5a9553677 100644
--- a/libs/libcurl/src/vquic/curl_ngtcp2.c
+++ b/libs/libcurl/src/vquic/curl_ngtcp2.c
@@ -22,7 +22,7 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
#include <ngtcp2/ngtcp2.h>
@@ -32,48 +32,51 @@
#include <openssl/err.h>
#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
#include <ngtcp2/ngtcp2_crypto_boringssl.h>
+#elif defined(OPENSSL_QUIC_API2)
+#include <ngtcp2/ngtcp2_crypto_ossl.h>
#else
#include <ngtcp2/ngtcp2_crypto_quictls.h>
#endif
-#include "vtls/openssl.h"
+#include "../vtls/openssl.h"
#elif defined(USE_GNUTLS)
#include <ngtcp2/ngtcp2_crypto_gnutls.h>
-#include "vtls/gtls.h"
+#include "../vtls/gtls.h"
#elif defined(USE_WOLFSSL)
#include <ngtcp2/ngtcp2_crypto_wolfssl.h>
-#include "vtls/wolfssl.h"
+#include "../vtls/wolfssl.h"
#endif
-#include "urldata.h"
-#include "hash.h"
-#include "sendf.h"
-#include "strdup.h"
-#include "rand.h"
-#include "multiif.h"
-#include "strcase.h"
-#include "cfilters.h"
-#include "cf-socket.h"
-#include "connect.h"
-#include "progress.h"
-#include "strerror.h"
-#include "dynbuf.h"
-#include "http1.h"
-#include "select.h"
-#include "inet_pton.h"
-#include "transfer.h"
+#include "../urldata.h"
+#include "../uint-hash.h"
+#include "../sendf.h"
+#include "../strdup.h"
+#include "../rand.h"
+#include "../multiif.h"
+#include "../strcase.h"
+#include "../cfilters.h"
+#include "../cf-socket.h"
+#include "../connect.h"
+#include "../progress.h"
+#include "../strerror.h"
+#include "../curlx/dynbuf.h"
+#include "../http1.h"
+#include "../select.h"
+#include "../curlx/inet_pton.h"
+#include "../transfer.h"
#include "vquic.h"
#include "vquic_int.h"
#include "vquic-tls.h"
-#include "vtls/keylog.h"
-#include "vtls/vtls.h"
+#include "../vtls/keylog.h"
+#include "../vtls/vtls.h"
+#include "../vtls/vtls_scache.h"
#include "curl_ngtcp2.h"
-#include "warnless.h"
+#include "../curlx/warnless.h"
/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
#define QUIC_MAX_STREAMS (256*1024)
@@ -86,6 +89,10 @@
* Chunk size is large enough to take a full DATA frame */
#define H3_STREAM_WINDOW_SIZE (128 * 1024)
#define H3_STREAM_CHUNK_SIZE (16 * 1024)
+#if H3_STREAM_CHUNK_SIZE < NGTCP2_MAX_UDP_PAYLOAD_SIZE
+#error H3_STREAM_CHUNK_SIZE smaller than NGTCP2_MAX_UDP_PAYLOAD_SIZE
+#endif
+
/* The pool keeps spares around and half of a full stream windows
* seems good. More does not seem to improve performance.
* The benefit of the pool is that stream buffer to not keep
@@ -116,6 +123,9 @@ struct cf_ngtcp2_ctx {
struct cf_quic_ctx q;
struct ssl_peer peer;
struct curl_tls_ctx tls;
+#ifdef OPENSSL_QUIC_API2
+ ngtcp2_crypto_ossl_ctx *ossl_ctx;
+#endif
ngtcp2_path connected_path;
ngtcp2_conn *qconn;
ngtcp2_cid dcid;
@@ -132,13 +142,20 @@ struct cf_ngtcp2_ctx {
struct curltime handshake_at; /* time connect handshake finished */
struct bufc_pool stream_bufcp; /* chunk pool for streams */
struct dynbuf scratch; /* temp buffer for header construction */
- struct Curl_hash streams; /* hash `data->mid` to `h3_stream_ctx` */
+ struct uint_hash streams; /* hash `data->mid` to `h3_stream_ctx` */
size_t max_stream_window; /* max flow window for one stream */
- uint64_t max_idle_ms; /* max idle time for QUIC connection */
uint64_t used_bidi_streams; /* bidi streams we have opened */
uint64_t max_bidi_streams; /* max bidi streams we can open */
+ size_t earlydata_max; /* max amount of early data supported by
+ server on session reuse */
+ size_t earlydata_skip; /* sending bytes to skip when earlydata
+ * is accepted by peer */
+ CURLcode tls_vrfy_result; /* result of TLS peer verification */
int qlogfd;
BIT(initialized);
+ BIT(tls_handshake_complete); /* TLS handshake is done */
+ BIT(use_earlydata); /* Using 0RTT data */
+ BIT(earlydata_accepted); /* 0RTT was acceptd by server */
BIT(shutdown_started); /* queued shutdown packets */
};
@@ -147,7 +164,7 @@ struct cf_ngtcp2_ctx {
#define CF_CTX_CALL_DATA(cf) \
((struct cf_ngtcp2_ctx *)(cf)->ctx)->call_data
-static void h3_stream_hash_free(void *stream);
+static void h3_stream_hash_free(unsigned int id, void *stream);
static void cf_ngtcp2_ctx_init(struct cf_ngtcp2_ctx *ctx)
{
@@ -155,26 +172,64 @@ static void cf_ngtcp2_ctx_init(struct cf_ngtcp2_ctx *ctx)
ctx->qlogfd = -1;
ctx->version = NGTCP2_PROTO_VER_MAX;
ctx->max_stream_window = H3_STREAM_WINDOW_SIZE;
- ctx->max_idle_ms = CURL_QUIC_MAX_IDLE_MS;
Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
H3_STREAM_POOL_SPARES);
- Curl_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER);
- Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free);
+ curlx_dyn_init(&ctx->scratch, CURL_MAX_HTTP_HEADER);
+ Curl_uint_hash_init(&ctx->streams, 63, h3_stream_hash_free);
ctx->initialized = TRUE;
}
static void cf_ngtcp2_ctx_free(struct cf_ngtcp2_ctx *ctx)
{
if(ctx && ctx->initialized) {
+ Curl_vquic_tls_cleanup(&ctx->tls);
+ vquic_ctx_free(&ctx->q);
Curl_bufcp_free(&ctx->stream_bufcp);
- Curl_dyn_free(&ctx->scratch);
- Curl_hash_clean(&ctx->streams);
- Curl_hash_destroy(&ctx->streams);
+ curlx_dyn_free(&ctx->scratch);
+ Curl_uint_hash_destroy(&ctx->streams);
Curl_ssl_peer_cleanup(&ctx->peer);
}
free(ctx);
}
+static void cf_ngtcp2_setup_keep_alive(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ const ngtcp2_transport_params *rp;
+ /* Peer should have sent us its transport parameters. If it
+ * announces a positive `max_idle_timeout` it will close the
+ * connection when it does not hear from us for that time.
+ *
+ * Some servers use this as a keep-alive timer at a rather low
+ * value. We are doing HTTP/3 here and waiting for the response
+ * to a request may take a considerable amount of time. We need
+ * to prevent the peer's QUIC stack from closing in this case.
+ */
+ if(!ctx->qconn)
+ return;
+
+ rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn);
+ if(!rp || !rp->max_idle_timeout) {
+ ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, UINT64_MAX);
+ CURL_TRC_CF(data, cf, "no peer idle timeout, unset keep-alive");
+ }
+ else if(!Curl_uint_hash_count(&ctx->streams)) {
+ ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, UINT64_MAX);
+ CURL_TRC_CF(data, cf, "no active streams, unset keep-alive");
+ }
+ else {
+ ngtcp2_duration keep_ns;
+ keep_ns = (rp->max_idle_timeout > 1) ? (rp->max_idle_timeout / 2) : 1;
+ ngtcp2_conn_set_keep_alive_timeout(ctx->qconn, keep_ns);
+ CURL_TRC_CF(data, cf, "peer idle timeout is %" FMT_PRIu64 "ms, "
+ "set keep-alive to %" FMT_PRIu64 " ms.",
+ (curl_uint64_t)(rp->max_idle_timeout / NGTCP2_MILLISECONDS),
+ (curl_uint64_t)(keep_ns / NGTCP2_MILLISECONDS));
+ }
+}
+
+
struct pkt_io_ctx;
static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
struct Curl_easy *data,
@@ -195,18 +250,13 @@ struct h3_stream_ctx {
curl_off_t upload_left; /* number of request bytes left to upload */
int status_code; /* HTTP status code */
CURLcode xfer_result; /* result from xfer_resp_write(_hd) */
- bool resp_hds_complete; /* we have a complete, final response */
- bool closed; /* TRUE on stream close */
- bool reset; /* TRUE on stream reset */
- bool send_closed; /* stream is local closed */
+ BIT(resp_hds_complete); /* we have a complete, final response */
+ BIT(closed); /* TRUE on stream close */
+ BIT(reset); /* TRUE on stream reset */
+ BIT(send_closed); /* stream is local closed */
BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */
};
-#define H3_STREAM_CTX(ctx,data) ((struct h3_stream_ctx *)(\
- data? Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL))
-#define H3_STREAM_CTX_ID(ctx,id) ((struct h3_stream_ctx *)(\
- Curl_hash_offt_get(&(ctx)->streams, (id))))
-
static void h3_stream_ctx_free(struct h3_stream_ctx *stream)
{
Curl_bufq_free(&stream->sendbuf);
@@ -214,8 +264,9 @@ static void h3_stream_ctx_free(struct h3_stream_ctx *stream)
free(stream);
}
-static void h3_stream_hash_free(void *stream)
+static void h3_stream_hash_free(unsigned int id, void *stream)
{
+ (void)id;
DEBUGASSERT(stream);
h3_stream_ctx_free((struct h3_stream_ctx *)stream);
}
@@ -243,11 +294,14 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf,
stream->sendbuf_len_in_flight = 0;
Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
- if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) {
+ if(!Curl_uint_hash_set(&ctx->streams, data->mid, stream)) {
h3_stream_ctx_free(stream);
return CURLE_OUT_OF_MEMORY;
}
+ if(Curl_uint_hash_count(&ctx->streams) == 1)
+ cf_ngtcp2_setup_keep_alive(cf, data);
+
return CURLE_OK;
}
@@ -282,40 +336,10 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] easy handle is done",
stream->id);
cf_ngtcp2_stream_close(cf, data, stream);
- Curl_hash_offt_remove(&ctx->streams, data->mid);
- }
-}
-
-static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- int64_t stream_id,
- struct h3_stream_ctx **pstream)
-{
- struct cf_ngtcp2_ctx *ctx = cf->ctx;
- struct h3_stream_ctx *stream;
-
- (void)cf;
- stream = H3_STREAM_CTX(ctx, data);
- if(stream && stream->id == stream_id) {
- *pstream = stream;
- return data;
- }
- else {
- struct Curl_llist_node *e;
- DEBUGASSERT(data->multi);
- for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
- struct Curl_easy *sdata = Curl_node_elem(e);
- if(sdata->conn != data->conn)
- continue;
- stream = H3_STREAM_CTX(ctx, sdata);
- if(stream && stream->id == stream_id) {
- *pstream = stream;
- return sdata;
- }
- }
+ Curl_uint_hash_remove(&ctx->streams, data->mid);
+ if(!Curl_uint_hash_count(&ctx->streams))
+ cf_ngtcp2_setup_keep_alive(cf, data);
}
- *pstream = NULL;
- return NULL;
}
static void h3_drain_stream(struct Curl_cfilter *cf,
@@ -383,7 +407,7 @@ static void quic_printf(void *user_data, const char *fmt, ...)
struct Curl_cfilter *cf = user_data;
struct cf_ngtcp2_ctx *ctx = cf->ctx;
- (void)ctx; /* TODO: need an easy handle to infof() message */
+ (void)ctx; /* need an easy handle to infof() message */
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
@@ -436,18 +460,66 @@ static void quic_settings(struct cf_ngtcp2_ctx *ctx,
t->initial_max_stream_data_uni = ctx->max_stream_window;
t->initial_max_streams_bidi = QUIC_MAX_STREAMS;
t->initial_max_streams_uni = QUIC_MAX_STREAMS;
- t->max_idle_timeout = (ctx->max_idle_ms * NGTCP2_MILLISECONDS);
+ t->max_idle_timeout = 0; /* no idle timeout from our side */
if(ctx->qlogfd != -1) {
s->qlog_write = qlog_callback;
}
}
-static CURLcode init_ngh3_conn(struct Curl_cfilter *cf);
+static CURLcode init_ngh3_conn(struct Curl_cfilter *cf,
+ struct Curl_easy *data);
-static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data)
+static int cf_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data)
{
- (void)user_data;
+ struct Curl_cfilter *cf = user_data;
+ struct cf_ngtcp2_ctx *ctx = cf ? cf->ctx : NULL;
+ struct Curl_easy *data;
+
(void)tconn;
+ DEBUGASSERT(ctx);
+ data = CF_DATA_CURRENT(cf);
+ DEBUGASSERT(data);
+ if(!ctx || !data)
+ return NGHTTP3_ERR_CALLBACK_FAILURE;
+
+ ctx->handshake_at = curlx_now();
+ ctx->tls_handshake_complete = TRUE;
+ cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
+
+ ctx->tls_vrfy_result = Curl_vquic_tls_verify_peer(&ctx->tls, cf,
+ data, &ctx->peer);
+ CURL_TRC_CF(data, cf, "handshake complete after %dms",
+ (int)curlx_timediff(ctx->handshake_at, ctx->started_at));
+ /* In case of earlydata, where we simulate being connected, update
+ * the handshake time when we really did connect */
+ if(ctx->use_earlydata)
+ Curl_pgrsTimeWas(data, TIMER_APPCONNECT, ctx->handshake_at);
+ if(ctx->use_earlydata) {
+#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA)
+ ctx->earlydata_accepted =
+ (SSL_get_early_data_status(ctx->tls.ossl.ssl) !=
+ SSL_EARLY_DATA_REJECTED);
+#endif
+#ifdef USE_GNUTLS
+ int flags = gnutls_session_get_flags(ctx->tls.gtls.session);
+ ctx->earlydata_accepted = !!(flags & GNUTLS_SFLAGS_EARLY_DATA);
+#endif
+#ifdef USE_WOLFSSL
+#ifdef WOLFSSL_EARLY_DATA
+ ctx->earlydata_accepted =
+ (wolfSSL_get_early_data_status(ctx->tls.wssl.ssl) !=
+ WOLFSSL_EARLY_DATA_REJECTED);
+#else
+ DEBUGASSERT(0); /* should not come here if ED is disabled. */
+ ctx->earlydata_accepted = FALSE;
+#endif /* WOLFSSL_EARLY_DATA */
+#endif
+ CURL_TRC_CF(data, cf, "server did%s accept %zu bytes of early data",
+ ctx->earlydata_accepted ? "" : " not", ctx->earlydata_skip);
+ Curl_pgrsEarlyData(data, ctx->earlydata_accepted ?
+ (curl_off_t)ctx->earlydata_skip :
+ -(curl_off_t)ctx->earlydata_skip);
+ }
return 0;
}
@@ -519,7 +591,7 @@ static int cb_recv_stream_data(ngtcp2_conn *tconn, uint32_t flags,
CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] read_stream(len=%zu) -> %zd",
stream_id, buflen, nconsumed);
if(nconsumed < 0) {
- struct h3_stream_ctx *stream = H3_STREAM_CTX_ID(ctx, stream_id);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
if(data && stream) {
CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] error on known stream, "
"reset=%d, closed=%d",
@@ -651,28 +723,26 @@ static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn,
return 0;
}
-static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t sid,
+static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id,
uint64_t max_data, void *user_data,
void *stream_user_data)
{
struct Curl_cfilter *cf = user_data;
struct cf_ngtcp2_ctx *ctx = cf->ctx;
- curl_int64_t stream_id = (curl_int64_t)sid;
- struct Curl_easy *data = CF_DATA_CURRENT(cf);
- struct Curl_easy *s_data;
+ struct Curl_easy *s_data = stream_user_data;
struct h3_stream_ctx *stream;
int rv;
(void)tconn;
(void)max_data;
- (void)stream_user_data;
rv = nghttp3_conn_unblock_stream(ctx->h3conn, stream_id);
if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
return NGTCP2_ERR_CALLBACK_FAILURE;
}
- s_data = get_stream_easy(cf, data, stream_id, &stream);
- if(s_data && stream && stream->quic_flow_blocked) {
- CURL_TRC_CF(s_data, cf, "[%" FMT_PRId64 "] unblock quic flow", stream_id);
+ stream = H3_STREAM_CTX(ctx, s_data);
+ if(stream && stream->quic_flow_blocked) {
+ CURL_TRC_CF(s_data, cf, "[%" FMT_PRId64 "] unblock quic flow",
+ (curl_int64_t)stream_id);
stream->quic_flow_blocked = FALSE;
h3_drain_stream(cf, s_data);
}
@@ -717,16 +787,19 @@ static int cb_recv_rx_key(ngtcp2_conn *tconn, ngtcp2_encryption_level level,
void *user_data)
{
struct Curl_cfilter *cf = user_data;
+ struct cf_ngtcp2_ctx *ctx = cf ? cf->ctx : NULL;
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
(void)tconn;
- if(level != NGTCP2_ENCRYPTION_LEVEL_1RTT) {
+ if(level != NGTCP2_ENCRYPTION_LEVEL_1RTT)
return 0;
- }
- if(init_ngh3_conn(cf) != CURLE_OK) {
- return NGTCP2_ERR_CALLBACK_FAILURE;
+ DEBUGASSERT(ctx);
+ DEBUGASSERT(data);
+ if(ctx && data && !ctx->h3conn) {
+ if(init_ngh3_conn(cf, data))
+ return NGTCP2_ERR_CALLBACK_FAILURE;
}
-
return 0;
}
@@ -739,7 +812,7 @@ static ngtcp2_callbacks ng_callbacks = {
ngtcp2_crypto_client_initial_cb,
NULL, /* recv_client_initial */
ngtcp2_crypto_recv_crypto_data_cb,
- cb_handshake_completed,
+ cf_ngtcp2_handshake_completed,
NULL, /* recv_version_negotiation */
ngtcp2_crypto_encrypt_cb,
ngtcp2_crypto_decrypt_cb,
@@ -949,7 +1022,7 @@ static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id,
if(!stream)
return NGHTTP3_ERR_CALLBACK_FAILURE;
- h3_xfer_write_resp(cf, data, stream, (char *)buf, blen, FALSE);
+ h3_xfer_write_resp(cf, data, stream, (const char *)buf, blen, FALSE);
if(blen) {
CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] ACK %zu bytes of DATA",
stream->id, blen);
@@ -1032,18 +1105,18 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid,
(const char *)h3val.base, h3val.len);
if(result)
return -1;
- Curl_dyn_reset(&ctx->scratch);
- result = Curl_dyn_addn(&ctx->scratch, STRCONST("HTTP/3 "));
+ curlx_dyn_reset(&ctx->scratch);
+ result = curlx_dyn_addn(&ctx->scratch, STRCONST("HTTP/3 "));
if(!result)
- result = Curl_dyn_addn(&ctx->scratch,
- (const char *)h3val.base, h3val.len);
+ result = curlx_dyn_addn(&ctx->scratch,
+ (const char *)h3val.base, h3val.len);
if(!result)
- result = Curl_dyn_addn(&ctx->scratch, STRCONST(" \r\n"));
+ result = curlx_dyn_addn(&ctx->scratch, STRCONST(" \r\n"));
if(!result)
- h3_xfer_write_resp_hd(cf, data, stream, Curl_dyn_ptr(&ctx->scratch),
- Curl_dyn_len(&ctx->scratch), FALSE);
+ h3_xfer_write_resp_hd(cf, data, stream, curlx_dyn_ptr(&ctx->scratch),
+ curlx_dyn_len(&ctx->scratch), FALSE);
CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] status: %s",
- stream_id, Curl_dyn_ptr(&ctx->scratch));
+ stream_id, curlx_dyn_ptr(&ctx->scratch));
if(result) {
return -1;
}
@@ -1053,19 +1126,19 @@ static int cb_h3_recv_header(nghttp3_conn *conn, int64_t sid,
CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] header: %.*s: %.*s",
stream_id, (int)h3name.len, h3name.base,
(int)h3val.len, h3val.base);
- Curl_dyn_reset(&ctx->scratch);
- result = Curl_dyn_addn(&ctx->scratch,
- (const char *)h3name.base, h3name.len);
+ curlx_dyn_reset(&ctx->scratch);
+ result = curlx_dyn_addn(&ctx->scratch,
+ (const char *)h3name.base, h3name.len);
if(!result)
- result = Curl_dyn_addn(&ctx->scratch, STRCONST(": "));
+ result = curlx_dyn_addn(&ctx->scratch, STRCONST(": "));
if(!result)
- result = Curl_dyn_addn(&ctx->scratch,
- (const char *)h3val.base, h3val.len);
+ result = curlx_dyn_addn(&ctx->scratch,
+ (const char *)h3val.base, h3val.len);
if(!result)
- result = Curl_dyn_addn(&ctx->scratch, STRCONST("\r\n"));
+ result = curlx_dyn_addn(&ctx->scratch, STRCONST("\r\n"));
if(!result)
- h3_xfer_write_resp_hd(cf, data, stream, Curl_dyn_ptr(&ctx->scratch),
- Curl_dyn_len(&ctx->scratch), FALSE);
+ h3_xfer_write_resp_hd(cf, data, stream, curlx_dyn_ptr(&ctx->scratch),
+ curlx_dyn_len(&ctx->scratch), FALSE);
}
return 0;
}
@@ -1128,14 +1201,15 @@ static nghttp3_callbacks ngh3_callbacks = {
NULL /* recv_settings */
};
-static CURLcode init_ngh3_conn(struct Curl_cfilter *cf)
+static CURLcode init_ngh3_conn(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
{
struct cf_ngtcp2_ctx *ctx = cf->ctx;
- CURLcode result;
- int rc;
int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id;
+ int rc;
if(ngtcp2_conn_get_streams_uni_left(ctx->qconn) < 3) {
+ failf(data, "QUIC connection lacks 3 uni streams to run HTTP/3");
return CURLE_QUIC_CONNECT_ERROR;
}
@@ -1147,45 +1221,47 @@ static CURLcode init_ngh3_conn(struct Curl_cfilter *cf)
nghttp3_mem_default(),
cf);
if(rc) {
- result = CURLE_OUT_OF_MEMORY;
- goto fail;
+ failf(data, "error creating nghttp3 connection instance");
+ return CURLE_OUT_OF_MEMORY;
}
rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &ctrl_stream_id, NULL);
if(rc) {
- result = CURLE_QUIC_CONNECT_ERROR;
- goto fail;
+ failf(data, "error creating HTTP/3 control stream: %s",
+ ngtcp2_strerror(rc));
+ return CURLE_QUIC_CONNECT_ERROR;
}
rc = nghttp3_conn_bind_control_stream(ctx->h3conn, ctrl_stream_id);
if(rc) {
- result = CURLE_QUIC_CONNECT_ERROR;
- goto fail;
+ failf(data, "error binding HTTP/3 control stream: %s",
+ ngtcp2_strerror(rc));
+ return CURLE_QUIC_CONNECT_ERROR;
}
rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_enc_stream_id, NULL);
if(rc) {
- result = CURLE_QUIC_CONNECT_ERROR;
- goto fail;
+ failf(data, "error creating HTTP/3 qpack encoding stream: %s",
+ ngtcp2_strerror(rc));
+ return CURLE_QUIC_CONNECT_ERROR;
}
rc = ngtcp2_conn_open_uni_stream(ctx->qconn, &qpack_dec_stream_id, NULL);
if(rc) {
- result = CURLE_QUIC_CONNECT_ERROR;
- goto fail;
+ failf(data, "error creating HTTP/3 qpack decoding stream: %s",
+ ngtcp2_strerror(rc));
+ return CURLE_QUIC_CONNECT_ERROR;
}
rc = nghttp3_conn_bind_qpack_streams(ctx->h3conn, qpack_enc_stream_id,
qpack_dec_stream_id);
if(rc) {
- result = CURLE_QUIC_CONNECT_ERROR;
- goto fail;
+ failf(data, "error binding HTTP/3 qpack streams: %s",
+ ngtcp2_strerror(rc));
+ return CURLE_QUIC_CONNECT_ERROR;
}
return CURLE_OK;
-fail:
-
- return result;
}
static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
@@ -1236,6 +1312,10 @@ static ssize_t cf_ngtcp2_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
DEBUGASSERT(ctx->h3conn);
*err = CURLE_OK;
+ /* handshake verification failed in callback, do not recv anything */
+ if(ctx->tls_vrfy_result)
+ return ctx->tls_vrfy_result;
+
pktx_init(&pktx, cf, data);
if(!stream || ctx->shutdown_started) {
@@ -1346,7 +1426,7 @@ cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id,
while(nvecs < veccnt &&
Curl_bufq_peek_at(&stream->sendbuf,
stream->sendbuf_len_in_flight,
- (const unsigned char **)&vec[nvecs].base,
+ CURL_UNCONST(&vec[nvecs].base),
&vec[nvecs].len)) {
stream->sendbuf_len_in_flight += vec[nvecs].len;
nwritten += vec[nvecs].len;
@@ -1521,7 +1601,7 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
{
struct cf_ngtcp2_ctx *ctx = cf->ctx;
struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
- ssize_t sent = 0;
+ ssize_t sent = -1;
struct cf_call_data save;
struct pkt_io_ctx pktx;
CURLcode result;
@@ -1533,18 +1613,20 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
pktx_init(&pktx, cf, data);
*err = CURLE_OK;
- (void)eos; /* TODO: use for stream EOF and block handling */
+ /* handshake verification failed in callback, do not send anything */
+ if(ctx->tls_vrfy_result)
+ return ctx->tls_vrfy_result;
+
+ (void)eos; /* use for stream EOF and block handling */
result = cf_progress_ingress(cf, data, &pktx);
if(result) {
*err = result;
- sent = -1;
}
if(!stream || stream->id < 0) {
if(ctx->shutdown_started) {
CURL_TRC_CF(data, cf, "cannot open stream on closed connection");
*err = CURLE_SEND_ERROR;
- sent = -1;
goto out;
}
sent = h3_stream_open(cf, data, buf, len, err);
@@ -1558,7 +1640,6 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] xfer write failed", stream->id);
cf_ngtcp2_stream_close(cf, data, stream);
*err = stream->xfer_result;
- sent = -1;
goto out;
}
else if(stream->closed) {
@@ -1583,7 +1664,6 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
else if(ctx->shutdown_started) {
CURL_TRC_CF(data, cf, "cannot send on closed connection");
*err = CURLE_SEND_ERROR;
- sent = -1;
goto out;
}
else {
@@ -1598,6 +1678,9 @@ static ssize_t cf_ngtcp2_send(struct Curl_cfilter *cf, struct Curl_easy *data,
(void)nghttp3_conn_resume_stream(ctx->h3conn, stream->id);
}
+ if(sent > 0 && !ctx->tls_handshake_complete && ctx->use_earlydata)
+ ctx->earlydata_skip += sent;
+
result = cf_progress_egress(cf, data, &pktx);
if(result) {
*err = result;
@@ -1616,17 +1699,6 @@ out:
return sent;
}
-static CURLcode qng_verify_peer(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- struct cf_ngtcp2_ctx *ctx = cf->ctx;
-
- cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
- cf->conn->httpversion = 30;
-
- return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer);
-}
-
static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen,
struct sockaddr_storage *remote_addr,
socklen_t remote_addrlen, int ecn,
@@ -1696,7 +1768,7 @@ static ssize_t read_pkt_to_send(void *userp,
uint32_t flags;
int64_t stream_id;
int fin;
- ssize_t nwritten, n;
+ ssize_t nwritten = 0, n;
veccnt = 0;
stream_id = -1;
fin = 0;
@@ -1708,13 +1780,12 @@ static ssize_t read_pkt_to_send(void *userp,
* When ngtcp2 is happy (because it has no other frame that would fit
* or it has nothing more to send), it returns the total length
* of the assembled packet. This may be 0 if there was nothing to send. */
- nwritten = 0;
*err = CURLE_OK;
for(;;) {
if(ctx->h3conn && ngtcp2_conn_get_max_data_left(ctx->qconn)) {
veccnt = nghttp3_conn_writev_stream(ctx->h3conn, &stream_id, &fin, vec,
- sizeof(vec) / sizeof(vec[0]));
+ CURL_ARRAYSIZE(vec));
if(veccnt < 0) {
failf(x->data, "nghttp3_conn_writev_stream returned error: %s",
nghttp3_strerror((int)veccnt));
@@ -1920,8 +1991,8 @@ static CURLcode h3_data_pause(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool pause)
{
- /* TODO: there seems right now no API in ngtcp2 to shrink/enlarge
- * the streams windows. As we do in HTTP/2. */
+ /* There seems to exist no API in ngtcp2 to shrink/enlarge the streams
+ * windows. As we do in HTTP/2. */
if(!pause) {
h3_drain_stream(cf, data);
Curl_expire(data, 0, EXPIRE_RUN_NOW);
@@ -1946,9 +2017,6 @@ static CURLcode cf_ngtcp2_data_event(struct Curl_cfilter *cf,
case CF_CTRL_DATA_PAUSE:
result = h3_data_pause(cf, data, (arg1 != 0));
break;
- case CF_CTRL_DATA_DETACH:
- h3_data_done(cf, data);
- break;
case CF_CTRL_DATA_DONE:
h3_data_done(cf, data);
break;
@@ -1991,10 +2059,20 @@ static void cf_ngtcp2_ctx_close(struct cf_ngtcp2_ctx *ctx)
ctx->qlogfd = -1;
Curl_vquic_tls_cleanup(&ctx->tls);
vquic_ctx_free(&ctx->q);
- if(ctx->h3conn)
+ if(ctx->h3conn) {
nghttp3_conn_del(ctx->h3conn);
- if(ctx->qconn)
+ ctx->h3conn = NULL;
+ }
+ if(ctx->qconn) {
ngtcp2_conn_del(ctx->qconn);
+ ctx->qconn = NULL;
+ }
+#ifdef OPENSSL_QUIC_API2
+ if(ctx->ossl_ctx) {
+ ngtcp2_crypto_ossl_ctx_del(ctx->ossl_ctx);
+ ctx->ossl_ctx = NULL;
+ }
+#endif
ctx->call_data = save;
}
@@ -2034,6 +2112,7 @@ static CURLcode cf_ngtcp2_shutdown(struct Curl_cfilter *cf,
}
}
+ DEBUGASSERT(Curl_bufq_is_empty(&ctx->q.sendbuf));
ctx->shutdown_started = TRUE;
nwritten = ngtcp2_conn_write_connection_close(
ctx->qconn, NULL, /* path */
@@ -2043,14 +2122,21 @@ static CURLcode cf_ngtcp2_shutdown(struct Curl_cfilter *cf,
CURL_TRC_CF(data, cf, "start shutdown(err_type=%d, err_code=%"
FMT_PRIu64 ") -> %d", ctx->last_error.type,
(curl_uint64_t)ctx->last_error.error_code, (int)nwritten);
+ /* there are cases listed in ngtcp2 documentation where this call
+ * may fail. Since we are doing a connection shutdown as graceful
+ * as we can, such an error is ignored here. */
if(nwritten > 0) {
- Curl_bufq_write(&ctx->q.sendbuf, (const unsigned char *)buffer,
- (size_t)nwritten, &result);
+ /* Ignore amount written. sendbuf was empty and has always room for
+ * NGTCP2_MAX_UDP_PAYLOAD_SIZE. It can only completely fail, in which
+ * case `result` is set non zero. */
+ (void)Curl_bufq_write(&ctx->q.sendbuf, (const unsigned char *)buffer,
+ (size_t)nwritten, &result);
if(result) {
CURL_TRC_CF(data, cf, "error %d adding shutdown packets to sendbuf, "
"aborting shutdown", result);
goto out;
}
+
ctx->q.no_gso = TRUE;
ctx->q.gsolen = (size_t)nwritten;
ctx->q.split_len = 0;
@@ -2110,6 +2196,7 @@ static void cf_ngtcp2_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
{
CURL_TRC_CF(data, cf, "destroy");
if(cf->ctx) {
+ cf_ngtcp2_close(cf, data);
cf_ngtcp2_ctx_free(cf->ctx);
cf->ctx = NULL;
}
@@ -2131,7 +2218,24 @@ static int quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
ctx = cf ? cf->ctx : NULL;
data = cf ? CF_DATA_CURRENT(cf) : NULL;
if(cf && data && ctx) {
- Curl_ossl_add_session(cf, data, &ctx->peer, ssl_sessionid);
+ unsigned char *quic_tp = NULL;
+ size_t quic_tp_len = 0;
+#ifdef HAVE_OPENSSL_EARLYDATA
+ ngtcp2_ssize tplen;
+ uint8_t tpbuf[256];
+
+ tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf,
+ sizeof(tpbuf));
+ if(tplen < 0)
+ CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s",
+ ngtcp2_strerror((int)tplen));
+ else {
+ quic_tp = (unsigned char *)tpbuf;
+ quic_tp_len = (size_t)tplen;
+ }
+#endif
+ Curl_ossl_add_session(cf, data, ctx->peer.scache_key, ssl_sessionid,
+ SSL_version(ssl), "h3", quic_tp, quic_tp_len);
return 1;
}
return 0;
@@ -2139,6 +2243,24 @@ static int quic_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
#endif /* USE_OPENSSL */
#ifdef USE_GNUTLS
+
+static const char *gtls_hs_msg_name(int mtype)
+{
+ switch(mtype) {
+ case 1: return "ClientHello";
+ case 2: return "ServerHello";
+ case 4: return "SessionTicket";
+ case 8: return "EncryptedExtensions";
+ case 11: return "Certificate";
+ case 13: return "CertificateRequest";
+ case 15: return "CertificateVerify";
+ case 20: return "Finished";
+ case 24: return "KeyUpdate";
+ case 254: return "MessageHash";
+ }
+ return "Unknown";
+}
+
static int quic_gtls_handshake_cb(gnutls_session_t session, unsigned int htype,
unsigned when, unsigned int incoming,
const gnutls_datum_t *msg)
@@ -2152,13 +2274,28 @@ static int quic_gtls_handshake_cb(gnutls_session_t session, unsigned int htype,
if(when && cf && ctx) { /* after message has been processed */
struct Curl_easy *data = CF_DATA_CURRENT(cf);
DEBUGASSERT(data);
- if(data) {
- CURL_TRC_CF(data, cf, "handshake: %s message type %d",
- incoming ? "incoming" : "outgoing", htype);
- }
+ if(!data)
+ return 0;
+ CURL_TRC_CF(data, cf, "SSL message: %s %s [%d]",
+ incoming ? "<-" : "->", gtls_hs_msg_name(htype), htype);
switch(htype) {
case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: {
- (void)Curl_gtls_update_session_id(cf, data, session, &ctx->peer, "h3");
+ ngtcp2_ssize tplen;
+ uint8_t tpbuf[256];
+ unsigned char *quic_tp = NULL;
+ size_t quic_tp_len = 0;
+
+ tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf,
+ sizeof(tpbuf));
+ if(tplen < 0)
+ CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s",
+ ngtcp2_strerror((int)tplen));
+ else {
+ quic_tp = (unsigned char *)tpbuf;
+ quic_tp_len = (size_t)tplen;
+ }
+ (void)Curl_gtls_cache_session(cf, data, ctx->peer.scache_key,
+ session, 0, "h3", quic_tp, quic_tp_len);
break;
}
default:
@@ -2181,16 +2318,32 @@ static int wssl_quic_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session)
struct Curl_easy *data = CF_DATA_CURRENT(cf);
DEBUGASSERT(data);
if(data && ctx) {
- (void)wssl_cache_session(cf, data, &ctx->peer, session);
+ ngtcp2_ssize tplen;
+ uint8_t tpbuf[256];
+ unsigned char *quic_tp = NULL;
+ size_t quic_tp_len = 0;
+
+ tplen = ngtcp2_conn_encode_0rtt_transport_params(ctx->qconn, tpbuf,
+ sizeof(tpbuf));
+ if(tplen < 0)
+ CURL_TRC_CF(data, cf, "error encoding 0RTT transport data: %s",
+ ngtcp2_strerror((int)tplen));
+ else {
+ quic_tp = (unsigned char *)tpbuf;
+ quic_tp_len = (size_t)tplen;
+ }
+ (void)Curl_wssl_cache_session(cf, data, ctx->peer.scache_key,
+ session, wolfSSL_version(ssl),
+ "h3", quic_tp, quic_tp_len);
}
}
return 0;
}
#endif /* USE_WOLFSSL */
-static CURLcode tls_ctx_setup(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- void *user_data)
+static CURLcode cf_ngtcp2_tls_ctx_setup(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ void *user_data)
{
struct curl_tls_ctx *ctx = user_data;
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
@@ -2202,6 +2355,8 @@ static CURLcode tls_ctx_setup(struct Curl_cfilter *cf,
failf(data, "ngtcp2_crypto_boringssl_configure_client_context failed");
return CURLE_FAILED_INIT;
}
+#elif defined(OPENSSL_QUIC_API2)
+ /* nothing to do */
#else
if(ngtcp2_crypto_quictls_configure_client_context(ctx->ossl.ssl_ctx) != 0) {
failf(data, "ngtcp2_crypto_quictls_configure_client_context failed");
@@ -2231,18 +2386,80 @@ static CURLcode tls_ctx_setup(struct Curl_cfilter *cf,
}
#elif defined(USE_WOLFSSL)
- if(ngtcp2_crypto_wolfssl_configure_client_context(ctx->wssl.ctx) != 0) {
+ if(ngtcp2_crypto_wolfssl_configure_client_context(ctx->wssl.ssl_ctx) != 0) {
failf(data, "ngtcp2_crypto_wolfssl_configure_client_context failed");
return CURLE_FAILED_INIT;
}
if(ssl_config->primary.cache_session) {
/* Register to get notified when a new session is received */
- wolfSSL_CTX_sess_set_new_cb(ctx->wssl.ctx, wssl_quic_new_session_cb);
+ wolfSSL_CTX_sess_set_new_cb(ctx->wssl.ssl_ctx, wssl_quic_new_session_cb);
}
#endif
return CURLE_OK;
}
+static CURLcode cf_ngtcp2_on_session_reuse(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct alpn_spec *alpns,
+ struct Curl_ssl_session *scs,
+ bool *do_early_data)
+{
+ struct cf_ngtcp2_ctx *ctx = cf->ctx;
+ CURLcode result = CURLE_OK;
+
+ *do_early_data = FALSE;
+#if defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA)
+ ctx->earlydata_max = scs->earlydata_max;
+#endif
+#ifdef USE_GNUTLS
+ ctx->earlydata_max =
+ gnutls_record_get_max_early_data_size(ctx->tls.gtls.session);
+#endif
+#ifdef USE_WOLFSSL
+#ifdef WOLFSSL_EARLY_DATA
+ ctx->earlydata_max = scs->earlydata_max;
+#else
+ ctx->earlydata_max = 0;
+#endif /* WOLFSSL_EARLY_DATA */
+#endif
+#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \
+ (defined(USE_OPENSSL) && defined(HAVE_OPENSSL_EARLYDATA))
+ if((!ctx->earlydata_max)) {
+ CURL_TRC_CF(data, cf, "SSL session does not allow earlydata");
+ }
+ else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) {
+ CURL_TRC_CF(data, cf, "SSL session from different ALPN, no early data");
+ }
+ else if(!scs->quic_tp || !scs->quic_tp_len) {
+ CURL_TRC_CF(data, cf, "no 0RTT transport parameters, no early data, ");
+ }
+ else {
+ int rv;
+ rv = ngtcp2_conn_decode_and_set_0rtt_transport_params(
+ ctx->qconn, (const uint8_t *)scs->quic_tp, scs->quic_tp_len);
+ if(rv)
+ CURL_TRC_CF(data, cf, "no early data, failed to set 0RTT transport "
+ "parameters: %s", ngtcp2_strerror(rv));
+ else {
+ infof(data, "SSL session allows %zu bytes of early data, "
+ "reusing ALPN '%s'", ctx->earlydata_max, scs->alpn);
+ result = init_ngh3_conn(cf, data);
+ if(!result) {
+ ctx->use_earlydata = TRUE;
+ cf->connected = TRUE;
+ *do_early_data = TRUE;
+ }
+ }
+ }
+#else /* not supported in the TLS backend */
+ (void)data;
+ (void)ctx;
+ (void)scs;
+ (void)alpns;
+#endif
+ return result;
+}
+
/*
* Might be called twice for happy eyeballs.
*/
@@ -2256,23 +2473,11 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
CURLcode result;
const struct Curl_sockaddr_ex *sockaddr = NULL;
int qfd;
+static const struct alpn_spec ALPN_SPEC_H3 = {
+ { "h3", "h3-29" }, 2
+};
DEBUGASSERT(ctx->initialized);
- result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC);
- if(result)
- return result;
-
-#define H3_ALPN "\x2h3\x5h3-29"
- result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
- H3_ALPN, sizeof(H3_ALPN) - 1,
- tls_ctx_setup, &ctx->tls, &ctx->conn_ref);
- if(result)
- return result;
-
-#ifdef USE_OPENSSL
- SSL_set_quic_use_legacy_codepoint(ctx->tls.ossl.ssl, 0);
-#endif
-
ctx->dcid.datalen = NGTCP2_MAX_CIDLEN;
result = Curl_rand(data, ctx->dcid.data, NGTCP2_MAX_CIDLEN);
if(result)
@@ -2314,27 +2519,45 @@ static CURLcode cf_connect_start(struct Curl_cfilter *cf,
if(rc)
return CURLE_QUIC_CONNECT_ERROR;
-#ifdef USE_OPENSSL
+ ctx->conn_ref.get_conn = get_conn;
+ ctx->conn_ref.user_data = cf;
+
+ result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer, &ALPN_SPEC_H3,
+ cf_ngtcp2_tls_ctx_setup, &ctx->tls,
+ &ctx->conn_ref,
+ cf_ngtcp2_on_session_reuse);
+ if(result)
+ return result;
+
+#if defined(USE_OPENSSL) && defined(OPENSSL_QUIC_API2)
+ if(ngtcp2_crypto_ossl_ctx_new(&ctx->ossl_ctx, ctx->tls.ossl.ssl) != 0) {
+ failf(data, "ngtcp2_crypto_ossl_ctx_new failed");
+ return CURLE_FAILED_INIT;
+ }
+ ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->ossl_ctx);
+ if(ngtcp2_crypto_ossl_configure_client_session(ctx->tls.ossl.ssl) != 0) {
+ failf(data, "ngtcp2_crypto_ossl_configure_client_session failed");
+ return CURLE_FAILED_INIT;
+ }
+#elif defined(USE_OPENSSL)
+ SSL_set_quic_use_legacy_codepoint(ctx->tls.ossl.ssl, 0);
ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.ossl.ssl);
#elif defined(USE_GNUTLS)
ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.gtls.session);
#elif defined(USE_WOLFSSL)
- ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.wssl.handle);
+ ngtcp2_conn_set_tls_native_handle(ctx->qconn, ctx->tls.wssl.ssl);
#else
#error "ngtcp2 TLS backend not defined"
#endif
ngtcp2_ccerr_default(&ctx->last_error);
- ctx->conn_ref.get_conn = get_conn;
- ctx->conn_ref.user_data = cf;
-
return CURLE_OK;
}
static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool blocking, bool *done)
+ bool *done)
{
struct cf_ngtcp2_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
@@ -2349,13 +2572,13 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf,
/* Connect the UDP filter first */
if(!cf->next->connected) {
- result = Curl_conn_cf_connect(cf->next, data, blocking, done);
+ result = Curl_conn_cf_connect(cf->next, data, done);
if(result || !*done)
return result;
}
*done = FALSE;
- now = Curl_now();
+ now = curlx_now();
pktx_init(&pktx, cf, data);
CF_DATA_SAVE(save, cf, data);
@@ -2365,6 +2588,11 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf,
result = cf_connect_start(cf, data, &pktx);
if(result)
goto out;
+ if(cf->connected) {
+ cf->conn->alpn = CURL_HTTP_VERSION_3;
+ *done = TRUE;
+ goto out;
+ }
result = cf_progress_egress(cf, data, &pktx);
/* we do not expect to be able to recv anything yet */
goto out;
@@ -2379,10 +2607,7 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf,
goto out;
if(ngtcp2_conn_get_handshake_completed(ctx->qconn)) {
- ctx->handshake_at = now;
- CURL_TRC_CF(data, cf, "handshake complete after %dms",
- (int)Curl_timediff(now, ctx->started_at));
- result = qng_verify_peer(cf, data);
+ result = ctx->tls_vrfy_result;
if(!result) {
CURL_TRC_CF(data, cf, "peer verified");
cf->connected = TRUE;
@@ -2440,7 +2665,7 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf,
}
else if(ctx->max_bidi_streams) {
uint64_t avail_bidi_streams = 0;
- uint64_t max_streams = CONN_INUSE(cf->conn);
+ uint64_t max_streams = CONN_ATTACHED(cf->conn);
if(ctx->max_bidi_streams > ctx->used_bidi_streams)
avail_bidi_streams = ctx->max_bidi_streams - ctx->used_bidi_streams;
max_streams += avail_bidi_streams;
@@ -2449,14 +2674,14 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf,
else /* transport params not arrived yet? take our default. */
*pres1 = (int)Curl_multi_max_concurrent_streams(data->multi);
CURL_TRC_CF(data, cf, "query conn[%" FMT_OFF_T "]: "
- "MAX_CONCURRENT -> %d (%zu in use)",
- cf->conn->connection_id, *pres1, CONN_INUSE(cf->conn));
+ "MAX_CONCURRENT -> %d (%u in use)",
+ cf->conn->connection_id, *pres1, CONN_ATTACHED(cf->conn));
CF_DATA_RESTORE(cf, save);
return CURLE_OK;
}
case CF_QUERY_CONNECT_REPLY_MS:
if(ctx->q.got_first_byte) {
- timediff_t ms = Curl_timediff(ctx->q.first_byte_at, ctx->started_at);
+ timediff_t ms = curlx_timediff(ctx->q.first_byte_at, ctx->started_at);
*pres1 = (ms < INT_MAX) ? (int)ms : INT_MAX;
}
else
@@ -2474,6 +2699,9 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf,
*when = ctx->handshake_at;
return CURLE_OK;
}
+ case CF_QUERY_HTTP_VERSION:
+ *pres1 = 30;
+ return CURLE_OK;
default:
break;
}
@@ -2496,21 +2724,12 @@ static bool cf_ngtcp2_conn_is_alive(struct Curl_cfilter *cf,
if(!ctx->qconn || ctx->shutdown_started)
goto out;
- /* Both sides of the QUIC connection announce they max idle times in
- * the transport parameters. Look at the minimum of both and if
- * we exceed this, regard the connection as dead. The other side
- * may have completely purged it and will no longer respond
- * to any packets from us. */
+ /* We do not announce a max idle timeout, but when the peer does
+ * it will close the connection when it expires. */
rp = ngtcp2_conn_get_remote_transport_params(ctx->qconn);
- if(rp) {
- timediff_t idletime;
- uint64_t idle_ms = ctx->max_idle_ms;
-
- if(rp->max_idle_timeout &&
- (rp->max_idle_timeout / NGTCP2_MILLISECONDS) < idle_ms)
- idle_ms = (rp->max_idle_timeout / NGTCP2_MILLISECONDS);
- idletime = Curl_timediff(Curl_now(), ctx->q.last_io);
- if(idletime > 0 && (uint64_t)idletime > idle_ms)
+ if(rp && rp->max_idle_timeout) {
+ timediff_t idletime = curlx_timediff(curlx_now(), ctx->q.last_io);
+ if(idletime > 0 && (uint64_t)idletime > rp->max_idle_timeout)
goto out;
}
@@ -2536,7 +2755,7 @@ out:
struct Curl_cftype Curl_cft_http3 = {
"HTTP/3",
- CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
+ CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX | CF_TYPE_HTTP,
0,
cf_ngtcp2_destroy,
cf_ngtcp2_connect,
diff --git a/libs/libcurl/src/vquic/curl_ngtcp2.h b/libs/libcurl/src/vquic/curl_ngtcp2.h
index 7b8a158b76..313d853644 100644
--- a/libs/libcurl/src/vquic/curl_ngtcp2.h
+++ b/libs/libcurl/src/vquic/curl_ngtcp2.h
@@ -24,7 +24,7 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
@@ -33,6 +33,9 @@
#endif
#include <ngtcp2/ngtcp2_crypto.h>
+#ifdef OPENSSL_QUIC_API2
+#include <ngtcp2/ngtcp2_crypto_ossl.h>
+#endif
#include <nghttp3/nghttp3.h>
#ifdef USE_OPENSSL
#include <openssl/ssl.h>
@@ -44,7 +47,7 @@
struct Curl_cfilter;
-#include "urldata.h"
+#include "../urldata.h"
void Curl_ngtcp2_ver(char *p, size_t len);
diff --git a/libs/libcurl/src/vquic/curl_osslq.c b/libs/libcurl/src/vquic/curl_osslq.c
index c3d695d415..3a7f9b185e 100644
--- a/libs/libcurl/src/vquic/curl_osslq.c
+++ b/libs/libcurl/src/vquic/curl_osslq.c
@@ -22,7 +22,7 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)
@@ -31,36 +31,36 @@
#include <openssl/err.h>
#include <nghttp3/nghttp3.h>
-#include "urldata.h"
-#include "hash.h"
-#include "sendf.h"
-#include "strdup.h"
-#include "rand.h"
-#include "multiif.h"
-#include "strcase.h"
-#include "cfilters.h"
-#include "cf-socket.h"
-#include "connect.h"
-#include "progress.h"
-#include "strerror.h"
-#include "dynbuf.h"
-#include "http1.h"
-#include "select.h"
-#include "inet_pton.h"
+#include "../urldata.h"
+#include "../hash.h"
+#include "../sendf.h"
+#include "../strdup.h"
+#include "../rand.h"
+#include "../multiif.h"
+#include "../strcase.h"
+#include "../cfilters.h"
+#include "../cf-socket.h"
+#include "../connect.h"
+#include "../progress.h"
+#include "../strerror.h"
+#include "../curlx/dynbuf.h"
+#include "../http1.h"
+#include "../select.h"
+#include "../curlx/inet_pton.h"
+#include "../uint-hash.h"
#include "vquic.h"
#include "vquic_int.h"
#include "vquic-tls.h"
-#include "vtls/keylog.h"
-#include "vtls/vtls.h"
-#include "vtls/openssl.h"
+#include "../vtls/keylog.h"
+#include "../vtls/vtls.h"
+#include "../vtls/openssl.h"
#include "curl_osslq.h"
-
-#include "warnless.h"
+#include "../curlx/warnless.h"
/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
/* A stream window is the maximum amount we need to buffer for
* each active transfer. We use HTTP/3 flow control and only ACK
@@ -82,10 +82,6 @@
#define H3_STREAM_SEND_CHUNKS \
(H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE)
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
-#endif
-
#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
typedef uint32_t sslerr_t;
#else
@@ -175,7 +171,7 @@ static CURLcode make_bio_addr(BIO_ADDR **pbio_addr,
switch(addr->family) {
case AF_INET: {
struct sockaddr_in * const sin =
- (struct sockaddr_in * const)(void *)&addr->curl_sa_addr;
+ (struct sockaddr_in * const)CURL_UNCONST(&addr->curl_sa_addr);
if(!BIO_ADDR_rawmake(ba, AF_INET, &sin->sin_addr,
sizeof(sin->sin_addr), sin->sin_port)) {
goto out;
@@ -186,7 +182,7 @@ static CURLcode make_bio_addr(BIO_ADDR **pbio_addr,
#ifdef USE_IPV6
case AF_INET6: {
struct sockaddr_in6 * const sin =
- (struct sockaddr_in6 * const)(void *)&addr->curl_sa_addr;
+ (struct sockaddr_in6 * const)CURL_UNCONST(&addr->curl_sa_addr);
if(!BIO_ADDR_rawmake(ba, AF_INET6, &sin->sin6_addr,
sizeof(sin->sin6_addr), sin->sin6_port)) {
}
@@ -289,9 +285,12 @@ struct cf_osslq_ctx {
struct curltime handshake_at; /* time connect handshake finished */
struct curltime first_byte_at; /* when first byte was recvd */
struct bufc_pool stream_bufcp; /* chunk pool for streams */
- struct Curl_hash streams; /* hash `data->mid` to `h3_stream_ctx` */
+ struct uint_hash streams; /* hash `data->mid` to `h3_stream_ctx` */
size_t max_stream_window; /* max flow window for one stream */
uint64_t max_idle_ms; /* max idle time for QUIC connection */
+ SSL_POLL_ITEM *poll_items; /* Array for polling on writable state */
+ struct Curl_easy **curl_items; /* Array of easy objs */
+ size_t items_max; /* max elements in poll/curl_items */
BIT(initialized);
BIT(got_first_byte); /* if first byte was received */
BIT(x509_store_setup); /* if x509 store has been set up */
@@ -300,14 +299,17 @@ struct cf_osslq_ctx {
BIT(need_send); /* QUIC connection needs to send */
};
-static void h3_stream_hash_free(void *stream);
+static void h3_stream_hash_free(unsigned int id, void *stream);
static void cf_osslq_ctx_init(struct cf_osslq_ctx *ctx)
{
DEBUGASSERT(!ctx->initialized);
Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
H3_STREAM_POOL_SPARES);
- Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free);
+ Curl_uint_hash_init(&ctx->streams, 63, h3_stream_hash_free);
+ ctx->poll_items = NULL;
+ ctx->curl_items = NULL;
+ ctx->items_max = 0;
ctx->initialized = TRUE;
}
@@ -315,9 +317,10 @@ static void cf_osslq_ctx_free(struct cf_osslq_ctx *ctx)
{
if(ctx && ctx->initialized) {
Curl_bufcp_free(&ctx->stream_bufcp);
- Curl_hash_clean(&ctx->streams);
- Curl_hash_destroy(&ctx->streams);
+ Curl_uint_hash_destroy(&ctx->streams);
Curl_ssl_peer_cleanup(&ctx->peer);
+ free(ctx->poll_items);
+ free(ctx->curl_items);
}
free(ctx);
}
@@ -450,7 +453,7 @@ static CURLcode cf_osslq_h3conn_add_stream(struct cf_osslq_h3conn *h3,
struct cf_osslq_ctx *ctx = cf->ctx;
curl_int64_t stream_id = (curl_int64_t)SSL_get_stream_id(stream_ssl);
- if(h3->remote_ctrl_n >= ARRAYSIZE(h3->remote_ctrl)) {
+ if(h3->remote_ctrl_n >= CURL_ARRAYSIZE(h3->remote_ctrl)) {
/* rejected, we are full */
CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] rejecting remote stream",
stream_id);
@@ -562,7 +565,6 @@ static CURLcode cf_osslq_verify_peer(struct Curl_cfilter *cf,
struct cf_osslq_ctx *ctx = cf->ctx;
cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
- cf->conn->httpversion = 30;
return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer);
}
@@ -581,16 +583,13 @@ struct h3_stream_ctx {
curl_off_t upload_left; /* number of request bytes left to upload */
curl_off_t download_recvd; /* number of response DATA bytes received */
int status_code; /* HTTP status code */
- bool resp_hds_complete; /* we have a complete, final response */
- bool closed; /* TRUE on stream close */
- bool reset; /* TRUE on stream reset */
- bool send_closed; /* stream is local closed */
+ BIT(resp_hds_complete); /* we have a complete, final response */
+ BIT(closed); /* TRUE on stream close */
+ BIT(reset); /* TRUE on stream reset */
+ BIT(send_closed); /* stream is local closed */
BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */
};
-#define H3_STREAM_CTX(ctx,data) ((struct h3_stream_ctx *)(\
- data? Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL))
-
static void h3_stream_ctx_free(struct h3_stream_ctx *stream)
{
cf_osslq_stream_cleanup(&stream->s);
@@ -600,8 +599,9 @@ static void h3_stream_ctx_free(struct h3_stream_ctx *stream)
free(stream);
}
-static void h3_stream_hash_free(void *stream)
+static void h3_stream_hash_free(unsigned int id, void *stream)
{
+ (void)id;
DEBUGASSERT(stream);
h3_stream_ctx_free((struct h3_stream_ctx *)stream);
}
@@ -634,7 +634,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf,
stream->recv_buf_nonflow = 0;
Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
- if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) {
+ if(!Curl_uint_hash_set(&ctx->streams, data->mid, stream)) {
h3_stream_ctx_free(stream);
return CURLE_OUT_OF_MEMORY;
}
@@ -651,7 +651,7 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
if(stream) {
CURL_TRC_CF(data, cf, "[%"FMT_PRId64"] easy handle is done",
stream->s.id);
- if(ctx->h3.conn && !stream->closed) {
+ if(ctx->h3.conn && (stream->s.id >= 0) && !stream->closed) {
nghttp3_conn_shutdown_stream_read(ctx->h3.conn, stream->s.id);
nghttp3_conn_close_stream(ctx->h3.conn, stream->s.id,
NGHTTP3_H3_REQUEST_CANCELLED);
@@ -659,8 +659,26 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
stream->closed = TRUE;
}
- Curl_hash_offt_remove(&ctx->streams, data->mid);
+ Curl_uint_hash_remove(&ctx->streams, data->mid);
+ }
+}
+
+struct cf_ossq_find_ctx {
+ curl_int64_t stream_id;
+ struct h3_stream_ctx *stream;
+};
+
+static bool cf_osslq_find_stream(unsigned int mid, void *val, void *user_data)
+{
+ struct h3_stream_ctx *stream = val;
+ struct cf_ossq_find_ctx *fctx = user_data;
+
+ (void)mid;
+ if(stream && stream->s.id == fctx->stream_id) {
+ fctx->stream = stream;
+ return FALSE; /* stop iterating */
}
+ return TRUE;
}
static struct cf_osslq_stream *cf_osslq_get_qstream(struct Curl_cfilter *cf,
@@ -683,17 +701,12 @@ static struct cf_osslq_stream *cf_osslq_get_qstream(struct Curl_cfilter *cf,
return &ctx->h3.s_qpack_dec;
}
else {
- struct Curl_llist_node *e;
- DEBUGASSERT(data->multi);
- for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
- struct Curl_easy *sdata = Curl_node_elem(e);
- if(sdata->conn != data->conn)
- continue;
- stream = H3_STREAM_CTX(ctx, sdata);
- if(stream && stream->s.id == stream_id) {
- return &stream->s;
- }
- }
+ struct cf_ossq_find_ctx fctx;
+ fctx.stream_id = stream_id;
+ fctx.stream = NULL;
+ Curl_uint_hash_visit(&ctx->streams, cf_osslq_find_stream, &fctx);
+ if(fctx.stream)
+ return &fctx.stream->s;
}
return NULL;
}
@@ -816,7 +829,7 @@ static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream3_id,
return NGHTTP3_ERR_CALLBACK_FAILURE;
}
stream->download_recvd += (curl_off_t)buflen;
- CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] DATA len=%zu, total=%zd",
+ CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] DATA len=%zu, total=%" FMT_OFF_T,
stream->s.id, buflen, stream->download_recvd);
h3_drain_stream(cf, data);
return 0;
@@ -1009,7 +1022,7 @@ cb_h3_read_req_body(nghttp3_conn *conn, int64_t stream_id,
while(nvecs < veccnt &&
Curl_bufq_peek_at(&stream->sendbuf,
stream->sendbuf_len_in_flight,
- (const unsigned char **)&vec[nvecs].base,
+ CURL_UNCONST(&vec[nvecs].base),
&vec[nvecs].len)) {
stream->sendbuf_len_in_flight += vec[nvecs].len;
nwritten += vec[nvecs].len;
@@ -1160,16 +1173,15 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
const struct Curl_sockaddr_ex *peer_addr = NULL;
BIO *bio = NULL;
BIO_ADDR *baddr = NULL;
+static const struct alpn_spec ALPN_SPEC_H3 = {
+ { "h3" }, 1
+};
DEBUGASSERT(ctx->initialized);
- result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC);
- if(result)
- goto out;
#define H3_ALPN "\x2h3"
result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
- H3_ALPN, sizeof(H3_ALPN) - 1,
- NULL, NULL, NULL);
+ &ALPN_SPEC_H3, NULL, NULL, NULL, NULL);
if(result)
goto out;
@@ -1224,17 +1236,6 @@ static CURLcode cf_osslq_ctx_start(struct Curl_cfilter *cf,
goto out;
}
-#ifdef SSL_VALUE_QUIC_IDLE_TIMEOUT
- /* Added in OpenSSL v3.3.x */
- if(!SSL_set_feature_request_uint(ctx->tls.ossl.ssl,
- SSL_VALUE_QUIC_IDLE_TIMEOUT,
- CURL_QUIC_MAX_IDLE_MS)) {
- CURL_TRC_CF(data, cf, "error setting idle timeout, ");
- result = CURLE_FAILED_INIT;
- goto out;
- }
-#endif
-
SSL_set_bio(ctx->tls.ossl.ssl, bio, bio);
bio = NULL;
SSL_set_connect_state(ctx->tls.ossl.ssl);
@@ -1399,6 +1400,29 @@ out:
return result;
}
+struct cf_ossq_recv_ctx {
+ struct Curl_cfilter *cf;
+ struct Curl_multi *multi;
+ CURLcode result;
+};
+
+static bool cf_osslq_iter_recv(unsigned int mid, void *val, void *user_data)
+{
+ struct h3_stream_ctx *stream = val;
+ struct cf_ossq_recv_ctx *rctx = user_data;
+
+ (void)mid;
+ if(stream && !stream->closed && !Curl_bufq_is_full(&stream->recvbuf)) {
+ struct Curl_easy *sdata = Curl_multi_get_easy(rctx->multi, mid);
+ if(sdata) {
+ rctx->result = cf_osslq_stream_recv(&stream->s, rctx->cf, sdata);
+ if(rctx->result)
+ return FALSE; /* abort iteration */
+ }
+ }
+ return TRUE;
+}
+
static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
@@ -1435,22 +1459,14 @@ static CURLcode cf_progress_ingress(struct Curl_cfilter *cf,
}
if(ctx->h3.conn) {
- struct Curl_llist_node *e;
- struct h3_stream_ctx *stream;
- /* PULL all open streams */
+ struct cf_ossq_recv_ctx rctx;
+
DEBUGASSERT(data->multi);
- for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
- struct Curl_easy *sdata = Curl_node_elem(e);
- if(sdata->conn == data->conn && CURL_WANT_RECV(sdata)) {
- stream = H3_STREAM_CTX(ctx, sdata);
- if(stream && !stream->closed &&
- !Curl_bufq_is_full(&stream->recvbuf)) {
- result = cf_osslq_stream_recv(&stream->s, cf, sdata);
- if(result)
- goto out;
- }
- }
- }
+ rctx.cf = cf;
+ rctx.multi = data->multi;
+ rctx.result = CURLE_OK;
+ Curl_uint_hash_visit(&ctx->streams, cf_osslq_iter_recv, &rctx);
+ result = rctx.result;
}
out:
@@ -1458,30 +1474,109 @@ out:
return result;
}
+struct cf_ossq_fill_ctx {
+ struct cf_osslq_ctx *ctx;
+ struct Curl_multi *multi;
+ size_t n;
+};
+
+static bool cf_osslq_collect_block_send(unsigned int mid, void *val,
+ void *user_data)
+{
+ struct h3_stream_ctx *stream = val;
+ struct cf_ossq_fill_ctx *fctx = user_data;
+ struct cf_osslq_ctx *ctx = fctx->ctx;
+
+ if(fctx->n >= ctx->items_max) /* should not happen, prevent mayhem */
+ return FALSE;
+
+ if(stream && stream->s.ssl && stream->s.send_blocked) {
+ struct Curl_easy *sdata = Curl_multi_get_easy(fctx->multi, mid);
+ if(sdata) {
+ ctx->poll_items[fctx->n].desc = SSL_as_poll_descriptor(stream->s.ssl);
+ ctx->poll_items[fctx->n].events = SSL_POLL_EVENT_W;
+ ctx->curl_items[fctx->n] = sdata;
+ fctx->n++;
+ }
+ }
+ return TRUE;
+}
+
/* Iterate over all streams and check if blocked can be unblocked */
static CURLcode cf_osslq_check_and_unblock(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_osslq_ctx *ctx = cf->ctx;
struct h3_stream_ctx *stream;
+ size_t poll_count;
+ size_t result_count = 0;
+ size_t idx_count = 0;
+ CURLcode res = CURLE_OK;
+ struct timeval timeout;
+ void *tmpptr;
if(ctx->h3.conn) {
- struct Curl_llist_node *e;
- for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
- struct Curl_easy *sdata = Curl_node_elem(e);
- if(sdata->conn == data->conn) {
- stream = H3_STREAM_CTX(ctx, sdata);
- if(stream && stream->s.ssl && stream->s.send_blocked &&
- !SSL_want_write(stream->s.ssl)) {
- nghttp3_conn_unblock_stream(ctx->h3.conn, stream->s.id);
- stream->s.send_blocked = FALSE;
- h3_drain_stream(cf, sdata);
- CURL_TRC_CF(sdata, cf, "unblocked");
+ struct cf_ossq_fill_ctx fill_ctx;
+
+ if(ctx->items_max < Curl_uint_hash_count(&ctx->streams)) {
+ size_t nmax = Curl_uint_hash_count(&ctx->streams);
+ ctx->items_max = 0;
+ tmpptr = realloc(ctx->poll_items, nmax * sizeof(SSL_POLL_ITEM));
+ if(!tmpptr) {
+ free(ctx->poll_items);
+ ctx->poll_items = NULL;
+ res = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ ctx->poll_items = tmpptr;
+
+ tmpptr = realloc(ctx->curl_items, nmax * sizeof(struct Curl_easy *));
+ if(!tmpptr) {
+ free(ctx->curl_items);
+ ctx->curl_items = NULL;
+ res = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ ctx->curl_items = tmpptr;
+ ctx->items_max = nmax;
+ }
+
+ fill_ctx.ctx = ctx;
+ fill_ctx.multi = data->multi;
+ fill_ctx.n = 0;
+ Curl_uint_hash_visit(&ctx->streams, cf_osslq_collect_block_send,
+ &fill_ctx);
+ poll_count = fill_ctx.n;
+ if(poll_count) {
+ CURL_TRC_CF(data, cf, "polling %zu blocked streams", poll_count);
+
+ memset(&timeout, 0, sizeof(struct timeval));
+ res = CURLE_UNRECOVERABLE_POLL;
+ if(!SSL_poll(ctx->poll_items, poll_count, sizeof(SSL_POLL_ITEM),
+ &timeout, 0, &result_count))
+ goto out;
+
+ res = CURLE_OK;
+
+ for(idx_count = 0; idx_count < poll_count && result_count > 0;
+ idx_count++) {
+ if(ctx->poll_items[idx_count].revents & SSL_POLL_EVENT_W) {
+ stream = H3_STREAM_CTX(ctx, ctx->curl_items[idx_count]);
+ DEBUGASSERT(stream); /* should still exist */
+ if(stream) {
+ nghttp3_conn_unblock_stream(ctx->h3.conn, stream->s.id);
+ stream->s.send_blocked = FALSE;
+ h3_drain_stream(cf, ctx->curl_items[idx_count]);
+ CURL_TRC_CF(ctx->curl_items[idx_count], cf, "unblocked");
+ }
+ result_count--;
}
}
}
}
- return CURLE_OK;
+
+out:
+ return res;
}
static CURLcode h3_send_streams(struct Curl_cfilter *cf,
@@ -1504,7 +1599,7 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf,
bool blocked = FALSE, eos_written = FALSE;
n = nghttp3_conn_writev_stream(ctx->h3.conn, &stream_id, &eos,
- vec, ARRAYSIZE(vec));
+ vec, CURL_ARRAYSIZE(vec));
if(n < 0) {
failf(data, "nghttp3_conn_writev_stream returned error: %s",
nghttp3_strerror((int)n));
@@ -1569,7 +1664,7 @@ static CURLcode h3_send_streams(struct Curl_cfilter *cf,
if(acked_len > 0 || (eos && !s->send_blocked)) {
/* Since QUIC buffers the data written internally, we can tell
* nghttp3 that it can move forward on it */
- ctx->q.last_io = Curl_now();
+ ctx->q.last_io = curlx_now();
rv = nghttp3_conn_add_write_offset(ctx->h3.conn, s->id, acked_len);
if(rv && rv != NGHTTP3_ERR_STREAM_NOT_FOUND) {
failf(data, "nghttp3_conn_add_write_offset returned error: %s\n",
@@ -1662,7 +1757,7 @@ out:
static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool blocking, bool *done)
+ bool *done)
{
struct cf_osslq_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
@@ -1677,13 +1772,13 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
/* Connect the UDP filter first */
if(!cf->next->connected) {
- result = Curl_conn_cf_connect(cf->next, data, blocking, done);
+ result = Curl_conn_cf_connect(cf->next, data, done);
if(result || !*done)
return result;
}
*done = FALSE;
- now = Curl_now();
+ now = curlx_now();
CF_DATA_SAVE(save, cf, data);
if(!ctx->tls.ossl.ssl) {
@@ -1697,7 +1792,7 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
int readable = SOCKET_READABLE(ctx->q.sockfd, 0);
if(readable > 0 && (readable & CURL_CSELECT_IN)) {
ctx->got_first_byte = TRUE;
- ctx->first_byte_at = Curl_now();
+ ctx->first_byte_at = curlx_now();
}
}
@@ -1717,7 +1812,7 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
ctx->handshake_at = now;
ctx->q.last_io = now;
CURL_TRC_CF(data, cf, "handshake complete after %dms",
- (int)Curl_timediff(now, ctx->started_at));
+ (int)curlx_timediff(now, ctx->started_at));
result = cf_osslq_verify_peer(cf, data);
if(!result) {
CURL_TRC_CF(data, cf, "peer verified");
@@ -1924,7 +2019,7 @@ static ssize_t cf_osslq_send(struct Curl_cfilter *cf, struct Curl_easy *data,
ssize_t nwritten;
CURLcode result;
- (void)eos; /* TODO: use to end stream */
+ (void)eos; /* use to end stream */
CF_DATA_SAVE(save, cf, data);
DEBUGASSERT(cf->connected);
DEBUGASSERT(ctx->tls.ossl.ssl);
@@ -2139,9 +2234,6 @@ static CURLcode cf_osslq_data_event(struct Curl_cfilter *cf,
case CF_CTRL_DATA_PAUSE:
result = h3_data_pause(cf, data, (arg1 != 0));
break;
- case CF_CTRL_DATA_DETACH:
- h3_data_done(cf, data);
- break;
case CF_CTRL_DATA_DONE:
h3_data_done(cf, data);
break;
@@ -2196,7 +2288,7 @@ static bool cf_osslq_conn_is_alive(struct Curl_cfilter *cf,
goto out;
}
CURL_TRC_CF(data, cf, "negotiated idle timeout: %zums", (size_t)idle_ms);
- idletime = Curl_timediff(Curl_now(), ctx->q.last_io);
+ idletime = curlx_timediff(curlx_now(), ctx->q.last_io);
if(idletime > 0 && (uint64_t)idletime > idle_ms)
goto out;
}
@@ -2273,17 +2365,17 @@ static CURLcode cf_osslq_query(struct Curl_cfilter *cf,
return CURLE_HTTP3;
}
/* we report avail + in_use */
- v += CONN_INUSE(cf->conn);
+ v += CONN_ATTACHED(cf->conn);
*pres1 = (v > INT_MAX) ? INT_MAX : (int)v;
#else
*pres1 = 100;
#endif
- CURL_TRC_CF(data, cf, "query max_conncurrent -> %d", *pres1);
+ CURL_TRC_CF(data, cf, "query max_concurrent -> %d", *pres1);
return CURLE_OK;
}
case CF_QUERY_CONNECT_REPLY_MS:
if(ctx->got_first_byte) {
- timediff_t ms = Curl_timediff(ctx->first_byte_at, ctx->started_at);
+ timediff_t ms = curlx_timediff(ctx->first_byte_at, ctx->started_at);
*pres1 = (ms < INT_MAX) ? (int)ms : INT_MAX;
}
else
@@ -2301,6 +2393,9 @@ static CURLcode cf_osslq_query(struct Curl_cfilter *cf,
*when = ctx->handshake_at;
return CURLE_OK;
}
+ case CF_QUERY_HTTP_VERSION:
+ *pres1 = 30;
+ return CURLE_OK;
default:
break;
}
@@ -2311,7 +2406,7 @@ static CURLcode cf_osslq_query(struct Curl_cfilter *cf,
struct Curl_cftype Curl_cft_http3 = {
"HTTP/3",
- CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
+ CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX | CF_TYPE_HTTP,
0,
cf_osslq_destroy,
cf_osslq_connect,
diff --git a/libs/libcurl/src/vquic/curl_osslq.h b/libs/libcurl/src/vquic/curl_osslq.h
index f34d84baf8..f0e670c106 100644
--- a/libs/libcurl/src/vquic/curl_osslq.h
+++ b/libs/libcurl/src/vquic/curl_osslq.h
@@ -24,7 +24,7 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)
@@ -34,7 +34,7 @@
struct Curl_cfilter;
-#include "urldata.h"
+#include "../urldata.h"
void Curl_osslq_ver(char *p, size_t len);
diff --git a/libs/libcurl/src/vquic/curl_quiche.c b/libs/libcurl/src/vquic/curl_quiche.c
index 8cdd512dd0..d8bbbd72f6 100644
--- a/libs/libcurl/src/vquic/curl_quiche.c
+++ b/libs/libcurl/src/vquic/curl_quiche.c
@@ -22,40 +22,40 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_QUICHE
#include <quiche.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
-#include "bufq.h"
-#include "hash.h"
-#include "urldata.h"
-#include "cfilters.h"
-#include "cf-socket.h"
-#include "sendf.h"
-#include "strdup.h"
-#include "rand.h"
-#include "strcase.h"
-#include "multiif.h"
-#include "connect.h"
-#include "progress.h"
-#include "strerror.h"
-#include "http1.h"
+#include "../bufq.h"
+#include "../uint-hash.h"
+#include "../urldata.h"
+#include "../cfilters.h"
+#include "../cf-socket.h"
+#include "../sendf.h"
+#include "../strdup.h"
+#include "../rand.h"
+#include "../strcase.h"
+#include "../multiif.h"
+#include "../connect.h"
+#include "../progress.h"
+#include "../strerror.h"
+#include "../http1.h"
#include "vquic.h"
#include "vquic_int.h"
#include "vquic-tls.h"
#include "curl_quiche.h"
-#include "transfer.h"
-#include "inet_pton.h"
-#include "vtls/openssl.h"
-#include "vtls/keylog.h"
-#include "vtls/vtls.h"
+#include "../transfer.h"
+#include "../curlx/inet_pton.h"
+#include "../vtls/openssl.h"
+#include "../vtls/keylog.h"
+#include "../vtls/vtls.h"
/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
/* HTTP/3 error values defined in RFC 9114, ch. 8.1 */
#define CURL_H3_NO_ERROR (0x0100)
@@ -97,7 +97,7 @@ struct cf_quiche_ctx {
struct curltime started_at; /* time the current attempt started */
struct curltime handshake_at; /* time connect handshake finished */
struct bufc_pool stream_bufcp; /* chunk pool for streams */
- struct Curl_hash streams; /* hash `data->mid` to `stream_ctx` */
+ struct uint_hash streams; /* hash `data->mid` to `stream_ctx` */
curl_off_t data_recvd;
BIT(initialized);
BIT(goaway); /* got GOAWAY from server */
@@ -115,7 +115,7 @@ static void quiche_debug_log(const char *line, void *argp)
}
#endif
-static void h3_stream_hash_free(void *stream);
+static void h3_stream_hash_free(unsigned int id, void *stream);
static void cf_quiche_ctx_init(struct cf_quiche_ctx *ctx)
{
@@ -128,7 +128,7 @@ static void cf_quiche_ctx_init(struct cf_quiche_ctx *ctx)
#endif
Curl_bufcp_init(&ctx->stream_bufcp, H3_STREAM_CHUNK_SIZE,
H3_STREAM_POOL_SPARES);
- Curl_hash_offt_init(&ctx->streams, 63, h3_stream_hash_free);
+ Curl_uint_hash_init(&ctx->streams, 63, h3_stream_hash_free);
ctx->data_recvd = 0;
ctx->initialized = TRUE;
}
@@ -142,8 +142,7 @@ static void cf_quiche_ctx_free(struct cf_quiche_ctx *ctx)
Curl_ssl_peer_cleanup(&ctx->peer);
vquic_ctx_free(&ctx->q);
Curl_bufcp_free(&ctx->stream_bufcp);
- Curl_hash_clean(&ctx->streams);
- Curl_hash_destroy(&ctx->streams);
+ Curl_uint_hash_destroy(&ctx->streams);
}
free(ctx);
}
@@ -166,7 +165,7 @@ static CURLcode cf_flush_egress(struct Curl_cfilter *cf,
/**
* All about the H3 internals of a stream
*/
-struct stream_ctx {
+struct h3_stream_ctx {
curl_uint64_t id; /* HTTP/3 protocol stream identifier */
struct bufq recvbuf; /* h3 response */
struct h1_req_parser h1; /* h1 request parsing */
@@ -180,47 +179,87 @@ struct stream_ctx {
BIT(quic_flow_blocked); /* stream is blocked by QUIC flow control */
};
-#define H3_STREAM_CTX(ctx,data) ((struct stream_ctx *)(\
- data? Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL))
-
-static void h3_stream_ctx_free(struct stream_ctx *stream)
+static void h3_stream_ctx_free(struct h3_stream_ctx *stream)
{
Curl_bufq_free(&stream->recvbuf);
Curl_h1_req_parse_free(&stream->h1);
free(stream);
}
-static void h3_stream_hash_free(void *stream)
+static void h3_stream_hash_free(unsigned int id, void *stream)
{
+ (void)id;
DEBUGASSERT(stream);
- h3_stream_ctx_free((struct stream_ctx *)stream);
+ h3_stream_ctx_free((struct h3_stream_ctx *)stream);
+}
+
+typedef bool cf_quiche_svisit(struct Curl_cfilter *cf,
+ struct Curl_easy *sdata,
+ struct h3_stream_ctx *stream,
+ void *user_data);
+
+struct cf_quiche_visit_ctx {
+ struct Curl_cfilter *cf;
+ struct Curl_multi *multi;
+ cf_quiche_svisit *cb;
+ void *user_data;
+};
+
+static bool cf_quiche_stream_do(unsigned int mid, void *val, void *user_data)
+{
+ struct cf_quiche_visit_ctx *vctx = user_data;
+ struct h3_stream_ctx *stream = val;
+ struct Curl_easy *sdata = Curl_multi_get_easy(vctx->multi, mid);
+ if(sdata)
+ return vctx->cb(vctx->cf, sdata, stream, vctx->user_data);
+ return TRUE;
}
-static void check_resumes(struct Curl_cfilter *cf,
- struct Curl_easy *data)
+static void cf_quiche_for_all_streams(struct Curl_cfilter *cf,
+ struct Curl_multi *multi,
+ cf_quiche_svisit *do_cb,
+ void *user_data)
{
struct cf_quiche_ctx *ctx = cf->ctx;
- struct Curl_llist_node *e;
+ struct cf_quiche_visit_ctx vctx;
+ vctx.cf = cf;
+ vctx.multi = multi;
+ vctx.cb = do_cb;
+ vctx.user_data = user_data;
+ Curl_uint_hash_visit(&ctx->streams, cf_quiche_stream_do, &vctx);
+}
- DEBUGASSERT(data->multi);
- for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
- struct Curl_easy *sdata = Curl_node_elem(e);
- if(sdata->conn == data->conn) {
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, sdata);
- if(stream && stream->quic_flow_blocked) {
- stream->quic_flow_blocked = FALSE;
- Curl_expire(data, 0, EXPIRE_RUN_NOW);
- CURL_TRC_CF(data, cf, "[%"FMT_PRIu64"] unblock", stream->id);
- }
- }
+static bool cf_quiche_do_resume(struct Curl_cfilter *cf,
+ struct Curl_easy *sdata,
+ struct h3_stream_ctx *stream,
+ void *user_data)
+{
+ (void)user_data;
+ if(stream->quic_flow_blocked) {
+ stream->quic_flow_blocked = FALSE;
+ Curl_expire(sdata, 0, EXPIRE_RUN_NOW);
+ CURL_TRC_CF(sdata, cf, "[%"FMT_PRIu64"] unblock", stream->id);
}
+ return TRUE;
+}
+
+static bool cf_quiche_do_expire(struct Curl_cfilter *cf,
+ struct Curl_easy *sdata,
+ struct h3_stream_ctx *stream,
+ void *user_data)
+{
+ (void)stream;
+ (void)user_data;
+ CURL_TRC_CF(sdata, cf, "conn closed, expire transfer");
+ Curl_expire(sdata, 0, EXPIRE_RUN_NOW);
+ return TRUE;
}
static CURLcode h3_data_setup(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_quiche_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
if(stream)
return CURLE_OK;
@@ -234,7 +273,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf,
H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
- if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) {
+ if(!Curl_uint_hash_set(&ctx->streams, data->mid, stream)) {
h3_stream_ctx_free(stream);
return CURLE_OUT_OF_MEMORY;
}
@@ -245,7 +284,7 @@ static CURLcode h3_data_setup(struct Curl_cfilter *cf,
static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct cf_quiche_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
CURLcode result;
(void)cf;
@@ -264,7 +303,7 @@ static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
if(result)
CURL_TRC_CF(data, cf, "data_done, flush egress -> %d", result);
}
- Curl_hash_offt_remove(&ctx->streams, data->mid);
+ Curl_uint_hash_remove(&ctx->streams, data->mid);
}
}
@@ -272,7 +311,7 @@ static void h3_drain_stream(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_quiche_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
unsigned char bits;
(void)cf;
@@ -285,52 +324,12 @@ static void h3_drain_stream(struct Curl_cfilter *cf,
}
}
-static struct Curl_easy *get_stream_easy(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- curl_uint64_t stream_id,
- struct stream_ctx **pstream)
-{
- struct cf_quiche_ctx *ctx = cf->ctx;
- struct stream_ctx *stream;
-
- (void)cf;
- stream = H3_STREAM_CTX(ctx, data);
- if(stream && stream->id == stream_id) {
- *pstream = stream;
- return data;
- }
- else {
- struct Curl_llist_node *e;
- DEBUGASSERT(data->multi);
- for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
- struct Curl_easy *sdata = Curl_node_elem(e);
- if(sdata->conn != data->conn)
- continue;
- stream = H3_STREAM_CTX(ctx, sdata);
- if(stream && stream->id == stream_id) {
- *pstream = stream;
- return sdata;
- }
- }
- }
- *pstream = NULL;
- return NULL;
-}
-
static void cf_quiche_expire_conn_closed(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
- struct Curl_llist_node *e;
-
DEBUGASSERT(data->multi);
CURL_TRC_CF(data, cf, "conn closed, expire all transfers");
- for(e = Curl_llist_head(&data->multi->process); e; e = Curl_node_next(e)) {
- struct Curl_easy *sdata = Curl_node_elem(e);
- if(sdata == data || sdata->conn != data->conn)
- continue;
- CURL_TRC_CF(sdata, cf, "conn closed, expire transfer");
- Curl_expire(sdata, 0, EXPIRE_RUN_NOW);
- }
+ cf_quiche_for_all_streams(cf, data->multi, cf_quiche_do_expire, NULL);
}
/*
@@ -343,7 +342,7 @@ static CURLcode write_resp_raw(struct Curl_cfilter *cf,
const void *mem, size_t memlen)
{
struct cf_quiche_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
CURLcode result = CURLE_OK;
ssize_t nwritten;
@@ -374,7 +373,7 @@ static int cb_each_header(uint8_t *name, size_t name_len,
{
struct cb_ctx *x = argp;
struct cf_quiche_ctx *ctx = x->cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, x->data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, x->data);
CURLcode result;
if(!stream)
@@ -414,7 +413,7 @@ static ssize_t stream_resp_read(void *reader_ctx,
{
struct cb_ctx *x = reader_ctx;
struct cf_quiche_ctx *ctx = x->cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, x->data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, x->data);
ssize_t nread;
if(!stream) {
@@ -438,7 +437,7 @@ static CURLcode cf_recv_body(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_quiche_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
ssize_t nwritten;
struct cb_ctx cb_ctx;
CURLcode result = CURLE_OK;
@@ -496,7 +495,7 @@ static const char *cf_ev_name(quiche_h3_event *ev)
static CURLcode h3_process_event(struct Curl_cfilter *cf,
struct Curl_easy *data,
- struct stream_ctx *stream,
+ struct h3_stream_ctx *stream,
quiche_h3_event *ev)
{
struct cb_ctx cb_ctx;
@@ -557,14 +556,48 @@ static CURLcode h3_process_event(struct Curl_cfilter *cf,
return result;
}
+static CURLcode cf_quiche_ev_process(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct h3_stream_ctx *stream,
+ quiche_h3_event *ev)
+{
+ CURLcode result = h3_process_event(cf, data, stream, ev);
+ h3_drain_stream(cf, data);
+ if(result)
+ CURL_TRC_CF(data, cf, "error processing event %s "
+ "for [%"FMT_PRIu64"] -> %d", cf_ev_name(ev),
+ stream->id, result);
+ return result;
+}
+
+struct cf_quich_disp_ctx {
+ curl_uint64_t stream_id;
+ struct Curl_cfilter *cf;
+ struct Curl_multi *multi;
+ quiche_h3_event *ev;
+ CURLcode result;
+};
+
+static bool cf_quiche_disp_event(unsigned int mid, void *val, void *user_data)
+{
+ struct cf_quich_disp_ctx *dctx = user_data;
+ struct h3_stream_ctx *stream = val;
+
+ if(stream->id == dctx->stream_id) {
+ struct Curl_easy *sdata = Curl_multi_get_easy(dctx->multi, mid);
+ if(sdata)
+ dctx->result = cf_quiche_ev_process(dctx->cf, sdata, stream, dctx->ev);
+ return FALSE; /* stop iterating */
+ }
+ return TRUE;
+}
+
static CURLcode cf_poll_events(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_quiche_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = NULL;
- struct Curl_easy *sdata;
+ struct h3_stream_ctx *stream = NULL;
quiche_h3_event *ev;
- CURLcode result;
/* Take in the events and distribute them to the transfers. */
while(ctx->h3c) {
@@ -576,28 +609,27 @@ static CURLcode cf_poll_events(struct Curl_cfilter *cf,
CURL_TRC_CF(data, cf, "error poll: %"FMT_PRId64, stream3_id);
return CURLE_HTTP3;
}
-
- sdata = get_stream_easy(cf, data, stream3_id, &stream);
- if(!sdata || !stream) {
- CURL_TRC_CF(data, cf, "discard event %s for unknown [%"FMT_PRId64"]",
- cf_ev_name(ev), stream3_id);
- }
else {
- result = h3_process_event(cf, sdata, stream, ev);
- h3_drain_stream(cf, sdata);
- if(result) {
- CURL_TRC_CF(data, cf, "error processing event %s "
- "for [%"FMT_PRIu64"] -> %d", cf_ev_name(ev),
- stream3_id, result);
- if(data == sdata) {
- /* Only report this error to the caller if it is about the
- * transfer we were called with. Otherwise we fail a transfer
- * due to a problem in another one. */
- quiche_h3_event_free(ev);
+ struct cf_quich_disp_ctx dctx;
+ dctx.stream_id = (curl_uint64_t)stream3_id;
+ dctx.cf = cf;
+ dctx.multi = data->multi;
+ dctx.ev = ev;
+ dctx.result = CURLE_OK;
+ stream = H3_STREAM_CTX(ctx, data);
+ if(stream && stream->id == dctx.stream_id) {
+ /* event for calling transfer */
+ CURLcode result = cf_quiche_ev_process(cf, data, stream, ev);
+ quiche_h3_event_free(ev);
+ if(result)
return result;
- }
}
- quiche_h3_event_free(ev);
+ else {
+ /* another transfer, do not return errors, as they are not for
+ * the calling transfer */
+ Curl_uint_hash_visit(&ctx->streams, cf_quiche_disp_event, &dctx);
+ quiche_h3_event_free(ev);
+ }
}
}
return CURLE_OK;
@@ -627,7 +659,8 @@ static CURLcode recv_pkt(const unsigned char *pkt, size_t pktlen,
recv_info.from = (struct sockaddr *)remote_addr;
recv_info.from_len = remote_addrlen;
- nread = quiche_conn_recv(ctx->qconn, (unsigned char *)pkt, pktlen,
+ nread = quiche_conn_recv(ctx->qconn,
+ (unsigned char *)CURL_UNCONST(pkt), pktlen,
&recv_info);
if(nread < 0) {
if(QUICHE_ERR_DONE == nread) {
@@ -686,7 +719,8 @@ static CURLcode cf_process_ingress(struct Curl_cfilter *cf,
if(rctx.pkts > 0) {
/* quiche digested ingress packets. It might have opened flow control
* windows again. */
- check_resumes(cf, data);
+ DEBUGASSERT(data->multi);
+ cf_quiche_for_all_streams(cf, data->multi, cf_quiche_do_resume, NULL);
}
return cf_poll_events(cf, data);
}
@@ -811,7 +845,7 @@ static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
CURLcode *err)
{
struct cf_quiche_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
ssize_t nread = -1;
DEBUGASSERT(stream);
@@ -843,7 +877,7 @@ static ssize_t cf_quiche_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
char *buf, size_t len, CURLcode *err)
{
struct cf_quiche_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
ssize_t nread = -1;
CURLcode result;
@@ -916,7 +950,7 @@ out:
static ssize_t cf_quiche_send_body(struct Curl_cfilter *cf,
struct Curl_easy *data,
- struct stream_ctx *stream,
+ struct h3_stream_ctx *stream,
const void *buf, size_t len, bool eos,
CURLcode *err)
{
@@ -924,10 +958,10 @@ static ssize_t cf_quiche_send_body(struct Curl_cfilter *cf,
ssize_t nwritten;
nwritten = quiche_h3_send_body(ctx->h3c, ctx->qconn, stream->id,
- (uint8_t *)buf, len, eos);
+ (uint8_t *)CURL_UNCONST(buf), len, eos);
if(nwritten == QUICHE_H3_ERR_DONE || (nwritten == 0 && len > 0)) {
- /* TODO: we seem to be blocked on flow control and should HOLD
- * sending. But when do we open again? */
+ /* Blocked on flow control and should HOLD sending. But when do we open
+ * again? */
if(!quiche_conn_stream_writable(ctx->qconn, stream->id, len)) {
CURL_TRC_CF(data, cf, "[%" FMT_PRIu64 "] send_body(len=%zu) "
"-> window exhausted", stream->id, len);
@@ -975,7 +1009,7 @@ static ssize_t h3_open_stream(struct Curl_cfilter *cf,
CURLcode *err)
{
struct cf_quiche_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
size_t nheader, i;
curl_int64_t stream3_id;
struct dynhds h2_headers;
@@ -1095,7 +1129,7 @@ static ssize_t cf_quiche_send(struct Curl_cfilter *cf, struct Curl_easy *data,
CURLcode *err)
{
struct cf_quiche_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
CURLcode result;
ssize_t nwritten;
@@ -1154,7 +1188,7 @@ static bool stream_is_writeable(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
struct cf_quiche_ctx *ctx = cf->ctx;
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
return stream && (quiche_conn_stream_writable(
ctx->qconn, (curl_uint64_t)stream->id, 1) > 0);
@@ -1172,7 +1206,7 @@ static void cf_quiche_adjust_pollset(struct Curl_cfilter *cf,
Curl_pollset_check(data, ps, ctx->q.sockfd, &want_recv, &want_send);
if(want_recv || want_send) {
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
bool c_exhaust, s_exhaust;
c_exhaust = FALSE; /* Have not found any call in quiche that tells
@@ -1195,7 +1229,7 @@ static bool cf_quiche_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data)
{
struct cf_quiche_ctx *ctx = cf->ctx;
- const struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ const struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
(void)cf;
return stream && !Curl_bufq_is_empty(&stream->recvbuf);
}
@@ -1204,8 +1238,8 @@ static CURLcode h3_data_pause(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool pause)
{
- /* TODO: there seems right now no API in quiche to shrink/enlarge
- * the streams windows. As we do in HTTP/2. */
+ /* There seems to exist no API in quiche to shrink/enlarge the streams
+ * windows. As we do in HTTP/2. */
if(!pause) {
h3_drain_stream(cf, data);
Curl_expire(data, 0, EXPIRE_RUN_NOW);
@@ -1228,14 +1262,11 @@ static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf,
case CF_CTRL_DATA_PAUSE:
result = h3_data_pause(cf, data, (arg1 != 0));
break;
- case CF_CTRL_DATA_DETACH:
- h3_data_done(cf, data);
- break;
case CF_CTRL_DATA_DONE:
h3_data_done(cf, data);
break;
case CF_CTRL_DATA_DONE_SEND: {
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
if(stream && !stream->send_closed) {
unsigned char body[1];
ssize_t sent;
@@ -1249,7 +1280,7 @@ static CURLcode cf_quiche_data_event(struct Curl_cfilter *cf,
break;
}
case CF_CTRL_DATA_IDLE: {
- struct stream_ctx *stream = H3_STREAM_CTX(ctx, data);
+ struct h3_stream_ctx *stream = H3_STREAM_CTX(ctx, data);
if(stream && !stream->closed) {
result = cf_flush_egress(cf, data);
if(result)
@@ -1270,6 +1301,9 @@ static CURLcode cf_quiche_ctx_open(struct Curl_cfilter *cf,
int rv;
CURLcode result;
const struct Curl_sockaddr_ex *sockaddr;
+static const struct alpn_spec ALPN_SPEC_H3 = {
+ { "h3" }, 1
+};
DEBUGASSERT(ctx->q.sockfd != CURL_SOCKET_BAD);
DEBUGASSERT(ctx->initialized);
@@ -1278,17 +1312,12 @@ static CURLcode cf_quiche_ctx_open(struct Curl_cfilter *cf,
if(result)
return result;
- result = Curl_ssl_peer_init(&ctx->peer, cf, TRNSPRT_QUIC);
- if(result)
- return result;
-
ctx->cfg = quiche_config_new(QUICHE_PROTOCOL_VERSION);
if(!ctx->cfg) {
failf(data, "cannot create quiche config");
return CURLE_FAILED_INIT;
}
quiche_config_enable_pacing(ctx->cfg, FALSE);
- quiche_config_set_max_idle_timeout(ctx->cfg, CURL_QUIC_MAX_IDLE_MS);
quiche_config_set_initial_max_data(ctx->cfg, (1 * 1024 * 1024)
/* (QUIC_MAX_STREAMS/2) * H3_STREAM_WINDOW_SIZE */);
quiche_config_set_initial_max_streams_bidi(ctx->cfg, QUIC_MAX_STREAMS);
@@ -1305,15 +1334,12 @@ static CURLcode cf_quiche_ctx_open(struct Curl_cfilter *cf,
10 * QUIC_MAX_STREAMS * H3_STREAM_WINDOW_SIZE);
quiche_config_set_max_stream_window(ctx->cfg, 10 * H3_STREAM_WINDOW_SIZE);
quiche_config_set_application_protos(ctx->cfg,
- (uint8_t *)
- QUICHE_H3_APPLICATION_PROTOCOL,
+ (uint8_t *)CURL_UNCONST(QUICHE_H3_APPLICATION_PROTOCOL),
sizeof(QUICHE_H3_APPLICATION_PROTOCOL)
- 1);
result = Curl_vquic_tls_init(&ctx->tls, cf, data, &ctx->peer,
- QUICHE_H3_APPLICATION_PROTOCOL,
- sizeof(QUICHE_H3_APPLICATION_PROTOCOL) - 1,
- NULL, NULL, cf);
+ &ALPN_SPEC_H3, NULL, NULL, cf, NULL);
if(result)
return result;
@@ -1379,14 +1405,13 @@ static CURLcode cf_quiche_verify_peer(struct Curl_cfilter *cf,
struct cf_quiche_ctx *ctx = cf->ctx;
cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
- cf->conn->httpversion = 30;
return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer);
}
static CURLcode cf_quiche_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool blocking, bool *done)
+ bool *done)
{
struct cf_quiche_ctx *ctx = cf->ctx;
CURLcode result = CURLE_OK;
@@ -1398,7 +1423,7 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf,
/* Connect the UDP filter first */
if(!cf->next->connected) {
- result = Curl_conn_cf_connect(cf->next, data, blocking, done);
+ result = Curl_conn_cf_connect(cf->next, data, done);
if(result || !*done)
return result;
}
@@ -1427,7 +1452,7 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf,
if(quiche_conn_is_established(ctx->qconn)) {
ctx->handshake_at = ctx->q.last_op;
CURL_TRC_CF(data, cf, "handshake complete after %dms",
- (int)Curl_timediff(ctx->handshake_at, ctx->started_at));
+ (int)curlx_timediff(ctx->handshake_at, ctx->started_at));
result = cf_quiche_verify_peer(cf, data);
if(!result) {
CURL_TRC_CF(data, cf, "peer verified");
@@ -1543,19 +1568,19 @@ static CURLcode cf_quiche_query(struct Curl_cfilter *cf,
switch(query) {
case CF_QUERY_MAX_CONCURRENT: {
- curl_uint64_t max_streams = CONN_INUSE(cf->conn);
+ curl_uint64_t max_streams = CONN_ATTACHED(cf->conn);
if(!ctx->goaway) {
max_streams += quiche_conn_peer_streams_left_bidi(ctx->qconn);
}
*pres1 = (max_streams > INT_MAX) ? INT_MAX : (int)max_streams;
CURL_TRC_CF(data, cf, "query conn[%" FMT_OFF_T "]: "
- "MAX_CONCURRENT -> %d (%zu in use)",
- cf->conn->connection_id, *pres1, CONN_INUSE(cf->conn));
+ "MAX_CONCURRENT -> %d (%u in use)",
+ cf->conn->connection_id, *pres1, CONN_ATTACHED(cf->conn));
return CURLE_OK;
}
case CF_QUERY_CONNECT_REPLY_MS:
if(ctx->q.got_first_byte) {
- timediff_t ms = Curl_timediff(ctx->q.first_byte_at, ctx->started_at);
+ timediff_t ms = curlx_timediff(ctx->q.first_byte_at, ctx->started_at);
*pres1 = (ms < INT_MAX) ? (int)ms : INT_MAX;
}
else
@@ -1573,6 +1598,9 @@ static CURLcode cf_quiche_query(struct Curl_cfilter *cf,
*when = ctx->handshake_at;
return CURLE_OK;
}
+ case CF_QUERY_HTTP_VERSION:
+ *pres1 = 30;
+ return CURLE_OK;
default:
break;
}
@@ -1620,7 +1648,7 @@ static bool cf_quiche_conn_is_alive(struct Curl_cfilter *cf,
struct Curl_cftype Curl_cft_http3 = {
"HTTP/3",
- CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
+ CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX | CF_TYPE_HTTP,
0,
cf_quiche_destroy,
cf_quiche_connect,
diff --git a/libs/libcurl/src/vquic/curl_quiche.h b/libs/libcurl/src/vquic/curl_quiche.h
index 6af88dd479..caf21e0137 100644
--- a/libs/libcurl/src/vquic/curl_quiche.h
+++ b/libs/libcurl/src/vquic/curl_quiche.h
@@ -24,7 +24,7 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_QUICHE
diff --git a/libs/libcurl/src/vquic/vquic-tls.c b/libs/libcurl/src/vquic/vquic-tls.c
index b246d7fdc3..7eb343538e 100644
--- a/libs/libcurl/src/vquic/vquic-tls.c
+++ b/libs/libcurl/src/vquic/vquic-tls.c
@@ -22,238 +22,82 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_HTTP3) && \
(defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL))
#ifdef USE_OPENSSL
#include <openssl/err.h>
-#include "vtls/openssl.h"
+#include "../vtls/openssl.h"
#elif defined(USE_GNUTLS)
#include <gnutls/abstract.h>
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
#include <gnutls/crypto.h>
#include <nettle/sha2.h>
-#include "vtls/gtls.h"
+#include "../vtls/gtls.h"
#elif defined(USE_WOLFSSL)
#include <wolfssl/options.h>
#include <wolfssl/ssl.h>
#include <wolfssl/quic.h>
-#include "vtls/wolfssl.h"
+#include "../vtls/wolfssl.h"
#endif
-#include "urldata.h"
-#include "curl_trc.h"
-#include "cfilters.h"
-#include "multiif.h"
-#include "vtls/keylog.h"
-#include "vtls/vtls.h"
+#include "../urldata.h"
+#include "../curl_trc.h"
+#include "../cfilters.h"
+#include "../multiif.h"
+#include "../vtls/keylog.h"
+#include "../vtls/vtls.h"
+#include "../vtls/vtls_scache.h"
#include "vquic-tls.h"
/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
-
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
-#endif
-
-#if defined(USE_WOLFSSL)
-
-#define QUIC_CIPHERS \
- "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \
- "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
-#define QUIC_GROUPS "P-256:P-384:P-521"
-
-#if defined(HAVE_SECRET_CALLBACK)
-static void keylog_callback(const WOLFSSL *ssl, const char *line)
-{
- (void)ssl;
- Curl_tls_keylog_write_line(line);
-}
-#endif
-
-static CURLcode wssl_init_ctx(struct curl_tls_ctx *ctx,
- struct Curl_cfilter *cf,
- struct Curl_easy *data,
- Curl_vquic_tls_ctx_setup *cb_setup,
- void *cb_user_data)
-{
- struct ssl_primary_config *conn_config;
- CURLcode result = CURLE_FAILED_INIT;
-
- conn_config = Curl_ssl_cf_get_primary_config(cf);
- if(!conn_config) {
- result = CURLE_FAILED_INIT;
- goto out;
- }
-
- ctx->wssl.ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
- if(!ctx->wssl.ctx) {
- result = CURLE_OUT_OF_MEMORY;
- goto out;
- }
-
- if(cb_setup) {
- result = cb_setup(cf, data, cb_user_data);
- if(result)
- goto out;
- }
-
- wolfSSL_CTX_set_default_verify_paths(ctx->wssl.ctx);
-
- if(wolfSSL_CTX_set_cipher_list(ctx->wssl.ctx, conn_config->cipher_list13 ?
- conn_config->cipher_list13 :
- QUIC_CIPHERS) != 1) {
- char error_buffer[256];
- ERR_error_string_n(ERR_get_error(), error_buffer, sizeof(error_buffer));
- failf(data, "wolfSSL failed to set ciphers: %s", error_buffer);
- result = CURLE_BAD_FUNCTION_ARGUMENT;
- goto out;
- }
-
- if(wolfSSL_CTX_set1_groups_list(ctx->wssl.ctx, conn_config->curves ?
- conn_config->curves :
- (char *)QUIC_GROUPS) != 1) {
- failf(data, "wolfSSL failed to set curves");
- result = CURLE_BAD_FUNCTION_ARGUMENT;
- goto out;
- }
-
- /* Open the file if a TLS or QUIC backend has not done this before. */
- Curl_tls_keylog_open();
- if(Curl_tls_keylog_enabled()) {
-#if defined(HAVE_SECRET_CALLBACK)
- wolfSSL_CTX_set_keylog_callback(ctx->wssl.ctx, keylog_callback);
-#else
- failf(data, "wolfSSL was built without keylog callback");
- result = CURLE_NOT_BUILT_IN;
- goto out;
-#endif
- }
-
- if(conn_config->verifypeer) {
- const char * const ssl_cafile = conn_config->CAfile;
- const char * const ssl_capath = conn_config->CApath;
-
- wolfSSL_CTX_set_verify(ctx->wssl.ctx, SSL_VERIFY_PEER, NULL);
- if(ssl_cafile || ssl_capath) {
- /* tell wolfSSL where to find CA certificates that are used to verify
- the server's certificate. */
- int rc =
- wolfSSL_CTX_load_verify_locations_ex(ctx->wssl.ctx, ssl_cafile,
- ssl_capath,
- WOLFSSL_LOAD_FLAG_IGNORE_ERR);
- if(SSL_SUCCESS != rc) {
- /* Fail if we insist on successfully verifying the server. */
- failf(data, "error setting certificate verify locations:"
- " CAfile: %s CApath: %s",
- ssl_cafile ? ssl_cafile : "none",
- ssl_capath ? ssl_capath : "none");
- result = CURLE_SSL_CACERT_BADFILE;
- goto out;
- }
- infof(data, " CAfile: %s", ssl_cafile ? ssl_cafile : "none");
- infof(data, " CApath: %s", ssl_capath ? ssl_capath : "none");
- }
-#ifdef CURL_CA_FALLBACK
- else {
- /* verifying the peer without any CA certificates will not work so
- use wolfSSL's built-in default as fallback */
- wolfSSL_CTX_set_default_verify_paths(ctx->wssl.ctx);
- }
-#endif
- }
- else {
- wolfSSL_CTX_set_verify(ctx->wssl.ctx, SSL_VERIFY_NONE, NULL);
- }
-
- /* give application a chance to interfere with SSL set up. */
- if(data->set.ssl.fsslctx) {
- Curl_set_in_callback(data, TRUE);
- result = (*data->set.ssl.fsslctx)(data, ctx->wssl.ctx,
- data->set.ssl.fsslctxp);
- Curl_set_in_callback(data, FALSE);
- if(result) {
- failf(data, "error signaled by ssl ctx callback");
- goto out;
- }
- }
- result = CURLE_OK;
-
-out:
- if(result && ctx->wssl.ctx) {
- SSL_CTX_free(ctx->wssl.ctx);
- ctx->wssl.ctx = NULL;
- }
- return result;
-}
-
-/** SSL callbacks ***/
-
-static CURLcode wssl_init_ssl(struct curl_tls_ctx *ctx,
- struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct ssl_peer *peer,
- const char *alpn, size_t alpn_len,
- void *user_data)
-{
- struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
-
- DEBUGASSERT(!ctx->wssl.handle);
- DEBUGASSERT(ctx->wssl.ctx);
- ctx->wssl.handle = wolfSSL_new(ctx->wssl.ctx);
-
- wolfSSL_set_app_data(ctx->wssl.handle, user_data);
- wolfSSL_set_connect_state(ctx->wssl.handle);
- wolfSSL_set_quic_use_legacy_codepoint(ctx->wssl.handle, 0);
-
- if(alpn)
- wolfSSL_set_alpn_protos(ctx->wssl.handle, (const unsigned char *)alpn,
- (unsigned int)alpn_len);
-
- if(peer->sni) {
- wolfSSL_UseSNI(ctx->wssl.handle, WOLFSSL_SNI_HOST_NAME,
- peer->sni, (unsigned short)strlen(peer->sni));
- }
-
- if(ssl_config->primary.cache_session) {
- (void)wssl_setup_session(cf, data, &ctx->wssl, peer);
- }
-
- return CURLE_OK;
-}
-#endif /* defined(USE_WOLFSSL) */
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
- const char *alpn, size_t alpn_len,
+ const struct alpn_spec *alpns,
Curl_vquic_tls_ctx_setup *cb_setup,
- void *cb_user_data, void *ssl_user_data)
+ void *cb_user_data, void *ssl_user_data,
+ Curl_vquic_session_reuse_cb *session_reuse_cb)
{
+ char tls_id[80];
CURLcode result;
#ifdef USE_OPENSSL
- (void)result;
- return Curl_ossl_ctx_init(&ctx->ossl, cf, data, peer, TRNSPRT_QUIC,
- (const unsigned char *)alpn, alpn_len,
- cb_setup, cb_user_data, NULL, ssl_user_data);
+ Curl_ossl_version(tls_id, sizeof(tls_id));
#elif defined(USE_GNUTLS)
- (void)result;
- return Curl_gtls_ctx_init(&ctx->gtls, cf, data, peer,
- (const unsigned char *)alpn, alpn_len, NULL,
- cb_setup, cb_user_data, ssl_user_data);
+ Curl_gtls_version(tls_id, sizeof(tls_id));
#elif defined(USE_WOLFSSL)
- result = wssl_init_ctx(ctx, cf, data, cb_setup, cb_user_data);
+ Curl_wssl_version(tls_id, sizeof(tls_id));
+#else
+#error "no TLS lib in used, should not happen"
+ return CURLE_FAILED_INIT;
+#endif
+ (void)session_reuse_cb;
+ result = Curl_ssl_peer_init(peer, cf, tls_id, TRNSPRT_QUIC);
if(result)
return result;
- return wssl_init_ssl(ctx, cf, data, peer, alpn, alpn_len, ssl_user_data);
+#ifdef USE_OPENSSL
+ (void)result;
+ return Curl_ossl_ctx_init(&ctx->ossl, cf, data, peer, alpns,
+ cb_setup, cb_user_data, NULL, ssl_user_data,
+ session_reuse_cb);
+#elif defined(USE_GNUTLS)
+ return Curl_gtls_ctx_init(&ctx->gtls, cf, data, peer, alpns,
+ cb_setup, cb_user_data, ssl_user_data,
+ session_reuse_cb);
+#elif defined(USE_WOLFSSL)
+ return Curl_wssl_ctx_init(&ctx->wssl, cf, data, peer, alpns,
+ cb_setup, cb_user_data,
+ ssl_user_data, session_reuse_cb);
#else
#error "no TLS lib in used, should not happen"
return CURLE_FAILED_INIT;
@@ -272,10 +116,10 @@ void Curl_vquic_tls_cleanup(struct curl_tls_ctx *ctx)
gnutls_deinit(ctx->gtls.session);
Curl_gtls_shared_creds_free(&ctx->gtls.shared_creds);
#elif defined(USE_WOLFSSL)
- if(ctx->wssl.handle)
- wolfSSL_free(ctx->wssl.handle);
- if(ctx->wssl.ctx)
- wolfSSL_CTX_free(ctx->wssl.ctx);
+ if(ctx->wssl.ssl)
+ wolfSSL_free(ctx->wssl.ssl);
+ if(ctx->wssl.ssl_ctx)
+ wolfSSL_CTX_free(ctx->wssl.ssl_ctx);
#endif
memset(ctx, 0, sizeof(*ctx));
}
@@ -335,17 +179,20 @@ CURLcode Curl_vquic_tls_verify_peer(struct curl_tls_ctx *ctx,
#elif defined(USE_WOLFSSL)
(void)data;
if(conn_config->verifyhost) {
- if(peer->sni) {
- WOLFSSL_X509* cert = wolfSSL_get_peer_certificate(ctx->wssl.handle);
- if(wolfSSL_X509_check_host(cert, peer->sni, strlen(peer->sni), 0, NULL)
- == WOLFSSL_FAILURE) {
- result = CURLE_PEER_FAILED_VERIFICATION;
- }
- wolfSSL_X509_free(cert);
+ char *snihost = peer->sni ? peer->sni : peer->hostname;
+ WOLFSSL_X509* cert = wolfSSL_get_peer_certificate(ctx->wssl.ssl);
+ if(wolfSSL_X509_check_host(cert, snihost, strlen(snihost), 0, NULL)
+ == WOLFSSL_FAILURE) {
+ result = CURLE_PEER_FAILED_VERIFICATION;
}
-
+ wolfSSL_X509_free(cert);
}
+ if(!result)
+ result = Curl_wssl_verify_pinned(cf, data, &ctx->wssl);
#endif
+ /* on error, remove any session we might have in the pool */
+ if(result)
+ Curl_ssl_scache_remove_all(cf, data, peer->scache_key);
return result;
}
diff --git a/libs/libcurl/src/vquic/vquic-tls.h b/libs/libcurl/src/vquic/vquic-tls.h
index 558170810b..0b9a1ec4f7 100644
--- a/libs/libcurl/src/vquic/vquic-tls.h
+++ b/libs/libcurl/src/vquic/vquic-tls.h
@@ -24,14 +24,19 @@
*
***************************************************************************/
-#include "curl_setup.h"
-#include "bufq.h"
-#include "vtls/openssl.h"
+#include "../curl_setup.h"
+#include "../bufq.h"
+#include "../vtls/vtls.h"
+#include "../vtls/vtls_int.h"
+#include "../vtls/openssl.h"
#if defined(USE_HTTP3) && \
(defined(USE_OPENSSL) || defined(USE_GNUTLS) || defined(USE_WOLFSSL))
-#include "vtls/wolfssl.h"
+#include "../vtls/wolfssl.h"
+
+struct ssl_peer;
+struct Curl_ssl_session;
struct curl_tls_ctx {
#ifdef USE_OPENSSL
@@ -39,7 +44,7 @@ struct curl_tls_ctx {
#elif defined(USE_GNUTLS)
struct gtls_ctx gtls;
#elif defined(USE_WOLFSSL)
- struct wolfssl_ctx wssl;
+ struct wssl_ctx wssl;
#endif
};
@@ -54,6 +59,12 @@ typedef CURLcode Curl_vquic_tls_ctx_setup(struct Curl_cfilter *cf,
struct Curl_easy *data,
void *cb_user_data);
+typedef CURLcode Curl_vquic_session_reuse_cb(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct alpn_spec *alpns,
+ struct Curl_ssl_session *scs,
+ bool *do_early_data);
+
/**
* Initialize the QUIC TLS instances based of the SSL configurations
* for the connection filter, transfer and peer.
@@ -61,21 +72,21 @@ typedef CURLcode Curl_vquic_tls_ctx_setup(struct Curl_cfilter *cf,
* @param cf the connection filter involved
* @param data the transfer involved
* @param peer the peer that will be connected to
- * @param alpn the ALPN string in protocol format ((len+bytes+)+),
- * may be NULL
- * @param alpn_len the overall number of bytes in `alpn`
+ * @param alpns the ALPN specifications to negotiate, may be NULL
* @param cb_setup optional callback for early TLS config
- ± @param cb_user_data user_data param for callback
+ * @param cb_user_data user_data param for callback
* @param ssl_user_data optional pointer to set in TLS application context
+ * @param session_reuse_cb callback to handle session reuse, signal early data
*/
CURLcode Curl_vquic_tls_init(struct curl_tls_ctx *ctx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
- const char *alpn, size_t alpn_len,
+ const struct alpn_spec *alpns,
Curl_vquic_tls_ctx_setup *cb_setup,
void *cb_user_data,
- void *ssl_user_data);
+ void *ssl_user_data,
+ Curl_vquic_session_reuse_cb *session_reuse_cb);
/**
* Cleanup all data that has been initialized.
diff --git a/libs/libcurl/src/vquic/vquic.c b/libs/libcurl/src/vquic/vquic.c
index 960c710170..4b7ab97c5b 100644
--- a/libs/libcurl/src/vquic/vquic.c
+++ b/libs/libcurl/src/vquic/vquic.c
@@ -22,7 +22,7 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef HAVE_NETINET_UDP_H
#include <netinet/udp.h>
@@ -30,39 +30,44 @@
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
-#include "urldata.h"
-#include "bufq.h"
-#include "dynbuf.h"
-#include "cfilters.h"
-#include "curl_trc.h"
+#include "../urldata.h"
+#include "../bufq.h"
+#include "../curlx/dynbuf.h"
+#include "../cfilters.h"
+#include "../curl_trc.h"
#include "curl_msh3.h"
#include "curl_ngtcp2.h"
#include "curl_osslq.h"
#include "curl_quiche.h"
-#include "multiif.h"
-#include "rand.h"
+#include "../multiif.h"
+#include "../rand.h"
#include "vquic.h"
#include "vquic_int.h"
-#include "strerror.h"
+#include "../strerror.h"
+#include "../curlx/strparse.h"
/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
#ifdef USE_HTTP3
-#ifdef O_BINARY
-#define QLOGMODE O_WRONLY|O_CREAT|O_BINARY
-#else
-#define QLOGMODE O_WRONLY|O_CREAT
-#endif
-
#define NW_CHUNK_SIZE (64 * 1024)
#define NW_SEND_CHUNKS 2
+int Curl_vquic_init(void)
+{
+#if defined(USE_NGTCP2) && defined(OPENSSL_QUIC_API2)
+ if(ngtcp2_crypto_ossl_init())
+ return 0;
+#endif
+
+ return 1;
+}
+
void Curl_quic_ver(char *p, size_t len)
{
#if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
@@ -87,10 +92,10 @@ CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx)
#endif
#ifdef DEBUGBUILD
{
- char *p = getenv("CURL_DBG_QUIC_WBLOCK");
+ const char *p = getenv("CURL_DBG_QUIC_WBLOCK");
if(p) {
- long l = strtol(p, NULL, 10);
- if(l >= 0 && l <= 100)
+ curl_off_t l;
+ if(!curlx_str_number(&p, &l, 100))
qctx->wblock_percent = (int)l;
}
}
@@ -107,7 +112,7 @@ void vquic_ctx_free(struct cf_quic_ctx *qctx)
void vquic_ctx_update_time(struct cf_quic_ctx *qctx)
{
- qctx->last_op = Curl_now();
+ qctx->last_op = curlx_now();
}
static CURLcode send_packet_no_gso(struct Curl_cfilter *cf,
@@ -132,7 +137,7 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf,
#endif
*psent = 0;
- msg_iov.iov_base = (uint8_t *)pkt;
+ msg_iov.iov_base = (uint8_t *)CURL_UNCONST(pkt);
msg_iov.iov_len = pktlen;
msg.msg_iov = &msg_iov;
msg.msg_iovlen = 1;
@@ -153,23 +158,24 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf,
#endif
- while((sent = sendmsg(qctx->sockfd, &msg, 0)) == -1 && SOCKERRNO == EINTR)
+ while((sent = sendmsg(qctx->sockfd, &msg, 0)) == -1 &&
+ SOCKERRNO == SOCKEINTR)
;
if(sent == -1) {
switch(SOCKERRNO) {
case EAGAIN:
-#if EAGAIN != EWOULDBLOCK
- case EWOULDBLOCK:
+#if EAGAIN != SOCKEWOULDBLOCK
+ case SOCKEWOULDBLOCK:
#endif
return CURLE_AGAIN;
- case EMSGSIZE:
+ case SOCKEMSGSIZE:
/* UDP datagram is too large; caused by PMTUD. Just let it be lost. */
break;
case EIO:
if(pktlen > gsolen) {
/* GSO failure */
- failf(data, "sendmsg() returned %zd (errno %d); disable GSO", sent,
+ infof(data, "sendmsg() returned %zd (errno %d); disable GSO", sent,
SOCKERRNO);
qctx->no_gso = TRUE;
return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent);
@@ -191,16 +197,16 @@ static CURLcode do_sendmsg(struct Curl_cfilter *cf,
while((sent = send(qctx->sockfd,
(const char *)pkt, (SEND_TYPE_ARG3)pktlen, 0)) == -1 &&
- SOCKERRNO == EINTR)
+ SOCKERRNO == SOCKEINTR)
;
if(sent == -1) {
- if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
+ if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) {
return CURLE_AGAIN;
}
else {
failf(data, "send() returned %zd (errno %d)", sent, SOCKERRNO);
- if(SOCKERRNO != EMSGSIZE) {
+ if(SOCKERRNO != SOCKEMSGSIZE) {
return CURLE_SEND_ERROR;
}
/* UDP datagram is too large; caused by PMTUD. Just let it be
@@ -248,6 +254,7 @@ static CURLcode vquic_send_packets(struct Curl_cfilter *cf,
/* simulate network blocking/partial writes */
if(qctx->wblock_percent > 0) {
unsigned char c;
+ *psent = 0;
Curl_rand(data, &c, 1);
if(c >= ((100-qctx->wblock_percent)*256/100)) {
CURL_TRC_CF(data, cf, "vquic_flush() simulate EWOULDBLOCK");
@@ -363,7 +370,7 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf,
struct mmsghdr mmsg[MMSG_NUM];
uint8_t msg_ctrl[MMSG_NUM * CMSG_SPACE(sizeof(int))];
struct sockaddr_storage remote_addr[MMSG_NUM];
- size_t total_nread = 0, pkts;
+ size_t total_nread = 0, pkts = 0;
int mcount, i, n;
char errstr[STRERROR_LEN];
CURLcode result = CURLE_OK;
@@ -380,10 +387,9 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf,
goto out;
bufs = (uint8_t (*)[64*1024])sockbuf;
- pkts = 0;
total_nread = 0;
while(pkts < max_pkts) {
- n = (int)CURLMIN(MMSG_NUM, max_pkts);
+ n = (int)CURLMIN(CURLMIN(MMSG_NUM, IOV_MAX), max_pkts);
memset(&mmsg, 0, sizeof(mmsg));
for(i = 0; i < n; ++i) {
msg_iov[i].iov_base = bufs[i];
@@ -397,14 +403,14 @@ static CURLcode recvmmsg_packets(struct Curl_cfilter *cf,
}
while((mcount = recvmmsg(qctx->sockfd, mmsg, n, 0, NULL)) == -1 &&
- SOCKERRNO == EINTR)
+ SOCKERRNO == SOCKEINTR)
;
if(mcount == -1) {
- if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
+ if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) {
CURL_TRC_CF(data, cf, "ingress, recvmmsg -> EAGAIN");
goto out;
}
- if(!cf->connected && SOCKERRNO == ECONNREFUSED) {
+ if(!cf->connected && SOCKERRNO == SOCKECONNREFUSED) {
struct ip_quadruple ip;
Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip);
failf(data, "QUIC: connection to %s port %u refused",
@@ -475,27 +481,28 @@ static CURLcode recvmsg_packets(struct Curl_cfilter *cf,
size_t pktlen;
size_t offset, to;
- msg_iov.iov_base = buf;
- msg_iov.iov_len = (int)sizeof(buf);
-
- memset(&msg, 0, sizeof(msg));
- msg.msg_iov = &msg_iov;
- msg.msg_iovlen = 1;
- msg.msg_control = msg_ctrl;
-
DEBUGASSERT(max_pkts > 0);
for(pkts = 0, total_nread = 0; pkts < max_pkts;) {
+ /* fully initialise this on each call to `recvmsg()`. There seem to
+ * operating systems out there that mess with `msg_iov.iov_len`. */
+ memset(&msg, 0, sizeof(msg));
+ msg_iov.iov_base = buf;
+ msg_iov.iov_len = (int)sizeof(buf);
+ msg.msg_iov = &msg_iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = msg_ctrl;
msg.msg_name = &remote_addr;
msg.msg_namelen = sizeof(remote_addr);
msg.msg_controllen = sizeof(msg_ctrl);
+
while((nread = recvmsg(qctx->sockfd, &msg, 0)) == -1 &&
- SOCKERRNO == EINTR)
+ SOCKERRNO == SOCKEINTR)
;
if(nread == -1) {
- if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
+ if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) {
goto out;
}
- if(!cf->connected && SOCKERRNO == ECONNREFUSED) {
+ if(!cf->connected && SOCKERRNO == SOCKECONNREFUSED) {
struct ip_quadruple ip;
Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip);
failf(data, "QUIC: connection to %s port %u refused",
@@ -563,14 +570,14 @@ static CURLcode recvfrom_packets(struct Curl_cfilter *cf,
while((nread = recvfrom(qctx->sockfd, (char *)buf, bufsize, 0,
(struct sockaddr *)&remote_addr,
&remote_addrlen)) == -1 &&
- SOCKERRNO == EINTR)
+ SOCKERRNO == SOCKEINTR)
;
if(nread == -1) {
- if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) {
+ if(SOCKERRNO == EAGAIN || SOCKERRNO == SOCKEWOULDBLOCK) {
CURL_TRC_CF(data, cf, "ingress, recvfrom -> EAGAIN");
goto out;
}
- if(!cf->connected && SOCKERRNO == ECONNREFUSED) {
+ if(!cf->connected && SOCKERRNO == SOCKECONNREFUSED) {
struct ip_quadruple ip;
Curl_cf_socket_peek(cf->next, data, NULL, NULL, &ip);
failf(data, "QUIC: connection to %s port %u refused",
@@ -644,25 +651,25 @@ CURLcode Curl_qlogdir(struct Curl_easy *data,
struct dynbuf fname;
CURLcode result;
unsigned int i;
- Curl_dyn_init(&fname, DYN_QLOG_NAME);
- result = Curl_dyn_add(&fname, qlog_dir);
+ curlx_dyn_init(&fname, DYN_QLOG_NAME);
+ result = curlx_dyn_add(&fname, qlog_dir);
if(!result)
- result = Curl_dyn_add(&fname, "/");
+ result = curlx_dyn_add(&fname, "/");
for(i = 0; (i < scidlen) && !result; i++) {
char hex[3];
msnprintf(hex, 3, "%02x", scid[i]);
- result = Curl_dyn_add(&fname, hex);
+ result = curlx_dyn_add(&fname, hex);
}
if(!result)
- result = Curl_dyn_add(&fname, ".sqlog");
+ result = curlx_dyn_add(&fname, ".sqlog");
if(!result) {
- int qlogfd = open(Curl_dyn_ptr(&fname), QLOGMODE,
+ int qlogfd = open(curlx_dyn_ptr(&fname), O_WRONLY|O_CREAT|CURL_O_BINARY,
data->set.new_file_perms);
if(qlogfd != -1)
*qlogfdp = qlogfd;
}
- Curl_dyn_free(&fname);
+ curlx_dyn_free(&fname);
if(result)
return result;
}
@@ -695,24 +702,6 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
#endif
}
-bool Curl_conn_is_http3(const struct Curl_easy *data,
- const struct connectdata *conn,
- int sockindex)
-{
-#if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
- return Curl_conn_is_ngtcp2(data, conn, sockindex);
-#elif defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)
- return Curl_conn_is_osslq(data, conn, sockindex);
-#elif defined(USE_QUICHE)
- return Curl_conn_is_quiche(data, conn, sockindex);
-#elif defined(USE_MSH3)
- return Curl_conn_is_msh3(data, conn, sockindex);
-#else
- return ((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
- (conn->httpversion == 30));
-#endif
-}
-
CURLcode Curl_conn_may_http3(struct Curl_easy *data,
const struct connectdata *conn)
{
diff --git a/libs/libcurl/src/vquic/vquic.h b/libs/libcurl/src/vquic/vquic.h
index dde9003fa3..0534d49286 100644
--- a/libs/libcurl/src/vquic/vquic.h
+++ b/libs/libcurl/src/vquic/vquic.h
@@ -24,7 +24,7 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_HTTP3
struct Curl_cfilter;
@@ -33,6 +33,7 @@ struct connectdata;
struct Curl_addrinfo;
void Curl_quic_ver(char *p, size_t len);
+int Curl_vquic_init(void);
CURLcode Curl_qlogdir(struct Curl_easy *data,
unsigned char *scid,
@@ -46,16 +47,10 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
const struct Curl_addrinfo *ai,
int transport);
-bool Curl_conn_is_http3(const struct Curl_easy *data,
- const struct connectdata *conn,
- int sockindex);
-
extern struct Curl_cftype Curl_cft_http3;
-#else /* USE_HTTP3 */
-
-#define Curl_conn_is_http3(a,b,c) FALSE
-
+#else
+#define Curl_vquic_init() 1
#endif /* !USE_HTTP3 */
CURLcode Curl_conn_may_http3(struct Curl_easy *data,
diff --git a/libs/libcurl/src/vquic/vquic_int.h b/libs/libcurl/src/vquic/vquic_int.h
index 52203735bf..75dfd33d26 100644
--- a/libs/libcurl/src/vquic/vquic_int.h
+++ b/libs/libcurl/src/vquic/vquic_int.h
@@ -24,15 +24,13 @@
*
***************************************************************************/
-#include "curl_setup.h"
-#include "bufq.h"
+#include "../curl_setup.h"
+#include "../bufq.h"
#ifdef USE_HTTP3
#define MAX_PKT_BURST 10
#define MAX_UDP_PAYLOAD_SIZE 1452
-/* Default QUIC connection timeout we announce from our side */
-#define CURL_QUIC_MAX_IDLE_MS (120 * 1000)
struct cf_quic_ctx {
curl_socket_t sockfd; /* connected UDP socket */
@@ -53,6 +51,9 @@ struct cf_quic_ctx {
BIT(no_gso); /* do not use gso on sending */
};
+#define H3_STREAM_CTX(ctx,data) \
+ (data ? Curl_uint_hash_get(&(ctx)->streams, (data)->mid) : NULL)
+
CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx);
void vquic_ctx_free(struct cf_quic_ctx *qctx);
diff --git a/libs/libcurl/src/vssh/curl_path.c b/libs/libcurl/src/vssh/curl_path.c
index 7cf09022e0..f6837e7c9a 100644
--- a/libs/libcurl/src/vssh/curl_path.c
+++ b/libs/libcurl/src/vssh/curl_path.c
@@ -22,15 +22,15 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_SSH)
#include "curl_path.h"
#include <curl/curl.h>
-#include "curl_memory.h"
-#include "escape.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../escape.h"
+#include "../memdebug.h"
#define MAX_SSHPATH_LEN 100000 /* arbitrary */
@@ -50,13 +50,13 @@ CURLcode Curl_getworkingpath(struct Curl_easy *data,
return result;
/* new path to switch to in case we need to */
- Curl_dyn_init(&npath, MAX_SSHPATH_LEN);
+ curlx_dyn_init(&npath, MAX_SSHPATH_LEN);
/* Check for /~/, indicating relative to the user's home directory */
if((data->conn->handler->protocol & CURLPROTO_SCP) &&
(working_path_len > 3) && (!memcmp(working_path, "/~/", 3))) {
/* It is referenced to the home directory, so strip the leading '/~/' */
- if(Curl_dyn_addn(&npath, &working_path[3], working_path_len - 3)) {
+ if(curlx_dyn_addn(&npath, &working_path[3], working_path_len - 3)) {
free(working_path);
return CURLE_OUT_OF_MEMORY;
}
@@ -64,7 +64,7 @@ CURLcode Curl_getworkingpath(struct Curl_easy *data,
else if((data->conn->handler->protocol & CURLPROTO_SFTP) &&
(!strcmp("/~", working_path) ||
((working_path_len > 2) && !memcmp(working_path, "/~/", 3)))) {
- if(Curl_dyn_add(&npath, homedir)) {
+ if(curlx_dyn_add(&npath, homedir)) {
free(working_path);
return CURLE_OUT_OF_MEMORY;
}
@@ -73,24 +73,24 @@ CURLcode Curl_getworkingpath(struct Curl_easy *data,
const char *p;
int copyfrom = 3;
/* Copy a separating '/' if homedir does not end with one */
- len = Curl_dyn_len(&npath);
- p = Curl_dyn_ptr(&npath);
+ len = curlx_dyn_len(&npath);
+ p = curlx_dyn_ptr(&npath);
if(len && (p[len-1] != '/'))
copyfrom = 2;
- if(Curl_dyn_addn(&npath,
- &working_path[copyfrom], working_path_len - copyfrom)) {
+ if(curlx_dyn_addn(&npath, &working_path[copyfrom],
+ working_path_len - copyfrom)) {
free(working_path);
return CURLE_OUT_OF_MEMORY;
}
}
}
- if(Curl_dyn_len(&npath)) {
+ if(curlx_dyn_len(&npath)) {
free(working_path);
/* store the pointer for the caller to receive */
- *path = Curl_dyn_ptr(&npath);
+ *path = curlx_dyn_ptr(&npath);
}
else
*path = working_path;
@@ -133,7 +133,7 @@ CURLcode Curl_get_pathname(const char **cpp, char **path, const char *homedir)
if(!*cp || !homedir)
return CURLE_QUOTE_ERROR;
- Curl_dyn_init(&out, MAX_PATHLENGTH);
+ curlx_dyn_init(&out, MAX_PATHLENGTH);
/* Ignore leading whitespace */
cp += strspn(cp, WHITESPACE);
@@ -158,12 +158,12 @@ CURLcode Curl_get_pathname(const char **cpp, char **path, const char *homedir)
goto fail;
}
}
- result = Curl_dyn_addn(&out, &cp[i], 1);
+ result = curlx_dyn_addn(&out, &cp[i], 1);
if(result)
return result;
}
- if(!Curl_dyn_len(&out))
+ if(!curlx_dyn_len(&out))
goto fail;
/* return pointer to second parameter if it exists */
@@ -180,23 +180,23 @@ CURLcode Curl_get_pathname(const char **cpp, char **path, const char *homedir)
/* Handling for relative path - prepend home directory */
if(cp[0] == '/' && cp[1] == '~' && cp[2] == '/') {
- result = Curl_dyn_add(&out, homedir);
+ result = curlx_dyn_add(&out, homedir);
if(!result)
- result = Curl_dyn_addn(&out, "/", 1);
+ result = curlx_dyn_addn(&out, "/", 1);
if(result)
return result;
cp += 3;
}
/* Copy path name up until first "whitespace" */
- result = Curl_dyn_addn(&out, cp, (end - cp));
+ result = curlx_dyn_addn(&out, cp, (end - cp));
if(result)
return result;
}
- *path = Curl_dyn_ptr(&out);
+ *path = curlx_dyn_ptr(&out);
return CURLE_OK;
fail:
- Curl_dyn_free(&out);
+ curlx_dyn_free(&out);
return CURLE_QUOTE_ERROR;
}
diff --git a/libs/libcurl/src/vssh/curl_path.h b/libs/libcurl/src/vssh/curl_path.h
index 4bac618e13..93c98bde95 100644
--- a/libs/libcurl/src/vssh/curl_path.h
+++ b/libs/libcurl/src/vssh/curl_path.h
@@ -24,9 +24,9 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#include <curl/curl.h>
-#include "urldata.h"
+#include "../urldata.h"
CURLcode Curl_getworkingpath(struct Curl_easy *data,
char *homedir,
diff --git a/libs/libcurl/src/vssh/libssh.c b/libs/libcurl/src/vssh/libssh.c
index b1e9e79396..8de13c5e26 100644
--- a/libs/libcurl/src/vssh/libssh.c
+++ b/libs/libcurl/src/vssh/libssh.c
@@ -25,7 +25,7 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_LIBSSH
@@ -46,29 +46,29 @@
#endif
#include <curl/curl.h>
-#include "urldata.h"
-#include "sendf.h"
-#include "hostip.h"
-#include "progress.h"
-#include "transfer.h"
-#include "escape.h"
-#include "http.h" /* for HTTP proxy tunnel stuff */
+#include "../urldata.h"
+#include "../sendf.h"
+#include "../hostip.h"
+#include "../progress.h"
+#include "../transfer.h"
+#include "../escape.h"
+#include "../http.h" /* for HTTP proxy tunnel stuff */
#include "ssh.h"
-#include "url.h"
-#include "speedcheck.h"
-#include "getinfo.h"
-#include "strdup.h"
-#include "strcase.h"
-#include "vtls/vtls.h"
-#include "cfilters.h"
-#include "connect.h"
-#include "inet_ntop.h"
-#include "parsedate.h" /* for the week day and month names */
-#include "sockaddr.h" /* required for Curl_sockaddr_storage */
-#include "strtoofft.h"
-#include "multiif.h"
-#include "select.h"
-#include "warnless.h"
+#include "../url.h"
+#include "../speedcheck.h"
+#include "../getinfo.h"
+#include "../strdup.h"
+#include "../strcase.h"
+#include "../vtls/vtls.h"
+#include "../cfilters.h"
+#include "../connect.h"
+#include "../inet_ntop.h"
+#include "../parsedate.h" /* for the week day and month names */
+#include "../sockaddr.h" /* required for Curl_sockaddr_storage */
+#include "../curlx/strparse.h"
+#include "../multiif.h"
+#include "../select.h"
+#include "../curlx/warnless.h"
#include "curl_path.h"
#ifdef HAVE_SYS_STAT_H
@@ -82,9 +82,9 @@
#endif
/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
/* A recent macro provided by libssh. Or make our own. */
#ifndef SSH_STRING_FREE_CHAR
@@ -130,14 +130,19 @@ CURLcode sftp_perform(struct Curl_easy *data,
bool *connected,
bool *dophase_done);
-static void sftp_quote(struct Curl_easy *data);
-static void sftp_quote_stat(struct Curl_easy *data);
+static void sftp_quote(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp);
+static void sftp_quote_stat(struct Curl_easy *data, struct ssh_conn *sshc);
static int myssh_getsock(struct Curl_easy *data,
struct connectdata *conn, curl_socket_t *sock);
-static void myssh_block2waitfor(struct connectdata *conn, bool block);
+static void myssh_block2waitfor(struct connectdata *conn,
+ struct ssh_conn *sshc,
+ bool block);
static CURLcode myssh_setup_connection(struct Curl_easy *data,
struct connectdata *conn);
+static void sshc_cleanup(struct ssh_conn *sshc);
/*
* SCP protocol handler.
@@ -161,6 +166,7 @@ const struct Curl_handler Curl_handler_scp = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_SSH, /* defport */
CURLPROTO_SCP, /* protocol */
CURLPROTO_SCP, /* family */
@@ -189,6 +195,7 @@ const struct Curl_handler Curl_handler_sftp = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_SSH, /* defport */
CURLPROTO_SFTP, /* protocol */
CURLPROTO_SFTP, /* family */
@@ -221,23 +228,23 @@ static CURLcode sftp_error_to_CURLE(int err)
}
#ifndef DEBUGBUILD
-#define state(x,y) mystate(x,y)
+#define myssh_state(x,y,z) myssh_set_state(x,y,z)
#else
-#define state(x,y) mystate(x,y, __LINE__)
+#define myssh_state(x,y,z) myssh_set_state(x,y,z, __LINE__)
#endif
/*
* SSH State machine related code
*/
/* This is the ONLY way to change SSH state! */
-static void mystate(struct Curl_easy *data, sshstate nowstate
+static void myssh_set_state(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ sshstate nowstate
#ifdef DEBUGBUILD
- , int lineno
+ , int lineno
#endif
)
{
- struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
/* for debug purposes */
static const char *const names[] = {
@@ -310,7 +317,7 @@ static void mystate(struct Curl_easy *data, sshstate nowstate
lineno);
}
#endif
-
+ (void)data;
sshc->state = nowstate;
}
@@ -324,11 +331,9 @@ static void mystate(struct Curl_easy *data, sshstate nowstate
*
* Returns SSH_OK or SSH_ERROR.
*/
-static int myssh_is_known(struct Curl_easy *data)
+static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc)
{
int rc;
- struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
ssh_key pubkey;
size_t hlen;
unsigned char *hash = NULL;
@@ -340,17 +345,11 @@ static int myssh_is_known(struct Curl_easy *data)
struct curl_khkey *knownkeyp = NULL;
curl_sshkeycallback func =
data->set.ssh_keyfunc;
-
-#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
struct ssh_knownhosts_entry *knownhostsentry = NULL;
struct curl_khkey knownkey;
-#endif
-#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,8,0)
rc = ssh_get_server_publickey(sshc->ssh_session, &pubkey);
-#else
- rc = ssh_get_publickey(sshc->ssh_session, &pubkey);
-#endif
+
if(rc != SSH_OK)
return rc;
@@ -386,7 +385,6 @@ static int myssh_is_known(struct Curl_easy *data)
if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
-#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
/* Get the known_key from the known hosts file */
vstate = ssh_session_get_known_hosts_entry(sshc->ssh_session,
&knownhostsentry);
@@ -444,22 +442,6 @@ static int myssh_is_known(struct Curl_easy *data)
break;
}
-#else
- vstate = ssh_is_server_known(sshc->ssh_session);
- switch(vstate) {
- case SSH_SERVER_KNOWN_OK:
- keymatch = CURLKHMATCH_OK;
- break;
- case SSH_SERVER_FILE_NOT_FOUND:
- case SSH_SERVER_NOT_KNOWN:
- keymatch = CURLKHMATCH_MISSING;
- break;
- default:
- keymatch = CURLKHMATCH_MISMATCH;
- break;
- }
-#endif
-
if(func) { /* use callback to determine action */
rc = ssh_pki_export_pubkey_base64(pubkey, &found_base64);
if(rc != SSH_OK)
@@ -476,18 +458,14 @@ static int myssh_is_known(struct Curl_easy *data)
foundkey.keytype = CURLKHTYPE_RSA1;
break;
case SSH_KEYTYPE_ECDSA:
-#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
case SSH_KEYTYPE_ECDSA_P256:
case SSH_KEYTYPE_ECDSA_P384:
case SSH_KEYTYPE_ECDSA_P521:
-#endif
foundkey.keytype = CURLKHTYPE_ECDSA;
break;
-#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,7,0)
case SSH_KEYTYPE_ED25519:
foundkey.keytype = CURLKHTYPE_ED25519;
break;
-#endif
case SSH_KEYTYPE_DSS:
foundkey.keytype = CURLKHTYPE_DSS;
break;
@@ -504,11 +482,7 @@ static int myssh_is_known(struct Curl_easy *data)
switch(rc) {
case CURLKHSTAT_FINE_ADD_TO_FILE:
-#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,8,0)
rc = ssh_session_update_known_hosts(sshc->ssh_session);
-#else
- rc = ssh_write_knownhost(sshc->ssh_session);
-#endif
if(rc != SSH_OK) {
goto cleanup;
}
@@ -539,22 +513,20 @@ cleanup:
if(hash)
ssh_clean_pubkey_hash(&hash);
ssh_key_free(pubkey);
-#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
if(knownhostsentry) {
ssh_knownhosts_entry_free(knownhostsentry);
}
-#endif
return rc;
}
-#define MOVE_TO_ERROR_STATE(_r) do { \
- state(data, SSH_SESSION_DISCONNECT); \
- sshc->actualcode = _r; \
- rc = SSH_ERROR; \
+#define MOVE_TO_ERROR_STATE(_r) do { \
+ myssh_state(data, sshc, SSH_SESSION_DISCONNECT); \
+ sshc->actualcode = _r; \
+ rc = SSH_ERROR; \
} while(0)
#define MOVE_TO_SFTP_CLOSE_STATE() do { \
- state(data, SSH_SFTP_CLOSE); \
+ myssh_state(data, sshc, SSH_SFTP_CLOSE); \
sshc->actualcode = \
sftp_error_to_CURLE(sftp_get_error(sshc->sftp_session)); \
rc = SSH_ERROR; \
@@ -563,7 +535,7 @@ cleanup:
#define MOVE_TO_PASSWD_AUTH do { \
if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) { \
rc = SSH_OK; \
- state(data, SSH_AUTH_PASS_INIT); \
+ myssh_state(data, sshc, SSH_AUTH_PASS_INIT); \
} \
else { \
MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); \
@@ -573,7 +545,7 @@ cleanup:
#define MOVE_TO_KEY_AUTH do { \
if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) { \
rc = SSH_OK; \
- state(data, SSH_AUTH_KEY_INIT); \
+ myssh_state(data, sshc, SSH_AUTH_KEY_INIT); \
} \
else { \
MOVE_TO_PASSWD_AUTH; \
@@ -583,7 +555,7 @@ cleanup:
#define MOVE_TO_GSSAPI_AUTH do { \
if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) { \
rc = SSH_OK; \
- state(data, SSH_AUTH_GSSAPI); \
+ myssh_state(data, sshc, SSH_AUTH_GSSAPI); \
} \
else { \
MOVE_TO_KEY_AUTH; \
@@ -591,10 +563,10 @@ cleanup:
} while(0)
static
-int myssh_auth_interactive(struct connectdata *conn)
+int myssh_auth_interactive(struct connectdata *conn,
+ struct ssh_conn *sshc)
{
int rc;
- struct ssh_conn *sshc = &conn->proto.sshc;
int nprompts;
restart:
@@ -655,178 +627,493 @@ restart:
return rc;
}
-/*
- * ssh_statemach_act() runs the SSH state machine as far as it can without
- * blocking and without reaching the end. The data the pointer 'block' points
- * to will be set to TRUE if the libssh function returns SSH_AGAIN
- * meaning it wants to be called again when the socket is ready
- */
-static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
+static void myssh_state_init(struct Curl_easy *data,
+ struct ssh_conn *sshc)
{
- CURLcode result = CURLE_OK;
- struct connectdata *conn = data->conn;
- struct SSHPROTO *protop = data->req.p.ssh;
- struct ssh_conn *sshc = &conn->proto.sshc;
- curl_socket_t sock = conn->sock[FIRSTSOCKET];
- int rc = SSH_NO_ERROR, err;
- int seekerr = CURL_SEEKFUNC_OK;
- const char *err_msg;
- *block = 0; /* we are not blocking by default */
-
- do {
-
- switch(sshc->state) {
- case SSH_INIT:
- sshc->secondCreateDirs = 0;
- sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = CURLE_OK;
+ sshc->secondCreateDirs = 0;
+ sshc->nextstate = SSH_NO_STATE;
+ sshc->actualcode = CURLE_OK;
#if 0
- ssh_set_log_level(SSH_LOG_PROTOCOL);
+ ssh_set_log_level(SSH_LOG_PROTOCOL);
#endif
- /* Set libssh to non-blocking, since everything internally is
- non-blocking */
- ssh_set_blocking(sshc->ssh_session, 0);
+ /* Set libssh to non-blocking, since everything internally is
+ non-blocking */
+ ssh_set_blocking(sshc->ssh_session, 0);
- state(data, SSH_S_STARTUP);
- FALLTHROUGH();
+ myssh_state(data, sshc, SSH_S_STARTUP);
+}
- case SSH_S_STARTUP:
- rc = ssh_connect(sshc->ssh_session);
+static int myssh_state_startup(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ struct connectdata *conn = data->conn;
+ int rc = ssh_connect(sshc->ssh_session);
- myssh_block2waitfor(conn, (rc == SSH_AGAIN));
- if(rc == SSH_AGAIN) {
- DEBUGF(infof(data, "ssh_connect -> EAGAIN"));
- break;
- }
+ myssh_block2waitfor(conn, sshc, (rc == SSH_AGAIN));
+ if(rc == SSH_AGAIN) {
+ DEBUGF(infof(data, "ssh_connect -> EAGAIN"));
+ }
+ else if(rc != SSH_OK) {
+ failf(data, "Failure establishing ssh session");
+ MOVE_TO_ERROR_STATE(CURLE_FAILED_INIT);
+ }
+ else
+ myssh_state(data, sshc, SSH_HOSTKEY);
- if(rc != SSH_OK) {
- failf(data, "Failure establishing ssh session");
- MOVE_TO_ERROR_STATE(CURLE_FAILED_INIT);
- break;
- }
+ return rc;
+}
- state(data, SSH_HOSTKEY);
+static int myssh_state_authlist(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ int rc;
+ sshc->authed = FALSE;
- FALLTHROUGH();
- case SSH_HOSTKEY:
+ rc = ssh_userauth_none(sshc->ssh_session, NULL);
+ if(rc == SSH_AUTH_AGAIN)
+ return SSH_AGAIN;
+
+ if(rc == SSH_AUTH_SUCCESS) {
+ sshc->authed = TRUE;
+ infof(data, "Authenticated with none");
+ myssh_state(data, sshc, SSH_AUTH_DONE);
+ return rc;
+ }
+ else if(rc == SSH_AUTH_ERROR) {
+ MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
+ return rc;
+ }
+
+ sshc->auth_methods =
+ (unsigned int)ssh_userauth_list(sshc->ssh_session, NULL);
+ if(sshc->auth_methods)
+ infof(data, "SSH authentication methods available: %s%s%s%s",
+ sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY ?
+ "public key, ": "",
+ sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC ?
+ "GSSAPI, " : "",
+ sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE ?
+ "keyboard-interactive, " : "",
+ sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD ?
+ "password": "");
+ if(sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) {
+ myssh_state(data, sshc, SSH_AUTH_PKEY_INIT);
+ infof(data, "Authentication using SSH public key file");
+ }
+ else if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) {
+ myssh_state(data, sshc, SSH_AUTH_GSSAPI);
+ }
+ else if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) {
+ myssh_state(data, sshc, SSH_AUTH_KEY_INIT);
+ }
+ else if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) {
+ myssh_state(data, sshc, SSH_AUTH_PASS_INIT);
+ }
+ else { /* unsupported authentication method */
+ MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
+ }
+ return rc;
+}
+
+static int myssh_state_auth_pkey_init(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ int rc;
+ if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY)) {
+ MOVE_TO_GSSAPI_AUTH;
+ return 0;
+ }
+
+ /* Two choices, (1) private key was given on CMD,
+ * (2) use the "default" keys. */
+ if(data->set.str[STRING_SSH_PRIVATE_KEY]) {
+ if(sshc->pubkey && !data->set.ssl.key_passwd) {
+ rc = ssh_userauth_try_publickey(sshc->ssh_session, NULL,
+ sshc->pubkey);
+ if(rc == SSH_AUTH_AGAIN)
+ return SSH_AGAIN;
- rc = myssh_is_known(data);
if(rc != SSH_OK) {
- MOVE_TO_ERROR_STATE(CURLE_PEER_FAILED_VERIFICATION);
- break;
+ MOVE_TO_GSSAPI_AUTH;
+ return rc;
}
+ }
- state(data, SSH_AUTHLIST);
- FALLTHROUGH();
- case SSH_AUTHLIST:{
- sshc->authed = FALSE;
+ rc = ssh_pki_import_privkey_file(data->
+ set.str[STRING_SSH_PRIVATE_KEY],
+ data->set.ssl.key_passwd, NULL,
+ NULL, &sshc->privkey);
+ if(rc != SSH_OK) {
+ failf(data, "Could not load private key file %s",
+ data->set.str[STRING_SSH_PRIVATE_KEY]);
+ MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
+ return rc;
+ }
- rc = ssh_userauth_none(sshc->ssh_session, NULL);
- if(rc == SSH_AUTH_AGAIN) {
- rc = SSH_AGAIN;
- break;
- }
+ myssh_state(data, sshc, SSH_AUTH_PKEY);
+ }
+ else {
+ rc = ssh_userauth_publickey_auto(sshc->ssh_session, NULL,
+ data->set.ssl.key_passwd);
+ if(rc == SSH_AUTH_AGAIN)
+ return SSH_AGAIN;
+
+ if(rc == SSH_AUTH_SUCCESS) {
+ rc = SSH_OK;
+ sshc->authed = TRUE;
+ infof(data, "Completed public key authentication");
+ myssh_state(data, sshc, SSH_AUTH_DONE);
+ return rc;
+ }
- if(rc == SSH_AUTH_SUCCESS) {
- sshc->authed = TRUE;
- infof(data, "Authenticated with none");
- state(data, SSH_AUTH_DONE);
- break;
- }
- else if(rc == SSH_AUTH_ERROR) {
- MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
- break;
- }
+ MOVE_TO_GSSAPI_AUTH;
+ }
+ return rc;
+}
- sshc->auth_methods =
- (unsigned int)ssh_userauth_list(sshc->ssh_session, NULL);
- if(sshc->auth_methods)
- infof(data, "SSH authentication methods available: %s%s%s%s",
- sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY ?
- "public key, ": "",
- sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC ?
- "GSSAPI, " : "",
- sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE ?
- "keyboard-interactive, " : "",
- sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD ?
- "password": "");
- if(sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) {
- state(data, SSH_AUTH_PKEY_INIT);
- infof(data, "Authentication using SSH public key file");
- }
- else if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) {
- state(data, SSH_AUTH_GSSAPI);
- }
- else if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) {
- state(data, SSH_AUTH_KEY_INIT);
- }
- else if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) {
- state(data, SSH_AUTH_PASS_INIT);
- }
- else { /* unsupported authentication method */
- MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
- break;
+static int myssh_state_upload_init(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp)
+{
+ int flags;
+ int rc = 0;
+
+ if(data->state.resume_from) {
+ sftp_attributes attrs;
+
+ if(data->state.resume_from < 0) {
+ attrs = sftp_stat(sshc->sftp_session, sshp->path);
+ if(attrs) {
+ curl_off_t size = attrs->size;
+ if(size < 0) {
+ failf(data, "Bad file size (%" FMT_OFF_T ")", size);
+ MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME);
+ return rc;
}
+ data->state.resume_from = attrs->size;
- break;
+ sftp_attributes_free(attrs);
}
- case SSH_AUTH_PKEY_INIT:
- if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY)) {
- MOVE_TO_GSSAPI_AUTH;
- break;
+ else {
+ data->state.resume_from = 0;
}
+ }
+ }
- /* Two choices, (1) private key was given on CMD,
- * (2) use the "default" keys. */
- if(data->set.str[STRING_SSH_PRIVATE_KEY]) {
- if(sshc->pubkey && !data->set.ssl.key_passwd) {
- rc = ssh_userauth_try_publickey(sshc->ssh_session, NULL,
- sshc->pubkey);
- if(rc == SSH_AUTH_AGAIN) {
- rc = SSH_AGAIN;
- break;
- }
+ if(data->set.remote_append)
+ /* Try to open for append, but create if nonexisting */
+ flags = O_WRONLY|O_CREAT|O_APPEND;
+ else if(data->state.resume_from > 0)
+ /* If we have restart position then open for append */
+ flags = O_WRONLY|O_APPEND;
+ else
+ /* Clear file before writing (normal behavior) */
+ flags = O_WRONLY|O_CREAT|O_TRUNC;
+
+ if(sshc->sftp_file)
+ sftp_close(sshc->sftp_file);
+ sshc->sftp_file =
+ sftp_open(sshc->sftp_session, sshp->path,
+ flags, (mode_t)data->set.new_file_perms);
+ if(!sshc->sftp_file) {
+ int err = sftp_get_error(sshc->sftp_session);
+
+ if(((err == SSH_FX_NO_SUCH_FILE || err == SSH_FX_FAILURE ||
+ err == SSH_FX_NO_SUCH_PATH)) &&
+ (data->set.ftp_create_missing_dirs &&
+ (strlen(sshp->path) > 1))) {
+ /* try to create the path remotely */
+ rc = 0;
+ sshc->secondCreateDirs = 1;
+ myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS_INIT);
+ return rc;
+ }
+ else {
+ MOVE_TO_SFTP_CLOSE_STATE();
+ return rc;
+ }
+ }
- if(rc != SSH_OK) {
- MOVE_TO_GSSAPI_AUTH;
- break;
- }
- }
+ /* If we have a restart point then we need to seek to the correct
+ position. */
+ if(data->state.resume_from > 0) {
+ int seekerr = CURL_SEEKFUNC_OK;
+ /* Let's read off the proper amount of bytes from the input. */
+ if(data->set.seek_func) {
+ Curl_set_in_callback(data, TRUE);
+ seekerr = data->set.seek_func(data->set.seek_client,
+ data->state.resume_from, SEEK_SET);
+ Curl_set_in_callback(data, FALSE);
+ }
- rc = ssh_pki_import_privkey_file(data->
- set.str[STRING_SSH_PRIVATE_KEY],
- data->set.ssl.key_passwd, NULL,
- NULL, &sshc->privkey);
- if(rc != SSH_OK) {
- failf(data, "Could not load private key file %s",
- data->set.str[STRING_SSH_PRIVATE_KEY]);
- MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
- break;
+ if(seekerr != CURL_SEEKFUNC_OK) {
+ curl_off_t passed = 0;
+
+ if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
+ failf(data, "Could not seek stream");
+ MOVE_TO_ERROR_STATE(CURLE_FTP_COULDNT_USE_REST);
+ return rc;
+ }
+ /* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */
+ do {
+ char scratch[4*1024];
+ size_t readthisamountnow =
+ (data->state.resume_from - passed >
+ (curl_off_t)sizeof(scratch)) ?
+ sizeof(scratch) : curlx_sotouz(data->state.resume_from - passed);
+
+ size_t actuallyread =
+ data->state.fread_func(scratch, 1,
+ readthisamountnow, data->state.in);
+
+ passed += actuallyread;
+ if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
+ /* this checks for greater-than only to make sure that the
+ CURL_READFUNC_ABORT return code still aborts */
+ failf(data, "Failed to read data");
+ MOVE_TO_ERROR_STATE(CURLE_FTP_COULDNT_USE_REST);
+ return rc;
}
+ } while(passed < data->state.resume_from);
+ }
- state(data, SSH_AUTH_PKEY);
- break;
+ /* now, decrease the size of the read */
+ if(data->state.infilesize > 0) {
+ data->state.infilesize -= data->state.resume_from;
+ data->req.size = data->state.infilesize;
+ Curl_pgrsSetUploadSize(data, data->state.infilesize);
+ }
+ rc = sftp_seek64(sshc->sftp_file, data->state.resume_from);
+ if(rc) {
+ MOVE_TO_SFTP_CLOSE_STATE();
+ return rc;
+ }
+ }
+ if(data->state.infilesize > 0) {
+ data->req.size = data->state.infilesize;
+ Curl_pgrsSetUploadSize(data, data->state.infilesize);
+ }
+ /* upload data */
+ Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
+
+ /* not set by Curl_xfer_setup to preserve keepon bits */
+ data->conn->sockfd = data->conn->writesockfd;
+
+ /* store this original bitmask setup to use later on if we cannot
+ figure out a "real" bitmask */
+ sshc->orig_waitfor = data->req.keepon;
+
+ /* we want to use the _sending_ function even when the socket turns
+ out readable as the underlying libssh sftp send function will deal
+ with both accordingly */
+ data->state.select_bits = CURL_CSELECT_OUT;
+
+ /* since we do not really wait for anything at this point, we want the
+ state machine to move on as soon as possible so we set a very short
+ timeout here */
+ Curl_expire(data, 0, EXPIRE_RUN_NOW);
+#if LIBSSH_VERSION_INT > SSH_VERSION_INT(0, 11, 0)
+ sshc->sftp_send_state = 0;
+#endif
+ myssh_state(data, sshc, SSH_STOP);
+ return rc;
+}
+
+static int myssh_state_sftp_download_stat(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ curl_off_t size;
+ int rc = 0;
+ sftp_attributes attrs = sftp_fstat(sshc->sftp_file);
+ if(!attrs ||
+ !(attrs->flags & SSH_FILEXFER_ATTR_SIZE) ||
+ (attrs->size == 0)) {
+ /*
+ * sftp_fstat did not return an error, so maybe the server
+ * just does not support stat()
+ * OR the server does not return a file size with a stat()
+ * OR file size is 0
+ */
+ data->req.size = -1;
+ data->req.maxdownload = -1;
+ Curl_pgrsSetDownloadSize(data, -1);
+ size = 0;
+ if(attrs)
+ sftp_attributes_free(attrs);
+ }
+ else {
+ size = attrs->size;
+
+ sftp_attributes_free(attrs);
+
+ if(size < 0) {
+ failf(data, "Bad file size (%" FMT_OFF_T ")", size);
+ MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME);
+ return rc;
+ }
+ if(data->state.use_range) {
+ curl_off_t from, to;
+ const char *p = data->state.range;
+ int from_t, to_t;
+
+ from_t = curlx_str_number(&p, &from, CURL_OFF_T_MAX);
+ if(from_t == STRE_OVERFLOW) {
+ MOVE_TO_ERROR_STATE(CURLE_RANGE_ERROR);
+ return rc;
+ }
+ curlx_str_passblanks(&p);
+ (void)curlx_str_single(&p, '-');
+
+ to_t = curlx_str_numblanks(&p, &to);
+ if(to_t == STRE_OVERFLOW)
+ return CURLE_RANGE_ERROR;
+
+ if((to_t == STRE_NO_NUM) || (to >= size)) {
+ to = size - 1;
+ to_t = STRE_OK;
+ }
+
+ if(from_t == STRE_NO_NUM) {
+ /* from is relative to end of file */
+ from = size - to;
+ to = size - 1;
+ from_t = STRE_OK;
+ }
+ if(from > size) {
+ failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%"
+ FMT_OFF_T ")", from, size);
+ MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME);
+ return rc;
+ }
+ if(from > to) {
+ from = to;
+ size = 0;
}
else {
- rc = ssh_userauth_publickey_auto(sshc->ssh_session, NULL,
- data->set.ssl.key_passwd);
- if(rc == SSH_AUTH_AGAIN) {
- rc = SSH_AGAIN;
- break;
- }
- if(rc == SSH_AUTH_SUCCESS) {
- rc = SSH_OK;
- sshc->authed = TRUE;
- infof(data, "Completed public key authentication");
- state(data, SSH_AUTH_DONE);
- break;
+ if((to - from) == CURL_OFF_T_MAX) {
+ MOVE_TO_ERROR_STATE(CURLE_RANGE_ERROR);
+ return rc;
}
+ size = to - from + 1;
+ }
- MOVE_TO_GSSAPI_AUTH;
+ rc = sftp_seek64(sshc->sftp_file, from);
+ if(rc) {
+ MOVE_TO_SFTP_CLOSE_STATE();
+ return rc;
+ }
+ }
+ data->req.size = size;
+ data->req.maxdownload = size;
+ Curl_pgrsSetDownloadSize(data, size);
+ }
+
+ /* We can resume if we can seek to the resume position */
+ if(data->state.resume_from) {
+ if(data->state.resume_from < 0) {
+ /* We are supposed to download the last abs(from) bytes */
+ if((curl_off_t)size < -data->state.resume_from) {
+ failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%"
+ FMT_OFF_T ")", data->state.resume_from, size);
+ MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME);
+ return rc;
+ }
+ /* download from where? */
+ data->state.resume_from += size;
+ }
+ else {
+ if((curl_off_t)size < data->state.resume_from) {
+ failf(data, "Offset (%" FMT_OFF_T
+ ") was beyond file size (%" FMT_OFF_T ")",
+ data->state.resume_from, size);
+ MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME);
+ return rc;
+ }
+ }
+ /* Now store the number of bytes we are expected to download */
+ data->req.size = size - data->state.resume_from;
+ data->req.maxdownload = size - data->state.resume_from;
+ Curl_pgrsSetDownloadSize(data,
+ size - data->state.resume_from);
+
+ rc = sftp_seek64(sshc->sftp_file, data->state.resume_from);
+ if(rc) {
+ MOVE_TO_SFTP_CLOSE_STATE();
+ return rc;
+ }
+ }
+
+ /* Setup the actual download */
+ if(data->req.size == 0) {
+ /* no data to transfer */
+ Curl_xfer_setup_nop(data);
+ infof(data, "File already completely downloaded");
+ myssh_state(data, sshc, SSH_STOP);
+ return rc;
+ }
+ Curl_xfer_setup1(data, CURL_XFER_RECV, data->req.size, FALSE);
+
+ /* not set by Curl_xfer_setup to preserve keepon bits */
+ data->conn->writesockfd = data->conn->sockfd;
+
+ /* we want to use the _receiving_ function even when the socket turns
+ out writableable as the underlying libssh recv function will deal
+ with both accordingly */
+ data->state.select_bits = CURL_CSELECT_IN;
+
+ sshc->sftp_recv_state = 0;
+ myssh_state(data, sshc, SSH_STOP);
+
+ return rc;
+}
+
+/*
+ * ssh_statemach_act() runs the SSH state machine as far as it can without
+ * blocking and without reaching the end. The data the pointer 'block' points
+ * to will be set to TRUE if the libssh function returns SSH_AGAIN
+ * meaning it wants to be called again when the socket is ready
+ */
+static CURLcode myssh_statemach_act(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp,
+ bool *block)
+{
+ CURLcode result = CURLE_OK;
+ struct connectdata *conn = data->conn;
+ curl_socket_t sock = conn->sock[FIRSTSOCKET];
+ int rc = SSH_NO_ERROR, err;
+ const char *err_msg;
+ *block = 0; /* we are not blocking by default */
+
+ do {
+
+ switch(sshc->state) {
+ case SSH_INIT:
+ myssh_state_init(data, sshc);
+ FALLTHROUGH();
+
+ case SSH_S_STARTUP:
+ rc = myssh_state_startup(data, sshc);
+ if(rc)
+ break;
+ FALLTHROUGH();
+ case SSH_HOSTKEY:
+ rc = myssh_is_known(data, sshc);
+ if(rc != SSH_OK) {
+ MOVE_TO_ERROR_STATE(CURLE_PEER_FAILED_VERIFICATION);
+ break;
}
+
+ myssh_state(data, sshc, SSH_AUTHLIST);
+ FALLTHROUGH();
+ case SSH_AUTHLIST:
+ rc = myssh_state_authlist(data, sshc);
+ break;
+ case SSH_AUTH_PKEY_INIT:
+ rc = myssh_state_auth_pkey_init(data, sshc);
break;
case SSH_AUTH_PKEY:
rc = ssh_userauth_publickey(sshc->ssh_session, NULL, sshc->privkey);
@@ -838,7 +1125,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
if(rc == SSH_AUTH_SUCCESS) {
sshc->authed = TRUE;
infof(data, "Completed public key authentication");
- state(data, SSH_AUTH_DONE);
+ myssh_state(data, sshc, SSH_AUTH_DONE);
break;
}
else {
@@ -863,7 +1150,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
rc = SSH_OK;
sshc->authed = TRUE;
infof(data, "Completed gssapi authentication");
- state(data, SSH_AUTH_DONE);
+ myssh_state(data, sshc, SSH_AUTH_DONE);
break;
}
@@ -872,7 +1159,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
case SSH_AUTH_KEY_INIT:
if(data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD) {
- state(data, SSH_AUTH_KEY);
+ myssh_state(data, sshc, SSH_AUTH_KEY);
}
else {
MOVE_TO_PASSWD_AUTH;
@@ -881,14 +1168,14 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
case SSH_AUTH_KEY:
/* keyboard-interactive authentication */
- rc = myssh_auth_interactive(conn);
+ rc = myssh_auth_interactive(conn, sshc);
if(rc == SSH_AGAIN) {
break;
}
if(rc == SSH_OK) {
sshc->authed = TRUE;
infof(data, "completed keyboard interactive authentication");
- state(data, SSH_AUTH_DONE);
+ myssh_state(data, sshc, SSH_AUTH_DONE);
}
else {
MOVE_TO_PASSWD_AUTH;
@@ -900,7 +1187,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
break;
}
- state(data, SSH_AUTH_PASS);
+ myssh_state(data, sshc, SSH_AUTH_PASS);
FALLTHROUGH();
case SSH_AUTH_PASS:
@@ -913,7 +1200,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
if(rc == SSH_AUTH_SUCCESS) {
sshc->authed = TRUE;
infof(data, "Completed password authentication");
- state(data, SSH_AUTH_DONE);
+ myssh_state(data, sshc, SSH_AUTH_DONE);
}
else {
MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
@@ -938,11 +1225,11 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
conn->writesockfd = CURL_SOCKET_BAD;
if(conn->handler->protocol == CURLPROTO_SFTP) {
- state(data, SSH_SFTP_INIT);
+ myssh_state(data, sshc, SSH_SFTP_INIT);
break;
}
infof(data, "SSH CONNECT phase done");
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
case SSH_SFTP_INIT:
@@ -963,7 +1250,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
MOVE_TO_ERROR_STATE(sftp_error_to_CURLE(SSH_FX_FAILURE));
break;
}
- state(data, SSH_SFTP_REALPATH);
+ myssh_state(data, sshc, SSH_SFTP_REALPATH);
FALLTHROUGH();
case SSH_SFTP_REALPATH:
/*
@@ -974,31 +1261,34 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT);
break;
}
- data->state.most_recent_ftp_entrypath = sshc->homedir;
+ free(data->state.most_recent_ftp_entrypath);
+ data->state.most_recent_ftp_entrypath = strdup(sshc->homedir);
+ if(!data->state.most_recent_ftp_entrypath)
+ return CURLE_OUT_OF_MEMORY;
/* This is the last step in the SFTP connect phase. Do note that while
we get the homedir here, we get the "workingpath" in the DO action
since the homedir will remain the same between request but the
working path will not. */
DEBUGF(infof(data, "SSH CONNECT phase done"));
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
case SSH_SFTP_QUOTE_INIT:
- result = Curl_getworkingpath(data, sshc->homedir, &protop->path);
+ result = Curl_getworkingpath(data, sshc->homedir, &sshp->path);
if(result) {
sshc->actualcode = result;
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
}
if(data->set.quote) {
infof(data, "Sending quote commands");
sshc->quote_item = data->set.quote;
- state(data, SSH_SFTP_QUOTE);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE);
}
else {
- state(data, SSH_SFTP_GETINFO);
+ myssh_state(data, sshc, SSH_SFTP_GETINFO);
}
break;
@@ -1006,16 +1296,16 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
if(data->set.postquote) {
infof(data, "Sending quote commands");
sshc->quote_item = data->set.postquote;
- state(data, SSH_SFTP_QUOTE);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE);
}
else {
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
}
break;
case SSH_SFTP_QUOTE:
/* Send any quote commands */
- sftp_quote(data);
+ sftp_quote(data, sshc, sshp);
break;
case SSH_SFTP_NEXT_QUOTE:
@@ -1025,21 +1315,21 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
sshc->quote_item = sshc->quote_item->next;
if(sshc->quote_item) {
- state(data, SSH_SFTP_QUOTE);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE);
}
else {
if(sshc->nextstate != SSH_NO_STATE) {
- state(data, sshc->nextstate);
+ myssh_state(data, sshc, sshc->nextstate);
sshc->nextstate = SSH_NO_STATE;
}
else {
- state(data, SSH_SFTP_GETINFO);
+ myssh_state(data, sshc, SSH_SFTP_GETINFO);
}
}
break;
case SSH_SFTP_QUOTE_STAT:
- sftp_quote_stat(data);
+ sftp_quote_stat(data, sshc);
break;
case SSH_SFTP_QUOTE_SETSTAT:
@@ -1050,7 +1340,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
Curl_safefree(sshc->quote_path2);
failf(data, "Attempt to set SFTP stats failed: %s",
ssh_get_error(sshc->ssh_session));
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
/* sshc->actualcode = sftp_error_to_CURLE(err);
@@ -1058,7 +1348,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
* the error the libssh2 backend is returning */
break;
}
- state(data, SSH_SFTP_NEXT_QUOTE);
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
break;
case SSH_SFTP_QUOTE_SYMLINK:
@@ -1069,12 +1359,12 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
Curl_safefree(sshc->quote_path2);
failf(data, "symlink command failed: %s",
ssh_get_error(sshc->ssh_session));
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
- state(data, SSH_SFTP_NEXT_QUOTE);
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
break;
case SSH_SFTP_QUOTE_MKDIR:
@@ -1084,12 +1374,12 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
Curl_safefree(sshc->quote_path1);
failf(data, "mkdir command failed: %s",
ssh_get_error(sshc->ssh_session));
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
- state(data, SSH_SFTP_NEXT_QUOTE);
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
break;
case SSH_SFTP_QUOTE_RENAME:
@@ -1100,12 +1390,12 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
Curl_safefree(sshc->quote_path2);
failf(data, "rename command failed: %s",
ssh_get_error(sshc->ssh_session));
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
- state(data, SSH_SFTP_NEXT_QUOTE);
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
break;
case SSH_SFTP_QUOTE_RMDIR:
@@ -1114,12 +1404,12 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
Curl_safefree(sshc->quote_path1);
failf(data, "rmdir command failed: %s",
ssh_get_error(sshc->ssh_session));
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
- state(data, SSH_SFTP_NEXT_QUOTE);
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
break;
case SSH_SFTP_QUOTE_UNLINK:
@@ -1128,12 +1418,12 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
Curl_safefree(sshc->quote_path1);
failf(data, "rm command failed: %s",
ssh_get_error(sshc->ssh_session));
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
}
- state(data, SSH_SFTP_NEXT_QUOTE);
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
break;
case SSH_SFTP_QUOTE_STATVFS:
@@ -1145,7 +1435,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
Curl_safefree(sshc->quote_path1);
failf(data, "statvfs command failed: %s",
ssh_get_error(sshc->ssh_session));
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
break;
@@ -1178,7 +1468,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
if(!tmp) {
result = CURLE_OUT_OF_MEMORY;
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
break;
}
@@ -1186,21 +1476,21 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp));
free(tmp);
if(result) {
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = result;
}
}
- state(data, SSH_SFTP_NEXT_QUOTE);
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
break;
}
case SSH_SFTP_GETINFO:
if(data->set.get_filetime) {
- state(data, SSH_SFTP_FILETIME);
+ myssh_state(data, sshc, SSH_SFTP_FILETIME);
}
else {
- state(data, SSH_SFTP_TRANS_INIT);
+ myssh_state(data, sshc, SSH_SFTP_TRANS_INIT);
}
break;
@@ -1208,178 +1498,38 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
{
sftp_attributes attrs;
- attrs = sftp_stat(sshc->sftp_session, protop->path);
+ attrs = sftp_stat(sshc->sftp_session, sshp->path);
if(attrs) {
data->info.filetime = attrs->mtime;
sftp_attributes_free(attrs);
}
- state(data, SSH_SFTP_TRANS_INIT);
+ myssh_state(data, sshc, SSH_SFTP_TRANS_INIT);
break;
}
case SSH_SFTP_TRANS_INIT:
if(data->state.upload)
- state(data, SSH_SFTP_UPLOAD_INIT);
+ myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT);
else {
- if(protop->path[strlen(protop->path)-1] == '/')
- state(data, SSH_SFTP_READDIR_INIT);
+ if(sshp->path[strlen(sshp->path)-1] == '/')
+ myssh_state(data, sshc, SSH_SFTP_READDIR_INIT);
else
- state(data, SSH_SFTP_DOWNLOAD_INIT);
+ myssh_state(data, sshc, SSH_SFTP_DOWNLOAD_INIT);
}
break;
case SSH_SFTP_UPLOAD_INIT:
- {
- int flags;
-
- if(data->state.resume_from) {
- sftp_attributes attrs;
-
- if(data->state.resume_from < 0) {
- attrs = sftp_stat(sshc->sftp_session, protop->path);
- if(attrs) {
- curl_off_t size = attrs->size;
- if(size < 0) {
- failf(data, "Bad file size (%" FMT_OFF_T ")", size);
- MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME);
- break;
- }
- data->state.resume_from = attrs->size;
-
- sftp_attributes_free(attrs);
- }
- else {
- data->state.resume_from = 0;
- }
- }
- }
-
- if(data->set.remote_append)
- /* Try to open for append, but create if nonexisting */
- flags = O_WRONLY|O_CREAT|O_APPEND;
- else if(data->state.resume_from > 0)
- /* If we have restart position then open for append */
- flags = O_WRONLY|O_APPEND;
- else
- /* Clear file before writing (normal behavior) */
- flags = O_WRONLY|O_CREAT|O_TRUNC;
-
- if(sshc->sftp_file)
- sftp_close(sshc->sftp_file);
- sshc->sftp_file =
- sftp_open(sshc->sftp_session, protop->path,
- flags, (mode_t)data->set.new_file_perms);
- if(!sshc->sftp_file) {
- err = sftp_get_error(sshc->sftp_session);
-
- if(((err == SSH_FX_NO_SUCH_FILE || err == SSH_FX_FAILURE ||
- err == SSH_FX_NO_SUCH_PATH)) &&
- (data->set.ftp_create_missing_dirs &&
- (strlen(protop->path) > 1))) {
- /* try to create the path remotely */
- rc = 0;
- sshc->secondCreateDirs = 1;
- state(data, SSH_SFTP_CREATE_DIRS_INIT);
- break;
- }
- else {
- MOVE_TO_SFTP_CLOSE_STATE();
- break;
- }
- }
-
- /* If we have a restart point then we need to seek to the correct
- position. */
- if(data->state.resume_from > 0) {
- /* Let's read off the proper amount of bytes from the input. */
- if(data->set.seek_func) {
- Curl_set_in_callback(data, TRUE);
- seekerr = data->set.seek_func(data->set.seek_client,
- data->state.resume_from, SEEK_SET);
- Curl_set_in_callback(data, FALSE);
- }
-
- if(seekerr != CURL_SEEKFUNC_OK) {
- curl_off_t passed = 0;
-
- if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
- failf(data, "Could not seek stream");
- return CURLE_FTP_COULDNT_USE_REST;
- }
- /* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */
- do {
- char scratch[4*1024];
- size_t readthisamountnow =
- (data->state.resume_from - passed >
- (curl_off_t)sizeof(scratch)) ?
- sizeof(scratch) : curlx_sotouz(data->state.resume_from - passed);
-
- size_t actuallyread =
- data->state.fread_func(scratch, 1,
- readthisamountnow, data->state.in);
-
- passed += actuallyread;
- if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
- /* this checks for greater-than only to make sure that the
- CURL_READFUNC_ABORT return code still aborts */
- failf(data, "Failed to read data");
- MOVE_TO_ERROR_STATE(CURLE_FTP_COULDNT_USE_REST);
- break;
- }
- } while(passed < data->state.resume_from);
- if(rc)
- break;
- }
-
- /* now, decrease the size of the read */
- if(data->state.infilesize > 0) {
- data->state.infilesize -= data->state.resume_from;
- data->req.size = data->state.infilesize;
- Curl_pgrsSetUploadSize(data, data->state.infilesize);
- }
-
- rc = sftp_seek64(sshc->sftp_file, data->state.resume_from);
- if(rc) {
- MOVE_TO_SFTP_CLOSE_STATE();
- break;
- }
- }
- if(data->state.infilesize > 0) {
- data->req.size = data->state.infilesize;
- Curl_pgrsSetUploadSize(data, data->state.infilesize);
- }
- /* upload data */
- Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
-
- /* not set by Curl_xfer_setup to preserve keepon bits */
- conn->sockfd = conn->writesockfd;
-
- /* store this original bitmask setup to use later on if we cannot
- figure out a "real" bitmask */
- sshc->orig_waitfor = data->req.keepon;
-
- /* we want to use the _sending_ function even when the socket turns
- out readable as the underlying libssh sftp send function will deal
- with both accordingly */
- data->state.select_bits = CURL_CSELECT_OUT;
-
- /* since we do not really wait for anything at this point, we want the
- state machine to move on as soon as possible so we set a very short
- timeout here */
- Curl_expire(data, 0, EXPIRE_RUN_NOW);
-
- state(data, SSH_STOP);
+ rc = myssh_state_upload_init(data, sshc, sshp);
break;
- }
case SSH_SFTP_CREATE_DIRS_INIT:
- if(strlen(protop->path) > 1) {
- sshc->slash_pos = protop->path + 1; /* ignore the leading '/' */
- state(data, SSH_SFTP_CREATE_DIRS);
+ if(strlen(sshp->path) > 1) {
+ sshc->slash_pos = sshp->path + 1; /* ignore the leading '/' */
+ myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS);
}
else {
- state(data, SSH_SFTP_UPLOAD_INIT);
+ myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT);
}
break;
@@ -1388,16 +1538,16 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
if(sshc->slash_pos) {
*sshc->slash_pos = 0;
- infof(data, "Creating directory '%s'", protop->path);
- state(data, SSH_SFTP_CREATE_DIRS_MKDIR);
+ infof(data, "Creating directory '%s'", sshp->path);
+ myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR);
break;
}
- state(data, SSH_SFTP_UPLOAD_INIT);
+ myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT);
break;
case SSH_SFTP_CREATE_DIRS_MKDIR:
/* 'mode' - parameter is preliminary - default to 0644 */
- rc = sftp_mkdir(sshc->sftp_session, protop->path,
+ rc = sftp_mkdir(sshc->sftp_session, sshp->path,
(mode_t)data->set.new_directory_perms);
*sshc->slash_pos = '/';
++sshc->slash_pos;
@@ -1416,13 +1566,13 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
}
rc = 0; /* clear rc and continue */
}
- state(data, SSH_SFTP_CREATE_DIRS);
+ myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS);
break;
case SSH_SFTP_READDIR_INIT:
Curl_pgrsSetDownloadSize(data, -1);
if(data->req.no_body) {
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
}
@@ -1431,18 +1581,18 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
* listing
*/
sshc->sftp_dir = sftp_opendir(sshc->sftp_session,
- protop->path);
+ sshp->path);
if(!sshc->sftp_dir) {
failf(data, "Could not open directory for reading: %s",
ssh_get_error(sshc->ssh_session));
MOVE_TO_SFTP_CLOSE_STATE();
break;
}
- state(data, SSH_SFTP_READDIR);
+ myssh_state(data, sshc, SSH_SFTP_READDIR);
break;
case SSH_SFTP_READDIR:
- Curl_dyn_reset(&sshc->readdir_buf);
+ curlx_dyn_reset(&sshc->readdir_buf);
if(sshc->readdir_attrs)
sftp_attributes_free(sshc->readdir_attrs);
@@ -1457,7 +1607,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
tmpLine = aprintf("%s\n", sshc->readdir_filename);
if(!tmpLine) {
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
@@ -1466,39 +1616,39 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
free(tmpLine);
if(result) {
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
}
}
else {
- if(Curl_dyn_add(&sshc->readdir_buf, sshc->readdir_longentry)) {
+ if(curlx_dyn_add(&sshc->readdir_buf, sshc->readdir_longentry)) {
sshc->actualcode = CURLE_OUT_OF_MEMORY;
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
}
if((sshc->readdir_attrs->flags & SSH_FILEXFER_ATTR_PERMISSIONS) &&
((sshc->readdir_attrs->permissions & SSH_S_IFMT) ==
SSH_S_IFLNK)) {
- sshc->readdir_linkPath = aprintf("%s%s", protop->path,
+ sshc->readdir_linkPath = aprintf("%s%s", sshp->path,
sshc->readdir_filename);
if(!sshc->readdir_linkPath) {
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
- state(data, SSH_SFTP_READDIR_LINK);
+ myssh_state(data, sshc, SSH_SFTP_READDIR_LINK);
break;
}
- state(data, SSH_SFTP_READDIR_BOTTOM);
+ myssh_state(data, sshc, SSH_SFTP_READDIR_BOTTOM);
break;
}
}
else if(sftp_dir_eof(sshc->sftp_dir)) {
- state(data, SSH_SFTP_READDIR_DONE);
+ myssh_state(data, sshc, SSH_SFTP_READDIR_DONE);
break;
}
else {
@@ -1540,8 +1690,8 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
Curl_safefree(sshc->readdir_linkPath);
- if(Curl_dyn_addf(&sshc->readdir_buf, " -> %s",
- sshc->readdir_filename)) {
+ if(curlx_dyn_addf(&sshc->readdir_buf, " -> %s",
+ sshc->readdir_filename)) {
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
@@ -1551,24 +1701,24 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
sshc->readdir_filename = NULL;
sshc->readdir_longentry = NULL;
- state(data, SSH_SFTP_READDIR_BOTTOM);
+ myssh_state(data, sshc, SSH_SFTP_READDIR_BOTTOM);
FALLTHROUGH();
case SSH_SFTP_READDIR_BOTTOM:
- if(Curl_dyn_addn(&sshc->readdir_buf, "\n", 1))
+ if(curlx_dyn_addn(&sshc->readdir_buf, "\n", 1))
result = CURLE_OUT_OF_MEMORY;
else
result = Curl_client_write(data, CLIENTWRITE_BODY,
- Curl_dyn_ptr(&sshc->readdir_buf),
- Curl_dyn_len(&sshc->readdir_buf));
+ curlx_dyn_ptr(&sshc->readdir_buf),
+ curlx_dyn_len(&sshc->readdir_buf));
ssh_string_free_char(sshc->readdir_tmp);
sshc->readdir_tmp = NULL;
if(result) {
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
}
else
- state(data, SSH_SFTP_READDIR);
+ myssh_state(data, sshc, SSH_SFTP_READDIR);
break;
case SSH_SFTP_READDIR_DONE:
@@ -1577,7 +1727,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
/* no data to transfer */
Curl_xfer_setup_nop(data);
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
case SSH_SFTP_DOWNLOAD_INIT:
@@ -1587,7 +1737,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
if(sshc->sftp_file)
sftp_close(sshc->sftp_file);
- sshc->sftp_file = sftp_open(sshc->sftp_session, protop->path,
+ sshc->sftp_file = sftp_open(sshc->sftp_session, sshp->path,
O_RDONLY, (mode_t)data->set.new_file_perms);
if(!sshc->sftp_file) {
failf(data, "Could not open remote file for reading: %s",
@@ -1597,160 +1747,19 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
break;
}
sftp_file_set_nonblocking(sshc->sftp_file);
- state(data, SSH_SFTP_DOWNLOAD_STAT);
+ myssh_state(data, sshc, SSH_SFTP_DOWNLOAD_STAT);
break;
case SSH_SFTP_DOWNLOAD_STAT:
- {
- sftp_attributes attrs;
- curl_off_t size;
-
- attrs = sftp_fstat(sshc->sftp_file);
- if(!attrs ||
- !(attrs->flags & SSH_FILEXFER_ATTR_SIZE) ||
- (attrs->size == 0)) {
- /*
- * sftp_fstat did not return an error, so maybe the server
- * just does not support stat()
- * OR the server does not return a file size with a stat()
- * OR file size is 0
- */
- data->req.size = -1;
- data->req.maxdownload = -1;
- Curl_pgrsSetDownloadSize(data, -1);
- size = 0;
- }
- else {
- size = attrs->size;
-
- sftp_attributes_free(attrs);
-
- if(size < 0) {
- failf(data, "Bad file size (%" FMT_OFF_T ")", size);
- return CURLE_BAD_DOWNLOAD_RESUME;
- }
- if(data->state.use_range) {
- curl_off_t from, to;
- char *ptr;
- char *ptr2;
- CURLofft to_t;
- CURLofft from_t;
-
- from_t = curlx_strtoofft(data->state.range, &ptr, 10, &from);
- if(from_t == CURL_OFFT_FLOW) {
- return CURLE_RANGE_ERROR;
- }
- while(*ptr && (ISBLANK(*ptr) || (*ptr == '-')))
- ptr++;
- to_t = curlx_strtoofft(ptr, &ptr2, 10, &to);
- if(to_t == CURL_OFFT_FLOW) {
- return CURLE_RANGE_ERROR;
- }
- if((to_t == CURL_OFFT_INVAL) /* no "to" value given */
- || (to >= size)) {
- to = size - 1;
- }
- if(from_t) {
- /* from is relative to end of file */
- from = size - to;
- to = size - 1;
- }
- if(from > size) {
- failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%"
- FMT_OFF_T ")", from, size);
- return CURLE_BAD_DOWNLOAD_RESUME;
- }
- if(from > to) {
- from = to;
- size = 0;
- }
- else {
- if((to - from) == CURL_OFF_T_MAX)
- return CURLE_RANGE_ERROR;
- size = to - from + 1;
- }
-
- rc = sftp_seek64(sshc->sftp_file, from);
- if(rc) {
- MOVE_TO_SFTP_CLOSE_STATE();
- break;
- }
- }
- data->req.size = size;
- data->req.maxdownload = size;
- Curl_pgrsSetDownloadSize(data, size);
- }
-
- /* We can resume if we can seek to the resume position */
- if(data->state.resume_from) {
- if(data->state.resume_from < 0) {
- /* We are supposed to download the last abs(from) bytes */
- if((curl_off_t)size < -data->state.resume_from) {
- failf(data, "Offset (%" FMT_OFF_T ") was beyond file size (%"
- FMT_OFF_T ")", data->state.resume_from, size);
- return CURLE_BAD_DOWNLOAD_RESUME;
- }
- /* download from where? */
- data->state.resume_from += size;
- }
- else {
- if((curl_off_t)size < data->state.resume_from) {
- failf(data, "Offset (%" FMT_OFF_T
- ") was beyond file size (%" FMT_OFF_T ")",
- data->state.resume_from, size);
- return CURLE_BAD_DOWNLOAD_RESUME;
- }
- }
- /* Now store the number of bytes we are expected to download */
- data->req.size = size - data->state.resume_from;
- data->req.maxdownload = size - data->state.resume_from;
- Curl_pgrsSetDownloadSize(data,
- size - data->state.resume_from);
-
- rc = sftp_seek64(sshc->sftp_file, data->state.resume_from);
- if(rc) {
- MOVE_TO_SFTP_CLOSE_STATE();
- break;
- }
- }
- }
-
- /* Setup the actual download */
- if(data->req.size == 0) {
- /* no data to transfer */
- Curl_xfer_setup_nop(data);
- infof(data, "File already completely downloaded");
- state(data, SSH_STOP);
+ rc = myssh_state_sftp_download_stat(data, sshc);
break;
- }
- Curl_xfer_setup1(data, CURL_XFER_RECV, data->req.size, FALSE);
-
- /* not set by Curl_xfer_setup to preserve keepon bits */
- conn->writesockfd = conn->sockfd;
-
- /* we want to use the _receiving_ function even when the socket turns
- out writableable as the underlying libssh recv function will deal
- with both accordingly */
- data->state.select_bits = CURL_CSELECT_IN;
-
- if(result) {
- /* this should never occur; the close state should be entered
- at the time the error occurs */
- state(data, SSH_SFTP_CLOSE);
- sshc->actualcode = result;
- }
- else {
- sshc->sftp_recv_state = 0;
- state(data, SSH_STOP);
- }
- break;
case SSH_SFTP_CLOSE:
if(sshc->sftp_file) {
sftp_close(sshc->sftp_file);
sshc->sftp_file = NULL;
}
- Curl_safefree(protop->path);
+ Curl_safefree(sshp->path);
DEBUGF(infof(data, "SFTP DONE done"));
@@ -1759,11 +1768,11 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
SSH_SFTP_CLOSE to pass the correct result back */
if(sshc->nextstate != SSH_NO_STATE &&
sshc->nextstate != SSH_SFTP_CLOSE) {
- state(data, sshc->nextstate);
+ myssh_state(data, sshc, sshc->nextstate);
sshc->nextstate = SSH_SFTP_CLOSE;
}
else {
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
result = sshc->actualcode;
}
break;
@@ -1772,6 +1781,13 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
/* during times we get here due to a broken transfer and then the
sftp_handle might not have been taken down so make sure that is done
before we proceed */
+ ssh_set_blocking(sshc->ssh_session, 0);
+#if LIBSSH_VERSION_INT > SSH_VERSION_INT(0, 11, 0)
+ if(sshc->sftp_aio) {
+ sftp_aio_free(sshc->sftp_aio);
+ sshc->sftp_aio = NULL;
+ }
+#endif
if(sshc->sftp_file) {
sftp_close(sshc->sftp_file);
@@ -1784,16 +1800,15 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
}
SSH_STRING_FREE_CHAR(sshc->homedir);
- data->state.most_recent_ftp_entrypath = NULL;
- state(data, SSH_SESSION_DISCONNECT);
+ myssh_state(data, sshc, SSH_SESSION_DISCONNECT);
break;
case SSH_SCP_TRANS_INIT:
- result = Curl_getworkingpath(data, sshc->homedir, &protop->path);
+ result = Curl_getworkingpath(data, sshc->homedir, &sshp->path);
if(result) {
sshc->actualcode = result;
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
}
@@ -1809,13 +1824,13 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
}
sshc->scp_session =
- ssh_scp_new(sshc->ssh_session, SSH_SCP_WRITE, protop->path);
- state(data, SSH_SCP_UPLOAD_INIT);
+ ssh_scp_new(sshc->ssh_session, SSH_SCP_WRITE, sshp->path);
+ myssh_state(data, sshc, SSH_SCP_UPLOAD_INIT);
}
else {
sshc->scp_session =
- ssh_scp_new(sshc->ssh_session, SSH_SCP_READ, protop->path);
- state(data, SSH_SCP_DOWNLOAD_INIT);
+ ssh_scp_new(sshc->ssh_session, SSH_SCP_READ, sshp->path);
+ myssh_state(data, sshc, SSH_SCP_DOWNLOAD_INIT);
}
if(!sshc->scp_session) {
@@ -1836,9 +1851,10 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
break;
}
- rc = ssh_scp_push_file(sshc->scp_session, protop->path,
- data->state.infilesize,
- (int)data->set.new_file_perms);
+ rc = ssh_scp_push_file64(sshc->scp_session, sshp->path,
+ (uint64_t)data->state.infilesize,
+ (int)data->set.new_file_perms);
+
if(rc != SSH_OK) {
err_msg = ssh_get_error(sshc->ssh_session);
failf(data, "%s", err_msg);
@@ -1861,7 +1877,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
with both accordingly */
data->state.select_bits = CURL_CSELECT_OUT;
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
@@ -1874,7 +1890,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT);
break;
}
- state(data, SSH_SCP_DOWNLOAD);
+ myssh_state(data, sshc, SSH_SCP_DOWNLOAD);
FALLTHROUGH();
case SSH_SCP_DOWNLOAD:{
@@ -1901,14 +1917,14 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
with both accordingly */
data->state.select_bits = CURL_CSELECT_IN;
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
}
case SSH_SCP_DONE:
if(data->state.upload)
- state(data, SSH_SCP_SEND_EOF);
+ myssh_state(data, sshc, SSH_SCP_SEND_EOF);
else
- state(data, SSH_SCP_CHANNEL_FREE);
+ myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE);
break;
case SSH_SCP_SEND_EOF:
@@ -1926,7 +1942,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
}
}
- state(data, SSH_SCP_CHANNEL_FREE);
+ myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE);
break;
case SSH_SCP_CHANNEL_FREE:
@@ -1938,7 +1954,7 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
ssh_set_blocking(sshc->ssh_session, 0);
- state(data, SSH_SESSION_DISCONNECT);
+ myssh_state(data, sshc, SSH_SESSION_DISCONNECT);
FALLTHROUGH();
case SSH_SESSION_DISCONNECT:
@@ -1950,6 +1966,15 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
sshc->scp_session = NULL;
}
+ if(sshc->sftp_file) {
+ sftp_close(sshc->sftp_file);
+ sshc->sftp_file = NULL;
+ }
+ if(sshc->sftp_session) {
+ sftp_free(sshc->sftp_session);
+ sshc->sftp_session = NULL;
+ }
+
ssh_disconnect(sshc->ssh_session);
if(!ssh_version(SSH_VERSION_INT(0, 10, 0))) {
/* conn->sock[FIRSTSOCKET] is closed by ssh_disconnect behind our back,
@@ -1959,64 +1984,25 @@ static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
}
SSH_STRING_FREE_CHAR(sshc->homedir);
- data->state.most_recent_ftp_entrypath = NULL;
- state(data, SSH_SESSION_FREE);
+ myssh_state(data, sshc, SSH_SESSION_FREE);
FALLTHROUGH();
case SSH_SESSION_FREE:
- if(sshc->ssh_session) {
- ssh_free(sshc->ssh_session);
- sshc->ssh_session = NULL;
- }
-
- /* worst-case scenario cleanup */
-
- DEBUGASSERT(sshc->ssh_session == NULL);
- DEBUGASSERT(sshc->scp_session == NULL);
-
- if(sshc->readdir_tmp) {
- ssh_string_free_char(sshc->readdir_tmp);
- sshc->readdir_tmp = NULL;
- }
-
- if(sshc->quote_attrs)
- sftp_attributes_free(sshc->quote_attrs);
-
- if(sshc->readdir_attrs)
- sftp_attributes_free(sshc->readdir_attrs);
-
- if(sshc->readdir_link_attrs)
- sftp_attributes_free(sshc->readdir_link_attrs);
-
- if(sshc->privkey)
- ssh_key_free(sshc->privkey);
- if(sshc->pubkey)
- ssh_key_free(sshc->pubkey);
-
- Curl_safefree(sshc->rsa_pub);
- Curl_safefree(sshc->rsa);
- Curl_safefree(sshc->quote_path1);
- Curl_safefree(sshc->quote_path2);
- Curl_dyn_free(&sshc->readdir_buf);
- Curl_safefree(sshc->readdir_linkPath);
- SSH_STRING_FREE_CHAR(sshc->homedir);
-
+ sshc_cleanup(sshc);
/* the code we are about to return */
result = sshc->actualcode;
-
memset(sshc, 0, sizeof(struct ssh_conn));
-
connclose(conn, "SSH session free");
sshc->state = SSH_SESSION_FREE; /* current */
sshc->nextstate = SSH_NO_STATE;
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
case SSH_QUIT:
default:
/* internal error */
sshc->nextstate = SSH_NO_STATE;
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
}
@@ -2056,10 +2042,10 @@ static int myssh_getsock(struct Curl_easy *data,
return bitmap;
}
-static void myssh_block2waitfor(struct connectdata *conn, bool block)
+static void myssh_block2waitfor(struct connectdata *conn,
+ struct ssh_conn *sshc,
+ bool block)
{
- struct ssh_conn *sshc = &conn->proto.sshc;
-
/* If it did not block, or nothing was returned by ssh_get_poll_flags
* have the original set */
conn->waitfor = sshc->orig_waitfor;
@@ -2080,30 +2066,35 @@ static CURLcode myssh_multi_statemach(struct Curl_easy *data,
bool *done)
{
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+ struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY);
bool block; /* we store the status and use that to provide a ssh_getsock()
implementation */
- CURLcode result = myssh_statemach_act(data, &block);
+ CURLcode result;
+ if(!sshc || !sshp)
+ return CURLE_FAILED_INIT;
+ result = myssh_statemach_act(data, sshc, sshp, &block);
*done = (sshc->state == SSH_STOP);
- myssh_block2waitfor(conn, block);
+ myssh_block2waitfor(conn, sshc, block);
return result;
}
static CURLcode myssh_block_statemach(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp,
bool disconnect)
{
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
CURLcode result = CURLE_OK;
while((sshc->state != SSH_STOP) && !result) {
bool block;
timediff_t left = 1000;
- struct curltime now = Curl_now();
+ struct curltime now = curlx_now();
- result = myssh_statemach_act(data, &block);
+ result = myssh_statemach_act(data, sshc, sshp, &block);
if(result)
break;
@@ -2134,19 +2125,46 @@ static CURLcode myssh_block_statemach(struct Curl_easy *data,
return result;
}
+static void myssh_easy_dtor(void *key, size_t klen, void *entry)
+{
+ struct SSHPROTO *sshp = entry;
+ (void)key;
+ (void)klen;
+ Curl_safefree(sshp->path);
+ free(sshp);
+}
+
+static void myssh_conn_dtor(void *key, size_t klen, void *entry)
+{
+ struct ssh_conn *sshc = entry;
+ (void)key;
+ (void)klen;
+ sshc_cleanup(sshc);
+ free(sshc);
+}
+
/*
* SSH setup connection
*/
static CURLcode myssh_setup_connection(struct Curl_easy *data,
struct connectdata *conn)
{
- struct SSHPROTO *ssh;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct SSHPROTO *sshp;
+ struct ssh_conn *sshc;
+
+ sshc = calloc(1, sizeof(*sshc));
+ if(!sshc)
+ return CURLE_OUT_OF_MEMORY;
+
+ curlx_dyn_init(&sshc->readdir_buf, CURL_PATH_MAX * 2);
+ sshc->initialised = TRUE;
+ if(Curl_conn_meta_set(conn, CURL_META_SSH_CONN, sshc, myssh_conn_dtor))
+ return CURLE_OUT_OF_MEMORY;
- data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO));
- if(!ssh)
+ sshp = calloc(1, sizeof(*sshp));
+ if(!sshp ||
+ Curl_meta_set(data, CURL_META_SSH_EASY, sshp, myssh_easy_dtor))
return CURLE_OUT_OF_MEMORY;
- Curl_dyn_init(&sshc->readdir_buf, CURL_PATH_MAX * 2);
return CURLE_OK;
}
@@ -2160,15 +2178,15 @@ static Curl_send scp_send, sftp_send;
*/
static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
{
- struct ssh_conn *ssh;
CURLcode result;
struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+ struct SSHPROTO *ssh = Curl_meta_get(data, CURL_META_SSH_EASY);
curl_socket_t sock = conn->sock[FIRSTSOCKET];
int rc;
- /* initialize per-handle data if not already */
- if(!data->req.p.ssh)
- myssh_setup_connection(data, conn);
+ if(!sshc || !ssh)
+ return CURLE_FAILED_INIT;
/* We default to persistent connections. We set this already in this connect
function to make the reuse checks properly be able to check this bit. */
@@ -2183,27 +2201,32 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
conn->send[FIRSTSOCKET] = sftp_send;
}
- ssh = &conn->proto.sshc;
-
- ssh->ssh_session = ssh_new();
- if(!ssh->ssh_session) {
+ sshc->ssh_session = ssh_new();
+ if(!sshc->ssh_session) {
failf(data, "Failure initialising ssh session");
return CURLE_FAILED_INIT;
}
- rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_HOST, conn->host.name);
+ if(conn->bits.ipv6_ip) {
+ char ipv6[MAX_IPADR_LEN];
+ msnprintf(ipv6, sizeof(ipv6), "[%s]", conn->host.name);
+ rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_HOST, ipv6);
+ }
+ else
+ rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_HOST, conn->host.name);
+
if(rc != SSH_OK) {
failf(data, "Could not set remote host");
return CURLE_FAILED_INIT;
}
- rc = ssh_options_parse_config(ssh->ssh_session, NULL);
+ rc = ssh_options_parse_config(sshc->ssh_session, NULL);
if(rc != SSH_OK) {
infof(data, "Could not parse SSH configuration files");
/* ignore */
}
- rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_FD, &sock);
+ rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_FD, &sock);
if(rc != SSH_OK) {
failf(data, "Could not set socket");
return CURLE_FAILED_INIT;
@@ -2211,7 +2234,7 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
if(conn->user && conn->user[0] != '\0') {
infof(data, "User: %s", conn->user);
- rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_USER, conn->user);
+ rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_USER, conn->user);
if(rc != SSH_OK) {
failf(data, "Could not set user");
return CURLE_FAILED_INIT;
@@ -2220,7 +2243,7 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
infof(data, "Known hosts: %s", data->set.str[STRING_SSH_KNOWNHOSTS]);
- rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_KNOWNHOSTS,
+ rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_KNOWNHOSTS,
data->set.str[STRING_SSH_KNOWNHOSTS]);
if(rc != SSH_OK) {
failf(data, "Could not set known hosts file path");
@@ -2229,7 +2252,7 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
}
if(conn->remote_port) {
- rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_PORT,
+ rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_PORT,
&conn->remote_port);
if(rc != SSH_OK) {
failf(data, "Could not set remote port");
@@ -2238,7 +2261,7 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
}
if(data->set.ssh_compression) {
- rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_COMPRESSION,
+ rc = ssh_options_set(sshc->ssh_session, SSH_OPTIONS_COMPRESSION,
"zlib,zlib@openssh.com,none");
if(rc != SSH_OK) {
failf(data, "Could not set compression");
@@ -2246,12 +2269,12 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
}
}
- ssh->privkey = NULL;
- ssh->pubkey = NULL;
+ sshc->privkey = NULL;
+ sshc->pubkey = NULL;
if(data->set.str[STRING_SSH_PUBLIC_KEY]) {
rc = ssh_pki_import_pubkey_file(data->set.str[STRING_SSH_PUBLIC_KEY],
- &ssh->pubkey);
+ &sshc->pubkey);
if(rc != SSH_OK) {
failf(data, "Could not load public key file");
return CURLE_FAILED_INIT;
@@ -2261,7 +2284,7 @@ static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
/* we do not verify here, we do it at the state machine,
* after connection */
- state(data, SSH_INIT);
+ myssh_state(data, sshc, SSH_INIT);
result = myssh_multi_statemach(data, done);
@@ -2295,13 +2318,16 @@ CURLcode scp_perform(struct Curl_easy *data,
bool *connected, bool *dophase_done)
{
CURLcode result = CURLE_OK;
+ struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
DEBUGF(infof(data, "DO phase starts"));
*dophase_done = FALSE; /* not done yet */
+ if(!sshc)
+ return CURLE_FAILED_INIT;
/* start the first command in the DO phase */
- state(data, SSH_SCP_TRANS_INIT);
+ myssh_state(data, sshc, SSH_SCP_TRANS_INIT);
result = myssh_multi_statemach(data, dophase_done);
@@ -2319,9 +2345,11 @@ static CURLcode myssh_do_it(struct Curl_easy *data, bool *done)
CURLcode result;
bool connected = FALSE;
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
*done = FALSE; /* default to false */
+ if(!sshc)
+ return CURLE_FAILED_INIT;
data->req.size = -1; /* make sure this is unknown at this point */
@@ -2342,6 +2370,62 @@ static CURLcode myssh_do_it(struct Curl_easy *data, bool *done)
return result;
}
+static void sshc_cleanup(struct ssh_conn *sshc)
+{
+ if(sshc->initialised) {
+ if(sshc->sftp_file) {
+ sftp_close(sshc->sftp_file);
+ sshc->sftp_file = NULL;
+ }
+ if(sshc->sftp_session) {
+ sftp_free(sshc->sftp_session);
+ sshc->sftp_session = NULL;
+ }
+ if(sshc->ssh_session) {
+ ssh_free(sshc->ssh_session);
+ sshc->ssh_session = NULL;
+ }
+
+ /* worst-case scenario cleanup */
+ DEBUGASSERT(sshc->ssh_session == NULL);
+ DEBUGASSERT(sshc->scp_session == NULL);
+
+ if(sshc->readdir_tmp) {
+ ssh_string_free_char(sshc->readdir_tmp);
+ sshc->readdir_tmp = NULL;
+ }
+ if(sshc->quote_attrs) {
+ sftp_attributes_free(sshc->quote_attrs);
+ sshc->quote_attrs = NULL;
+ }
+ if(sshc->readdir_attrs) {
+ sftp_attributes_free(sshc->readdir_attrs);
+ sshc->readdir_attrs = NULL;
+ }
+ if(sshc->readdir_link_attrs) {
+ sftp_attributes_free(sshc->readdir_link_attrs);
+ sshc->readdir_link_attrs = NULL;
+ }
+ if(sshc->privkey) {
+ ssh_key_free(sshc->privkey);
+ sshc->privkey = NULL;
+ }
+ if(sshc->pubkey) {
+ ssh_key_free(sshc->pubkey);
+ sshc->pubkey = NULL;
+ }
+
+ Curl_safefree(sshc->rsa_pub);
+ Curl_safefree(sshc->rsa);
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ curlx_dyn_free(&sshc->readdir_buf);
+ Curl_safefree(sshc->readdir_linkPath);
+ SSH_STRING_FREE_CHAR(sshc->homedir);
+ sshc->initialised = FALSE;
+ }
+}
+
/* BLOCKING, but the function is using the state machine so the only reason
this is still blocking is that the multi interface code has no support for
disconnecting operations that takes a while */
@@ -2350,15 +2434,16 @@ static CURLcode scp_disconnect(struct Curl_easy *data,
bool dead_connection)
{
CURLcode result = CURLE_OK;
- struct ssh_conn *ssh = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+ struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY);
(void) dead_connection;
- if(ssh->ssh_session) {
+ if(sshc && sshc->ssh_session && sshp) {
/* only if there is a session still around to use! */
- state(data, SSH_SESSION_DISCONNECT);
+ myssh_state(data, sshc, SSH_SESSION_DISCONNECT);
- result = myssh_block_statemach(data, TRUE);
+ result = myssh_block_statemach(data, sshc, sshp, TRUE);
}
return result;
@@ -2366,20 +2451,20 @@ static CURLcode scp_disconnect(struct Curl_easy *data,
/* generic done function for both SCP and SFTP called from their specific
done functions */
-static CURLcode myssh_done(struct Curl_easy *data, CURLcode status)
+static CURLcode myssh_done(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ CURLcode status)
{
CURLcode result = CURLE_OK;
- struct SSHPROTO *protop = data->req.p.ssh;
+ struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY);
- if(!status) {
+ if(!status && sshp) {
/* run the state-machine */
- result = myssh_block_statemach(data, FALSE);
+ result = myssh_block_statemach(data, sshc, sshp, FALSE);
}
else
result = status;
- if(protop)
- Curl_safefree(protop->path);
if(Curl_pgrsDone(data))
return CURLE_ABORTED_BY_CALLBACK;
@@ -2391,13 +2476,15 @@ static CURLcode myssh_done(struct Curl_easy *data, CURLcode status)
static CURLcode scp_done(struct Curl_easy *data, CURLcode status,
bool premature)
{
+ struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
(void) premature; /* not used */
+ if(!sshc)
+ return CURLE_FAILED_INIT;
if(!status)
- state(data, SSH_SCP_DONE);
-
- return myssh_done(data, status);
+ myssh_state(data, sshc, SSH_SCP_DONE);
+ return myssh_done(data, sshc, status);
}
static ssize_t scp_send(struct Curl_easy *data, int sockindex,
@@ -2405,17 +2492,22 @@ static ssize_t scp_send(struct Curl_easy *data, int sockindex,
{
int rc;
struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
(void) sockindex; /* we only support SCP on the fixed known primary socket */
- (void) err;
(void)eos;
- rc = ssh_scp_write(conn->proto.sshc.scp_session, mem, len);
+ if(!sshc) {
+ *err = CURLE_FAILED_INIT;
+ return -1;
+ }
+
+ rc = ssh_scp_write(sshc->scp_session, mem, len);
#if 0
/* The following code is misleading, mostly added as wishful thinking
* that libssh at some point will implement non-blocking ssh_scp_write/read.
* Currently rc can only be number of bytes read or SSH_ERROR. */
- myssh_block2waitfor(conn, (rc == SSH_AGAIN));
+ myssh_block2waitfor(conn, sshc, (rc == SSH_AGAIN));
if(rc == SSH_AGAIN) {
*err = CURLE_AGAIN;
@@ -2436,18 +2528,22 @@ static ssize_t scp_recv(struct Curl_easy *data, int sockindex,
{
ssize_t nread;
struct connectdata *conn = data->conn;
- (void) err;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
(void) sockindex; /* we only support SCP on the fixed known primary socket */
+ if(!sshc) {
+ *err = CURLE_FAILED_INIT;
+ return -1;
+ }
/* libssh returns int */
- nread = ssh_scp_read(conn->proto.sshc.scp_session, mem, len);
+ nread = ssh_scp_read(sshc->scp_session, mem, len);
#if 0
/* The following code is misleading, mostly added as wishful thinking
* that libssh at some point will implement non-blocking ssh_scp_write/read.
* Currently rc can only be SSH_OK or SSH_ERROR. */
- myssh_block2waitfor(conn, (nread == SSH_AGAIN));
+ myssh_block2waitfor(conn, sshc, (nread == SSH_AGAIN));
if(nread == SSH_AGAIN) {
*err = CURLE_AGAIN;
nread = -1;
@@ -2475,14 +2571,17 @@ CURLcode sftp_perform(struct Curl_easy *data,
bool *connected,
bool *dophase_done)
{
+ struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
CURLcode result = CURLE_OK;
DEBUGF(infof(data, "DO phase starts"));
*dophase_done = FALSE; /* not done yet */
+ if(!sshc)
+ return CURLE_FAILED_INIT;
/* start the first command in the DO phase */
- state(data, SSH_SFTP_QUOTE_INIT);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_INIT);
/* run the state-machine */
result = myssh_multi_statemach(data, dophase_done);
@@ -2514,38 +2613,40 @@ static CURLcode sftp_disconnect(struct Curl_easy *data,
struct connectdata *conn,
bool dead_connection)
{
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+ struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY);
CURLcode result = CURLE_OK;
(void) dead_connection;
DEBUGF(infof(data, "SSH DISCONNECT starts now"));
- if(conn->proto.sshc.ssh_session) {
+ if(sshc && sshc->ssh_session && sshp) {
/* only if there is a session still around to use! */
- state(data, SSH_SFTP_SHUTDOWN);
- result = myssh_block_statemach(data, TRUE);
+ myssh_state(data, sshc, SSH_SFTP_SHUTDOWN);
+ result = myssh_block_statemach(data, sshc, sshp, TRUE);
}
DEBUGF(infof(data, "SSH DISCONNECT is done"));
-
return result;
-
}
static CURLcode sftp_done(struct Curl_easy *data, CURLcode status,
bool premature)
{
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+ if(!sshc)
+ return CURLE_FAILED_INIT;
if(!status) {
/* Post quote commands are executed after the SFTP_CLOSE state to avoid
errors that could happen due to open file handles during POSTQUOTE
operation */
if(!premature && data->set.postquote && !conn->bits.retry)
sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT;
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
}
- return myssh_done(data, status);
+ return myssh_done(data, sshc, status);
}
/* return number of sent bytes */
@@ -2555,18 +2656,55 @@ static ssize_t sftp_send(struct Curl_easy *data, int sockindex,
{
ssize_t nwrite;
struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
(void)sockindex;
(void)eos;
+ if(!sshc) {
+ *err = CURLE_FAILED_INIT;
+ return -1;
+ }
/* limit the writes to the maximum specified in Section 3 of
* https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-02
*/
if(len > 32768)
len = 32768;
+#if LIBSSH_VERSION_INT > SSH_VERSION_INT(0, 11, 0)
+ switch(sshc->sftp_send_state) {
+ case 0:
+ sftp_file_set_nonblocking(sshc->sftp_file);
+ if(sftp_aio_begin_write(sshc->sftp_file, mem, len,
+ &sshc->sftp_aio) == SSH_ERROR) {
+ *err = CURLE_SEND_ERROR;
+ return -1;
+ }
+ sshc->sftp_send_state = 1;
+ FALLTHROUGH();
+ case 1:
+ nwrite = sftp_aio_wait_write(&sshc->sftp_aio);
+ myssh_block2waitfor(conn, sshc, (nwrite == SSH_AGAIN) ? TRUE : FALSE);
+ if(nwrite == SSH_AGAIN) {
+ *err = CURLE_AGAIN;
+ return 0;
+ }
+ else if(nwrite < 0) {
+ *err = CURLE_SEND_ERROR;
+ return -1;
+ }
+ if(sshc->sftp_aio) {
+ sftp_aio_free(sshc->sftp_aio);
+ sshc->sftp_aio = NULL;
+ }
+ sshc->sftp_send_state = 0;
+ return nwrite;
+ default:
+ /* we never reach here */
+ return -1;
+ }
+#else
+ nwrite = sftp_write(sshc->sftp_file, mem, len);
- nwrite = sftp_write(conn->proto.sshc.sftp_file, mem, len);
-
- myssh_block2waitfor(conn, FALSE);
+ myssh_block2waitfor(conn, sshc, FALSE);
#if 0 /* not returned by libssh on write */
if(nwrite == SSH_AGAIN) {
@@ -2581,6 +2719,7 @@ static ssize_t sftp_send(struct Curl_easy *data, int sockindex,
}
return nwrite;
+#endif
}
/*
@@ -2592,29 +2731,31 @@ static ssize_t sftp_recv(struct Curl_easy *data, int sockindex,
{
ssize_t nread;
struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
(void)sockindex;
DEBUGASSERT(len < CURL_MAX_READ_SIZE);
+ if(!sshc) {
+ *err = CURLE_FAILED_INIT;
+ return -1;
+ }
- switch(conn->proto.sshc.sftp_recv_state) {
+ switch(sshc->sftp_recv_state) {
case 0:
- conn->proto.sshc.sftp_file_index =
- sftp_async_read_begin(conn->proto.sshc.sftp_file,
- (uint32_t)len);
- if(conn->proto.sshc.sftp_file_index < 0) {
+ sshc->sftp_file_index =
+ sftp_async_read_begin(sshc->sftp_file, (uint32_t)len);
+ if(sshc->sftp_file_index < 0) {
*err = CURLE_RECV_ERROR;
return -1;
}
FALLTHROUGH();
case 1:
- conn->proto.sshc.sftp_recv_state = 1;
-
- nread = sftp_async_read(conn->proto.sshc.sftp_file,
- mem, (uint32_t)len,
- (uint32_t)conn->proto.sshc.sftp_file_index);
+ sshc->sftp_recv_state = 1;
+ nread = sftp_async_read(sshc->sftp_file, mem, (uint32_t)len,
+ (uint32_t)sshc->sftp_file_index);
- myssh_block2waitfor(conn, (nread == SSH_AGAIN));
+ myssh_block2waitfor(conn, sshc, (nread == SSH_AGAIN));
if(nread == SSH_AGAIN) {
*err = CURLE_AGAIN;
@@ -2625,7 +2766,7 @@ static ssize_t sftp_recv(struct Curl_easy *data, int sockindex,
return -1;
}
- conn->proto.sshc.sftp_recv_state = 0;
+ sshc->sftp_recv_state = 0;
return nread;
default:
@@ -2634,12 +2775,11 @@ static ssize_t sftp_recv(struct Curl_easy *data, int sockindex,
}
}
-static void sftp_quote(struct Curl_easy *data)
+static void sftp_quote(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp)
{
const char *cp;
- struct connectdata *conn = data->conn;
- struct SSHPROTO *protop = data->req.p.ssh;
- struct ssh_conn *sshc = &conn->proto.sshc;
CURLcode result;
/*
@@ -2660,15 +2800,14 @@ static void sftp_quote(struct Curl_easy *data)
if(strcasecompare("pwd", cmd)) {
/* output debug output if that is requested */
- char *tmp = aprintf("257 \"%s\" is current directory.\n",
- protop->path);
+ char *tmp = aprintf("257 \"%s\" is current directory.\n", sshp->path);
if(!tmp) {
sshc->actualcode = CURLE_OUT_OF_MEMORY;
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
return;
}
- Curl_debug(data, CURLINFO_HEADER_OUT, (char *) "PWD\n", 4);
+ Curl_debug(data, CURLINFO_HEADER_OUT, "PWD\n", 4);
Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp));
/* this sends an FTP-like "header" to the header callback so that the
@@ -2677,12 +2816,12 @@ static void sftp_quote(struct Curl_easy *data)
result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp));
free(tmp);
if(result) {
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = result;
}
else
- state(data, SSH_SFTP_NEXT_QUOTE);
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
return;
}
@@ -2693,7 +2832,7 @@ static void sftp_quote(struct Curl_easy *data)
cp = strchr(cmd, ' ');
if(!cp) {
failf(data, "Syntax error in SFTP command. Supply parameter(s)");
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
return;
@@ -2709,7 +2848,7 @@ static void sftp_quote(struct Curl_easy *data)
failf(data, "Out of memory");
else
failf(data, "Syntax error: Bad first parameter");
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = result;
return;
@@ -2721,11 +2860,11 @@ static void sftp_quote(struct Curl_easy *data)
* OpenSSH's sftp program and call the appropriate libssh
* functions.
*/
- if(strncasecompare(cmd, "chgrp ", 6) ||
- strncasecompare(cmd, "chmod ", 6) ||
- strncasecompare(cmd, "chown ", 6) ||
- strncasecompare(cmd, "atime ", 6) ||
- strncasecompare(cmd, "mtime ", 6)) {
+ if(!strncmp(cmd, "chgrp ", 6) ||
+ !strncmp(cmd, "chmod ", 6) ||
+ !strncmp(cmd, "chown ", 6) ||
+ !strncmp(cmd, "atime ", 6) ||
+ !strncmp(cmd, "mtime ", 6)) {
/* attribute change */
/* sshc->quote_path1 contains the mode to set */
@@ -2738,17 +2877,17 @@ static void sftp_quote(struct Curl_easy *data)
failf(data, "Syntax error in chgrp/chmod/chown/atime/mtime: "
"Bad second parameter");
Curl_safefree(sshc->quote_path1);
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = result;
return;
}
sshc->quote_attrs = NULL;
- state(data, SSH_SFTP_QUOTE_STAT);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_STAT);
return;
}
- if(strncasecompare(cmd, "ln ", 3) ||
- strncasecompare(cmd, "symlink ", 8)) {
+ if(!strncmp(cmd, "ln ", 3) ||
+ !strncmp(cmd, "symlink ", 8)) {
/* symbolic linking */
/* sshc->quote_path1 is the source */
/* get the destination */
@@ -2759,20 +2898,20 @@ static void sftp_quote(struct Curl_easy *data)
else
failf(data, "Syntax error in ln/symlink: Bad second parameter");
Curl_safefree(sshc->quote_path1);
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = result;
return;
}
- state(data, SSH_SFTP_QUOTE_SYMLINK);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_SYMLINK);
return;
}
- else if(strncasecompare(cmd, "mkdir ", 6)) {
+ else if(!strncmp(cmd, "mkdir ", 6)) {
/* create dir */
- state(data, SSH_SFTP_QUOTE_MKDIR);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_MKDIR);
return;
}
- else if(strncasecompare(cmd, "rename ", 7)) {
+ else if(!strncmp(cmd, "rename ", 7)) {
/* rename file */
/* first param is the source path */
/* second param is the dest. path */
@@ -2783,26 +2922,26 @@ static void sftp_quote(struct Curl_easy *data)
else
failf(data, "Syntax error in rename: Bad second parameter");
Curl_safefree(sshc->quote_path1);
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = result;
return;
}
- state(data, SSH_SFTP_QUOTE_RENAME);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_RENAME);
return;
}
- else if(strncasecompare(cmd, "rmdir ", 6)) {
+ else if(!strncmp(cmd, "rmdir ", 6)) {
/* delete dir */
- state(data, SSH_SFTP_QUOTE_RMDIR);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_RMDIR);
return;
}
- else if(strncasecompare(cmd, "rm ", 3)) {
- state(data, SSH_SFTP_QUOTE_UNLINK);
+ else if(!strncmp(cmd, "rm ", 3)) {
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_UNLINK);
return;
}
#ifdef HAS_STATVFS_SUPPORT
- else if(strncasecompare(cmd, "statvfs ", 8)) {
- state(data, SSH_SFTP_QUOTE_STATVFS);
+ else if(!strncmp(cmd, "statvfs ", 8)) {
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_STATVFS);
return;
}
#endif
@@ -2810,15 +2949,14 @@ static void sftp_quote(struct Curl_easy *data)
failf(data, "Unknown SFTP command");
Curl_safefree(sshc->quote_path1);
Curl_safefree(sshc->quote_path2);
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
}
-static void sftp_quote_stat(struct Curl_easy *data)
+static void sftp_quote_stat(struct Curl_easy *data,
+ struct ssh_conn *sshc)
{
- struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
char *cmd = sshc->quote_item->data;
sshc->acceptfail = FALSE;
@@ -2845,59 +2983,63 @@ static void sftp_quote_stat(struct Curl_easy *data)
Curl_safefree(sshc->quote_path2);
failf(data, "Attempt to get SFTP stats failed: %d",
sftp_get_error(sshc->sftp_session));
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
return;
}
/* Now set the new attributes... */
- if(strncasecompare(cmd, "chgrp", 5)) {
- sshc->quote_attrs->gid = (uint32_t)strtoul(sshc->quote_path1, NULL, 10);
+ if(!strncmp(cmd, "chgrp", 5)) {
+ const char *p = sshc->quote_path1;
+ curl_off_t gid;
+ (void)curlx_str_number(&p, &gid, UINT_MAX);
+ sshc->quote_attrs->gid = (uint32_t)gid;
if(sshc->quote_attrs->gid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
- !sshc->acceptfail) {
+ !sshc->acceptfail) {
Curl_safefree(sshc->quote_path1);
Curl_safefree(sshc->quote_path2);
failf(data, "Syntax error: chgrp gid not a number");
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
return;
}
sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID;
}
- else if(strncasecompare(cmd, "chmod", 5)) {
- mode_t perms;
- perms = (mode_t)strtoul(sshc->quote_path1, NULL, 8);
- /* permissions are octal */
- if(perms == 0 && !ISDIGIT(sshc->quote_path1[0])) {
+ else if(!strncmp(cmd, "chmod", 5)) {
+ curl_off_t perms;
+ const char *p = sshc->quote_path1;
+ if(curlx_str_octal(&p, &perms, 07777)) {
Curl_safefree(sshc->quote_path1);
Curl_safefree(sshc->quote_path2);
failf(data, "Syntax error: chmod permissions not a number");
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
return;
}
- sshc->quote_attrs->permissions = perms;
+ sshc->quote_attrs->permissions = (mode_t)perms;
sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_PERMISSIONS;
}
- else if(strncasecompare(cmd, "chown", 5)) {
- sshc->quote_attrs->uid = (uint32_t)strtoul(sshc->quote_path1, NULL, 10);
+ else if(!strncmp(cmd, "chown", 5)) {
+ const char *p = sshc->quote_path1;
+ curl_off_t uid;
+ (void)curlx_str_number(&p, &uid, UINT_MAX);
if(sshc->quote_attrs->uid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
- !sshc->acceptfail) {
+ !sshc->acceptfail) {
Curl_safefree(sshc->quote_path1);
Curl_safefree(sshc->quote_path2);
failf(data, "Syntax error: chown uid not a number");
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
return;
}
sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID;
}
- else if(strncasecompare(cmd, "atime", 5) ||
- strncasecompare(cmd, "mtime", 5)) {
+ else if(!strncmp(cmd, "atime", 5) ||
+ !strncmp(cmd, "mtime", 5)) {
time_t date = Curl_getdate_capped(sshc->quote_path1);
bool fail = FALSE;
if(date == -1) {
@@ -2913,12 +3055,12 @@ static void sftp_quote_stat(struct Curl_easy *data)
if(fail) {
Curl_safefree(sshc->quote_path1);
Curl_safefree(sshc->quote_path2);
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
sshc->actualcode = CURLE_QUOTE_ERROR;
return;
}
- if(strncasecompare(cmd, "atime", 5))
+ if(!strncmp(cmd, "atime", 5))
sshc->quote_attrs->atime = (uint32_t)date;
else /* mtime */
sshc->quote_attrs->mtime = (uint32_t)date;
@@ -2927,7 +3069,7 @@ static void sftp_quote_stat(struct Curl_easy *data)
}
/* Now send the completed structure... */
- state(data, SSH_SFTP_QUOTE_SETSTAT);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_SETSTAT);
return;
}
diff --git a/libs/libcurl/src/vssh/libssh2.c b/libs/libcurl/src/vssh/libssh2.c
index ee3847324b..f0081ecb0b 100644
--- a/libs/libcurl/src/vssh/libssh2.c
+++ b/libs/libcurl/src/vssh/libssh2.c
@@ -24,7 +24,7 @@
/* #define CURL_LIBSSH2_DEBUG */
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_LIBSSH2
@@ -49,55 +49,45 @@
#endif
#include <curl/curl.h>
-#include "urldata.h"
-#include "sendf.h"
-#include "hostip.h"
-#include "progress.h"
-#include "transfer.h"
-#include "escape.h"
-#include "http.h" /* for HTTP proxy tunnel stuff */
+#include "../urldata.h"
+#include "../sendf.h"
+#include "../hostip.h"
+#include "../progress.h"
+#include "../transfer.h"
+#include "../escape.h"
+#include "../http.h" /* for HTTP proxy tunnel stuff */
#include "ssh.h"
-#include "url.h"
-#include "speedcheck.h"
-#include "getinfo.h"
-#include "strdup.h"
-#include "strcase.h"
-#include "vtls/vtls.h"
-#include "cfilters.h"
-#include "connect.h"
-#include "inet_ntop.h"
-#include "parsedate.h" /* for the week day and month names */
-#include "sockaddr.h" /* required for Curl_sockaddr_storage */
-#include "strtoofft.h"
-#include "multiif.h"
-#include "select.h"
-#include "warnless.h"
+#include "../url.h"
+#include "../speedcheck.h"
+#include "../getinfo.h"
+#include "../strdup.h"
+#include "../strcase.h"
+#include "../vtls/vtls.h"
+#include "../cfilters.h"
+#include "../connect.h"
+#include "../inet_ntop.h"
+#include "../parsedate.h" /* for the week day and month names */
+#include "../sockaddr.h" /* required for Curl_sockaddr_storage */
+#include "../multiif.h"
+#include "../select.h"
+#include "../curlx/warnless.h"
#include "curl_path.h"
-
-#include <curl_base64.h> /* for base64 encoding/decoding */
-#include <curl_sha256.h>
-
+#include "../curlx/strparse.h"
+#include "../curlx/base64.h" /* for base64 encoding/decoding */
+#include "../curl_sha256.h"
/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
-
-#if LIBSSH2_VERSION_NUM >= 0x010206
-/* libssh2_sftp_statvfs and friends were added in 1.2.6 */
-#define HAS_STATVFS_SUPPORT 1
-#endif
-
-#define sftp_libssh2_realpath(s,p,t,m) \
- libssh2_sftp_symlink_ex((s), (p), curlx_uztoui(strlen(p)), \
- (t), (m), LIBSSH2_SFTP_REALPATH)
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
/* Local functions: */
static const char *sftp_libssh2_strerror(unsigned long err);
static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc);
static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc);
static LIBSSH2_FREE_FUNC(my_libssh2_free);
-static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data);
+static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data,
+ struct ssh_conn *sshc);
static CURLcode ssh_connect(struct Curl_easy *data, bool *done);
static CURLcode ssh_multi_statemach(struct Curl_easy *data, bool *done);
static CURLcode ssh_do(struct Curl_easy *data, bool *done);
@@ -116,7 +106,8 @@ static int ssh_getsock(struct Curl_easy *data, struct connectdata *conn,
static CURLcode ssh_setup_connection(struct Curl_easy *data,
struct connectdata *conn);
static void ssh_attach(struct Curl_easy *data, struct connectdata *conn);
-
+static CURLcode sshc_cleanup(struct ssh_conn *sshc, struct Curl_easy *data,
+ bool block);
/*
* SCP protocol handler.
*/
@@ -139,6 +130,7 @@ const struct Curl_handler Curl_handler_scp = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ssh_attach, /* attach */
+ ZERO_NULL, /* follow */
PORT_SSH, /* defport */
CURLPROTO_SCP, /* protocol */
CURLPROTO_SCP, /* family */
@@ -169,6 +161,7 @@ const struct Curl_handler Curl_handler_sftp = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ssh_attach, /* attach */
+ ZERO_NULL, /* follow */
PORT_SSH, /* defport */
CURLPROTO_SFTP, /* protocol */
CURLPROTO_SFTP, /* family */
@@ -306,10 +299,10 @@ static LIBSSH2_FREE_FUNC(my_libssh2_free)
* SSH State machine related code
*/
/* This is the ONLY way to change SSH state! */
-static void state(struct Curl_easy *data, sshstate nowstate)
+static void myssh_state(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ sshstate nowstate)
{
- struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
/* for debug purposes */
static const char * const names[] = {
@@ -376,19 +369,17 @@ static void state(struct Curl_easy *data, sshstate nowstate)
};
/* a precaution to make sure the lists are in sync */
- DEBUGASSERT(sizeof(names)/sizeof(names[0]) == SSH_LAST);
+ DEBUGASSERT(CURL_ARRAYSIZE(names) == SSH_LAST);
if(sshc->state != nowstate) {
infof(data, "SFTP %p state change from %s to %s",
(void *)sshc, names[sshc->state], names[nowstate]);
}
#endif
-
+ (void)data;
sshc->state = nowstate;
}
-
-#ifdef HAVE_LIBSSH2_KNOWNHOST_API
static int sshkeycallback(CURL *easy,
const struct curl_khkey *knownkey, /* known */
const struct curl_khkey *foundkey, /* found */
@@ -403,37 +394,7 @@ static int sshkeycallback(CURL *easy,
/* we only allow perfect matches, and we reject everything else */
return (match != CURLKHMATCH_OK) ? CURLKHSTAT_REJECT : CURLKHSTAT_FINE;
}
-#endif
-
-/*
- * Earlier libssh2 versions did not have the ability to seek to 64-bit
- * positions with 32-bit size_t.
- */
-#ifdef HAVE_LIBSSH2_SFTP_SEEK64
-#define SFTP_SEEK(x,y) libssh2_sftp_seek64(x, (libssh2_uint64_t)y)
-#else
-#define SFTP_SEEK(x,y) libssh2_sftp_seek(x, (size_t)y)
-#endif
-/*
- * Earlier libssh2 versions did not do SCP properly beyond 32-bit sizes on
- * 32-bit architectures so we check of the necessary function is present.
- */
-#ifndef HAVE_LIBSSH2_SCP_SEND64
-#define SCP_SEND(a,b,c,d) libssh2_scp_send_ex(a, b, (int)(c), (size_t)d, 0, 0)
-#else
-#define SCP_SEND(a,b,c,d) libssh2_scp_send64(a, b, (int)(c), \
- (libssh2_int64_t)d, 0, 0)
-#endif
-
-/*
- * libssh2 1.2.8 fixed the problem with 32-bit ints used for sockets on win64.
- */
-#ifdef HAVE_LIBSSH2_SESSION_HANDSHAKE
-#define session_startup(x,y) libssh2_session_handshake(x, y)
-#else
-#define session_startup(x,y) libssh2_session_startup(x, (int)y)
-#endif
static enum curl_khtype convert_ssh2_keytype(int sshkeytype)
{
enum curl_khtype keytype = CURLKHTYPE_UNKNOWN;
@@ -468,18 +429,17 @@ static enum curl_khtype convert_ssh2_keytype(int sshkeytype)
return keytype;
}
-static CURLcode ssh_knownhost(struct Curl_easy *data)
+static CURLcode ssh_knownhost(struct Curl_easy *data,
+ struct ssh_conn *sshc)
{
int sshkeytype = 0;
size_t keylen = 0;
int rc = 0;
CURLcode result = CURLE_OK;
-#ifdef HAVE_LIBSSH2_KNOWNHOST_API
if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
/* we are asked to verify the host against a file */
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
struct libssh2_knownhost *host = NULL;
const char *remotekey = libssh2_session_hostkey(sshc->ssh_session,
&keylen, &sshkeytype);
@@ -535,7 +495,6 @@ static CURLcode ssh_knownhost(struct Curl_easy *data)
/* no check means failure! */
rc = CURLKHSTAT_REJECT;
else {
-#ifdef HAVE_LIBSSH2_KNOWNHOST_CHECKP
keycheck = libssh2_knownhost_checkp(sshc->kh,
conn->host.name,
(conn->remote_port != PORT_SSH) ?
@@ -545,15 +504,6 @@ static CURLcode ssh_knownhost(struct Curl_easy *data)
LIBSSH2_KNOWNHOST_KEYENC_RAW|
keybit,
&host);
-#else
- keycheck = libssh2_knownhost_check(sshc->kh,
- conn->host.name,
- remotekey, keylen,
- LIBSSH2_KNOWNHOST_TYPE_PLAIN|
- LIBSSH2_KNOWNHOST_KEYENC_RAW|
- keybit,
- &host);
-#endif
infof(data, "SSH host check: %d, key: %s", keycheck,
(keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) ?
@@ -594,11 +544,11 @@ static CURLcode ssh_knownhost(struct Curl_easy *data)
switch(rc) {
default: /* unknown return codes will equal reject */
case CURLKHSTAT_REJECT:
- state(data, SSH_SESSION_FREE);
+ myssh_state(data, sshc, SSH_SESSION_FREE);
FALLTHROUGH();
case CURLKHSTAT_DEFER:
/* DEFER means bail out but keep the SSH_HOSTKEY state */
- result = sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
+ result = CURLE_PEER_FAILED_VERIFICATION;
break;
case CURLKHSTAT_FINE_REPLACE:
/* remove old host+key that does not match */
@@ -637,23 +587,19 @@ static CURLcode ssh_knownhost(struct Curl_easy *data)
break;
}
}
-#else /* HAVE_LIBSSH2_KNOWNHOST_API */
- (void)data;
-#endif
return result;
}
-static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
+static CURLcode ssh_check_fingerprint(struct Curl_easy *data,
+ struct ssh_conn *sshc)
{
- struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5];
const char *pubkey_sha256 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256];
infof(data, "SSH MD5 public key: %s",
- pubkey_md5 != NULL ? pubkey_md5 : "NULL");
+ pubkey_md5 != NULL ? pubkey_md5 : "NULL");
infof(data, "SSH SHA256 public key: %s",
- pubkey_sha256 != NULL ? pubkey_sha256 : "NULL");
+ pubkey_sha256 != NULL ? pubkey_sha256 : "NULL");
if(pubkey_sha256) {
const char *fingerprint = NULL;
@@ -682,25 +628,22 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
failf(data,
"Denied establishing ssh session: sha256 fingerprint "
"not available");
- state(data, SSH_SESSION_FREE);
- sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
- return sshc->actualcode;
+ myssh_state(data, sshc, SSH_SESSION_FREE);
+ return CURLE_PEER_FAILED_VERIFICATION;
}
/* The length of fingerprint is 32 bytes for SHA256.
* See libssh2_hostkey_hash documentation. */
- if(Curl_base64_encode(fingerprint, 32, &fingerprint_b64,
- &fingerprint_b64_len) != CURLE_OK) {
- state(data, SSH_SESSION_FREE);
- sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
- return sshc->actualcode;
+ if(curlx_base64_encode(fingerprint, 32, &fingerprint_b64,
+ &fingerprint_b64_len) != CURLE_OK) {
+ myssh_state(data, sshc, SSH_SESSION_FREE);
+ return CURLE_PEER_FAILED_VERIFICATION;
}
if(!fingerprint_b64) {
failf(data, "sha256 fingerprint could not be encoded");
- state(data, SSH_SESSION_FREE);
- sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
- return sshc->actualcode;
+ myssh_state(data, sshc, SSH_SESSION_FREE);
+ return CURLE_PEER_FAILED_VERIFICATION;
}
infof(data, "SSH SHA256 fingerprint: %s", fingerprint_b64);
@@ -725,9 +668,8 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
"Denied establishing ssh session: mismatch sha256 fingerprint. "
"Remote %s is not equal to %s", fingerprint_b64, pubkey_sha256);
free(fingerprint_b64);
- state(data, SSH_SESSION_FREE);
- sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
- return sshc->actualcode;
+ myssh_state(data, sshc, SSH_SESSION_FREE);
+ return CURLE_PEER_FAILED_VERIFICATION;
}
free(fingerprint_b64);
@@ -737,7 +679,7 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
if(pubkey_md5) {
char md5buffer[33];
- const char *fingerprint = NULL;
+ const char *fingerprint;
fingerprint = libssh2_hostkey_hash(sshc->ssh_session,
LIBSSH2_HOSTKEY_HASH_MD5);
@@ -765,9 +707,8 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
"Denied establishing ssh session: md5 fingerprint "
"not available");
}
- state(data, SSH_SESSION_FREE);
- sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
- return sshc->actualcode;
+ myssh_state(data, sshc, SSH_SESSION_FREE);
+ return CURLE_PEER_FAILED_VERIFICATION;
}
infof(data, "MD5 checksum match");
}
@@ -787,20 +728,18 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
(int)keytype, remotekey, keylen);
Curl_set_in_callback(data, FALSE);
if(rc!= CURLKHMATCH_OK) {
- state(data, SSH_SESSION_FREE);
- sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
- return sshc->actualcode;
+ myssh_state(data, sshc, SSH_SESSION_FREE);
+ return CURLE_PEER_FAILED_VERIFICATION;
}
}
else {
- state(data, SSH_SESSION_FREE);
- sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION;
- return sshc->actualcode;
+ myssh_state(data, sshc, SSH_SESSION_FREE);
+ return CURLE_PEER_FAILED_VERIFICATION;
}
return CURLE_OK;
}
else {
- return ssh_knownhost(data);
+ return ssh_knownhost(data, sshc);
}
}
else {
@@ -813,12 +752,11 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data)
* ssh_force_knownhost_key_type() will check the known hosts file and try to
* force a specific public key type from the server if an entry is found.
*/
-static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data)
+static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data,
+ struct ssh_conn *sshc)
{
CURLcode result = CURLE_OK;
-#ifdef HAVE_LIBSSH2_KNOWNHOST_API
-
#ifdef LIBSSH2_KNOWNHOST_KEY_ED25519
static const char * const hostkey_method_ssh_ed25519
= "ssh-ed25519";
@@ -844,14 +782,15 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data)
const char *hostkey_method = NULL;
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
struct libssh2_knownhost* store = NULL;
const char *kh_name_end = NULL;
size_t kh_name_size = 0;
int port = 0;
bool found = FALSE;
- if(sshc->kh && !data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) {
+ if(sshc->kh &&
+ !data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] &&
+ !data->set.str[STRING_SSH_HOST_PUBLIC_KEY_SHA256]) {
/* lets try to find our host in the known hosts file */
while(!libssh2_knownhost_get(sshc->kh, &store, store)) {
/* For non-standard ports, the name will be enclosed in */
@@ -914,12 +853,10 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data)
break;
#endif
case LIBSSH2_KNOWNHOST_KEY_SSHRSA:
-#ifdef HAVE_LIBSSH2_VERSION
if(libssh2_version(0x010900))
/* since 1.9.0 libssh2_session_method_pref() works as expected */
hostkey_method = hostkey_method_ssh_rsa_all;
else
-#endif
/* old libssh2 which cannot correctly remove unsupported methods due
* to bug in src/kex.c or does not support the new methods anyways.
*/
@@ -954,8 +891,6 @@ static CURLcode ssh_force_knownhost_key_type(struct Curl_easy *data)
}
}
-#endif /* HAVE_LIBSSH2_KNOWNHOST_API */
-
return result;
}
@@ -990,7 +925,7 @@ static CURLcode sftp_quote(struct Curl_easy *data,
char *tmp = aprintf("257 \"%s\" is current directory.\n", sshp->path);
if(!tmp)
return CURLE_OUT_OF_MEMORY;
- Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"PWD\n", 4);
+ Curl_debug(data, CURLINFO_HEADER_OUT, "PWD\n", 4);
Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp));
/* this sends an FTP-like "header" to the header callback so that the
@@ -999,7 +934,7 @@ static CURLcode sftp_quote(struct Curl_easy *data,
result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp));
free(tmp);
if(!result)
- state(data, SSH_SFTP_NEXT_QUOTE);
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
return result;
}
@@ -1029,11 +964,11 @@ static CURLcode sftp_quote(struct Curl_easy *data,
* Instead, we scan for commands used by OpenSSH's sftp program and call the
* appropriate libssh2 functions.
*/
- if(strncasecompare(cmd, "chgrp ", 6) ||
- strncasecompare(cmd, "chmod ", 6) ||
- strncasecompare(cmd, "chown ", 6) ||
- strncasecompare(cmd, "atime ", 6) ||
- strncasecompare(cmd, "mtime ", 6)) {
+ if(!strncmp(cmd, "chgrp ", 6) ||
+ !strncmp(cmd, "chmod ", 6) ||
+ !strncmp(cmd, "chown ", 6) ||
+ !strncmp(cmd, "atime ", 6) ||
+ !strncmp(cmd, "mtime ", 6)) {
/* attribute change */
/* sshc->quote_path1 contains the mode to set */
@@ -1046,11 +981,11 @@ static CURLcode sftp_quote(struct Curl_easy *data,
return result;
}
memset(&sshp->quote_attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
- state(data, SSH_SFTP_QUOTE_STAT);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_STAT);
return result;
}
- if(strncasecompare(cmd, "ln ", 3) ||
- strncasecompare(cmd, "symlink ", 8)) {
+ if(!strncmp(cmd, "ln ", 3) ||
+ !strncmp(cmd, "symlink ", 8)) {
/* symbolic linking */
/* sshc->quote_path1 is the source */
/* get the destination */
@@ -1061,15 +996,15 @@ static CURLcode sftp_quote(struct Curl_easy *data,
Curl_safefree(sshc->quote_path1);
return result;
}
- state(data, SSH_SFTP_QUOTE_SYMLINK);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_SYMLINK);
return result;
}
- else if(strncasecompare(cmd, "mkdir ", 6)) {
+ else if(!strncmp(cmd, "mkdir ", 6)) {
/* create dir */
- state(data, SSH_SFTP_QUOTE_MKDIR);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_MKDIR);
return result;
}
- else if(strncasecompare(cmd, "rename ", 7)) {
+ else if(!strncmp(cmd, "rename ", 7)) {
/* rename file */
/* first param is the source path */
/* second param is the dest. path */
@@ -1080,24 +1015,22 @@ static CURLcode sftp_quote(struct Curl_easy *data,
Curl_safefree(sshc->quote_path1);
return result;
}
- state(data, SSH_SFTP_QUOTE_RENAME);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_RENAME);
return result;
}
- else if(strncasecompare(cmd, "rmdir ", 6)) {
+ else if(!strncmp(cmd, "rmdir ", 6)) {
/* delete dir */
- state(data, SSH_SFTP_QUOTE_RMDIR);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_RMDIR);
return result;
}
- else if(strncasecompare(cmd, "rm ", 3)) {
- state(data, SSH_SFTP_QUOTE_UNLINK);
+ else if(!strncmp(cmd, "rm ", 3)) {
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_UNLINK);
return result;
}
-#ifdef HAS_STATVFS_SUPPORT
- else if(strncasecompare(cmd, "statvfs ", 8)) {
- state(data, SSH_SFTP_QUOTE_STATVFS);
+ else if(!strncmp(cmd, "statvfs ", 8)) {
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_STATVFS);
return result;
}
-#endif
failf(data, "Unknown SFTP command");
Curl_safefree(sshc->quote_path1);
@@ -1161,6 +1094,7 @@ sftp_upload_init(struct Curl_easy *data,
LIBSSH2_SFTP_OPENFILE);
if(!sshc->sftp_handle) {
+ CURLcode result;
unsigned long sftperr;
int rc = libssh2_session_last_errno(sshc->ssh_session);
@@ -1177,12 +1111,10 @@ sftp_upload_init(struct Curl_easy *data,
sftperr = LIBSSH2_FX_OK; /* not an sftp error at all */
if(sshc->secondCreateDirs) {
- state(data, SSH_SFTP_CLOSE);
- sshc->actualcode = sftperr != LIBSSH2_FX_OK ?
- sftp_libssh2_error_to_CURLE(sftperr) : CURLE_SSH;
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
failf(data, "Creating the dir/file failed: %s",
sftp_libssh2_strerror(sftperr));
- return CURLE_OK;
+ return sftp_libssh2_error_to_CURLE(sftperr);
}
if(((sftperr == LIBSSH2_FX_NO_SUCH_FILE) ||
(sftperr == LIBSSH2_FX_FAILURE) ||
@@ -1191,24 +1123,23 @@ sftp_upload_init(struct Curl_easy *data,
(strlen(sshp->path) > 1))) {
/* try to create the path remotely */
sshc->secondCreateDirs = 1;
- state(data, SSH_SFTP_CREATE_DIRS_INIT);
+ myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS_INIT);
return CURLE_OK;
}
- state(data, SSH_SFTP_CLOSE);
- sshc->actualcode = sftperr != LIBSSH2_FX_OK ?
- sftp_libssh2_error_to_CURLE(sftperr) : CURLE_SSH;
- if(!sshc->actualcode) {
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ result = sftp_libssh2_error_to_CURLE(sftperr);
+ if(!result) {
/* Sometimes, for some reason libssh2_sftp_last_error() returns zero
even though libssh2_sftp_open() failed previously! We need to
work around that! */
- sshc->actualcode = CURLE_SSH;
+ result = CURLE_SSH;
sftperr = LIBSSH2_FX_OK;
}
failf(data, "Upload failed: %s (%lu/%d)",
sftperr != LIBSSH2_FX_OK ?
sftp_libssh2_strerror(sftperr) : "ssh error",
sftperr, rc);
- return sshc->actualcode;
+ return result;
}
/* If we have a restart point then we need to seek to the correct
@@ -1262,7 +1193,8 @@ sftp_upload_init(struct Curl_easy *data,
Curl_pgrsSetUploadSize(data, data->state.infilesize);
}
- SFTP_SEEK(sshc->sftp_handle, data->state.resume_from);
+ libssh2_sftp_seek64(sshc->sftp_handle,
+ (libssh2_uint64_t)data->state.resume_from);
}
if(data->state.infilesize > 0) {
data->req.size = data->state.infilesize;
@@ -1288,13 +1220,15 @@ sftp_upload_init(struct Curl_easy *data,
timeout here */
Curl_expire(data, 0, EXPIRE_RUN_NOW);
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
return CURLE_OK;
}
-static CURLcode
-sftp_pkey_init(struct Curl_easy *data,
- struct ssh_conn *sshc)
+/* make sure that this does not collide with an actual libssh2 error code */
+#define ERROR_LIBBSH2 1
+
+static CURLcode ssh_state_pkey_init(struct Curl_easy *data,
+ struct ssh_conn *sshc)
{
/*
* Check the supported auth types in the order I feel is most secure
@@ -1323,7 +1257,7 @@ sftp_pkey_init(struct Curl_easy *data,
if(!sshc->rsa)
out_of_memory = TRUE;
else if(stat(sshc->rsa, &sbuf)) {
- Curl_safefree(sshc->rsa);
+ free(sshc->rsa);
sshc->rsa = aprintf("%s/.ssh/id_dsa", home);
if(!sshc->rsa)
out_of_memory = TRUE;
@@ -1337,10 +1271,10 @@ sftp_pkey_init(struct Curl_easy *data,
/* Nothing found; try the current dir. */
sshc->rsa = strdup("id_rsa");
if(sshc->rsa && stat(sshc->rsa, &sbuf)) {
- Curl_safefree(sshc->rsa);
+ free(sshc->rsa);
sshc->rsa = strdup("id_dsa");
if(sshc->rsa && stat(sshc->rsa, &sbuf)) {
- Curl_safefree(sshc->rsa);
+ free(sshc->rsa);
/* Out of guesses. Set to the empty string to avoid
* surprising info messages. */
sshc->rsa = strdup("");
@@ -1365,8 +1299,7 @@ sftp_pkey_init(struct Curl_easy *data,
if(out_of_memory || !sshc->rsa) {
Curl_safefree(sshc->rsa);
Curl_safefree(sshc->rsa_pub);
- state(data, SSH_SESSION_FREE);
- sshc->actualcode = CURLE_OUT_OF_MEMORY;
+ myssh_state(data, sshc, SSH_SESSION_FREE);
return CURLE_OUT_OF_MEMORY;
}
@@ -1378,12 +1311,12 @@ sftp_pkey_init(struct Curl_easy *data,
infof(data, "Using SSH public key file '%s'", sshc->rsa_pub);
infof(data, "Using SSH private key file '%s'", sshc->rsa);
- state(data, SSH_AUTH_PKEY);
+ myssh_state(data, sshc, SSH_AUTH_PKEY);
}
else {
- state(data, SSH_AUTH_PASS_INIT);
+ myssh_state(data, sshc, SSH_AUTH_PASS_INIT);
}
- return CURLE_OK;
+ return 0;
}
static CURLcode
@@ -1405,7 +1338,7 @@ sftp_quote_stat(struct Curl_easy *data,
sshc->acceptfail = TRUE;
}
- if(!strncasecompare(cmd, "chmod", 5)) {
+ if(!!strncmp(cmd, "chmod", 5)) {
/* Since chown and chgrp only set owner OR group but libssh2 wants to set
* them both at once, we need to obtain the current ownership first. This
* takes an extra protocol round trip.
@@ -1427,8 +1360,11 @@ sftp_quote_stat(struct Curl_easy *data,
}
/* Now set the new attributes... */
- if(strncasecompare(cmd, "chgrp", 5)) {
- sshp->quote_attrs.gid = strtoul(sshc->quote_path1, NULL, 10);
+ if(!strncmp(cmd, "chgrp", 5)) {
+ const char *p = sshc->quote_path1;
+ curl_off_t gid;
+ (void)curlx_str_number(&p, &gid, ULONG_MAX);
+ sshp->quote_attrs.gid = (unsigned long)gid;
sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID;
if(sshp->quote_attrs.gid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
!sshc->acceptfail) {
@@ -1436,18 +1372,23 @@ sftp_quote_stat(struct Curl_easy *data,
goto fail;
}
}
- else if(strncasecompare(cmd, "chmod", 5)) {
- sshp->quote_attrs.permissions = strtoul(sshc->quote_path1, NULL, 8);
- sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS;
+ else if(!strncmp(cmd, "chmod", 5)) {
+ curl_off_t perms;
+ const char *p = sshc->quote_path1;
/* permissions are octal */
- if(sshp->quote_attrs.permissions == 0 &&
- !ISDIGIT(sshc->quote_path1[0])) {
+ if(curlx_str_octal(&p, &perms, 07777)) {
failf(data, "Syntax error: chmod permissions not a number");
goto fail;
}
+
+ sshp->quote_attrs.permissions = (unsigned long)perms;
+ sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS;
}
- else if(strncasecompare(cmd, "chown", 5)) {
- sshp->quote_attrs.uid = strtoul(sshc->quote_path1, NULL, 10);
+ else if(!strncmp(cmd, "chown", 5)) {
+ const char *p = sshc->quote_path1;
+ curl_off_t uid;
+ (void)curlx_str_number(&p, &uid, ULONG_MAX);
+ sshp->quote_attrs.uid = (unsigned long)uid;
sshp->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID;
if(sshp->quote_attrs.uid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
!sshc->acceptfail) {
@@ -1455,8 +1396,8 @@ sftp_quote_stat(struct Curl_easy *data,
goto fail;
}
}
- else if(strncasecompare(cmd, "atime", 5) ||
- strncasecompare(cmd, "mtime", 5)) {
+ else if(!strncmp(cmd, "atime", 5) ||
+ !strncmp(cmd, "mtime", 5)) {
time_t date = Curl_getdate_capped(sshc->quote_path1);
bool fail = FALSE;
@@ -1473,7 +1414,7 @@ sftp_quote_stat(struct Curl_easy *data,
#endif
if(fail)
goto fail;
- if(strncasecompare(cmd, "atime", 5))
+ if(!strncmp(cmd, "atime", 5))
sshp->quote_attrs.atime = (unsigned long)date;
else /* mtime */
sshp->quote_attrs.mtime = (unsigned long)date;
@@ -1482,7 +1423,7 @@ sftp_quote_stat(struct Curl_easy *data,
}
/* Now send the completed structure... */
- state(data, SSH_SFTP_QUOTE_SETSTAT);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_SETSTAT);
return CURLE_OK;
fail:
Curl_safefree(sshc->quote_path1);
@@ -1526,20 +1467,19 @@ sftp_download_stat(struct Curl_easy *data,
}
if(data->state.use_range) {
curl_off_t from, to;
- char *ptr;
- char *ptr2;
- CURLofft to_t;
- CURLofft from_t;
+ const char *p = data->state.range;
+ int to_t, from_t;
- from_t = curlx_strtoofft(data->state.range, &ptr, 10, &from);
- if(from_t == CURL_OFFT_FLOW)
+ from_t = curlx_str_number(&p, &from, CURL_OFF_T_MAX);
+ if(from_t == STRE_OVERFLOW)
return CURLE_RANGE_ERROR;
- while(*ptr && (ISBLANK(*ptr) || (*ptr == '-')))
- ptr++;
- to_t = curlx_strtoofft(ptr, &ptr2, 10, &to);
- if(to_t == CURL_OFFT_FLOW)
+ curlx_str_passblanks(&p);
+ (void)curlx_str_single(&p, '-');
+
+ to_t = curlx_str_numblanks(&p, &to);
+ if(to_t == STRE_OVERFLOW)
return CURLE_RANGE_ERROR;
- if((to_t == CURL_OFFT_INVAL) /* no "to" value given */
+ if((to_t == STRE_NO_NUM) /* no "to" value given */
|| (to >= size)) {
to = size - 1;
}
@@ -1563,7 +1503,7 @@ sftp_download_stat(struct Curl_easy *data,
size = to - from + 1;
}
- SFTP_SEEK(sshc->sftp_handle, from);
+ libssh2_sftp_seek64(sshc->sftp_handle, (libssh2_uint64_t)from);
}
data->req.size = size;
data->req.maxdownload = size;
@@ -1596,7 +1536,8 @@ sftp_download_stat(struct Curl_easy *data,
data->req.maxdownload = attrs.filesize - data->state.resume_from;
Curl_pgrsSetDownloadSize(data,
attrs.filesize - data->state.resume_from);
- SFTP_SEEK(sshc->sftp_handle, data->state.resume_from);
+ libssh2_sftp_seek64(sshc->sftp_handle,
+ (libssh2_uint64_t)data->state.resume_from);
}
/* Setup the actual download */
@@ -1604,7 +1545,7 @@ sftp_download_stat(struct Curl_easy *data,
/* no data to transfer */
Curl_xfer_setup_nop(data);
infof(data, "File already completely downloaded");
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
return CURLE_OK;
}
Curl_xfer_setup1(data, CURL_XFER_RECV, data->req.size, FALSE);
@@ -1616,7 +1557,7 @@ sftp_download_stat(struct Curl_easy *data,
out writableable as the underlying libssh2 recv function will deal
with both accordingly */
data->state.select_bits = CURL_CSELECT_IN;
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
return CURLE_OK;
}
@@ -1644,44 +1585,1107 @@ static CURLcode sftp_readdir(struct Curl_easy *data,
sshp->readdir_filename,
readdir_len);
if(!result)
- result = Curl_client_write(data, CLIENTWRITE_BODY,
- (char *)"\n", 1);
+ result = Curl_client_write(data, CLIENTWRITE_BODY, "\n", 1);
if(result)
return result;
}
else {
- result = Curl_dyn_add(&sshp->readdir, sshp->readdir_longentry);
+ result = curlx_dyn_add(&sshp->readdir, sshp->readdir_longentry);
if(!result) {
if((sshp->readdir_attrs.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) &&
((sshp->readdir_attrs.permissions & LIBSSH2_SFTP_S_IFMT) ==
LIBSSH2_SFTP_S_IFLNK)) {
- Curl_dyn_init(&sshp->readdir_link, CURL_PATH_MAX);
- result = Curl_dyn_addf(&sshp->readdir_link, "%s%s", sshp->path,
- sshp->readdir_filename);
- state(data, SSH_SFTP_READDIR_LINK);
+ result = curlx_dyn_addf(&sshp->readdir_link, "%s%s", sshp->path,
+ sshp->readdir_filename);
+ myssh_state(data, sshc, SSH_SFTP_READDIR_LINK);
}
else {
- state(data, SSH_SFTP_READDIR_BOTTOM);
+ myssh_state(data, sshc, SSH_SFTP_READDIR_BOTTOM);
}
}
return result;
}
}
- else if(rc == 0) {
- state(data, SSH_SFTP_READDIR_DONE);
+ else if(!rc) {
+ myssh_state(data, sshc, SSH_SFTP_READDIR_DONE);
}
- else if(rc < 0) {
+ else {
unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
- result = sftp_libssh2_error_to_CURLE(sftperr);
- sshc->actualcode = result ? result : CURLE_SSH;
+ result = sftperr ? sftp_libssh2_error_to_CURLE(sftperr) : CURLE_SSH;
failf(data, "Could not open remote file for reading: %s :: %d",
sftp_libssh2_strerror(sftperr),
libssh2_session_last_errno(sshc->ssh_session));
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
}
return result;
}
+
+static CURLcode ssh_state_init(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ CURLcode result;
+ sshc->secondCreateDirs = 0;
+ sshc->nextstate = SSH_NO_STATE;
+
+ /* Set libssh2 to non-blocking, since everything internally is
+ non-blocking */
+ libssh2_session_set_blocking(sshc->ssh_session, 0);
+
+ result = ssh_force_knownhost_key_type(data, sshc);
+ if(result)
+ myssh_state(data, sshc, SSH_SESSION_FREE);
+ else
+ myssh_state(data, sshc, SSH_S_STARTUP);
+ return result;
+}
+
+static CURLcode ssh_state_startup(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ struct connectdata *conn = data->conn;
+ int rc = libssh2_session_handshake(sshc->ssh_session,
+ conn->sock[FIRSTSOCKET]);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0);
+ failf(data, "Failure establishing ssh session: %d, %s", rc, err_msg);
+
+ myssh_state(data, sshc, SSH_SESSION_FREE);
+ return CURLE_FAILED_INIT;
+ }
+
+ myssh_state(data, sshc, SSH_HOSTKEY);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_hostkey(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ /*
+ * Before we authenticate we should check the hostkey's fingerprint
+ * against our known hosts. How that is handled (reading from file,
+ * whatever) is up to us.
+ */
+ CURLcode result = ssh_check_fingerprint(data, sshc);
+ if(!result)
+ myssh_state(data, sshc, SSH_AUTHLIST);
+ return result;
+}
+
+static CURLcode ssh_state_authlist(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ /*
+ * Figure out authentication methods
+ * NB: As soon as we have provided a username to an openssh server we
+ * must never change it later. Thus, always specify the correct username
+ * here, even though the libssh2 docs kind of indicate that it should be
+ * possible to get a 'generic' list (not user-specific) of authentication
+ * methods, presumably with a blank username. That will not work in my
+ * experience.
+ * So always specify it here.
+ */
+ struct connectdata *conn = data->conn;
+ sshc->authlist = libssh2_userauth_list(sshc->ssh_session,
+ conn->user,
+ curlx_uztoui(strlen(conn->user)));
+
+ if(!sshc->authlist) {
+ int rc;
+ if(libssh2_userauth_authenticated(sshc->ssh_session)) {
+ sshc->authed = TRUE;
+ infof(data, "SSH user accepted with no authentication");
+ myssh_state(data, sshc, SSH_AUTH_DONE);
+ return CURLE_OK;
+ }
+ rc = libssh2_session_last_errno(sshc->ssh_session);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ myssh_state(data, sshc, SSH_SESSION_FREE);
+ return libssh2_session_error_to_CURLE(rc);
+ }
+ infof(data, "SSH authentication methods available: %s",
+ sshc->authlist);
+
+ myssh_state(data, sshc, SSH_AUTH_PKEY_INIT);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_auth_pkey(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ /* The function below checks if the files exists, no need to stat() here.
+ */
+ struct connectdata *conn = data->conn;
+ int rc =
+ libssh2_userauth_publickey_fromfile_ex(sshc->ssh_session,
+ conn->user,
+ curlx_uztoui(
+ strlen(conn->user)),
+ sshc->rsa_pub,
+ sshc->rsa, sshc->passphrase);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ Curl_safefree(sshc->rsa_pub);
+ Curl_safefree(sshc->rsa);
+
+ if(rc == 0) {
+ sshc->authed = TRUE;
+ infof(data, "Initialized SSH public key authentication");
+ myssh_state(data, sshc, SSH_AUTH_DONE);
+ }
+ else {
+ char *err_msg = NULL;
+ char unknown[] = "Reason unknown (-1)";
+ if(rc == -1) {
+ /* No error message has been set and the last set error message, if
+ any, is from a previous error so ignore it. #11837 */
+ err_msg = unknown;
+ }
+ else {
+ (void)libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0);
+ }
+ infof(data, "SSH public key authentication failed: %s", err_msg);
+ myssh_state(data, sshc, SSH_AUTH_PASS_INIT);
+ }
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_auth_pass_init(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ if((data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD) &&
+ (strstr(sshc->authlist, "password") != NULL)) {
+ myssh_state(data, sshc, SSH_AUTH_PASS);
+ }
+ else {
+ myssh_state(data, sshc, SSH_AUTH_HOST_INIT);
+ }
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_auth_pass(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ struct connectdata *conn = data->conn;
+ int rc =
+ libssh2_userauth_password_ex(sshc->ssh_session, conn->user,
+ curlx_uztoui(strlen(conn->user)),
+ conn->passwd,
+ curlx_uztoui(strlen(conn->passwd)),
+ NULL);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return CURLE_AGAIN;
+ }
+ if(rc == 0) {
+ sshc->authed = TRUE;
+ infof(data, "Initialized password authentication");
+ myssh_state(data, sshc, SSH_AUTH_DONE);
+ }
+ else {
+ myssh_state(data, sshc, SSH_AUTH_HOST_INIT);
+ }
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_auth_host_init(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ if((data->set.ssh_auth_types & CURLSSH_AUTH_HOST) &&
+ (strstr(sshc->authlist, "hostbased") != NULL)) {
+ myssh_state(data, sshc, SSH_AUTH_HOST);
+ }
+ else {
+ myssh_state(data, sshc, SSH_AUTH_AGENT_INIT);
+ }
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_auth_agent_init(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ int rc = 0;
+ if((data->set.ssh_auth_types & CURLSSH_AUTH_AGENT)
+ && (strstr(sshc->authlist, "publickey") != NULL)) {
+
+ /* Connect to the ssh-agent */
+ /* The agent could be shared by a curl thread i believe
+ but nothing obvious as keys can be added/removed at any time */
+ if(!sshc->ssh_agent) {
+ sshc->ssh_agent = libssh2_agent_init(sshc->ssh_session);
+ if(!sshc->ssh_agent) {
+ infof(data, "Could not create agent object");
+
+ myssh_state(data, sshc, SSH_AUTH_KEY_INIT);
+ return CURLE_OK;
+ }
+ }
+
+ rc = libssh2_agent_connect(sshc->ssh_agent);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+ if(rc < 0) {
+ infof(data, "Failure connecting to agent");
+ myssh_state(data, sshc, SSH_AUTH_KEY_INIT);
+ }
+ else {
+ myssh_state(data, sshc, SSH_AUTH_AGENT_LIST);
+ }
+ }
+ else
+ myssh_state(data, sshc, SSH_AUTH_KEY_INIT);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_auth_agent_list(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ int rc = libssh2_agent_list_identities(sshc->ssh_agent);
+
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+ if(rc < 0) {
+ infof(data, "Failure requesting identities to agent");
+ myssh_state(data, sshc, SSH_AUTH_KEY_INIT);
+ }
+ else {
+ myssh_state(data, sshc, SSH_AUTH_AGENT);
+ sshc->sshagent_prev_identity = NULL;
+ }
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_auth_agent(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ /* as prev_identity evolves only after an identity user auth finished we
+ can safely request it again as long as EAGAIN is returned here or by
+ libssh2_agent_userauth */
+ int rc = libssh2_agent_get_identity(sshc->ssh_agent,
+ &sshc->sshagent_identity,
+ sshc->sshagent_prev_identity);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc == 0) {
+ struct connectdata *conn = data->conn;
+ rc = libssh2_agent_userauth(sshc->ssh_agent, conn->user,
+ sshc->sshagent_identity);
+
+ if(rc < 0) {
+ if(rc != LIBSSH2_ERROR_EAGAIN) {
+ /* tried and failed? go to next identity */
+ sshc->sshagent_prev_identity = sshc->sshagent_identity;
+ }
+ return CURLE_OK;
+ }
+ }
+
+ if(rc < 0)
+ infof(data, "Failure requesting identities to agent");
+ else if(rc == 1)
+ infof(data, "No identity would match");
+
+ if(rc == LIBSSH2_ERROR_NONE) {
+ sshc->authed = TRUE;
+ infof(data, "Agent based authentication successful");
+ myssh_state(data, sshc, SSH_AUTH_DONE);
+ }
+ else {
+ myssh_state(data, sshc, SSH_AUTH_KEY_INIT);
+ }
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_auth_key_init(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ if((data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD)
+ && (strstr(sshc->authlist, "keyboard-interactive") != NULL)) {
+ myssh_state(data, sshc, SSH_AUTH_KEY);
+ }
+ else {
+ myssh_state(data, sshc, SSH_AUTH_DONE);
+ }
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_auth_key(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ /* Authentication failed. Continue with keyboard-interactive now. */
+ struct connectdata *conn = data->conn;
+ int rc =
+ libssh2_userauth_keyboard_interactive_ex(sshc->ssh_session,
+ conn->user,
+ curlx_uztoui(
+ strlen(conn->user)),
+ &kbd_callback);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc == 0) {
+ sshc->authed = TRUE;
+ infof(data, "Initialized keyboard interactive authentication");
+ myssh_state(data, sshc, SSH_AUTH_DONE);
+ return CURLE_OK;
+ }
+ return CURLE_LOGIN_DENIED;
+}
+
+static CURLcode ssh_state_auth_done(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ struct connectdata *conn = data->conn;
+ if(!sshc->authed) {
+ failf(data, "Authentication failure");
+ myssh_state(data, sshc, SSH_SESSION_FREE);
+ return CURLE_LOGIN_DENIED;
+ }
+
+ /*
+ * At this point we have an authenticated ssh session.
+ */
+ infof(data, "Authentication complete");
+
+ Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSH is connected */
+
+ conn->sockfd = conn->sock[FIRSTSOCKET];
+ conn->writesockfd = CURL_SOCKET_BAD;
+
+ if(conn->handler->protocol == CURLPROTO_SFTP) {
+ myssh_state(data, sshc, SSH_SFTP_INIT);
+ return CURLE_OK;
+ }
+ infof(data, "SSH CONNECT phase done");
+ myssh_state(data, sshc, SSH_STOP);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_init(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ /*
+ * Start the libssh2 sftp session
+ */
+ sshc->sftp_session = libssh2_sftp_init(sshc->ssh_session);
+ if(!sshc->sftp_session) {
+ char *err_msg = NULL;
+ if(libssh2_session_last_errno(sshc->ssh_session) ==
+ LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ (void)libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0);
+ failf(data, "Failure initializing sftp session: %s", err_msg);
+ myssh_state(data, sshc, SSH_SESSION_FREE);
+ return CURLE_FAILED_INIT;
+ }
+ myssh_state(data, sshc, SSH_SFTP_REALPATH);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_realpath(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp)
+{
+ /*
+ * Get the "home" directory
+ */
+ int rc = libssh2_sftp_symlink_ex(sshc->sftp_session,
+ ".", curlx_uztoui(strlen(".")),
+ sshp->readdir_filename, CURL_PATH_MAX,
+ LIBSSH2_SFTP_REALPATH);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc > 0) {
+ /* It seems that this string is not always null-terminated */
+ sshp->readdir_filename[rc] = '\0';
+ free(sshc->homedir);
+ sshc->homedir = strdup(sshp->readdir_filename);
+ if(!sshc->homedir) {
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ free(data->state.most_recent_ftp_entrypath);
+ data->state.most_recent_ftp_entrypath = strdup(sshc->homedir);
+ if(!data->state.most_recent_ftp_entrypath)
+ return CURLE_OUT_OF_MEMORY;
+ }
+ else {
+ /* Return the error type */
+ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ CURLcode result;
+ if(sftperr)
+ result = sftp_libssh2_error_to_CURLE(sftperr);
+ else
+ /* in this case, the error was not in the SFTP level but for example
+ a time-out or similar */
+ result = CURLE_SSH;
+ DEBUGF(infof(data, "error = %lu makes libcurl = %d",
+ sftperr, (int)result));
+ myssh_state(data, sshc, SSH_STOP);
+ return result;
+ }
+
+ /* This is the last step in the SFTP connect phase. Do note that while
+ we get the homedir here, we get the "workingpath" in the DO action
+ since the homedir will remain the same between request but the
+ working path will not. */
+ DEBUGF(infof(data, "SSH CONNECT phase done"));
+ myssh_state(data, sshc, SSH_STOP);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_quote_init(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp)
+{
+ CURLcode result = Curl_getworkingpath(data, sshc->homedir, &sshp->path);
+ if(result) {
+ myssh_state(data, sshc, SSH_STOP);
+ return result;
+ }
+
+ if(data->set.quote) {
+ infof(data, "Sending quote commands");
+ sshc->quote_item = data->set.quote;
+ myssh_state(data, sshc, SSH_SFTP_QUOTE);
+ }
+ else {
+ myssh_state(data, sshc, SSH_SFTP_GETINFO);
+ }
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_postquote_init(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ if(data->set.postquote) {
+ infof(data, "Sending quote commands");
+ sshc->quote_item = data->set.postquote;
+ myssh_state(data, sshc, SSH_SFTP_QUOTE);
+ }
+ else {
+ myssh_state(data, sshc, SSH_STOP);
+ }
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_quote(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp)
+{
+ /* Send quote commands */
+ CURLcode result = sftp_quote(data, sshc, sshp);
+ if(result) {
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ }
+ return result;
+}
+
+static CURLcode ssh_state_sftp_next_quote(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+
+ sshc->quote_item = sshc->quote_item->next;
+
+ if(sshc->quote_item) {
+ myssh_state(data, sshc, SSH_SFTP_QUOTE);
+ }
+ else {
+ if(sshc->nextstate != SSH_NO_STATE) {
+ myssh_state(data, sshc, sshc->nextstate);
+ sshc->nextstate = SSH_NO_STATE;
+ }
+ else {
+ myssh_state(data, sshc, SSH_SFTP_GETINFO);
+ }
+ }
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_quote_stat(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp,
+ bool *blockp)
+{
+ CURLcode result = sftp_quote_stat(data, sshc, sshp, blockp);
+ if(result) {
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ }
+ return result;
+}
+
+static CURLcode ssh_state_sftp_quote_setstat(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp)
+{
+ int rc =
+ libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2,
+ curlx_uztoui(strlen(sshc->quote_path2)),
+ LIBSSH2_SFTP_SETSTAT,
+ &sshp->quote_attrs);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc && !sshc->acceptfail) {
+ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ failf(data, "Attempt to set SFTP stats failed: %s",
+ sftp_libssh2_strerror(sftperr));
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ return CURLE_QUOTE_ERROR;
+ }
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_quote_symlink(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ int rc =
+ libssh2_sftp_symlink_ex(sshc->sftp_session, sshc->quote_path1,
+ curlx_uztoui(strlen(sshc->quote_path1)),
+ sshc->quote_path2,
+ curlx_uztoui(strlen(sshc->quote_path2)),
+ LIBSSH2_SFTP_SYMLINK);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc && !sshc->acceptfail) {
+ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ failf(data, "symlink command failed: %s",
+ sftp_libssh2_strerror(sftperr));
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ return CURLE_QUOTE_ERROR;
+ }
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_quote_mkdir(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ int rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshc->quote_path1,
+ curlx_uztoui(strlen(sshc->quote_path1)),
+ (long)data->set.new_directory_perms);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc && !sshc->acceptfail) {
+ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ Curl_safefree(sshc->quote_path1);
+ failf(data, "mkdir command failed: %s",
+ sftp_libssh2_strerror(sftperr));
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ return CURLE_QUOTE_ERROR;
+ }
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_quote_rename(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ int rc =
+ libssh2_sftp_rename_ex(sshc->sftp_session, sshc->quote_path1,
+ curlx_uztoui(strlen(sshc->quote_path1)),
+ sshc->quote_path2,
+ curlx_uztoui(strlen(sshc->quote_path2)),
+ LIBSSH2_SFTP_RENAME_OVERWRITE |
+ LIBSSH2_SFTP_RENAME_ATOMIC |
+ LIBSSH2_SFTP_RENAME_NATIVE);
+
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc && !sshc->acceptfail) {
+ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ failf(data, "rename command failed: %s",
+ sftp_libssh2_strerror(sftperr));
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ return CURLE_QUOTE_ERROR;
+ }
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_quote_rmdir(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ int rc = libssh2_sftp_rmdir_ex(sshc->sftp_session, sshc->quote_path1,
+ curlx_uztoui(strlen(sshc->quote_path1)));
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc && !sshc->acceptfail) {
+ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ Curl_safefree(sshc->quote_path1);
+ failf(data, "rmdir command failed: %s",
+ sftp_libssh2_strerror(sftperr));
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ return CURLE_QUOTE_ERROR;
+ }
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_quote_unlink(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ int rc = libssh2_sftp_unlink_ex(sshc->sftp_session, sshc->quote_path1,
+ curlx_uztoui(strlen(sshc->quote_path1)));
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc && !sshc->acceptfail) {
+ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ Curl_safefree(sshc->quote_path1);
+ failf(data, "rm command failed: %s", sftp_libssh2_strerror(sftperr));
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ return CURLE_QUOTE_ERROR;
+ }
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_quote_statvfs(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ LIBSSH2_SFTP_STATVFS statvfs;
+ int rc = libssh2_sftp_statvfs(sshc->sftp_session, sshc->quote_path1,
+ curlx_uztoui(strlen(sshc->quote_path1)),
+ &statvfs);
+
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc && !sshc->acceptfail) {
+ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ Curl_safefree(sshc->quote_path1);
+ failf(data, "statvfs command failed: %s",
+ sftp_libssh2_strerror(sftperr));
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ return CURLE_QUOTE_ERROR;
+ }
+ else if(rc == 0) {
+#ifdef _MSC_VER
+#define CURL_LIBSSH2_VFS_SIZE_MASK "I64u"
+#else
+#define CURL_LIBSSH2_VFS_SIZE_MASK "llu"
+#endif
+ CURLcode result;
+ char *tmp = aprintf("statvfs:\n"
+ "f_bsize: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_frsize: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_blocks: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_bfree: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_bavail: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_files: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_ffree: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_favail: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_fsid: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_flag: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
+ "f_namemax: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n",
+ statvfs.f_bsize, statvfs.f_frsize,
+ statvfs.f_blocks, statvfs.f_bfree,
+ statvfs.f_bavail, statvfs.f_files,
+ statvfs.f_ffree, statvfs.f_favail,
+ statvfs.f_fsid, statvfs.f_flag,
+ statvfs.f_namemax);
+ if(!tmp) {
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp));
+ free(tmp);
+ if(result) {
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ sshc->nextstate = SSH_NO_STATE;
+ return result;
+ }
+ }
+ myssh_state(data, sshc, SSH_SFTP_NEXT_QUOTE);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_create_dirs_mkdir(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp)
+{
+ /* 'mode' - parameter is preliminary - default to 0644 */
+ int rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshp->path,
+ curlx_uztoui(strlen(sshp->path)),
+ (long)data->set.new_directory_perms);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ *sshc->slash_pos = '/';
+ ++sshc->slash_pos;
+ if(rc < 0) {
+ /*
+ * Abort if failure was not that the dir already exists or the
+ * permission was denied (creation might succeed further down the
+ * path) - retry on unspecific FAILURE also
+ */
+ unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ if((sftperr != LIBSSH2_FX_FILE_ALREADY_EXISTS) &&
+ (sftperr != LIBSSH2_FX_FAILURE) &&
+ (sftperr != LIBSSH2_FX_PERMISSION_DENIED)) {
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ return sftp_libssh2_error_to_CURLE(sftperr);
+ }
+ }
+ myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_readdir_init(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp)
+{
+ Curl_pgrsSetDownloadSize(data, -1);
+ if(data->req.no_body) {
+ myssh_state(data, sshc, SSH_STOP);
+ return CURLE_OK;
+ }
+
+ /*
+ * This is a directory that we are trying to get, so produce a directory
+ * listing
+ */
+ sshc->sftp_handle =
+ libssh2_sftp_open_ex(sshc->sftp_session, sshp->path,
+ curlx_uztoui(strlen(sshp->path)),
+ 0, 0, LIBSSH2_SFTP_OPENDIR);
+ if(!sshc->sftp_handle) {
+ unsigned long sftperr;
+ if(libssh2_session_last_errno(sshc->ssh_session) == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ failf(data, "Could not open directory for reading: %s",
+ sftp_libssh2_strerror(sftperr));
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ return sftp_libssh2_error_to_CURLE(sftperr);
+ }
+ myssh_state(data, sshc, SSH_SFTP_READDIR);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_readdir_link(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp)
+{
+ CURLcode result;
+ int rc =
+ libssh2_sftp_symlink_ex(sshc->sftp_session,
+ curlx_dyn_ptr(&sshp->readdir_link),
+ (unsigned int)
+ curlx_dyn_len(&sshp->readdir_link),
+ sshp->readdir_filename,
+ CURL_PATH_MAX, LIBSSH2_SFTP_READLINK);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ curlx_dyn_free(&sshp->readdir_link);
+
+ /* append filename and extra output */
+ result = curlx_dyn_addf(&sshp->readdir, " -> %s", sshp->readdir_filename);
+ if(result)
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ else
+ myssh_state(data, sshc, SSH_SFTP_READDIR_BOTTOM);
+ return result;
+}
+
+static CURLcode ssh_state_scp_download_init(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp)
+{
+ curl_off_t bytecount;
+
+ /*
+ * We must check the remote file; if it is a directory no values will
+ * be set in sb
+ */
+
+ /*
+ * If support for >2GB files exists, use it.
+ */
+
+ /* get a fresh new channel from the ssh layer */
+#if LIBSSH2_VERSION_NUM < 0x010700
+ struct stat sb;
+ memset(&sb, 0, sizeof(struct stat));
+ sshc->ssh_channel = libssh2_scp_recv(sshc->ssh_session,
+ sshp->path, &sb);
+#else
+ libssh2_struct_stat sb;
+ memset(&sb, 0, sizeof(libssh2_struct_stat));
+ sshc->ssh_channel = libssh2_scp_recv2(sshc->ssh_session,
+ sshp->path, &sb);
+#endif
+
+ if(!sshc->ssh_channel) {
+ int ssh_err;
+ char *err_msg = NULL;
+
+ if(libssh2_session_last_errno(sshc->ssh_session) ==
+ LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0));
+ failf(data, "%s", err_msg);
+ myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE);
+ return libssh2_session_error_to_CURLE(ssh_err);
+ }
+
+ /* download data */
+ bytecount = (curl_off_t)sb.st_size;
+ data->req.maxdownload = (curl_off_t)sb.st_size;
+ Curl_xfer_setup1(data, CURL_XFER_RECV, bytecount, FALSE);
+
+ /* not set by Curl_xfer_setup to preserve keepon bits */
+ data->conn->writesockfd = data->conn->sockfd;
+
+ /* we want to use the _receiving_ function even when the socket turns
+ out writableable as the underlying libssh2 recv function will deal
+ with both accordingly */
+ data->state.select_bits = CURL_CSELECT_IN;
+
+ myssh_state(data, sshc, SSH_STOP);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_close(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp)
+{
+ int rc = 0;
+ if(sshc->sftp_handle) {
+ rc = libssh2_sftp_close(sshc->sftp_handle);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc < 0) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0);
+ infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg);
+ }
+ sshc->sftp_handle = NULL;
+ }
+
+ Curl_safefree(sshp->path);
+
+ DEBUGF(infof(data, "SFTP DONE done"));
+
+ /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT
+ After nextstate is executed, the control should come back to
+ SSH_SFTP_CLOSE to pass the correct result back */
+ if(sshc->nextstate != SSH_NO_STATE &&
+ sshc->nextstate != SSH_SFTP_CLOSE) {
+ myssh_state(data, sshc, sshc->nextstate);
+ sshc->nextstate = SSH_SFTP_CLOSE;
+ }
+ else
+ myssh_state(data, sshc, SSH_STOP);
+
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_shutdown(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ /* during times we get here due to a broken transfer and then the
+ sftp_handle might not have been taken down so make sure that is done
+ before we proceed */
+ int rc = 0;
+ if(sshc->sftp_handle) {
+ rc = libssh2_sftp_close(sshc->sftp_handle);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc < 0) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session, &err_msg,
+ NULL, 0);
+ infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg);
+ }
+ sshc->sftp_handle = NULL;
+ }
+ if(sshc->sftp_session) {
+ rc = libssh2_sftp_shutdown(sshc->sftp_session);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc < 0) {
+ infof(data, "Failed to stop libssh2 sftp subsystem");
+ }
+ sshc->sftp_session = NULL;
+ }
+
+ Curl_safefree(sshc->homedir);
+
+ myssh_state(data, sshc, SSH_SESSION_DISCONNECT);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_sftp_download_init(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp)
+{
+ /*
+ * Work on getting the specified file
+ */
+ sshc->sftp_handle =
+ libssh2_sftp_open_ex(sshc->sftp_session, sshp->path,
+ curlx_uztoui(strlen(sshp->path)),
+ LIBSSH2_FXF_READ, (long)data->set.new_file_perms,
+ LIBSSH2_SFTP_OPENFILE);
+ if(!sshc->sftp_handle) {
+ unsigned long sftperr;
+ if(libssh2_session_last_errno(sshc->ssh_session) ==
+ LIBSSH2_ERROR_EAGAIN) {
+ return CURLE_AGAIN;
+ }
+ sftperr = libssh2_sftp_last_error(sshc->sftp_session);
+ failf(data, "Could not open remote file for reading: %s",
+ sftp_libssh2_strerror(sftperr));
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
+ return sftp_libssh2_error_to_CURLE(sftperr);
+ }
+ myssh_state(data, sshc, SSH_SFTP_DOWNLOAD_STAT);
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_scp_upload_init(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp)
+{
+ /*
+ * libssh2 requires that the destination path is a full path that
+ * includes the destination file and name OR ends in a "/" . If this is
+ * not done the destination file will be named the same name as the last
+ * directory in the path.
+ */
+ sshc->ssh_channel =
+ libssh2_scp_send64(sshc->ssh_session, sshp->path,
+ (int)data->set.new_file_perms,
+ (libssh2_int64_t)data->state.infilesize, 0, 0);
+ if(!sshc->ssh_channel) {
+ int ssh_err;
+ char *err_msg = NULL;
+ CURLcode result;
+ if(libssh2_session_last_errno(sshc->ssh_session) ==
+ LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0));
+ failf(data, "%s", err_msg);
+ myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE);
+ result = libssh2_session_error_to_CURLE(ssh_err);
+
+ /* Map generic errors to upload failed */
+ if(result == CURLE_SSH ||
+ result == CURLE_REMOTE_FILE_NOT_FOUND)
+ result = CURLE_UPLOAD_FAILED;
+ return result;
+ }
+
+ /* upload data */
+ data->req.size = data->state.infilesize;
+ Curl_pgrsSetUploadSize(data, data->state.infilesize);
+ Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
+
+ /* not set by Curl_xfer_setup to preserve keepon bits */
+ data->conn->sockfd = data->conn->writesockfd;
+
+ /* store this original bitmask setup to use later on if we cannot
+ figure out a "real" bitmask */
+ sshc->orig_waitfor = data->req.keepon;
+
+ /* we want to use the _sending_ function even when the socket turns
+ out readable as the underlying libssh2 scp send function will deal
+ with both accordingly */
+ data->state.select_bits = CURL_CSELECT_OUT;
+
+ myssh_state(data, sshc, SSH_STOP);
+
+ return CURLE_OK;
+}
+
+static CURLcode ssh_state_session_disconnect(struct Curl_easy *data,
+ struct ssh_conn *sshc)
+{
+ /* during weird times when we have been prematurely aborted, the channel
+ is still alive when we reach this state and we MUST kill the channel
+ properly first */
+ int rc = 0;
+ if(sshc->ssh_channel) {
+ rc = libssh2_channel_free(sshc->ssh_channel);
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_OK;
+
+ if(rc < 0) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0);
+ infof(data, "Failed to free libssh2 scp subsystem: %d %s",
+ rc, err_msg);
+ }
+ sshc->ssh_channel = NULL;
+ }
+
+ if(sshc->ssh_session) {
+ rc = libssh2_session_disconnect(sshc->ssh_session, "Shutdown");
+ if(rc == LIBSSH2_ERROR_EAGAIN)
+ return CURLE_AGAIN;
+
+ if(rc < 0) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0);
+ infof(data, "Failed to disconnect libssh2 session: %d %s",
+ rc, err_msg);
+ }
+ }
+
+ Curl_safefree(sshc->homedir);
+
+ myssh_state(data, sshc, SSH_SESSION_FREE);
+ return CURLE_OK;
+}
/*
* ssh_statemachine() runs the SSH state machine as far as it can without
* blocking and without reaching the end. The data the pointer 'block' points
@@ -1689,723 +2693,195 @@ static CURLcode sftp_readdir(struct Curl_easy *data,
* meaning it wants to be called again when the socket is ready
*/
-static CURLcode ssh_statemachine(struct Curl_easy *data, bool *block)
+static CURLcode ssh_statemachine(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp,
+ bool *block)
{
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
- struct SSHPROTO *sshp = data->req.p.ssh;
- struct ssh_conn *sshc = &conn->proto.sshc;
-
- int rc = LIBSSH2_ERROR_NONE;
*block = 0; /* we are not blocking by default */
do {
switch(sshc->state) {
case SSH_INIT:
- sshc->secondCreateDirs = 0;
- sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = CURLE_OK;
-
- /* Set libssh2 to non-blocking, since everything internally is
- non-blocking */
- libssh2_session_set_blocking(sshc->ssh_session, 0);
-
- result = ssh_force_knownhost_key_type(data);
- if(result) {
- state(data, SSH_SESSION_FREE);
- sshc->actualcode = result;
+ result = ssh_state_init(data, sshc);
+ if(result)
break;
- }
-
- state(data, SSH_S_STARTUP);
FALLTHROUGH();
case SSH_S_STARTUP:
- rc = session_startup(sshc->ssh_session, conn->sock[FIRSTSOCKET]);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc) {
- char *err_msg = NULL;
- (void)libssh2_session_last_error(sshc->ssh_session, &err_msg, NULL, 0);
- failf(data, "Failure establishing ssh session: %d, %s", rc, err_msg);
-
- state(data, SSH_SESSION_FREE);
- sshc->actualcode = CURLE_FAILED_INIT;
+ result = ssh_state_startup(data, sshc);
+ if(result)
break;
- }
-
- state(data, SSH_HOSTKEY);
-
FALLTHROUGH();
+
case SSH_HOSTKEY:
- /*
- * Before we authenticate we should check the hostkey's fingerprint
- * against our known hosts. How that is handled (reading from file,
- * whatever) is up to us.
- */
- result = ssh_check_fingerprint(data);
- if(!result)
- state(data, SSH_AUTHLIST);
- /* ssh_check_fingerprint sets state appropriately on error */
+ result = ssh_state_hostkey(data, sshc);
break;
case SSH_AUTHLIST:
- /*
- * Figure out authentication methods
- * NB: As soon as we have provided a username to an openssh server we
- * must never change it later. Thus, always specify the correct username
- * here, even though the libssh2 docs kind of indicate that it should be
- * possible to get a 'generic' list (not user-specific) of authentication
- * methods, presumably with a blank username. That will not work in my
- * experience.
- * So always specify it here.
- */
- sshc->authlist = libssh2_userauth_list(sshc->ssh_session,
- conn->user,
- curlx_uztoui(strlen(conn->user)));
-
- if(!sshc->authlist) {
- if(libssh2_userauth_authenticated(sshc->ssh_session)) {
- sshc->authed = TRUE;
- infof(data, "SSH user accepted with no authentication");
- state(data, SSH_AUTH_DONE);
- break;
- }
- rc = libssh2_session_last_errno(sshc->ssh_session);
- if(rc == LIBSSH2_ERROR_EAGAIN)
- rc = LIBSSH2_ERROR_EAGAIN;
- else {
- state(data, SSH_SESSION_FREE);
- sshc->actualcode = libssh2_session_error_to_CURLE(rc);
- }
- break;
- }
- infof(data, "SSH authentication methods available: %s",
- sshc->authlist);
-
- state(data, SSH_AUTH_PKEY_INIT);
+ result = ssh_state_authlist(data, sshc);
break;
case SSH_AUTH_PKEY_INIT:
- result = sftp_pkey_init(data, sshc);
+ result = ssh_state_pkey_init(data, sshc);
break;
case SSH_AUTH_PKEY:
- /* The function below checks if the files exists, no need to stat() here.
- */
- rc = libssh2_userauth_publickey_fromfile_ex(sshc->ssh_session,
- conn->user,
- curlx_uztoui(
- strlen(conn->user)),
- sshc->rsa_pub,
- sshc->rsa, sshc->passphrase);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
-
- Curl_safefree(sshc->rsa_pub);
- Curl_safefree(sshc->rsa);
-
- if(rc == 0) {
- sshc->authed = TRUE;
- infof(data, "Initialized SSH public key authentication");
- state(data, SSH_AUTH_DONE);
- }
- else {
- char *err_msg = NULL;
- char unknown[] = "Reason unknown (-1)";
- if(rc == -1) {
- /* No error message has been set and the last set error message, if
- any, is from a previous error so ignore it. #11837 */
- err_msg = unknown;
- }
- else {
- (void)libssh2_session_last_error(sshc->ssh_session,
- &err_msg, NULL, 0);
- }
- infof(data, "SSH public key authentication failed: %s", err_msg);
- state(data, SSH_AUTH_PASS_INIT);
- rc = 0; /* clear rc and continue */
- }
+ result = ssh_state_auth_pkey(data, sshc);
break;
case SSH_AUTH_PASS_INIT:
- if((data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD) &&
- (strstr(sshc->authlist, "password") != NULL)) {
- state(data, SSH_AUTH_PASS);
- }
- else {
- state(data, SSH_AUTH_HOST_INIT);
- rc = 0; /* clear rc and continue */
- }
+ result = ssh_state_auth_pass_init(data, sshc);
break;
case SSH_AUTH_PASS:
- rc = libssh2_userauth_password_ex(sshc->ssh_session, conn->user,
- curlx_uztoui(strlen(conn->user)),
- conn->passwd,
- curlx_uztoui(strlen(conn->passwd)),
- NULL);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc == 0) {
- sshc->authed = TRUE;
- infof(data, "Initialized password authentication");
- state(data, SSH_AUTH_DONE);
- }
- else {
- state(data, SSH_AUTH_HOST_INIT);
- rc = 0; /* clear rc and continue */
- }
+ result = ssh_state_auth_pass(data, sshc);
break;
case SSH_AUTH_HOST_INIT:
- if((data->set.ssh_auth_types & CURLSSH_AUTH_HOST) &&
- (strstr(sshc->authlist, "hostbased") != NULL)) {
- state(data, SSH_AUTH_HOST);
- }
- else {
- state(data, SSH_AUTH_AGENT_INIT);
- }
+ result = ssh_state_auth_host_init(data, sshc);
break;
case SSH_AUTH_HOST:
- state(data, SSH_AUTH_AGENT_INIT);
+ myssh_state(data, sshc, SSH_AUTH_AGENT_INIT);
break;
case SSH_AUTH_AGENT_INIT:
-#ifdef HAVE_LIBSSH2_AGENT_API
- if((data->set.ssh_auth_types & CURLSSH_AUTH_AGENT)
- && (strstr(sshc->authlist, "publickey") != NULL)) {
-
- /* Connect to the ssh-agent */
- /* The agent could be shared by a curl thread i believe
- but nothing obvious as keys can be added/removed at any time */
- if(!sshc->ssh_agent) {
- sshc->ssh_agent = libssh2_agent_init(sshc->ssh_session);
- if(!sshc->ssh_agent) {
- infof(data, "Could not create agent object");
-
- state(data, SSH_AUTH_KEY_INIT);
- break;
- }
- }
-
- rc = libssh2_agent_connect(sshc->ssh_agent);
- if(rc == LIBSSH2_ERROR_EAGAIN)
- break;
- if(rc < 0) {
- infof(data, "Failure connecting to agent");
- state(data, SSH_AUTH_KEY_INIT);
- rc = 0; /* clear rc and continue */
- }
- else {
- state(data, SSH_AUTH_AGENT_LIST);
- }
- }
- else
-#endif /* HAVE_LIBSSH2_AGENT_API */
- state(data, SSH_AUTH_KEY_INIT);
+ result = ssh_state_auth_agent_init(data, sshc);
break;
case SSH_AUTH_AGENT_LIST:
-#ifdef HAVE_LIBSSH2_AGENT_API
- rc = libssh2_agent_list_identities(sshc->ssh_agent);
-
- if(rc == LIBSSH2_ERROR_EAGAIN)
- break;
- if(rc < 0) {
- infof(data, "Failure requesting identities to agent");
- state(data, SSH_AUTH_KEY_INIT);
- rc = 0; /* clear rc and continue */
- }
- else {
- state(data, SSH_AUTH_AGENT);
- sshc->sshagent_prev_identity = NULL;
- }
-#endif
+ result = ssh_state_auth_agent_list(data, sshc);
break;
case SSH_AUTH_AGENT:
-#ifdef HAVE_LIBSSH2_AGENT_API
- /* as prev_identity evolves only after an identity user auth finished we
- can safely request it again as long as EAGAIN is returned here or by
- libssh2_agent_userauth */
- rc = libssh2_agent_get_identity(sshc->ssh_agent,
- &sshc->sshagent_identity,
- sshc->sshagent_prev_identity);
- if(rc == LIBSSH2_ERROR_EAGAIN)
- break;
-
- if(rc == 0) {
- rc = libssh2_agent_userauth(sshc->ssh_agent, conn->user,
- sshc->sshagent_identity);
-
- if(rc < 0) {
- if(rc != LIBSSH2_ERROR_EAGAIN) {
- /* tried and failed? go to next identity */
- sshc->sshagent_prev_identity = sshc->sshagent_identity;
- }
- break;
- }
- }
-
- if(rc < 0)
- infof(data, "Failure requesting identities to agent");
- else if(rc == 1)
- infof(data, "No identity would match");
-
- if(rc == LIBSSH2_ERROR_NONE) {
- sshc->authed = TRUE;
- infof(data, "Agent based authentication successful");
- state(data, SSH_AUTH_DONE);
- }
- else {
- state(data, SSH_AUTH_KEY_INIT);
- rc = 0; /* clear rc and continue */
- }
-#endif
+ result = ssh_state_auth_agent(data, sshc);
break;
case SSH_AUTH_KEY_INIT:
- if((data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD)
- && (strstr(sshc->authlist, "keyboard-interactive") != NULL)) {
- state(data, SSH_AUTH_KEY);
- }
- else {
- state(data, SSH_AUTH_DONE);
- }
+ result = ssh_state_auth_key_init(data, sshc);
break;
case SSH_AUTH_KEY:
- /* Authentication failed. Continue with keyboard-interactive now. */
- rc = libssh2_userauth_keyboard_interactive_ex(sshc->ssh_session,
- conn->user,
- curlx_uztoui(
- strlen(conn->user)),
- &kbd_callback);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc == 0) {
- sshc->authed = TRUE;
- infof(data, "Initialized keyboard interactive authentication");
- }
- state(data, SSH_AUTH_DONE);
+ result = ssh_state_auth_key(data, sshc);
break;
case SSH_AUTH_DONE:
- if(!sshc->authed) {
- failf(data, "Authentication failure");
- state(data, SSH_SESSION_FREE);
- sshc->actualcode = CURLE_LOGIN_DENIED;
- break;
- }
-
- /*
- * At this point we have an authenticated ssh session.
- */
- infof(data, "Authentication complete");
-
- Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSH is connected */
-
- conn->sockfd = conn->sock[FIRSTSOCKET];
- conn->writesockfd = CURL_SOCKET_BAD;
-
- if(conn->handler->protocol == CURLPROTO_SFTP) {
- state(data, SSH_SFTP_INIT);
- break;
- }
- infof(data, "SSH CONNECT phase done");
- state(data, SSH_STOP);
+ result = ssh_state_auth_done(data, sshc);
break;
case SSH_SFTP_INIT:
- /*
- * Start the libssh2 sftp session
- */
- sshc->sftp_session = libssh2_sftp_init(sshc->ssh_session);
- if(!sshc->sftp_session) {
- char *err_msg = NULL;
- if(libssh2_session_last_errno(sshc->ssh_session) ==
- LIBSSH2_ERROR_EAGAIN) {
- rc = LIBSSH2_ERROR_EAGAIN;
- break;
- }
-
- (void)libssh2_session_last_error(sshc->ssh_session,
- &err_msg, NULL, 0);
- failf(data, "Failure initializing sftp session: %s", err_msg);
- state(data, SSH_SESSION_FREE);
- sshc->actualcode = CURLE_FAILED_INIT;
- break;
- }
- state(data, SSH_SFTP_REALPATH);
+ result = ssh_state_sftp_init(data, sshc);
break;
case SSH_SFTP_REALPATH:
- /*
- * Get the "home" directory
- */
- rc = sftp_libssh2_realpath(sshc->sftp_session, ".",
- sshp->readdir_filename, CURL_PATH_MAX);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc > 0) {
- /* It seems that this string is not always NULL terminated */
- sshp->readdir_filename[rc] = '\0';
- sshc->homedir = strdup(sshp->readdir_filename);
- if(!sshc->homedir) {
- state(data, SSH_SFTP_CLOSE);
- sshc->actualcode = CURLE_OUT_OF_MEMORY;
- break;
- }
- data->state.most_recent_ftp_entrypath = sshc->homedir;
- }
- else {
- /* Return the error type */
- unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
- if(sftperr)
- result = sftp_libssh2_error_to_CURLE(sftperr);
- else
- /* in this case, the error was not in the SFTP level but for example
- a time-out or similar */
- result = CURLE_SSH;
- sshc->actualcode = result;
- DEBUGF(infof(data, "error = %lu makes libcurl = %d",
- sftperr, (int)result));
- state(data, SSH_STOP);
- break;
- }
-
- /* This is the last step in the SFTP connect phase. Do note that while
- we get the homedir here, we get the "workingpath" in the DO action
- since the homedir will remain the same between request but the
- working path will not. */
- DEBUGF(infof(data, "SSH CONNECT phase done"));
- state(data, SSH_STOP);
- break;
+ result = ssh_state_sftp_realpath(data, sshc, sshp);
+ break;
case SSH_SFTP_QUOTE_INIT:
-
- result = Curl_getworkingpath(data, sshc->homedir, &sshp->path);
- if(result) {
- sshc->actualcode = result;
- state(data, SSH_STOP);
- break;
- }
-
- if(data->set.quote) {
- infof(data, "Sending quote commands");
- sshc->quote_item = data->set.quote;
- state(data, SSH_SFTP_QUOTE);
- }
- else {
- state(data, SSH_SFTP_GETINFO);
- }
+ result = ssh_state_sftp_quote_init(data, sshc, sshp);
break;
case SSH_SFTP_POSTQUOTE_INIT:
- if(data->set.postquote) {
- infof(data, "Sending quote commands");
- sshc->quote_item = data->set.postquote;
- state(data, SSH_SFTP_QUOTE);
- }
- else {
- state(data, SSH_STOP);
- }
+ result = ssh_state_sftp_postquote_init(data, sshc);
break;
case SSH_SFTP_QUOTE:
- /* Send quote commands */
- result = sftp_quote(data, sshc, sshp);
- if(result) {
- state(data, SSH_SFTP_CLOSE);
- sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = result;
- }
+ result = ssh_state_sftp_quote(data, sshc, sshp);
break;
case SSH_SFTP_NEXT_QUOTE:
- Curl_safefree(sshc->quote_path1);
- Curl_safefree(sshc->quote_path2);
-
- sshc->quote_item = sshc->quote_item->next;
-
- if(sshc->quote_item) {
- state(data, SSH_SFTP_QUOTE);
- }
- else {
- if(sshc->nextstate != SSH_NO_STATE) {
- state(data, sshc->nextstate);
- sshc->nextstate = SSH_NO_STATE;
- }
- else {
- state(data, SSH_SFTP_GETINFO);
- }
- }
+ result = ssh_state_sftp_next_quote(data, sshc);
break;
case SSH_SFTP_QUOTE_STAT:
- result = sftp_quote_stat(data, sshc, sshp, block);
- if(result) {
- state(data, SSH_SFTP_CLOSE);
- sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = result;
- }
+ result = ssh_state_sftp_quote_stat(data, sshc, sshp, block);
break;
case SSH_SFTP_QUOTE_SETSTAT:
- rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2,
- curlx_uztoui(strlen(sshc->quote_path2)),
- LIBSSH2_SFTP_SETSTAT,
- &sshp->quote_attrs);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc && !sshc->acceptfail) {
- unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
- Curl_safefree(sshc->quote_path1);
- Curl_safefree(sshc->quote_path2);
- failf(data, "Attempt to set SFTP stats failed: %s",
- sftp_libssh2_strerror(sftperr));
- state(data, SSH_SFTP_CLOSE);
- sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = CURLE_QUOTE_ERROR;
- break;
- }
- state(data, SSH_SFTP_NEXT_QUOTE);
+ result = ssh_state_sftp_quote_setstat(data, sshc, sshp);
break;
case SSH_SFTP_QUOTE_SYMLINK:
- rc = libssh2_sftp_symlink_ex(sshc->sftp_session, sshc->quote_path1,
- curlx_uztoui(strlen(sshc->quote_path1)),
- sshc->quote_path2,
- curlx_uztoui(strlen(sshc->quote_path2)),
- LIBSSH2_SFTP_SYMLINK);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc && !sshc->acceptfail) {
- unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
- Curl_safefree(sshc->quote_path1);
- Curl_safefree(sshc->quote_path2);
- failf(data, "symlink command failed: %s",
- sftp_libssh2_strerror(sftperr));
- state(data, SSH_SFTP_CLOSE);
- sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = CURLE_QUOTE_ERROR;
- break;
- }
- state(data, SSH_SFTP_NEXT_QUOTE);
+ result = ssh_state_sftp_quote_symlink(data, sshc);
break;
case SSH_SFTP_QUOTE_MKDIR:
- rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshc->quote_path1,
- curlx_uztoui(strlen(sshc->quote_path1)),
- (long)data->set.new_directory_perms);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc && !sshc->acceptfail) {
- unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
- Curl_safefree(sshc->quote_path1);
- failf(data, "mkdir command failed: %s",
- sftp_libssh2_strerror(sftperr));
- state(data, SSH_SFTP_CLOSE);
- sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = CURLE_QUOTE_ERROR;
- break;
- }
- state(data, SSH_SFTP_NEXT_QUOTE);
+ result = ssh_state_sftp_quote_mkdir(data, sshc);
break;
case SSH_SFTP_QUOTE_RENAME:
- rc = libssh2_sftp_rename_ex(sshc->sftp_session, sshc->quote_path1,
- curlx_uztoui(strlen(sshc->quote_path1)),
- sshc->quote_path2,
- curlx_uztoui(strlen(sshc->quote_path2)),
- LIBSSH2_SFTP_RENAME_OVERWRITE |
- LIBSSH2_SFTP_RENAME_ATOMIC |
- LIBSSH2_SFTP_RENAME_NATIVE);
-
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc && !sshc->acceptfail) {
- unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
- Curl_safefree(sshc->quote_path1);
- Curl_safefree(sshc->quote_path2);
- failf(data, "rename command failed: %s",
- sftp_libssh2_strerror(sftperr));
- state(data, SSH_SFTP_CLOSE);
- sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = CURLE_QUOTE_ERROR;
- break;
- }
- state(data, SSH_SFTP_NEXT_QUOTE);
+ result = ssh_state_sftp_quote_rename(data, sshc);
break;
case SSH_SFTP_QUOTE_RMDIR:
- rc = libssh2_sftp_rmdir_ex(sshc->sftp_session, sshc->quote_path1,
- curlx_uztoui(strlen(sshc->quote_path1)));
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc && !sshc->acceptfail) {
- unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
- Curl_safefree(sshc->quote_path1);
- failf(data, "rmdir command failed: %s",
- sftp_libssh2_strerror(sftperr));
- state(data, SSH_SFTP_CLOSE);
- sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = CURLE_QUOTE_ERROR;
- break;
- }
- state(data, SSH_SFTP_NEXT_QUOTE);
+ result = ssh_state_sftp_quote_rmdir(data, sshc);
break;
case SSH_SFTP_QUOTE_UNLINK:
- rc = libssh2_sftp_unlink_ex(sshc->sftp_session, sshc->quote_path1,
- curlx_uztoui(strlen(sshc->quote_path1)));
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc && !sshc->acceptfail) {
- unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
- Curl_safefree(sshc->quote_path1);
- failf(data, "rm command failed: %s", sftp_libssh2_strerror(sftperr));
- state(data, SSH_SFTP_CLOSE);
- sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = CURLE_QUOTE_ERROR;
- break;
- }
- state(data, SSH_SFTP_NEXT_QUOTE);
+ result = ssh_state_sftp_quote_unlink(data, sshc);
break;
-#ifdef HAS_STATVFS_SUPPORT
case SSH_SFTP_QUOTE_STATVFS:
- {
- LIBSSH2_SFTP_STATVFS statvfs;
- rc = libssh2_sftp_statvfs(sshc->sftp_session, sshc->quote_path1,
- curlx_uztoui(strlen(sshc->quote_path1)),
- &statvfs);
-
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc && !sshc->acceptfail) {
- unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
- Curl_safefree(sshc->quote_path1);
- failf(data, "statvfs command failed: %s",
- sftp_libssh2_strerror(sftperr));
- state(data, SSH_SFTP_CLOSE);
- sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = CURLE_QUOTE_ERROR;
- break;
- }
- else if(rc == 0) {
-#ifdef _MSC_VER
-#define CURL_LIBSSH2_VFS_SIZE_MASK "I64u"
-#else
-#define CURL_LIBSSH2_VFS_SIZE_MASK "llu"
-#endif
- char *tmp = aprintf("statvfs:\n"
- "f_bsize: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
- "f_frsize: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
- "f_blocks: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
- "f_bfree: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
- "f_bavail: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
- "f_files: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
- "f_ffree: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
- "f_favail: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
- "f_fsid: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
- "f_flag: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n"
- "f_namemax: %" CURL_LIBSSH2_VFS_SIZE_MASK "\n",
- statvfs.f_bsize, statvfs.f_frsize,
- statvfs.f_blocks, statvfs.f_bfree,
- statvfs.f_bavail, statvfs.f_files,
- statvfs.f_ffree, statvfs.f_favail,
- statvfs.f_fsid, statvfs.f_flag,
- statvfs.f_namemax);
- if(!tmp) {
- result = CURLE_OUT_OF_MEMORY;
- state(data, SSH_SFTP_CLOSE);
- sshc->nextstate = SSH_NO_STATE;
- break;
- }
-
- result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp));
- free(tmp);
- if(result) {
- state(data, SSH_SFTP_CLOSE);
- sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = result;
- }
- }
- state(data, SSH_SFTP_NEXT_QUOTE);
+ result = ssh_state_sftp_quote_statvfs(data, sshc);
break;
- }
-#endif
+
case SSH_SFTP_GETINFO:
- {
if(data->set.get_filetime) {
- state(data, SSH_SFTP_FILETIME);
+ myssh_state(data, sshc, SSH_SFTP_FILETIME);
}
else {
- state(data, SSH_SFTP_TRANS_INIT);
+ myssh_state(data, sshc, SSH_SFTP_TRANS_INIT);
}
break;
- }
case SSH_SFTP_FILETIME:
{
LIBSSH2_SFTP_ATTRIBUTES attrs;
- rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path,
- curlx_uztoui(strlen(sshp->path)),
- LIBSSH2_SFTP_STAT, &attrs);
+ int rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshp->path,
+ curlx_uztoui(strlen(sshp->path)),
+ LIBSSH2_SFTP_STAT, &attrs);
if(rc == LIBSSH2_ERROR_EAGAIN) {
+ result = CURLE_AGAIN;
break;
}
if(rc == 0) {
data->info.filetime = (time_t)attrs.mtime;
}
- state(data, SSH_SFTP_TRANS_INIT);
+ myssh_state(data, sshc, SSH_SFTP_TRANS_INIT);
break;
}
case SSH_SFTP_TRANS_INIT:
if(data->state.upload)
- state(data, SSH_SFTP_UPLOAD_INIT);
+ myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT);
else {
if(sshp->path[strlen(sshp->path)-1] == '/')
- state(data, SSH_SFTP_READDIR_INIT);
+ myssh_state(data, sshc, SSH_SFTP_READDIR_INIT);
else
- state(data, SSH_SFTP_DOWNLOAD_INIT);
+ myssh_state(data, sshc, SSH_SFTP_DOWNLOAD_INIT);
}
break;
case SSH_SFTP_UPLOAD_INIT:
result = sftp_upload_init(data, sshc, sshp, block);
if(result) {
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = result;
}
break;
case SSH_SFTP_CREATE_DIRS_INIT:
if(strlen(sshp->path) > 1) {
sshc->slash_pos = sshp->path + 1; /* ignore the leading '/' */
- state(data, SSH_SFTP_CREATE_DIRS);
+ myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS);
}
else {
- state(data, SSH_SFTP_UPLOAD_INIT);
+ myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT);
}
break;
@@ -2415,401 +2891,121 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, bool *block)
*sshc->slash_pos = 0;
infof(data, "Creating directory '%s'", sshp->path);
- state(data, SSH_SFTP_CREATE_DIRS_MKDIR);
+ myssh_state(data, sshc, SSH_SFTP_CREATE_DIRS_MKDIR);
break;
}
- state(data, SSH_SFTP_UPLOAD_INIT);
+ myssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT);
break;
case SSH_SFTP_CREATE_DIRS_MKDIR:
- /* 'mode' - parameter is preliminary - default to 0644 */
- rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshp->path,
- curlx_uztoui(strlen(sshp->path)),
- (long)data->set.new_directory_perms);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- *sshc->slash_pos = '/';
- ++sshc->slash_pos;
- if(rc < 0) {
- /*
- * Abort if failure was not that the dir already exists or the
- * permission was denied (creation might succeed further down the
- * path) - retry on unspecific FAILURE also
- */
- unsigned long sftperr = libssh2_sftp_last_error(sshc->sftp_session);
- if((sftperr != LIBSSH2_FX_FILE_ALREADY_EXISTS) &&
- (sftperr != LIBSSH2_FX_FAILURE) &&
- (sftperr != LIBSSH2_FX_PERMISSION_DENIED)) {
- result = sftp_libssh2_error_to_CURLE(sftperr);
- state(data, SSH_SFTP_CLOSE);
- sshc->actualcode = result ? result : CURLE_SSH;
- break;
- }
- rc = 0; /* clear rc and continue */
- }
- state(data, SSH_SFTP_CREATE_DIRS);
+ result = ssh_state_sftp_create_dirs_mkdir(data, sshc, sshp);
break;
case SSH_SFTP_READDIR_INIT:
- Curl_pgrsSetDownloadSize(data, -1);
- if(data->req.no_body) {
- state(data, SSH_STOP);
- break;
- }
-
- /*
- * This is a directory that we are trying to get, so produce a directory
- * listing
- */
- sshc->sftp_handle =
- libssh2_sftp_open_ex(sshc->sftp_session, sshp->path,
- curlx_uztoui(strlen(sshp->path)),
- 0, 0, LIBSSH2_SFTP_OPENDIR);
- if(!sshc->sftp_handle) {
- unsigned long sftperr;
- if(libssh2_session_last_errno(sshc->ssh_session) ==
- LIBSSH2_ERROR_EAGAIN) {
- rc = LIBSSH2_ERROR_EAGAIN;
- break;
- }
- sftperr = libssh2_sftp_last_error(sshc->sftp_session);
- failf(data, "Could not open directory for reading: %s",
- sftp_libssh2_strerror(sftperr));
- state(data, SSH_SFTP_CLOSE);
- result = sftp_libssh2_error_to_CURLE(sftperr);
- sshc->actualcode = result ? result : CURLE_SSH;
- break;
- }
- Curl_dyn_init(&sshp->readdir, CURL_PATH_MAX * 2);
- state(data, SSH_SFTP_READDIR);
+ result = ssh_state_sftp_readdir_init(data, sshc, sshp);
break;
case SSH_SFTP_READDIR:
result = sftp_readdir(data, sshc, sshp, block);
if(result) {
- sshc->actualcode = result;
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
}
break;
case SSH_SFTP_READDIR_LINK:
- rc =
- libssh2_sftp_symlink_ex(sshc->sftp_session,
- Curl_dyn_ptr(&sshp->readdir_link),
- (unsigned int)
- Curl_dyn_len(&sshp->readdir_link),
- sshp->readdir_filename,
- CURL_PATH_MAX, LIBSSH2_SFTP_READLINK);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- Curl_dyn_free(&sshp->readdir_link);
-
- /* append filename and extra output */
- result = Curl_dyn_addf(&sshp->readdir, " -> %s", sshp->readdir_filename);
-
- if(result) {
- state(data, SSH_SFTP_CLOSE);
- sshc->actualcode = result;
- break;
- }
-
- state(data, SSH_SFTP_READDIR_BOTTOM);
+ result = ssh_state_sftp_readdir_link(data, sshc, sshp);
break;
case SSH_SFTP_READDIR_BOTTOM:
- result = Curl_dyn_addn(&sshp->readdir, "\n", 1);
+ result = curlx_dyn_addn(&sshp->readdir, "\n", 1);
if(!result)
result = Curl_client_write(data, CLIENTWRITE_BODY,
- Curl_dyn_ptr(&sshp->readdir),
- Curl_dyn_len(&sshp->readdir));
+ curlx_dyn_ptr(&sshp->readdir),
+ curlx_dyn_len(&sshp->readdir));
if(result) {
- Curl_dyn_free(&sshp->readdir);
- state(data, SSH_STOP);
+ curlx_dyn_free(&sshp->readdir);
+ myssh_state(data, sshc, SSH_STOP);
}
else {
- Curl_dyn_reset(&sshp->readdir);
- state(data, SSH_SFTP_READDIR);
+ curlx_dyn_reset(&sshp->readdir);
+ myssh_state(data, sshc, SSH_SFTP_READDIR);
}
break;
case SSH_SFTP_READDIR_DONE:
- if(libssh2_sftp_closedir(sshc->sftp_handle) ==
- LIBSSH2_ERROR_EAGAIN) {
- rc = LIBSSH2_ERROR_EAGAIN;
- break;
- }
- sshc->sftp_handle = NULL;
+ if(libssh2_sftp_closedir(sshc->sftp_handle) == LIBSSH2_ERROR_EAGAIN)
+ result = CURLE_AGAIN;
+ else {
+ sshc->sftp_handle = NULL;
- /* no data to transfer */
- Curl_xfer_setup_nop(data);
- state(data, SSH_STOP);
+ /* no data to transfer */
+ Curl_xfer_setup_nop(data);
+ myssh_state(data, sshc, SSH_STOP);
+ }
break;
case SSH_SFTP_DOWNLOAD_INIT:
- /*
- * Work on getting the specified file
- */
- sshc->sftp_handle =
- libssh2_sftp_open_ex(sshc->sftp_session, sshp->path,
- curlx_uztoui(strlen(sshp->path)),
- LIBSSH2_FXF_READ, (long)data->set.new_file_perms,
- LIBSSH2_SFTP_OPENFILE);
- if(!sshc->sftp_handle) {
- unsigned long sftperr;
- if(libssh2_session_last_errno(sshc->ssh_session) ==
- LIBSSH2_ERROR_EAGAIN) {
- rc = LIBSSH2_ERROR_EAGAIN;
- break;
- }
- sftperr = libssh2_sftp_last_error(sshc->sftp_session);
- failf(data, "Could not open remote file for reading: %s",
- sftp_libssh2_strerror(sftperr));
- state(data, SSH_SFTP_CLOSE);
- result = sftp_libssh2_error_to_CURLE(sftperr);
- sshc->actualcode = result ? result : CURLE_SSH;
- break;
- }
- state(data, SSH_SFTP_DOWNLOAD_STAT);
+ result = ssh_state_sftp_download_init(data, sshc, sshp);
break;
case SSH_SFTP_DOWNLOAD_STAT:
result = sftp_download_stat(data, sshc, sshp, block);
if(result) {
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->nextstate = SSH_NO_STATE;
- sshc->actualcode = result;
}
break;
case SSH_SFTP_CLOSE:
- if(sshc->sftp_handle) {
- rc = libssh2_sftp_close(sshc->sftp_handle);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc < 0) {
- char *err_msg = NULL;
- (void)libssh2_session_last_error(sshc->ssh_session,
- &err_msg, NULL, 0);
- infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg);
- }
- sshc->sftp_handle = NULL;
- }
-
- Curl_safefree(sshp->path);
-
- DEBUGF(infof(data, "SFTP DONE done"));
-
- /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT
- After nextstate is executed, the control should come back to
- SSH_SFTP_CLOSE to pass the correct result back */
- if(sshc->nextstate != SSH_NO_STATE &&
- sshc->nextstate != SSH_SFTP_CLOSE) {
- state(data, sshc->nextstate);
- sshc->nextstate = SSH_SFTP_CLOSE;
- }
- else {
- state(data, SSH_STOP);
- result = sshc->actualcode;
- }
+ result = ssh_state_sftp_close(data, sshc, sshp);
break;
case SSH_SFTP_SHUTDOWN:
- /* during times we get here due to a broken transfer and then the
- sftp_handle might not have been taken down so make sure that is done
- before we proceed */
-
- if(sshc->sftp_handle) {
- rc = libssh2_sftp_close(sshc->sftp_handle);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc < 0) {
- char *err_msg = NULL;
- (void)libssh2_session_last_error(sshc->ssh_session, &err_msg,
- NULL, 0);
- infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg);
- }
- sshc->sftp_handle = NULL;
- }
- if(sshc->sftp_session) {
- rc = libssh2_sftp_shutdown(sshc->sftp_session);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc < 0) {
- infof(data, "Failed to stop libssh2 sftp subsystem");
- }
- sshc->sftp_session = NULL;
- }
-
- Curl_safefree(sshc->homedir);
- data->state.most_recent_ftp_entrypath = NULL;
-
- state(data, SSH_SESSION_DISCONNECT);
+ result = ssh_state_sftp_shutdown(data, sshc);
break;
case SSH_SCP_TRANS_INIT:
result = Curl_getworkingpath(data, sshc->homedir, &sshp->path);
if(result) {
- sshc->actualcode = result;
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
}
if(data->state.upload) {
if(data->state.infilesize < 0) {
failf(data, "SCP requires a known file size for upload");
- sshc->actualcode = CURLE_UPLOAD_FAILED;
- state(data, SSH_SCP_CHANNEL_FREE);
+ result = CURLE_UPLOAD_FAILED;
+ myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE);
break;
}
- state(data, SSH_SCP_UPLOAD_INIT);
+ myssh_state(data, sshc, SSH_SCP_UPLOAD_INIT);
}
else {
- state(data, SSH_SCP_DOWNLOAD_INIT);
+ myssh_state(data, sshc, SSH_SCP_DOWNLOAD_INIT);
}
break;
case SSH_SCP_UPLOAD_INIT:
- /*
- * libssh2 requires that the destination path is a full path that
- * includes the destination file and name OR ends in a "/" . If this is
- * not done the destination file will be named the same name as the last
- * directory in the path.
- */
- sshc->ssh_channel =
- SCP_SEND(sshc->ssh_session, sshp->path, data->set.new_file_perms,
- data->state.infilesize);
- if(!sshc->ssh_channel) {
- int ssh_err;
- char *err_msg = NULL;
-
- if(libssh2_session_last_errno(sshc->ssh_session) ==
- LIBSSH2_ERROR_EAGAIN) {
- rc = LIBSSH2_ERROR_EAGAIN;
- break;
- }
-
- ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session,
- &err_msg, NULL, 0));
- failf(data, "%s", err_msg);
- state(data, SSH_SCP_CHANNEL_FREE);
- sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err);
- /* Map generic errors to upload failed */
- if(sshc->actualcode == CURLE_SSH ||
- sshc->actualcode == CURLE_REMOTE_FILE_NOT_FOUND)
- sshc->actualcode = CURLE_UPLOAD_FAILED;
- break;
- }
-
- /* upload data */
- data->req.size = data->state.infilesize;
- Curl_pgrsSetUploadSize(data, data->state.infilesize);
- Curl_xfer_setup1(data, CURL_XFER_SEND, -1, FALSE);
-
- /* not set by Curl_xfer_setup to preserve keepon bits */
- conn->sockfd = conn->writesockfd;
-
- if(result) {
- state(data, SSH_SCP_CHANNEL_FREE);
- sshc->actualcode = result;
- }
- else {
- /* store this original bitmask setup to use later on if we cannot
- figure out a "real" bitmask */
- sshc->orig_waitfor = data->req.keepon;
-
- /* we want to use the _sending_ function even when the socket turns
- out readable as the underlying libssh2 scp send function will deal
- with both accordingly */
- data->state.select_bits = CURL_CSELECT_OUT;
-
- state(data, SSH_STOP);
- }
+ result = ssh_state_scp_upload_init(data, sshc, sshp);
break;
case SSH_SCP_DOWNLOAD_INIT:
- {
- curl_off_t bytecount;
-
- /*
- * We must check the remote file; if it is a directory no values will
- * be set in sb
- */
-
- /*
- * If support for >2GB files exists, use it.
- */
-
- /* get a fresh new channel from the ssh layer */
-#if LIBSSH2_VERSION_NUM < 0x010700
- struct stat sb;
- memset(&sb, 0, sizeof(struct stat));
- sshc->ssh_channel = libssh2_scp_recv(sshc->ssh_session,
- sshp->path, &sb);
-#else
- libssh2_struct_stat sb;
- memset(&sb, 0, sizeof(libssh2_struct_stat));
- sshc->ssh_channel = libssh2_scp_recv2(sshc->ssh_session,
- sshp->path, &sb);
-#endif
-
- if(!sshc->ssh_channel) {
- int ssh_err;
- char *err_msg = NULL;
-
- if(libssh2_session_last_errno(sshc->ssh_session) ==
- LIBSSH2_ERROR_EAGAIN) {
- rc = LIBSSH2_ERROR_EAGAIN;
- break;
- }
-
-
- ssh_err = (int)(libssh2_session_last_error(sshc->ssh_session,
- &err_msg, NULL, 0));
- failf(data, "%s", err_msg);
- state(data, SSH_SCP_CHANNEL_FREE);
- sshc->actualcode = libssh2_session_error_to_CURLE(ssh_err);
- break;
- }
-
- /* download data */
- bytecount = (curl_off_t)sb.st_size;
- data->req.maxdownload = (curl_off_t)sb.st_size;
- Curl_xfer_setup1(data, CURL_XFER_RECV, bytecount, FALSE);
-
- /* not set by Curl_xfer_setup to preserve keepon bits */
- conn->writesockfd = conn->sockfd;
-
- /* we want to use the _receiving_ function even when the socket turns
- out writableable as the underlying libssh2 recv function will deal
- with both accordingly */
- data->state.select_bits = CURL_CSELECT_IN;
-
- if(result) {
- state(data, SSH_SCP_CHANNEL_FREE);
- sshc->actualcode = result;
- }
- else
- state(data, SSH_STOP);
- }
- break;
+ result = ssh_state_scp_download_init(data, sshc, sshp);
+ break;
case SSH_SCP_DONE:
if(data->state.upload)
- state(data, SSH_SCP_SEND_EOF);
+ myssh_state(data, sshc, SSH_SCP_SEND_EOF);
else
- state(data, SSH_SCP_CHANNEL_FREE);
+ myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE);
break;
case SSH_SCP_SEND_EOF:
if(sshc->ssh_channel) {
- rc = libssh2_channel_send_eof(sshc->ssh_channel);
+ int rc = libssh2_channel_send_eof(sshc->ssh_channel);
if(rc == LIBSSH2_ERROR_EAGAIN) {
+ result = CURLE_AGAIN;
break;
}
if(rc) {
@@ -2820,13 +3016,14 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, bool *block)
rc, err_msg);
}
}
- state(data, SSH_SCP_WAIT_EOF);
+ myssh_state(data, sshc, SSH_SCP_WAIT_EOF);
break;
case SSH_SCP_WAIT_EOF:
if(sshc->ssh_channel) {
- rc = libssh2_channel_wait_eof(sshc->ssh_channel);
+ int rc = libssh2_channel_wait_eof(sshc->ssh_channel);
if(rc == LIBSSH2_ERROR_EAGAIN) {
+ result = CURLE_AGAIN;
break;
}
if(rc) {
@@ -2836,13 +3033,14 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, bool *block)
infof(data, "Failed to get channel EOF: %d %s", rc, err_msg);
}
}
- state(data, SSH_SCP_WAIT_CLOSE);
+ myssh_state(data, sshc, SSH_SCP_WAIT_CLOSE);
break;
case SSH_SCP_WAIT_CLOSE:
if(sshc->ssh_channel) {
- rc = libssh2_channel_wait_closed(sshc->ssh_channel);
+ int rc = libssh2_channel_wait_closed(sshc->ssh_channel);
if(rc == LIBSSH2_ERROR_EAGAIN) {
+ result = CURLE_AGAIN;
break;
}
if(rc) {
@@ -2852,13 +3050,14 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, bool *block)
infof(data, "Channel failed to close: %d %s", rc, err_msg);
}
}
- state(data, SSH_SCP_CHANNEL_FREE);
+ myssh_state(data, sshc, SSH_SCP_CHANNEL_FREE);
break;
case SSH_SCP_CHANNEL_FREE:
if(sshc->ssh_channel) {
- rc = libssh2_channel_free(sshc->ssh_channel);
+ int rc = libssh2_channel_free(sshc->ssh_channel);
if(rc == LIBSSH2_ERROR_EAGAIN) {
+ result = CURLE_AGAIN;
break;
}
if(rc < 0) {
@@ -2871,141 +3070,38 @@ static CURLcode ssh_statemachine(struct Curl_easy *data, bool *block)
sshc->ssh_channel = NULL;
}
DEBUGF(infof(data, "SCP DONE phase complete"));
-#if 0 /* PREV */
- state(data, SSH_SESSION_DISCONNECT);
-#endif
- state(data, SSH_STOP);
- result = sshc->actualcode;
+ myssh_state(data, sshc, SSH_STOP);
break;
case SSH_SESSION_DISCONNECT:
- /* during weird times when we have been prematurely aborted, the channel
- is still alive when we reach this state and we MUST kill the channel
- properly first */
- if(sshc->ssh_channel) {
- rc = libssh2_channel_free(sshc->ssh_channel);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc < 0) {
- char *err_msg = NULL;
- (void)libssh2_session_last_error(sshc->ssh_session,
- &err_msg, NULL, 0);
- infof(data, "Failed to free libssh2 scp subsystem: %d %s",
- rc, err_msg);
- }
- sshc->ssh_channel = NULL;
- }
-
- if(sshc->ssh_session) {
- rc = libssh2_session_disconnect(sshc->ssh_session, "Shutdown");
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc < 0) {
- char *err_msg = NULL;
- (void)libssh2_session_last_error(sshc->ssh_session,
- &err_msg, NULL, 0);
- infof(data, "Failed to disconnect libssh2 session: %d %s",
- rc, err_msg);
- }
- }
-
- Curl_safefree(sshc->homedir);
- data->state.most_recent_ftp_entrypath = NULL;
-
- state(data, SSH_SESSION_FREE);
+ result = ssh_state_session_disconnect(data, sshc);
break;
case SSH_SESSION_FREE:
-#ifdef HAVE_LIBSSH2_KNOWNHOST_API
- if(sshc->kh) {
- libssh2_knownhost_free(sshc->kh);
- sshc->kh = NULL;
- }
-#endif
-
-#ifdef HAVE_LIBSSH2_AGENT_API
- if(sshc->ssh_agent) {
- rc = libssh2_agent_disconnect(sshc->ssh_agent);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc < 0) {
- char *err_msg = NULL;
- (void)libssh2_session_last_error(sshc->ssh_session,
- &err_msg, NULL, 0);
- infof(data, "Failed to disconnect from libssh2 agent: %d %s",
- rc, err_msg);
- }
- libssh2_agent_free(sshc->ssh_agent);
- sshc->ssh_agent = NULL;
-
- /* NB: there is no need to free identities, they are part of internal
- agent stuff */
- sshc->sshagent_identity = NULL;
- sshc->sshagent_prev_identity = NULL;
- }
-#endif
-
- if(sshc->ssh_session) {
- rc = libssh2_session_free(sshc->ssh_session);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- break;
- }
- if(rc < 0) {
- char *err_msg = NULL;
- (void)libssh2_session_last_error(sshc->ssh_session,
- &err_msg, NULL, 0);
- infof(data, "Failed to free libssh2 session: %d %s", rc, err_msg);
- }
- sshc->ssh_session = NULL;
- }
-
- /* worst-case scenario cleanup */
-
- DEBUGASSERT(sshc->ssh_session == NULL);
- DEBUGASSERT(sshc->ssh_channel == NULL);
- DEBUGASSERT(sshc->sftp_session == NULL);
- DEBUGASSERT(sshc->sftp_handle == NULL);
-#ifdef HAVE_LIBSSH2_KNOWNHOST_API
- DEBUGASSERT(sshc->kh == NULL);
-#endif
-#ifdef HAVE_LIBSSH2_AGENT_API
- DEBUGASSERT(sshc->ssh_agent == NULL);
-#endif
-
- Curl_safefree(sshc->rsa_pub);
- Curl_safefree(sshc->rsa);
- Curl_safefree(sshc->quote_path1);
- Curl_safefree(sshc->quote_path2);
- Curl_safefree(sshc->homedir);
-
+ result = sshc_cleanup(sshc, data, FALSE);
+ if(result)
+ break;
/* the code we are about to return */
- result = sshc->actualcode;
-
memset(sshc, 0, sizeof(struct ssh_conn));
-
connclose(conn, "SSH session free");
sshc->state = SSH_SESSION_FREE; /* current */
- sshc->nextstate = SSH_NO_STATE;
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
case SSH_QUIT:
default:
/* internal error */
- sshc->nextstate = SSH_NO_STATE;
- state(data, SSH_STOP);
+ myssh_state(data, sshc, SSH_STOP);
break;
}
- } while(!rc && (sshc->state != SSH_STOP));
+ } while(!result && (sshc->state != SSH_STOP));
- if(rc == LIBSSH2_ERROR_EAGAIN) {
+ if(result == CURLE_AGAIN) {
/* we would block, we need to wait for the socket to be ready (in the
right direction too)! */
*block = TRUE;
+ result = CURLE_OK;
}
return result;
@@ -3038,10 +3134,11 @@ static int ssh_getsock(struct Curl_easy *data,
* function in all cases so that when it _does not_ return EAGAIN we can
* restore the default wait bits.
*/
-static void ssh_block2waitfor(struct Curl_easy *data, bool block)
+static void ssh_block2waitfor(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ bool block)
{
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
int dir = 0;
if(block) {
dir = libssh2_session_block_directions(sshc->ssh_session);
@@ -3061,35 +3158,39 @@ static void ssh_block2waitfor(struct Curl_easy *data, bool block)
static CURLcode ssh_multi_statemach(struct Curl_easy *data, bool *done)
{
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+ struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY);
CURLcode result = CURLE_OK;
bool block; /* we store the status and use that to provide a ssh_getsock()
implementation */
+ if(!sshc || !sshp)
+ return CURLE_FAILED_INIT;
+
do {
- result = ssh_statemachine(data, &block);
+ result = ssh_statemachine(data, sshc, sshp, &block);
*done = (sshc->state == SSH_STOP);
/* if there is no error, it is not done and it did not EWOULDBLOCK, then
try again */
} while(!result && !*done && !block);
- ssh_block2waitfor(data, block);
+ ssh_block2waitfor(data, sshc, block);
return result;
}
static CURLcode ssh_block_statemach(struct Curl_easy *data,
- struct connectdata *conn,
+ struct ssh_conn *sshc,
+ struct SSHPROTO *sshp,
bool disconnect)
{
- struct ssh_conn *sshc = &conn->proto.sshc;
CURLcode result = CURLE_OK;
- struct curltime dis = Curl_now();
+ struct curltime dis = curlx_now();
while((sshc->state != SSH_STOP) && !result) {
bool block;
timediff_t left = 1000;
- struct curltime now = Curl_now();
+ struct curltime now = curlx_now();
- result = ssh_statemachine(data, &block);
+ result = ssh_statemachine(data, sshc, sshp, &block);
if(result)
break;
@@ -3107,7 +3208,7 @@ static CURLcode ssh_block_statemach(struct Curl_easy *data,
return CURLE_OPERATION_TIMEDOUT;
}
}
- else if(Curl_timediff(now, dis) > 1000) {
+ else if(curlx_timediff(now, dis) > 1000) {
/* disconnect timeout */
failf(data, "Disconnect timed out");
result = CURLE_OK;
@@ -3116,7 +3217,7 @@ static CURLcode ssh_block_statemach(struct Curl_easy *data,
if(block) {
int dir = libssh2_session_block_directions(sshc->ssh_session);
- curl_socket_t sock = conn->sock[FIRSTSOCKET];
+ curl_socket_t sock = data->conn->sock[FIRSTSOCKET];
curl_socket_t fd_read = CURL_SOCKET_BAD;
curl_socket_t fd_write = CURL_SOCKET_BAD;
if(LIBSSH2_SESSION_BLOCK_INBOUND & dir)
@@ -3132,17 +3233,51 @@ static CURLcode ssh_block_statemach(struct Curl_easy *data,
return result;
}
+static void myssh_easy_dtor(void *key, size_t klen, void *entry)
+{
+ struct SSHPROTO *sshp = entry;
+ (void)key;
+ (void)klen;
+ Curl_safefree(sshp->path);
+ curlx_dyn_free(&sshp->readdir);
+ curlx_dyn_free(&sshp->readdir_link);
+ free(sshp);
+}
+
+static void myssh_conn_dtor(void *key, size_t klen, void *entry)
+{
+ struct ssh_conn *sshc = entry;
+ (void)key;
+ (void)klen;
+ sshc_cleanup(sshc, NULL, TRUE);
+ free(sshc);
+}
+
/*
* SSH setup and connection
*/
static CURLcode ssh_setup_connection(struct Curl_easy *data,
struct connectdata *conn)
{
- struct SSHPROTO *ssh;
+ struct ssh_conn *sshc;
+ struct SSHPROTO *sshp;
(void)conn;
- data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO));
- if(!ssh)
+ sshc = calloc(1, sizeof(*sshc));
+ if(!sshc)
+ return CURLE_OUT_OF_MEMORY;
+
+ sshc->initialised = TRUE;
+ if(Curl_conn_meta_set(conn, CURL_META_SSH_CONN, sshc, myssh_conn_dtor))
+ return CURLE_OUT_OF_MEMORY;
+
+ sshp = calloc(1, sizeof(*sshp));
+ if(!sshp)
+ return CURLE_OUT_OF_MEMORY;
+
+ curlx_dyn_init(&sshp->readdir, CURL_PATH_MAX * 2);
+ curlx_dyn_init(&sshp->readdir_link, CURL_PATH_MAX);
+ if(Curl_meta_set(data, CURL_META_SSH_EASY, sshp, myssh_easy_dtor))
return CURLE_OUT_OF_MEMORY;
return CURLE_OK;
@@ -3160,20 +3295,23 @@ static ssize_t ssh_tls_recv(libssh2_socket_t sock, void *buffer,
CURLcode result;
struct connectdata *conn = data->conn;
Curl_recv *backup = conn->recv[0];
- struct ssh_conn *ssh = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
int socknum = Curl_conn_sockindex(data, sock);
(void)flags;
+ if(!sshc)
+ return -1;
+
/* swap in the TLS reader function for this call only, and then swap back
the SSH one again */
- conn->recv[0] = ssh->tls_recv;
+ conn->recv[0] = sshc->tls_recv;
result = Curl_conn_recv(data, socknum, buffer, length, &nread);
conn->recv[0] = backup;
if(result == CURLE_AGAIN)
return -EAGAIN; /* magic return code for libssh2 */
else if(result)
return -1; /* generic error */
- Curl_debug(data, CURLINFO_DATA_IN, (char *)buffer, (size_t)nread);
+ Curl_debug(data, CURLINFO_DATA_IN, (const char *)buffer, (size_t)nread);
return nread;
}
@@ -3185,20 +3323,23 @@ static ssize_t ssh_tls_send(libssh2_socket_t sock, const void *buffer,
CURLcode result;
struct connectdata *conn = data->conn;
Curl_send *backup = conn->send[0];
- struct ssh_conn *ssh = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
int socknum = Curl_conn_sockindex(data, sock);
(void)flags;
+ if(!sshc)
+ return -1;
+
/* swap in the TLS writer function for this call only, and then swap back
the SSH one again */
- conn->send[0] = ssh->tls_send;
+ conn->send[0] = sshc->tls_send;
result = Curl_conn_send(data, socknum, buffer, length, FALSE, &nwrite);
conn->send[0] = backup;
if(result == CURLE_AGAIN)
return -EAGAIN; /* magic return code for libssh2 */
else if(result)
return -1; /* error */
- Curl_debug(data, CURLINFO_DATA_OUT, (char *)buffer, nwrite);
+ Curl_debug(data, CURLINFO_DATA_OUT, (const char *)buffer, nwrite);
return (ssize_t)nwrite;
}
#endif
@@ -3212,27 +3353,50 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done)
#ifdef CURL_LIBSSH2_DEBUG
curl_socket_t sock;
#endif
- struct ssh_conn *sshc;
- CURLcode result;
struct connectdata *conn = data->conn;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+ CURLcode result;
- /* initialize per-handle data if not already */
- if(!data->req.p.ssh) {
- result = ssh_setup_connection(data, conn);
- if(result)
- return result;
+#if LIBSSH2_VERSION_NUM >= 0x010b00
+ {
+ const char *crypto_str;
+ switch(libssh2_crypto_engine()) {
+ case libssh2_gcrypt:
+ crypto_str = "libgcrypt";
+ break;
+ case libssh2_mbedtls:
+ crypto_str = "mbedTLS";
+ break;
+ case libssh2_openssl:
+ crypto_str = "openssl compatible";
+ break;
+ case libssh2_os400qc3:
+ crypto_str = "OS400QC3";
+ break;
+ case libssh2_wincng:
+ crypto_str = "WinCNG";
+ break;
+ default:
+ crypto_str = NULL;
+ break;
+ }
+ if(crypto_str)
+ infof(data, "libssh2 cryptography backend: %s", crypto_str);
}
+#endif
+
+ if(!sshc)
+ return CURLE_FAILED_INIT;
/* We default to persistent connections. We set this already in this connect
function to make the reuse checks properly be able to check this bit. */
connkeep(conn, "SSH default");
- sshc = &conn->proto.sshc;
-
+ if(conn->user)
+ infof(data, "User: '%s'", conn->user);
+ else
+ infof(data, "User: NULL");
#ifdef CURL_LIBSSH2_DEBUG
- if(conn->user) {
- infof(data, "User: %s", conn->user);
- }
if(conn->passwd) {
infof(data, "Password: %s", conn->passwd);
}
@@ -3323,14 +3487,11 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done)
conn->send[FIRSTSOCKET] = sftp_send;
}
- if(data->set.ssh_compression) {
-#if LIBSSH2_VERSION_NUM >= 0x010208
- if(libssh2_session_flag(sshc->ssh_session, LIBSSH2_FLAG_COMPRESS, 1) < 0)
-#endif
- infof(data, "Failed to enable compression for ssh session");
+ if(data->set.ssh_compression &&
+ libssh2_session_flag(sshc->ssh_session, LIBSSH2_FLAG_COMPRESS, 1) < 0) {
+ infof(data, "Failed to enable compression for ssh session");
}
-#ifdef HAVE_LIBSSH2_KNOWNHOST_API
if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
int rc;
sshc->kh = libssh2_knownhost_init(sshc->ssh_session);
@@ -3348,14 +3509,13 @@ static CURLcode ssh_connect(struct Curl_easy *data, bool *done)
infof(data, "Failed to read known hosts from %s",
data->set.str[STRING_SSH_KNOWNHOSTS]);
}
-#endif /* HAVE_LIBSSH2_KNOWNHOST_API */
#ifdef CURL_LIBSSH2_DEBUG
libssh2_trace(sshc->ssh_session, ~0);
infof(data, "SSH socket: %d", (int)sock);
#endif /* CURL_LIBSSH2_DEBUG */
- state(data, SSH_INIT);
+ myssh_state(data, sshc, SSH_INIT);
result = ssh_multi_statemach(data, done);
@@ -3376,14 +3536,17 @@ CURLcode scp_perform(struct Curl_easy *data,
bool *connected,
bool *dophase_done)
{
+ struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
CURLcode result = CURLE_OK;
DEBUGF(infof(data, "DO phase starts"));
*dophase_done = FALSE; /* not done yet */
+ if(!sshc)
+ return CURLE_FAILED_INIT;
/* start the first command in the DO phase */
- state(data, SSH_SCP_TRANS_INIT);
+ myssh_state(data, sshc, SSH_SCP_TRANS_INIT);
/* run the state-machine */
result = ssh_multi_statemach(data, dophase_done);
@@ -3420,13 +3583,13 @@ static CURLcode ssh_do(struct Curl_easy *data, bool *done)
CURLcode result;
bool connected = FALSE;
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
*done = FALSE; /* default to false */
+ if(!sshc)
+ return CURLE_FAILED_INIT;
data->req.size = -1; /* make sure this is unknown at this point */
-
- sshc->actualcode = CURLE_OK; /* reset error code */
sshc->secondCreateDirs = 0; /* reset the create dir attempt state
variable */
@@ -3443,6 +3606,110 @@ static CURLcode ssh_do(struct Curl_easy *data, bool *done)
return result;
}
+static CURLcode sshc_cleanup(struct ssh_conn *sshc, struct Curl_easy *data,
+ bool block)
+{
+ int rc;
+
+ if(sshc->initialised) {
+ if(sshc->kh) {
+ libssh2_knownhost_free(sshc->kh);
+ sshc->kh = NULL;
+ }
+
+ if(sshc->ssh_agent) {
+ rc = libssh2_agent_disconnect(sshc->ssh_agent);
+ if(!block && (rc == LIBSSH2_ERROR_EAGAIN))
+ return CURLE_AGAIN;
+
+ if((rc < 0) && data) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0);
+ infof(data, "Failed to disconnect from libssh2 agent: %d %s",
+ rc, err_msg);
+ }
+ libssh2_agent_free(sshc->ssh_agent);
+ sshc->ssh_agent = NULL;
+
+ /* NB: there is no need to free identities, they are part of internal
+ agent stuff */
+ sshc->sshagent_identity = NULL;
+ sshc->sshagent_prev_identity = NULL;
+ }
+
+ if(sshc->sftp_handle) {
+ rc = libssh2_sftp_close(sshc->sftp_handle);
+ if(!block && (rc == LIBSSH2_ERROR_EAGAIN))
+ return CURLE_AGAIN;
+
+ if((rc < 0) && data) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session, &err_msg,
+ NULL, 0);
+ infof(data, "Failed to close libssh2 file: %d %s", rc, err_msg);
+ }
+ sshc->sftp_handle = NULL;
+ }
+
+ if(sshc->ssh_channel) {
+ rc = libssh2_channel_free(sshc->ssh_channel);
+ if(!block && (rc == LIBSSH2_ERROR_EAGAIN))
+ return CURLE_AGAIN;
+
+ if((rc < 0) && data) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0);
+ infof(data, "Failed to free libssh2 scp subsystem: %d %s",
+ rc, err_msg);
+ }
+ sshc->ssh_channel = NULL;
+ }
+
+ if(sshc->sftp_session) {
+ rc = libssh2_sftp_shutdown(sshc->sftp_session);
+ if(!block && (rc == LIBSSH2_ERROR_EAGAIN))
+ return CURLE_AGAIN;
+
+ if((rc < 0) && data)
+ infof(data, "Failed to stop libssh2 sftp subsystem");
+ sshc->sftp_session = NULL;
+ }
+
+ if(sshc->ssh_session) {
+ rc = libssh2_session_free(sshc->ssh_session);
+ if(!block && (rc == LIBSSH2_ERROR_EAGAIN))
+ return CURLE_AGAIN;
+
+ if((rc < 0) && data) {
+ char *err_msg = NULL;
+ (void)libssh2_session_last_error(sshc->ssh_session,
+ &err_msg, NULL, 0);
+ infof(data, "Failed to free libssh2 session: %d %s", rc, err_msg);
+ }
+ sshc->ssh_session = NULL;
+ }
+
+ /* worst-case scenario cleanup */
+ DEBUGASSERT(sshc->ssh_session == NULL);
+ DEBUGASSERT(sshc->ssh_channel == NULL);
+ DEBUGASSERT(sshc->sftp_session == NULL);
+ DEBUGASSERT(sshc->sftp_handle == NULL);
+ DEBUGASSERT(sshc->kh == NULL);
+ DEBUGASSERT(sshc->ssh_agent == NULL);
+
+ Curl_safefree(sshc->rsa_pub);
+ Curl_safefree(sshc->rsa);
+ Curl_safefree(sshc->quote_path1);
+ Curl_safefree(sshc->quote_path2);
+ Curl_safefree(sshc->homedir);
+ sshc->initialised = FALSE;
+ }
+ return CURLE_OK;
+}
+
+
/* BLOCKING, but the function is using the state machine so the only reason
this is still blocking is that the multi interface code has no support for
disconnecting operations that takes a while */
@@ -3451,15 +3718,18 @@ static CURLcode scp_disconnect(struct Curl_easy *data,
bool dead_connection)
{
CURLcode result = CURLE_OK;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+ struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY);
(void) dead_connection;
- if(sshc->ssh_session) {
+ if(sshc && sshc->ssh_session && sshp) {
/* only if there is a session still around to use! */
- state(data, SSH_SESSION_DISCONNECT);
- result = ssh_block_statemach(data, conn, TRUE);
+ myssh_state(data, sshc, SSH_SESSION_DISCONNECT);
+ result = ssh_block_statemach(data, sshc, sshp, TRUE);
}
+ if(sshc)
+ return sshc_cleanup(sshc, data, TRUE);
return result;
}
@@ -3467,19 +3737,19 @@ static CURLcode scp_disconnect(struct Curl_easy *data,
done functions */
static CURLcode ssh_done(struct Curl_easy *data, CURLcode status)
{
+ struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
+ struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY);
CURLcode result = CURLE_OK;
- struct SSHPROTO *sshp = data->req.p.ssh;
- struct connectdata *conn = data->conn;
+
+ if(!sshc || !sshp)
+ return CURLE_FAILED_INIT;
if(!status)
/* run the state-machine */
- result = ssh_block_statemach(data, conn, FALSE);
+ result = ssh_block_statemach(data, sshc, sshp, FALSE);
else
result = status;
- Curl_safefree(sshp->path);
- Curl_dyn_free(&sshp->readdir);
-
if(Curl_pgrsDone(data))
return CURLE_ABORTED_BY_CALLBACK;
@@ -3491,13 +3761,13 @@ static CURLcode ssh_done(struct Curl_easy *data, CURLcode status)
static CURLcode scp_done(struct Curl_easy *data, CURLcode status,
bool premature)
{
+ struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
(void)premature; /* not used */
- if(!status)
- state(data, SSH_SCP_DONE);
+ if(sshc && !status)
+ myssh_state(data, sshc, SSH_SCP_DONE);
return ssh_done(data, status);
-
}
static ssize_t scp_send(struct Curl_easy *data, int sockindex,
@@ -3505,14 +3775,18 @@ static ssize_t scp_send(struct Curl_easy *data, int sockindex,
{
ssize_t nwrite;
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
(void)sockindex; /* we only support SCP on the fixed known primary socket */
(void)eos;
+ if(!sshc) {
+ *err = CURLE_FAILED_INIT;
+ return -1;
+ }
/* libssh2_channel_write() returns int! */
nwrite = (ssize_t) libssh2_channel_write(sshc->ssh_channel, mem, len);
- ssh_block2waitfor(data, (nwrite == LIBSSH2_ERROR_EAGAIN));
+ ssh_block2waitfor(data, sshc, (nwrite == LIBSSH2_ERROR_EAGAIN));
if(nwrite == LIBSSH2_ERROR_EAGAIN) {
*err = CURLE_AGAIN;
@@ -3531,13 +3805,17 @@ static ssize_t scp_recv(struct Curl_easy *data, int sockindex,
{
ssize_t nread;
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
(void)sockindex; /* we only support SCP on the fixed known primary socket */
+ if(!sshc) {
+ *err = CURLE_FAILED_INIT;
+ return -1;
+ }
/* libssh2_channel_read() returns int */
nread = (ssize_t) libssh2_channel_read(sshc->ssh_channel, mem, len);
- ssh_block2waitfor(data, (nread == LIBSSH2_ERROR_EAGAIN));
+ ssh_block2waitfor(data, sshc, (nread == LIBSSH2_ERROR_EAGAIN));
if(nread == LIBSSH2_ERROR_EAGAIN) {
*err = CURLE_AGAIN;
nread = -1;
@@ -3564,14 +3842,17 @@ CURLcode sftp_perform(struct Curl_easy *data,
bool *connected,
bool *dophase_done)
{
+ struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
CURLcode result = CURLE_OK;
DEBUGF(infof(data, "DO phase starts"));
*dophase_done = FALSE; /* not done yet */
+ if(!sshc)
+ return CURLE_FAILED_INIT;
/* start the first command in the DO phase */
- state(data, SSH_SFTP_QUOTE_INIT);
+ myssh_state(data, sshc, SSH_SFTP_QUOTE_INIT);
/* run the state-machine */
result = ssh_multi_statemach(data, dophase_done);
@@ -3604,18 +3885,21 @@ static CURLcode sftp_disconnect(struct Curl_easy *data,
struct connectdata *conn, bool dead_connection)
{
CURLcode result = CURLE_OK;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+ struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY);
(void) dead_connection;
DEBUGF(infof(data, "SSH DISCONNECT starts now"));
- if(sshc->ssh_session) {
+ if(sshc && sshc->ssh_session && sshp) {
/* only if there is a session still around to use! */
- state(data, SSH_SFTP_SHUTDOWN);
- result = ssh_block_statemach(data, conn, TRUE);
+ myssh_state(data, sshc, SSH_SFTP_SHUTDOWN);
+ result = ssh_block_statemach(data, sshc, sshp, TRUE);
}
DEBUGF(infof(data, "SSH DISCONNECT is done"));
+ if(sshc)
+ sshc_cleanup(sshc, data, TRUE);
return result;
@@ -3625,7 +3909,10 @@ static CURLcode sftp_done(struct Curl_easy *data, CURLcode status,
bool premature)
{
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+
+ if(!sshc)
+ return CURLE_FAILED_INIT;
if(!status) {
/* Post quote commands are executed after the SFTP_CLOSE state to avoid
@@ -3633,7 +3920,7 @@ static CURLcode sftp_done(struct Curl_easy *data, CURLcode status,
operation */
if(!premature && data->set.postquote && !conn->bits.retry)
sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT;
- state(data, SSH_SFTP_CLOSE);
+ myssh_state(data, sshc, SSH_SFTP_CLOSE);
}
return ssh_done(data, status);
}
@@ -3644,13 +3931,17 @@ static ssize_t sftp_send(struct Curl_easy *data, int sockindex,
{
ssize_t nwrite;
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
(void)sockindex;
(void)eos;
+ if(!sshc) {
+ *err = CURLE_FAILED_INIT;
+ return -1;
+ }
nwrite = libssh2_sftp_write(sshc->sftp_handle, mem, len);
- ssh_block2waitfor(data, (nwrite == LIBSSH2_ERROR_EAGAIN));
+ ssh_block2waitfor(data, sshc, (nwrite == LIBSSH2_ERROR_EAGAIN));
if(nwrite == LIBSSH2_ERROR_EAGAIN) {
*err = CURLE_AGAIN;
@@ -3673,12 +3964,16 @@ static ssize_t sftp_recv(struct Curl_easy *data, int sockindex,
{
ssize_t nread;
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
(void)sockindex;
+ if(!sshc) {
+ *err = CURLE_FAILED_INIT;
+ return -1;
+ }
nread = libssh2_sftp_read(sshc->sftp_handle, mem, len);
- ssh_block2waitfor(data, (nread == LIBSSH2_ERROR_EAGAIN));
+ ssh_block2waitfor(data, sshc, (nread == LIBSSH2_ERROR_EAGAIN));
if(nread == LIBSSH2_ERROR_EAGAIN) {
*err = CURLE_AGAIN;
@@ -3759,25 +4054,21 @@ static const char *sftp_libssh2_strerror(unsigned long err)
CURLcode Curl_ssh_init(void)
{
-#ifdef HAVE_LIBSSH2_INIT
if(libssh2_init(0)) {
DEBUGF(fprintf(stderr, "Error: libssh2_init failed\n"));
return CURLE_FAILED_INIT;
}
-#endif
return CURLE_OK;
}
void Curl_ssh_cleanup(void)
{
-#ifdef HAVE_LIBSSH2_EXIT
(void)libssh2_exit();
-#endif
}
void Curl_ssh_version(char *buffer, size_t buflen)
{
- (void)msnprintf(buffer, buflen, "libssh2/%s", CURL_LIBSSH2_VERSION);
+ (void)msnprintf(buffer, buflen, "libssh2/%s", libssh2_version(0));
}
/* The SSH session is associated with the *CONNECTION* but the callback user
@@ -3789,8 +4080,8 @@ static void ssh_attach(struct Curl_easy *data, struct connectdata *conn)
DEBUGASSERT(data);
DEBUGASSERT(conn);
if(conn->handler->protocol & PROTO_FAMILY_SSH) {
- struct ssh_conn *sshc = &conn->proto.sshc;
- if(sshc->ssh_session) {
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+ if(sshc && sshc->ssh_session) {
/* only re-attach if the session already exists */
void **abstract = libssh2_session_abstract(sshc->ssh_session);
*abstract = data;
diff --git a/libs/libcurl/src/vssh/ssh.h b/libs/libcurl/src/vssh/ssh.h
index 192c1d7970..73aec97a97 100644
--- a/libs/libcurl/src/vssh/ssh.h
+++ b/libs/libcurl/src/vssh/ssh.h
@@ -24,7 +24,7 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_LIBSSH2)
#include <libssh2.h>
@@ -41,6 +41,11 @@
#include "curl_path.h"
+/* meta key for storing protocol meta at easy handle */
+#define CURL_META_SSH_EASY "meta:proto:ssh:easy"
+/* meta key for storing protocol meta at connection */
+#define CURL_META_SSH_CONN "meta:proto:ssh:conn"
+
/****************************************************************************
* SSH unique setup
***************************************************************************/
@@ -141,12 +146,8 @@ struct ssh_conn {
const char *passphrase; /* pass-phrase to use */
char *rsa_pub; /* strdup'ed public key file */
char *rsa; /* strdup'ed private key file */
- bool authed; /* the connection has been authenticated fine */
- bool acceptfail; /* used by the SFTP_QUOTE (continue if
- quote command fails) */
sshstate state; /* always use ssh.c:state() to change state! */
sshstate nextstate; /* the state to goto after stopping */
- CURLcode actualcode; /* the actual error code */
struct curl_slist *quote_item; /* for the quote option */
char *quote_path1; /* two generic pointers for the QUOTE stuff */
char *quote_path2;
@@ -162,6 +163,7 @@ struct ssh_conn {
char *slash_pos; /* used by the SFTP_CREATE_DIRS state */
#if defined(USE_LIBSSH)
+ CURLcode actualcode; /* the actual error code */
char *readdir_linkPath;
size_t readdir_len;
struct dynbuf readdir_buf;
@@ -177,6 +179,10 @@ struct ssh_conn {
sftp_dir sftp_dir;
unsigned sftp_recv_state; /* 0 or 1 */
+#if LIBSSH_VERSION_INT > SSH_VERSION_INT(0, 11, 0)
+ sftp_aio sftp_aio;
+ unsigned sftp_send_state; /* 0 or 1 */
+#endif
int sftp_file_index; /* for async read */
sftp_attributes readdir_attrs; /* used by the SFTP readdir actions */
sftp_attributes readdir_link_attrs; /* used by the SFTP readdir actions */
@@ -197,63 +203,38 @@ struct ssh_conn {
Curl_send *tls_send;
#endif
-#ifdef HAVE_LIBSSH2_AGENT_API
LIBSSH2_AGENT *ssh_agent; /* proxy to ssh-agent/pageant */
- struct libssh2_agent_publickey *sshagent_identity,
- *sshagent_prev_identity;
-#endif
-
- /* note that HAVE_LIBSSH2_KNOWNHOST_API is a define set in the libssh2.h
- header */
-#ifdef HAVE_LIBSSH2_KNOWNHOST_API
+ struct libssh2_agent_publickey *sshagent_identity;
+ struct libssh2_agent_publickey *sshagent_prev_identity;
LIBSSH2_KNOWNHOSTS *kh;
-#endif
#elif defined(USE_WOLFSSH)
+ CURLcode actualcode; /* the actual error code */
WOLFSSH *ssh_session;
WOLFSSH_CTX *ctx;
word32 handleSz;
byte handle[WOLFSSH_MAX_HANDLE];
curl_off_t offset;
#endif /* USE_LIBSSH */
+ BIT(initialised);
+ BIT(authed); /* the connection has been authenticated fine */
+ BIT(acceptfail); /* used by the SFTP_QUOTE (continue if
+ quote command fails) */
};
-#if defined(USE_LIBSSH2)
-
-/* Feature detection based on version numbers to better work with
- non-configure platforms */
-
-#if !defined(LIBSSH2_VERSION_NUM) || (LIBSSH2_VERSION_NUM < 0x001000)
-# error "SCP/SFTP protocols require libssh2 0.16 or later"
-#endif
-
-#if LIBSSH2_VERSION_NUM >= 0x010000
-#define HAVE_LIBSSH2_SFTP_SEEK64 1
+#ifdef USE_LIBSSH
+#if LIBSSH_VERSION_INT < SSH_VERSION_INT(0, 9, 0)
+# error "SCP/SFTP protocols require libssh 0.9.0 or later"
#endif
-
-#if LIBSSH2_VERSION_NUM >= 0x010100
-#define HAVE_LIBSSH2_VERSION 1
-#endif
-
-#if LIBSSH2_VERSION_NUM >= 0x010205
-#define HAVE_LIBSSH2_INIT 1
-#define HAVE_LIBSSH2_EXIT 1
#endif
-#if LIBSSH2_VERSION_NUM >= 0x010206
-#define HAVE_LIBSSH2_KNOWNHOST_CHECKP 1
-#define HAVE_LIBSSH2_SCP_SEND64 1
-#endif
+#if defined(USE_LIBSSH2)
-#if LIBSSH2_VERSION_NUM >= 0x010208
-#define HAVE_LIBSSH2_SESSION_HANDSHAKE 1
-#endif
+/* Feature detection based on version numbers to better work with
+ non-configure platforms */
-#ifdef HAVE_LIBSSH2_VERSION
-/* get it runtime if possible */
-#define CURL_LIBSSH2_VERSION libssh2_version(0)
-#else
-/* use build-time if runtime not possible */
-#define CURL_LIBSSH2_VERSION LIBSSH2_VERSION
+#if !defined(LIBSSH2_VERSION_NUM) || (LIBSSH2_VERSION_NUM < 0x010208)
+# error "SCP/SFTP protocols require libssh2 1.2.8 or later"
+/* 1.2.8 was released on April 5 2011 */
#endif
#endif /* USE_LIBSSH2 */
diff --git a/libs/libcurl/src/vssh/wolfssh.c b/libs/libcurl/src/vssh/wolfssh.c
index 377aa99bcc..7bf3e8a90b 100644
--- a/libs/libcurl/src/vssh/wolfssh.c
+++ b/libs/libcurl/src/vssh/wolfssh.c
@@ -22,30 +22,30 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_WOLFSSH
#include <limits.h>
-#include "urldata.h"
-#include "cfilters.h"
-#include "connect.h"
-#include "sendf.h"
-#include "progress.h"
+#include "../urldata.h"
+#include "../url.h"
+#include "../cfilters.h"
+#include "../connect.h"
+#include "../sendf.h"
+#include "../progress.h"
#include "curl_path.h"
-#include "strtoofft.h"
-#include "transfer.h"
-#include "speedcheck.h"
-#include "select.h"
-#include "multiif.h"
-#include "warnless.h"
-#include "strdup.h"
+#include "../transfer.h"
+#include "../speedcheck.h"
+#include "../select.h"
+#include "../multiif.h"
+#include "../curlx/warnless.h"
+#include "../strdup.h"
/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
static CURLcode wssh_connect(struct Curl_easy *data, bool *done);
static CURLcode wssh_multi_statemach(struct Curl_easy *data, bool *done);
@@ -71,6 +71,7 @@ static int wssh_getsock(struct Curl_easy *data,
curl_socket_t *sock);
static CURLcode wssh_setup_connection(struct Curl_easy *data,
struct connectdata *conn);
+static void wssh_sshc_cleanup(struct ssh_conn *sshc);
#if 0
/*
@@ -95,6 +96,7 @@ const struct Curl_handler Curl_handler_scp = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_SSH, /* defport */
CURLPROTO_SCP, /* protocol */
PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION
@@ -125,6 +127,7 @@ const struct Curl_handler Curl_handler_sftp = {
ZERO_NULL, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ ZERO_NULL, /* follow */
PORT_SSH, /* defport */
CURLPROTO_SFTP, /* protocol */
CURLPROTO_SFTP, /* family */
@@ -136,10 +139,10 @@ const struct Curl_handler Curl_handler_sftp = {
* SSH State machine related code
*/
/* This is the ONLY way to change SSH state! */
-static void state(struct Curl_easy *data, sshstate nowstate)
+static void wssh_state(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ sshstate nowstate)
{
- struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
/* for debug purposes */
static const char * const names[] = {
@@ -206,14 +209,14 @@ static void state(struct Curl_easy *data, sshstate nowstate)
};
/* a precaution to make sure the lists are in sync */
- DEBUGASSERT(sizeof(names)/sizeof(names[0]) == SSH_LAST);
+ DEBUGASSERT(CURL_ARRAYSIZE(names) == SSH_LAST);
if(sshc->state != nowstate) {
infof(data, "wolfssh %p state change from %s to %s",
(void *)sshc, names[sshc->state], names[nowstate]);
}
#endif
-
+ (void)data;
sshc->state = nowstate;
}
@@ -250,19 +253,23 @@ static ssize_t wsftp_send(struct Curl_easy *data, int sockindex,
const void *mem, size_t len, bool eos, CURLcode *err)
{
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
word32 offset[2];
int rc;
(void)sockindex;
(void)eos;
+ if(!sshc) {
+ *err = CURLE_FAILED_INIT;
+ return -1;
+ }
offset[0] = (word32)sshc->offset & 0xFFFFFFFF;
offset[1] = (word32)(sshc->offset >> 32) & 0xFFFFFFFF;
rc = wolfSSH_SFTP_SendWritePacket(sshc->ssh_session, sshc->handle,
sshc->handleSz,
&offset[0],
- (byte *)mem, (word32)len);
+ (byte *)CURL_UNCONST(mem), (word32)len);
if(rc == WS_FATAL_ERROR)
rc = wolfSSH_get_error(sshc->ssh_session);
@@ -296,10 +303,14 @@ static ssize_t wsftp_recv(struct Curl_easy *data, int sockindex,
{
int rc;
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
word32 offset[2];
(void)sockindex;
+ if(!sshc) {
+ *err = CURLE_FAILED_INIT;
+ return -1;
+ }
offset[0] = (word32)sshc->offset & 0xFFFFFFFF;
offset[1] = (word32)(sshc->offset >> 32) & 0xFFFFFFFF;
@@ -331,17 +342,45 @@ static ssize_t wsftp_recv(struct Curl_easy *data, int sockindex,
return (ssize_t)rc;
}
+static void wssh_easy_dtor(void *key, size_t klen, void *entry)
+{
+ struct SSHPROTO *sshp = entry;
+ (void)key;
+ (void)klen;
+ Curl_safefree(sshp->path);
+ free(sshp);
+}
+
+static void wssh_conn_dtor(void *key, size_t klen, void *entry)
+{
+ struct ssh_conn *sshc = entry;
+ (void)key;
+ (void)klen;
+ wssh_sshc_cleanup(sshc);
+ free(sshc);
+}
+
/*
* SSH setup and connection
*/
static CURLcode wssh_setup_connection(struct Curl_easy *data,
struct connectdata *conn)
{
- struct SSHPROTO *ssh;
+ struct ssh_conn *sshc;
+ struct SSHPROTO *sshp;
(void)conn;
- data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO));
- if(!ssh)
+ sshc = calloc(1, sizeof(*sshc));
+ if(!sshc)
+ return CURLE_OUT_OF_MEMORY;
+
+ sshc->initialised = TRUE;
+ if(Curl_conn_meta_set(conn, CURL_META_SSH_CONN, sshc, wssh_conn_dtor))
+ return CURLE_OUT_OF_MEMORY;
+
+ sshp = calloc(1, sizeof(*sshp));
+ if(!sshp ||
+ Curl_meta_set(data, CURL_META_SSH_EASY, sshp, wssh_easy_dtor))
return CURLE_OUT_OF_MEMORY;
return CURLE_OK;
@@ -366,13 +405,13 @@ static int userauth(byte authtype,
static CURLcode wssh_connect(struct Curl_easy *data, bool *done)
{
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
+ struct SSHPROTO *sshp = Curl_meta_get(data, CURL_META_SSH_EASY);
curl_socket_t sock = conn->sock[FIRSTSOCKET];
int rc;
- /* initialize per-handle data if not already */
- if(!data->req.p.ssh)
- wssh_setup_connection(data, conn);
+ if(!sshc || !sshp)
+ return CURLE_FAILED_INIT;
/* We default to persistent connections. We set this already in this connect
function to make the reuse checks properly be able to check this bit. */
@@ -386,7 +425,6 @@ static CURLcode wssh_connect(struct Curl_easy *data, bool *done)
conn->recv[FIRSTSOCKET] = wsftp_recv;
conn->send[FIRSTSOCKET] = wsftp_send;
}
- sshc = &conn->proto.sshc;
sshc->ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL);
if(!sshc->ctx) {
failf(data, "No wolfSSH context");
@@ -421,14 +459,13 @@ static CURLcode wssh_connect(struct Curl_easy *data, bool *done)
*done = TRUE;
if(conn->handler->protocol & CURLPROTO_SCP)
- state(data, SSH_INIT);
+ wssh_state(data, sshc, SSH_INIT);
else
- state(data, SSH_SFTP_INIT);
+ wssh_state(data, sshc, SSH_SFTP_INIT);
return wssh_multi_statemach(data, done);
error:
- wolfSSH_free(sshc->ssh_session);
- wolfSSH_CTX_free(sshc->ctx);
+ wssh_sshc_cleanup(sshc);
return CURLE_FAILED_INIT;
}
@@ -439,20 +476,24 @@ error:
* wants to be called again when the socket is ready
*/
-static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
+static CURLcode wssh_statemach_act(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ bool *block)
{
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
- struct SSHPROTO *sftp_scp = data->req.p.ssh;
+ struct SSHPROTO *sftp_scp = Curl_meta_get(data, CURL_META_SSH_EASY);
WS_SFTPNAME *name;
int rc = 0;
*block = FALSE; /* we are not blocking by default */
+ if(!sftp_scp)
+ return CURLE_FAILED_INIT;
+
do {
switch(sshc->state) {
case SSH_INIT:
- state(data, SSH_S_STARTUP);
+ wssh_state(data, sshc, SSH_S_STARTUP);
break;
case SSH_S_STARTUP:
@@ -470,11 +511,11 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
return CURLE_OK;
}
else if(rc != WS_SUCCESS) {
- state(data, SSH_STOP);
+ wssh_state(data, sshc, SSH_STOP);
return CURLE_SSH;
}
infof(data, "wolfssh connected");
- state(data, SSH_STOP);
+ wssh_state(data, sshc, SSH_STOP);
break;
case SSH_STOP:
break;
@@ -495,7 +536,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
}
else if(rc == WS_SUCCESS) {
infof(data, "wolfssh SFTP connected");
- state(data, SSH_SFTP_REALPATH);
+ wssh_state(data, sshc, SSH_SFTP_REALPATH);
}
else {
failf(data, "wolfssh SFTP connect error %d", rc);
@@ -503,7 +544,8 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
}
break;
case SSH_SFTP_REALPATH:
- name = wolfSSH_SFTP_RealPath(sshc->ssh_session, (char *)".");
+ name = wolfSSH_SFTP_RealPath(sshc->ssh_session,
+ (char *)CURL_UNCONST("."));
rc = wolfSSH_get_error(sshc->ssh_session);
if(rc == WS_WANT_READ) {
*block = TRUE;
@@ -520,7 +562,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
if(!sshc->homedir)
sshc->actualcode = CURLE_OUT_OF_MEMORY;
wolfSSH_SFTPNAME_list_free(name);
- state(data, SSH_STOP);
+ wssh_state(data, sshc, SSH_STOP);
return CURLE_OK;
}
failf(data, "wolfssh SFTP realpath %d", rc);
@@ -530,35 +572,35 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
result = Curl_getworkingpath(data, sshc->homedir, &sftp_scp->path);
if(result) {
sshc->actualcode = result;
- state(data, SSH_STOP);
+ wssh_state(data, sshc, SSH_STOP);
break;
}
if(data->set.quote) {
infof(data, "Sending quote commands");
sshc->quote_item = data->set.quote;
- state(data, SSH_SFTP_QUOTE);
+ wssh_state(data, sshc, SSH_SFTP_QUOTE);
}
else {
- state(data, SSH_SFTP_GETINFO);
+ wssh_state(data, sshc, SSH_SFTP_GETINFO);
}
break;
case SSH_SFTP_GETINFO:
if(data->set.get_filetime) {
- state(data, SSH_SFTP_FILETIME);
+ wssh_state(data, sshc, SSH_SFTP_FILETIME);
}
else {
- state(data, SSH_SFTP_TRANS_INIT);
+ wssh_state(data, sshc, SSH_SFTP_TRANS_INIT);
}
break;
case SSH_SFTP_TRANS_INIT:
if(data->state.upload)
- state(data, SSH_SFTP_UPLOAD_INIT);
+ wssh_state(data, sshc, SSH_SFTP_UPLOAD_INIT);
else {
if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/')
- state(data, SSH_SFTP_READDIR_INIT);
+ wssh_state(data, sshc, SSH_SFTP_READDIR_INIT);
else
- state(data, SSH_SFTP_DOWNLOAD_INIT);
+ wssh_state(data, sshc, SSH_SFTP_DOWNLOAD_INIT);
}
break;
case SSH_SFTP_UPLOAD_INIT: {
@@ -621,7 +663,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
failf(data, "wolfssh SFTP upload open failed: %d", rc);
return CURLE_SSH;
}
- state(data, SSH_SFTP_DOWNLOAD_STAT);
+ wssh_state(data, sshc, SSH_SFTP_DOWNLOAD_STAT);
/* If we have a restart point then we need to seek to the correct
position. */
@@ -687,7 +729,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
conn->sockfd = conn->writesockfd;
if(result) {
- state(data, SSH_SFTP_CLOSE);
+ wssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->actualcode = result;
}
else {
@@ -705,7 +747,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
timeout here */
Curl_expire(data, 0, EXPIRE_RUN_NOW);
- state(data, SSH_STOP);
+ wssh_state(data, sshc, SSH_STOP);
}
break;
}
@@ -728,7 +770,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
}
else if(rc == WS_SUCCESS) {
infof(data, "wolfssh SFTP open succeeded");
- state(data, SSH_SFTP_DOWNLOAD_STAT);
+ wssh_state(data, sshc, SSH_SFTP_DOWNLOAD_STAT);
return CURLE_OK;
}
@@ -783,7 +825,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
/* no data to transfer */
Curl_xfer_setup_nop(data);
infof(data, "File already completely downloaded");
- state(data, SSH_STOP);
+ wssh_state(data, sshc, SSH_STOP);
break;
}
Curl_xfer_setup1(data, CURL_XFER_RECV, data->req.size, FALSE);
@@ -799,20 +841,24 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
if(result) {
/* this should never occur; the close state should be entered
at the time the error occurs */
- state(data, SSH_SFTP_CLOSE);
+ wssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->actualcode = result;
}
else {
- state(data, SSH_STOP);
+ wssh_state(data, sshc, SSH_STOP);
}
break;
}
case SSH_SFTP_CLOSE:
- if(sshc->handleSz)
+ if(sshc->handleSz) {
rc = wolfSSH_SFTP_Close(sshc->ssh_session, sshc->handle,
sshc->handleSz);
- else
+ if(rc != WS_SUCCESS)
+ rc = wolfSSH_get_error(sshc->ssh_session);
+ }
+ else {
rc = WS_SUCCESS; /* directory listing */
+ }
if(rc == WS_WANT_READ) {
*block = TRUE;
conn->waitfor = KEEP_RECV;
@@ -824,7 +870,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
return CURLE_OK;
}
else if(rc == WS_SUCCESS) {
- state(data, SSH_STOP);
+ wssh_state(data, sshc, SSH_STOP);
return CURLE_OK;
}
@@ -834,10 +880,10 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
case SSH_SFTP_READDIR_INIT:
Curl_pgrsSetDownloadSize(data, -1);
if(data->req.no_body) {
- state(data, SSH_STOP);
+ wssh_state(data, sshc, SSH_STOP);
break;
}
- state(data, SSH_SFTP_READDIR);
+ wssh_state(data, sshc, SSH_SFTP_READDIR);
break;
case SSH_SFTP_READDIR:
@@ -865,7 +911,7 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
data->set.list_only ?
name->fName : name->lName);
if(!line) {
- state(data, SSH_SFTP_CLOSE);
+ wssh_state(data, sshc, SSH_SFTP_CLOSE);
sshc->actualcode = CURLE_OUT_OF_MEMORY;
break;
}
@@ -879,17 +925,15 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
name = name->next;
}
wolfSSH_SFTPNAME_list_free(origname);
- state(data, SSH_STOP);
+ wssh_state(data, sshc, SSH_STOP);
return result;
}
failf(data, "wolfssh SFTP ls failed: %d", rc);
return CURLE_SSH;
case SSH_SFTP_SHUTDOWN:
- Curl_safefree(sshc->homedir);
- wolfSSH_free(sshc->ssh_session);
- wolfSSH_CTX_free(sshc->ctx);
- state(data, SSH_STOP);
+ wssh_sshc_cleanup(sshc);
+ wssh_state(data, sshc, SSH_STOP);
return CURLE_OK;
default:
break;
@@ -902,12 +946,15 @@ static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
static CURLcode wssh_multi_statemach(struct Curl_easy *data, bool *done)
{
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
CURLcode result = CURLE_OK;
bool block; /* we store the status and use that to provide a ssh_getsock()
implementation */
+ if(!sshc)
+ return CURLE_FAILED_INIT;
+
do {
- result = wssh_statemach_act(data, &block);
+ result = wssh_statemach_act(data, sshc, &block);
*done = (sshc->state == SSH_STOP);
/* if there is no error, it is not done and it did not EWOULDBLOCK, then
try again */
@@ -932,6 +979,7 @@ CURLcode wscp_perform(struct Curl_easy *data,
static
CURLcode wsftp_perform(struct Curl_easy *data,
+ struct ssh_conn *sshc,
bool *connected,
bool *dophase_done)
{
@@ -942,7 +990,7 @@ CURLcode wsftp_perform(struct Curl_easy *data,
*dophase_done = FALSE; /* not done yet */
/* start the first command in the DO phase */
- state(data, SSH_SFTP_QUOTE_INIT);
+ wssh_state(data, sshc, SSH_SFTP_QUOTE_INIT);
/* run the state-machine */
result = wssh_multi_statemach(data, dophase_done);
@@ -964,9 +1012,12 @@ static CURLcode wssh_do(struct Curl_easy *data, bool *done)
CURLcode result;
bool connected = FALSE;
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
*done = FALSE; /* default to false */
+ if(!sshc)
+ return CURLE_FAILED_INIT;
+
data->req.size = -1; /* make sure this is unknown at this point */
sshc->actualcode = CURLE_OK; /* reset error code */
sshc->secondCreateDirs = 0; /* reset the create dir attempt state
@@ -980,24 +1031,24 @@ static CURLcode wssh_do(struct Curl_easy *data, bool *done)
if(conn->handler->protocol & CURLPROTO_SCP)
result = wscp_perform(data, &connected, done);
else
- result = wsftp_perform(data, &connected, done);
+ result = wsftp_perform(data, sshc, &connected, done);
return result;
}
static CURLcode wssh_block_statemach(struct Curl_easy *data,
- bool disconnect)
+ struct ssh_conn *sshc,
+ bool disconnect)
{
struct connectdata *conn = data->conn;
- struct ssh_conn *sshc = &conn->proto.sshc;
CURLcode result = CURLE_OK;
while((sshc->state != SSH_STOP) && !result) {
bool block;
timediff_t left = 1000;
- struct curltime now = Curl_now();
+ struct curltime now = curlx_now();
- result = wssh_statemach_act(data, &block);
+ result = wssh_statemach_act(data, sshc, &block);
if(result)
break;
@@ -1037,20 +1088,19 @@ static CURLcode wssh_block_statemach(struct Curl_easy *data,
/* generic done function for both SCP and SFTP called from their specific
done functions */
-static CURLcode wssh_done(struct Curl_easy *data, CURLcode status)
+static CURLcode wssh_done(struct Curl_easy *data,
+ struct ssh_conn *sshc,
+ CURLcode status)
{
CURLcode result = CURLE_OK;
- struct SSHPROTO *sftp_scp = data->req.p.ssh;
if(!status) {
/* run the state-machine */
- result = wssh_block_statemach(data, FALSE);
+ result = wssh_block_statemach(data, sshc, FALSE);
}
else
result = status;
- if(sftp_scp)
- Curl_safefree(sftp_scp->path);
if(Curl_pgrsDone(data))
return CURLE_ABORTED_BY_CALLBACK;
@@ -1058,6 +1108,19 @@ static CURLcode wssh_done(struct Curl_easy *data, CURLcode status)
return result;
}
+static void wssh_sshc_cleanup(struct ssh_conn *sshc)
+{
+ if(sshc->ssh_session) {
+ wolfSSH_free(sshc->ssh_session);
+ sshc->ssh_session = NULL;
+ }
+ if(sshc->ctx) {
+ wolfSSH_CTX_free(sshc->ctx);
+ sshc->ctx = NULL;
+ }
+ Curl_safefree(sshc->homedir);
+}
+
#if 0
static CURLcode wscp_done(struct Curl_easy *data,
CURLcode code, bool premature)
@@ -1083,11 +1146,11 @@ static CURLcode wscp_doing(struct Curl_easy *data,
static CURLcode wscp_disconnect(struct Curl_easy *data,
struct connectdata *conn, bool dead_connection)
{
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
CURLcode result = CURLE_OK;
- (void)data;
- (void)conn;
(void)dead_connection;
-
+ if(sshc)
+ wssh_sshc_cleanup(sshc);
return result;
}
#endif
@@ -1095,10 +1158,14 @@ static CURLcode wscp_disconnect(struct Curl_easy *data,
static CURLcode wsftp_done(struct Curl_easy *data,
CURLcode code, bool premature)
{
+ struct ssh_conn *sshc = Curl_conn_meta_get(data->conn, CURL_META_SSH_CONN);
(void)premature;
- state(data, SSH_SFTP_CLOSE);
+ if(!sshc)
+ return CURLE_FAILED_INIT;
+
+ wssh_state(data, sshc, SSH_SFTP_CLOSE);
- return wssh_done(data, code);
+ return wssh_done(data, sshc, code);
}
static CURLcode wsftp_doing(struct Curl_easy *data,
@@ -1116,17 +1183,20 @@ static CURLcode wsftp_disconnect(struct Curl_easy *data,
struct connectdata *conn,
bool dead)
{
+ struct ssh_conn *sshc = Curl_conn_meta_get(conn, CURL_META_SSH_CONN);
CURLcode result = CURLE_OK;
(void)dead;
DEBUGF(infof(data, "SSH DISCONNECT starts now"));
- if(conn->proto.sshc.ssh_session) {
+ if(sshc && sshc->ssh_session) {
/* only if there is a session still around to use! */
- state(data, SSH_SFTP_SHUTDOWN);
- result = wssh_block_statemach(data, TRUE);
+ wssh_state(data, sshc, SSH_SFTP_SHUTDOWN);
+ result = wssh_block_statemach(data, sshc, TRUE);
}
+ if(sshc)
+ wssh_sshc_cleanup(sshc);
DEBUGF(infof(data, "SSH DISCONNECT is done"));
return result;
}
diff --git a/libs/libcurl/src/vtls/bearssl.c b/libs/libcurl/src/vtls/bearssl.c
index f4d0cc43b3..4df187dc50 100644
--- a/libs/libcurl/src/vtls/bearssl.c
+++ b/libs/libcurl/src/vtls/bearssl.c
@@ -21,7 +21,7 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_BEARSSL
@@ -29,19 +29,20 @@
#include "bearssl.h"
#include "cipher_suite.h"
-#include "urldata.h"
-#include "sendf.h"
-#include "inet_pton.h"
+#include "../urldata.h"
+#include "../sendf.h"
+#include "../curlx/inet_pton.h"
#include "vtls.h"
#include "vtls_int.h"
-#include "connect.h"
-#include "select.h"
-#include "multiif.h"
-#include "curl_printf.h"
+#include "vtls_scache.h"
+#include "../connect.h"
+#include "../select.h"
+#include "../multiif.h"
+#include "../curl_printf.h"
/* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
struct x509_context {
const br_x509_class *vtable;
@@ -152,7 +153,7 @@ static CURLcode load_cafile(struct cafile_source *source,
}
else if(source->type == CAFILE_SOURCE_BLOB) {
n = source->len;
- p = (unsigned char *) source->data;
+ p = (const unsigned char *) source->data;
}
while(n) {
pushed = br_pem_decoder_push(&pc, p, n);
@@ -337,7 +338,7 @@ static unsigned x509_end_chain(const br_x509_class **ctx)
static const br_x509_pkey *x509_get_pkey(const br_x509_class *const *ctx,
unsigned *usages)
{
- struct x509_context *x509 = (struct x509_context *)ctx;
+ struct x509_context *x509 = (struct x509_context *)CURL_UNCONST(ctx);
if(!x509->verifypeer) {
/* Nothing in the chain is verified, just return the public key of the
@@ -483,7 +484,7 @@ static const uint16_t ciphertable[] = {
BR_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, /* 0xCCA9 */
};
-#define NUM_OF_CIPHERS (sizeof(ciphertable) / sizeof(ciphertable[0]))
+#define NUM_OF_CIPHERS CURL_ARRAYSIZE(ciphertable)
static CURLcode bearssl_set_selected_ciphers(struct Curl_easy *data,
br_ssl_engine_context *ssl_eng,
@@ -609,20 +610,19 @@ static CURLcode bearssl_connect_step1(struct Curl_cfilter *cf,
br_ssl_engine_set_x509(&backend->ctx.eng, &backend->x509.vtable);
if(ssl_config->primary.cache_session) {
- void *sdata;
- size_t slen;
- const br_ssl_session_parameters *session;
-
- CURL_TRC_CF(data, cf, "connect_step1, check session cache");
- Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, &connssl->peer, &sdata, &slen, NULL) &&
- slen == sizeof(*session)) {
- session = sdata;
+ struct Curl_ssl_session *sc_session = NULL;
+
+ ret = Curl_ssl_scache_take(cf, data, connssl->peer.scache_key,
+ &sc_session);
+ if(!ret && sc_session && sc_session->sdata && sc_session->sdata_len) {
+ const br_ssl_session_parameters *session;
+ session = (const br_ssl_session_parameters *)sc_session->sdata;
br_ssl_engine_set_session_parameters(&backend->ctx.eng, session);
session_set = 1;
infof(data, "BearSSL: reusing session ID");
+ /* single use of sessions */
+ Curl_ssl_scache_return(cf, data, connssl->peer.scache_key, sc_session);
}
- Curl_ssl_sessionid_unlock(data);
}
if(connssl->alpn) {
@@ -729,7 +729,7 @@ static CURLcode bearssl_run_until(struct Curl_cfilter *cf,
return CURLE_OK;
if(state & BR_SSL_SENDREC) {
buf = br_ssl_engine_sendrec_buf(&backend->ctx.eng, &len);
- ret = Curl_conn_cf_send(cf->next, data, (char *)buf, len, FALSE,
+ ret = Curl_conn_cf_send(cf->next, data, (const char *)buf, len, FALSE,
&result);
CURL_TRC_CF(data, cf, "ssl_send(len=%zu) -> %zd, %d", len, ret, result);
if(ret <= 0) {
@@ -804,12 +804,6 @@ static CURLcode bearssl_connect_step2(struct Curl_cfilter *cf,
return ret;
}
-static void bearssl_session_free(void *sessionid, size_t idsize)
-{
- (void)idsize;
- free(sessionid);
-}
-
static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
@@ -832,17 +826,22 @@ static CURLcode bearssl_connect_step3(struct Curl_cfilter *cf,
}
if(ssl_config->primary.cache_session) {
+ struct Curl_ssl_session *sc_session;
br_ssl_session_parameters *session;
session = malloc(sizeof(*session));
if(!session)
return CURLE_OUT_OF_MEMORY;
br_ssl_engine_get_session_parameters(&backend->ctx.eng, session);
- Curl_ssl_sessionid_lock(data);
- ret = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL,
- session, sizeof(*session),
- bearssl_session_free);
- Curl_ssl_sessionid_unlock(data);
+ ret = Curl_ssl_session_create((unsigned char *)session, sizeof(*session),
+ (int)session->version,
+ connssl->negotiated.alpn,
+ 0, 0, &sc_session);
+ if(!ret) {
+ ret = Curl_ssl_scache_put(cf, data, connssl->peer.scache_key,
+ sc_session);
+ /* took ownership of `sc_session` */
+ }
if(ret)
return ret;
}
@@ -912,18 +911,14 @@ static ssize_t bearssl_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
return applen;
}
-static CURLcode bearssl_connect_common(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool nonblocking,
- bool *done)
+static CURLcode bearssl_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done)
{
CURLcode ret;
struct ssl_connect_data *connssl = cf->ctx;
- curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
- timediff_t timeout_ms;
- int what;
- CURL_TRC_CF(data, cf, "connect_common(blocking=%d)", !nonblocking);
+ CURL_TRC_CF(data, cf, "connect()");
/* check if the connection has already been established */
if(ssl_connection_complete == connssl->state) {
CURL_TRC_CF(data, cf, "connect_common, connected");
@@ -931,61 +926,18 @@ static CURLcode bearssl_connect_common(struct Curl_cfilter *cf,
return CURLE_OK;
}
+ *done = FALSE;
+ connssl->io_need = CURL_SSL_IO_NEED_NONE;
+
if(ssl_connect_1 == connssl->connecting_state) {
ret = bearssl_connect_step1(cf, data);
if(ret)
return ret;
}
- while(ssl_connect_2 == connssl->connecting_state) {
- /* check allowed time left */
- timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
- if(timeout_ms < 0) {
- /* no need to continue if time already is up */
- failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
-
- /* if ssl is expecting something, check if it is available. */
- if(connssl->io_need) {
- curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND) ?
- sockfd : CURL_SOCKET_BAD;
- curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV) ?
- sockfd : CURL_SOCKET_BAD;
-
- CURL_TRC_CF(data, cf, "connect_common, check socket");
- what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
- nonblocking ? 0 : timeout_ms);
- CURL_TRC_CF(data, cf, "connect_common, check socket -> %d", what);
- if(what < 0) {
- /* fatal error */
- failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
- return CURLE_SSL_CONNECT_ERROR;
- }
- else if(0 == what) {
- if(nonblocking) {
- *done = FALSE;
- return CURLE_OK;
- }
- else {
- /* timeout */
- failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
- }
- /* socket is readable or writable */
- }
-
- /* Run transaction, and return to the caller if it failed or if this
- * connection is done nonblocking and this loop would execute again. This
- * permits the owner of a multi handle to abort a connection attempt
- * before step2 has completed while ensuring that a client using select()
- * or epoll() will always have a valid fdset to wait on.
- */
- connssl->io_need = CURL_SSL_IO_NEED_NONE;
+ if(ssl_connect_2 == connssl->connecting_state) {
ret = bearssl_connect_step2(cf, data);
- if(ret || (nonblocking && (ssl_connect_2 == connssl->connecting_state)))
+ if(ret)
return ret;
}
@@ -999,11 +951,6 @@ static CURLcode bearssl_connect_common(struct Curl_cfilter *cf,
connssl->state = ssl_connection_complete;
*done = TRUE;
}
- else
- *done = FALSE;
-
- /* Reset our connect state machine */
- connssl->connecting_state = ssl_connect_1;
return CURLE_OK;
}
@@ -1045,28 +992,6 @@ static CURLcode bearssl_random(struct Curl_easy *data UNUSED_PARAM,
return CURLE_OK;
}
-static CURLcode bearssl_connect(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- CURLcode ret;
- bool done = FALSE;
-
- ret = bearssl_connect_common(cf, data, FALSE, &done);
- if(ret)
- return ret;
-
- DEBUGASSERT(done);
-
- return CURLE_OK;
-}
-
-static CURLcode bearssl_connect_nonblocking(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool *done)
-{
- return bearssl_connect_common(cf, data, TRUE, done);
-}
-
static void *bearssl_get_internals(struct ssl_connect_data *connssl,
CURLINFO info UNUSED_PARAM)
{
@@ -1154,27 +1079,23 @@ const struct Curl_ssl Curl_ssl_bearssl = {
sizeof(struct bearssl_ssl_backend_data),
- Curl_none_init, /* init */
- Curl_none_cleanup, /* cleanup */
+ NULL, /* init */
+ NULL, /* cleanup */
bearssl_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
bearssl_shutdown, /* shutdown */
bearssl_data_pending, /* data_pending */
bearssl_random, /* random */
- Curl_none_cert_status_request, /* cert_status_request */
+ NULL, /* cert_status_request */
bearssl_connect, /* connect */
- bearssl_connect_nonblocking, /* connect_nonblocking */
Curl_ssl_adjust_pollset, /* adjust_pollset */
bearssl_get_internals, /* get_internals */
bearssl_close, /* close_one */
- Curl_none_close_all, /* close_all */
- Curl_none_set_engine, /* set_engine */
- Curl_none_set_engine_default, /* set_engine_default */
- Curl_none_engines_list, /* engines_list */
- Curl_none_false_start, /* false_start */
+ NULL, /* close_all */
+ NULL, /* set_engine */
+ NULL, /* set_engine_default */
+ NULL, /* engines_list */
+ NULL, /* false_start */
bearssl_sha256sum, /* sha256sum */
- NULL, /* associate_connection */
- NULL, /* disassociate_connection */
bearssl_recv, /* recv decrypted data */
bearssl_send, /* send data to encrypt */
NULL, /* get_channel_binding */
diff --git a/libs/libcurl/src/vtls/bearssl.h b/libs/libcurl/src/vtls/bearssl.h
index 76857f9419..aca1a7bd6a 100644
--- a/libs/libcurl/src/vtls/bearssl.h
+++ b/libs/libcurl/src/vtls/bearssl.h
@@ -24,7 +24,7 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_BEARSSL
diff --git a/libs/libcurl/src/vtls/cipher_suite.c b/libs/libcurl/src/vtls/cipher_suite.c
index ca7c5ba46d..68bf3b6305 100644
--- a/libs/libcurl/src/vtls/cipher_suite.c
+++ b/libs/libcurl/src/vtls/cipher_suite.c
@@ -21,13 +21,13 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || \
defined(USE_BEARSSL) || defined(USE_RUSTLS)
#include "cipher_suite.h"
-#include "curl_printf.h"
-#include "strcase.h"
+#include "../curl_printf.h"
+#include "../strcase.h"
#include <string.h>
/*
@@ -725,7 +725,7 @@ static const struct cs_entry cs_list [] = {
CS_ENTRY(0xCCAE, RSA,PSK,CHACHA20,POLY1305,,,,),
#endif
};
-#define CS_LIST_LEN (sizeof(cs_list) / sizeof(cs_list[0]))
+#define CS_LIST_LEN CURL_ARRAYSIZE(cs_list)
static int cs_str_to_zip(const char *cs_str, size_t cs_len,
uint8_t zip[6])
@@ -786,12 +786,12 @@ static int cs_zip_to_str(const uint8_t zip[6],
/* unzip the 8 indexes */
indexes[0] = zip[0] >> 2;
- indexes[1] = ((zip[0] << 4) & 0x3F) | zip[1] >> 4;
- indexes[2] = ((zip[1] << 2) & 0x3F) | zip[2] >> 6;
+ indexes[1] = (uint8_t)(((zip[0] << 4) & 0x3F) | zip[1] >> 4);
+ indexes[2] = (uint8_t)(((zip[1] << 2) & 0x3F) | zip[2] >> 6);
indexes[3] = ((zip[2] << 0) & 0x3F);
indexes[4] = zip[3] >> 2;
- indexes[5] = ((zip[3] << 4) & 0x3F) | zip[4] >> 4;
- indexes[6] = ((zip[4] << 2) & 0x3F) | zip[5] >> 6;
+ indexes[5] = (uint8_t)(((zip[3] << 4) & 0x3F) | zip[4] >> 4);
+ indexes[6] = (uint8_t)(((zip[4] << 2) & 0x3F) | zip[5] >> 6);
indexes[7] = ((zip[5] << 0) & 0x3F);
if(indexes[0] == CS_TXT_IDX_TLS)
diff --git a/libs/libcurl/src/vtls/cipher_suite.h b/libs/libcurl/src/vtls/cipher_suite.h
index 8f3381d1b9..f25de29b52 100644
--- a/libs/libcurl/src/vtls/cipher_suite.h
+++ b/libs/libcurl/src/vtls/cipher_suite.h
@@ -24,7 +24,7 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_SECTRANSP) || defined(USE_MBEDTLS) || \
defined(USE_BEARSSL) || defined(USE_RUSTLS)
diff --git a/libs/libcurl/src/vtls/gtls.c b/libs/libcurl/src/vtls/gtls.c
index 398af77bf3..7f2401afd5 100644
--- a/libs/libcurl/src/vtls/gtls.c
+++ b/libs/libcurl/src/vtls/gtls.c
@@ -30,7 +30,7 @@
* since they were not present in 1.0.X.
*/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_GNUTLS
@@ -40,36 +40,28 @@
#include <gnutls/crypto.h>
#include <nettle/sha2.h>
-#include "urldata.h"
-#include "sendf.h"
-#include "inet_pton.h"
+#include "../urldata.h"
+#include "../sendf.h"
+#include "../curlx/inet_pton.h"
#include "keylog.h"
#include "gtls.h"
#include "vtls.h"
#include "vtls_int.h"
-#include "vauth/vauth.h"
-#include "parsedate.h"
-#include "connect.h" /* for the connect timeout */
-#include "progress.h"
-#include "select.h"
-#include "strcase.h"
-#include "warnless.h"
+#include "vtls_scache.h"
+#include "../vauth/vauth.h"
+#include "../parsedate.h"
+#include "../connect.h" /* for the connect timeout */
+#include "../progress.h"
+#include "../select.h"
+#include "../strcase.h"
+#include "../strdup.h"
+#include "../curlx/warnless.h"
#include "x509asn1.h"
-#include "multiif.h"
-#include "curl_printf.h"
-#include "curl_memory.h"
+#include "../multiif.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
/* The last #include file should be: */
-#include "memdebug.h"
-
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
-#endif
-
-#define QUIC_PRIORITY \
- "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \
- "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \
- "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \
- "%DISABLE_TLS13_COMPAT_MODE"
+#include "../memdebug.h"
/* Enable GnuTLS debugging by defining GTLSDEBUG */
/*#define GTLSDEBUG */
@@ -108,6 +100,7 @@ static ssize_t gtls_push(void *s, const void *buf, size_t blen)
blen, nwritten, result);
backend->gtls.io_result = result;
if(nwritten < 0) {
+ /* !checksrc! disable ERRNOVAR 1 */
gnutls_transport_set_errno(backend->gtls.session,
(CURLE_AGAIN == result) ? EAGAIN : EINVAL);
nwritten = -1;
@@ -129,6 +122,7 @@ static ssize_t gtls_pull(void *s, void *buf, size_t blen)
if(!backend->gtls.shared_creds->trust_setup) {
result = Curl_gtls_client_trust_setup(cf, data, &backend->gtls);
if(result) {
+ /* !checksrc! disable ERRNOVAR 1 */
gnutls_transport_set_errno(backend->gtls.session, EINVAL);
backend->gtls.io_result = result;
return -1;
@@ -140,6 +134,7 @@ static ssize_t gtls_pull(void *s, void *buf, size_t blen)
blen, nread, result);
backend->gtls.io_result = result;
if(nread < 0) {
+ /* !checksrc! disable ERRNOVAR 1 */
gnutls_transport_set_errno(backend->gtls.session,
(CURLE_AGAIN == result) ? EAGAIN : EINVAL);
nread = -1;
@@ -238,117 +233,70 @@ static void unload_file(gnutls_datum_t data)
}
-/* this function does a SSL/TLS (re-)handshake */
+/* this function does an SSL/TLS (re-)handshake */
static CURLcode handshake(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool duringconnect,
- bool nonblocking)
+ struct Curl_easy *data)
{
struct ssl_connect_data *connssl = cf->ctx;
struct gtls_ssl_backend_data *backend =
(struct gtls_ssl_backend_data *)connssl->backend;
gnutls_session_t session;
- curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
+ int rc;
DEBUGASSERT(backend);
session = backend->gtls.session;
- connssl->connecting_state = ssl_connect_2;
-
- for(;;) {
- timediff_t timeout_ms;
- int rc;
-
- /* check allowed time left */
- timeout_ms = Curl_timeleft(data, NULL, duringconnect);
- if(timeout_ms < 0) {
- /* no need to continue if time already is up */
- failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
-
- /* if ssl is expecting something, check if it is available. */
- if(connssl->io_need) {
- int what;
- curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND) ?
- sockfd : CURL_SOCKET_BAD;
- curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV) ?
- sockfd : CURL_SOCKET_BAD;
-
- what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
- nonblocking ? 0 :
- timeout_ms ? timeout_ms : 1000);
- if(what < 0) {
- /* fatal error */
- failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
- return CURLE_SSL_CONNECT_ERROR;
- }
- else if(0 == what) {
- if(nonblocking)
- return CURLE_AGAIN;
- else if(timeout_ms) {
- /* timeout */
- failf(data, "SSL connection timeout at %ld", (long)timeout_ms);
- return CURLE_OPERATION_TIMEDOUT;
- }
- }
- /* socket is readable or writable */
- }
+ connssl->io_need = CURL_SSL_IO_NEED_NONE;
+ backend->gtls.io_result = CURLE_OK;
+ rc = gnutls_handshake(session);
- connssl->io_need = CURL_SSL_IO_NEED_NONE;
- backend->gtls.io_result = CURLE_OK;
- rc = gnutls_handshake(session);
+ if(!backend->gtls.shared_creds->trust_setup) {
+ /* After having send off the ClientHello, we prepare the trust
+ * store to verify the coming certificate from the server */
+ CURLcode result = Curl_gtls_client_trust_setup(cf, data, &backend->gtls);
+ if(result)
+ return result;
+ }
- if(!backend->gtls.shared_creds->trust_setup) {
- /* After having send off the ClientHello, we prepare the trust
- * store to verify the coming certificate from the server */
- CURLcode result = Curl_gtls_client_trust_setup(cf, data, &backend->gtls);
- if(result)
- return result;
- }
+ if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) {
+ connssl->io_need =
+ gnutls_record_get_direction(session) ?
+ CURL_SSL_IO_NEED_SEND : CURL_SSL_IO_NEED_RECV;
+ return CURLE_AGAIN;
+ }
+ else if((rc < 0) && !gnutls_error_is_fatal(rc)) {
+ const char *strerr = NULL;
- if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) {
- connssl->io_need =
- gnutls_record_get_direction(session) ?
- CURL_SSL_IO_NEED_SEND : CURL_SSL_IO_NEED_RECV;
- continue;
+ if(rc == GNUTLS_E_WARNING_ALERT_RECEIVED) {
+ gnutls_alert_description_t alert = gnutls_alert_get(session);
+ strerr = gnutls_alert_get_name(alert);
}
- else if((rc < 0) && !gnutls_error_is_fatal(rc)) {
- const char *strerr = NULL;
- if(rc == GNUTLS_E_WARNING_ALERT_RECEIVED) {
- gnutls_alert_description_t alert = gnutls_alert_get(session);
- strerr = gnutls_alert_get_name(alert);
- }
+ if(!strerr)
+ strerr = gnutls_strerror(rc);
- if(!strerr)
- strerr = gnutls_strerror(rc);
+ infof(data, "gnutls_handshake() warning: %s", strerr);
+ return CURLE_AGAIN;
+ }
+ else if((rc < 0) && backend->gtls.io_result) {
+ return backend->gtls.io_result;
+ }
+ else if(rc < 0) {
+ const char *strerr = NULL;
- infof(data, "gnutls_handshake() warning: %s", strerr);
- continue;
+ if(rc == GNUTLS_E_FATAL_ALERT_RECEIVED) {
+ gnutls_alert_description_t alert = gnutls_alert_get(session);
+ strerr = gnutls_alert_get_name(alert);
}
- else if((rc < 0) && backend->gtls.io_result) {
- return backend->gtls.io_result;
- }
- else if(rc < 0) {
- const char *strerr = NULL;
-
- if(rc == GNUTLS_E_FATAL_ALERT_RECEIVED) {
- gnutls_alert_description_t alert = gnutls_alert_get(session);
- strerr = gnutls_alert_get_name(alert);
- }
-
- if(!strerr)
- strerr = gnutls_strerror(rc);
- failf(data, "GnuTLS, handshake failed: %s", strerr);
- return CURLE_SSL_CONNECT_ERROR;
- }
+ if(!strerr)
+ strerr = gnutls_strerror(rc);
- /* Reset our connect state machine */
- connssl->connecting_state = ssl_connect_1;
- return CURLE_OK;
+ failf(data, "GnuTLS, handshake failed: %s", strerr);
+ return CURLE_SSL_CONNECT_ERROR;
}
+
+ return CURLE_OK;
}
static gnutls_x509_crt_fmt_t gnutls_do_file_type(const char *type)
@@ -368,12 +316,18 @@ static gnutls_x509_crt_fmt_t gnutls_do_file_type(const char *type)
*/
#define GNUTLS_SRP "+SRP"
+#define QUIC_PRIORITY \
+ "NORMAL:-VERS-ALL:+VERS-TLS1.3:-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:" \
+ "+CHACHA20-POLY1305:+AES-128-CCM:-GROUP-ALL:+GROUP-SECP256R1:" \
+ "+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1:" \
+ "%DISABLE_TLS13_COMPAT_MODE"
+
static CURLcode
gnutls_set_ssl_version_min_max(struct Curl_easy *data,
struct ssl_peer *peer,
struct ssl_primary_config *conn_config,
const char **prioritylist,
- const char *tls13support)
+ bool tls13support)
{
long ssl_version = conn_config->version;
long ssl_version_max = conn_config->version_max;
@@ -472,7 +426,7 @@ CURLcode Curl_gtls_shared_creds_create(struct Curl_easy *data,
}
shared->refcount = 1;
- shared->time = Curl_now();
+ shared->time = curlx_now();
*pcreds = shared;
return CURLE_OK;
}
@@ -586,8 +540,8 @@ static bool gtls_shared_creds_expired(const struct Curl_easy *data,
const struct gtls_shared_creds *sc)
{
const struct ssl_general_config *cfg = &data->set.general_ssl;
- struct curltime now = Curl_now();
- timediff_t elapsed_ms = Curl_timediff(now, sc->time);
+ struct curltime now = curlx_now();
+ timediff_t elapsed_ms = curlx_timediff(now, sc->time);
timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000;
if(timeout_ms < 0)
@@ -613,7 +567,7 @@ gtls_get_cached_creds(struct Curl_cfilter *cf, struct Curl_easy *data)
if(data->multi) {
shared_creds = Curl_hash_pick(&data->multi->proto_hash,
- (void *)MPROTO_GTLS_X509_KEY,
+ CURL_UNCONST(MPROTO_GTLS_X509_KEY),
sizeof(MPROTO_GTLS_X509_KEY)-1);
if(shared_creds && shared_creds->creds &&
!gtls_shared_creds_expired(data, shared_creds) &&
@@ -657,7 +611,7 @@ static void gtls_set_cached_creds(struct Curl_cfilter *cf,
return;
if(!Curl_hash_add2(&data->multi->proto_hash,
- (void *)MPROTO_GTLS_X509_KEY,
+ CURL_UNCONST(MPROTO_GTLS_X509_KEY),
sizeof(MPROTO_GTLS_X509_KEY)-1,
sc, gtls_shared_creds_hash_free)) {
Curl_gtls_shared_creds_free(&sc); /* down reference again */
@@ -714,21 +668,20 @@ CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf,
return CURLE_OK;
}
-static void gtls_sessionid_free(void *sessionid, size_t idsize)
-{
- (void)idsize;
- free(sessionid);
-}
-
-CURLcode Curl_gtls_update_session_id(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- gnutls_session_t session,
- struct ssl_peer *peer,
- const char *alpn)
+CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ gnutls_session_t session,
+ curl_off_t valid_until,
+ const char *alpn,
+ unsigned char *quic_tp,
+ size_t quic_tp_len)
{
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
- void *connect_sessionid;
- size_t connect_idsize = 0;
+ struct Curl_ssl_session *sc_session;
+ unsigned char *sdata, *qtp_clone = NULL;
+ size_t sdata_len = 0;
+ size_t earlydata_max = 0;
CURLcode result = CURLE_OK;
if(!ssl_config->primary.cache_session)
@@ -740,35 +693,68 @@ CURLcode Curl_gtls_update_session_id(struct Curl_cfilter *cf,
detect that. */
/* get the session ID data size */
- gnutls_session_get_data(session, NULL, &connect_idsize);
- if(!connect_idsize) /* gnutls does this for some version combinations */
+ gnutls_session_get_data(session, NULL, &sdata_len);
+ if(!sdata_len) /* gnutls does this for some version combinations */
return CURLE_OK;
- connect_sessionid = malloc(connect_idsize); /* get a buffer for it */
- if(!connect_sessionid)
+ sdata = malloc(sdata_len); /* get a buffer for it */
+ if(!sdata)
return CURLE_OUT_OF_MEMORY;
/* extract session ID to the allocated buffer */
- gnutls_session_get_data(session, connect_sessionid, &connect_idsize);
-
- CURL_TRC_CF(data, cf, "get session id (len=%zu, alpn=%s) and store in cache",
- connect_idsize, alpn ? alpn : "-");
- Curl_ssl_sessionid_lock(data);
- /* store this session id, takes ownership */
- result = Curl_ssl_set_sessionid(cf, data, peer, alpn,
- connect_sessionid, connect_idsize,
- gtls_sessionid_free);
- Curl_ssl_sessionid_unlock(data);
+ gnutls_session_get_data(session, sdata, &sdata_len);
+ earlydata_max = gnutls_record_get_max_early_data_size(session);
+
+ CURL_TRC_CF(data, cf, "get session id (len=%zu, alpn=%s, earlymax=%zu) "
+ "and store in cache", sdata_len, alpn ? alpn : "-",
+ earlydata_max);
+ if(quic_tp && quic_tp_len) {
+ qtp_clone = Curl_memdup0((char *)quic_tp, quic_tp_len);
+ if(!qtp_clone) {
+ free(sdata);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+
+ result = Curl_ssl_session_create2(sdata, sdata_len,
+ Curl_glts_get_ietf_proto(session),
+ alpn, valid_until, earlydata_max,
+ qtp_clone, quic_tp_len,
+ &sc_session);
+ /* call took ownership of `sdata` and `qtp_clone` */
+ if(!result) {
+ result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session);
+ /* took ownership of `sc_session` */
+ }
return result;
}
+int Curl_glts_get_ietf_proto(gnutls_session_t session)
+{
+ switch(gnutls_protocol_get_version(session)) {
+ case GNUTLS_SSL3:
+ return CURL_IETF_PROTO_SSL3;
+ case GNUTLS_TLS1_0:
+ return CURL_IETF_PROTO_TLS1;
+ case GNUTLS_TLS1_1:
+ return CURL_IETF_PROTO_TLS1_1;
+ case GNUTLS_TLS1_2:
+ return CURL_IETF_PROTO_TLS1_2;
+ case GNUTLS_TLS1_3:
+ return CURL_IETF_PROTO_TLS1_3;
+ default:
+ return CURL_IETF_PROTO_UNKNOWN;
+ }
+}
+
static CURLcode cf_gtls_update_session_id(struct Curl_cfilter *cf,
struct Curl_easy *data,
gnutls_session_t session)
{
struct ssl_connect_data *connssl = cf->ctx;
- return Curl_gtls_update_session_id(cf, data, session, &connssl->peer,
- connssl->alpn_negotiated);
+ return Curl_gtls_cache_session(cf, data, connssl->peer.scache_key,
+ session, 0, connssl->negotiated.alpn,
+ NULL, 0);
}
static int gtls_handshake_cb(gnutls_session_t session, unsigned int htype,
@@ -797,9 +783,67 @@ static int gtls_handshake_cb(gnutls_session_t session, unsigned int htype,
return 0;
}
+static CURLcode gtls_set_priority(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct gtls_ctx *gtls,
+ const char *priority)
+{
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct dynbuf buf;
+ const char *err = NULL;
+ CURLcode result = CURLE_OK;
+ int rc;
+
+ curlx_dyn_init(&buf, 4096);
+
+#ifdef USE_GNUTLS_SRP
+ if(conn_config->username) {
+ /* Only add SRP to the cipher list if SRP is requested. Otherwise
+ * GnuTLS will disable TLS 1.3 support. */
+ result = curlx_dyn_add(&buf, priority);
+ if(!result)
+ result = curlx_dyn_add(&buf, ":" GNUTLS_SRP);
+ if(result)
+ goto out;
+ priority = curlx_dyn_ptr(&buf);
+ }
+#endif
+
+ if(conn_config->cipher_list) {
+ if((conn_config->cipher_list[0] == '+') ||
+ (conn_config->cipher_list[0] == '-') ||
+ (conn_config->cipher_list[0] == '!')) {
+ /* add it to out own */
+ if(!curlx_dyn_len(&buf)) { /* not added yet */
+ result = curlx_dyn_add(&buf, priority);
+ if(result)
+ goto out;
+ }
+ result = curlx_dyn_addf(&buf, ":%s", conn_config->cipher_list);
+ if(result)
+ goto out;
+ priority = curlx_dyn_ptr(&buf);
+ }
+ else /* replace our own completely */
+ priority = conn_config->cipher_list;
+ }
+
+ infof(data, "GnuTLS priority: %s", priority);
+ rc = gnutls_priority_set_direct(gtls->session, priority, &err);
+ if(rc != GNUTLS_E_SUCCESS) {
+ failf(data, "Error %d setting GnuTLS priority: %s", rc, err);
+ result = CURLE_SSL_CONNECT_ERROR;
+ }
+
+out:
+ curlx_dyn_free(&buf);
+ return result;
+}
+
static CURLcode gtls_client_init(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
+ size_t earlydata_max,
struct gtls_ctx *gtls)
{
struct ssl_primary_config *config = Curl_ssl_cf_get_primary_config(cf);
@@ -808,8 +852,7 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf,
int rc;
bool sni = TRUE; /* default is SNI enabled */
const char *prioritylist;
- const char *err = NULL;
- const char *tls13support;
+ bool tls13support;
CURLcode result;
if(!gtls_inited)
@@ -853,6 +896,14 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf,
/* Initialize TLS session as a client */
init_flags = GNUTLS_CLIENT;
+ if(peer->transport == TRNSPRT_QUIC && earlydata_max > 0)
+ init_flags |= GNUTLS_ENABLE_EARLY_DATA | GNUTLS_NO_END_OF_EARLY_DATA;
+ else if(earlydata_max > 0 && earlydata_max != 0xFFFFFFFFUL)
+ /* See https://gitlab.com/gnutls/gnutls/-/issues/1619
+ * We cannot differentiate between a session announcing no earldata
+ * and one announcing 0xFFFFFFFFUL. On TCP+TLS, this is unlikely, but
+ * on QUIC this is common. */
+ init_flags |= GNUTLS_ENABLE_EARLY_DATA;
#if defined(GNUTLS_FORCE_CLIENT_CERT)
init_flags |= GNUTLS_FORCE_CLIENT_CERT;
@@ -874,6 +925,8 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf,
init_flags |= GNUTLS_NO_STATUS_REQUEST;
#endif
+ CURL_TRC_CF(data, cf, "gnutls_init(flags=%x), earlydata=%zu",
+ init_flags, earlydata_max);
rc = gnutls_init(&gtls->session, init_flags);
if(rc != GNUTLS_E_SUCCESS) {
failf(data, "gnutls_init() failed: %d", rc);
@@ -894,7 +947,7 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf,
return CURLE_SSL_CONNECT_ERROR;
/* "In GnuTLS 3.6.5, TLS 1.3 is enabled by default" */
- tls13support = gnutls_check_version("3.6.5");
+ tls13support = !!gnutls_check_version("3.6.5");
/* Ensure +SRP comes at the *end* of all relevant strings so that it can be
* removed if a runtime error indicates that SRP is not supported by this
@@ -919,33 +972,9 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf,
if(result)
return result;
-#ifdef USE_GNUTLS_SRP
- /* Only add SRP to the cipher list if SRP is requested. Otherwise
- * GnuTLS will disable TLS 1.3 support. */
- if(config->username) {
- char *prioritysrp = aprintf("%s:" GNUTLS_SRP, prioritylist);
- if(!prioritysrp)
- return CURLE_OUT_OF_MEMORY;
- rc = gnutls_priority_set_direct(gtls->session, prioritysrp, &err);
- free(prioritysrp);
-
- if((rc == GNUTLS_E_INVALID_REQUEST) && err) {
- infof(data, "This GnuTLS does not support SRP");
- }
- }
- else {
-#endif
- infof(data, "GnuTLS ciphers: %s", prioritylist);
- rc = gnutls_priority_set_direct(gtls->session, prioritylist, &err);
-#ifdef USE_GNUTLS_SRP
- }
-#endif
-
- if(rc != GNUTLS_E_SUCCESS) {
- failf(data, "Error %d setting GnuTLS cipher list starting with %s",
- rc, err);
- return CURLE_SSL_CONNECT_ERROR;
- }
+ result = gtls_set_priority(cf, data, gtls, prioritylist);
+ if(result)
+ return result;
if(config->clientcert) {
if(!gtls->shared_creds->trust_setup) {
@@ -965,7 +994,7 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf,
return CURLE_SSL_CONNECT_ERROR;
}
}
- else if(ssl_config->key_passwd) {
+ else {
const unsigned int supported_key_encryption_algorithms =
GNUTLS_PKCS_USE_PKCS12_3DES | GNUTLS_PKCS_USE_PKCS12_ARCFOUR |
GNUTLS_PKCS_USE_PKCS12_RC2_40 | GNUTLS_PKCS_USE_PBES2_3DES |
@@ -980,22 +1009,12 @@ static CURLcode gtls_client_init(struct Curl_cfilter *cf,
supported_key_encryption_algorithms);
if(rc != GNUTLS_E_SUCCESS) {
failf(data,
- "error reading X.509 potentially-encrypted key file: %s",
+ "error reading X.509 %skey file: %s",
+ ssl_config->key_passwd ? "potentially-encrypted " : "",
gnutls_strerror(rc));
return CURLE_SSL_CONNECT_ERROR;
}
}
- else {
- if(gnutls_certificate_set_x509_key_file(
- gtls->shared_creds->creds,
- config->clientcert,
- ssl_config->key ? ssl_config->key : config->clientcert,
- gnutls_do_file_type(ssl_config->cert_type) ) !=
- GNUTLS_E_SUCCESS) {
- failf(data, "error reading X.509 key or certificate file");
- return CURLE_SSL_CONNECT_ERROR;
- }
- }
}
#ifdef USE_GNUTLS_SRP
@@ -1046,117 +1065,136 @@ static int keylog_callback(gnutls_session_t session, const char *label,
return 0;
}
+static CURLcode gtls_on_session_reuse(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct alpn_spec *alpns,
+ struct Curl_ssl_session *scs,
+ bool *do_early_data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct gtls_ssl_backend_data *backend =
+ (struct gtls_ssl_backend_data *)connssl->backend;
+ CURLcode result = CURLE_OK;
+
+ *do_early_data = FALSE;
+ connssl->earlydata_max =
+ gnutls_record_get_max_early_data_size(backend->gtls.session);
+ if((!connssl->earlydata_max || connssl->earlydata_max == 0xFFFFFFFFUL)) {
+ /* Seems to be GnuTLS way to signal no EarlyData in session */
+ CURL_TRC_CF(data, cf, "SSL session does not allow earlydata");
+ }
+ else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) {
+ CURL_TRC_CF(data, cf, "SSL session has different ALPN, no early data");
+ }
+ else {
+ infof(data, "SSL session allows %zu bytes of early data, "
+ "reusing ALPN '%s'", connssl->earlydata_max, scs->alpn);
+ connssl->earlydata_state = ssl_earlydata_await;
+ connssl->state = ssl_connection_deferred;
+ result = Curl_alpn_set_negotiated(cf, data, connssl,
+ (const unsigned char *)scs->alpn,
+ scs->alpn ? strlen(scs->alpn) : 0);
+ *do_early_data = !result;
+ }
+ return result;
+}
+
CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
- const unsigned char *alpn, size_t alpn_len,
- struct ssl_connect_data *connssl,
+ const struct alpn_spec *alpns_requested,
Curl_gtls_ctx_setup_cb *cb_setup,
void *cb_user_data,
- void *ssl_user_data)
+ void *ssl_user_data,
+ Curl_gtls_init_session_reuse_cb *sess_reuse_cb)
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
- gnutls_datum_t gtls_alpns[5];
+ struct Curl_ssl_session *scs = NULL;
+ gnutls_datum_t gtls_alpns[ALPN_ENTRIES_MAX];
size_t gtls_alpns_count = 0;
- CURLcode result;
+ bool gtls_session_setup = FALSE;
+ struct alpn_spec alpns;
+ CURLcode result = CURLE_OK;
+ int rc;
DEBUGASSERT(gctx);
+ Curl_alpn_copy(&alpns, alpns_requested);
- result = gtls_client_init(cf, data, peer, gctx);
- if(result)
- return result;
-
- gnutls_session_set_ptr(gctx->session, ssl_user_data);
-
- if(cb_setup) {
- result = cb_setup(cf, data, cb_user_data);
+ /* This might be a reconnect, so we check for a session ID in the cache
+ to speed up things. We need to do this before constructing the gnutls
+ session since we need to set flags depending on the kind of reuse. */
+ if(conn_config->cache_session) {
+ result = Curl_ssl_scache_take(cf, data, peer->scache_key, &scs);
if(result)
- return result;
- }
+ goto out;
- /* Open the file if a TLS or QUIC backend has not done this before. */
- Curl_tls_keylog_open();
- if(Curl_tls_keylog_enabled()) {
- gnutls_session_set_keylog_function(gctx->session, keylog_callback);
- }
+ if(scs && scs->sdata && scs->sdata_len &&
+ (!scs->alpn || Curl_alpn_contains_proto(&alpns, scs->alpn))) {
+ /* we got a cached session, use it! */
- /* This might be a reconnect, so we check for a session ID in the cache
- to speed up things */
- if(conn_config->cache_session) {
- void *ssl_sessionid;
- size_t ssl_idsize;
- char *session_alpn;
- Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, peer,
- &ssl_sessionid, &ssl_idsize, &session_alpn)) {
- /* we got a session id, use it! */
- int rc;
-
- rc = gnutls_session_set_data(gctx->session, ssl_sessionid, ssl_idsize);
+ result = gtls_client_init(cf, data, peer, scs->earlydata_max, gctx);
+ if(result)
+ goto out;
+ gtls_session_setup = TRUE;
+
+ rc = gnutls_session_set_data(gctx->session, scs->sdata, scs->sdata_len);
if(rc < 0)
- infof(data, "SSL failed to set session ID");
+ infof(data, "SSL session not accepted by GnuTLS, continuing without");
else {
- infof(data, "SSL reusing session ID (size=%zu, alpn=%s)",
- ssl_idsize, session_alpn ? session_alpn : "-");
-#ifdef DEBUGBUILD
- if((ssl_config->earlydata || !!getenv("CURL_USE_EARLYDATA")) &&
-#else
- if(ssl_config->earlydata &&
-#endif
- !cf->conn->connect_only && connssl &&
- (gnutls_protocol_get_version(gctx->session) == GNUTLS_TLS1_3) &&
- Curl_alpn_contains_proto(connssl->alpn, session_alpn)) {
- connssl->earlydata_max =
- gnutls_record_get_max_early_data_size(gctx->session);
- if((!connssl->earlydata_max ||
- connssl->earlydata_max == 0xFFFFFFFFUL)) {
- /* Seems to be GnuTLS way to signal no EarlyData in session */
- CURL_TRC_CF(data, cf, "TLS session does not allow earlydata");
- }
- else {
- CURL_TRC_CF(data, cf, "TLS session allows %zu earlydata bytes, "
- "reusing ALPN '%s'",
- connssl->earlydata_max, session_alpn);
- connssl->earlydata_state = ssl_earlydata_use;
- connssl->state = ssl_connection_deferred;
- result = Curl_alpn_set_negotiated(cf, data, connssl,
- (const unsigned char *)session_alpn,
- session_alpn ? strlen(session_alpn) : 0);
+ infof(data, "SSL reusing session with ALPN '%s'",
+ scs->alpn ? scs->alpn : "-");
+ if(ssl_config->earlydata && scs->alpn &&
+ !cf->conn->connect_only &&
+ (gnutls_protocol_get_version(gctx->session) == GNUTLS_TLS1_3)) {
+ bool do_early_data = FALSE;
+ if(sess_reuse_cb) {
+ result = sess_reuse_cb(cf, data, &alpns, scs, &do_early_data);
if(result)
- return result;
+ goto out;
+ }
+ if(do_early_data) {
/* We only try the ALPN protocol the session used before,
* otherwise we might send early data for the wrong protocol */
- gtls_alpns[0].data = (unsigned char *)session_alpn;
- gtls_alpns[0].size = (unsigned)strlen(session_alpn);
- gtls_alpns_count = 1;
+ Curl_alpn_restrict_to(&alpns, scs->alpn);
}
}
}
}
- Curl_ssl_sessionid_unlock(data);
+ }
+
+ if(!gtls_session_setup) {
+ result = gtls_client_init(cf, data, peer, 0, gctx);
+ if(result)
+ goto out;
+ }
+
+ gnutls_session_set_ptr(gctx->session, ssl_user_data);
+
+ if(cb_setup) {
+ result = cb_setup(cf, data, cb_user_data);
+ if(result)
+ goto out;
+ }
+
+ /* Open the file if a TLS or QUIC backend has not done this before. */
+ Curl_tls_keylog_open();
+ if(Curl_tls_keylog_enabled()) {
+ gnutls_session_set_keylog_function(gctx->session, keylog_callback);
}
/* convert the ALPN string from our arguments to a list of strings that
* gnutls wants and will convert internally back to this string for sending
* to the server. nice. */
- if(!gtls_alpns_count && alpn && alpn_len) {
- size_t i, alen = alpn_len;
- unsigned char *s = (unsigned char *)alpn;
- unsigned char slen;
- for(i = 0; (i < ARRAYSIZE(gtls_alpns)) && alen; ++i) {
- slen = s[0];
- if(slen >= alen)
- return CURLE_FAILED_INIT;
- gtls_alpns[i].data = s + 1;
- gtls_alpns[i].size = slen;
- s += slen + 1;
- alen -= (size_t)slen + 1;
+ if(!gtls_alpns_count && alpns.count) {
+ size_t i;
+ DEBUGASSERT(CURL_ARRAYSIZE(gtls_alpns) >= alpns.count);
+ for(i = 0; i < alpns.count; ++i) {
+ gtls_alpns[i].data = (unsigned char *)alpns.entries[i];
+ gtls_alpns[i].size = (unsigned int)strlen(alpns.entries[i]);
}
- if(alen) /* not all alpn chars used, wrong format or too many */
- return CURLE_FAILED_INIT;
- gtls_alpns_count = i;
+ gtls_alpns_count = alpns.count;
}
if(gtls_alpns_count &&
@@ -1164,10 +1202,12 @@ CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
gtls_alpns, (unsigned int)gtls_alpns_count,
GNUTLS_ALPN_MANDATORY)) {
failf(data, "failed setting ALPN");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
}
- return CURLE_OK;
+out:
+ Curl_ssl_scache_return(cf, data, peer->scache_key, scs);
+ return result;
}
static CURLcode
@@ -1176,32 +1216,24 @@ gtls_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
struct ssl_connect_data *connssl = cf->ctx;
struct gtls_ssl_backend_data *backend =
(struct gtls_ssl_backend_data *)connssl->backend;
- struct alpn_proto_buf proto;
CURLcode result;
DEBUGASSERT(backend);
- DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
if(connssl->state == ssl_connection_complete)
/* to make us tolerant against being called more than once for the
same connection */
return CURLE_OK;
- memset(&proto, 0, sizeof(proto));
- if(connssl->alpn) {
- result = Curl_alpn_to_proto_buf(&proto, connssl->alpn);
- if(result) {
- failf(data, "Error determining ALPN");
- return CURLE_SSL_CONNECT_ERROR;
- }
- }
-
result = Curl_gtls_ctx_init(&backend->gtls, cf, data, &connssl->peer,
- proto.data, proto.len, connssl, NULL, NULL, cf);
+ connssl->alpn, NULL, NULL, cf,
+ gtls_on_session_reuse);
if(result)
return result;
if(connssl->alpn && (connssl->state != ssl_connection_deferred)) {
+ struct alpn_proto_buf proto;
+ memset(&proto, 0, sizeof(proto));
Curl_alpn_to_proto_str(&proto, connssl->alpn);
infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
}
@@ -1560,10 +1592,10 @@ Curl_gtls_verifyserver(struct Curl_easy *data,
unsigned char addrbuf[sizeof(struct use_addr)];
size_t addrlen = 0;
- if(Curl_inet_pton(AF_INET, peer->hostname, addrbuf) > 0)
+ if(curlx_inet_pton(AF_INET, peer->hostname, addrbuf) > 0)
addrlen = 4;
#ifdef USE_IPV6
- else if(Curl_inet_pton(AF_INET6, peer->hostname, addrbuf) > 0)
+ else if(curlx_inet_pton(AF_INET6, peer->hostname, addrbuf) > 0)
addrlen = 16;
#endif
@@ -1749,30 +1781,6 @@ out:
return result;
}
-static CURLcode gtls_set_earlydata(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const void *buf, size_t blen)
-{
- struct ssl_connect_data *connssl = cf->ctx;
- ssize_t nwritten = 0;
- CURLcode result = CURLE_OK;
-
- DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_use);
- DEBUGASSERT(Curl_bufq_is_empty(&connssl->earlydata));
- if(blen) {
- if(blen > connssl->earlydata_max)
- blen = connssl->earlydata_max;
- nwritten = Curl_bufq_write(&connssl->earlydata, buf, blen, &result);
- CURL_TRC_CF(data, cf, "gtls_set_earlydata(len=%zu) -> %zd",
- blen, nwritten);
- if(nwritten < 0)
- return result;
- }
- connssl->earlydata_state = ssl_earlydata_sending;
- connssl->earlydata_skip = Curl_bufq_len(&connssl->earlydata);
- return CURLE_OK;
-}
-
static CURLcode gtls_send_earlydata(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
@@ -1807,8 +1815,7 @@ static CURLcode gtls_send_earlydata(struct Curl_cfilter *cf,
Curl_bufq_skip(&connssl->earlydata, (size_t)n);
}
/* sent everything there was */
- infof(data, "SSL sending %" FMT_OFF_T " bytes of early data",
- connssl->earlydata_skip);
+ infof(data, "SSL sending %zu bytes of early data", connssl->earlydata_skip);
out:
return result;
}
@@ -1822,17 +1829,22 @@ out:
'ssl_connect_2' (doing handshake with the server), and
'ssl_connect_3' (verifying and getting stats).
*/
-static CURLcode
-gtls_connect_common(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool nonblocking,
- bool *done) {
+static CURLcode gtls_connect_common(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done) {
struct ssl_connect_data *connssl = cf->ctx;
struct gtls_ssl_backend_data *backend =
(struct gtls_ssl_backend_data *)connssl->backend;
CURLcode result = CURLE_OK;
DEBUGASSERT(backend);
+ /* check if the connection has already been established */
+ if(ssl_connection_complete == connssl->state) {
+ *done = TRUE;
+ return CURLE_OK;
+ }
+
+ *done = FALSE;
/* Initiate the connection, if not already done */
if(connssl->connecting_state == ssl_connect_1) {
@@ -1843,7 +1855,7 @@ gtls_connect_common(struct Curl_cfilter *cf,
}
if(connssl->connecting_state == ssl_connect_2) {
- if(connssl->earlydata_state == ssl_earlydata_use) {
+ if(connssl->earlydata_state == ssl_earlydata_await) {
goto out;
}
else if(connssl->earlydata_state == ssl_earlydata_sending) {
@@ -1851,13 +1863,11 @@ gtls_connect_common(struct Curl_cfilter *cf,
if(result)
goto out;
connssl->earlydata_state = ssl_earlydata_sent;
- if(!Curl_ssl_cf_is_proxy(cf))
- Curl_pgrsEarlyData(data, (curl_off_t)connssl->earlydata_skip);
}
DEBUGASSERT((connssl->earlydata_state == ssl_earlydata_none) ||
(connssl->earlydata_state == ssl_earlydata_sent));
- result = handshake(cf, data, TRUE, nonblocking);
+ result = handshake(cf, data);
if(result)
goto out;
connssl->connecting_state = ssl_connect_3;
@@ -1872,7 +1882,6 @@ gtls_connect_common(struct Curl_cfilter *cf,
goto out;
connssl->state = ssl_connection_complete;
- connssl->connecting_state = ssl_connect_1;
rc = gnutls_alpn_get_selected_protocol(backend->gtls.session, &proto);
if(rc) { /* No ALPN from server */
@@ -1885,79 +1894,43 @@ gtls_connect_common(struct Curl_cfilter *cf,
if(result)
goto out;
- if(connssl->earlydata_state == ssl_earlydata_sent) {
- if(gnutls_session_get_flags(backend->gtls.session) &
- GNUTLS_SFLAGS_EARLY_DATA) {
- connssl->earlydata_state = ssl_earlydata_accepted;
- infof(data, "Server accepted %zu bytes of TLS early data.",
- connssl->earlydata_skip);
- }
- else {
- connssl->earlydata_state = ssl_earlydata_rejected;
- if(!Curl_ssl_cf_is_proxy(cf))
- Curl_pgrsEarlyData(data, -(curl_off_t)connssl->earlydata_skip);
- infof(data, "Server rejected TLS early data.");
- connssl->earlydata_skip = 0;
- }
+ if(connssl->earlydata_state > ssl_earlydata_none) {
+ /* We should be in this state by now */
+ DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_sent);
+ connssl->earlydata_state =
+ (gnutls_session_get_flags(backend->gtls.session) &
+ GNUTLS_SFLAGS_EARLY_DATA) ?
+ ssl_earlydata_accepted : ssl_earlydata_rejected;
}
+ connssl->connecting_state = ssl_connect_done;
}
+ if(connssl->connecting_state == ssl_connect_done)
+ DEBUGASSERT(connssl->state == ssl_connection_complete);
+
out:
if(result == CURLE_AGAIN) {
*done = FALSE;
return CURLE_OK;
}
- *done = ((connssl->connecting_state == ssl_connect_1) ||
+ *done = ((connssl->state == ssl_connection_complete) ||
(connssl->state == ssl_connection_deferred));
+ CURL_TRC_CF(data, cf, "gtls_connect_common() -> %d, done=%d", result, *done);
return result;
}
-static CURLcode gtls_connect_nonblocking(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool *done)
+static CURLcode gtls_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done)
{
struct ssl_connect_data *connssl = cf->ctx;
- if(connssl->state == ssl_connection_deferred) {
+ if((connssl->state == ssl_connection_deferred) &&
+ (connssl->earlydata_state == ssl_earlydata_await)) {
/* We refuse to be pushed, we are waiting for someone to send/recv. */
*done = TRUE;
return CURLE_OK;
}
- return gtls_connect_common(cf, data, TRUE, done);
-}
-
-static CURLcode gtls_connect(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- CURLcode result;
- bool done = FALSE;
-
- result = gtls_connect_common(cf, data, FALSE, &done);
- if(result)
- return result;
-
- DEBUGASSERT(done);
-
- return CURLE_OK;
-}
-
-static CURLcode gtls_connect_deferred(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const void *buf,
- size_t blen,
- bool *done)
-{
- struct ssl_connect_data *connssl = cf->ctx;
- CURLcode result = CURLE_OK;
-
- DEBUGASSERT(connssl->state == ssl_connection_deferred);
- *done = FALSE;
- if(connssl->earlydata_state == ssl_earlydata_use) {
- result = gtls_set_earlydata(cf, data, buf, blen);
- if(result)
- return result;
- }
-
- return gtls_connect_common(cf, data, TRUE, done);
+ return gtls_connect_common(cf, data, done);
}
static bool gtls_data_pending(struct Curl_cfilter *cf,
@@ -1987,38 +1960,9 @@ static ssize_t gtls_send(struct Curl_cfilter *cf,
ssize_t rc;
size_t nwritten, total_written = 0;
+ (void)data;
DEBUGASSERT(backend);
- if(connssl->state == ssl_connection_deferred) {
- bool done = FALSE;
- *curlcode = gtls_connect_deferred(cf, data, buf, blen, &done);
- if(*curlcode) {
- rc = -1;
- goto out;
- }
- else if(!done) {
- *curlcode = CURLE_AGAIN;
- rc = -1;
- goto out;
- }
- DEBUGASSERT(connssl->state == ssl_connection_complete);
- }
-
- if(connssl->earlydata_skip) {
- if(connssl->earlydata_skip >= blen) {
- connssl->earlydata_skip -= blen;
- *curlcode = CURLE_OK;
- rc = (ssize_t)blen;
- goto out;
- }
- else {
- total_written += connssl->earlydata_skip;
- buf = ((const char *)buf) + connssl->earlydata_skip;
- blen -= connssl->earlydata_skip;
- connssl->earlydata_skip = 0;
- }
- }
-
while(blen) {
backend->gtls.io_result = CURLE_OK;
rc = gnutls_record_send(backend->gtls.session, buf, blen);
@@ -2039,7 +1983,7 @@ static ssize_t gtls_send(struct Curl_cfilter *cf,
nwritten = (size_t)rc;
total_written += nwritten;
DEBUGASSERT(nwritten <= blen);
- buf = (char *)buf + nwritten;
+ buf = (char *)CURL_UNCONST(buf) + nwritten;
blen -= nwritten;
}
rc = total_written;
@@ -2165,21 +2109,6 @@ static ssize_t gtls_recv(struct Curl_cfilter *cf,
(void)data;
DEBUGASSERT(backend);
- if(connssl->state == ssl_connection_deferred) {
- bool done = FALSE;
- *curlcode = gtls_connect_deferred(cf, data, NULL, 0, &done);
- if(*curlcode) {
- ret = -1;
- goto out;
- }
- else if(!done) {
- *curlcode = CURLE_AGAIN;
- ret = -1;
- goto out;
- }
- DEBUGASSERT(connssl->state == ssl_connection_complete);
- }
-
ret = gnutls_record_recv(backend->gtls.session, buf, buffersize);
if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) {
*curlcode = CURLE_AGAIN;
@@ -2190,9 +2119,8 @@ static ssize_t gtls_recv(struct Curl_cfilter *cf,
if(ret == GNUTLS_E_REHANDSHAKE) {
/* BLOCKING call, this is bad but a work-around for now. Fixing this "the
proper way" takes a whole lot of work. */
- CURLcode result = handshake(cf, data, FALSE, FALSE);
+ CURLcode result = handshake(cf, data);
if(result)
- /* handshake() writes error message on its own */
*curlcode = result;
else
*curlcode = CURLE_AGAIN; /* then return as if this was a wouldblock */
@@ -2213,7 +2141,7 @@ out:
return ret;
}
-static size_t gtls_version(char *buffer, size_t size)
+size_t Curl_gtls_version(char *buffer, size_t size)
{
return msnprintf(buffer, size, "GnuTLS/%s", gnutls_check_version(NULL));
}
@@ -2262,31 +2190,28 @@ const struct Curl_ssl Curl_ssl_gnutls = {
SSLSUPP_CERTINFO |
SSLSUPP_PINNEDPUBKEY |
SSLSUPP_HTTPS_PROXY |
+ SSLSUPP_CIPHER_LIST |
SSLSUPP_CA_CACHE,
sizeof(struct gtls_ssl_backend_data),
gtls_init, /* init */
gtls_cleanup, /* cleanup */
- gtls_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
+ Curl_gtls_version, /* version */
gtls_shutdown, /* shutdown */
gtls_data_pending, /* data_pending */
gtls_random, /* random */
gtls_cert_status_request, /* cert_status_request */
gtls_connect, /* connect */
- gtls_connect_nonblocking, /* connect_nonblocking */
Curl_ssl_adjust_pollset, /* adjust_pollset */
gtls_get_internals, /* get_internals */
gtls_close, /* close_one */
- Curl_none_close_all, /* close_all */
- Curl_none_set_engine, /* set_engine */
- Curl_none_set_engine_default, /* set_engine_default */
- Curl_none_engines_list, /* engines_list */
- Curl_none_false_start, /* false_start */
+ NULL, /* close_all */
+ NULL, /* set_engine */
+ NULL, /* set_engine_default */
+ NULL, /* engines_list */
+ NULL, /* false_start */
gtls_sha256sum, /* sha256sum */
- NULL, /* associate_connection */
- NULL, /* disassociate_connection */
gtls_recv, /* recv decrypted data */
gtls_send, /* send data to encrypt */
NULL, /* get_channel_binding */
diff --git a/libs/libcurl/src/vtls/gtls.h b/libs/libcurl/src/vtls/gtls.h
index 4f3c089bc6..dc8de777af 100644
--- a/libs/libcurl/src/vtls/gtls.h
+++ b/libs/libcurl/src/vtls/gtls.h
@@ -24,13 +24,13 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#include <curl/curl.h>
#ifdef USE_GNUTLS
#include <gnutls/gnutls.h>
-#include "timeval.h"
+#include "../curlx/timeval.h"
#ifdef HAVE_GNUTLS_SRP
/* the function exists */
@@ -42,10 +42,14 @@
struct Curl_easy;
struct Curl_cfilter;
+struct alpn_spec;
struct ssl_primary_config;
struct ssl_config_data;
struct ssl_peer;
struct ssl_connect_data;
+struct Curl_ssl_session;
+
+int Curl_glts_get_ietf_proto(gnutls_session_t session);
struct gtls_shared_creds {
gnutls_certificate_credentials_t creds;
@@ -70,19 +74,27 @@ struct gtls_ctx {
BIT(sent_shutdown);
};
+size_t Curl_gtls_version(char *buffer, size_t size);
+
typedef CURLcode Curl_gtls_ctx_setup_cb(struct Curl_cfilter *cf,
struct Curl_easy *data,
void *user_data);
+typedef CURLcode Curl_gtls_init_session_reuse_cb(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct alpn_spec *alpns,
+ struct Curl_ssl_session *scs,
+ bool *do_early_data);
+
CURLcode Curl_gtls_ctx_init(struct gtls_ctx *gctx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
- const unsigned char *alpn, size_t alpn_len,
- struct ssl_connect_data *connssl,
+ const struct alpn_spec *alpns,
Curl_gtls_ctx_setup_cb *cb_setup,
void *cb_user_data,
- void *ssl_user_data);
+ void *ssl_user_data,
+ Curl_gtls_init_session_reuse_cb *sess_reuse_cb);
CURLcode Curl_gtls_client_trust_setup(struct Curl_cfilter *cf,
struct Curl_easy *data,
@@ -96,11 +108,14 @@ CURLcode Curl_gtls_verifyserver(struct Curl_easy *data,
const char *pinned_key);
/* Extract TLS session and place in cache, if configured. */
-CURLcode Curl_gtls_update_session_id(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- gnutls_session_t session,
- struct ssl_peer *peer,
- const char *alpn);
+CURLcode Curl_gtls_cache_session(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ gnutls_session_t session,
+ curl_off_t valid_until,
+ const char *alpn,
+ unsigned char *quic_tp,
+ size_t quic_tp_len);
extern const struct Curl_ssl Curl_ssl_gnutls;
diff --git a/libs/libcurl/src/vtls/hostcheck.c b/libs/libcurl/src/vtls/hostcheck.c
index 79ed869e87..482725cb87 100644
--- a/libs/libcurl/src/vtls/hostcheck.c
+++ b/libs/libcurl/src/vtls/hostcheck.c
@@ -22,7 +22,7 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_OPENSSL) \
|| defined(USE_SCHANNEL)
@@ -34,15 +34,14 @@
#ifdef HAVE_NETINET_IN6_H
#include <netinet/in6.h>
#endif
-#include "curl_memrchr.h"
-
+#include "../curl_memrchr.h"
#include "hostcheck.h"
-#include "strcase.h"
-#include "hostip.h"
+#include "../strcase.h"
+#include "../hostip.h"
-#include "curl_memory.h"
+#include "../curl_memory.h"
/* The last #include file should be: */
-#include "memdebug.h"
+#include "../memdebug.h"
/* check the two input strings with given length, but do not
assume they end in nul-bytes */
diff --git a/libs/libcurl/src/vtls/keylog.c b/libs/libcurl/src/vtls/keylog.c
index e403114934..3e6bdff54e 100644
--- a/libs/libcurl/src/vtls/keylog.c
+++ b/libs/libcurl/src/vtls/keylog.c
@@ -21,32 +21,22 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_OPENSSL) || \
defined(USE_GNUTLS) || \
defined(USE_WOLFSSL) || \
(defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || \
- defined(USE_QUICHE)
+ defined(USE_QUICHE) || \
+ defined(USE_RUSTLS)
#include "keylog.h"
#include <curl/curl.h>
+#include "../escape.h"
/* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
-
-#define KEYLOG_LABEL_MAXLEN (sizeof("CLIENT_HANDSHAKE_TRAFFIC_SECRET") - 1)
-
-#define CLIENT_RANDOM_SIZE 32
-
-/*
- * The master secret in TLS 1.2 and before is always 48 bytes. In TLS 1.3, the
- * secret size depends on the cipher suite's hash function which is 32 bytes
- * for SHA-256 and 48 bytes for SHA-384.
- */
-#define SECRET_MAXLEN 48
-
+#include "../curl_memory.h"
+#include "../memdebug.h"
/* The fp for the open SSLKEYLOGFILE, or NULL if not open */
static FILE *keylog_file_fp;
@@ -104,7 +94,7 @@ Curl_tls_keylog_write_line(const char *line)
linelen = strlen(line);
if(linelen == 0 || linelen > sizeof(buf) - 2) {
- /* Empty line or too big to fit in a LF and NUL. */
+ /* Empty line or too big to fit in an LF and NUL. */
return FALSE;
}
@@ -125,10 +115,9 @@ Curl_tls_keylog_write(const char *label,
const unsigned char client_random[CLIENT_RANDOM_SIZE],
const unsigned char *secret, size_t secretlen)
{
- const char *hex = "0123456789ABCDEF";
size_t pos, i;
- char line[KEYLOG_LABEL_MAXLEN + 1 + 2 * CLIENT_RANDOM_SIZE + 1 +
- 2 * SECRET_MAXLEN + 1 + 1];
+ unsigned char line[KEYLOG_LABEL_MAXLEN + 1 + 2 * CLIENT_RANDOM_SIZE + 1 +
+ 2 * SECRET_MAXLEN + 1 + 1];
if(!keylog_file_fp) {
return FALSE;
@@ -145,22 +134,22 @@ Curl_tls_keylog_write(const char *label,
/* Client Random */
for(i = 0; i < CLIENT_RANDOM_SIZE; i++) {
- line[pos++] = hex[client_random[i] >> 4];
- line[pos++] = hex[client_random[i] & 0xF];
+ Curl_hexbyte(&line[pos], client_random[i], FALSE);
+ pos += 2;
}
line[pos++] = ' ';
/* Secret */
for(i = 0; i < secretlen; i++) {
- line[pos++] = hex[secret[i] >> 4];
- line[pos++] = hex[secret[i] & 0xF];
+ Curl_hexbyte(&line[pos], secret[i], FALSE);
+ pos += 2;
}
line[pos++] = '\n';
line[pos] = '\0';
/* Using fputs here instead of fprintf since libcurl's fprintf replacement
may not be thread-safe. */
- fputs(line, keylog_file_fp);
+ fputs((char *)line, keylog_file_fp);
return TRUE;
}
diff --git a/libs/libcurl/src/vtls/keylog.h b/libs/libcurl/src/vtls/keylog.h
index 3a48017f81..88151dea2d 100644
--- a/libs/libcurl/src/vtls/keylog.h
+++ b/libs/libcurl/src/vtls/keylog.h
@@ -23,7 +23,18 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
+
+#define KEYLOG_LABEL_MAXLEN (sizeof("CLIENT_HANDSHAKE_TRAFFIC_SECRET") - 1)
+
+#define CLIENT_RANDOM_SIZE 32
+
+/*
+ * The master secret in TLS 1.2 and before is always 48 bytes. In TLS 1.3, the
+ * secret size depends on the cipher suite's hash function which is 32 bytes
+ * for SHA-256 and 48 bytes for SHA-384.
+ */
+#define SECRET_MAXLEN 48
/*
* Opens the TLS key log file if requested by the user. The SSLKEYLOGFILE
@@ -50,7 +61,7 @@ bool Curl_tls_keylog_write(const char *label,
const unsigned char *secret, size_t secretlen);
/*
- * Appends a line to the key log file, ensure it is terminated by a LF.
+ * Appends a line to the key log file, ensure it is terminated by an LF.
* Returns true iff the key log file is open and a valid line was provided.
*/
bool Curl_tls_keylog_write_line(const char *line);
diff --git a/libs/libcurl/src/vtls/mbedtls.c b/libs/libcurl/src/vtls/mbedtls.c
index 20226b74bd..0404c83407 100644
--- a/libs/libcurl/src/vtls/mbedtls.c
+++ b/libs/libcurl/src/vtls/mbedtls.c
@@ -29,7 +29,7 @@
*
*/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_MBEDTLS
@@ -54,35 +54,33 @@
# ifdef MBEDTLS_DEBUG
# include <mbedtls/debug.h>
# endif
-#endif
+#endif /* MBEDTLS_VERSION_MAJOR >= 2 */
#include "cipher_suite.h"
-#include "strcase.h"
-#include "urldata.h"
-#include "sendf.h"
-#include "inet_pton.h"
+#include "../strcase.h"
+#include "../urldata.h"
+#include "../sendf.h"
+#include "../curlx/inet_pton.h"
#include "mbedtls.h"
#include "vtls.h"
#include "vtls_int.h"
+#include "vtls_scache.h"
#include "x509asn1.h"
-#include "parsedate.h"
-#include "connect.h" /* for the connect timeout */
-#include "select.h"
-#include "multiif.h"
+#include "../parsedate.h"
+#include "../connect.h" /* for the connect timeout */
+#include "../select.h"
+#include "../multiif.h"
#include "mbedtls_threadlock.h"
-#include "strdup.h"
+#include "../strdup.h"
/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
/* ALPN for http2 */
-#ifdef USE_HTTP2
-# undef HAS_ALPN
-# ifdef MBEDTLS_SSL_ALPN
-# define HAS_ALPN
-# endif
+#if defined(USE_HTTP2) && defined(MBEDTLS_SSL_ALPN)
+# define HAS_ALPN_MBEDTLS
#endif
struct mbed_ssl_backend_data {
@@ -96,33 +94,40 @@ struct mbed_ssl_backend_data {
#endif
mbedtls_pk_context pk;
mbedtls_ssl_config config;
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_MBEDTLS
const char *protocols[3];
#endif
int *ciphersuites;
+ size_t send_blocked_len;
BIT(initialized); /* mbedtls_ssl_context is initialized */
BIT(sent_shutdown);
+ BIT(send_blocked);
};
/* apply threading? */
-#if (defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \
- defined(_WIN32)
-#define THREADING_SUPPORT
+#if (defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \
+ defined(_WIN32)
+#define HAS_THREADING_SUPPORT
#endif
#ifndef MBEDTLS_ERROR_C
#define mbedtls_strerror(a,b,c) b[0] = 0
#endif
+/* PSA can be used independently of TLS 1.3 */
+#if defined(MBEDTLS_USE_PSA_CRYPTO) && MBEDTLS_VERSION_NUMBER >= 0x03060000
+#define HAS_PSA_SUPPORT
+#endif
+
#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && MBEDTLS_VERSION_NUMBER >= 0x03060000
-#define TLS13_SUPPORT
+#define HAS_TLS13_SUPPORT
#endif
-#if defined(TLS13_SUPPORT) && defined(MBEDTLS_SSL_SESSION_TICKETS)
+#if defined(HAS_TLS13_SUPPORT) && defined(MBEDTLS_SSL_SESSION_TICKETS)
#define HAS_SESSION_TICKETS
#endif
-#if defined(THREADING_SUPPORT)
+#ifdef HAS_THREADING_SUPPORT
static mbedtls_entropy_context ts_entropy;
static int entropy_init_initialized = 0;
@@ -160,7 +165,7 @@ static int entropy_func_mutex(void *data, unsigned char *output, size_t len)
return ret;
}
-#endif /* THREADING_SUPPORT */
+#endif /* HAS_THREADING_SUPPORT */
#ifdef MBEDTLS_DEBUG
static void mbed_debug(void *context, int level, const char *f_name,
@@ -193,7 +198,7 @@ static int mbedtls_bio_cf_write(void *bio,
if(!data)
return 0;
- nwritten = Curl_conn_cf_send(cf->next, data, (char *)buf, blen, FALSE,
+ nwritten = Curl_conn_cf_send(cf->next, data, (const char *)buf, blen, FALSE,
&result);
CURL_TRC_CF(data, cf, "mbedtls_bio_cf_out_write(len=%zu) -> %zd, err=%d",
blen, nwritten, result);
@@ -268,7 +273,13 @@ mbed_set_ssl_version_min_max(struct Curl_easy *data,
#else
/* mbedTLS 3.2.0 (2022) introduced new methods for setting TLS version */
mbedtls_ssl_protocol_version ver_min = MBEDTLS_SSL_VERSION_TLS1_2;
- mbedtls_ssl_protocol_version ver_max = MBEDTLS_SSL_VERSION_TLS1_2;
+ mbedtls_ssl_protocol_version ver_max =
+#ifdef HAS_TLS13_SUPPORT
+ MBEDTLS_SSL_VERSION_TLS1_3
+#else
+ MBEDTLS_SSL_VERSION_TLS1_2
+#endif
+ ;
#endif
switch(conn_config->version) {
@@ -287,10 +298,14 @@ mbed_set_ssl_version_min_max(struct Curl_easy *data,
case CURL_SSLVERSION_TLSv1_1:
#endif
case CURL_SSLVERSION_TLSv1_2:
- /* ver_min = MBEDTLS_SSL_VERSION_TLS1_2; */
+#if MBEDTLS_VERSION_NUMBER < 0x03020000
+ ver_min = MBEDTLS_SSL_MINOR_VERSION_3; /* TLS 1.2 */
+#else
+ ver_min = MBEDTLS_SSL_VERSION_TLS1_2;
+#endif
break;
case CURL_SSLVERSION_TLSv1_3:
-#ifdef TLS13_SUPPORT
+#ifdef HAS_TLS13_SUPPORT
ver_min = MBEDTLS_SSL_VERSION_TLS1_3;
break;
#endif
@@ -304,12 +319,16 @@ mbed_set_ssl_version_min_max(struct Curl_easy *data,
case CURL_SSLVERSION_MAX_DEFAULT:
case CURL_SSLVERSION_MAX_NONE:
case CURL_SSLVERSION_MAX_TLSv1_3:
-#ifdef TLS13_SUPPORT
+#ifdef HAS_TLS13_SUPPORT
ver_max = MBEDTLS_SSL_VERSION_TLS1_3;
break;
#endif
case CURL_SSLVERSION_MAX_TLSv1_2:
- /* ver_max = MBEDTLS_SSL_VERSION_TLS1_2; */
+#if MBEDTLS_VERSION_NUMBER < 0x03020000
+ ver_max = MBEDTLS_SSL_MINOR_VERSION_3; /* TLS 1.2 */
+#else
+ ver_max = MBEDTLS_SSL_VERSION_TLS1_2;
+#endif
break;
#if MBEDTLS_VERSION_NUMBER < 0x03000000
case CURL_SSLVERSION_MAX_TLSv1_1:
@@ -395,7 +414,7 @@ mbed_set_selected_ciphers(struct Curl_easy *data,
if(!selected)
return CURLE_OUT_OF_MEMORY;
-#ifndef TLS13_SUPPORT
+#ifndef HAS_TLS13_SUPPORT
(void) ciphers13, (void) j;
#else
if(!ciphers13) {
@@ -443,7 +462,7 @@ add_ciphers:
selected[count++] = id;
}
-#ifdef TLS13_SUPPORT
+#ifdef HAS_TLS13_SUPPORT
if(ciphers == ciphers13 && ciphers12) {
ciphers = ciphers12;
goto add_ciphers;
@@ -585,17 +604,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
return CURLE_NOT_BUILT_IN;
}
-#ifdef TLS13_SUPPORT
- ret = psa_crypto_init();
- if(ret != PSA_SUCCESS) {
- mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
- failf(data, "mbedTLS psa_crypto_init returned (-0x%04X) %s",
- -ret, errorbuf);
- return CURLE_SSL_CONNECT_ERROR;
- }
-#endif /* TLS13_SUPPORT */
-
-#ifdef THREADING_SUPPORT
+#ifdef HAS_THREADING_SUPPORT
mbedtls_ctr_drbg_init(&backend->ctr_drbg);
ret = mbedtls_ctr_drbg_seed(&backend->ctr_drbg, entropy_func_mutex,
@@ -618,7 +627,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
-ret, errorbuf);
return CURLE_FAILED_INIT;
}
-#endif /* THREADING_SUPPORT */
+#endif /* HAS_THREADING_SUPPORT */
/* Load the trusted CA */
mbedtls_x509_crt_init(&backend->cacert);
@@ -731,6 +740,9 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
ret = mbedtls_pk_parse_keyfile(&backend->pk, ssl_config->key,
ssl_config->key_passwd);
#endif
+ if(ret == 0 && !(mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_RSA) ||
+ mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_ECKEY)))
+ ret = MBEDTLS_ERR_PK_TYPE_MISMATCH;
if(ret) {
mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
@@ -759,6 +771,9 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
(const unsigned char *)passwd,
passwd ? strlen(passwd) : 0);
#endif
+ if(ret == 0 && !(mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_RSA) ||
+ mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_ECKEY)))
+ ret = MBEDTLS_ERR_PK_TYPE_MISMATCH;
if(ret) {
mbedtls_strerror(ret, errorbuf, sizeof(errorbuf));
@@ -767,10 +782,6 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
return CURLE_SSL_CERTPROBLEM;
}
}
-
- if(ret == 0 && !(mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_RSA) ||
- mbedtls_pk_can_do(&backend->pk, MBEDTLS_PK_ECKEY)))
- ret = MBEDTLS_ERR_PK_TYPE_MISMATCH;
}
/* Load the CRL */
@@ -812,7 +823,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
return CURLE_SSL_CONNECT_ERROR;
}
-#ifdef MBEDTLS_SSL_TLS1_3_SIGNAL_NEW_SESSION_TICKETS_ENABLED
+#if defined(HAS_SESSION_TICKETS) && MBEDTLS_VERSION_NUMBER >= 0x03060100
/* New in mbedTLS 3.6.1, need to enable, default is now disabled */
mbedtls_ssl_conf_tls13_enable_signal_new_session_tickets(&backend->config,
MBEDTLS_SSL_TLS1_3_SIGNAL_NEW_SESSION_TICKETS_ENABLED);
@@ -851,7 +862,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
mbedtls_bio_cf_read,
NULL /* rev_timeout() */);
-#ifndef TLS13_SUPPORT
+#ifndef HAS_TLS13_SUPPORT
if(conn_config->cipher_list) {
CURLcode result = mbed_set_selected_ciphers(data, backend,
conn_config->cipher_list,
@@ -885,29 +896,30 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
/* Check if there is a cached ID we can/should use here! */
if(ssl_config->primary.cache_session) {
- void *sdata = NULL;
- size_t slen = 0;
+ struct Curl_ssl_session *sc_session = NULL;
+ CURLcode result;
- Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, &connssl->peer,
- &sdata, &slen, NULL) && slen) {
+ result = Curl_ssl_scache_take(cf, data, connssl->peer.scache_key,
+ &sc_session);
+ if(!result && sc_session && sc_session->sdata && sc_session->sdata_len) {
mbedtls_ssl_session session;
mbedtls_ssl_session_init(&session);
- ret = mbedtls_ssl_session_load(&session, sdata, slen);
+ ret = mbedtls_ssl_session_load(&session, sc_session->sdata,
+ sc_session->sdata_len);
if(ret) {
- failf(data, "error loading cached session: -0x%x", -ret);
+ failf(data, "SSL session error loading: -0x%x", -ret);
}
else {
ret = mbedtls_ssl_set_session(&backend->ssl, &session);
if(ret)
- failf(data, "error setting session: -0x%x", -ret);
+ failf(data, "SSL session error setting: -0x%x", -ret);
else
infof(data, "SSL reusing session ID");
}
mbedtls_ssl_session_free(&session);
}
- Curl_ssl_sessionid_unlock(data);
+ Curl_ssl_scache_return(cf, data, connssl->peer.scache_key, sc_session);
}
mbedtls_ssl_conf_ca_chain(&backend->config,
@@ -932,7 +944,7 @@ mbed_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
return CURLE_SSL_CONNECT_ERROR;
}
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_MBEDTLS
if(connssl->alpn) {
struct alpn_proto_buf proto;
size_t i;
@@ -1110,7 +1122,7 @@ pinnedpubkey_error:
}
}
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_MBEDTLS
if(connssl->alpn) {
const char *proto = mbedtls_ssl_get_alpn_protocol(&backend->ssl);
@@ -1125,12 +1137,6 @@ pinnedpubkey_error:
return CURLE_OK;
}
-static void mbedtls_session_free(void *session, size_t slen)
-{
- (void)slen;
- free(session);
-}
-
static CURLcode
mbed_new_session(struct Curl_cfilter *cf, struct Curl_easy *data)
{
@@ -1138,48 +1144,64 @@ mbed_new_session(struct Curl_cfilter *cf, struct Curl_easy *data)
struct mbed_ssl_backend_data *backend =
(struct mbed_ssl_backend_data *)connssl->backend;
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ mbedtls_ssl_session session;
+ bool msession_alloced = FALSE;
+ struct Curl_ssl_session *sc_session = NULL;
+ unsigned char *sdata = NULL;
+ size_t slen = 0;
+ int ietf_tls_id;
CURLcode result = CURLE_OK;
+ int ret;
DEBUGASSERT(backend);
- if(ssl_config->primary.cache_session) {
- int ret;
- mbedtls_ssl_session session;
- unsigned char *sdata = NULL;
- size_t slen = 0;
+ if(!ssl_config->primary.cache_session)
+ return CURLE_OK;
- mbedtls_ssl_session_init(&session);
- ret = mbedtls_ssl_get_session(&backend->ssl, &session);
- if(ret) {
- if(ret != MBEDTLS_ERR_SSL_ALLOC_FAILED)
- mbedtls_ssl_session_free(&session);
- failf(data, "mbedtls_ssl_get_session returned -0x%x", -ret);
- return CURLE_SSL_CONNECT_ERROR;
- }
+ mbedtls_ssl_session_init(&session);
+ ret = mbedtls_ssl_get_session(&backend->ssl, &session);
+ msession_alloced = (ret != MBEDTLS_ERR_SSL_ALLOC_FAILED);
+ if(ret) {
+ failf(data, "mbedtls_ssl_get_session returned -0x%x", -ret);
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
+ }
- mbedtls_ssl_session_save(&session, NULL, 0, &slen);
- if(!slen) {
- failf(data, "failed to serialize session: length is 0");
- }
- else {
- sdata = malloc(slen);
- if(sdata) {
- ret = mbedtls_ssl_session_save(&session, sdata, slen, &slen);
- if(ret) {
- failf(data, "failed to serialize session: -0x%x", -ret);
- }
- else {
- Curl_ssl_sessionid_lock(data);
- result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL,
- sdata, slen, mbedtls_session_free);
- Curl_ssl_sessionid_unlock(data);
- if(!result)
- sdata = NULL;
- }
- }
- }
- mbedtls_ssl_session_free(&session);
- free(sdata);
+ mbedtls_ssl_session_save(&session, NULL, 0, &slen);
+ if(!slen) {
+ failf(data, "failed to serialize session: length is 0");
+ goto out;
}
+
+ sdata = malloc(slen);
+ if(!sdata) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ ret = mbedtls_ssl_session_save(&session, sdata, slen, &slen);
+ if(ret) {
+ failf(data, "failed to serialize session: -0x%x", -ret);
+ goto out;
+ }
+
+#if MBEDTLS_VERSION_NUMBER >= 0x03020000
+ ietf_tls_id = mbedtls_ssl_get_version_number(&backend->ssl);
+#else
+ ietf_tls_id = CURL_IETF_PROTO_UNKNOWN;
+#endif
+ result = Curl_ssl_session_create(sdata, slen,
+ ietf_tls_id,
+ connssl->negotiated.alpn, 0, 0,
+ &sc_session);
+ sdata = NULL; /* call took ownership */
+ if(!result)
+ result = Curl_ssl_scache_put(cf, data, connssl->peer.scache_key,
+ sc_session);
+
+out:
+ if(msession_alloced)
+ mbedtls_ssl_session_free(&session);
+ free(sdata);
return result;
}
@@ -1194,27 +1216,41 @@ static ssize_t mbed_send(struct Curl_cfilter *cf, struct Curl_easy *data,
(void)data;
DEBUGASSERT(backend);
- ret = mbedtls_ssl_write(&backend->ssl, (unsigned char *)mem, len);
+ /* mbedtls is picky when a mbedtls_ssl_write) was previously blocked.
+ * It requires to be called with the same amount of bytes again, or it
+ * will lose bytes, e.g. reporting all was sent but they were not.
+ * Remember the blocked length and use that when set. */
+ if(backend->send_blocked) {
+ DEBUGASSERT(backend->send_blocked_len <= len);
+ CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> previously blocked "
+ "on %zu bytes", len, backend->send_blocked_len);
+ len = backend->send_blocked_len;
+ }
+
+ ret = mbedtls_ssl_write(&backend->ssl, (const unsigned char *)mem, len);
if(ret < 0) {
CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> -0x%04X",
len, -ret);
*curlcode = ((ret == MBEDTLS_ERR_SSL_WANT_WRITE)
-#ifdef TLS13_SUPPORT
+#ifdef HAS_TLS13_SUPPORT
|| (ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET)
#endif
) ? CURLE_AGAIN : CURLE_SEND_ERROR;
ret = -1;
+ if((*curlcode == CURLE_AGAIN) && !backend->send_blocked) {
+ backend->send_blocked = TRUE;
+ backend->send_blocked_len = len;
+ }
+ }
+ else {
+ CURL_TRC_CF(data, cf, "mbedtls_ssl_write(len=%zu) -> %d", len, ret);
+ backend->send_blocked = FALSE;
}
return ret;
}
-static void mbedtls_close_all(struct Curl_easy *data)
-{
- (void)data;
-}
-
static CURLcode mbedtls_shutdown(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool send_shutdown, bool *done)
@@ -1270,7 +1306,7 @@ static CURLcode mbedtls_shutdown(struct Curl_cfilter *cf,
* WANT_READ, but has not encountered an EAGAIN. */
if(ret == MBEDTLS_ERR_SSL_WANT_READ)
ret = mbedtls_ssl_read(&backend->ssl, buf, sizeof(buf));
-#ifdef TLS13_SUPPORT
+#ifdef HAS_TLS13_SUPPORT
if(ret == MBEDTLS_ERR_SSL_RECEIVED_NEW_SESSION_TICKET)
continue;
#endif
@@ -1324,9 +1360,9 @@ static void mbedtls_close(struct Curl_cfilter *cf, struct Curl_easy *data)
mbedtls_ssl_config_free(&backend->config);
mbedtls_ssl_free(&backend->ssl);
mbedtls_ctr_drbg_free(&backend->ctr_drbg);
-#ifndef THREADING_SUPPORT
+#ifndef HAS_THREADING_SUPPORT
mbedtls_entropy_free(&backend->entropy);
-#endif /* THREADING_SUPPORT */
+#endif /* HAS_THREADING_SUPPORT */
backend->initialized = FALSE;
}
}
@@ -1420,16 +1456,12 @@ static CURLcode mbedtls_random(struct Curl_easy *data,
#endif
}
-static CURLcode
-mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
- bool nonblocking,
- bool *done)
+static CURLcode mbedtls_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done)
{
CURLcode retcode;
struct ssl_connect_data *connssl = cf->ctx;
- curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
- timediff_t timeout_ms;
- int what;
/* check if the connection has already been established */
if(ssl_connection_complete == connssl->state) {
@@ -1437,73 +1469,20 @@ mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
return CURLE_OK;
}
- if(ssl_connect_1 == connssl->connecting_state) {
- /* Find out how much more time we are allowed */
- timeout_ms = Curl_timeleft(data, NULL, TRUE);
+ *done = FALSE;
+ connssl->io_need = CURL_SSL_IO_NEED_NONE;
- if(timeout_ms < 0) {
- /* no need to continue if time already is up */
- failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
+ if(ssl_connect_1 == connssl->connecting_state) {
retcode = mbed_connect_step1(cf, data);
if(retcode)
return retcode;
}
- while(ssl_connect_2 == connssl->connecting_state) {
-
- /* check allowed time left */
- timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
- if(timeout_ms < 0) {
- /* no need to continue if time already is up */
- failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
-
- /* if ssl is expecting something, check if it is available. */
- if(connssl->io_need) {
- curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND) ?
- sockfd : CURL_SOCKET_BAD;
- curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV) ?
- sockfd : CURL_SOCKET_BAD;
-
- what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
- nonblocking ? 0 : timeout_ms);
- if(what < 0) {
- /* fatal error */
- failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
- return CURLE_SSL_CONNECT_ERROR;
- }
- else if(0 == what) {
- if(nonblocking) {
- *done = FALSE;
- return CURLE_OK;
- }
- else {
- /* timeout */
- failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
- }
- /* socket is readable or writable */
- }
-
- /* Run transaction, and return to the caller if it failed or if
- * this connection is part of a multi handle and this loop would
- * execute again. This permits the owner of a multi handle to
- * abort a connection attempt before step2 has completed while
- * ensuring that a client using select() or epoll() will always
- * have a valid fdset to wait on.
- */
- connssl->io_need = CURL_SSL_IO_NEED_NONE;
+ if(ssl_connect_2 == connssl->connecting_state) {
retcode = mbed_connect_step2(cf, data);
- if(retcode ||
- (nonblocking && (ssl_connect_2 == connssl->connecting_state)))
+ if(retcode)
return retcode;
-
- } /* repeat step2 until all transactions are done. */
+ }
if(ssl_connect_3 == connssl->connecting_state) {
/* For tls1.3 we get notified about new sessions */
@@ -1528,34 +1507,6 @@ mbed_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
connssl->state = ssl_connection_complete;
*done = TRUE;
}
- else
- *done = FALSE;
-
- /* Reset our connect state machine */
- connssl->connecting_state = ssl_connect_1;
-
- return CURLE_OK;
-}
-
-static CURLcode mbedtls_connect_nonblocking(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool *done)
-{
- return mbed_connect_common(cf, data, TRUE, done);
-}
-
-
-static CURLcode mbedtls_connect(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- CURLcode retcode;
- bool done = FALSE;
-
- retcode = mbed_connect_common(cf, data, FALSE, &done);
- if(retcode)
- return retcode;
-
- DEBUGASSERT(done);
return CURLE_OK;
}
@@ -1568,15 +1519,29 @@ static int mbedtls_init(void)
{
if(!Curl_mbedtlsthreadlock_thread_setup())
return 0;
-#ifdef THREADING_SUPPORT
+#ifdef HAS_THREADING_SUPPORT
entropy_init_mutex(&ts_entropy);
#endif
+#ifdef HAS_PSA_SUPPORT
+ {
+ int ret;
+#ifdef HAS_THREADING_SUPPORT
+ Curl_mbedtlsthreadlock_lock_function(0);
+#endif
+ ret = psa_crypto_init();
+#ifdef HAS_THREADING_SUPPORT
+ Curl_mbedtlsthreadlock_unlock_function(0);
+#endif
+ if(ret != PSA_SUCCESS)
+ return 0;
+ }
+#endif /* HAS_PSA_SUPPORT */
return 1;
}
static void mbedtls_cleanup(void)
{
-#ifdef THREADING_SUPPORT
+#ifdef HAS_THREADING_SUPPORT
entropy_cleanup_mutex(&ts_entropy);
#endif
(void)Curl_mbedtlsthreadlock_thread_cleanup();
@@ -1599,7 +1564,6 @@ static CURLcode mbedtls_sha256sum(const unsigned char *input,
unsigned char *sha256sum,
size_t sha256len UNUSED_PARAM)
{
- /* TODO: explain this for different mbedtls 2.x vs 3 version */
(void)sha256len;
#if MBEDTLS_VERSION_NUMBER < 0x02070000
mbedtls_sha256(input, inputlen, sha256sum, 0);
@@ -1633,7 +1597,7 @@ const struct Curl_ssl Curl_ssl_mbedtls = {
SSLSUPP_CERTINFO |
SSLSUPP_PINNEDPUBKEY |
SSLSUPP_SSL_CTX |
-#ifdef TLS13_SUPPORT
+#ifdef HAS_TLS13_SUPPORT
SSLSUPP_TLS13_CIPHERSUITES |
#endif
SSLSUPP_HTTPS_PROXY |
@@ -1644,24 +1608,20 @@ const struct Curl_ssl Curl_ssl_mbedtls = {
mbedtls_init, /* init */
mbedtls_cleanup, /* cleanup */
mbedtls_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
mbedtls_shutdown, /* shutdown */
mbedtls_data_pending, /* data_pending */
mbedtls_random, /* random */
- Curl_none_cert_status_request, /* cert_status_request */
+ NULL, /* cert_status_request */
mbedtls_connect, /* connect */
- mbedtls_connect_nonblocking, /* connect_nonblocking */
Curl_ssl_adjust_pollset, /* adjust_pollset */
mbedtls_get_internals, /* get_internals */
mbedtls_close, /* close_one */
- mbedtls_close_all, /* close_all */
- Curl_none_set_engine, /* set_engine */
- Curl_none_set_engine_default, /* set_engine_default */
- Curl_none_engines_list, /* engines_list */
- Curl_none_false_start, /* false_start */
+ NULL, /* close_all */
+ NULL, /* set_engine */
+ NULL, /* set_engine_default */
+ NULL, /* engines_list */
+ NULL, /* false_start */
mbedtls_sha256sum, /* sha256sum */
- NULL, /* associate_connection */
- NULL, /* disassociate_connection */
mbed_recv, /* recv decrypted data */
mbed_send, /* send data to encrypt */
NULL, /* get_channel_binding */
diff --git a/libs/libcurl/src/vtls/mbedtls.h b/libs/libcurl/src/vtls/mbedtls.h
index 91ede31e02..33ba492336 100644
--- a/libs/libcurl/src/vtls/mbedtls.h
+++ b/libs/libcurl/src/vtls/mbedtls.h
@@ -24,7 +24,7 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_MBEDTLS
diff --git a/libs/libcurl/src/vtls/mbedtls_threadlock.c b/libs/libcurl/src/vtls/mbedtls_threadlock.c
index d6d20328a7..ed8531f9f1 100644
--- a/libs/libcurl/src/vtls/mbedtls_threadlock.c
+++ b/libs/libcurl/src/vtls/mbedtls_threadlock.c
@@ -22,7 +22,7 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_MBEDTLS) && \
((defined(USE_THREADS_POSIX) && defined(HAVE_PTHREAD_H)) || \
@@ -36,10 +36,10 @@
#endif
#include "mbedtls_threadlock.h"
-#include "curl_printf.h"
-#include "curl_memory.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
/* The last #include file should be: */
-#include "memdebug.h"
+#include "../memdebug.h"
/* number of thread locks */
#define NUMT 2
diff --git a/libs/libcurl/src/vtls/mbedtls_threadlock.h b/libs/libcurl/src/vtls/mbedtls_threadlock.h
index ae651c3866..e99b051cc0 100644
--- a/libs/libcurl/src/vtls/mbedtls_threadlock.h
+++ b/libs/libcurl/src/vtls/mbedtls_threadlock.h
@@ -24,7 +24,7 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_MBEDTLS
diff --git a/libs/libcurl/src/vtls/openssl.c b/libs/libcurl/src/vtls/openssl.c
index 86931089b1..2556b5c57f 100644
--- a/libs/libcurl/src/vtls/openssl.c
+++ b/libs/libcurl/src/vtls/openssl.c
@@ -27,14 +27,14 @@
* but vtls.c should ever call or use these functions.
*/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_QUICHE) || defined(USE_OPENSSL)
#include <limits.h>
/* Wincrypt must be included before anything that could include OpenSSL. */
-#if defined(USE_WIN32_CRYPTO)
+#ifdef USE_WIN32_CRYPTO
#include <wincrypt.h>
/* Undefine wincrypt conflicting symbols for BoringSSL. */
#undef X509_NAME
@@ -45,24 +45,27 @@
#undef OCSP_RESPONSE
#endif
-#include "urldata.h"
-#include "sendf.h"
-#include "formdata.h" /* for the boundary function */
-#include "url.h" /* for the ssl config check function */
-#include "inet_pton.h"
+#include "../urldata.h"
+#include "../sendf.h"
+#include "../formdata.h" /* for the boundary function */
+#include "../url.h" /* for the ssl config check function */
+#include "../curlx/inet_pton.h"
#include "openssl.h"
-#include "connect.h"
-#include "slist.h"
-#include "select.h"
+#include "../connect.h"
+#include "../slist.h"
+#include "../select.h"
#include "vtls.h"
#include "vtls_int.h"
-#include "vauth/vauth.h"
+#include "vtls_scache.h"
+#include "../vauth/vauth.h"
#include "keylog.h"
-#include "strcase.h"
+#include "../strcase.h"
#include "hostcheck.h"
-#include "multiif.h"
-#include "strerror.h"
-#include "curl_printf.h"
+#include "../multiif.h"
+#include "../curlx/strparse.h"
+#include "../strdup.h"
+#include "../strerror.h"
+#include "../curl_printf.h"
#include <openssl/ssl.h>
#include <openssl/rand.h>
@@ -82,37 +85,49 @@
#include <openssl/tls1.h>
#include <openssl/evp.h>
-#ifdef USE_ECH
-# ifndef OPENSSL_IS_BORINGSSL
+#ifdef HAVE_SSL_SET1_ECH_CONFIG_LIST
+#define USE_ECH_OPENSSL
+#endif
+
+#ifdef USE_ECH_OPENSSL
+# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
# include <openssl/ech.h>
# endif
-# include "curl_base64.h"
-# define ECH_ENABLED(__data__) \
- (__data__->set.tls_ech && \
- !(__data__->set.tls_ech & CURLECH_DISABLE)\
- )
-#endif /* USE_ECH */
-
-#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_OCSP)
+#endif /* USE_ECH_OPENSSL */
+
+#ifndef OPENSSL_NO_OCSP
#include <openssl/ocsp.h>
#endif
-#if (OPENSSL_VERSION_NUMBER >= 0x0090700fL) && /* 0.9.7 or later */ \
- !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_UI_CONSOLE)
+#if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_UI_CONSOLE)
#define USE_OPENSSL_ENGINE
#include <openssl/engine.h>
#endif
-#include "warnless.h"
+#ifdef LIBRESSL_VERSION_NUMBER
+# /* As of LibreSSL 2.0.0-4.0.0: OPENSSL_VERSION_NUMBER == 0x20000000L */
+# if LIBRESSL_VERSION_NUMBER < 0x2090100fL /* 2019-04-13 */
+# error "LibreSSL 2.9.1 or later required"
+# endif
+#elif OPENSSL_VERSION_NUMBER < 0x1000201fL /* 2015-03-19 */
+# error "OpenSSL 1.0.2a or later required"
+#endif
-/* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#if OPENSSL_VERSION_NUMBER >= 0x3000000fL && !defined(OPENSSL_NO_UI_CONSOLE)
+#include <openssl/provider.h>
+#include <openssl/store.h>
+/* this is used in the following conditions to make them easier to read */
+#define OPENSSL_HAS_PROVIDERS
-#ifndef ARRAYSIZE
-#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
+static void ossl_provider_cleanup(struct Curl_easy *data);
#endif
+#include "../curlx/warnless.h"
+
+/* The last #include files should be: */
+#include "../curl_memory.h"
+#include "../memdebug.h"
+
/* Uncomment the ALLOW_RENEG line to a real #define if you want to allow TLS
renegotiations when built with BoringSSL. Renegotiating is non-compliant
with HTTP/2 and "an extremely dangerous protocol feature". Beware.
@@ -124,57 +139,28 @@
#error "OPENSSL_VERSION_NUMBER not defined"
#endif
-#ifdef USE_OPENSSL_ENGINE
+#if defined(USE_OPENSSL_ENGINE) || defined(OPENSSL_HAS_PROVIDERS)
#include <openssl/ui.h>
#endif
-#if OPENSSL_VERSION_NUMBER >= 0x00909000L
-#define SSL_METHOD_QUAL const
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+#define OSSL_UI_METHOD_CAST(x) (x)
#else
-#define SSL_METHOD_QUAL
+#define OSSL_UI_METHOD_CAST(x) CURL_UNCONST(x)
#endif
-#if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
-#define HAVE_ERR_REMOVE_THREAD_STATE 1
-#endif
-
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && /* OpenSSL 1.1.0+ */ \
- !(defined(LIBRESSL_VERSION_NUMBER) && \
- LIBRESSL_VERSION_NUMBER < 0x20700000L)
-#define SSLEAY_VERSION_NUMBER OPENSSL_VERSION_NUMBER
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L /* OpenSSL 1.1.0+ and LibreSSL */
#define HAVE_X509_GET0_EXTENSIONS 1 /* added in 1.1.0 -pre1 */
#define HAVE_OPAQUE_EVP_PKEY 1 /* since 1.1.0 -pre3 */
#define HAVE_OPAQUE_RSA_DSA_DH 1 /* since 1.1.0 -pre5 */
-#define CONST_EXTS const
#define HAVE_ERR_REMOVE_THREAD_STATE_DEPRECATED 1
-
-/* funny typecast define due to difference in API */
-#ifdef LIBRESSL_VERSION_NUMBER
-#define ARG2_X509_signature_print (X509_ALGOR *)
-#else
-#define ARG2_X509_signature_print
-#endif
-
#else
/* For OpenSSL before 1.1.0 */
#define ASN1_STRING_get0_data(x) ASN1_STRING_data(x)
#define X509_get0_notBefore(x) X509_get_notBefore(x)
#define X509_get0_notAfter(x) X509_get_notAfter(x)
-#define CONST_EXTS /* nope */
-#ifndef LIBRESSL_VERSION_NUMBER
#define OpenSSL_version_num() SSLeay()
#endif
-#endif
-
-#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) && /* 1.0.2 or later */ \
- !(defined(LIBRESSL_VERSION_NUMBER) && \
- LIBRESSL_VERSION_NUMBER < 0x20700000L)
-#define HAVE_X509_GET0_SIGNATURE 1
-#endif
-
-#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) /* 1.0.2 or later */
-#define HAVE_SSL_GET_SHUTDOWN 1
-#endif
#if OPENSSL_VERSION_NUMBER >= 0x10002003L && \
OPENSSL_VERSION_NUMBER <= 0x10002FFFL && \
@@ -182,11 +168,6 @@
#define HAVE_SSL_COMP_FREE_COMPRESSION_METHODS 1
#endif
-#if (OPENSSL_VERSION_NUMBER < 0x0090808fL)
-/* not present in older OpenSSL */
-#define OPENSSL_load_builtin_modules(x)
-#endif
-
#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
#define HAVE_EVP_PKEY_GET_PARAMS 1
#endif
@@ -200,18 +181,6 @@
#define FREE_PKEY_PARAM_BIGNUM(name)
#endif
-/*
- * Whether SSL_CTX_set_keylog_callback is available.
- * OpenSSL: supported since 1.1.1 https://github.com/openssl/openssl/pull/2287
- * BoringSSL: supported since d28f59c27bac (committed 2015-11-19)
- * LibreSSL: not supported. 3.5.0+ has a stub function that does nothing.
- */
-#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \
- !defined(LIBRESSL_VERSION_NUMBER)) || \
- defined(OPENSSL_IS_BORINGSSL)
-#define HAVE_KEYLOG_CALLBACK
-#endif
-
/* Whether SSL_CTX_set_ciphersuites is available.
* OpenSSL: supported since 1.1.1 (commit a53b5be6a05)
* BoringSSL: no
@@ -223,35 +192,32 @@
LIBRESSL_VERSION_NUMBER >= 0x3040100fL)) && \
!defined(OPENSSL_IS_BORINGSSL)
#define HAVE_SSL_CTX_SET_CIPHERSUITES
- #if !defined(OPENSSL_IS_AWSLC)
+ #ifndef OPENSSL_IS_AWSLC
#define HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH
#endif
#endif
-/*
- * Whether SSL_CTX_set1_curves_list is available.
- * OpenSSL: supported since 1.0.2, see
- * https://docs.openssl.org/master/man3/SSL_CTX_set1_curves/
- * BoringSSL: supported since 5fd1807d95f7 (committed 2016-09-30)
- * LibreSSL: since 2.5.3 (April 12, 2017)
+/* Whether SSL_CTX_set1_sigalgs_list is available
+ * OpenSSL: supported since 1.0.2 (commit 0b362de5f575)
+ * BoringSSL: supported since 0.20240913.0 (commit 826ce15)
+ * LibreSSL: no
*/
-#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) || \
- defined(OPENSSL_IS_BORINGSSL)
-#define HAVE_SSL_CTX_SET_EC_CURVES
+#if (OPENSSL_VERSION_NUMBER >= 0x10002000L && \
+ !defined(LIBRESSL_VERSION_NUMBER))
+ #define HAVE_SSL_CTX_SET1_SIGALGS
#endif
-#if defined(LIBRESSL_VERSION_NUMBER)
+#ifdef LIBRESSL_VERSION_NUMBER
#define OSSL_PACKAGE "LibreSSL"
#elif defined(OPENSSL_IS_BORINGSSL)
#define OSSL_PACKAGE "BoringSSL"
#elif defined(OPENSSL_IS_AWSLC)
#define OSSL_PACKAGE "AWS-LC"
+#elif (defined(USE_NGTCP2) && defined(USE_NGHTTP3) && \
+ !defined(OPENSSL_QUIC_API2)) || defined(USE_MSH3)
+#define OSSL_PACKAGE "quictls"
#else
-# if (defined(USE_NGTCP2) && defined(USE_NGHTTP3)) || defined(USE_MSH3)
-# define OSSL_PACKAGE "quictls"
-# else
-# define OSSL_PACKAGE "OpenSSL"
-#endif
+#define OSSL_PACKAGE "OpenSSL"
#endif
#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
@@ -285,8 +251,6 @@ typedef int numcert_t;
#endif
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \
- !(defined(LIBRESSL_VERSION_NUMBER) && \
- LIBRESSL_VERSION_NUMBER < 0x2070100fL) && \
!defined(OPENSSL_IS_BORINGSSL) && \
!defined(OPENSSL_IS_AWSLC)
#define HAVE_OPENSSL_VERSION
@@ -307,13 +271,6 @@ typedef unsigned long sslerr_t;
#define HAVE_SSL_X509_STORE_SHARE
#endif
-/* What API version do we use? */
-#if defined(LIBRESSL_VERSION_NUMBER)
-#define USE_PRE_1_1_API (LIBRESSL_VERSION_NUMBER < 0x2070000f)
-#else /* !LIBRESSL_VERSION_NUMBER */
-#define USE_PRE_1_1_API (OPENSSL_VERSION_NUMBER < 0x10100000L)
-#endif /* !LIBRESSL_VERSION_NUMBER */
-
static CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl);
static CURLcode push_certinfo(struct Curl_easy *data,
@@ -377,10 +334,16 @@ static int asn1_object_dump(ASN1_OBJECT *a, char *buf, size_t len)
static CURLcode X509V3_ext(struct Curl_easy *data,
int certnum,
- CONST_EXTS STACK_OF(X509_EXTENSION) *exts)
+ const STACK_OF(X509_EXTENSION) *extsarg)
{
int i;
CURLcode result = CURLE_OK;
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
+ !defined(LIBRESSL_VERSION_NUMBER)
+ const STACK_OF(X509_EXTENSION) *exts = extsarg;
+#else
+ STACK_OF(X509_EXTENSION) *exts = CURL_UNCONST(extsarg);
+#endif
if((int)sk_X509_EXTENSION_num(exts) <= 0)
/* no extensions, bail out */
@@ -469,7 +432,7 @@ static CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl)
if(result)
break;
-#if defined(HAVE_X509_GET0_SIGNATURE) && defined(HAVE_X509_GET0_EXTENSIONS)
+#ifdef HAVE_X509_GET0_EXTENSIONS
{
const X509_ALGOR *sigalg = NULL;
X509_PUBKEY *xpubkey = NULL;
@@ -686,21 +649,19 @@ static CURLcode ossl_certchain(struct Curl_easy *data, SSL *ssl)
#ifdef USE_OPENSSL
-#if USE_PRE_1_1_API
-#if !defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER < 0x2070000fL
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
#define BIO_set_init(x,v) ((x)->init=(v))
#define BIO_get_data(x) ((x)->ptr)
#define BIO_set_data(x,v) ((x)->ptr=(v))
-#endif
#define BIO_get_shutdown(x) ((x)->shutdown)
#define BIO_set_shutdown(x,v) ((x)->shutdown=(v))
-#endif /* USE_PRE_1_1_API */
+#endif /* HAVE_PRE_1_1_API */
static int ossl_bio_cf_create(BIO *bio)
{
BIO_set_shutdown(bio, 1);
BIO_set_init(bio, 1);
-#if USE_PRE_1_1_API
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
bio->num = -1;
#endif
BIO_set_data(bio, NULL);
@@ -737,9 +698,11 @@ static long ossl_bio_cf_ctrl(BIO *bio, int cmd, long num, void *ptr)
ret = 1;
break;
#ifdef BIO_CTRL_EOF
- case BIO_CTRL_EOF:
+ case BIO_CTRL_EOF: {
/* EOF has been reached on input? */
- return (!cf->next || !cf->next->connected);
+ struct ssl_connect_data *connssl = cf->ctx;
+ return connssl->peer_closed;
+ }
#endif
default:
ret = 0;
@@ -817,7 +780,7 @@ static int ossl_bio_cf_in_read(BIO *bio, char *buf, int blen)
return (int)nread;
}
-#if USE_PRE_1_1_API
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
static BIO_METHOD ossl_bio_cf_meth_1_0 = {
BIO_TYPE_MEM,
@@ -863,14 +826,6 @@ static void ossl_bio_cf_method_free(BIO_METHOD *m)
#endif
-/*
- * Number of bytes to read from the random number seed file. This must be
- * a finite value (because some entropy "files" like /dev/urandom have
- * an infinite length), but must be large enough to provide enough
- * entropy to properly seed OpenSSL's PRNG.
- */
-#define RAND_LOAD_LENGTH 1024
-
#ifdef HAVE_KEYLOG_CALLBACK
static void ossl_keylog_callback(const SSL *ssl, const char *line)
{
@@ -894,9 +849,7 @@ ossl_log_tls12_secret(const SSL *ssl, bool *keylog_done)
if(!session || *keylog_done)
return;
-#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
- !(defined(LIBRESSL_VERSION_NUMBER) && \
- LIBRESSL_VERSION_NUMBER < 0x20700000L)
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
/* ssl->s3 is not checked in OpenSSL 1.1.0-pre6, but let's assume that
* we have a valid SSL context if we have a non-NULL session. */
SSL_get_client_random(ssl, client_random, SSL3_RANDOM_SIZE);
@@ -943,15 +896,15 @@ static const char *SSL_ERROR_to_str(int err)
return "SSL_ERROR_WANT_CONNECT";
case SSL_ERROR_WANT_ACCEPT:
return "SSL_ERROR_WANT_ACCEPT";
-#if defined(SSL_ERROR_WANT_ASYNC)
+#ifdef SSL_ERROR_WANT_ASYNC
case SSL_ERROR_WANT_ASYNC:
return "SSL_ERROR_WANT_ASYNC";
#endif
-#if defined(SSL_ERROR_WANT_ASYNC_JOB)
+#ifdef SSL_ERROR_WANT_ASYNC_JOB
case SSL_ERROR_WANT_ASYNC_JOB:
return "SSL_ERROR_WANT_ASYNC_JOB";
#endif
-#if defined(SSL_ERROR_WANT_EARLY)
+#ifdef SSL_ERROR_WANT_EARLY
case SSL_ERROR_WANT_EARLY:
return "SSL_ERROR_WANT_EARLY";
#endif
@@ -960,8 +913,6 @@ static const char *SSL_ERROR_to_str(int err)
}
}
-static size_t ossl_version(char *buffer, size_t size);
-
/* Return error string for last OpenSSL error
*/
static char *ossl_strerror(unsigned long error, char *buf, size_t size)
@@ -970,7 +921,7 @@ static char *ossl_strerror(unsigned long error, char *buf, size_t size)
DEBUGASSERT(size);
*buf = '\0';
- len = ossl_version(buf, size);
+ len = Curl_ossl_version(buf, size);
DEBUGASSERT(len < (size - 2));
if(len < (size - 2)) {
buf += len;
@@ -996,14 +947,14 @@ static char *ossl_strerror(unsigned long error, char *buf, size_t size)
}
static int passwd_callback(char *buf, int num, int encrypting,
- void *global_passwd)
+ void *password)
{
DEBUGASSERT(0 == encrypting);
- if(!encrypting && num >= 0) {
- int klen = curlx_uztosi(strlen((char *)global_passwd));
+ if(!encrypting && num >= 0 && password) {
+ int klen = curlx_uztosi(strlen((char *)password));
if(num > klen) {
- memcpy(buf, global_passwd, klen + 1);
+ memcpy(buf, password, klen + 1);
return klen;
}
}
@@ -1015,7 +966,7 @@ static int passwd_callback(char *buf, int num, int encrypting,
*/
static bool rand_enough(void)
{
- return (0 != RAND_status());
+ return 0 != RAND_status();
}
static CURLcode ossl_seed(struct Curl_easy *data)
@@ -1043,13 +994,13 @@ static CURLcode ossl_seed(struct Curl_easy *data)
size_t len = sizeof(randb);
size_t i, i_max;
for(i = 0, i_max = len / sizeof(struct curltime); i < i_max; ++i) {
- struct curltime tv = Curl_now();
+ struct curltime tv = curlx_now();
Curl_wait_ms(1);
tv.tv_sec *= (time_t)i + 1;
tv.tv_usec *= (int)i + 2;
- tv.tv_sec ^= ((Curl_now().tv_sec + (time_t)Curl_now().tv_usec) *
+ tv.tv_sec ^= ((curlx_now().tv_sec + (time_t)curlx_now().tv_usec) *
(time_t)(i + 3)) << 8;
- tv.tv_usec ^= (int) ((Curl_now().tv_sec + (time_t)Curl_now().tv_usec) *
+ tv.tv_usec ^= (int) ((curlx_now().tv_sec + (time_t)curlx_now().tv_usec) *
(time_t)(i + 4)) << 16;
memcpy(&randb[i * sizeof(struct curltime)], &tv,
sizeof(struct curltime));
@@ -1057,6 +1008,14 @@ static CURLcode ossl_seed(struct Curl_easy *data)
RAND_add(randb, (int)len, (double)len/2);
} while(!rand_enough());
+ /*
+ * Number of bytes to read from the random number seed file. This must be
+ * a finite value (because some entropy "files" like /dev/urandom have
+ * an infinite length), but must be large enough to provide enough
+ * entropy to properly seed OpenSSL's PRNG.
+ */
+# define RAND_LOAD_LENGTH 1024
+
{
/* generates a default path for the random seed file */
char fname[256];
@@ -1071,8 +1030,8 @@ static CURLcode ossl_seed(struct Curl_easy *data)
}
infof(data, "libcurl is now using a weak random seed");
- return (rand_enough() ? CURLE_OK :
- CURLE_SSL_CONNECT_ERROR /* confusing error code */);
+ return rand_enough() ? CURLE_OK :
+ CURLE_SSL_CONNECT_ERROR; /* confusing error code */
#endif
}
@@ -1082,6 +1041,9 @@ static CURLcode ossl_seed(struct Curl_easy *data)
#ifndef SSL_FILETYPE_PKCS12
#define SSL_FILETYPE_PKCS12 43
#endif
+#ifndef SSL_FILETYPE_PROVIDER
+#define SSL_FILETYPE_PROVIDER 44
+#endif
static int ossl_do_file_type(const char *type)
{
if(!type || !type[0])
@@ -1090,6 +1052,8 @@ static int ossl_do_file_type(const char *type)
return SSL_FILETYPE_PEM;
if(strcasecompare(type, "DER"))
return SSL_FILETYPE_ASN1;
+ if(strcasecompare(type, "PROV"))
+ return SSL_FILETYPE_PROVIDER;
if(strcasecompare(type, "ENG"))
return SSL_FILETYPE_ENGINE;
if(strcasecompare(type, "P12"))
@@ -1097,7 +1061,7 @@ static int ossl_do_file_type(const char *type)
return -1;
}
-#ifdef USE_OPENSSL_ENGINE
+#if defined(USE_OPENSSL_ENGINE) || defined(OPENSSL_HAS_PROVIDERS)
/*
* Supply default password to the engine user interface conversation.
* The password is passed by OpenSSL engine from ENGINE_load_private_key()
@@ -1145,16 +1109,19 @@ static int ssl_ui_writer(UI *ui, UI_STRING *uis)
*/
static bool is_pkcs11_uri(const char *string)
{
- return (string && strncasecompare(string, "pkcs11:", 7));
+ return string && strncasecompare(string, "pkcs11:", 7);
}
#endif
static CURLcode ossl_set_engine(struct Curl_easy *data, const char *engine);
+#if defined(OPENSSL_HAS_PROVIDERS)
+static CURLcode ossl_set_provider(struct Curl_easy *data,
+ const char *provider);
+#endif
-static int
-SSL_CTX_use_certificate_blob(SSL_CTX *ctx, const struct curl_blob *blob,
- int type, const char *key_passwd)
+static int use_certificate_blob(SSL_CTX *ctx, const struct curl_blob *blob,
+ int type, const char *key_passwd)
{
int ret = 0;
X509 *x = NULL;
@@ -1171,7 +1138,7 @@ SSL_CTX_use_certificate_blob(SSL_CTX *ctx, const struct curl_blob *blob,
else if(type == SSL_FILETYPE_PEM) {
/* ERR_R_PEM_LIB; */
x = PEM_read_bio_X509(in, NULL,
- passwd_callback, (void *)key_passwd);
+ passwd_callback, CURL_UNCONST(key_passwd));
}
else {
ret = 0;
@@ -1190,9 +1157,8 @@ end:
return ret;
}
-static int
-SSL_CTX_use_PrivateKey_blob(SSL_CTX *ctx, const struct curl_blob *blob,
- int type, const char *key_passwd)
+static int use_privatekey_blob(SSL_CTX *ctx, const struct curl_blob *blob,
+ int type, const char *key_passwd)
{
int ret = 0;
EVP_PKEY *pkey = NULL;
@@ -1202,17 +1168,15 @@ SSL_CTX_use_PrivateKey_blob(SSL_CTX *ctx, const struct curl_blob *blob,
if(type == SSL_FILETYPE_PEM)
pkey = PEM_read_bio_PrivateKey(in, NULL, passwd_callback,
- (void *)key_passwd);
+ CURL_UNCONST(key_passwd));
else if(type == SSL_FILETYPE_ASN1)
pkey = d2i_PrivateKey_bio(in, NULL);
- else {
- ret = 0;
+ else
goto end;
- }
- if(!pkey) {
- ret = 0;
+
+ if(!pkey)
goto end;
- }
+
ret = SSL_CTX_use_PrivateKey(ctx, pkey);
EVP_PKEY_free(pkey);
end:
@@ -1221,16 +1185,11 @@ end:
}
static int
-SSL_CTX_use_certificate_chain_blob(SSL_CTX *ctx, const struct curl_blob *blob,
- const char *key_passwd)
+use_certificate_chain_blob(SSL_CTX *ctx, const struct curl_blob *blob,
+ const char *key_passwd)
{
-/* SSL_CTX_add1_chain_cert introduced in OpenSSL 1.0.2 */
-#if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) && /* OpenSSL 1.0.2 or later */ \
- !(defined(LIBRESSL_VERSION_NUMBER) && \
- (LIBRESSL_VERSION_NUMBER < 0x2090100fL)) /* LibreSSL 2.9.1 or later */
int ret = 0;
X509 *x = NULL;
- void *passwd_callback_userdata = (void *)key_passwd;
BIO *in = BIO_new_mem_buf(blob->data, (int)(blob->len));
if(!in)
return CURLE_OUT_OF_MEMORY;
@@ -1238,12 +1197,9 @@ SSL_CTX_use_certificate_chain_blob(SSL_CTX *ctx, const struct curl_blob *blob,
ERR_clear_error();
x = PEM_read_bio_X509_AUX(in, NULL,
- passwd_callback, (void *)key_passwd);
-
- if(!x) {
- ret = 0;
+ passwd_callback, CURL_UNCONST(key_passwd));
+ if(!x)
goto end;
- }
ret = SSL_CTX_use_certificate(ctx, x);
@@ -1260,7 +1216,7 @@ SSL_CTX_use_certificate_chain_blob(SSL_CTX *ctx, const struct curl_blob *blob,
}
while((ca = PEM_read_bio_X509(in, NULL, passwd_callback,
- passwd_callback_userdata))
+ CURL_UNCONST(key_passwd)))
!= NULL) {
if(!SSL_CTX_add0_chain_cert(ctx, ca)) {
@@ -1282,12 +1238,6 @@ end:
X509_free(x);
BIO_free(in);
return ret;
-#else
- (void)ctx; /* unused */
- (void)blob; /* unused */
- (void)key_passwd; /* unused */
- return 0;
-#endif
}
static
@@ -1306,7 +1256,8 @@ int cert_stuff(struct Curl_easy *data,
int file_type = ossl_do_file_type(cert_type);
- if(cert_file || cert_blob || (file_type == SSL_FILETYPE_ENGINE)) {
+ if(cert_file || cert_blob || (file_type == SSL_FILETYPE_ENGINE) ||
+ (file_type == SSL_FILETYPE_PROVIDER)) {
SSL *ssl;
X509 *x509;
int cert_done = 0;
@@ -1324,7 +1275,7 @@ int cert_stuff(struct Curl_easy *data,
case SSL_FILETYPE_PEM:
/* SSL_CTX_use_certificate_chain_file() only works on PEM files */
cert_use_result = cert_blob ?
- SSL_CTX_use_certificate_chain_blob(ctx, cert_blob, key_passwd) :
+ use_certificate_chain_blob(ctx, cert_blob, key_passwd) :
SSL_CTX_use_certificate_chain_file(ctx, cert_file);
if(cert_use_result != 1) {
failf(data,
@@ -1344,8 +1295,7 @@ int cert_stuff(struct Curl_easy *data,
ASN1 files. */
cert_use_result = cert_blob ?
- SSL_CTX_use_certificate_blob(ctx, cert_blob,
- file_type, key_passwd) :
+ use_certificate_blob(ctx, cert_blob, file_type, key_passwd) :
SSL_CTX_use_certificate_file(ctx, cert_file, file_type);
if(cert_use_result != 1) {
failf(data,
@@ -1383,7 +1333,7 @@ int cert_stuff(struct Curl_easy *data,
/* Does the engine supports LOAD_CERT_CTRL ? */
if(!ENGINE_ctrl(data->state.engine, ENGINE_CTRL_GET_CMD_FROM_NAME,
- 0, (void *)cmd_name, NULL)) {
+ 0, CURL_UNCONST(cmd_name), NULL)) {
failf(data, "ssl engine does not support loading certificates");
return 0;
}
@@ -1418,9 +1368,70 @@ int cert_stuff(struct Curl_easy *data,
}
}
break;
-#else
- failf(data, "file type ENG for certificate not implemented");
- return 0;
+#endif
+#if defined(OPENSSL_HAS_PROVIDERS)
+ /* fall through to compatible provider */
+ case SSL_FILETYPE_PROVIDER:
+ {
+ /* Implicitly use pkcs11 provider if none was provided and the
+ * cert_file is a PKCS#11 URI */
+ if(!data->state.provider) {
+ if(is_pkcs11_uri(cert_file)) {
+ if(ossl_set_provider(data, "pkcs11") != CURLE_OK) {
+ return 0;
+ }
+ }
+ }
+
+ if(data->state.provider) {
+ /* Load the certificate from the provider */
+ OSSL_STORE_INFO *info = NULL;
+ X509 *cert = NULL;
+ OSSL_STORE_CTX *store =
+ OSSL_STORE_open_ex(cert_file, data->state.libctx,
+ NULL, NULL, NULL, NULL, NULL, NULL);
+ if(!store) {
+ failf(data, "Failed to open OpenSSL store: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ return 0;
+ }
+ if(OSSL_STORE_expect(store, OSSL_STORE_INFO_CERT) != 1) {
+ failf(data, "Failed to set store preference. Ignoring the error: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ }
+
+ info = OSSL_STORE_load(store);
+ if(info) {
+ int ossl_type = OSSL_STORE_INFO_get_type(info);
+
+ if(ossl_type == OSSL_STORE_INFO_CERT)
+ cert = OSSL_STORE_INFO_get1_CERT(info);
+ OSSL_STORE_INFO_free(info);
+ }
+ OSSL_STORE_close(store);
+ if(!cert) {
+ failf(data, "No cert found in the openssl store: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ return 0;
+ }
+
+ if(SSL_CTX_use_certificate(ctx, cert) != 1) {
+ failf(data, "unable to set client certificate [%s]",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ return 0;
+ }
+ X509_free(cert); /* we do not need the handle any more... */
+ }
+ else {
+ failf(data, "crypto provider not set, cannot load certificate");
+ return 0;
+ }
+ }
+ break;
#endif
case SSL_FILETYPE_PKCS12:
@@ -1469,8 +1480,7 @@ int cert_stuff(struct Curl_easy *data,
PKCS12_PBE_add();
- if(!PKCS12_parse(p12, key_passwd, &pri, &x509,
- &ca)) {
+ if(!PKCS12_parse(p12, key_passwd, &pri, &x509, &ca)) {
failf(data,
"could not parse PKCS12 file, check password, " OSSL_PACKAGE
" error %s",
@@ -1554,7 +1564,7 @@ fail:
FALLTHROUGH();
case SSL_FILETYPE_ASN1:
cert_use_result = key_blob ?
- SSL_CTX_use_PrivateKey_blob(ctx, key_blob, file_type, key_passwd) :
+ use_privatekey_blob(ctx, key_blob, file_type, key_passwd) :
SSL_CTX_use_PrivateKey_file(ctx, key_file, file_type);
if(cert_use_result != 1) {
failf(data, "unable to set private key file: '%s' type %s",
@@ -1580,7 +1590,7 @@ fail:
if(data->state.engine) {
UI_METHOD *ui_method =
- UI_create_method((char *)"curl user interface");
+ UI_create_method(OSSL_UI_METHOD_CAST("curl user interface"));
if(!ui_method) {
failf(data, "unable do create " OSSL_PACKAGE
" user-interface method");
@@ -1611,10 +1621,87 @@ fail:
}
}
break;
-#else
- failf(data, "file type ENG for private key not supported");
- return 0;
#endif
+#if defined(OPENSSL_HAS_PROVIDERS)
+ /* fall through to compatible provider */
+ case SSL_FILETYPE_PROVIDER:
+ {
+ /* Implicitly use pkcs11 provider if none was provided and the
+ * key_file is a PKCS#11 URI */
+ if(!data->state.provider) {
+ if(is_pkcs11_uri(key_file)) {
+ if(ossl_set_provider(data, "pkcs11") != CURLE_OK) {
+ return 0;
+ }
+ }
+ }
+
+ if(data->state.provider) {
+ /* Load the private key from the provider */
+ EVP_PKEY *priv_key = NULL;
+ OSSL_STORE_CTX *store = NULL;
+ OSSL_STORE_INFO *info = NULL;
+ UI_METHOD *ui_method =
+ UI_create_method(OSSL_UI_METHOD_CAST("curl user interface"));
+ if(!ui_method) {
+ failf(data, "unable do create " OSSL_PACKAGE
+ " user-interface method");
+ return 0;
+ }
+ UI_method_set_opener(ui_method, UI_method_get_opener(UI_OpenSSL()));
+ UI_method_set_closer(ui_method, UI_method_get_closer(UI_OpenSSL()));
+ UI_method_set_reader(ui_method, ssl_ui_reader);
+ UI_method_set_writer(ui_method, ssl_ui_writer);
+
+ store = OSSL_STORE_open_ex(key_file, data->state.libctx,
+ data->state.propq, ui_method, NULL, NULL,
+ NULL, NULL);
+ if(!store) {
+ failf(data, "Failed to open OpenSSL store: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ return 0;
+ }
+ if(OSSL_STORE_expect(store, OSSL_STORE_INFO_PKEY) != 1) {
+ failf(data, "Failed to set store preference. Ignoring the error: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ }
+
+ info = OSSL_STORE_load(store);
+ if(info) {
+ int ossl_type = OSSL_STORE_INFO_get_type(info);
+
+ if(ossl_type == OSSL_STORE_INFO_PKEY)
+ priv_key = OSSL_STORE_INFO_get1_PKEY(info);
+ OSSL_STORE_INFO_free(info);
+ }
+ OSSL_STORE_close(store);
+ UI_destroy_method(ui_method);
+ if(!priv_key) {
+ failf(data, "No private key found in the openssl store: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ return 0;
+ }
+
+ if(SSL_CTX_use_PrivateKey(ctx, priv_key) != 1) {
+ failf(data, "unable to set private key [%s]",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ EVP_PKEY_free(priv_key);
+ return 0;
+ }
+ EVP_PKEY_free(priv_key); /* we do not need the handle any more... */
+ }
+ else {
+ failf(data, "crypto provider not set, cannot load private key");
+ return 0;
+ }
+ }
+ break;
+#endif
+
case SSL_FILETYPE_PKCS12:
if(!cert_done) {
failf(data, "file type P12 for private key not supported");
@@ -1689,11 +1776,11 @@ static CURLcode x509_name_oneline(X509_NAME *a, struct dynbuf *d)
CURLcode result = CURLE_OUT_OF_MEMORY;
if(bio_out) {
- Curl_dyn_reset(d);
+ curlx_dyn_reset(d);
rc = X509_NAME_print_ex(bio_out, a, 0, XN_FLAG_SEP_SPLUS_SPC);
if(rc != -1) {
BIO_get_mem_ptr(bio_out, &biomem);
- result = Curl_dyn_addn(d, biomem->data, biomem->length);
+ result = curlx_dyn_addn(d, biomem->data, biomem->length);
BIO_free(bio_out);
}
}
@@ -1708,8 +1795,7 @@ static CURLcode x509_name_oneline(X509_NAME *a, struct dynbuf *d)
*/
static int ossl_init(void)
{
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \
- (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x2070000fL)
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
const uint64_t flags =
#ifdef OPENSSL_INIT_ENGINE_ALL_BUILTIN
/* not present in BoringSSL */
@@ -1759,8 +1845,7 @@ static int ossl_init(void)
/* Global cleanup */
static void ossl_cleanup(void)
{
-#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && \
- (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER >= 0x2070000fL)
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
/* OpenSSL 1.1 deprecates all these cleanup functions and
turns them into no-ops in OpenSSL 1.0 compatibility mode */
#else
@@ -1776,11 +1861,7 @@ static void ossl_cleanup(void)
ERR_free_strings();
/* Free thread local error state, destroying hash upon zero refcount */
-#ifdef HAVE_ERR_REMOVE_THREAD_STATE
ERR_remove_thread_state(NULL);
-#else
- ERR_remove_state(0);
-#endif
/* Free all memory allocated by all configuration modules */
CONF_modules_free();
@@ -1793,47 +1874,39 @@ static void ossl_cleanup(void)
Curl_tls_keylog_close();
}
-/* Selects an OpenSSL crypto engine
+/* Selects an OpenSSL crypto engine or provider.
*/
-static CURLcode ossl_set_engine(struct Curl_easy *data, const char *engine)
+static CURLcode ossl_set_engine(struct Curl_easy *data, const char *name)
{
#ifdef USE_OPENSSL_ENGINE
- ENGINE *e;
+ CURLcode result = CURLE_SSL_ENGINE_NOTFOUND;
+ ENGINE *e = ENGINE_by_id(name);
-#if OPENSSL_VERSION_NUMBER >= 0x00909000L
- e = ENGINE_by_id(engine);
-#else
- /* avoid memory leak */
- for(e = ENGINE_get_first(); e; e = ENGINE_get_next(e)) {
- const char *e_id = ENGINE_get_id(e);
- if(!strcmp(engine, e_id))
- break;
- }
-#endif
-
- if(!e) {
- failf(data, "SSL Engine '%s' not found", engine);
- return CURLE_SSL_ENGINE_NOTFOUND;
- }
+ if(e) {
- if(data->state.engine) {
- ENGINE_finish(data->state.engine);
- ENGINE_free(data->state.engine);
- data->state.engine = NULL;
- }
- if(!ENGINE_init(e)) {
- char buf[256];
-
- ENGINE_free(e);
- failf(data, "Failed to initialise SSL Engine '%s': %s",
- engine, ossl_strerror(ERR_get_error(), buf, sizeof(buf)));
- return CURLE_SSL_ENGINE_INITFAILED;
+ if(data->state.engine) {
+ ENGINE_finish(data->state.engine);
+ ENGINE_free(data->state.engine);
+ data->state.engine = NULL;
+ }
+ if(!ENGINE_init(e)) {
+ char buf[256];
+
+ ENGINE_free(e);
+ failf(data, "Failed to initialise SSL Engine '%s': %s",
+ name, ossl_strerror(ERR_get_error(), buf, sizeof(buf)));
+ result = CURLE_SSL_ENGINE_INITFAILED;
+ e = NULL;
+ }
+ data->state.engine = e;
+ return result;
}
- data->state.engine = e;
- return CURLE_OK;
+#endif
+#ifdef OPENSSL_HAS_PROVIDERS
+ return ossl_set_provider(data, name);
#else
- (void)engine;
- failf(data, "SSL Engine not supported");
+ (void)name;
+ failf(data, "OpenSSL engine not found");
return CURLE_SSL_ENGINE_NOTFOUND;
#endif
}
@@ -1882,6 +1955,102 @@ static struct curl_slist *ossl_engines_list(struct Curl_easy *data)
return list;
}
+#if defined(OPENSSL_HAS_PROVIDERS)
+
+static void ossl_provider_cleanup(struct Curl_easy *data)
+{
+ if(data->state.baseprov) {
+ OSSL_PROVIDER_unload(data->state.baseprov);
+ data->state.baseprov = NULL;
+ }
+ if(data->state.provider) {
+ OSSL_PROVIDER_unload(data->state.provider);
+ data->state.provider = NULL;
+ }
+ OSSL_LIB_CTX_free(data->state.libctx);
+ data->state.libctx = NULL;
+ Curl_safefree(data->state.propq);
+ data->state.provider_loaded = FALSE;
+}
+
+#define MAX_PROVIDER_LEN 128 /* reasonable */
+
+/* Selects an OpenSSL crypto provider.
+ *
+ * A provider might need an associated property, a string passed on to
+ * OpenSSL. Specify this as [PROVIDER][:PROPERTY]: separate the name and the
+ * property with a colon. No colon means no property is set.
+ *
+ * An example provider + property looks like "tpm2:?provider=tpm2".
+ */
+static CURLcode ossl_set_provider(struct Curl_easy *data, const char *iname)
+{
+ char name[MAX_PROVIDER_LEN + 1];
+ struct Curl_str prov;
+ const char *propq = NULL;
+
+ if(!iname) {
+ /* clear and cleanup provider use */
+ ossl_provider_cleanup(data);
+ return CURLE_OK;
+ }
+ if(curlx_str_until(&iname, &prov, MAX_PROVIDER_LEN, ':'))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ if(!curlx_str_single(&iname, ':'))
+ /* there was a colon, get the propq until the end of string */
+ propq = iname;
+
+ /* we need the name in a buffer, null-terminated */
+ memcpy(name, curlx_str(&prov), curlx_strlen(&prov));
+ name[curlx_strlen(&prov)] = 0;
+
+ if(!data->state.libctx) {
+ OSSL_LIB_CTX *libctx = OSSL_LIB_CTX_new();
+ if(!libctx)
+ return CURLE_OUT_OF_MEMORY;
+ if(propq) {
+ data->state.propq = strdup(propq);
+ if(!data->state.propq) {
+ OSSL_LIB_CTX_free(libctx);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ data->state.libctx = libctx;
+ }
+
+ if(OSSL_PROVIDER_available(data->state.libctx, name)) {
+ /* already loaded through the configuration - no action needed */
+ data->state.provider_loaded = TRUE;
+ return CURLE_OK;
+ }
+
+ data->state.provider =
+ OSSL_PROVIDER_try_load(data->state.libctx, name, 1);
+ if(!data->state.provider) {
+ char error_buffer[256];
+ failf(data, "Failed to initialize provider: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ ossl_provider_cleanup(data);
+ return CURLE_SSL_ENGINE_NOTFOUND;
+ }
+
+ /* load the base provider as well */
+ data->state.baseprov =
+ OSSL_PROVIDER_try_load(data->state.libctx, "base", 1);
+ if(!data->state.baseprov) {
+ ossl_provider_cleanup(data);
+ failf(data, "Failed to load base");
+ return CURLE_SSL_ENGINE_NOTFOUND;
+ }
+ else
+ data->state.provider_loaded = TRUE;
+ return CURLE_OK;
+}
+#endif
+
+
static CURLcode ossl_shutdown(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool send_shutdown, bool *done)
@@ -2021,13 +2190,6 @@ static void ossl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
}
}
-static void ossl_session_free(void *sessionid, size_t idsize)
-{
- /* free the ID */
- (void)idsize;
- free(sessionid);
-}
-
/*
* This function is called when the 'data' struct is going away. Close
* down everything and free all resources!
@@ -2043,8 +2205,10 @@ static void ossl_close_all(struct Curl_easy *data)
#else
(void)data;
#endif
-#if !defined(HAVE_ERR_REMOVE_THREAD_STATE_DEPRECATED) && \
- defined(HAVE_ERR_REMOVE_THREAD_STATE)
+#ifdef OPENSSL_HAS_PROVIDERS
+ ossl_provider_cleanup(data);
+#endif
+#ifndef HAVE_ERR_REMOVE_THREAD_STATE_DEPRECATED
/* OpenSSL 1.0.1 and 1.0.2 build an error queue that is stored per-thread
so we need to clean it here in case the thread will be killed. All OpenSSL
code should extract the error in association with the error so clearing
@@ -2121,14 +2285,14 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data,
hostlen = strlen(peer->hostname);
switch(peer->type) {
case CURL_SSL_PEER_IPV4:
- if(!Curl_inet_pton(AF_INET, peer->hostname, &addr))
+ if(!curlx_inet_pton(AF_INET, peer->hostname, &addr))
return CURLE_PEER_FAILED_VERIFICATION;
target = GEN_IPADD;
addrlen = sizeof(struct in_addr);
break;
#ifdef USE_IPV6
case CURL_SSL_PEER_IPV6:
- if(!Curl_inet_pton(AF_INET6, peer->hostname, &addr))
+ if(!curlx_inet_pton(AF_INET6, peer->hostname, &addr))
return CURLE_PEER_FAILED_VERIFICATION;
target = GEN_IPADD;
addrlen = sizeof(struct in6_addr);
@@ -2174,7 +2338,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data,
/* only check alternatives of the same type the target is */
if(check->type == target) {
/* get data and length */
- const char *altptr = (char *)ASN1_STRING_get0_data(check->d.ia5);
+ const char *altptr = (const char *)ASN1_STRING_get0_data(check->d.ia5);
size_t altlen = (size_t) ASN1_STRING_length(check->d.ia5);
switch(target) {
@@ -2260,7 +2424,7 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data,
if(tmp) {
if(ASN1_STRING_type(tmp) == V_ASN1_UTF8STRING) {
cnlen = ASN1_STRING_length(tmp);
- cn = (unsigned char *) ASN1_STRING_get0_data(tmp);
+ cn = (unsigned char *)CURL_UNCONST(ASN1_STRING_get0_data(tmp));
}
else { /* not a UTF8 name */
cnlen = ASN1_STRING_to_UTF8(&cn, tmp);
@@ -2302,14 +2466,13 @@ static CURLcode ossl_verifyhost(struct Curl_easy *data,
return result;
}
-#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
- !defined(OPENSSL_NO_OCSP)
+#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_OCSP)
static CURLcode verifystatus(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ossl_ctx *octx)
{
int i, ocsp_status;
-#if defined(OPENSSL_IS_AWSLC)
+#ifdef OPENSSL_IS_AWSLC
const uint8_t *status;
#else
unsigned char *status;
@@ -2368,34 +2531,6 @@ static CURLcode verifystatus(struct Curl_cfilter *cf,
}
st = SSL_CTX_get_cert_store(octx->ssl_ctx);
-#if ((OPENSSL_VERSION_NUMBER <= 0x1000201fL) /* Fixed after 1.0.2a */ || \
- (defined(LIBRESSL_VERSION_NUMBER) && \
- LIBRESSL_VERSION_NUMBER <= 0x2040200fL))
- /* The authorized responder cert in the OCSP response MUST be signed by the
- peer cert's issuer (see RFC6960 section 4.2.2.2). If that is a root cert,
- no problem, but if it is an intermediate cert OpenSSL has a bug where it
- expects this issuer to be present in the chain embedded in the OCSP
- response. So we add it if necessary. */
-
- /* First make sure the peer cert chain includes both a peer and an issuer,
- and the OCSP response contains a responder cert. */
- if(sk_X509_num(ch) >= 2 && sk_X509_num(br->certs) >= 1) {
- X509 *responder = sk_X509_value(br->certs, sk_X509_num(br->certs) - 1);
-
- /* Find issuer of responder cert and add it to the OCSP response chain */
- for(i = 0; i < sk_X509_num(ch); i++) {
- X509 *issuer = sk_X509_value(ch, i);
- if(X509_check_issued(issuer, responder) == X509_V_OK) {
- if(!OCSP_basic_add1_cert(br, issuer)) {
- failf(data, "Could not add issuer cert to OCSP response");
- result = CURLE_SSL_INVALIDCERTSTATUS;
- goto end;
- }
- }
- }
- }
-#endif
-
if(OCSP_basic_verify(br, ch, st, 0) <= 0) {
failf(data, "OCSP response verification failed");
result = CURLE_SSL_INVALIDCERTSTATUS;
@@ -2667,15 +2802,15 @@ static void ossl_trace(int direction, int ssl_ver, int content_type,
tls_rt_name = "";
if(content_type == SSL3_RT_CHANGE_CIPHER_SPEC) {
- msg_type = *(char *)buf;
+ msg_type = *(const char *)buf;
msg_name = "Change cipher spec";
}
else if(content_type == SSL3_RT_ALERT) {
- msg_type = (((char *)buf)[0] << 8) + ((char *)buf)[1];
+ msg_type = (((const char *)buf)[0] << 8) + ((const char *)buf)[1];
msg_name = SSL_alert_desc_string_long(msg_type);
}
else {
- msg_type = *(char *)buf;
+ msg_type = *(const char *)buf;
msg_name = ssl_msg_type(ssl_ver, msg_type);
}
@@ -2687,7 +2822,7 @@ static void ossl_trace(int direction, int ssl_ver, int content_type,
}
Curl_debug(data, (direction == 1) ? CURLINFO_SSL_DATA_OUT :
- CURLINFO_SSL_DATA_IN, (char *)buf, len);
+ CURLINFO_SSL_DATA_IN, (const char *)buf, len);
(void) ssl;
}
#endif
@@ -2695,11 +2830,9 @@ static void ossl_trace(int direction, int ssl_ver, int content_type,
#ifdef USE_OPENSSL
/* ====================================================== */
-/* Check for OpenSSL 1.0.2 which has ALPN support. */
-#undef HAS_ALPN
-#if OPENSSL_VERSION_NUMBER >= 0x10002000L \
- && !defined(OPENSSL_NO_TLSEXT)
-# define HAS_ALPN 1
+/* Check for ALPN support. */
+#ifndef OPENSSL_NO_TLSEXT
+# define HAS_ALPN_OPENSSL
#endif
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) /* 1.1.0 */
@@ -2831,20 +2964,10 @@ ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
#endif
FALLTHROUGH();
case CURL_SSLVERSION_TLSv1_2:
-#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
*ctx_options |= SSL_OP_NO_TLSv1_1;
-#else
- failf(data, OSSL_PACKAGE " was built without TLS 1.2 support");
- return CURLE_NOT_BUILT_IN;
-#endif
FALLTHROUGH();
case CURL_SSLVERSION_TLSv1_1:
-#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
*ctx_options |= SSL_OP_NO_TLSv1;
-#else
- failf(data, OSSL_PACKAGE " was built without TLS 1.1 support");
- return CURLE_NOT_BUILT_IN;
-#endif
FALLTHROUGH();
case CURL_SSLVERSION_TLSv1_0:
case CURL_SSLVERSION_TLSv1:
@@ -2853,14 +2976,10 @@ ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
switch(ssl_version_max) {
case CURL_SSLVERSION_MAX_TLSv1_0:
-#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
*ctx_options |= SSL_OP_NO_TLSv1_1;
-#endif
FALLTHROUGH();
case CURL_SSLVERSION_MAX_TLSv1_1:
-#if OPENSSL_VERSION_NUMBER >= 0x1000100FL
*ctx_options |= SSL_OP_NO_TLSv1_2;
-#endif
FALLTHROUGH();
case CURL_SSLVERSION_MAX_TLSv1_2:
#ifdef TLS1_3_VERSION
@@ -2881,20 +3000,27 @@ ossl_set_ssl_version_min_max_legacy(ctx_option_t *ctx_options,
CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
struct Curl_easy *data,
- const struct ssl_peer *peer,
- SSL_SESSION *session)
+ const char *ssl_peer_key,
+ SSL_SESSION *session,
+ int ietf_tls_id,
+ const char *alpn,
+ unsigned char *quic_tp,
+ size_t quic_tp_len)
{
const struct ssl_config_data *config;
+ unsigned char *der_session_buf = NULL;
+ unsigned char *qtp_clone = NULL;
CURLcode result = CURLE_OK;
- size_t der_session_size;
- unsigned char *der_session_buf;
- unsigned char *der_session_ptr;
if(!cf || !data)
goto out;
config = Curl_ssl_cf_get_config(cf, data);
if(config->primary.cache_session) {
+ struct Curl_ssl_session *sc_session = NULL;
+ size_t der_session_size;
+ unsigned char *der_session_ptr;
+ size_t earlydata_max = 0;
der_session_size = i2d_SSL_SESSION(session, NULL);
if(der_session_size == 0) {
@@ -2911,17 +3037,35 @@ CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
der_session_size = i2d_SSL_SESSION(session, &der_session_ptr);
if(der_session_size == 0) {
result = CURLE_OUT_OF_MEMORY;
- free(der_session_buf);
goto out;
}
- Curl_ssl_sessionid_lock(data);
- result = Curl_ssl_set_sessionid(cf, data, peer, NULL, der_session_buf,
- der_session_size, ossl_session_free);
- Curl_ssl_sessionid_unlock(data);
+#ifdef HAVE_OPENSSL_EARLYDATA
+ earlydata_max = SSL_SESSION_get_max_early_data(session);
+#endif
+ if(quic_tp && quic_tp_len) {
+ qtp_clone = Curl_memdup0((char *)quic_tp, quic_tp_len);
+ if(!qtp_clone) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ }
+
+ result = Curl_ssl_session_create2(der_session_buf, der_session_size,
+ ietf_tls_id, alpn,
+ (curl_off_t)time(NULL) +
+ SSL_SESSION_get_timeout(session),
+ earlydata_max, qtp_clone, quic_tp_len,
+ &sc_session);
+ der_session_buf = NULL; /* took ownership of sdata */
+ if(!result) {
+ result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session);
+ /* took ownership of `sc_session` */
+ }
}
out:
+ free(der_session_buf);
return result;
}
@@ -2930,14 +3074,14 @@ out:
*/
static int ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid)
{
- struct Curl_cfilter *cf;
- struct Curl_easy *data;
- struct ssl_connect_data *connssl;
-
- cf = (struct Curl_cfilter*) SSL_get_app_data(ssl);
- connssl = cf ? cf->ctx : NULL;
- data = connssl ? CF_DATA_CURRENT(cf) : NULL;
- Curl_ossl_add_session(cf, data, &connssl->peer, ssl_sessionid);
+ struct Curl_cfilter *cf = (struct Curl_cfilter*) SSL_get_app_data(ssl);
+ if(cf) {
+ struct Curl_easy *data = CF_DATA_CURRENT(cf);
+ struct ssl_connect_data *connssl = cf->ctx;
+ Curl_ossl_add_session(cf, data, connssl->peer.scache_key, ssl_sessionid,
+ SSL_version(ssl), connssl->negotiated.alpn,
+ NULL, 0);
+ }
return 0;
}
@@ -2997,7 +3141,7 @@ static CURLcode load_cacert_from_memory(X509_STORE *store,
return (count > 0) ? CURLE_OK : CURLE_SSL_CACERT_BADFILE;
}
-#if defined(USE_WIN32_CRYPTO)
+#ifdef USE_WIN32_CRYPTO
static CURLcode import_windows_cert_store(struct Curl_easy *data,
const char *name,
X509_STORE *store,
@@ -3141,9 +3285,9 @@ static CURLcode import_windows_cert_store(struct Curl_easy *data,
}
#endif
-static CURLcode populate_x509_store(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- X509_STORE *store)
+static CURLcode ossl_populate_x509_store(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ X509_STORE *store)
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
@@ -3159,13 +3303,13 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf,
bool imported_native_ca = FALSE;
bool imported_ca_info_blob = FALSE;
- CURL_TRC_CF(data, cf, "populate_x509_store, path=%s, blob=%d",
+ CURL_TRC_CF(data, cf, "ossl_populate_x509_store, path=%s, blob=%d",
ssl_cafile ? ssl_cafile : "none", !!ca_info_blob);
if(!store)
return CURLE_OUT_OF_MEMORY;
if(verifypeer) {
-#if defined(USE_WIN32_CRYPTO)
+#ifdef USE_WIN32_CRYPTO
/* Import certificates from the Windows root certificate store if
requested.
https://stackoverflow.com/questions/9507184/
@@ -3177,7 +3321,7 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf,
"CA" /* Intermediate Certification Authorities */
};
size_t i;
- for(i = 0; i < ARRAYSIZE(storeNames); ++i) {
+ for(i = 0; i < CURL_ARRAYSIZE(storeNames); ++i) {
bool imported = FALSE;
result = import_windows_cert_store(data, storeNames[i], store,
&imported);
@@ -3283,7 +3427,7 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf,
https://web.archive.org/web/20190422050538/
rt.openssl.org/Ticket/Display.html?id=3621
*/
-#if defined(X509_V_FLAG_TRUSTED_FIRST)
+#ifdef X509_V_FLAG_TRUSTED_FIRST
X509_STORE_set_flags(store, X509_V_FLAG_TRUSTED_FIRST);
#endif
#ifdef X509_V_FLAG_PARTIAL_CHAIN
@@ -3304,7 +3448,7 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf,
return result;
}
-#if defined(HAVE_SSL_X509_STORE_SHARE)
+#ifdef HAVE_SSL_X509_STORE_SHARE
/* key to use at `multi->proto_hash` */
#define MPROTO_OSSL_X509_KEY "tls:ossl:x509:share"
@@ -3330,15 +3474,15 @@ static void oss_x509_share_free(void *key, size_t key_len, void *p)
}
static bool
-cached_x509_store_expired(const struct Curl_easy *data,
- const struct ossl_x509_share *mb)
+ossl_cached_x509_store_expired(const struct Curl_easy *data,
+ const struct ossl_x509_share *mb)
{
const struct ssl_general_config *cfg = &data->set.general_ssl;
if(cfg->ca_cache_timeout < 0)
return FALSE;
else {
- struct curltime now = Curl_now();
- timediff_t elapsed_ms = Curl_timediff(now, mb->time);
+ struct curltime now = curlx_now();
+ timediff_t elapsed_ms = curlx_timediff(now, mb->time);
timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000;
return elapsed_ms >= timeout_ms;
@@ -3346,8 +3490,8 @@ cached_x509_store_expired(const struct Curl_easy *data,
}
static bool
-cached_x509_store_different(struct Curl_cfilter *cf,
- const struct ossl_x509_share *mb)
+ossl_cached_x509_store_different(struct Curl_cfilter *cf,
+ const struct ossl_x509_share *mb)
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
if(!mb->CAfile || !conn_config->CAfile)
@@ -3356,8 +3500,8 @@ cached_x509_store_different(struct Curl_cfilter *cf,
return strcmp(mb->CAfile, conn_config->CAfile);
}
-static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf,
- const struct Curl_easy *data)
+static X509_STORE *ossl_get_cached_x509_store(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
{
struct Curl_multi *multi = data->multi;
struct ossl_x509_share *share;
@@ -3365,20 +3509,20 @@ static X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf,
DEBUGASSERT(multi);
share = multi ? Curl_hash_pick(&multi->proto_hash,
- (void *)MPROTO_OSSL_X509_KEY,
+ CURL_UNCONST(MPROTO_OSSL_X509_KEY),
sizeof(MPROTO_OSSL_X509_KEY)-1) : NULL;
if(share && share->store &&
- !cached_x509_store_expired(data, share) &&
- !cached_x509_store_different(cf, share)) {
+ !ossl_cached_x509_store_expired(data, share) &&
+ !ossl_cached_x509_store_different(cf, share)) {
store = share->store;
}
return store;
}
-static void set_cached_x509_store(struct Curl_cfilter *cf,
- const struct Curl_easy *data,
- X509_STORE *store)
+static void ossl_set_cached_x509_store(struct Curl_cfilter *cf,
+ const struct Curl_easy *data,
+ X509_STORE *store)
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct Curl_multi *multi = data->multi;
@@ -3388,7 +3532,7 @@ static void set_cached_x509_store(struct Curl_cfilter *cf,
if(!multi)
return;
share = Curl_hash_pick(&multi->proto_hash,
- (void *)MPROTO_OSSL_X509_KEY,
+ CURL_UNCONST(MPROTO_OSSL_X509_KEY),
sizeof(MPROTO_OSSL_X509_KEY)-1);
if(!share) {
@@ -3396,7 +3540,7 @@ static void set_cached_x509_store(struct Curl_cfilter *cf,
if(!share)
return;
if(!Curl_hash_add2(&multi->proto_hash,
- (void *)MPROTO_OSSL_X509_KEY,
+ CURL_UNCONST(MPROTO_OSSL_X509_KEY),
sizeof(MPROTO_OSSL_X509_KEY)-1,
share, oss_x509_share_free)) {
free(share);
@@ -3420,7 +3564,7 @@ static void set_cached_x509_store(struct Curl_cfilter *cf,
free(share->CAfile);
}
- share->time = Curl_now();
+ share->time = curlx_now();
share->store = store;
share->CAfile = CAfile;
}
@@ -3446,16 +3590,16 @@ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf,
!ssl_config->primary.CRLfile &&
!ssl_config->native_ca_store;
- cached_store = get_cached_x509_store(cf, data);
+ cached_store = ossl_get_cached_x509_store(cf, data);
if(cached_store && cache_criteria_met && X509_STORE_up_ref(cached_store)) {
SSL_CTX_set_cert_store(ssl_ctx, cached_store);
}
else {
X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx);
- result = populate_x509_store(cf, data, store);
+ result = ossl_populate_x509_store(cf, data, store);
if(result == CURLE_OK && cache_criteria_met) {
- set_cached_x509_store(cf, data, store);
+ ossl_set_cached_x509_store(cf, data, store);
}
}
@@ -3468,48 +3612,303 @@ CURLcode Curl_ssl_setup_x509_store(struct Curl_cfilter *cf,
{
X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx);
- return populate_x509_store(cf, data, store);
+ return ossl_populate_x509_store(cf, data, store);
}
#endif /* HAVE_SSL_X509_STORE_SHARE */
-CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
+
+static CURLcode
+ossl_init_session_and_alpns(struct ossl_ctx *octx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
- int transport, /* TCP or QUIC */
- const unsigned char *alpn, size_t alpn_len,
- Curl_ossl_ctx_setup_cb *cb_setup,
- void *cb_user_data,
- Curl_ossl_new_session_cb *cb_new_session,
- void *ssl_user_data)
+ const struct alpn_spec *alpns_requested,
+ Curl_ossl_init_session_reuse_cb *sess_reuse_cb)
{
- CURLcode result = CURLE_OK;
- const char *ciphers;
- SSL_METHOD_QUAL SSL_METHOD *req_method = NULL;
- ctx_option_t ctx_options = 0;
- SSL_SESSION *ssl_session = NULL;
- const unsigned char *der_sessionid = NULL;
- size_t der_sessionid_size = 0;
- struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
- const long int ssl_version_min = conn_config->version;
- char * const ssl_cert = ssl_config->primary.clientcert;
- const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob;
- const char * const ssl_cert_type = ssl_config->cert_type;
- const bool verifypeer = conn_config->verifypeer;
+ struct alpn_spec alpns;
char error_buffer[256];
+ CURLcode result;
- /* Make funny stuff to get random input */
- result = ossl_seed(data);
- if(result)
- return result;
+ Curl_alpn_copy(&alpns, alpns_requested);
- ssl_config->certverifyresult = !X509_V_OK;
+ octx->reused_session = FALSE;
+ if(ssl_config->primary.cache_session) {
+ struct Curl_ssl_session *scs = NULL;
+
+ result = Curl_ssl_scache_take(cf, data, peer->scache_key, &scs);
+ if(!result && scs && scs->sdata && scs->sdata_len) {
+ const unsigned char *der_sessionid = scs->sdata;
+ size_t der_sessionid_size = scs->sdata_len;
+ SSL_SESSION *ssl_session = NULL;
+
+ /* If OpenSSL does not accept the session from the cache, this
+ * is not an error. We just continue without it. */
+ ssl_session = d2i_SSL_SESSION(NULL, &der_sessionid,
+ (long)der_sessionid_size);
+ if(ssl_session) {
+ if(!SSL_set_session(octx->ssl, ssl_session)) {
+ infof(data, "SSL: SSL_set_session not accepted, "
+ "continuing without: %s",
+ ossl_strerror(ERR_get_error(), error_buffer,
+ sizeof(error_buffer)));
+ }
+ else {
+ infof(data, "SSL reusing session with ALPN '%s'",
+ scs->alpn ? scs->alpn : "-");
+ octx->reused_session = TRUE;
+#ifdef HAVE_OPENSSL_EARLYDATA
+ if(ssl_config->earlydata && scs->alpn &&
+ SSL_SESSION_get_max_early_data(ssl_session) &&
+ !cf->conn->connect_only &&
+ (SSL_version(octx->ssl) == TLS1_3_VERSION)) {
+ bool do_early_data = FALSE;
+ if(sess_reuse_cb) {
+ result = sess_reuse_cb(cf, data, &alpns, scs, &do_early_data);
+ if(result)
+ return result;
+ }
+ if(do_early_data) {
+ /* We only try the ALPN protocol the session used before,
+ * otherwise we might send early data for the wrong protocol */
+ Curl_alpn_restrict_to(&alpns, scs->alpn);
+ }
+ }
+#else
+ (void)sess_reuse_cb;
+#endif
+ }
+ SSL_SESSION_free(ssl_session);
+ }
+ else {
+ infof(data, "SSL session not accepted by OpenSSL, continuing without");
+ }
+ }
+ Curl_ssl_scache_return(cf, data, peer->scache_key, scs);
+ }
+
+#ifdef HAS_ALPN_OPENSSL
+ if(alpns.count) {
+ struct alpn_proto_buf proto;
+ memset(&proto, 0, sizeof(proto));
+ result = Curl_alpn_to_proto_buf(&proto, &alpns);
+ if(result) {
+ failf(data, "Error determining ALPN");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ if(SSL_set_alpn_protos(octx->ssl, proto.data, (int)proto.len)) {
+ failf(data, "Error setting ALPN");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+#endif
+
+ return CURLE_OK;
+}
+
+#ifdef USE_ECH_OPENSSL
+static CURLcode ossl_init_ech(struct ossl_ctx *octx,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct ssl_peer *peer)
+{
+ unsigned char *ech_config = NULL;
+ size_t ech_config_len = 0;
+ char *outername = data->set.str[STRING_ECH_PUBLIC];
+ int trying_ech_now = 0;
+ CURLcode result;
+
+ if(!ECH_ENABLED(data))
+ return CURLE_OK;
+
+ if(data->set.tls_ech & CURLECH_GREASE) {
+ infof(data, "ECH: will GREASE ClientHello");
+# if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
+ SSL_set_enable_ech_grease(octx->ssl, 1);
+# else
+ SSL_set_options(octx->ssl, SSL_OP_ECH_GREASE);
+# endif
+ }
+ else if(data->set.tls_ech & CURLECH_CLA_CFG) {
+# if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
+ /* have to do base64 decode here for BoringSSL */
+ const char *b64 = data->set.str[STRING_ECH_CONFIG];
- switch(transport) {
+ if(!b64) {
+ infof(data, "ECH: ECHConfig from command line empty");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ ech_config_len = 2 * strlen(b64);
+ result = curlx_base64_decode(b64, &ech_config, &ech_config_len);
+ if(result || !ech_config) {
+ infof(data, "ECH: cannot base64 decode ECHConfig from command line");
+ if(data->set.tls_ech & CURLECH_HARD)
+ return result;
+ }
+ if(SSL_set1_ech_config_list(octx->ssl, ech_config,
+ ech_config_len) != 1) {
+ infof(data, "ECH: SSL_ECH_set1_ech_config_list failed");
+ if(data->set.tls_ech & CURLECH_HARD) {
+ free(ech_config);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+ free(ech_config);
+ trying_ech_now = 1;
+# else
+ ech_config = (unsigned char *) data->set.str[STRING_ECH_CONFIG];
+ if(!ech_config) {
+ infof(data, "ECH: ECHConfig from command line empty");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ ech_config_len = strlen(data->set.str[STRING_ECH_CONFIG]);
+ if(SSL_set1_ech_config_list(octx->ssl, ech_config,
+ ech_config_len) != 1) {
+ infof(data, "ECH: SSL_ECH_set1_ech_config_list failed");
+ if(data->set.tls_ech & CURLECH_HARD)
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ else
+ trying_ech_now = 1;
+# endif
+ infof(data, "ECH: ECHConfig from command line");
+ }
+ else {
+ struct Curl_dns_entry *dns = NULL;
+
+ if(peer->hostname)
+ dns = Curl_dnscache_get(data, peer->hostname, peer->port,
+ cf->conn->ip_version);
+ if(!dns) {
+ infof(data, "ECH: requested but no DNS info available");
+ if(data->set.tls_ech & CURLECH_HARD)
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ else {
+ struct Curl_https_rrinfo *rinfo = NULL;
+
+ rinfo = dns->hinfo;
+ if(rinfo && rinfo->echconfiglist) {
+ unsigned char *ecl = rinfo->echconfiglist;
+ size_t elen = rinfo->echconfiglist_len;
+
+ infof(data, "ECH: ECHConfig from DoH HTTPS RR");
+ if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) {
+ infof(data, "ECH: SSL_set1_ech_config_list failed");
+ if(data->set.tls_ech & CURLECH_HARD)
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ else {
+ trying_ech_now = 1;
+ infof(data, "ECH: imported ECHConfigList of length %zu", elen);
+ }
+ }
+ else {
+ infof(data, "ECH: requested but no ECHConfig available");
+ if(data->set.tls_ech & CURLECH_HARD)
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ Curl_resolv_unlink(data, &dns);
+ }
+ }
+# if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
+ if(trying_ech_now && outername) {
+ infof(data, "ECH: setting public_name not supported with BoringSSL");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+# else
+ if(trying_ech_now && outername) {
+ infof(data, "ECH: inner: '%s', outer: '%s'",
+ peer->hostname ? peer->hostname : "NULL", outername);
+ result = SSL_ech_set1_server_names(octx->ssl,
+ peer->hostname, outername,
+ 0 /* do send outer */);
+ if(result != 1) {
+ infof(data, "ECH: rv failed to set server name(s) %d [ERROR]", result);
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+# endif /* OPENSSL_IS_BORINGSSL || OPENSSL_IS_AWSLC */
+ if(trying_ech_now
+ && SSL_set_min_proto_version(octx->ssl, TLS1_3_VERSION) != 1) {
+ infof(data, "ECH: cannot force TLSv1.3 [ERROR]");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ return CURLE_OK;
+}
+#endif /* USE_ECH_OPENSSL */
+
+
+static CURLcode ossl_init_ssl(struct ossl_ctx *octx,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct ssl_peer *peer,
+ const struct alpn_spec *alpns_requested,
+ void *ssl_user_data,
+ Curl_ossl_init_session_reuse_cb *sess_reuse_cb)
+{
+ /* Let's make an SSL structure */
+ if(octx->ssl)
+ SSL_free(octx->ssl);
+ octx->ssl = SSL_new(octx->ssl_ctx);
+ if(!octx->ssl) {
+ failf(data, "SSL: could not create a context (handle)");
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ SSL_set_app_data(octx->ssl, ssl_user_data);
+
+#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_OCSP)
+ if(Curl_ssl_cf_get_primary_config(cf)->verifystatus)
+ SSL_set_tlsext_status_type(octx->ssl, TLSEXT_STATUSTYPE_ocsp);
+#endif
+
+#if (defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)) && \
+ defined(ALLOW_RENEG)
+ SSL_set_renegotiate_mode(octx->ssl, ssl_renegotiate_freely);
+#endif
+
+ SSL_set_connect_state(octx->ssl);
+
+ octx->server_cert = NULL;
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+ if(peer->sni) {
+ if(!SSL_set_tlsext_host_name(octx->ssl, peer->sni)) {
+ failf(data, "Failed set SNI");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+
+#ifdef USE_ECH_OPENSSL
+ {
+ CURLcode result = ossl_init_ech(octx, cf, data, peer);
+ if(result)
+ return result;
+ }
+#endif /* USE_ECH_OPENSSL */
+
+#endif
+
+ return ossl_init_session_and_alpns(octx, cf, data, peer,
+ alpns_requested, sess_reuse_cb);
+}
+
+
+static CURLcode ossl_init_method(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct ssl_peer *peer,
+ const SSL_METHOD **pmethod,
+ unsigned int *pssl_version_min)
+{
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+
+ *pmethod = NULL;
+ *pssl_version_min = conn_config->version;
+ switch(peer->transport) {
case TRNSPRT_TCP:
/* check to see if we have been told to use an explicit SSL/TLS version */
- switch(ssl_version_min) {
+ switch(*pssl_version_min) {
case CURL_SSLVERSION_DEFAULT:
case CURL_SSLVERSION_TLSv1:
case CURL_SSLVERSION_TLSv1_0:
@@ -3518,9 +3917,9 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
case CURL_SSLVERSION_TLSv1_3:
/* it will be handled later with the context options */
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
- req_method = TLS_client_method();
+ *pmethod = TLS_client_method();
#else
- req_method = SSLv23_client_method();
+ *pmethod = SSLv23_client_method();
#endif
break;
case CURL_SSLVERSION_SSLv2:
@@ -3535,6 +3934,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
}
break;
case TRNSPRT_QUIC:
+ *pssl_version_min = CURL_SSLVERSION_TLSv1_3;
if(conn_config->version_max &&
(conn_config->version_max != CURL_SSLVERSION_MAX_TLSv1_3)) {
failf(data, "QUIC needs at least TLS version 1.3");
@@ -3542,21 +3942,65 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
}
#ifdef USE_OPENSSL_QUIC
- req_method = OSSL_QUIC_client_method();
+ *pmethod = OSSL_QUIC_client_method();
#elif (OPENSSL_VERSION_NUMBER >= 0x10100000L)
- req_method = TLS_method();
+ *pmethod = TLS_method();
#else
- req_method = SSLv23_client_method();
+ *pmethod = SSLv23_client_method();
#endif
break;
default:
- failf(data, "unsupported transport %d in SSL init", transport);
+ failf(data, "unsupported transport %d in SSL init", peer->transport);
return CURLE_SSL_CONNECT_ERROR;
}
+ return *pmethod ? CURLE_OK : CURLE_SSL_CONNECT_ERROR;
+}
+
+
+CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct ssl_peer *peer,
+ const struct alpn_spec *alpns_requested,
+ Curl_ossl_ctx_setup_cb *cb_setup,
+ void *cb_user_data,
+ Curl_ossl_new_session_cb *cb_new_session,
+ void *ssl_user_data,
+ Curl_ossl_init_session_reuse_cb *sess_reuse_cb)
+{
+ CURLcode result = CURLE_OK;
+ const char *ciphers;
+ const SSL_METHOD *req_method = NULL;
+ ctx_option_t ctx_options = 0;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ char * const ssl_cert = ssl_config->primary.clientcert;
+ const struct curl_blob *ssl_cert_blob = ssl_config->primary.cert_blob;
+ const char * const ssl_cert_type = ssl_config->cert_type;
+ const bool verifypeer = conn_config->verifypeer;
+ unsigned int ssl_version_min;
+ char error_buffer[256];
+
+ /* Make funny stuff to get random input */
+ result = ossl_seed(data);
+ if(result)
+ return result;
+
+ ssl_config->certverifyresult = !X509_V_OK;
+
+ result = ossl_init_method(cf, data, peer, &req_method, &ssl_version_min);
+ if(result)
+ return result;
+ DEBUGASSERT(req_method);
DEBUGASSERT(!octx->ssl_ctx);
- octx->ssl_ctx = SSL_CTX_new(req_method);
+ octx->ssl_ctx =
+#ifdef OPENSSL_HAS_PROVIDERS
+ data->state.libctx ?
+ SSL_CTX_new_ex(data->state.libctx, data->state.propq, req_method):
+#endif
+ SSL_CTX_new(req_method);
if(!octx->ssl_ctx) {
failf(data, "SSL: could not create a context: %s",
@@ -3606,7 +4050,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
CVE-2010-4180 when using previous OpenSSL versions we no longer enable
this option regardless of OpenSSL version and SSL_OP_ALL definition.
- OpenSSL added a work-around for a SSL 3.0/TLS 1.0 CBC vulnerability:
+ OpenSSL added a work-around for an SSL 3.0/TLS 1.0 CBC vulnerability:
https://web.archive.org/web/20240114184648/openssl.org/~bodo/tls-cbc.txt.
In 0.9.6e they added a bit to SSL_OP_ALL that _disables_ that work-around
despite the fact that SSL_OP_ALL is documented to do "rather harmless"
@@ -3674,31 +4118,10 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
SSL_CTX_set_mode(octx->ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
#endif
-#ifdef HAS_ALPN
- if(alpn && alpn_len) {
- if(SSL_CTX_set_alpn_protos(octx->ssl_ctx, alpn, (int)alpn_len)) {
- failf(data, "Error setting ALPN");
- return CURLE_SSL_CONNECT_ERROR;
- }
- }
-#endif
-
- if(ssl_cert || ssl_cert_blob || ssl_cert_type) {
- if(!result &&
- !cert_stuff(data, octx->ssl_ctx,
- ssl_cert, ssl_cert_blob, ssl_cert_type,
- ssl_config->key, ssl_config->key_blob,
- ssl_config->key_type, ssl_config->key_passwd))
- result = CURLE_SSL_CERTPROBLEM;
- if(result)
- /* failf() is already done in cert_stuff() */
- return result;
- }
-
ciphers = conn_config->cipher_list;
if(!ciphers && (peer->transport != TRNSPRT_QUIC))
ciphers = DEFAULT_CIPHER_SELECTION;
- if(ciphers) {
+ if(ciphers && (ssl_version_min < CURL_SSLVERSION_TLSv1_3)) {
if(!SSL_CTX_set_cipher_list(octx->ssl_ctx, ciphers)) {
failf(data, "failed setting cipher list: %s", ciphers);
return CURLE_SSL_CIPHER;
@@ -3709,7 +4132,9 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
{
const char *ciphers13 = conn_config->cipher_list13;
- if(ciphers13) {
+ if(ciphers13 &&
+ (!conn_config->version_max ||
+ (conn_config->version_max >= CURL_SSLVERSION_MAX_TLSv1_3))) {
if(!SSL_CTX_set_ciphersuites(octx->ssl_ctx, ciphers13)) {
failf(data, "failed setting TLS 1.3 cipher suite: %s", ciphers13);
return CURLE_SSL_CIPHER;
@@ -3719,21 +4144,51 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
}
#endif
+ if(ssl_cert || ssl_cert_blob || ssl_cert_type) {
+ if(!result &&
+ !cert_stuff(data, octx->ssl_ctx,
+ ssl_cert, ssl_cert_blob, ssl_cert_type,
+ ssl_config->key, ssl_config->key_blob,
+ ssl_config->key_type, ssl_config->key_passwd))
+ result = CURLE_SSL_CERTPROBLEM;
+ if(result)
+ /* failf() is already done in cert_stuff() */
+ return result;
+ }
+
#ifdef HAVE_SSL_CTX_SET_POST_HANDSHAKE_AUTH
/* OpenSSL 1.1.1 requires clients to opt-in for PHA */
SSL_CTX_set_post_handshake_auth(octx->ssl_ctx, 1);
#endif
-#ifdef HAVE_SSL_CTX_SET_EC_CURVES
{
const char *curves = conn_config->curves;
if(curves) {
- if(!SSL_CTX_set1_curves_list(octx->ssl_ctx, curves)) {
+#if defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)
+#define OSSL_CURVE_CAST(x) (x)
+#else
+#define OSSL_CURVE_CAST(x) (char *)CURL_UNCONST(x)
+#endif
+ if(!SSL_CTX_set1_curves_list(octx->ssl_ctx, OSSL_CURVE_CAST(curves))) {
failf(data, "failed setting curves list: '%s'", curves);
return CURLE_SSL_CIPHER;
}
}
}
+
+#ifdef HAVE_SSL_CTX_SET1_SIGALGS
+#define OSSL_SIGALG_CAST(x) OSSL_CURVE_CAST(x)
+ {
+ const char *signature_algorithms = conn_config->signature_algorithms;
+ if(signature_algorithms) {
+ if(!SSL_CTX_set1_sigalgs_list(octx->ssl_ctx,
+ OSSL_SIGALG_CAST(signature_algorithms))) {
+ failf(data, "failed setting signature algorithms: '%s'",
+ signature_algorithms);
+ return CURLE_SSL_CIPHER;
+ }
+ }
+ }
#endif
#ifdef USE_OPENSSL_SRP
@@ -3807,201 +4262,38 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
}
}
- /* Let's make an SSL structure */
- if(octx->ssl)
- SSL_free(octx->ssl);
- octx->ssl = SSL_new(octx->ssl_ctx);
- if(!octx->ssl) {
- failf(data, "SSL: could not create a context (handle)");
- return CURLE_OUT_OF_MEMORY;
- }
-
- SSL_set_app_data(octx->ssl, ssl_user_data);
-
-#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
- !defined(OPENSSL_NO_OCSP)
- if(conn_config->verifystatus)
- SSL_set_tlsext_status_type(octx->ssl, TLSEXT_STATUSTYPE_ocsp);
-#endif
-
-#if (defined(OPENSSL_IS_BORINGSSL) || defined(OPENSSL_IS_AWSLC)) && \
- defined(ALLOW_RENEG)
- SSL_set_renegotiate_mode(octx->ssl, ssl_renegotiate_freely);
-#endif
+ return ossl_init_ssl(octx, cf, data, peer, alpns_requested,
+ ssl_user_data, sess_reuse_cb);
+}
- SSL_set_connect_state(octx->ssl);
+static CURLcode ossl_on_session_reuse(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct alpn_spec *alpns,
+ struct Curl_ssl_session *scs,
+ bool *do_early_data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ CURLcode result = CURLE_OK;
- octx->server_cert = 0x0;
-#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
- if(peer->sni) {
- if(!SSL_set_tlsext_host_name(octx->ssl, peer->sni)) {
- failf(data, "Failed set SNI");
- return CURLE_SSL_CONNECT_ERROR;
- }
+ *do_early_data = FALSE;
+ connssl->earlydata_max = scs->earlydata_max;
+ if(!connssl->earlydata_max) {
+ CURL_TRC_CF(data, cf, "SSL session does not allow earlydata");
}
-
-#ifdef USE_ECH
- if(ECH_ENABLED(data)) {
- unsigned char *ech_config = NULL;
- size_t ech_config_len = 0;
- char *outername = data->set.str[STRING_ECH_PUBLIC];
- int trying_ech_now = 0;
-
- if(data->set.tls_ech & CURLECH_GREASE) {
- infof(data, "ECH: will GREASE ClientHello");
-# ifdef OPENSSL_IS_BORINGSSL
- SSL_set_enable_ech_grease(octx->ssl, 1);
-# else
- SSL_set_options(octx->ssl, SSL_OP_ECH_GREASE);
-# endif
- }
- else if(data->set.tls_ech & CURLECH_CLA_CFG) {
-# ifdef OPENSSL_IS_BORINGSSL
- /* have to do base64 decode here for boring */
- const char *b64 = data->set.str[STRING_ECH_CONFIG];
-
- if(!b64) {
- infof(data, "ECH: ECHConfig from command line empty");
- return CURLE_SSL_CONNECT_ERROR;
- }
- ech_config_len = 2 * strlen(b64);
- result = Curl_base64_decode(b64, &ech_config, &ech_config_len);
- if(result || !ech_config) {
- infof(data, "ECH: cannot base64 decode ECHConfig from command line");
- if(data->set.tls_ech & CURLECH_HARD)
- return result;
- }
- if(SSL_set1_ech_config_list(octx->ssl, ech_config,
- ech_config_len) != 1) {
- infof(data, "ECH: SSL_ECH_set1_echconfig failed");
- if(data->set.tls_ech & CURLECH_HARD) {
- free(ech_config);
- return CURLE_SSL_CONNECT_ERROR;
- }
- }
- free(ech_config);
- trying_ech_now = 1;
-# else
- ech_config = (unsigned char *) data->set.str[STRING_ECH_CONFIG];
- if(!ech_config) {
- infof(data, "ECH: ECHConfig from command line empty");
- return CURLE_SSL_CONNECT_ERROR;
- }
- ech_config_len = strlen(data->set.str[STRING_ECH_CONFIG]);
- if(SSL_ech_set1_echconfig(octx->ssl, ech_config, ech_config_len) != 1) {
- infof(data, "ECH: SSL_ECH_set1_echconfig failed");
- if(data->set.tls_ech & CURLECH_HARD)
- return CURLE_SSL_CONNECT_ERROR;
- }
- else
- trying_ech_now = 1;
-# endif
- infof(data, "ECH: ECHConfig from command line");
- }
- else {
- struct Curl_dns_entry *dns = NULL;
-
- if(peer->hostname)
- dns = Curl_fetch_addr(data, peer->hostname, peer->port);
- if(!dns) {
- infof(data, "ECH: requested but no DNS info available");
- if(data->set.tls_ech & CURLECH_HARD)
- return CURLE_SSL_CONNECT_ERROR;
- }
- else {
- struct Curl_https_rrinfo *rinfo = NULL;
-
- rinfo = dns->hinfo;
- if(rinfo && rinfo->echconfiglist) {
- unsigned char *ecl = rinfo->echconfiglist;
- size_t elen = rinfo->echconfiglist_len;
-
- infof(data, "ECH: ECHConfig from DoH HTTPS RR");
-# ifndef OPENSSL_IS_BORINGSSL
- if(SSL_ech_set1_echconfig(octx->ssl, ecl, elen) != 1) {
- infof(data, "ECH: SSL_ECH_set1_echconfig failed");
- if(data->set.tls_ech & CURLECH_HARD)
- return CURLE_SSL_CONNECT_ERROR;
- }
-# else
- if(SSL_set1_ech_config_list(octx->ssl, ecl, elen) != 1) {
- infof(data, "ECH: SSL_set1_ech_config_list failed (boring)");
- if(data->set.tls_ech & CURLECH_HARD)
- return CURLE_SSL_CONNECT_ERROR;
- }
-# endif
- else {
- trying_ech_now = 1;
- infof(data, "ECH: imported ECHConfigList of length %zu", elen);
- }
- }
- else {
- infof(data, "ECH: requested but no ECHConfig available");
- if(data->set.tls_ech & CURLECH_HARD)
- return CURLE_SSL_CONNECT_ERROR;
- }
- Curl_resolv_unlink(data, &dns);
- }
- }
-# ifdef OPENSSL_IS_BORINGSSL
- if(trying_ech_now && outername) {
- infof(data, "ECH: setting public_name not supported with BoringSSL");
- return CURLE_SSL_CONNECT_ERROR;
- }
-# else
- if(trying_ech_now && outername) {
- infof(data, "ECH: inner: '%s', outer: '%s'",
- peer->hostname ? peer->hostname : "NULL", outername);
- result = SSL_ech_set_server_names(octx->ssl,
- peer->hostname, outername,
- 0 /* do send outer */);
- if(result != 1) {
- infof(data, "ECH: rv failed to set server name(s) %d [ERROR]", result);
- return CURLE_SSL_CONNECT_ERROR;
- }
- }
-# endif /* not BORING */
- if(trying_ech_now
- && SSL_set_min_proto_version(octx->ssl, TLS1_3_VERSION) != 1) {
- infof(data, "ECH: cannot force TLSv1.3 [ERROR]");
- return CURLE_SSL_CONNECT_ERROR;
- }
+ else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) {
+ CURL_TRC_CF(data, cf, "SSL session has different ALPN, no early data");
}
-#endif /* USE_ECH */
-
-#endif
-
- octx->reused_session = FALSE;
- if(ssl_config->primary.cache_session) {
- Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, peer, (void **)&der_sessionid,
- &der_sessionid_size, NULL)) {
- /* we got a session id, use it! */
- ssl_session = d2i_SSL_SESSION(NULL, &der_sessionid,
- (long)der_sessionid_size);
- if(ssl_session) {
- if(!SSL_set_session(octx->ssl, ssl_session)) {
- Curl_ssl_sessionid_unlock(data);
- SSL_SESSION_free(ssl_session);
- failf(data, "SSL: SSL_set_session failed: %s",
- ossl_strerror(ERR_get_error(), error_buffer,
- sizeof(error_buffer)));
- return CURLE_SSL_CONNECT_ERROR;
- }
- SSL_SESSION_free(ssl_session);
- /* Informational message */
- infof(data, "SSL reusing session ID");
- octx->reused_session = TRUE;
- }
- else {
- Curl_ssl_sessionid_unlock(data);
- return CURLE_SSL_CONNECT_ERROR;
- }
- }
- Curl_ssl_sessionid_unlock(data);
+ else {
+ infof(data, "SSL session allows %zu bytes of early data, "
+ "reusing ALPN '%s'", connssl->earlydata_max, scs->alpn);
+ connssl->earlydata_state = ssl_earlydata_await;
+ connssl->state = ssl_connection_deferred;
+ result = Curl_alpn_set_negotiated(cf, data, connssl,
+ (const unsigned char *)scs->alpn,
+ scs->alpn ? strlen(scs->alpn) : 0);
+ *do_early_data = !result;
}
-
- return CURLE_OK;
+ return result;
}
static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
@@ -4009,26 +4301,16 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
{
struct ssl_connect_data *connssl = cf->ctx;
struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
- struct alpn_proto_buf proto;
BIO *bio;
CURLcode result;
DEBUGASSERT(ssl_connect_1 == connssl->connecting_state);
DEBUGASSERT(octx);
- memset(&proto, 0, sizeof(proto));
-#ifdef HAS_ALPN
- if(connssl->alpn) {
- result = Curl_alpn_to_proto_buf(&proto, connssl->alpn);
- if(result) {
- failf(data, "Error determining ALPN");
- return CURLE_SSL_CONNECT_ERROR;
- }
- }
-#endif
- result = Curl_ossl_ctx_init(octx, cf, data, &connssl->peer, TRNSPRT_TCP,
- proto.data, proto.len, NULL, NULL,
- ossl_new_session_cb, cf);
+ result = Curl_ossl_ctx_init(octx, cf, data, &connssl->peer,
+ connssl->alpn, NULL, NULL,
+ ossl_new_session_cb, cf,
+ ossl_on_session_reuse);
if(result)
return result;
@@ -4053,8 +4335,10 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
SSL_set_bio(octx->ssl, bio, bio);
#endif
-#ifdef HAS_ALPN
- if(connssl->alpn) {
+#ifdef HAS_ALPN_OPENSSL
+ if(connssl->alpn && (connssl->state != ssl_connection_deferred)) {
+ struct alpn_proto_buf proto;
+ memset(&proto, 0, sizeof(proto));
Curl_alpn_to_proto_str(&proto, connssl->alpn);
infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
}
@@ -4063,7 +4347,7 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
return CURLE_OK;
}
-#ifdef USE_ECH
+#ifdef USE_ECH_OPENSSL
/* If we have retry configs, then trace those out */
static void ossl_trace_ech_retry_configs(struct Curl_easy *data, SSL* ssl,
int reason)
@@ -4071,7 +4355,7 @@ static void ossl_trace_ech_retry_configs(struct Curl_easy *data, SSL* ssl,
CURLcode result = CURLE_OK;
size_t rcl = 0;
int rv = 1;
-# ifndef OPENSSL_IS_BORINGSSL
+# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
char *inner = NULL;
unsigned char *rcs = NULL;
char *outer = NULL;
@@ -4086,40 +4370,38 @@ static void ossl_trace_ech_retry_configs(struct Curl_easy *data, SSL* ssl,
/* nothing to trace if not doing ECH */
if(!ECH_ENABLED(data))
return;
-# ifndef OPENSSL_IS_BORINGSSL
- rv = SSL_ech_get_retry_config(ssl, &rcs, &rcl);
+# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
+ rv = SSL_ech_get1_retry_config(ssl, &rcs, &rcl);
# else
SSL_get0_ech_retry_configs(ssl, &rcs, &rcl);
rv = (int)rcl;
# endif
if(rv && rcs) {
-# define HEXSTR_MAX 800
char *b64str = NULL;
size_t blen = 0;
- result = Curl_base64_encode((const char *)rcs, rcl,
- &b64str, &blen);
- if(!result && b64str)
+ result = curlx_base64_encode((const char *)rcs, rcl, &b64str, &blen);
+ if(!result && b64str) {
infof(data, "ECH: retry_configs %s", b64str);
- free(b64str);
-# ifndef OPENSSL_IS_BORINGSSL
- rv = SSL_ech_get_status(ssl, &inner, &outer);
- infof(data, "ECH: retry_configs for %s from %s, %d %d",
- inner ? inner : "NULL", outer ? outer : "NULL", reason, rv);
+ free(b64str);
+#if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
+ rv = SSL_ech_get1_status(ssl, &inner, &outer);
+ infof(data, "ECH: retry_configs for %s from %s, %d %d",
+ inner ? inner : "NULL", outer ? outer : "NULL", reason, rv);
#else
- rv = SSL_ech_accepted(ssl);
- servername_type = SSL_get_servername_type(ssl);
- inner = SSL_get_servername(ssl, servername_type);
- SSL_get0_ech_name_override(ssl, &outer, &out_name_len);
- /* TODO: get the inner from boring */
- infof(data, "ECH: retry_configs for %s from %s, %d %d",
- inner ? inner : "NULL", outer ? outer : "NULL", reason, rv);
+ rv = SSL_ech_accepted(ssl);
+ servername_type = SSL_get_servername_type(ssl);
+ inner = SSL_get_servername(ssl, servername_type);
+ SSL_get0_ech_name_override(ssl, &outer, &out_name_len);
+ infof(data, "ECH: retry_configs for %s from %s, %d %d",
+ inner ? inner : "NULL", outer ? outer : "NULL", reason, rv);
#endif
+ }
}
else
infof(data, "ECH: no retry_configs (rv = %d)", rv);
-# ifndef OPENSSL_IS_BORINGSSL
+# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
OPENSSL_free((void *)rcs);
# endif
return;
@@ -4169,27 +4451,25 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
if(SSL_ERROR_WANT_READ == detail) {
CURL_TRC_CF(data, cf, "SSL_connect() -> want recv");
connssl->io_need = CURL_SSL_IO_NEED_RECV;
- return CURLE_OK;
+ return CURLE_AGAIN;
}
if(SSL_ERROR_WANT_WRITE == detail) {
CURL_TRC_CF(data, cf, "SSL_connect() -> want send");
connssl->io_need = CURL_SSL_IO_NEED_SEND;
- return CURLE_OK;
+ return CURLE_AGAIN;
}
#ifdef SSL_ERROR_WANT_ASYNC
if(SSL_ERROR_WANT_ASYNC == detail) {
CURL_TRC_CF(data, cf, "SSL_connect() -> want async");
connssl->io_need = CURL_SSL_IO_NEED_RECV;
- connssl->connecting_state = ssl_connect_2;
- return CURLE_OK;
+ return CURLE_AGAIN;
}
#endif
#ifdef SSL_ERROR_WANT_RETRY_VERIFY
if(SSL_ERROR_WANT_RETRY_VERIFY == detail) {
CURL_TRC_CF(data, cf, "SSL_connect() -> want retry_verify");
connssl->io_need = CURL_SSL_IO_NEED_RECV;
- connssl->connecting_state = ssl_connect_2;
- return CURLE_OK;
+ return CURLE_AGAIN;
}
#endif
else {
@@ -4220,16 +4500,13 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
lerr = SSL_get_verify_result(octx->ssl);
if(lerr != X509_V_OK) {
ssl_config->certverifyresult = lerr;
- msnprintf(error_buffer, sizeof(error_buffer),
- "SSL certificate problem: %s",
- X509_verify_cert_error_string(lerr));
+ failf(data, "SSL certificate problem: %s",
+ X509_verify_cert_error_string(lerr));
}
- else {
+ else
failf(data, "%s", "SSL certificate verification failed");
- return result;
- }
}
-#if defined(SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED)
+#ifdef SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED
/* SSL_R_TLSV13_ALERT_CERTIFICATE_REQUIRED is only available on
OpenSSL version above v1.1.1, not LibreSSL, BoringSSL, or AWS-LC */
else if((lib == ERR_LIB_SSL) &&
@@ -4241,9 +4518,9 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
ossl_strerror(errdetail, error_buffer, sizeof(error_buffer)));
}
#endif
-#ifdef USE_ECH
+#ifdef USE_ECH_OPENSSL
else if((lib == ERR_LIB_SSL) &&
-# ifndef OPENSSL_IS_BORINGSSL
+# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
(reason == SSL_R_ECH_REQUIRED)) {
# else
(reason == SSL_R_ECH_REJECTED)) {
@@ -4278,7 +4555,6 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
failf(data, OSSL_PACKAGE " SSL_connect: %s in connection to %s:%d ",
extramsg[0] ? extramsg : SSL_ERROR_to_str(detail),
connssl->peer.hostname, connssl->peer.port);
- return result;
}
return result;
@@ -4308,14 +4584,14 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
negotiated_group_name ? negotiated_group_name : "[blank]",
OBJ_nid2sn(psigtype_nid));
-#ifdef USE_ECH
-# ifndef OPENSSL_IS_BORINGSSL
+#ifdef USE_ECH_OPENSSL
+# if !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
if(ECH_ENABLED(data)) {
char *inner = NULL, *outer = NULL;
const char *status = NULL;
int rv;
- rv = SSL_ech_get_status(octx->ssl, &inner, &outer);
+ rv = SSL_ech_get1_status(octx->ssl, &inner, &outer);
switch(rv) {
case SSL_ECH_STATUS_SUCCESS:
status = "succeeded";
@@ -4367,10 +4643,10 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
else {
infof(data, "ECH: result: status is not attempted");
}
-# endif /* BORING */
-#endif /* USE_ECH */
+# endif /* !OPENSSL_IS_BORINGSSL && !OPENSSL_IS_AWSLC */
+#endif /* USE_ECH_OPENSSL */
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_OPENSSL
/* Sets data and len to negotiated protocol, len is 0 if no protocol was
* negotiated
*/
@@ -4496,7 +4772,7 @@ static void infof_certstack(struct Curl_easy *data, const SSL *ssl)
sizeof(group_name), NULL);
msnprintf(group_name_final, sizeof(group_name_final), "/%s", group_name);
}
- type_name = EVP_PKEY_get0_type_name(current_pkey);
+ type_name = current_pkey ? EVP_PKEY_get0_type_name(current_pkey) : NULL;
#else
get_group_name = 0;
type_name = NULL;
@@ -4536,7 +4812,7 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf,
DEBUGASSERT(octx);
- Curl_dyn_init(&dname, MAX_CERT_NAME_LENGTH);
+ curlx_dyn_init(&dname, MAX_CERT_NAME_LENGTH);
if(!mem) {
failf(data,
@@ -4566,19 +4842,20 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf,
result = x509_name_oneline(X509_get_subject_name(octx->server_cert),
&dname);
- infof(data, " subject: %s", result ? "[NONE]" : Curl_dyn_ptr(&dname));
+ infof(data, " subject: %s", result ? "[NONE]" : curlx_dyn_ptr(&dname));
#ifndef CURL_DISABLE_VERBOSE_STRINGS
{
+ char *buf;
long len;
ASN1_TIME_print(mem, X509_get0_notBefore(octx->server_cert));
- len = BIO_get_mem_data(mem, (char **) &ptr);
- infof(data, " start date: %.*s", (int)len, ptr);
+ len = BIO_get_mem_data(mem, (char **) &buf);
+ infof(data, " start date: %.*s", (int)len, buf);
(void)BIO_reset(mem);
ASN1_TIME_print(mem, X509_get0_notAfter(octx->server_cert));
- len = BIO_get_mem_data(mem, (char **) &ptr);
- infof(data, " expire date: %.*s", (int)len, ptr);
+ len = BIO_get_mem_data(mem, (char **) &buf);
+ infof(data, " expire date: %.*s", (int)len, buf);
(void)BIO_reset(mem);
}
#endif
@@ -4590,7 +4867,7 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf,
if(result) {
X509_free(octx->server_cert);
octx->server_cert = NULL;
- Curl_dyn_free(&dname);
+ curlx_dyn_free(&dname);
return result;
}
}
@@ -4603,8 +4880,8 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf,
result = CURLE_PEER_FAILED_VERIFICATION;
}
else {
- infof(data, " issuer: %s", Curl_dyn_ptr(&dname));
- Curl_dyn_free(&dname);
+ infof(data, " issuer: %s", curlx_dyn_ptr(&dname));
+ curlx_dyn_free(&dname);
/* We could do all sorts of certificate verification stuff here before
deallocating the certificate. */
@@ -4699,27 +4976,11 @@ CURLcode Curl_oss_check_peer_cert(struct Curl_cfilter *cf,
}
infof_certstack(data, octx->ssl);
-#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
- !defined(OPENSSL_NO_OCSP)
+#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_OCSP)
if(conn_config->verifystatus && !octx->reused_session) {
/* do not do this after Session ID reuse */
result = verifystatus(cf, data, octx);
if(result) {
- /* when verifystatus failed, remove the session id from the cache again
- if present */
- if(!Curl_ssl_cf_is_proxy(cf)) {
- void *old_ssl_sessionid = NULL;
- bool incache;
- Curl_ssl_sessionid_lock(data);
- incache = !(Curl_ssl_getsessionid(cf, data, peer,
- &old_ssl_sessionid, NULL, NULL));
- if(incache) {
- infof(data, "Remove session ID again from cache");
- Curl_ssl_delsessionid(data, old_ssl_sessionid);
- }
- Curl_ssl_sessionid_unlock(data);
- }
-
X509_free(octx->server_cert);
octx->server_cert = NULL;
return result;
@@ -4767,134 +5028,169 @@ static CURLcode ossl_connect_step3(struct Curl_cfilter *cf,
*/
result = Curl_oss_check_peer_cert(cf, data, octx, &connssl->peer);
- if(!result)
- connssl->connecting_state = ssl_connect_done;
+ if(result)
+ /* on error, remove sessions we might have in the pool */
+ Curl_ssl_scache_remove_all(cf, data, connssl->peer.scache_key);
return result;
}
-static CURLcode ossl_connect_common(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool nonblocking,
- bool *done)
+#ifdef HAVE_OPENSSL_EARLYDATA
+static CURLcode ossl_send_earlydata(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
+ CURLcode result = CURLE_OK;
+ const unsigned char *buf;
+ size_t blen, nwritten;
+ int rc;
+
+ DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_sending);
+ octx->io_result = CURLE_OK;
+ while(Curl_bufq_peek(&connssl->earlydata, &buf, &blen)) {
+ nwritten = 0;
+ rc = SSL_write_early_data(octx->ssl, buf, blen, &nwritten);
+ CURL_TRC_CF(data, cf, "SSL_write_early_data(len=%zu) -> %d, %zu",
+ blen, rc, nwritten);
+ if(rc <= 0) {
+ long sslerror;
+ char error_buffer[256];
+ int err = SSL_get_error(octx->ssl, rc);
+
+ switch(err) {
+ case SSL_ERROR_WANT_READ:
+ connssl->io_need = CURL_SSL_IO_NEED_RECV;
+ result = CURLE_AGAIN;
+ goto out;
+ case SSL_ERROR_WANT_WRITE:
+ connssl->io_need = CURL_SSL_IO_NEED_SEND;
+ result = CURLE_AGAIN;
+ goto out;
+ case SSL_ERROR_SYSCALL: {
+ int sockerr = SOCKERRNO;
+
+ if(octx->io_result == CURLE_AGAIN) {
+ result = CURLE_AGAIN;
+ goto out;
+ }
+ sslerror = ERR_get_error();
+ if(sslerror)
+ ossl_strerror(sslerror, error_buffer, sizeof(error_buffer));
+ else if(sockerr)
+ Curl_strerror(sockerr, error_buffer, sizeof(error_buffer));
+ else
+ msnprintf(error_buffer, sizeof(error_buffer), "%s",
+ SSL_ERROR_to_str(err));
+
+ failf(data, OSSL_PACKAGE " SSL_write:early_data: %s, errno %d",
+ error_buffer, sockerr);
+ result = CURLE_SEND_ERROR;
+ goto out;
+ }
+ case SSL_ERROR_SSL: {
+ /* A failure in the SSL library occurred, usually a protocol error.
+ The OpenSSL error queue contains more information on the error. */
+ sslerror = ERR_get_error();
+ failf(data, "SSL_write_early_data() error: %s",
+ ossl_strerror(sslerror, error_buffer, sizeof(error_buffer)));
+ result = CURLE_SEND_ERROR;
+ goto out;
+ }
+ default:
+ /* a true error */
+ failf(data, OSSL_PACKAGE " SSL_write_early_data: %s, errno %d",
+ SSL_ERROR_to_str(err), SOCKERRNO);
+ result = CURLE_SEND_ERROR;
+ goto out;
+ }
+ }
+ Curl_bufq_skip(&connssl->earlydata, nwritten);
+ }
+ /* sent everything there was */
+ infof(data, "SSL sending %zu bytes of early data", connssl->earlydata_skip);
+out:
+ return result;
+}
+#endif /* HAVE_OPENSSL_EARLYDATA */
+
+static CURLcode ossl_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done)
{
CURLcode result = CURLE_OK;
struct ssl_connect_data *connssl = cf->ctx;
- curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
- int what;
- connssl->io_need = CURL_SSL_IO_NEED_NONE;
/* check if the connection has already been established */
if(ssl_connection_complete == connssl->state) {
*done = TRUE;
return CURLE_OK;
}
- if(ssl_connect_1 == connssl->connecting_state) {
- /* Find out how much more time we are allowed */
- const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
- if(timeout_ms < 0) {
- /* no need to continue if time is already up */
- failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
+ *done = FALSE;
+ connssl->io_need = CURL_SSL_IO_NEED_NONE;
+ if(ssl_connect_1 == connssl->connecting_state) {
+ CURL_TRC_CF(data, cf, "ossl_connect, step1");
result = ossl_connect_step1(cf, data);
if(result)
goto out;
}
- while(ssl_connect_2 == connssl->connecting_state) {
-
- /* check allowed time left */
- const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
- if(timeout_ms < 0) {
- /* no need to continue if time already is up */
- failf(data, "SSL connection timeout");
- result = CURLE_OPERATION_TIMEDOUT;
+ if(ssl_connect_2 == connssl->connecting_state) {
+ CURL_TRC_CF(data, cf, "ossl_connect, step2");
+#ifdef HAVE_OPENSSL_EARLYDATA
+ if(connssl->earlydata_state == ssl_earlydata_await) {
goto out;
}
-
- /* if ssl is expecting something, check if it is available. */
- if(!nonblocking && connssl->io_need) {
- curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND) ?
- sockfd : CURL_SOCKET_BAD;
- curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV) ?
- sockfd : CURL_SOCKET_BAD;
-
- what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
- timeout_ms);
- if(what < 0) {
- /* fatal error */
- failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
- result = CURLE_SSL_CONNECT_ERROR;
- goto out;
- }
- if(0 == what) {
- /* timeout */
- failf(data, "SSL connection timeout");
- result = CURLE_OPERATION_TIMEDOUT;
+ else if(connssl->earlydata_state == ssl_earlydata_sending) {
+ result = ossl_send_earlydata(cf, data);
+ if(result)
goto out;
- }
- /* socket is readable or writable */
+ connssl->earlydata_state = ssl_earlydata_sent;
}
+#endif
+ DEBUGASSERT((connssl->earlydata_state == ssl_earlydata_none) ||
+ (connssl->earlydata_state == ssl_earlydata_sent));
- /* Run transaction, and return to the caller if it failed or if this
- * connection is done nonblocking and this loop would execute again. This
- * permits the owner of a multi handle to abort a connection attempt
- * before step2 has completed while ensuring that a client using select()
- * or epoll() will always have a valid fdset to wait on.
- */
result = ossl_connect_step2(cf, data);
- if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state)))
+ if(result)
goto out;
-
- } /* repeat step2 until all transactions are done. */
+ }
if(ssl_connect_3 == connssl->connecting_state) {
+ CURL_TRC_CF(data, cf, "ossl_connect, step3");
result = ossl_connect_step3(cf, data);
if(result)
goto out;
+ connssl->connecting_state = ssl_connect_done;
+#ifdef HAVE_OPENSSL_EARLYDATA
+ if(connssl->earlydata_state > ssl_earlydata_none) {
+ struct ossl_ctx *octx = (struct ossl_ctx *)connssl->backend;
+ /* We should be in this state by now */
+ DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_sent);
+ connssl->earlydata_state =
+ (SSL_get_early_data_status(octx->ssl) == SSL_EARLY_DATA_ACCEPTED) ?
+ ssl_earlydata_accepted : ssl_earlydata_rejected;
+ }
+#endif
}
if(ssl_connect_done == connssl->connecting_state) {
+ CURL_TRC_CF(data, cf, "ossl_connect, done");
connssl->state = ssl_connection_complete;
- *done = TRUE;
}
- else
- *done = FALSE;
-
- /* Reset our connect state machine */
- connssl->connecting_state = ssl_connect_1;
out:
+ if(result == CURLE_AGAIN) {
+ *done = FALSE;
+ return CURLE_OK;
+ }
+ *done = ((connssl->state == ssl_connection_complete) ||
+ (connssl->state == ssl_connection_deferred));
return result;
}
-static CURLcode ossl_connect_nonblocking(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool *done)
-{
- return ossl_connect_common(cf, data, TRUE, done);
-}
-
-static CURLcode ossl_connect(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- CURLcode result;
- bool done = FALSE;
-
- result = ossl_connect_common(cf, data, FALSE, &done);
- if(result)
- return result;
-
- DEBUGASSERT(done);
-
- return CURLE_OK;
-}
-
static bool ossl_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data)
{
@@ -5168,10 +5464,10 @@ static CURLcode ossl_get_channel_binding(struct Curl_easy *data, int sockindex,
}
/* Append "tls-server-end-point:" */
- if(Curl_dyn_addn(binding, prefix, sizeof(prefix) - 1) != CURLE_OK)
+ if(curlx_dyn_addn(binding, prefix, sizeof(prefix) - 1) != CURLE_OK)
return CURLE_OUT_OF_MEMORY;
/* Append digest */
- if(Curl_dyn_addn(binding, buf, length))
+ if(curlx_dyn_addn(binding, buf, length))
return CURLE_OUT_OF_MEMORY;
return CURLE_OK;
@@ -5184,10 +5480,9 @@ static CURLcode ossl_get_channel_binding(struct Curl_easy *data, int sockindex,
#endif
}
-static size_t ossl_version(char *buffer, size_t size)
+size_t Curl_ossl_version(char *buffer, size_t size)
{
#ifdef LIBRESSL_VERSION_NUMBER
-#ifdef HAVE_OPENSSL_VERSION
char *p;
size_t count;
const char *ver = OpenSSL_version(OPENSSL_VERSION);
@@ -5201,13 +5496,6 @@ static size_t ossl_version(char *buffer, size_t size)
*p = '_';
}
return count;
-#else
- return msnprintf(buffer, size, "%s/%lx.%lx.%lx",
- OSSL_PACKAGE,
- (LIBRESSL_VERSION_NUMBER >> 28) & 0xf,
- (LIBRESSL_VERSION_NUMBER >> 20) & 0xff,
- (LIBRESSL_VERSION_NUMBER >> 12) & 0xff);
-#endif
#elif defined(OPENSSL_IS_BORINGSSL)
#ifdef CURL_BORINGSSL_VERSION
return msnprintf(buffer, size, "%s/%s",
@@ -5231,25 +5519,19 @@ static size_t ossl_version(char *buffer, size_t size)
sub[2]='\0';
sub[1]='\0';
ssleay_value = OpenSSL_version_num();
- if(ssleay_value < 0x906000) {
- ssleay_value = SSLEAY_VERSION_NUMBER;
- sub[0]='\0';
- }
- else {
- if(ssleay_value&0xff0) {
- int minor_ver = (ssleay_value >> 4) & 0xff;
- if(minor_ver > 26) {
- /* handle extended version introduced for 0.9.8za */
- sub[1] = (char) ((minor_ver - 1) % 26 + 'a' + 1);
- sub[0] = 'z';
- }
- else {
- sub[0] = (char) (minor_ver + 'a' - 1);
- }
+ if(ssleay_value&0xff0) {
+ int minor_ver = (ssleay_value >> 4) & 0xff;
+ if(minor_ver > 26) {
+ /* handle extended version introduced for 0.9.8za */
+ sub[1] = (char) ((minor_ver - 1) % 26 + 'a' + 1);
+ sub[0] = 'z';
+ }
+ else {
+ sub[0] = (char) (minor_ver + 'a' - 1);
}
- else
- sub[0]='\0';
}
+ else
+ sub[0]='\0';
return msnprintf(buffer, size, "%s/%lx.%lx.%lx%s"
#ifdef OPENSSL_FIPS
@@ -5279,10 +5561,10 @@ static CURLcode ossl_random(struct Curl_easy *data,
}
/* RAND_bytes() returns 1 on success, 0 otherwise. */
rc = RAND_bytes(entropy, (ossl_valsize_t)curlx_uztosi(length));
- return (rc == 1 ? CURLE_OK : CURLE_FAILED_INIT);
+ return rc == 1 ? CURLE_OK : CURLE_FAILED_INIT;
}
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
+#ifndef OPENSSL_NO_SHA256
static CURLcode ossl_sha256sum(const unsigned char *tmp, /* input */
size_t tmplen,
unsigned char *sha256sum /* output */,
@@ -5308,8 +5590,7 @@ static CURLcode ossl_sha256sum(const unsigned char *tmp, /* input */
static bool ossl_cert_status_request(void)
{
-#if (OPENSSL_VERSION_NUMBER >= 0x0090808fL) && !defined(OPENSSL_NO_TLSEXT) && \
- !defined(OPENSSL_NO_OCSP)
+#if !defined(OPENSSL_NO_TLSEXT) && !defined(OPENSSL_NO_OCSP)
return TRUE;
#else
return FALSE;
@@ -5337,7 +5618,10 @@ const struct Curl_ssl Curl_ssl_openssl = {
#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
SSLSUPP_TLS13_CIPHERSUITES |
#endif
-#ifdef USE_ECH
+#ifdef HAVE_SSL_CTX_SET1_SIGALGS
+ SSLSUPP_SIGNATURE_ALGORITHMS |
+#endif
+#ifdef USE_ECH_OPENSSL
SSLSUPP_ECH |
#endif
SSLSUPP_CA_CACHE |
@@ -5348,29 +5632,25 @@ const struct Curl_ssl Curl_ssl_openssl = {
ossl_init, /* init */
ossl_cleanup, /* cleanup */
- ossl_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
+ Curl_ossl_version, /* version */
ossl_shutdown, /* shutdown */
ossl_data_pending, /* data_pending */
ossl_random, /* random */
ossl_cert_status_request, /* cert_status_request */
ossl_connect, /* connect */
- ossl_connect_nonblocking, /* connect_nonblocking */
Curl_ssl_adjust_pollset, /* adjust_pollset */
ossl_get_internals, /* get_internals */
ossl_close, /* close_one */
ossl_close_all, /* close_all */
- ossl_set_engine, /* set_engine */
+ ossl_set_engine, /* set_engine or provider */
ossl_set_engine_default, /* set_engine_default */
ossl_engines_list, /* engines_list */
- Curl_none_false_start, /* false_start */
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
+ NULL, /* false_start */
+#ifndef OPENSSL_NO_SHA256
ossl_sha256sum, /* sha256sum */
#else
NULL, /* sha256sum */
#endif
- NULL, /* use of data in this connection */
- NULL, /* remote of data from this connection */
ossl_recv, /* recv decrypted data */
ossl_send, /* send data to encrypt */
ossl_get_channel_binding /* get_channel_binding */
diff --git a/libs/libcurl/src/vtls/openssl.h b/libs/libcurl/src/vtls/openssl.h
index e662e23a8d..1ca175c942 100644
--- a/libs/libcurl/src/vtls/openssl.h
+++ b/libs/libcurl/src/vtls/openssl.h
@@ -24,19 +24,43 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_OPENSSL
/*
* This header should only be needed to get included by vtls.c, openssl.c
* and ngtcp2.c
*/
+#include <openssl/opensslv.h>
#include <openssl/ossl_typ.h>
#include <openssl/ssl.h>
-#include "urldata.h"
+#include "../urldata.h"
-/* Struct to hold a Curl OpenSSL instance */
+/*
+ * Whether SSL_CTX_set_keylog_callback is available.
+ * OpenSSL: supported since 1.1.1 https://github.com/openssl/openssl/pull/2287
+ * BoringSSL: supported since d28f59c27bac (committed 2015-11-19)
+ * LibreSSL: not supported. 3.5.0+ has a stub function that does nothing.
+ */
+#if (OPENSSL_VERSION_NUMBER >= 0x10101000L && \
+ !defined(LIBRESSL_VERSION_NUMBER)) || \
+ defined(OPENSSL_IS_BORINGSSL)
+#define HAVE_KEYLOG_CALLBACK
+#endif
+
+/* Check for OpenSSL 1.1.1 which has early data support. */
+#undef HAVE_OPENSSL_EARLYDATA
+#if OPENSSL_VERSION_NUMBER >= 0x10100010L && defined(TLS1_3_VERSION) && \
+ !defined(OPENSSL_IS_BORINGSSL) && !defined(OPENSSL_IS_AWSLC)
+#define HAVE_OPENSSL_EARLYDATA
+#endif
+
+struct alpn_spec;
+struct ssl_peer;
+struct Curl_ssl_session;
+
+/* Struct to hold a curl OpenSSL instance */
struct ossl_ctx {
/* these ones requires specific SSL-types */
SSL_CTX* ssl_ctx;
@@ -53,22 +77,29 @@ struct ossl_ctx {
BIT(reused_session); /* session-ID was reused for this */
};
+size_t Curl_ossl_version(char *buffer, size_t size);
+
typedef CURLcode Curl_ossl_ctx_setup_cb(struct Curl_cfilter *cf,
struct Curl_easy *data,
void *user_data);
typedef int Curl_ossl_new_session_cb(SSL *ssl, SSL_SESSION *ssl_sessionid);
+typedef CURLcode Curl_ossl_init_session_reuse_cb(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct alpn_spec *alpns,
+ struct Curl_ssl_session *scs,
+ bool *do_early_data);
CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
- int transport, /* TCP or QUIC */
- const unsigned char *alpn, size_t alpn_len,
+ const struct alpn_spec *alpns,
Curl_ossl_ctx_setup_cb *cb_setup,
void *cb_user_data,
Curl_ossl_new_session_cb *cb_new_session,
- void *ssl_user_data);
+ void *ssl_user_data,
+ Curl_ossl_init_session_reuse_cb *sess_reuse_cb);
#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
#define SSL_get1_peer_certificate SSL_get_peer_certificate
@@ -94,8 +125,12 @@ CURLcode Curl_ossl_ctx_configure(struct Curl_cfilter *cf,
*/
CURLcode Curl_ossl_add_session(struct Curl_cfilter *cf,
struct Curl_easy *data,
- const struct ssl_peer *peer,
- SSL_SESSION *ssl_sessionid);
+ const char *ssl_peer_key,
+ SSL_SESSION *ssl_sessionid,
+ int ietf_tls_id,
+ const char *alpn,
+ unsigned char *quic_tp,
+ size_t quic_tp_len);
/*
* Get the server cert, verify it and show it, etc., only call failf() if
diff --git a/libs/libcurl/src/vtls/rustls.c b/libs/libcurl/src/vtls/rustls.c
index b10328d4aa..4876175212 100644
--- a/libs/libcurl/src/vtls/rustls.c
+++ b/libs/libcurl/src/vtls/rustls.c
@@ -8,6 +8,7 @@
* Copyright (C) Jacob Hoffman-Andrews,
* <github@hoffman-andrews.com>
* Copyright (C) kpcyrd, <kpcyrd@archlinux.org>
+ * Copyright (C) Daniel McCarney, <daniel@binaryparadox.net>
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -23,27 +24,24 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_RUSTLS
-#include "curl_printf.h"
+#include "../curl_printf.h"
-#include <errno.h>
#include <rustls.h>
-#include "inet_pton.h"
-#include "urldata.h"
-#include "sendf.h"
+#include "../curlx/inet_pton.h"
+#include "../urldata.h"
+#include "../sendf.h"
#include "vtls.h"
#include "vtls_int.h"
#include "rustls.h"
-#include "select.h"
-#include "strerror.h"
-#include "multiif.h"
-#include "connect.h" /* for the connect timeout */
+#include "keylog.h"
+#include "../strerror.h"
#include "cipher_suite.h"
-#include "rand.h"
+#include "x509asn1.h"
struct rustls_ssl_backend_data
{
@@ -55,7 +53,7 @@ struct rustls_ssl_backend_data
};
/* For a given rustls_result error code, return the best-matching CURLcode. */
-static CURLcode map_error(rustls_result r)
+static CURLcode map_error(const rustls_result r)
{
if(rustls_result_is_cert_error(r)) {
return CURLE_PEER_FAILED_VERIFICATION;
@@ -70,10 +68,19 @@ static CURLcode map_error(rustls_result r)
}
}
+static void
+rustls_failf(struct Curl_easy *data, const rustls_result rr, const char *msg)
+{
+ char errorbuf[STRERROR_LEN];
+ size_t errorlen;
+ rustls_error(rr, errorbuf, sizeof(errorbuf), &errorlen);
+ failf(data, "%s: %.*s", msg, (int)errorlen, errorbuf);
+}
+
static bool
cr_data_pending(struct Curl_cfilter *cf, const struct Curl_easy *data)
{
- struct ssl_connect_data *ctx = cf->ctx;
+ const struct ssl_connect_data *ctx = cf->ctx;
struct rustls_ssl_backend_data *backend;
(void)data;
@@ -90,7 +97,7 @@ struct io_ctx {
static int
read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n)
{
- struct io_ctx *io_ctx = userdata;
+ const struct io_ctx *io_ctx = userdata;
struct ssl_connect_data *const connssl = io_ctx->cf->ctx;
CURLcode result;
int ret = 0;
@@ -98,6 +105,7 @@ read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n)
(char *)buf, len, &result);
if(nread < 0) {
nread = 0;
+ /* !checksrc! disable ERRNOVAR 4 */
if(CURLE_AGAIN == result)
ret = EAGAIN;
else
@@ -114,7 +122,7 @@ read_cb(void *userdata, uint8_t *buf, uintptr_t len, uintptr_t *out_n)
static int
write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n)
{
- struct io_ctx *io_ctx = userdata;
+ const struct io_ctx *io_ctx = userdata;
CURLcode result;
int ret = 0;
ssize_t nwritten = Curl_conn_cf_send(io_ctx->cf->next, io_ctx->data,
@@ -136,7 +144,7 @@ write_cb(void *userdata, const uint8_t *buf, uintptr_t len, uintptr_t *out_n)
static ssize_t tls_recv_more(struct Curl_cfilter *cf,
struct Curl_easy *data, CURLcode *err)
{
- struct ssl_connect_data *const connssl = cf->ctx;
+ const struct ssl_connect_data *const connssl = cf->ctx;
struct rustls_ssl_backend_data *const backend =
(struct rustls_ssl_backend_data *)connssl->backend;
struct io_ctx io_ctx;
@@ -162,11 +170,7 @@ static ssize_t tls_recv_more(struct Curl_cfilter *cf,
rresult = rustls_connection_process_new_packets(backend->conn);
if(rresult != RUSTLS_RESULT_OK) {
- char errorbuf[255];
- size_t errorlen;
- rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
- failf(data, "rustls_connection_process_new_packets: %.*s",
- (int)errorlen, errorbuf);
+ rustls_failf(data, rresult, "rustls_connection_process_new_packets");
*err = map_error(rresult);
return -1;
}
@@ -192,7 +196,7 @@ static ssize_t
cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
char *plainbuf, size_t plainlen, CURLcode *err)
{
- struct ssl_connect_data *const connssl = cf->ctx;
+ const struct ssl_connect_data *const connssl = cf->ctx;
struct rustls_ssl_backend_data *const backend =
(struct rustls_ssl_backend_data *)connssl->backend;
struct rustls_connection *rconn = NULL;
@@ -232,10 +236,7 @@ cr_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
}
else if(rresult != RUSTLS_RESULT_OK) {
/* n always equals 0 in this case, do not need to check it */
- char errorbuf[255];
- size_t errorlen;
- rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
- failf(data, "rustls_connection_read: %.*s", (int)errorlen, errorbuf);
+ rustls_failf(data, rresult, "rustls_connection_read");
*err = CURLE_RECV_ERROR;
nread = -1;
goto out;
@@ -278,7 +279,6 @@ static CURLcode cr_flush_out(struct Curl_cfilter *cf, struct Curl_easy *data,
rustls_io_result io_error;
size_t tlswritten = 0;
size_t tlswritten_total = 0;
- CURLcode result = CURLE_OK;
io_ctx.cf = cf;
io_ctx.data = data;
@@ -304,7 +304,7 @@ static CURLcode cr_flush_out(struct Curl_cfilter *cf, struct Curl_easy *data,
CURL_TRC_CF(data, cf, "cf_send: wrote %zu TLS bytes", tlswritten);
tlswritten_total += tlswritten;
}
- return result;
+ return CURLE_OK;
}
/*
@@ -321,14 +321,11 @@ static ssize_t
cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
const void *plainbuf, size_t plainlen, CURLcode *err)
{
- struct ssl_connect_data *const connssl = cf->ctx;
+ const struct ssl_connect_data *const connssl = cf->ctx;
struct rustls_ssl_backend_data *const backend =
(struct rustls_ssl_backend_data *)connssl->backend;
struct rustls_connection *rconn = NULL;
size_t plainwritten = 0;
- rustls_result rresult;
- char errorbuf[256];
- size_t errorlen;
const unsigned char *buf = plainbuf;
size_t blen = plainlen;
ssize_t nwritten = 0;
@@ -360,11 +357,11 @@ cr_send(struct Curl_cfilter *cf, struct Curl_easy *data,
}
if(blen > 0) {
+ rustls_result rresult;
CURL_TRC_CF(data, cf, "cf_send: adding %zu plain bytes to Rustls", blen);
rresult = rustls_connection_write(rconn, buf, blen, &plainwritten);
if(rresult != RUSTLS_RESULT_OK) {
- rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
- failf(data, "rustls_connection_write: %.*s", (int)errorlen, errorbuf);
+ rustls_failf(data, rresult, "rustls_connection_write");
*err = CURLE_WRITE_ERROR;
return -1;
}
@@ -418,9 +415,9 @@ read_file_into(const char *filename,
while(!feof(f)) {
uint8_t buf[256];
- size_t rr = fread(buf, 1, sizeof(buf), f);
+ const size_t rr = fread(buf, 1, sizeof(buf), f);
if(rr == 0 ||
- CURLE_OK != Curl_dyn_addn(out, buf, rr)) {
+ CURLE_OK != curlx_dyn_addn(out, buf, rr)) {
fclose(f);
return 0;
}
@@ -436,8 +433,8 @@ cr_get_selected_ciphers(struct Curl_easy *data,
const struct rustls_supported_ciphersuite **selected,
size_t *selected_size)
{
- size_t supported_len = *selected_size;
- size_t default_len = rustls_default_crypto_provider_ciphersuites_len();
+ const size_t supported_len = *selected_size;
+ const size_t default_len = rustls_default_crypto_provider_ciphersuites_len();
const struct rustls_supported_ciphersuite *entry;
const char *ciphers = ciphers12;
size_t count = 0, default13_count = 0, i, j;
@@ -522,247 +519,608 @@ add_ciphers:
*selected_size = count;
}
+static void
+cr_keylog_log_cb(struct rustls_str label,
+ const uint8_t *client_random, size_t client_random_len,
+ const uint8_t *secret, size_t secret_len)
+{
+ char clabel[KEYLOG_LABEL_MAXLEN];
+ (void)client_random_len;
+ DEBUGASSERT(client_random_len == CLIENT_RANDOM_SIZE);
+ /* Turning a "rustls_str" into a null delimited "c" string */
+ msnprintf(clabel, label.len + 1, "%.*s", (int)label.len, label.data);
+ Curl_tls_keylog_write(clabel, client_random, secret, secret_len);
+}
+
static CURLcode
-cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
- struct rustls_ssl_backend_data *const backend)
+init_config_builder(struct Curl_easy *data,
+ const struct ssl_primary_config *conn_config,
+ struct rustls_client_config_builder **config_builder)
{
- struct ssl_connect_data *connssl = cf->ctx;
- struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ const struct rustls_supported_ciphersuite **cipher_suites = NULL;
struct rustls_crypto_provider_builder *custom_provider_builder = NULL;
const struct rustls_crypto_provider *custom_provider = NULL;
- struct rustls_connection *rconn = NULL;
- struct rustls_client_config_builder *config_builder = NULL;
- const struct rustls_root_cert_store *roots = NULL;
- struct rustls_root_cert_store_builder *roots_builder = NULL;
- struct rustls_web_pki_server_cert_verifier_builder *verifier_builder = NULL;
- struct rustls_server_cert_verifier *server_cert_verifier = NULL;
- const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
- const char * const ssl_cafile =
- /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */
- (ca_info_blob ? NULL : conn_config->CAfile);
- const bool verifypeer = conn_config->verifypeer;
- char errorbuf[256];
- size_t errorlen;
- rustls_result result;
- DEBUGASSERT(backend);
- rconn = backend->conn;
+ uint16_t tls_versions[2] = {
+ RUSTLS_TLS_VERSION_TLSV1_2,
+ RUSTLS_TLS_VERSION_TLSV1_3,
+ };
+ size_t tls_versions_len = 2;
+ size_t cipher_suites_len =
+ rustls_default_crypto_provider_ciphersuites_len();
- {
- uint16_t tls_versions[2] = {
- RUSTLS_TLS_VERSION_TLSV1_2,
- RUSTLS_TLS_VERSION_TLSV1_3,
- };
- size_t tls_versions_len = 2;
- const struct rustls_supported_ciphersuite **cipher_suites;
- size_t cipher_suites_len =
- rustls_default_crypto_provider_ciphersuites_len();
-
- switch(conn_config->version) {
- case CURL_SSLVERSION_DEFAULT:
- case CURL_SSLVERSION_TLSv1:
- case CURL_SSLVERSION_TLSv1_0:
- case CURL_SSLVERSION_TLSv1_1:
- case CURL_SSLVERSION_TLSv1_2:
- break;
- case CURL_SSLVERSION_TLSv1_3:
- tls_versions[0] = RUSTLS_TLS_VERSION_TLSV1_3;
+ CURLcode result = CURLE_OK;
+ rustls_result rr;
+
+ switch(conn_config->version) {
+ case CURL_SSLVERSION_DEFAULT:
+ case CURL_SSLVERSION_TLSv1:
+ case CURL_SSLVERSION_TLSv1_0:
+ case CURL_SSLVERSION_TLSv1_1:
+ case CURL_SSLVERSION_TLSv1_2:
+ break;
+ case CURL_SSLVERSION_TLSv1_3:
+ tls_versions[0] = RUSTLS_TLS_VERSION_TLSV1_3;
+ tls_versions_len = 1;
+ break;
+ default:
+ failf(data, "rustls: unsupported minimum TLS version value");
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto cleanup;
+ }
+
+ switch(conn_config->version_max) {
+ case CURL_SSLVERSION_MAX_DEFAULT:
+ case CURL_SSLVERSION_MAX_NONE:
+ case CURL_SSLVERSION_MAX_TLSv1_3:
+ break;
+ case CURL_SSLVERSION_MAX_TLSv1_2:
+ if(tls_versions[0] == RUSTLS_TLS_VERSION_TLSV1_2) {
tls_versions_len = 1;
break;
- default:
- failf(data, "rustls: unsupported minimum TLS version value");
- return CURLE_BAD_FUNCTION_ARGUMENT;
}
+ FALLTHROUGH();
+ case CURL_SSLVERSION_MAX_TLSv1_1:
+ case CURL_SSLVERSION_MAX_TLSv1_0:
+ default:
+ failf(data, "rustls: unsupported maximum TLS version value");
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto cleanup;
+ }
- switch(conn_config->version_max) {
- case CURL_SSLVERSION_MAX_DEFAULT:
- case CURL_SSLVERSION_MAX_NONE:
- case CURL_SSLVERSION_MAX_TLSv1_3:
- break;
- case CURL_SSLVERSION_MAX_TLSv1_2:
- if(tls_versions[0] == RUSTLS_TLS_VERSION_TLSV1_2) {
- tls_versions_len = 1;
- break;
- }
- FALLTHROUGH();
- case CURL_SSLVERSION_MAX_TLSv1_1:
- case CURL_SSLVERSION_MAX_TLSv1_0:
- default:
- failf(data, "rustls: unsupported maximum TLS version value");
- return CURLE_BAD_FUNCTION_ARGUMENT;
- }
+#if defined(USE_ECH)
+ if(ECH_ENABLED(data)) {
+ tls_versions[0] = RUSTLS_TLS_VERSION_TLSV1_3;
+ tls_versions_len = 1;
+ infof(data, "rustls: ECH enabled, forcing TLSv1.3");
+ }
+#endif /* USE_ECH */
- cipher_suites = malloc(sizeof(cipher_suites) * (cipher_suites_len));
- if(!cipher_suites)
- return CURLE_OUT_OF_MEMORY;
-
- cr_get_selected_ciphers(data,
- conn_config->cipher_list,
- conn_config->cipher_list13,
- cipher_suites, &cipher_suites_len);
- if(cipher_suites_len == 0) {
- failf(data, "rustls: no supported cipher in list");
- free(cipher_suites);
- return CURLE_SSL_CIPHER;
- }
+ cipher_suites = malloc(sizeof(cipher_suites) * (cipher_suites_len));
+ if(!cipher_suites) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto cleanup;
+ }
- result = rustls_crypto_provider_builder_new_from_default(
- &custom_provider_builder);
- if(result != RUSTLS_RESULT_OK) {
- failf(data,
- "rustls: failed to create crypto provider builder from default");
- return CURLE_SSL_CIPHER;
- }
+ cr_get_selected_ciphers(data,
+ conn_config->cipher_list,
+ conn_config->cipher_list13,
+ cipher_suites, &cipher_suites_len);
+ if(cipher_suites_len == 0) {
+ failf(data, "rustls: no supported cipher in list");
+ result = CURLE_SSL_CIPHER;
+ goto cleanup;
+ }
- result =
- rustls_crypto_provider_builder_set_cipher_suites(
- custom_provider_builder,
- cipher_suites,
- cipher_suites_len);
- if(result != RUSTLS_RESULT_OK) {
- failf(data,
- "rustls: failed to set ciphersuites for crypto provider builder");
- rustls_crypto_provider_builder_free(custom_provider_builder);
- return CURLE_SSL_CIPHER;
- }
+ rr = rustls_crypto_provider_builder_new_from_default(
+ &custom_provider_builder);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr,
+ "failed to create crypto provider builder from default");
+ result = CURLE_SSL_CIPHER;
+ goto cleanup;
+ }
- result = rustls_crypto_provider_builder_build(
- custom_provider_builder, &custom_provider);
- if(result != RUSTLS_RESULT_OK) {
- failf(data, "rustls: failed to build custom crypto provider");
- rustls_crypto_provider_builder_free(custom_provider_builder);
- return CURLE_SSL_CIPHER;
- }
+ rr =
+ rustls_crypto_provider_builder_set_cipher_suites(
+ custom_provider_builder,
+ cipher_suites,
+ cipher_suites_len);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr,
+ "failed to set ciphersuites for crypto provider builder");
+ result = CURLE_SSL_CIPHER;
+ goto cleanup;
+ }
- result = rustls_client_config_builder_new_custom(custom_provider,
+ rr = rustls_crypto_provider_builder_build(
+ custom_provider_builder, &custom_provider);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "failed to build custom crypto provider");
+ result = CURLE_SSL_CIPHER;
+ goto cleanup;
+ }
+
+ rr = rustls_client_config_builder_new_custom(custom_provider,
tls_versions,
tls_versions_len,
- &config_builder);
+ config_builder);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "failed to create client config builder");
+ result = CURLE_SSL_CIPHER;
+ goto cleanup;
+ }
+
+cleanup:
+ if(cipher_suites) {
free(cipher_suites);
- if(result != RUSTLS_RESULT_OK) {
- failf(data, "rustls: failed to create client config");
- return CURLE_SSL_CIPHER;
- }
}
+ if(custom_provider_builder) {
+ rustls_crypto_provider_builder_free(custom_provider_builder);
+ }
+ if(custom_provider) {
+ rustls_crypto_provider_free(custom_provider);
+ }
+ return result;
+}
- rustls_crypto_provider_builder_free(custom_provider_builder);
- rustls_crypto_provider_free(custom_provider);
+static void
+init_config_builder_alpn(struct Curl_easy *data,
+ const struct ssl_connect_data *connssl,
+ struct rustls_client_config_builder *config_builder) {
+ struct alpn_proto_buf proto;
+ rustls_slice_bytes alpn[ALPN_ENTRIES_MAX];
+ size_t i;
- if(connssl->alpn) {
- struct alpn_proto_buf proto;
- rustls_slice_bytes alpn[ALPN_ENTRIES_MAX];
- size_t i;
+ for(i = 0; i < connssl->alpn->count; ++i) {
+ alpn[i].data = (const uint8_t *)connssl->alpn->entries[i];
+ alpn[i].len = strlen(connssl->alpn->entries[i]);
+ }
+ rustls_client_config_builder_set_alpn_protocols(config_builder, alpn,
+ connssl->alpn->count);
+ Curl_alpn_to_proto_str(&proto, connssl->alpn);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
+}
- for(i = 0; i < connssl->alpn->count; ++i) {
- alpn[i].data = (const uint8_t *)connssl->alpn->entries[i];
- alpn[i].len = strlen(connssl->alpn->entries[i]);
- }
- rustls_client_config_builder_set_alpn_protocols(config_builder, alpn,
- connssl->alpn->count);
- Curl_alpn_to_proto_str(&proto, connssl->alpn);
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
+static CURLcode
+init_config_builder_verifier_crl(
+ struct Curl_easy *data,
+ const struct ssl_primary_config *conn_config,
+ struct rustls_web_pki_server_cert_verifier_builder *builder)
+{
+ CURLcode result = CURLE_OK;
+ struct dynbuf crl_contents;
+ rustls_result rr;
+
+ curlx_dyn_init(&crl_contents, DYN_CRLFILE_SIZE);
+ if(!read_file_into(conn_config->CRLfile, &crl_contents)) {
+ failf(data, "rustls: failed to read revocation list file");
+ result = CURLE_SSL_CRL_BADFILE;
+ goto cleanup;
}
- if(!verifypeer) {
- rustls_client_config_builder_dangerous_set_certificate_verifier(
- config_builder, cr_verify_none);
+
+ rr = rustls_web_pki_server_cert_verifier_builder_add_crl(
+ builder,
+ curlx_dyn_uptr(&crl_contents),
+ curlx_dyn_len(&crl_contents));
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "failed to parse revocation list");
+ result = CURLE_SSL_CRL_BADFILE;
+ goto cleanup;
}
- else if(ca_info_blob || ssl_cafile) {
- roots_builder = rustls_root_cert_store_builder_new();
-
- if(ca_info_blob) {
- /* Enable strict parsing only if verification is not disabled. */
- result = rustls_root_cert_store_builder_add_pem(roots_builder,
- ca_info_blob->data,
- ca_info_blob->len,
- verifypeer);
- if(result != RUSTLS_RESULT_OK) {
- failf(data, "rustls: failed to parse trusted certificates from blob");
- rustls_root_cert_store_builder_free(roots_builder);
- rustls_client_config_builder_free(config_builder);
- return CURLE_SSL_CACERT_BADFILE;
- }
+
+cleanup:
+ curlx_dyn_free(&crl_contents);
+ return result;
+}
+
+static CURLcode
+init_config_builder_verifier(struct Curl_easy *data,
+ struct rustls_client_config_builder *builder,
+ const struct ssl_primary_config *conn_config,
+ const struct curl_blob *ca_info_blob,
+ const char * const ssl_cafile) {
+ const struct rustls_root_cert_store *roots = NULL;
+ struct rustls_root_cert_store_builder *roots_builder = NULL;
+ struct rustls_web_pki_server_cert_verifier_builder *verifier_builder = NULL;
+ struct rustls_server_cert_verifier *server_cert_verifier = NULL;
+ rustls_result rr = RUSTLS_RESULT_OK;
+ CURLcode result = CURLE_OK;
+
+ roots_builder = rustls_root_cert_store_builder_new();
+ if(ca_info_blob) {
+ rr = rustls_root_cert_store_builder_add_pem(roots_builder,
+ ca_info_blob->data,
+ ca_info_blob->len,
+ 1);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "failed to parse trusted certificates from blob");
+
+ result = CURLE_SSL_CACERT_BADFILE;
+ goto cleanup;
}
- else if(ssl_cafile) {
- /* Enable strict parsing only if verification is not disabled. */
- result = rustls_root_cert_store_builder_load_roots_from_file(
- roots_builder, ssl_cafile, verifypeer);
- if(result != RUSTLS_RESULT_OK) {
- failf(data, "rustls: failed to load trusted certificates");
- rustls_root_cert_store_builder_free(roots_builder);
- rustls_client_config_builder_free(config_builder);
- return CURLE_SSL_CACERT_BADFILE;
- }
+ }
+ else if(ssl_cafile) {
+ rr = rustls_root_cert_store_builder_load_roots_from_file(roots_builder,
+ ssl_cafile,
+ 1);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "failed to load trusted certificates");
+
+ result = CURLE_SSL_CACERT_BADFILE;
+ goto cleanup;
}
+ }
- result = rustls_root_cert_store_builder_build(roots_builder, &roots);
- rustls_root_cert_store_builder_free(roots_builder);
- if(result != RUSTLS_RESULT_OK) {
- failf(data, "rustls: failed to build trusted root certificate store");
- rustls_client_config_builder_free(config_builder);
- return CURLE_SSL_CACERT_BADFILE;
+ rr = rustls_root_cert_store_builder_build(roots_builder, &roots);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "failed to build trusted root certificate store");
+ result = CURLE_SSL_CACERT_BADFILE;
+ }
+
+ verifier_builder = rustls_web_pki_server_cert_verifier_builder_new(roots);
+
+ if(conn_config->CRLfile) {
+ result = init_config_builder_verifier_crl(data,
+ conn_config,
+ verifier_builder);
+ if(result != CURLE_OK) {
+ goto cleanup;
}
+ }
- verifier_builder = rustls_web_pki_server_cert_verifier_builder_new(roots);
+ rr = rustls_web_pki_server_cert_verifier_builder_build(
+ verifier_builder, &server_cert_verifier);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "failed to build certificate verifier");
+ result = CURLE_SSL_CACERT_BADFILE;
+ goto cleanup;
+ }
+
+ rustls_client_config_builder_set_server_verifier(builder,
+ server_cert_verifier);
+cleanup:
+ if(roots_builder) {
+ rustls_root_cert_store_builder_free(roots_builder);
+ }
+ if(roots) {
rustls_root_cert_store_free(roots);
+ }
+ if(verifier_builder) {
+ rustls_web_pki_server_cert_verifier_builder_free(verifier_builder);
+ }
+ if(server_cert_verifier) {
+ rustls_server_cert_verifier_free(server_cert_verifier);
+ }
- if(conn_config->CRLfile) {
- struct dynbuf crl_contents;
- Curl_dyn_init(&crl_contents, SIZE_MAX);
- if(!read_file_into(conn_config->CRLfile, &crl_contents)) {
- failf(data, "rustls: failed to read revocation list file");
- Curl_dyn_free(&crl_contents);
- rustls_web_pki_server_cert_verifier_builder_free(verifier_builder);
- return CURLE_SSL_CRL_BADFILE;
- }
+ return result;
+}
- result = rustls_web_pki_server_cert_verifier_builder_add_crl(
- verifier_builder,
- Curl_dyn_uptr(&crl_contents),
- Curl_dyn_len(&crl_contents));
- Curl_dyn_free(&crl_contents);
- if(result != RUSTLS_RESULT_OK) {
- failf(data, "rustls: failed to parse revocation list");
- rustls_web_pki_server_cert_verifier_builder_free(verifier_builder);
- return CURLE_SSL_CRL_BADFILE;
- }
+static CURLcode
+init_config_builder_platform_verifier(
+ struct Curl_easy *data,
+ struct rustls_client_config_builder *builder)
+{
+ struct rustls_server_cert_verifier *server_cert_verifier = NULL;
+ CURLcode result = CURLE_OK;
+ rustls_result rr;
+
+ rr = rustls_platform_server_cert_verifier(&server_cert_verifier);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "failed to create platform certificate verifier");
+ result = CURLE_SSL_CACERT_BADFILE;
+ goto cleanup;
+ }
+
+ rustls_client_config_builder_set_server_verifier(builder,
+ server_cert_verifier);
+
+cleanup:
+ if(server_cert_verifier) {
+ rustls_server_cert_verifier_free(server_cert_verifier);
+ }
+ return result;
+}
+
+static CURLcode
+init_config_builder_keylog(struct Curl_easy *data,
+ struct rustls_client_config_builder *builder)
+{
+ rustls_result rr;
+
+ Curl_tls_keylog_open();
+ if(!Curl_tls_keylog_enabled()) {
+ return CURLE_OK;
+ }
+
+ rr = rustls_client_config_builder_set_key_log(builder,
+ cr_keylog_log_cb,
+ NULL);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "rustls_client_config_builder_set_key_log");
+ Curl_tls_keylog_close();
+ return map_error(rr);
+ }
+
+ return CURLE_OK;
+}
+
+static CURLcode
+init_config_builder_client_auth(struct Curl_easy *data,
+ const struct ssl_primary_config *conn_config,
+ const struct ssl_config_data *ssl_config,
+ struct rustls_client_config_builder *builder)
+{
+ struct dynbuf cert_contents;
+ struct dynbuf key_contents;
+ rustls_result rr;
+ const struct rustls_certified_key *certified_key = NULL;
+ CURLcode result = CURLE_OK;
+
+ if(conn_config->clientcert && !ssl_config->key) {
+ failf(data, "rustls: must provide key with certificate '%s'",
+ conn_config->clientcert);
+ return CURLE_SSL_CERTPROBLEM;
+ }
+ else if(!conn_config->clientcert && ssl_config->key) {
+ failf(data, "rustls: must provide certificate with key '%s'",
+ conn_config->clientcert);
+ return CURLE_SSL_CERTPROBLEM;
+ }
+
+ curlx_dyn_init(&cert_contents, DYN_CERTFILE_SIZE);
+ curlx_dyn_init(&key_contents, DYN_KEYFILE_SIZE);
+
+ if(!read_file_into(conn_config->clientcert, &cert_contents)) {
+ failf(data, "rustls: failed to read client certificate file: '%s'",
+ conn_config->clientcert);
+ result = CURLE_SSL_CERTPROBLEM;
+ goto cleanup;
+ }
+
+ if(!read_file_into(ssl_config->key, &key_contents)) {
+ failf(data, "rustls: failed to read key file: '%s'", ssl_config->key);
+ result = CURLE_SSL_CERTPROBLEM;
+ goto cleanup;
+ }
+
+ rr = rustls_certified_key_build(curlx_dyn_uptr(&cert_contents),
+ curlx_dyn_len(&cert_contents),
+ curlx_dyn_uptr(&key_contents),
+ curlx_dyn_len(&key_contents),
+ &certified_key);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "rustls: failed to build certified key");
+ result = CURLE_SSL_CERTPROBLEM;
+ goto cleanup;
+ }
+
+ rr = rustls_certified_key_keys_match(certified_key);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data,
+ rr,
+ "rustls: client certificate and keypair files do not match:");
+
+ result = CURLE_SSL_CERTPROBLEM;
+ goto cleanup;
+ }
+
+ rr = rustls_client_config_builder_set_certified_key(builder,
+ &certified_key,
+ 1);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "rustls: failed to set certified key");
+ result = CURLE_SSL_CERTPROBLEM;
+ goto cleanup;
+ }
+
+cleanup:
+ curlx_dyn_free(&cert_contents);
+ curlx_dyn_free(&key_contents);
+ if(certified_key) {
+ rustls_certified_key_free(certified_key);
+ }
+ return result;
+}
+
+#if defined(USE_ECH)
+static CURLcode
+init_config_builder_ech(struct Curl_easy *data,
+ const struct ssl_connect_data *connssl,
+ struct rustls_client_config_builder *builder)
+{
+ const rustls_hpke *hpke = rustls_supported_hpke();
+ unsigned char *ech_config = NULL;
+ size_t ech_config_len = 0;
+ struct Curl_dns_entry *dns = NULL;
+ struct Curl_https_rrinfo *rinfo = NULL;
+ CURLcode result = CURLE_OK;
+ rustls_result rr;
+
+ if(!hpke) {
+ failf(data,
+ "rustls: ECH unavailable, rustls-ffi built without "
+ "HPKE compatible crypto provider");
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto cleanup;
+ }
+
+ if(data->set.str[STRING_ECH_PUBLIC]) {
+ failf(data, "rustls: ECH outername not supported");
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto cleanup;
+ }
+
+ if(data->set.tls_ech == CURLECH_GREASE) {
+ rr = rustls_client_config_builder_enable_ech_grease(builder, hpke);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "rustls: failed to configure ECH GREASE");
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto cleanup;
}
+ return CURLE_OK;
+ }
- result = rustls_web_pki_server_cert_verifier_builder_build(
- verifier_builder, &server_cert_verifier);
- rustls_web_pki_server_cert_verifier_builder_free(verifier_builder);
- if(result != RUSTLS_RESULT_OK) {
- failf(data, "rustls: failed to build certificate verifier");
- rustls_server_cert_verifier_free(server_cert_verifier);
+ if(data->set.tls_ech & CURLECH_CLA_CFG && data->set.str[STRING_ECH_CONFIG]) {
+ const char *b64 = data->set.str[STRING_ECH_CONFIG];
+ size_t decode_result;
+ if(!b64) {
+ infof(data, "rustls: ECHConfig from command line empty");
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto cleanup;
+ }
+ /* rustls-ffi expects the raw TLS encoded ECHConfigList bytes */
+ decode_result = curlx_base64_decode(b64, &ech_config, &ech_config_len);
+ if(decode_result || !ech_config) {
+ infof(data, "rustls: cannot base64 decode ECHConfig from command line");
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto cleanup;
+ }
+ }
+ else {
+ if(connssl->peer.hostname) {
+ dns = Curl_dnscache_get(data, connssl->peer.hostname,
+ connssl->peer.port, data->conn->ip_version);
+ }
+ if(!dns) {
+ failf(data, "rustls: ECH requested but no DNS info available");
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto cleanup;
+ }
+ rinfo = dns->hinfo;
+ if(!rinfo || !rinfo->echconfiglist) {
+ failf(data, "rustls: ECH requested but no ECHConfig available");
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto cleanup;
+ }
+ ech_config = rinfo->echconfiglist;
+ ech_config_len = rinfo->echconfiglist_len;
+ }
+
+ rr = rustls_client_config_builder_enable_ech(builder,
+ ech_config,
+ ech_config_len,
+ hpke);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "rustls: failed to configure ECH");
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto cleanup;
+ }
+cleanup:
+ /* if we base64 decoded, we can free now */
+ if(data->set.tls_ech & CURLECH_CLA_CFG && data->set.str[STRING_ECH_CONFIG]) {
+ free(ech_config);
+ }
+ if(dns) {
+ Curl_resolv_unlink(data, &dns);
+ }
+ return result;
+}
+#endif /* USE_ECH */
+
+static CURLcode
+cr_init_backend(struct Curl_cfilter *cf, struct Curl_easy *data,
+ struct rustls_ssl_backend_data *const backend)
+{
+ const struct ssl_connect_data *connssl = cf->ctx;
+ const struct ssl_primary_config *conn_config =
+ Curl_ssl_cf_get_primary_config(cf);
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ struct rustls_connection *rconn = NULL;
+ struct rustls_client_config_builder *config_builder = NULL;
+
+ const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
+ const char * const ssl_cafile =
+ /* CURLOPT_CAINFO_BLOB overrides CURLOPT_CAINFO */
+ (ca_info_blob ? NULL : conn_config->CAfile);
+ CURLcode result = CURLE_OK;
+ rustls_result rr;
+
+ DEBUGASSERT(backend);
+ rconn = backend->conn;
+
+ result = init_config_builder(data, conn_config, &config_builder);
+ if(result != CURLE_OK) {
+ return result;
+ }
+
+ if(connssl->alpn) {
+ init_config_builder_alpn(data, connssl, config_builder);
+ }
+
+ if(!conn_config->verifypeer) {
+ rustls_client_config_builder_dangerous_set_certificate_verifier(
+ config_builder, cr_verify_none);
+ }
+ else if(ssl_config->native_ca_store) {
+ result = init_config_builder_platform_verifier(data, config_builder);
+ if(result != CURLE_OK) {
+ rustls_client_config_builder_free(config_builder);
+ return result;
+ }
+ }
+ else if(ca_info_blob || ssl_cafile) {
+ result = init_config_builder_verifier(data,
+ config_builder,
+ conn_config,
+ ca_info_blob,
+ ssl_cafile);
+ if(result != CURLE_OK) {
rustls_client_config_builder_free(config_builder);
- return CURLE_SSL_CACERT_BADFILE;
+ return result;
}
+ }
- rustls_client_config_builder_set_server_verifier(config_builder,
- server_cert_verifier);
- rustls_server_cert_verifier_free(server_cert_verifier);
+ if(conn_config->clientcert || ssl_config->key) {
+ result = init_config_builder_client_auth(data,
+ conn_config,
+ ssl_config,
+ config_builder);
+ if(result != CURLE_OK) {
+ rustls_client_config_builder_free(config_builder);
+ return result;
+ }
+ }
+
+#if defined(USE_ECH)
+ if(ECH_ENABLED(data)) {
+ result = init_config_builder_ech(data, connssl, config_builder);
+ if(result != CURLE_OK && data->set.tls_ech & CURLECH_HARD) {
+ rustls_client_config_builder_free(config_builder);
+ return result;
+ }
+ }
+#endif /* USE_ECH */
+
+ result = init_config_builder_keylog(data, config_builder);
+ if(result != CURLE_OK) {
+ rustls_client_config_builder_free(config_builder);
+ return result;
}
- result = rustls_client_config_builder_build(
+ rr = rustls_client_config_builder_build(
config_builder,
&backend->config);
- if(result != RUSTLS_RESULT_OK) {
- failf(data, "rustls: failed to build client config");
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, rr, "failed to build client config");
+ rustls_client_config_builder_free(config_builder);
rustls_client_config_free(backend->config);
return CURLE_SSL_CONNECT_ERROR;
}
DEBUGASSERT(rconn == NULL);
- result = rustls_client_connection_new(backend->config,
- connssl->peer.hostname, &rconn);
- if(result != RUSTLS_RESULT_OK) {
- rustls_error(result, errorbuf, sizeof(errorbuf), &errorlen);
- failf(data, "rustls_client_connection_new: %.*s", (int)errorlen, errorbuf);
+ rr = rustls_client_connection_new(backend->config,
+ connssl->peer.hostname,
+ &rconn);
+ if(rr != RUSTLS_RESULT_OK) {
+ rustls_failf(data, result, "rustls_client_connection_new");
return CURLE_COULDNT_CONNECT;
}
DEBUGASSERT(rconn);
rustls_connection_set_userdata(rconn, backend);
backend->conn = rconn;
- return CURLE_OK;
+
+ return result;
}
static void
@@ -779,48 +1137,36 @@ cr_set_negotiated_alpn(struct Curl_cfilter *cf, struct Curl_easy *data,
/* Given an established network connection, do a TLS handshake.
*
- * If `blocking` is true, this function will block until the handshake is
- * complete. Otherwise it will return as soon as I/O would block.
- *
- * For the non-blocking I/O case, this function will set `*done` to true
- * once the handshake is complete. This function never reads the value of
- * `*done*`.
+ * This function will set `*done` to true once the handshake is complete.
+ * This function never reads the value of `*done*`.
*/
static CURLcode
-cr_connect_common(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool blocking,
- bool *done)
+cr_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data, bool *done)
{
struct ssl_connect_data *const connssl = cf->ctx;
- curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
- struct rustls_ssl_backend_data *const backend =
+ const struct rustls_ssl_backend_data *const backend =
(struct rustls_ssl_backend_data *)connssl->backend;
- struct rustls_connection *rconn = NULL;
+ const struct rustls_connection *rconn = NULL;
CURLcode tmperr = CURLE_OK;
int result;
- int what;
bool wants_read;
bool wants_write;
- curl_socket_t writefd;
- curl_socket_t readfd;
- timediff_t timeout_ms;
- timediff_t socket_check_timeout;
DEBUGASSERT(backend);
- CURL_TRC_CF(data, cf, "cr_connect_common, state=%d", connssl->state);
+ CURL_TRC_CF(data, cf, "cr_connect, state=%d", connssl->state);
*done = FALSE;
+
if(!backend->conn) {
result = cr_init_backend(cf, data,
(struct rustls_ssl_backend_data *)connssl->backend);
- CURL_TRC_CF(data, cf, "cr_connect_common, init backend -> %d", result);
+ CURL_TRC_CF(data, cf, "cr_connect, init backend -> %d", result);
if(result != CURLE_OK) {
return result;
}
connssl->state = ssl_connection_negotiating;
}
-
rconn = backend->conn;
/* Read/write data until the handshake is done or the socket would block. */
@@ -846,8 +1192,10 @@ cr_connect_common(struct Curl_cfilter *cf,
}
/* REALLY Done with the handshake. */
{
- uint16_t proto = rustls_connection_get_protocol_version(rconn);
- uint16_t cipher = rustls_connection_get_negotiated_ciphersuite(rconn);
+ const uint16_t proto =
+ rustls_connection_get_protocol_version(rconn);
+ const uint16_t cipher =
+ rustls_connection_get_negotiated_ciphersuite(rconn);
char buf[64] = "";
const char *ver = "TLS version unknown";
if(proto == RUSTLS_TLS_VERSION_TLSV1_3)
@@ -858,6 +1206,43 @@ cr_connect_common(struct Curl_cfilter *cf,
infof(data, "rustls: handshake complete, %s, cipher: %s",
ver, buf);
}
+ if(data->set.ssl.certinfo) {
+ size_t num_certs = 0;
+ while(rustls_connection_get_peer_certificate(rconn, (int)num_certs)) {
+ num_certs++;
+ }
+ result = Curl_ssl_init_certinfo(data, (int)num_certs);
+ if(result)
+ return result;
+ for(size_t i = 0; i < num_certs; i++) {
+ const rustls_certificate *cert;
+ const unsigned char *der_data;
+ size_t der_len;
+ rustls_result rresult = RUSTLS_RESULT_OK;
+ cert = rustls_connection_get_peer_certificate(rconn, i);
+ DEBUGASSERT(cert); /* Should exist since we counted already */
+ rresult = rustls_certificate_get_der(cert, &der_data, &der_len);
+ if(rresult != RUSTLS_RESULT_OK) {
+ char errorbuf[255];
+ size_t errorlen;
+ rustls_error(rresult, errorbuf, sizeof(errorbuf), &errorlen);
+ failf(data,
+ "Failed getting DER of server certificate #%ld: %.*s", i,
+ (int)errorlen, errorbuf);
+ return map_error(rresult);
+ }
+ {
+ const char *beg;
+ const char *end;
+ beg = (const char *)der_data;
+ end = (const char *)(der_data + der_len);
+ result = Curl_extract_certinfo(data, (int)i, beg, end);
+ if(result)
+ return result;
+ }
+ }
+ }
+
connssl->state = ssl_connection_complete;
*done = TRUE;
return CURLE_OK;
@@ -868,50 +1253,14 @@ cr_connect_common(struct Curl_cfilter *cf,
wants_write = rustls_connection_wants_write(rconn) ||
backend->plain_out_buffered;
DEBUGASSERT(wants_read || wants_write);
- writefd = wants_write ? sockfd : CURL_SOCKET_BAD;
- readfd = wants_read ? sockfd : CURL_SOCKET_BAD;
-
- /* check allowed time left */
- timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
- if(timeout_ms < 0) {
- /* no need to continue if time already is up */
- failf(data, "rustls: operation timed out before socket check");
- return CURLE_OPERATION_TIMEDOUT;
- }
-
- socket_check_timeout = blocking ? timeout_ms : 0;
-
- what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
- socket_check_timeout);
- if(what < 0) {
- /* fatal error */
- failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
- return CURLE_SSL_CONNECT_ERROR;
- }
- if(blocking && 0 == what) {
- failf(data, "rustls: connection timeout after %" FMT_TIMEDIFF_T " ms",
- socket_check_timeout);
- return CURLE_OPERATION_TIMEDOUT;
- }
- if(0 == what) {
- CURL_TRC_CF(data, cf, "Curl_socket_check: %s would block",
- wants_read && wants_write ? "writing and reading" :
- wants_write ? "writing" : "reading");
- if(wants_write)
- connssl->io_need |= CURL_SSL_IO_NEED_SEND;
- if(wants_read)
- connssl->io_need |= CURL_SSL_IO_NEED_RECV;
- return CURLE_OK;
- }
- /* socket is readable or writable */
if(wants_write) {
CURL_TRC_CF(data, cf, "rustls_connection wants us to write_tls.");
cr_send(cf, data, NULL, 0, &tmperr);
if(tmperr == CURLE_AGAIN) {
CURL_TRC_CF(data, cf, "writing would block");
- /* fall through */
+ connssl->io_need = CURL_SSL_IO_NEED_SEND;
+ return CURLE_OK;
}
else if(tmperr != CURLE_OK) {
return tmperr;
@@ -923,7 +1272,8 @@ cr_connect_common(struct Curl_cfilter *cf,
if(tls_recv_more(cf, data, &tmperr) < 0) {
if(tmperr == CURLE_AGAIN) {
CURL_TRC_CF(data, cf, "reading would block");
- /* fall through */
+ connssl->io_need = CURL_SSL_IO_NEED_RECV;
+ return CURLE_OK;
}
else if(tmperr == CURLE_RECV_ERROR) {
return CURLE_SSL_CONNECT_ERROR;
@@ -940,20 +1290,6 @@ cr_connect_common(struct Curl_cfilter *cf,
DEBUGASSERT(FALSE);
}
-static CURLcode
-cr_connect_nonblocking(struct Curl_cfilter *cf,
- struct Curl_easy *data, bool *done)
-{
- return cr_connect_common(cf, data, false, done);
-}
-
-static CURLcode
-cr_connect_blocking(struct Curl_cfilter *cf, struct Curl_easy *data)
-{
- bool done; /* unused */
- return cr_connect_common(cf, data, true, &done);
-}
-
static void *
cr_get_internals(struct ssl_connect_data *connssl,
CURLINFO info UNUSED_PARAM)
@@ -967,14 +1303,13 @@ cr_get_internals(struct ssl_connect_data *connssl,
static CURLcode
cr_shutdown(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool send_shutdown, bool *done)
+ const bool send_shutdown, bool *done)
{
struct ssl_connect_data *connssl = cf->ctx;
struct rustls_ssl_backend_data *backend =
(struct rustls_ssl_backend_data *)connssl->backend;
CURLcode result = CURLE_OK;
ssize_t nwritten, nread;
- char buf[1024];
size_t i;
DEBUGASSERT(backend);
@@ -1007,6 +1342,7 @@ cr_shutdown(struct Curl_cfilter *cf,
}
for(i = 0; i < 10; ++i) {
+ char buf[1024];
nread = cr_recv(cf, data, buf, (int)sizeof(buf), &result);
if(nread <= 0)
break;
@@ -1036,7 +1372,7 @@ out:
static void
cr_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
- struct ssl_connect_data *connssl = cf->ctx;
+ const struct ssl_connect_data *connssl = cf->ctx;
struct rustls_ssl_backend_data *backend =
(struct rustls_ssl_backend_data *)connssl->backend;
@@ -1054,7 +1390,7 @@ cr_close(struct Curl_cfilter *cf, struct Curl_easy *data)
static size_t cr_version(char *buffer, size_t size)
{
- struct rustls_str ver = rustls_version();
+ const struct rustls_str ver = rustls_version();
return msnprintf(buffer, size, "%.*s", (int)ver.len, ver.data);
}
@@ -1068,35 +1404,38 @@ cr_random(struct Curl_easy *data, unsigned char *entropy, size_t length)
return map_error(rresult);
}
+static void cr_cleanup(void)
+{
+ Curl_tls_keylog_close();
+}
+
const struct Curl_ssl Curl_ssl_rustls = {
{ CURLSSLBACKEND_RUSTLS, "rustls" },
SSLSUPP_CAINFO_BLOB | /* supports */
SSLSUPP_HTTPS_PROXY |
SSLSUPP_CIPHER_LIST |
- SSLSUPP_TLS13_CIPHERSUITES,
+ SSLSUPP_TLS13_CIPHERSUITES |
+ SSLSUPP_CERTINFO |
+ SSLSUPP_ECH,
sizeof(struct rustls_ssl_backend_data),
- Curl_none_init, /* init */
- Curl_none_cleanup, /* cleanup */
+ NULL, /* init */
+ cr_cleanup, /* cleanup */
cr_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
cr_shutdown, /* shutdown */
cr_data_pending, /* data_pending */
cr_random, /* random */
- Curl_none_cert_status_request, /* cert_status_request */
- cr_connect_blocking, /* connect */
- cr_connect_nonblocking, /* connect_nonblocking */
+ NULL, /* cert_status_request */
+ cr_connect, /* connect */
Curl_ssl_adjust_pollset, /* adjust_pollset */
cr_get_internals, /* get_internals */
cr_close, /* close_one */
- Curl_none_close_all, /* close_all */
- Curl_none_set_engine, /* set_engine */
- Curl_none_set_engine_default, /* set_engine_default */
- Curl_none_engines_list, /* engines_list */
- Curl_none_false_start, /* false_start */
+ NULL, /* close_all */
+ NULL, /* set_engine */
+ NULL, /* set_engine_default */
+ NULL, /* engines_list */
+ NULL, /* false_start */
NULL, /* sha256sum */
- NULL, /* associate_connection */
- NULL, /* disassociate_connection */
cr_recv, /* recv decrypted data */
cr_send, /* send data to encrypt */
NULL, /* get_channel_binding */
diff --git a/libs/libcurl/src/vtls/rustls.h b/libs/libcurl/src/vtls/rustls.h
index d7c56de190..5e084eb785 100644
--- a/libs/libcurl/src/vtls/rustls.h
+++ b/libs/libcurl/src/vtls/rustls.h
@@ -25,7 +25,7 @@
#ifndef HEADER_CURL_RUSTLS_H
#define HEADER_CURL_RUSTLS_H
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_RUSTLS
diff --git a/libs/libcurl/src/vtls/schannel.c b/libs/libcurl/src/vtls/schannel.c
index 85d018d322..a8b12d65e9 100644
--- a/libs/libcurl/src/vtls/schannel.c
+++ b/libs/libcurl/src/vtls/schannel.c
@@ -29,7 +29,7 @@
* but vtls.c should ever call or use these functions.
*/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_SCHANNEL
@@ -41,23 +41,26 @@
#include "schannel_int.h"
#include "vtls.h"
#include "vtls_int.h"
-#include "strcase.h"
-#include "sendf.h"
-#include "connect.h" /* for the connect timeout */
-#include "strerror.h"
-#include "select.h" /* for the socket readiness */
-#include "inet_pton.h" /* for IP addr SNI check */
-#include "curl_multibyte.h"
-#include "warnless.h"
+#include "vtls_scache.h"
+#include "../strcase.h"
+#include "../sendf.h"
+#include "../connect.h" /* for the connect timeout */
+#include "../strerror.h"
+#include "../select.h" /* for the socket readiness */
+#include "../curlx/inet_pton.h" /* for IP addr SNI check */
+#include "../curlx/multibyte.h"
+#include "../curlx/warnless.h"
#include "x509asn1.h"
-#include "curl_printf.h"
-#include "multiif.h"
-#include "version_win32.h"
-#include "rand.h"
+#include "../curl_printf.h"
+#include "../multiif.h"
+#include "../system_win32.h"
+#include "../curlx/version_win32.h"
+#include "../rand.h"
+#include "../curlx/strparse.h"
/* The last #include file should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
/* Some verbose debug messages are wrapped by SCH_DEV() instead of DEBUGF()
* and only shown if CURL_SCHANNEL_DEV_DEBUG was defined at build time. These
@@ -70,19 +73,6 @@
#define SCH_DEV(x) do { } while(0)
#endif
-/* ALPN requires version 8.1 of the Windows SDK, which was
- shipped with Visual Studio 2013, aka _MSC_VER 1800:
-
- https://technet.microsoft.com/en-us/library/hh831771%28v=ws.11%29.aspx
-*/
-#if defined(_MSC_VER) && (_MSC_VER >= 1800) && !defined(_USING_V110_SDK71_)
-# define HAS_ALPN 1
-#endif
-
-#ifndef BCRYPT_CHACHA20_POLY1305_ALGORITHM
-#define BCRYPT_CHACHA20_POLY1305_ALGORITHM L"CHACHA20_POLY1305"
-#endif
-
#ifndef BCRYPT_CHAIN_MODE_CCM
#define BCRYPT_CHAIN_MODE_CCM L"ChainingModeCCM"
#endif
@@ -148,13 +138,34 @@
*/
#ifndef CALG_SHA_256
-# define CALG_SHA_256 0x0000800c
+#define CALG_SHA_256 0x0000800c
+#endif
+
+/* Work around typo in CeGCC (as of 0.59.1) w32api headers */
+#if defined(__MINGW32CE__) && \
+ !defined(ALG_CLASS_DHASH) && defined(ALG_CLASS_HASH)
+#define ALG_CLASS_DHASH ALG_CLASS_HASH
#endif
#ifndef PKCS12_NO_PERSIST_KEY
#define PKCS12_NO_PERSIST_KEY 0x00008000
#endif
+#ifndef CERT_FIND_HAS_PRIVATE_KEY
+#define CERT_FIND_HAS_PRIVATE_KEY (21 << CERT_COMPARE_SHIFT)
+#endif
+
+/* ALPN requires version 8.1 of the Windows SDK, which was
+ shipped with Visual Studio 2013, aka _MSC_VER 1800:
+ https://technet.microsoft.com/en-us/library/hh831771%28v=ws.11%29.aspx
+ Or mingw-w64 9.0 or upper.
+*/
+#if (defined(__MINGW64_VERSION_MAJOR) && __MINGW64_VERSION_MAJOR >= 9) || \
+ (defined(_MSC_VER) && (_MSC_VER >= 1800) && !defined(_USING_V110_SDK71_))
+#define HAS_ALPN_SCHANNEL
+static bool s_win_has_alpn;
+#endif
+
static CURLcode schannel_pkp_pin_peer_pubkey(struct Curl_cfilter *cf,
struct Curl_easy *data,
const char *pinnedpubkey);
@@ -231,8 +242,6 @@ schannel_set_ssl_version_min_max(DWORD *enabled_protocols,
return CURLE_OK;
}
-/* longest is 26, buffer is slightly bigger */
-#define LONGEST_ALG_ID 32
#define CIPHEROPTION(x) {#x, x}
struct algo {
@@ -349,9 +358,9 @@ static const struct algo algs[]= {
};
static int
-get_alg_id_by_name(char *name)
+get_alg_id_by_name(const char *name)
{
- char *nameEnd = strchr(name, ':');
+ const char *nameEnd = strchr(name, ':');
size_t n = nameEnd ? (size_t)(nameEnd - name) : strlen(name);
int i;
@@ -368,12 +377,13 @@ static CURLcode
set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers,
ALG_ID *algIds)
{
- char *startCur = ciphers;
+ const char *startCur = ciphers;
int algCount = 0;
while(startCur && (0 != *startCur) && (algCount < NUM_CIPHERS)) {
- long alg = strtol(startCur, 0, 0);
- if(!alg)
+ curl_off_t alg;
+ if(curlx_str_number(&startCur, &alg, INT_MAX) || !alg)
alg = get_alg_id_by_name(startCur);
+
if(alg)
algIds[algCount++] = (ALG_ID)alg;
else if(!strncmp(startCur, "USE_STRONG_CRYPTO",
@@ -451,11 +461,6 @@ get_cert_location(TCHAR *path, DWORD *store_name, TCHAR **store_path,
}
#endif
-static bool algo(const char *check, char *namep, size_t nlen)
-{
- return (strlen(check) == nlen) && !strncmp(check, namep, nlen);
-}
-
static CURLcode
schannel_acquire_credential_handle(struct Curl_cfilter *cf,
struct Curl_easy *data)
@@ -610,6 +615,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
WCHAR* pszPassword;
size_t pwd_len = 0;
int str_w_len = 0;
+ int cert_find_flags;
const char *cert_showfilename_error = blob ?
"(memory blob)" : data->set.ssl.primary.clientcert;
curlx_unicodefree(cert_path);
@@ -658,8 +664,7 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
else
pszPassword[0] = 0;
- if(curlx_verify_windows_version(6, 0, 0, PLATFORM_WINNT,
- VERSION_GREATER_THAN_EQUAL))
+ if(Curl_isVistaOrGreater)
cert_store = PFXImportCertStore(&datablob, pszPassword,
PKCS12_NO_PERSIST_KEY);
else
@@ -682,9 +687,17 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
return CURLE_SSL_CERTPROBLEM;
}
+ /* CERT_FIND_HAS_PRIVATE_KEY is only available in Windows 8 / Server
+ 2012, (NT v6.2). For earlier versions we use CURL_FIND_ANY. */
+ if(curlx_verify_windows_version(6, 2, 0, PLATFORM_WINNT,
+ VERSION_GREATER_THAN_EQUAL))
+ cert_find_flags = CERT_FIND_HAS_PRIVATE_KEY;
+ else
+ cert_find_flags = CERT_FIND_ANY;
+
client_certs[0] = CertFindCertificateInStore(
cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0,
- CERT_FIND_ANY, NULL, NULL);
+ cert_find_flags, NULL, NULL);
if(!client_certs[0]) {
failf(data, "schannel: Failed to get certificate from file %s"
@@ -781,187 +794,14 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
curlx_verify_windows_version(10, 0, 17763, PLATFORM_WINNT,
VERSION_GREATER_THAN_EQUAL)) {
- char *ciphers13 = 0;
-
- bool disable_aes_gcm_sha384 = FALSE;
- bool disable_aes_gcm_sha256 = FALSE;
- bool disable_chacha_poly = FALSE;
- bool disable_aes_ccm_8_sha256 = FALSE;
- bool disable_aes_ccm_sha256 = FALSE;
-
SCH_CREDENTIALS credentials = { 0 };
TLS_PARAMETERS tls_parameters = { 0 };
- CRYPTO_SETTINGS crypto_settings[4] = { { 0 } };
- UNICODE_STRING blocked_ccm_modes[1] = { { 0 } };
- UNICODE_STRING blocked_gcm_modes[1] = { { 0 } };
-
- int crypto_settings_idx = 0;
-
-
- /* If TLS 1.3 ciphers are explicitly listed, then
- * disable all the ciphers and re-enable which
- * ciphers the user has provided.
- */
- ciphers13 = conn_config->cipher_list13;
- if(ciphers13) {
- const int remaining_ciphers = 5;
-
- /* detect which remaining ciphers to enable
- and then disable everything else.
- */
-
- char *startCur = ciphers13;
- int algCount = 0;
- char *nameEnd;
-
- disable_aes_gcm_sha384 = TRUE;
- disable_aes_gcm_sha256 = TRUE;
- disable_chacha_poly = TRUE;
- disable_aes_ccm_8_sha256 = TRUE;
- disable_aes_ccm_sha256 = TRUE;
-
- while(startCur && (0 != *startCur) && (algCount < remaining_ciphers)) {
- size_t n;
- char *namep;
- nameEnd = strchr(startCur, ':');
- n = nameEnd ? (size_t)(nameEnd - startCur) : strlen(startCur);
- namep = startCur;
-
- if(disable_aes_gcm_sha384 &&
- algo("TLS_AES_256_GCM_SHA384", namep, n)) {
- disable_aes_gcm_sha384 = FALSE;
- }
- else if(disable_aes_gcm_sha256
- && algo("TLS_AES_128_GCM_SHA256", namep, n)) {
- disable_aes_gcm_sha256 = FALSE;
- }
- else if(disable_chacha_poly
- && algo("TLS_CHACHA20_POLY1305_SHA256", namep, n)) {
- disable_chacha_poly = FALSE;
- }
- else if(disable_aes_ccm_8_sha256
- && algo("TLS_AES_128_CCM_8_SHA256", namep, n)) {
- disable_aes_ccm_8_sha256 = FALSE;
- }
- else if(disable_aes_ccm_sha256
- && algo("TLS_AES_128_CCM_SHA256", namep, n)) {
- disable_aes_ccm_sha256 = FALSE;
- }
- else {
- failf(data, "schannel: Unknown TLS 1.3 cipher: %.*s", (int)n, namep);
- return CURLE_SSL_CIPHER;
- }
-
- startCur = nameEnd;
- if(startCur)
- startCur++;
-
- algCount++;
- }
- }
-
- if(disable_aes_gcm_sha384 && disable_aes_gcm_sha256
- && disable_chacha_poly && disable_aes_ccm_8_sha256
- && disable_aes_ccm_sha256) {
- failf(data, "schannel: All available TLS 1.3 ciphers were disabled");
- return CURLE_SSL_CIPHER;
- }
-
- /* Disable TLS_AES_128_CCM_8_SHA256 and/or TLS_AES_128_CCM_SHA256 */
- if(disable_aes_ccm_8_sha256 || disable_aes_ccm_sha256) {
- /*
- Disallow AES_CCM algorithm.
- */
- blocked_ccm_modes[0].Length = sizeof(BCRYPT_CHAIN_MODE_CCM);
- blocked_ccm_modes[0].MaximumLength = sizeof(BCRYPT_CHAIN_MODE_CCM);
- blocked_ccm_modes[0].Buffer = (PWSTR)BCRYPT_CHAIN_MODE_CCM;
-
- crypto_settings[crypto_settings_idx].eAlgorithmUsage =
- TlsParametersCngAlgUsageCipher;
- crypto_settings[crypto_settings_idx].rgstrChainingModes =
- blocked_ccm_modes;
- crypto_settings[crypto_settings_idx].cChainingModes =
- ARRAYSIZE(blocked_ccm_modes);
- crypto_settings[crypto_settings_idx].strCngAlgId.Length =
- sizeof(BCRYPT_AES_ALGORITHM);
- crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength =
- sizeof(BCRYPT_AES_ALGORITHM);
- crypto_settings[crypto_settings_idx].strCngAlgId.Buffer =
- (PWSTR)BCRYPT_AES_ALGORITHM;
-
- /* only disabling one of the CCM modes */
- if(disable_aes_ccm_8_sha256 != disable_aes_ccm_sha256) {
- if(disable_aes_ccm_8_sha256)
- crypto_settings[crypto_settings_idx].dwMinBitLength = 128;
- else /* disable_aes_ccm_sha256 */
- crypto_settings[crypto_settings_idx].dwMaxBitLength = 64;
- }
-
- crypto_settings_idx++;
- }
-
- /* Disable TLS_AES_256_GCM_SHA384 and/or TLS_AES_128_GCM_SHA256 */
- if(disable_aes_gcm_sha384 || disable_aes_gcm_sha256) {
-
- /*
- Disallow AES_GCM algorithm
- */
- blocked_gcm_modes[0].Length = sizeof(BCRYPT_CHAIN_MODE_GCM);
- blocked_gcm_modes[0].MaximumLength = sizeof(BCRYPT_CHAIN_MODE_GCM);
- blocked_gcm_modes[0].Buffer = (PWSTR)BCRYPT_CHAIN_MODE_GCM;
-
- /* if only one is disabled, then explicitly disable the
- digest cipher suite (sha384 or sha256) */
- if(disable_aes_gcm_sha384 != disable_aes_gcm_sha256) {
- crypto_settings[crypto_settings_idx].eAlgorithmUsage =
- TlsParametersCngAlgUsageDigest;
- crypto_settings[crypto_settings_idx].strCngAlgId.Length =
- sizeof(disable_aes_gcm_sha384 ?
- BCRYPT_SHA384_ALGORITHM : BCRYPT_SHA256_ALGORITHM);
- crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength =
- sizeof(disable_aes_gcm_sha384 ?
- BCRYPT_SHA384_ALGORITHM : BCRYPT_SHA256_ALGORITHM);
- crypto_settings[crypto_settings_idx].strCngAlgId.Buffer =
- (PWSTR)(disable_aes_gcm_sha384 ?
- BCRYPT_SHA384_ALGORITHM : BCRYPT_SHA256_ALGORITHM);
- }
- else { /* Disable both AES_GCM ciphers */
- crypto_settings[crypto_settings_idx].eAlgorithmUsage =
- TlsParametersCngAlgUsageCipher;
- crypto_settings[crypto_settings_idx].strCngAlgId.Length =
- sizeof(BCRYPT_AES_ALGORITHM);
- crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength =
- sizeof(BCRYPT_AES_ALGORITHM);
- crypto_settings[crypto_settings_idx].strCngAlgId.Buffer =
- (PWSTR)BCRYPT_AES_ALGORITHM;
- }
-
- crypto_settings[crypto_settings_idx].rgstrChainingModes =
- blocked_gcm_modes;
- crypto_settings[crypto_settings_idx].cChainingModes = 1;
-
- crypto_settings_idx++;
- }
-
- /*
- Disable ChaCha20-Poly1305.
- */
- if(disable_chacha_poly) {
- crypto_settings[crypto_settings_idx].eAlgorithmUsage =
- TlsParametersCngAlgUsageCipher;
- crypto_settings[crypto_settings_idx].strCngAlgId.Length =
- sizeof(BCRYPT_CHACHA20_POLY1305_ALGORITHM);
- crypto_settings[crypto_settings_idx].strCngAlgId.MaximumLength =
- sizeof(BCRYPT_CHACHA20_POLY1305_ALGORITHM);
- crypto_settings[crypto_settings_idx].strCngAlgId.Buffer =
- (PWSTR)BCRYPT_CHACHA20_POLY1305_ALGORITHM;
- crypto_settings_idx++;
- }
+ CRYPTO_SETTINGS crypto_settings[1] = { { 0 } };
tls_parameters.pDisabledCrypto = crypto_settings;
/* The number of blocked suites */
- tls_parameters.cDisabledCrypto = (DWORD)crypto_settings_idx;
+ tls_parameters.cDisabledCrypto = (DWORD)0;
credentials.pTlsParameters = &tls_parameters;
credentials.cTlsParameters = 1;
@@ -979,16 +819,16 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
#endif
sspi_status =
- Curl_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR*)UNISP_NAME,
- SECPKG_CRED_OUTBOUND, NULL,
- &credentials, NULL, NULL,
- &backend->cred->cred_handle,
- &backend->cred->time_stamp);
+ Curl_pSecFn->AcquireCredentialsHandle(NULL,
+ (TCHAR *)CURL_UNCONST(UNISP_NAME),
+ SECPKG_CRED_OUTBOUND, NULL,
+ &credentials, NULL, NULL,
+ &backend->cred->cred_handle,
+ &backend->cred->time_stamp);
}
else {
- /* Pre-Windows 10 1809 or the user set a legacy algorithm list. Although MS
- does not document it, currently Schannel will not negotiate TLS 1.3 when
- SCHANNEL_CRED is used. */
+ /* Pre-Windows 10 1809 or the user set a legacy algorithm list.
+ Schannel will not negotiate TLS 1.3 when SCHANNEL_CRED is used. */
ALG_ID algIds[NUM_CIPHERS];
char *ciphers = conn_config->cipher_list;
SCHANNEL_CRED schannel_cred = { 0 };
@@ -998,16 +838,10 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
if(ciphers) {
if((enabled_protocols & SP_PROT_TLS1_3_CLIENT)) {
- infof(data, "schannel: WARNING: This version of Schannel may "
- "negotiate a less-secure TLS version than TLS 1.3 because the "
+ infof(data, "schannel: WARNING: This version of Schannel "
+ "negotiates a less-secure TLS version than TLS 1.3 because the "
"user set an algorithm cipher list.");
}
- if(conn_config->cipher_list13) {
- failf(data, "schannel: This version of Schannel does not support "
- "setting an algorithm cipher list and TLS 1.3 cipher list at "
- "the same time");
- return CURLE_SSL_CIPHER;
- }
result = set_ssl_ciphers(&schannel_cred, ciphers, algIds);
if(CURLE_OK != result) {
failf(data, "schannel: Failed setting algorithm cipher list");
@@ -1026,11 +860,12 @@ schannel_acquire_credential_handle(struct Curl_cfilter *cf,
#endif
sspi_status =
- Curl_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR*)UNISP_NAME,
- SECPKG_CRED_OUTBOUND, NULL,
- &schannel_cred, NULL, NULL,
- &backend->cred->cred_handle,
- &backend->cred->time_stamp);
+ Curl_pSecFn->AcquireCredentialsHandle(NULL,
+ (TCHAR *)CURL_UNCONST(UNISP_NAME),
+ SECPKG_CRED_OUTBOUND, NULL,
+ &schannel_cred, NULL, NULL,
+ &backend->cred->cred_handle,
+ &backend->cred->time_stamp);
}
#ifdef HAS_CLIENT_CERT_PATH
@@ -1066,17 +901,18 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
struct ssl_connect_data *connssl = cf->ctx;
struct schannel_ssl_backend_data *backend =
(struct schannel_ssl_backend_data *)connssl->backend;
+#ifndef UNDER_CE
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+#endif
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
SecBuffer outbuf;
SecBufferDesc outbuf_desc;
SecBuffer inbuf;
SecBufferDesc inbuf_desc;
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_SCHANNEL
unsigned char alpn_buffer[128];
#endif
SECURITY_STATUS sspi_status = SEC_E_OK;
- struct Curl_schannel_cred *old_cred = NULL;
CURLcode result;
DEBUGASSERT(backend);
@@ -1092,21 +928,15 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
"connect to some servers due to lack of SNI, algorithms, etc.");
}
-#ifdef HAS_ALPN
- /* ALPN is only supported on Windows 8.1 / Server 2012 R2 and above.
- Also it does not seem to be supported for WINE, see curl bug #983. */
- backend->use_alpn = connssl->alpn &&
- !GetProcAddress(GetModuleHandle(TEXT("ntdll")),
- "wine_get_version") &&
- curlx_verify_windows_version(6, 3, 0, PLATFORM_WINNT,
- VERSION_GREATER_THAN_EQUAL);
+#ifdef HAS_ALPN_SCHANNEL
+ backend->use_alpn = connssl->alpn && s_win_has_alpn;
#else
backend->use_alpn = FALSE;
#endif
-#ifdef _WIN32_WCE
+#ifdef UNDER_CE
#ifdef HAS_MANUAL_VERIFY_API
- /* certificate validation on CE does not seem to work right; we will
+ /* certificate validation on Windows CE does not seem to work right; we will
* do it following a more manual process. */
backend->use_manual_cred_validation = TRUE;
#else
@@ -1139,9 +969,10 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
/* check for an existing reusable credential handle */
if(ssl_config->primary.cache_session) {
- Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, &connssl->peer,
- (void **)&old_cred, NULL, NULL)) {
+ struct Curl_schannel_cred *old_cred;
+ Curl_ssl_scache_lock(data);
+ old_cred = Curl_ssl_scache_get_obj(cf, data, connssl->peer.scache_key);
+ if(old_cred) {
backend->cred = old_cred;
DEBUGF(infof(data, "schannel: reusing existing credential handle"));
@@ -1151,13 +982,13 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
"schannel: incremented credential handle refcount = %d",
backend->cred->refcount));
}
- Curl_ssl_sessionid_unlock(data);
+ Curl_ssl_scache_unlock(data);
}
if(!backend->cred) {
char *snihost;
result = schannel_acquire_credential_handle(cf, data);
- if(result)
+ if(result || !backend->cred)
return result;
/* schannel_acquire_credential_handle() sets backend->cred accordingly or
it returns error otherwise. */
@@ -1175,7 +1006,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
infof(data, "schannel: using IP address, SNI is not supported by OS.");
}
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_SCHANNEL
if(backend->use_alpn) {
int cur = 0;
int list_start_index = 0;
@@ -1223,7 +1054,7 @@ schannel_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0);
InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
}
-#else /* HAS_ALPN */
+#else /* HAS_ALPN_SCHANNEL */
InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0);
InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
#endif
@@ -1686,12 +1517,11 @@ add_cert_to_certinfo(const CERT_CONTEXT *ccert_context, bool reverse_order,
return args->result == CURLE_OK;
}
-static void schannel_session_free(void *sessionid, size_t idsize)
+static void schannel_session_free(void *sessionid)
{
/* this is expected to be called under sessionid lock */
struct Curl_schannel_cred *cred = sessionid;
- (void)idsize;
if(cred) {
cred->refcount--;
if(cred->refcount == 0) {
@@ -1718,7 +1548,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
CURLcode result = CURLE_OK;
SECURITY_STATUS sspi_status = SEC_E_OK;
CERT_CONTEXT *ccert_context = NULL;
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_SCHANNEL
SecPkgContext_ApplicationProtocol alpn_result;
#endif
@@ -1747,7 +1577,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
return CURLE_SSL_CONNECT_ERROR;
}
-#ifdef HAS_ALPN
+#ifdef HAS_ALPN_SCHANNEL
if(backend->use_alpn) {
sspi_status =
Curl_pSecFn->QueryContextAttributes(&backend->ctxt->ctxt_handle,
@@ -1784,14 +1614,12 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
/* save the current session data for possible reuse */
if(ssl_config->primary.cache_session) {
- Curl_ssl_sessionid_lock(data);
+ Curl_ssl_scache_lock(data);
/* Up ref count since call takes ownership */
backend->cred->refcount++;
- result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL,
- backend->cred,
- sizeof(struct Curl_schannel_cred),
- schannel_session_free);
- Curl_ssl_sessionid_unlock(data);
+ result = Curl_ssl_scache_add_obj(cf, data, connssl->peer.scache_key,
+ backend->cred, schannel_session_free);
+ Curl_ssl_scache_unlock(data);
if(result)
return result;
}
@@ -1816,6 +1644,7 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
args.data = data;
args.idx = 0;
args.certs_count = certs_count;
+ args.result = CURLE_OK;
traverse_cert_store(ccert_context, add_cert_to_certinfo, &args);
result = args.result;
}
@@ -1829,16 +1658,12 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
return CURLE_OK;
}
-static CURLcode
-schannel_connect_common(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool nonblocking, bool *done)
+static CURLcode schannel_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done)
{
- CURLcode result;
struct ssl_connect_data *connssl = cf->ctx;
- curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
- timediff_t timeout_ms;
- int what;
+ CURLcode result;
/* check if the connection has already been established */
if(ssl_connection_complete == connssl->state) {
@@ -1846,73 +1671,19 @@ schannel_connect_common(struct Curl_cfilter *cf,
return CURLE_OK;
}
- if(ssl_connect_1 == connssl->connecting_state) {
- /* check out how much more time we are allowed */
- timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
- if(timeout_ms < 0) {
- /* no need to continue if time already is up */
- failf(data, "SSL/TLS connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
+ *done = FALSE;
+ if(ssl_connect_1 == connssl->connecting_state) {
result = schannel_connect_step1(cf, data);
if(result)
return result;
}
- while(ssl_connect_2 == connssl->connecting_state) {
-
- /* check out how much more time we are allowed */
- timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
- if(timeout_ms < 0) {
- /* no need to continue if time already is up */
- failf(data, "SSL/TLS connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
-
- /* if ssl is expecting something, check if it is available. */
- if(connssl->io_need) {
-
- curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND) ?
- sockfd : CURL_SOCKET_BAD;
- curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV) ?
- sockfd : CURL_SOCKET_BAD;
-
- what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
- nonblocking ? 0 : timeout_ms);
- if(what < 0) {
- /* fatal error */
- failf(data, "select/poll on SSL/TLS socket, errno: %d", SOCKERRNO);
- return CURLE_SSL_CONNECT_ERROR;
- }
- else if(0 == what) {
- if(nonblocking) {
- *done = FALSE;
- return CURLE_OK;
- }
- else {
- /* timeout */
- failf(data, "SSL/TLS connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
- }
- /* socket is readable or writable */
- }
-
- /* Run transaction, and return to the caller if it failed or if
- * this connection is part of a multi handle and this loop would
- * execute again. This permits the owner of a multi handle to
- * abort a connection attempt before step2 has completed while
- * ensuring that a client using select() or epoll() will always
- * have a valid fdset to wait on.
- */
+ if(ssl_connect_2 == connssl->connecting_state) {
result = schannel_connect_step2(cf, data);
- if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state)))
+ if(result)
return result;
-
- } /* repeat step2 until all transactions are done. */
+ }
if(ssl_connect_3 == connssl->connecting_state) {
result = schannel_connect_step3(cf, data);
@@ -1939,11 +1710,6 @@ schannel_connect_common(struct Curl_cfilter *cf,
*done = TRUE;
}
- else
- *done = FALSE;
-
- /* reset our connection state machine */
- connssl->connecting_state = ssl_connect_1;
return CURLE_OK;
}
@@ -2189,7 +1955,6 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
backend->encdata_offset),
size, err);
if(*err) {
- nread = -1;
if(*err == CURLE_AGAIN)
SCH_DEV(infof(data, "schannel: recv returned CURLE_AGAIN"));
else if(*err == CURLE_RECV_ERROR)
@@ -2316,7 +2081,7 @@ schannel_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
connssl->connecting_state = ssl_connect_2;
connssl->io_need = CURL_SSL_IO_NEED_SEND;
backend->recv_renegotiating = TRUE;
- *err = schannel_connect_common(cf, data, FALSE, &done);
+ *err = schannel_connect(cf, data, &done);
backend->recv_renegotiating = FALSE;
if(*err) {
infof(data, "schannel: renegotiation failed");
@@ -2427,28 +2192,6 @@ cleanup:
return *err ? -1 : 0;
}
-static CURLcode schannel_connect_nonblocking(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool *done)
-{
- return schannel_connect_common(cf, data, TRUE, done);
-}
-
-static CURLcode schannel_connect(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- CURLcode result;
- bool done = FALSE;
-
- result = schannel_connect_common(cf, data, FALSE, &done);
- if(result)
- return result;
-
- DEBUGASSERT(done);
-
- return CURLE_OK;
-}
-
static bool schannel_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data)
{
@@ -2460,11 +2203,11 @@ static bool schannel_data_pending(struct Curl_cfilter *cf,
DEBUGASSERT(backend);
if(backend->ctxt) /* SSL/TLS is in use */
- return (backend->decdata_offset > 0 ||
- (backend->encdata_offset > 0 && !backend->encdata_is_incomplete) ||
- backend->recv_connection_closed ||
- backend->recv_sspi_close_notify ||
- backend->recv_unrecoverable_err);
+ return backend->decdata_offset > 0 ||
+ (backend->encdata_offset > 0 && !backend->encdata_is_incomplete) ||
+ backend->recv_connection_closed ||
+ backend->recv_sspi_close_notify ||
+ backend->recv_unrecoverable_err;
else
return FALSE;
}
@@ -2554,7 +2297,6 @@ static CURLcode schannel_shutdown(struct Curl_cfilter *cf,
Curl_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
if(!result) {
if(written < (ssize_t)outbuf.cbBuffer) {
- /* TODO: handle partial sends */
failf(data, "schannel: failed to send close msg: %s"
" (bytes written: %zd)", curl_easy_strerror(result), written);
result = CURLE_SEND_ERROR;
@@ -2630,9 +2372,9 @@ static void schannel_close(struct Curl_cfilter *cf, struct Curl_easy *data)
/* free SSPI Schannel API credential handle */
if(backend->cred) {
- Curl_ssl_sessionid_lock(data);
- schannel_session_free(backend->cred, 0);
- Curl_ssl_sessionid_unlock(data);
+ Curl_ssl_scache_lock(data);
+ schannel_session_free(backend->cred);
+ Curl_ssl_scache_unlock(data);
backend->cred = NULL;
}
@@ -2654,7 +2396,35 @@ static void schannel_close(struct Curl_cfilter *cf, struct Curl_easy *data)
static int schannel_init(void)
{
- return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0);
+#if defined(HAS_ALPN_SCHANNEL) && !defined(UNDER_CE)
+ bool wine = FALSE;
+ bool wine_has_alpn = FALSE;
+
+#ifndef CURL_WINDOWS_UWP
+ typedef const char *(APIENTRY *WINE_GET_VERSION_FN)(void);
+ /* GetModuleHandle() not available for UWP.
+ Assume no WINE because WINE has no UWP support. */
+ WINE_GET_VERSION_FN p_wine_get_version =
+ CURLX_FUNCTION_CAST(WINE_GET_VERSION_FN,
+ (GetProcAddress(GetModuleHandleA("ntdll"),
+ "wine_get_version")));
+ wine = !!p_wine_get_version;
+ if(wine) {
+ const char *wine_version = p_wine_get_version(); /* e.g. "6.0.2" */
+ /* Assume ALPN support with WINE 6.0 or upper */
+ wine_has_alpn = wine_version && atoi(wine_version) >= 6;
+ }
+#endif
+ if(wine)
+ s_win_has_alpn = wine_has_alpn;
+ else {
+ /* ALPN is supported on Windows 8.1 / Server 2012 R2 and above. */
+ s_win_has_alpn = curlx_verify_windows_version(6, 3, 0, PLATFORM_WINNT,
+ VERSION_GREATER_THAN_EQUAL);
+ }
+#endif /* HAS_ALPN_SCHANNEL && !UNDER_CE */
+
+ return Curl_sspi_global_init() == CURLE_OK ? 1 : 0;
}
static void schannel_cleanup(void)
@@ -2777,7 +2547,12 @@ static void schannel_checksum(const unsigned char *input,
if(!CryptCreateHash(hProv, algId, 0, 0, &hHash))
break; /* failed */
+#ifdef __MINGW32CE__
+ /* workaround for CeGCC, should be (const BYTE*) */
+ if(!CryptHashData(hHash, (BYTE*)CURL_UNCONST(input), (DWORD)inputlen, 0))
+#else
if(!CryptHashData(hHash, input, (DWORD)inputlen, 0))
+#endif
break; /* failed */
/* get hash size */
@@ -2841,7 +2616,7 @@ HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf,
}
share = Curl_hash_pick(&multi->proto_hash,
- (void *)MPROTO_SCHANNEL_CERT_SHARE_KEY,
+ CURL_UNCONST(MPROTO_SCHANNEL_CERT_SHARE_KEY),
sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY)-1);
if(!share || !share->cert_store) {
return NULL;
@@ -2857,8 +2632,8 @@ HCERTSTORE Curl_schannel_get_cached_cert_store(struct Curl_cfilter *cf,
negative timeout means retain forever. */
timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000;
if(timeout_ms >= 0) {
- now = Curl_now();
- elapsed_ms = Curl_timediff(now, share->time);
+ now = curlx_now();
+ elapsed_ms = curlx_timediff(now, share->time);
if(elapsed_ms >= timeout_ms) {
return NULL;
}
@@ -2919,7 +2694,7 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf,
}
share = Curl_hash_pick(&multi->proto_hash,
- (void *)MPROTO_SCHANNEL_CERT_SHARE_KEY,
+ CURL_UNCONST(MPROTO_SCHANNEL_CERT_SHARE_KEY),
sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY)-1);
if(!share) {
share = calloc(1, sizeof(*share));
@@ -2927,7 +2702,7 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf,
return FALSE;
}
if(!Curl_hash_add2(&multi->proto_hash,
- (void *)MPROTO_SCHANNEL_CERT_SHARE_KEY,
+ CURL_UNCONST(MPROTO_SCHANNEL_CERT_SHARE_KEY),
sizeof(MPROTO_SCHANNEL_CERT_SHARE_KEY)-1,
share, schannel_cert_share_free)) {
free(share);
@@ -2957,7 +2732,7 @@ bool Curl_schannel_set_cached_cert_store(struct Curl_cfilter *cf,
}
free(share->CAfile);
- share->time = Curl_now();
+ share->time = curlx_now();
share->cert_store = cert_store;
share->CAinfo_blob_size = CAinfo_blob_size;
share->CAfile = CAfile;
@@ -2974,7 +2749,6 @@ const struct Curl_ssl Curl_ssl_schannel = {
#ifndef CURL_WINDOWS_UWP
SSLSUPP_PINNEDPUBKEY |
#endif
- SSLSUPP_TLS13_CIPHERSUITES |
SSLSUPP_CA_CACHE |
SSLSUPP_HTTPS_PROXY |
SSLSUPP_CIPHER_LIST,
@@ -2984,24 +2758,20 @@ const struct Curl_ssl Curl_ssl_schannel = {
schannel_init, /* init */
schannel_cleanup, /* cleanup */
schannel_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
schannel_shutdown, /* shutdown */
schannel_data_pending, /* data_pending */
schannel_random, /* random */
- Curl_none_cert_status_request, /* cert_status_request */
+ NULL, /* cert_status_request */
schannel_connect, /* connect */
- schannel_connect_nonblocking, /* connect_nonblocking */
Curl_ssl_adjust_pollset, /* adjust_pollset */
schannel_get_internals, /* get_internals */
schannel_close, /* close_one */
- Curl_none_close_all, /* close_all */
- Curl_none_set_engine, /* set_engine */
- Curl_none_set_engine_default, /* set_engine_default */
- Curl_none_engines_list, /* engines_list */
- Curl_none_false_start, /* false_start */
+ NULL, /* close_all */
+ NULL, /* set_engine */
+ NULL, /* set_engine_default */
+ NULL, /* engines_list */
+ NULL, /* false_start */
schannel_sha256sum, /* sha256sum */
- NULL, /* associate_connection */
- NULL, /* disassociate_connection */
schannel_recv, /* recv decrypted data */
schannel_send, /* send data to encrypt */
NULL, /* get_channel_binding */
diff --git a/libs/libcurl/src/vtls/schannel.h b/libs/libcurl/src/vtls/schannel.h
index 438012d434..a0fb515aa7 100644
--- a/libs/libcurl/src/vtls/schannel.h
+++ b/libs/libcurl/src/vtls/schannel.h
@@ -24,13 +24,13 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_SCHANNEL
#ifdef _MSC_VER
#pragma warning(push)
-#pragma warning(disable: 4201)
+#pragma warning(disable:4201)
#endif
#include <subauth.h>
#ifdef _MSC_VER
@@ -50,10 +50,10 @@
#include <schnlsp.h>
#include <schannel.h>
-#include "curl_sspi.h"
+#include "../curl_sspi.h"
-#include "cfilters.h"
-#include "urldata.h"
+#include "../cfilters.h"
+#include "../urldata.h"
/* <wincrypt.h> has been included via the above <schnlsp.h>.
* Or in case of ldap.c, it was included via <winldap.h>.
diff --git a/libs/libcurl/src/vtls/schannel_int.h b/libs/libcurl/src/vtls/schannel_int.h
index 52d6fd5214..410a4808a2 100644
--- a/libs/libcurl/src/vtls/schannel_int.h
+++ b/libs/libcurl/src/vtls/schannel_int.h
@@ -24,7 +24,7 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_SCHANNEL
@@ -150,17 +150,17 @@ struct schannel_ssl_backend_data {
cannot be decrypted without another recv() (that is, status is
SEC_E_INCOMPLETE_MESSAGE) then set this true. after an recv() adds
more bytes into encdata then set this back to false. */
- bool encdata_is_incomplete;
unsigned long req_flags, ret_flags;
CURLcode recv_unrecoverable_err; /* schannel_recv had an unrecoverable err */
- bool recv_sspi_close_notify; /* true if connection closed by close_notify */
- bool recv_connection_closed; /* true if connection closed, regardless how */
- bool recv_renegotiating; /* true if recv is doing renegotiation */
- bool use_alpn; /* true if ALPN is used for this connection */
+ BIT(recv_sspi_close_notify); /* true if connection closed by close_notify */
+ BIT(recv_connection_closed); /* true if connection closed, regardless how */
+ BIT(recv_renegotiating); /* true if recv is doing renegotiation */
+ BIT(use_alpn); /* true if ALPN is used for this connection */
#ifdef HAS_MANUAL_VERIFY_API
- bool use_manual_cred_validation; /* true if manual cred validation is used */
+ BIT(use_manual_cred_validation); /* true if manual cred validation is used */
#endif
BIT(sent_shutdown);
+ BIT(encdata_is_incomplete);
};
/* key to use at `multi->proto_hash` */
diff --git a/libs/libcurl/src/vtls/schannel_verify.c b/libs/libcurl/src/vtls/schannel_verify.c
index ee960ed2c5..b4d7241790 100644
--- a/libs/libcurl/src/vtls/schannel_verify.c
+++ b/libs/libcurl/src/vtls/schannel_verify.c
@@ -29,7 +29,7 @@
* only be invoked by code in schannel.c.
*/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_SCHANNEL
#ifndef USE_WINDOWS_SSPI
@@ -39,24 +39,41 @@
#include "schannel.h"
#include "schannel_int.h"
-#include "inet_pton.h"
+#include "../curlx/inet_pton.h"
#include "vtls.h"
#include "vtls_int.h"
-#include "sendf.h"
-#include "strerror.h"
-#include "curl_multibyte.h"
-#include "curl_printf.h"
+#include "../sendf.h"
+#include "../strerror.h"
+#include "../curlx/winapi.h"
+#include "../curlx/multibyte.h"
+#include "../curl_printf.h"
#include "hostcheck.h"
-#include "version_win32.h"
+#include "../curlx/version_win32.h"
/* The last #include file should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
#define BACKEND ((struct schannel_ssl_backend_data *)connssl->backend)
#ifdef HAS_MANUAL_VERIFY_API
+#ifdef __MINGW32CE__
+#define CERT_QUERY_OBJECT_BLOB 0x00000002
+#define CERT_QUERY_CONTENT_CERT 1
+#define CERT_QUERY_CONTENT_FLAG_CERT (1 << CERT_QUERY_CONTENT_CERT)
+#define CERT_QUERY_FORMAT_BINARY 1
+#define CERT_QUERY_FORMAT_BASE64_ENCODED 2
+#define CERT_QUERY_FORMAT_ASN_ASCII_HEX_ENCODED 3
+#define CERT_QUERY_FORMAT_FLAG_ALL \
+ (1 << CERT_QUERY_FORMAT_BINARY) | \
+ (1 << CERT_QUERY_FORMAT_BASE64_ENCODED) | \
+ (1 << CERT_QUERY_FORMAT_ASN_ASCII_HEX_ENCODED)
+#define CERT_CHAIN_REVOCATION_CHECK_CHAIN 0x20000000
+#define CERT_NAME_DISABLE_IE4_UTF8_FLAG 0x00010000
+#define CERT_TRUST_IS_OFFLINE_REVOCATION 0x01000000
+#endif /* __MINGW32CE__ */
+
#define MAX_CAFILE_SIZE 1048576 /* 1 MiB */
#define BEGIN_CERT "-----BEGIN CERTIFICATE-----"
#define END_CERT "\n-----END CERTIFICATE-----"
@@ -76,6 +93,7 @@ struct cert_chain_engine_config_win7 {
HCERTSTORE hExclusiveTrustedPeople;
};
+#ifndef UNDER_CE
static int is_cr_or_lf(char c)
{
return c == '\r' || c == '\n';
@@ -138,13 +156,13 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store,
}
else {
CERT_BLOB cert_blob;
- CERT_CONTEXT *cert_context = NULL;
+ const CERT_CONTEXT *cert_context = NULL;
BOOL add_cert_result = FALSE;
DWORD actual_content_type = 0;
DWORD cert_size = (DWORD)
((end_cert_ptr + end_cert_len) - begin_cert_ptr);
- cert_blob.pbData = (BYTE *)begin_cert_ptr;
+ cert_blob.pbData = (BYTE *)CURL_UNCONST(begin_cert_ptr);
cert_blob.cbData = cert_size;
if(!CryptQueryObject(CERT_QUERY_OBJECT_BLOB,
&cert_blob,
@@ -157,12 +175,12 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store,
NULL,
NULL,
(const void **)&cert_context)) {
- char buffer[STRERROR_LEN];
+ char buffer[WINAPI_ERROR_LEN];
failf(data,
"schannel: failed to extract certificate from CA file "
"'%s': %s",
ca_file_text,
- Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+ curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
result = CURLE_SSL_CACERT_BADFILE;
more_certs = 0;
}
@@ -186,13 +204,13 @@ static CURLcode add_certs_data_to_store(HCERTSTORE trust_store,
NULL);
CertFreeCertificateContext(cert_context);
if(!add_cert_result) {
- char buffer[STRERROR_LEN];
+ char buffer[WINAPI_ERROR_LEN];
failf(data,
"schannel: failed to add certificate from CA file '%s' "
"to certificate store: %s",
ca_file_text,
- Curl_winapi_strerror(GetLastError(), buffer,
- sizeof(buffer)));
+ curlx_winapi_strerror(GetLastError(), buffer,
+ sizeof(buffer)));
result = CURLE_SSL_CACERT_BADFILE;
more_certs = 0;
}
@@ -232,13 +250,13 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store,
size_t ca_file_bufsize = 0;
DWORD total_bytes_read = 0;
- ca_file_tstr = curlx_convert_UTF8_to_tchar((char *)ca_file);
+ ca_file_tstr = curlx_convert_UTF8_to_tchar(ca_file);
if(!ca_file_tstr) {
- char buffer[STRERROR_LEN];
+ char buffer[WINAPI_ERROR_LEN];
failf(data,
"schannel: invalid path name for CA file '%s': %s",
ca_file,
- Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+ curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
result = CURLE_SSL_CACERT_BADFILE;
goto cleanup;
}
@@ -256,21 +274,21 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store,
FILE_ATTRIBUTE_NORMAL,
NULL);
if(ca_file_handle == INVALID_HANDLE_VALUE) {
- char buffer[STRERROR_LEN];
+ char buffer[WINAPI_ERROR_LEN];
failf(data,
"schannel: failed to open CA file '%s': %s",
ca_file,
- Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+ curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
result = CURLE_SSL_CACERT_BADFILE;
goto cleanup;
}
if(!GetFileSizeEx(ca_file_handle, &file_size)) {
- char buffer[STRERROR_LEN];
+ char buffer[WINAPI_ERROR_LEN];
failf(data,
"schannel: failed to determine size of CA file '%s': %s",
ca_file,
- Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+ curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
result = CURLE_SSL_CACERT_BADFILE;
goto cleanup;
}
@@ -296,11 +314,11 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store,
if(!ReadFile(ca_file_handle, ca_file_buffer + total_bytes_read,
bytes_to_read, &bytes_read, NULL)) {
- char buffer[STRERROR_LEN];
+ char buffer[WINAPI_ERROR_LEN];
failf(data,
"schannel: failed to read from CA file '%s': %s",
ca_file,
- Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+ curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
result = CURLE_SSL_CACERT_BADFILE;
goto cleanup;
}
@@ -313,7 +331,7 @@ static CURLcode add_certs_file_to_store(HCERTSTORE trust_store,
}
}
- /* Null terminate the buffer */
+ /* null-terminate the buffer */
ca_file_buffer[ca_file_bufsize] = '\0';
result = add_certs_data_to_store(trust_store,
@@ -330,9 +348,11 @@ cleanup:
return result;
}
+#endif
#endif /* HAS_MANUAL_VERIFY_API */
+#ifndef UNDER_CE
/*
* Returns the number of characters necessary to populate all the host_names.
* If host_names is not NULL, populate it with all the hostnames. Each string
@@ -380,6 +400,9 @@ static DWORD cert_get_name_string(struct Curl_easy *data,
(void)Win8_compat;
#endif
+ if(!alt_name_info)
+ return 0;
+
compute_content = host_names != NULL && length != 0;
/* Initialize default return values. */
@@ -442,14 +465,14 @@ static bool get_num_host_info(struct num_ip_data *ip_blob,
struct in6_addr ia6;
bool result = FALSE;
- int res = Curl_inet_pton(AF_INET, hostname, &ia);
+ int res = curlx_inet_pton(AF_INET, hostname, &ia);
if(res) {
ip_blob->size = sizeof(struct in_addr);
memcpy(&ip_blob->bData.ia, &ia, sizeof(struct in_addr));
result = TRUE;
}
else {
- res = Curl_inet_pton(AF_INET6, hostname, &ia6);
+ res = curlx_inet_pton(AF_INET6, hostname, &ia6);
if(res) {
ip_blob->size = sizeof(struct in6_addr);
memcpy(&ip_blob->bData.ia6, &ia6, sizeof(struct in6_addr));
@@ -511,15 +534,68 @@ static bool get_alt_name_info(struct Curl_easy *data,
#endif
return result;
}
+#endif /* !UNDER_CE */
/* Verify the server's hostname */
CURLcode Curl_verify_host(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
- struct ssl_connect_data *connssl = cf->ctx;
- SECURITY_STATUS sspi_status;
CURLcode result = CURLE_PEER_FAILED_VERIFICATION;
+ struct ssl_connect_data *connssl = cf->ctx;
CERT_CONTEXT *pCertContextServer = NULL;
+#ifdef UNDER_CE
+ TCHAR cert_hostname_buff[256];
+ DWORD len;
+
+ /* This code does not support certificates with multiple alternative names.
+ * Right now we are only asking for the first preferred alternative name.
+ * Instead we would need to do all via CERT_NAME_SEARCH_ALL_NAMES_FLAG
+ * (If Windows CE supports that?) and run this section in a loop for each.
+ * https://msdn.microsoft.com/en-us/library/windows/desktop/aa376086.aspx
+ * curl: (51) schannel: CertGetNameString() certificate hostname
+ * (.google.com) did not match connection (google.com)
+ */
+ len = CertGetNameString(pCertContextServer,
+ CERT_NAME_DNS_TYPE,
+ CERT_NAME_DISABLE_IE4_UTF8_FLAG,
+ NULL,
+ cert_hostname_buff,
+ 256);
+ if(len > 0) {
+ /* Comparing the cert name and the connection hostname encoded as UTF-8
+ * is acceptable since both values are assumed to use ASCII
+ * (or some equivalent) encoding
+ */
+ char *cert_hostname = curlx_convert_tchar_to_UTF8(cert_hostname_buff);
+ if(!cert_hostname) {
+ result = CURLE_OUT_OF_MEMORY;
+ }
+ else{
+ const char *conn_hostname = connssl->peer.hostname;
+ if(Curl_cert_hostcheck(cert_hostname, strlen(cert_hostname),
+ conn_hostname, strlen(conn_hostname))) {
+ infof(data,
+ "schannel: connection hostname (%s) validated "
+ "against certificate name (%s)\n",
+ conn_hostname, cert_hostname);
+ result = CURLE_OK;
+ }
+ else{
+ failf(data,
+ "schannel: connection hostname (%s) "
+ "does not match certificate name (%s)",
+ conn_hostname, cert_hostname);
+ }
+ Curl_safefree(cert_hostname);
+ }
+ }
+ else {
+ failf(data,
+ "schannel: CertGetNameString did not provide any "
+ "certificate name information");
+ }
+#else
+ SECURITY_STATUS sspi_status;
TCHAR *cert_hostname_buff = NULL;
size_t cert_hostname_buff_index = 0;
const char *conn_hostname = connssl->peer.hostname;
@@ -539,7 +615,7 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf,
&pCertContextServer);
if((sspi_status != SEC_E_OK) || !pCertContextServer) {
- char buffer[STRERROR_LEN];
+ char buffer[WINAPI_ERROR_LEN];
failf(data, "schannel: Failed to read remote certificate context: %s",
Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
goto cleanup;
@@ -554,7 +630,7 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf,
}
}
- if(p->size) {
+ if(p->size && alt_name_info) {
for(i = 0; i < alt_name_info->cAltEntry; ++i) {
PCERT_ALT_NAME_ENTRY entry = &alt_name_info->rgAltEntry[i];
if(entry->dwAltNameChoice == CERT_ALT_NAME_IP_ADDRESS) {
@@ -571,7 +647,6 @@ CURLcode Curl_verify_host(struct Curl_cfilter *cf,
}
}
}
-
else {
/* Determine the size of the string needed for the cert hostname */
len = cert_get_name_string(data, pCertContextServer,
@@ -665,6 +740,7 @@ cleanup:
if(pCertContextServer)
CertFreeCertificateContext(pCertContextServer);
+#endif /* !UNDER_CE */
return result;
}
@@ -682,23 +758,26 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
CERT_CONTEXT *pCertContextServer = NULL;
const CERT_CHAIN_CONTEXT *pChainContext = NULL;
HCERTCHAINENGINE cert_chain_engine = NULL;
+#ifndef UNDER_CE
HCERTSTORE trust_store = NULL;
HCERTSTORE own_trust_store = NULL;
+#endif /* !UNDER_CE */
DEBUGASSERT(BACKEND);
sspi_status =
Curl_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle,
- SECPKG_ATTR_REMOTE_CERT_CONTEXT,
- &pCertContextServer);
+ SECPKG_ATTR_REMOTE_CERT_CONTEXT,
+ &pCertContextServer);
if((sspi_status != SEC_E_OK) || !pCertContextServer) {
- char buffer[STRERROR_LEN];
+ char buffer[WINAPI_ERROR_LEN];
failf(data, "schannel: Failed to read remote certificate context: %s",
Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer)));
result = CURLE_PEER_FAILED_VERIFICATION;
}
+#ifndef UNDER_CE
if(result == CURLE_OK &&
(conn_config->CAfile || conn_config->ca_info_blob) &&
BACKEND->use_manual_cred_validation) {
@@ -728,9 +807,9 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
CERT_STORE_CREATE_NEW_FLAG,
NULL);
if(!trust_store) {
- char buffer[STRERROR_LEN];
+ char buffer[WINAPI_ERROR_LEN];
failf(data, "schannel: failed to create certificate store: %s",
- Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+ curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
result = CURLE_SSL_CACERT_BADFILE;
}
else {
@@ -775,14 +854,15 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
CertCreateCertificateChainEngine(
(CERT_CHAIN_ENGINE_CONFIG *)&engine_config, &cert_chain_engine);
if(!create_engine_result) {
- char buffer[STRERROR_LEN];
+ char buffer[WINAPI_ERROR_LEN];
failf(data,
"schannel: failed to create certificate chain engine: %s",
- Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+ curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
result = CURLE_SSL_CACERT_BADFILE;
}
}
}
+#endif /* !UNDER_CE */
if(result == CURLE_OK) {
CERT_CHAIN_PARA ChainPara;
@@ -799,9 +879,9 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
CERT_CHAIN_REVOCATION_CHECK_CHAIN),
NULL,
&pChainContext)) {
- char buffer[STRERROR_LEN];
+ char buffer[WINAPI_ERROR_LEN];
failf(data, "schannel: CertGetCertificateChain failed: %s",
- Curl_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
+ curlx_winapi_strerror(GetLastError(), buffer, sizeof(buffer)));
pChainContext = NULL;
result = CURLE_PEER_FAILED_VERIFICATION;
}
@@ -849,6 +929,7 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
}
}
+#ifndef UNDER_CE
if(cert_chain_engine) {
CertFreeCertificateChainEngine(cert_chain_engine);
}
@@ -856,6 +937,7 @@ CURLcode Curl_verify_certificate(struct Curl_cfilter *cf,
if(own_trust_store) {
CertCloseStore(own_trust_store, 0);
}
+#endif /* !UNDER_CE */
if(pChainContext)
CertFreeCertificateChain(pChainContext);
diff --git a/libs/libcurl/src/vtls/sectransp.c b/libs/libcurl/src/vtls/sectransp.c
index bf1d44dbdf..d05dd8a3f8 100644
--- a/libs/libcurl/src/vtls/sectransp.c
+++ b/libs/libcurl/src/vtls/sectransp.c
@@ -28,17 +28,18 @@
* TLS/SSL layer. No code but vtls.c should ever call or use these functions.
*/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_SECTRANSP
-#include "urldata.h" /* for the Curl_easy definition */
-#include "curl_base64.h"
-#include "strtok.h"
-#include "multiif.h"
-#include "strcase.h"
+#include "../urldata.h" /* for the Curl_easy definition */
+#include "../curlx/base64.h"
+#include "../curlx/strparse.h"
+#include "../multiif.h"
+#include "../strcase.h"
#include "x509asn1.h"
-#include "strerror.h"
+#include "vtls_scache.h"
+#include "../strerror.h"
#include "cipher_suite.h"
#ifdef __clang__
@@ -51,6 +52,11 @@
#pragma GCC diagnostic ignored "-Waddress"
#endif
+#if defined(__GNUC__) && defined(__APPLE__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#endif
+
#include <limits.h>
#include <Security/Security.h>
@@ -128,19 +134,19 @@
#include <sys/sysctl.h>
#endif /* CURL_BUILD_MAC */
-#include "sendf.h"
-#include "inet_pton.h"
-#include "connect.h"
-#include "select.h"
+#include "../sendf.h"
+#include "../curlx/inet_pton.h"
+#include "../connect.h"
+#include "../select.h"
#include "vtls.h"
#include "vtls_int.h"
#include "sectransp.h"
-#include "curl_printf.h"
-#include "strdup.h"
+#include "../curl_printf.h"
+#include "../strdup.h"
-#include "curl_memory.h"
+#include "../curl_memory.h"
/* The last #include file should be: */
-#include "memdebug.h"
+#include "../memdebug.h"
/* From MacTypes.h (which we cannot include because it is not present in
@@ -205,9 +211,6 @@ static const uint16_t default_ciphers[] = {
#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */
};
-#define DEFAULT_CIPHERS_LEN sizeof(default_ciphers)/sizeof(default_ciphers[0])
-
-
/* pinned public key support tests */
/* version 1 supports macOS 10.12+ and iOS 10+ */
@@ -260,7 +263,7 @@ static OSStatus sectransp_bio_cf_in_read(SSLConnectionRef connection,
void *buf,
size_t *dataLength) /* IN/OUT */
{
- struct Curl_cfilter *cf = (struct Curl_cfilter *)connection;
+ const struct Curl_cfilter *cf = (const struct Curl_cfilter *)connection;
struct ssl_connect_data *connssl = cf->ctx;
struct st_ssl_backend_data *backend =
(struct st_ssl_backend_data *)connssl->backend;
@@ -300,7 +303,7 @@ static OSStatus sectransp_bio_cf_out_write(SSLConnectionRef connection,
const void *buf,
size_t *dataLength) /* IN/OUT */
{
- struct Curl_cfilter *cf = (struct Curl_cfilter *)connection;
+ const struct Curl_cfilter *cf = (const struct Curl_cfilter *)connection;
struct ssl_connect_data *connssl = cf->ctx;
struct st_ssl_backend_data *backend =
(struct st_ssl_backend_data *)connssl->backend;
@@ -335,30 +338,28 @@ static OSStatus sectransp_bio_cf_out_write(SSLConnectionRef connection,
CF_INLINE void GetDarwinVersionNumber(int *major, int *minor)
{
int mib[2];
- char *os_version;
size_t os_version_len;
- char *os_version_major, *os_version_minor;
- char *tok_buf;
+ char buf[256];
/* Get the Darwin kernel version from the kernel using sysctl(): */
mib[0] = CTL_KERN;
mib[1] = KERN_OSRELEASE;
if(sysctl(mib, 2, NULL, &os_version_len, NULL, 0) == -1)
return;
- os_version = malloc(os_version_len*sizeof(char));
- if(!os_version)
- return;
- if(sysctl(mib, 2, os_version, &os_version_len, NULL, 0) == -1) {
- free(os_version);
- return;
+ if(os_version_len < sizeof(buf)) {
+ if(sysctl(mib, 2, buf, &os_version_len, NULL, 0) != -1) {
+ const char *os = buf;
+ curl_off_t fnum;
+ curl_off_t snum;
+ /* Parse the version: */
+ if(!curlx_str_number(&os, &fnum, INT_MAX) &&
+ !curlx_str_single(&os, '.') &&
+ !curlx_str_number(&os, &snum, INT_MAX)) {
+ *major = (int)fnum;
+ *minor = (int)snum;
+ }
+ }
}
-
- /* Parse the version: */
- os_version_major = strtok_r(os_version, ".", &tok_buf);
- os_version_minor = strtok_r(NULL, ".", &tok_buf);
- *major = atoi(os_version_major);
- *minor = atoi(os_version_minor);
- free(os_version);
}
#endif /* CURL_BUILD_MAC */
@@ -535,8 +536,8 @@ static OSStatus CopyIdentityWithLabel(char *label,
for(i = 0; i < keys_list_count; i++) {
OSStatus err = noErr;
SecCertificateRef cert = NULL;
- SecIdentityRef identity =
- (SecIdentityRef) CFArrayGetValueAtIndex(keys_list, i);
+ const void *item = CFArrayGetValueAtIndex(keys_list, i);
+ SecIdentityRef identity = (SecIdentityRef)CURL_UNCONST(item);
err = SecIdentityCopyCertificate(identity, &cert);
if(err == noErr) {
CFStringRef common_name = NULL;
@@ -665,22 +666,22 @@ static OSStatus CopyIdentityFromPKCS12File(const char *cPath,
count = CFArrayGetCount(items);
for(i = 0; i < count; i++) {
- CFTypeRef item = (CFTypeRef) CFArrayGetValueAtIndex(items, i);
- CFTypeID itemID = CFGetTypeID(item);
+ const CFTypeRef item = CFArrayGetValueAtIndex(items, i);
+ CFTypeID itemID = CFGetTypeID(item);
if(itemID == CFDictionaryGetTypeID()) {
- CFTypeRef identity = (CFTypeRef) CFDictionaryGetValue(
- (CFDictionaryRef) item,
- kSecImportItemIdentity);
+ const CFTypeRef identity = CFDictionaryGetValue(
+ (CFDictionaryRef)item,
+ kSecImportItemIdentity);
CFRetain(identity);
- *out_cert_and_key = (SecIdentityRef) identity;
+ *out_cert_and_key = (SecIdentityRef)CURL_UNCONST(identity);
break;
}
#if CURL_BUILD_MAC_10_7
else if(itemID == SecCertificateGetTypeID()) {
status = SecIdentityCreateWithCertificate(NULL,
- (SecCertificateRef) item,
- out_cert_and_key);
+ (SecCertificateRef)CURL_UNCONST(item),
+ out_cert_and_key);
break;
}
#endif
@@ -921,7 +922,7 @@ static CURLcode sectransp_set_default_ciphers(struct Curl_easy *data,
/* Intersect the ciphers supported by Secure Transport with the default
* ciphers, using the order of the former. */
for(i = 0; i < supported_len; i++) {
- for(j = 0; j < DEFAULT_CIPHERS_LEN; j++) {
+ for(j = 0; j < CURL_ARRAYSIZE(default_ciphers); j++) {
if(default_ciphers[j] == ciphers[i]) {
ciphers[count++] = ciphers[i];
break;
@@ -1015,7 +1016,7 @@ failed:
return ret;
}
-static void sectransp_session_free(void *sessionid, size_t idsize)
+static void sectransp_session_free(void *sessionid)
{
/* ST, as of iOS 5 and Mountain Lion, has no public method of deleting a
cached session ID inside the Security framework. There is a private
@@ -1023,7 +1024,6 @@ static void sectransp_session_free(void *sessionid, size_t idsize)
got your application rejected from the App Store due to the use of a
private API, so the best we can do is free up our own char array that we
created way back in sectransp_connect_step1... */
- (void)idsize;
Curl_safefree(sessionid);
}
@@ -1091,10 +1091,13 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
if(result != CURLE_OK)
return result;
-#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && \
- defined(HAVE_BUILTIN_AVAILABLE)
if(connssl->alpn) {
+#if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11
+#ifdef HAVE_BUILTIN_AVAILABLE
if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) {
+#else
+ if(&SSLSetALPNProtocols && &SSLCopyALPNProtocols) {
+#endif
struct alpn_proto_buf proto;
size_t i;
CFStringRef cstr;
@@ -1116,8 +1119,8 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
Curl_alpn_to_proto_str(&proto, connssl->alpn);
infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
}
+#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */
}
-#endif
if(ssl_config->key) {
infof(data, "WARNING: SSL: CURLOPT_SSLKEY is ignored by Secure "
@@ -1332,19 +1335,20 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
char *ssl_sessionid;
size_t ssl_sessionid_len;
- Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, &connssl->peer,
- (void **)&ssl_sessionid, &ssl_sessionid_len,
- NULL)) {
+ Curl_ssl_scache_lock(data);
+ ssl_sessionid = Curl_ssl_scache_get_obj(cf, data,
+ connssl->peer.scache_key);
+ if(ssl_sessionid) {
/* we got a session id, use it! */
- err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len);
- Curl_ssl_sessionid_unlock(data);
+ err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid,
+ strlen(ssl_sessionid));
+ Curl_ssl_scache_unlock(data);
if(err != noErr) {
failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err);
return CURLE_SSL_CONNECT_ERROR;
}
- /* Informational message */
- infof(data, "SSL reusing session ID");
+ else
+ infof(data, "SSL reusing session ID");
}
/* If there is not one, then let's make one up! This has to be done prior
to starting the handshake. */
@@ -1358,15 +1362,17 @@ static CURLcode sectransp_connect_step1(struct Curl_cfilter *cf,
err = SSLSetPeerID(backend->ssl_ctx, ssl_sessionid, ssl_sessionid_len);
if(err != noErr) {
- Curl_ssl_sessionid_unlock(data);
+ Curl_ssl_scache_unlock(data);
failf(data, "SSL: SSLSetPeerID() failed: OSStatus %d", err);
return CURLE_SSL_CONNECT_ERROR;
}
- result = Curl_ssl_set_sessionid(cf, data, &connssl->peer, NULL,
- ssl_sessionid, ssl_sessionid_len,
+ /* This is all a bit weird, as we have not handshaked yet.
+ * I hope this backend will go away soon. */
+ result = Curl_ssl_scache_add_obj(cf, data, connssl->peer.scache_key,
+ (void *)ssl_sessionid,
sectransp_session_free);
- Curl_ssl_sessionid_unlock(data);
+ Curl_ssl_scache_unlock(data);
if(result)
return result;
}
@@ -1429,7 +1435,7 @@ static long pem_to_der(const char *in, unsigned char **out, size_t *outlen)
}
b64[j] = '\0';
- err = Curl_base64_decode((const char *)b64, out, outlen);
+ err = curlx_base64_decode((const char *)b64, out, outlen);
free(b64);
if(err) {
free(*out);
@@ -1448,7 +1454,7 @@ static int read_cert(const char *file, unsigned char **out, size_t *outlen)
unsigned char buf[512];
struct dynbuf certs;
- Curl_dyn_init(&certs, MAX_CERTS_SIZE);
+ curlx_dyn_init(&certs, MAX_CERTS_SIZE);
fd = open(file, 0);
if(fd < 0)
@@ -1460,18 +1466,18 @@ static int read_cert(const char *file, unsigned char **out, size_t *outlen)
break;
if(n < 0) {
close(fd);
- Curl_dyn_free(&certs);
+ curlx_dyn_free(&certs);
return -1;
}
- if(Curl_dyn_addn(&certs, buf, n)) {
+ if(curlx_dyn_addn(&certs, buf, n)) {
close(fd);
return -1;
}
}
close(fd);
- *out = Curl_dyn_uptr(&certs);
- *outlen = Curl_dyn_len(&certs);
+ *out = curlx_dyn_uptr(&certs);
+ *outlen = curlx_dyn_len(&certs);
return 0;
}
@@ -1504,9 +1510,11 @@ static CURLcode append_cert_to_array(struct Curl_easy *data,
case CURLE_OK:
break;
case CURLE_PEER_FAILED_VERIFICATION:
+ CFRelease(cacert);
return CURLE_SSL_CACERT_BADFILE;
case CURLE_OUT_OF_MEMORY:
default:
+ CFRelease(cacert);
return result;
}
free(certp);
@@ -1687,7 +1695,8 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
const char *pinnedpubkey)
{ /* Scratch */
size_t pubkeylen, realpubkeylen, spkiHeaderLength = 24;
- unsigned char *pubkey = NULL, *realpubkey = NULL;
+ const unsigned char *pubkey = NULL;
+ unsigned char *realpubkey = NULL;
const unsigned char *spkiHeader = NULL;
CFDataRef publicKeyBits = NULL;
@@ -1737,7 +1746,7 @@ static CURLcode pkp_pin_peer_pubkey(struct Curl_easy *data,
#endif /* SECTRANSP_PINNEDPUBKEY_V2 */
pubkeylen = (size_t)CFDataGetLength(publicKeyBits);
- pubkey = (unsigned char *)CFDataGetBytePtr(publicKeyBits);
+ pubkey = (const unsigned char *)CFDataGetBytePtr(publicKeyBits);
switch(pubkeylen) {
case 526:
@@ -2083,10 +2092,13 @@ check_handshake:
break;
}
-#if (CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11) && \
- defined(HAVE_BUILTIN_AVAILABLE)
if(connssl->alpn) {
+#if CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11
+#ifdef HAVE_BUILTIN_AVAILABLE
if(__builtin_available(macOS 10.13.4, iOS 11, tvOS 11, *)) {
+#else
+ if(&SSLSetALPNProtocols && &SSLCopyALPNProtocols) {
+#endif
CFArrayRef alpnArr = NULL;
CFStringRef chosenProtocol = NULL;
err = SSLCopyALPNProtocols(backend->ssl_ctx, &alpnArr);
@@ -2113,8 +2125,8 @@ check_handshake:
if(alpnArr)
CFRelease(alpnArr);
}
+#endif /* CURL_BUILD_MAC_10_13 || CURL_BUILD_IOS_11 */
}
-#endif
return CURLE_OK;
}
@@ -2122,7 +2134,7 @@ check_handshake:
static CURLcode
add_cert_to_certinfo(struct Curl_easy *data,
- SecCertificateRef server_cert,
+ const SecCertificateRef server_cert,
int idx)
{
CURLcode result = CURLE_OK;
@@ -2142,7 +2154,7 @@ add_cert_to_certinfo(struct Curl_easy *data,
static CURLcode
collect_server_cert_single(struct Curl_cfilter *cf, struct Curl_easy *data,
- SecCertificateRef server_cert,
+ const SecCertificateRef server_cert,
CFIndex idx)
{
CURLcode result = CURLE_OK;
@@ -2239,8 +2251,8 @@ static CURLcode collect_server_cert(struct Curl_cfilter *cf,
if(ssl_config->certinfo)
result = Curl_ssl_init_certinfo(data, (int)count);
for(i = 0L ; !result && (i < count) ; i++) {
- server_cert = (SecCertificateRef)CFArrayGetValueAtIndex(server_certs,
- i);
+ const void *item = CFArrayGetValueAtIndex(server_certs, i);
+ server_cert = (SecCertificateRef)CURL_UNCONST(item);
result = collect_server_cert_single(cf, data, server_cert, i);
}
CFRelease(server_certs);
@@ -2256,7 +2268,8 @@ static CURLcode collect_server_cert(struct Curl_cfilter *cf,
if(ssl_config->certinfo)
result = Curl_ssl_init_certinfo(data, (int)count);
for(i = 0L ; !result && (i < count) ; i++) {
- server_cert = (SecCertificateRef)CFArrayGetValueAtIndex(server_certs, i);
+ const void *item = CFArrayGetValueAtIndex(server_certs, i);
+ server_cert = (SecCertificateRef)CURL_UNCONST(item);
result = collect_server_cert_single(cf, data, server_cert, i);
}
CFRelease(server_certs);
@@ -2283,15 +2296,12 @@ static CURLcode sectransp_connect_step3(struct Curl_cfilter *cf,
return CURLE_OK;
}
-static CURLcode
-sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
- bool nonblocking,
- bool *done)
+static CURLcode sectransp_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done)
{
CURLcode result;
struct ssl_connect_data *connssl = cf->ctx;
- curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
- int what;
/* check if the connection has already been established */
if(ssl_connection_complete == connssl->state) {
@@ -2299,73 +2309,20 @@ sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
return CURLE_OK;
}
- if(ssl_connect_1 == connssl->connecting_state) {
- /* Find out how much more time we are allowed */
- const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
- if(timeout_ms < 0) {
- /* no need to continue if time already is up */
- failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
+ *done = FALSE;
+ connssl->io_need = CURL_SSL_IO_NEED_NONE;
+ if(ssl_connect_1 == connssl->connecting_state) {
result = sectransp_connect_step1(cf, data);
if(result)
return result;
}
- while(ssl_connect_2 == connssl->connecting_state) {
-
- /* check allowed time left */
- const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
- if(timeout_ms < 0) {
- /* no need to continue if time already is up */
- failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
-
- /* if ssl is expecting something, check if it is available. */
- if(connssl->io_need) {
-
- curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND) ?
- sockfd : CURL_SOCKET_BAD;
- curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV) ?
- sockfd : CURL_SOCKET_BAD;
-
- what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
- nonblocking ? 0 : timeout_ms);
- if(what < 0) {
- /* fatal error */
- failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
- return CURLE_SSL_CONNECT_ERROR;
- }
- else if(0 == what) {
- if(nonblocking) {
- *done = FALSE;
- return CURLE_OK;
- }
- else {
- /* timeout */
- failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
- }
- /* socket is readable or writable */
- }
-
- /* Run transaction, and return to the caller if it failed or if this
- * connection is done nonblocking and this loop would execute again. This
- * permits the owner of a multi handle to abort a connection attempt
- * before step2 has completed while ensuring that a client using select()
- * or epoll() will always have a valid fdset to wait on.
- */
+ if(ssl_connect_2 == connssl->connecting_state) {
result = sectransp_connect_step2(cf, data);
- if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state)))
+ if(result)
return result;
-
- } /* repeat step2 until all transactions are done. */
-
+ }
if(ssl_connect_3 == connssl->connecting_state) {
result = sectransp_connect_step3(cf, data);
@@ -2378,34 +2335,6 @@ sectransp_connect_common(struct Curl_cfilter *cf, struct Curl_easy *data,
connssl->state = ssl_connection_complete;
*done = TRUE;
}
- else
- *done = FALSE;
-
- /* Reset our connect state machine */
- connssl->connecting_state = ssl_connect_1;
-
- return CURLE_OK;
-}
-
-static CURLcode sectransp_connect_nonblocking(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool *done)
-{
- return sectransp_connect_common(cf, data, TRUE, done);
-}
-
-static CURLcode sectransp_connect(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- CURLcode result;
- bool done = FALSE;
-
- result = sectransp_connect_common(cf, data, FALSE, &done);
-
- if(result)
- return result;
-
- DEBUGASSERT(done);
return CURLE_OK;
}
@@ -2424,7 +2353,7 @@ static CURLcode sectransp_shutdown(struct Curl_cfilter *cf,
struct st_ssl_backend_data *backend =
(struct st_ssl_backend_data *)connssl->backend;
CURLcode result = CURLE_OK;
- ssize_t nread;
+ ssize_t nread = 0;
char buf[1024];
size_t i;
@@ -2540,7 +2469,7 @@ static bool sectransp_data_pending(struct Curl_cfilter *cf,
DEBUGASSERT(backend);
if(backend->ssl_ctx) { /* SSL is in use */
- CURL_TRC_CF((struct Curl_easy *)data, cf, "data_pending");
+ CURL_TRC_CF((struct Curl_easy *)CURL_UNCONST(data), cf, "data_pending");
err = SSLGetBufferedReadSize(backend->ssl_ctx, &buffer);
if(err == noErr)
return buffer > 0UL;
@@ -2742,32 +2671,32 @@ const struct Curl_ssl Curl_ssl_sectransp = {
sizeof(struct st_ssl_backend_data),
- Curl_none_init, /* init */
- Curl_none_cleanup, /* cleanup */
+ NULL, /* init */
+ NULL, /* cleanup */
sectransp_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
sectransp_shutdown, /* shutdown */
sectransp_data_pending, /* data_pending */
sectransp_random, /* random */
- Curl_none_cert_status_request, /* cert_status_request */
+ NULL, /* cert_status_request */
sectransp_connect, /* connect */
- sectransp_connect_nonblocking, /* connect_nonblocking */
Curl_ssl_adjust_pollset, /* adjust_pollset */
sectransp_get_internals, /* get_internals */
sectransp_close, /* close_one */
- Curl_none_close_all, /* close_all */
- Curl_none_set_engine, /* set_engine */
- Curl_none_set_engine_default, /* set_engine_default */
- Curl_none_engines_list, /* engines_list */
+ NULL, /* close_all */
+ NULL, /* set_engine */
+ NULL, /* set_engine_default */
+ NULL, /* engines_list */
sectransp_false_start, /* false_start */
sectransp_sha256sum, /* sha256sum */
- NULL, /* associate_connection */
- NULL, /* disassociate_connection */
sectransp_recv, /* recv decrypted data */
sectransp_send, /* send data to encrypt */
NULL, /* get_channel_binding */
};
+#if defined(__GNUC__) && defined(__APPLE__)
+#pragma GCC diagnostic pop
+#endif
+
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
diff --git a/libs/libcurl/src/vtls/sectransp.h b/libs/libcurl/src/vtls/sectransp.h
index 3b79169ab3..9011f17508 100644
--- a/libs/libcurl/src/vtls/sectransp.h
+++ b/libs/libcurl/src/vtls/sectransp.h
@@ -24,7 +24,7 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_SECTRANSP
diff --git a/libs/libcurl/src/vtls/vtls.c b/libs/libcurl/src/vtls/vtls.c
index 0a90bf46fd..f707e92577 100644
--- a/libs/libcurl/src/vtls/vtls.c
+++ b/libs/libcurl/src/vtls/vtls.c
@@ -38,7 +38,7 @@
https://httpd.apache.org/docs/2.0/ssl/ssl_intro.html
*/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
@@ -50,11 +50,12 @@
#include <fcntl.h>
#endif
-#include "urldata.h"
-#include "cfilters.h"
+#include "../urldata.h"
+#include "../cfilters.h"
#include "vtls.h" /* generic SSL protos etc */
#include "vtls_int.h"
+#include "vtls_scache.h"
#include "openssl.h" /* OpenSSL versions */
#include "gtls.h" /* GnuTLS versions */
@@ -65,34 +66,30 @@
#include "bearssl.h" /* BearSSL versions */
#include "rustls.h" /* Rustls versions */
-#include "slist.h"
-#include "sendf.h"
-#include "strcase.h"
-#include "url.h"
-#include "progress.h"
-#include "share.h"
-#include "multiif.h"
-#include "timeval.h"
-#include "curl_md5.h"
-#include "warnless.h"
-#include "curl_base64.h"
-#include "curl_printf.h"
-#include "inet_pton.h"
-#include "connect.h"
-#include "select.h"
-#include "strdup.h"
-#include "rand.h"
+#include "../slist.h"
+#include "../sendf.h"
+#include "../strcase.h"
+#include "../url.h"
+#include "../progress.h"
+#include "../share.h"
+#include "../multiif.h"
+#include "../curlx/timeval.h"
+#include "../curl_md5.h"
+#include "../curl_sha256.h"
+#include "../curlx/warnless.h"
+#include "../curlx/base64.h"
+#include "../curl_printf.h"
+#include "../curlx/inet_pton.h"
+#include "../connect.h"
+#include "../select.h"
+#include "../strdup.h"
+#include "../rand.h"
/* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
-/* convenience macro to check if this handle is using a shared SSL session */
-#define SSLSESSION_SHARED(data) (data->share && \
- (data->share->specifier & \
- (1<<CURL_LOCK_DATA_SSL_SESSION)))
-
#define CLONE_STRING(var) \
do { \
if(source->var) { \
@@ -157,17 +154,19 @@ static const struct alpn_spec ALPN_SPEC_H2_H11 = {
};
#endif
-static const struct alpn_spec *alpn_get_spec(int httpwant, bool use_alpn)
+static const struct alpn_spec *
+alpn_get_spec(http_majors allowed, bool use_alpn)
{
if(!use_alpn)
return NULL;
#ifdef USE_HTTP2
- if(httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE)
+ if(allowed & CURL_HTTP_V2x) {
+ if(allowed & CURL_HTTP_V1x)
+ return &ALPN_SPEC_H2_H11;
return &ALPN_SPEC_H2;
- if(httpwant >= CURL_HTTP_VERSION_2)
- return &ALPN_SPEC_H2_H11;
+ }
#else
- (void)httpwant;
+ (void)allowed;
#endif
/* Use the ALPN protocol "http/1.1" for HTTP/1.x.
Avoid "http/1.0" because some servers do not support it. */
@@ -216,6 +215,7 @@ match_ssl_primary_config(struct Curl_easy *data,
strcasecompare(c1->cipher_list, c2->cipher_list) &&
strcasecompare(c1->cipher_list13, c2->cipher_list13) &&
strcasecompare(c1->curves, c2->curves) &&
+ strcasecompare(c1->signature_algorithms, c2->signature_algorithms) &&
strcasecompare(c1->CRLfile, c2->CRLfile) &&
strcasecompare(c1->pinned_key, c2->pinned_key))
return TRUE;
@@ -260,6 +260,7 @@ static bool clone_ssl_primary_config(struct ssl_primary_config *source,
CLONE_STRING(cipher_list13);
CLONE_STRING(pinned_key);
CLONE_STRING(curves);
+ CLONE_STRING(signature_algorithms);
CLONE_STRING(CRLfile);
#ifdef USE_TLS_SRP
CLONE_STRING(username);
@@ -282,6 +283,7 @@ static void free_primary_ssl_config(struct ssl_primary_config *sslc)
Curl_safefree(sslc->ca_info_blob);
Curl_safefree(sslc->issuercert_blob);
Curl_safefree(sslc->curves);
+ Curl_safefree(sslc->signature_algorithms);
Curl_safefree(sslc->CRLfile);
#ifdef USE_TLS_SRP
Curl_safefree(sslc->username);
@@ -300,6 +302,8 @@ CURLcode Curl_ssl_easy_config_complete(struct Curl_easy *data)
data->set.str[STRING_SSL_CIPHER_LIST];
data->set.ssl.primary.cipher_list13 =
data->set.str[STRING_SSL_CIPHER13_LIST];
+ data->set.ssl.primary.signature_algorithms =
+ data->set.str[STRING_SSL_SIGNATURE_ALGORITHMS];
data->set.ssl.primary.pinned_key =
data->set.str[STRING_SSL_PINNEDPUBLICKEY];
data->set.ssl.primary.cert_blob = data->set.blobs[BLOB_CERT];
@@ -426,7 +430,9 @@ int Curl_ssl_init(void)
return 1;
init_ssl = TRUE; /* never again */
- return Curl_ssl->init();
+ if(Curl_ssl->init)
+ return Curl_ssl->init();
+ return 1;
}
static bool ssl_prefs_check(struct Curl_easy *data)
@@ -463,9 +469,10 @@ static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data,
if(!ctx)
return NULL;
+ ctx->ssl_impl = Curl_ssl;
ctx->alpn = alpn;
Curl_bufq_init2(&ctx->earlydata, CURL_SSL_EARLY_MAX, 1, BUFQ_OPT_NO_SPARES);
- ctx->backend = calloc(1, Curl_ssl->sizeof_ssl_backend_data);
+ ctx->backend = calloc(1, ctx->ssl_impl->sizeof_ssl_backend_data);
if(!ctx->backend) {
free(ctx);
return NULL;
@@ -476,308 +483,13 @@ static struct ssl_connect_data *cf_ctx_new(struct Curl_easy *data,
static void cf_ctx_free(struct ssl_connect_data *ctx)
{
if(ctx) {
- Curl_safefree(ctx->alpn_negotiated);
+ Curl_safefree(ctx->negotiated.alpn);
Curl_bufq_free(&ctx->earlydata);
free(ctx->backend);
free(ctx);
}
}
-static CURLcode ssl_connect(struct Curl_cfilter *cf, struct Curl_easy *data)
-{
- struct ssl_connect_data *connssl = cf->ctx;
- CURLcode result;
-
- if(!ssl_prefs_check(data))
- return CURLE_SSL_CONNECT_ERROR;
-
- /* mark this is being ssl-enabled from here on. */
- connssl->state = ssl_connection_negotiating;
-
- result = Curl_ssl->connect_blocking(cf, data);
-
- if(!result) {
- DEBUGASSERT(connssl->state == ssl_connection_complete);
- }
-
- return result;
-}
-
-static CURLcode
-ssl_connect_nonblocking(struct Curl_cfilter *cf, struct Curl_easy *data,
- bool *done)
-{
- if(!ssl_prefs_check(data))
- return CURLE_SSL_CONNECT_ERROR;
-
- /* mark this is being ssl requested from here on. */
- return Curl_ssl->connect_nonblocking(cf, data, done);
-}
-
-/*
- * Lock shared SSL session data
- */
-void Curl_ssl_sessionid_lock(struct Curl_easy *data)
-{
- if(SSLSESSION_SHARED(data))
- Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
-}
-
-/*
- * Unlock shared SSL session data
- */
-void Curl_ssl_sessionid_unlock(struct Curl_easy *data)
-{
- if(SSLSESSION_SHARED(data))
- Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
-}
-
-/*
- * Check if there is a session ID for the given connection in the cache, and if
- * there is one suitable, it is provided. Returns TRUE when no entry matched.
- */
-bool Curl_ssl_getsessionid(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const struct ssl_peer *peer,
- void **ssl_sessionid,
- size_t *idsize, /* set 0 if unknown */
- char **palpn)
-{
- struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
- struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
- struct Curl_ssl_session *check;
- size_t i;
- long *general_age;
- bool no_match = TRUE;
-
- *ssl_sessionid = NULL;
- if(palpn)
- *palpn = NULL;
- if(!ssl_config)
- return TRUE;
-
- DEBUGASSERT(ssl_config->primary.cache_session);
-
- if(!ssl_config->primary.cache_session || !data->state.session)
- /* session ID reuse is disabled or the session cache has not been
- setup */
- return TRUE;
-
- /* Lock if shared */
- if(SSLSESSION_SHARED(data))
- general_age = &data->share->sessionage;
- else
- general_age = &data->state.sessionage;
-
- for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) {
- check = &data->state.session[i];
- if(!check->sessionid)
- /* not session ID means blank entry */
- continue;
- if(strcasecompare(peer->hostname, check->name) &&
- ((!cf->conn->bits.conn_to_host && !check->conn_to_host) ||
- (cf->conn->bits.conn_to_host && check->conn_to_host &&
- strcasecompare(cf->conn->conn_to_host.name, check->conn_to_host))) &&
- ((!cf->conn->bits.conn_to_port && check->conn_to_port == -1) ||
- (cf->conn->bits.conn_to_port && check->conn_to_port != -1 &&
- cf->conn->conn_to_port == check->conn_to_port)) &&
- (peer->port == check->remote_port) &&
- (peer->transport == check->transport) &&
- strcasecompare(cf->conn->handler->scheme, check->scheme) &&
- match_ssl_primary_config(data, conn_config, &check->ssl_config)) {
- /* yes, we have a session ID! */
- (*general_age)++; /* increase general age */
- check->age = *general_age; /* set this as used in this age */
- *ssl_sessionid = check->sessionid;
- if(idsize)
- *idsize = check->idsize;
- if(palpn)
- *palpn = check->alpn;
- no_match = FALSE;
- break;
- }
- }
-
- CURL_TRC_CF(data, cf, "%s cached session ID for %s://%s:%d",
- no_match ? "No" : "Found",
- cf->conn->handler->scheme, peer->hostname, peer->port);
- return no_match;
-}
-
-/*
- * Kill a single session ID entry in the cache.
- */
-void Curl_ssl_kill_session(struct Curl_ssl_session *session)
-{
- if(session->sessionid) {
- /* defensive check */
-
- /* free the ID the SSL-layer specific way */
- session->sessionid_free(session->sessionid, session->idsize);
-
- session->sessionid = NULL;
- session->sessionid_free = NULL;
- session->age = 0; /* fresh */
-
- free_primary_ssl_config(&session->ssl_config);
-
- Curl_safefree(session->name);
- Curl_safefree(session->conn_to_host);
- Curl_safefree(session->alpn);
- }
-}
-
-/*
- * Delete the given session ID from the cache.
- */
-void Curl_ssl_delsessionid(struct Curl_easy *data, void *ssl_sessionid)
-{
- size_t i;
-
- for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++) {
- struct Curl_ssl_session *check = &data->state.session[i];
-
- if(check->sessionid == ssl_sessionid) {
- Curl_ssl_kill_session(check);
- break;
- }
- }
-}
-
-CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const struct ssl_peer *peer,
- const char *alpn,
- void *ssl_sessionid,
- size_t idsize,
- Curl_ssl_sessionid_dtor *sessionid_free_cb)
-{
- struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
- struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
- size_t i;
- struct Curl_ssl_session *store;
- long oldest_age;
- char *clone_host = NULL;
- char *clone_conn_to_host = NULL;
- char *clone_alpn = NULL;
- int conn_to_port;
- long *general_age;
- void *old_sessionid;
- size_t old_size;
- CURLcode result = CURLE_OUT_OF_MEMORY;
-
- DEBUGASSERT(ssl_sessionid);
- DEBUGASSERT(sessionid_free_cb);
-
- if(!data->state.session) {
- sessionid_free_cb(ssl_sessionid, idsize);
- return CURLE_OK;
- }
-
- if(!Curl_ssl_getsessionid(cf, data, peer, &old_sessionid, &old_size, NULL)) {
- if((old_size == idsize) &&
- ((old_sessionid == ssl_sessionid) ||
- (idsize && !memcmp(old_sessionid, ssl_sessionid, idsize)))) {
- /* the very same */
- sessionid_free_cb(ssl_sessionid, idsize);
- return CURLE_OK;
- }
- Curl_ssl_delsessionid(data, old_sessionid);
- }
-
- store = &data->state.session[0];
- oldest_age = data->state.session[0].age; /* zero if unused */
- DEBUGASSERT(ssl_config->primary.cache_session);
- (void)ssl_config;
-
- clone_host = strdup(peer->hostname);
- if(!clone_host)
- goto out;
-
- if(cf->conn->bits.conn_to_host) {
- clone_conn_to_host = strdup(cf->conn->conn_to_host.name);
- if(!clone_conn_to_host)
- goto out;
- }
-
- clone_alpn = alpn ? strdup(alpn) : NULL;
- if(alpn && !clone_alpn)
- goto out;
-
- if(cf->conn->bits.conn_to_port)
- conn_to_port = cf->conn->conn_to_port;
- else
- conn_to_port = -1;
-
- /* Now we should add the session ID and the hostname to the cache, (remove
- the oldest if necessary) */
-
- /* If using shared SSL session, lock! */
- if(SSLSESSION_SHARED(data)) {
- general_age = &data->share->sessionage;
- }
- else {
- general_age = &data->state.sessionage;
- }
-
- /* find an empty slot for us, or find the oldest */
- for(i = 1; (i < data->set.general_ssl.max_ssl_sessions) &&
- data->state.session[i].sessionid; i++) {
- if(data->state.session[i].age < oldest_age) {
- oldest_age = data->state.session[i].age;
- store = &data->state.session[i];
- }
- }
- if(i == data->set.general_ssl.max_ssl_sessions)
- /* cache is full, we must "kill" the oldest entry! */
- Curl_ssl_kill_session(store);
- else
- store = &data->state.session[i]; /* use this slot */
-
- /* now init the session struct wisely */
- if(!clone_ssl_primary_config(conn_config, &store->ssl_config)) {
- free_primary_ssl_config(&store->ssl_config);
- store->sessionid = NULL; /* let caller free sessionid */
- goto out;
- }
- store->sessionid = ssl_sessionid;
- store->idsize = idsize;
- store->sessionid_free = sessionid_free_cb;
- store->age = *general_age; /* set current age */
- /* free it if there is one already present */
- free(store->name);
- free(store->conn_to_host);
- store->name = clone_host; /* clone hostname */
- clone_host = NULL;
- store->conn_to_host = clone_conn_to_host; /* clone connect to hostname */
- clone_conn_to_host = NULL;
- store->conn_to_port = conn_to_port; /* connect to port number */
- store->alpn = clone_alpn;
- clone_alpn = NULL;
- /* port number */
- store->remote_port = peer->port;
- store->scheme = cf->conn->handler->scheme;
- store->transport = peer->transport;
-
- result = CURLE_OK;
-
-out:
- free(clone_host);
- free(clone_conn_to_host);
- free(clone_alpn);
- if(result) {
- failf(data, "Failed to add Session ID to cache for %s://%s:%d [%s]",
- store->scheme, store->name, store->remote_port,
- Curl_ssl_cf_is_proxy(cf) ? "PROXY" : "server");
- sessionid_free_cb(ssl_sessionid, idsize);
- return result;
- }
- CURL_TRC_CF(data, cf, "Added Session ID to cache for %s://%s:%d [%s]",
- store->scheme, store->name, store->remote_port,
- Curl_ssl_cf_is_proxy(cf) ? "PROXY" : "server");
- return CURLE_OK;
-}
-
CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex,
struct dynbuf *binding)
{
@@ -788,18 +500,8 @@ CURLcode Curl_ssl_get_channel_binding(struct Curl_easy *data, int sockindex,
void Curl_ssl_close_all(struct Curl_easy *data)
{
- /* kill the session ID cache if not shared */
- if(data->state.session && !SSLSESSION_SHARED(data)) {
- size_t i;
- for(i = 0; i < data->set.general_ssl.max_ssl_sessions; i++)
- /* the single-killer function handles empty table slots */
- Curl_ssl_kill_session(&data->state.session[i]);
-
- /* free the cache data */
- Curl_safefree(data->state.session);
- }
-
- Curl_ssl->close_all(data);
+ if(Curl_ssl->close_all)
+ Curl_ssl->close_all(data);
}
void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data,
@@ -828,43 +530,26 @@ void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data,
*/
CURLcode Curl_ssl_set_engine(struct Curl_easy *data, const char *engine)
{
- return Curl_ssl->set_engine(data, engine);
+ if(Curl_ssl->set_engine)
+ return Curl_ssl->set_engine(data, engine);
+ return CURLE_NOT_BUILT_IN;
}
/* Selects the default SSL crypto engine
*/
CURLcode Curl_ssl_set_engine_default(struct Curl_easy *data)
{
- return Curl_ssl->set_engine_default(data);
+ if(Curl_ssl->set_engine_default)
+ return Curl_ssl->set_engine_default(data);
+ return CURLE_NOT_BUILT_IN;
}
/* Return list of OpenSSL crypto engine names. */
struct curl_slist *Curl_ssl_engines_list(struct Curl_easy *data)
{
- return Curl_ssl->engines_list(data);
-}
-
-/*
- * This sets up a session ID cache to the specified size. Make sure this code
- * is agnostic to what underlying SSL technology we use.
- */
-CURLcode Curl_ssl_initsessions(struct Curl_easy *data, size_t amount)
-{
- struct Curl_ssl_session *session;
-
- if(data->state.session)
- /* this is just a precaution to prevent multiple inits */
- return CURLE_OK;
-
- session = calloc(amount, sizeof(struct Curl_ssl_session));
- if(!session)
- return CURLE_OUT_OF_MEMORY;
-
- /* store the info in the SSL section */
- data->set.general_ssl.max_ssl_sessions = amount;
- data->state.session = session;
- data->state.sessionage = 1; /* this is brand new */
- return CURLE_OK;
+ if(Curl_ssl->engines_list)
+ return Curl_ssl->engines_list(data);
+ return NULL;
}
static size_t multissl_version(char *buffer, size_t size);
@@ -931,17 +616,17 @@ CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data,
DEBUGASSERT(certnum < ci->num_of_certs);
- Curl_dyn_init(&build, CURL_X509_STR_MAX);
+ curlx_dyn_init(&build, CURL_X509_STR_MAX);
- if(Curl_dyn_add(&build, label) ||
- Curl_dyn_addn(&build, ":", 1) ||
- Curl_dyn_addn(&build, value, valuelen))
+ if(curlx_dyn_add(&build, label) ||
+ curlx_dyn_addn(&build, ":", 1) ||
+ curlx_dyn_addn(&build, value, valuelen))
return CURLE_OUT_OF_MEMORY;
nl = Curl_slist_append_nodup(ci->certinfo[certnum],
- Curl_dyn_ptr(&build));
+ curlx_dyn_ptr(&build));
if(!nl) {
- Curl_dyn_free(&build);
+ curlx_dyn_free(&build);
curl_slist_free_all(ci->certinfo[certnum]);
result = CURLE_OUT_OF_MEMORY;
}
@@ -950,7 +635,7 @@ CURLcode Curl_ssl_push_certinfo_len(struct Curl_easy *data,
return result;
}
-/* get 32 bits of random */
+/* get length bytes of randomness */
CURLcode Curl_ssl_random(struct Curl_easy *data,
unsigned char *entropy,
size_t length)
@@ -978,7 +663,7 @@ static CURLcode pubkey_pem_to_der(const char *pem,
if(!pem)
return CURLE_BAD_CONTENT_ENCODING;
- Curl_dyn_init(&pbuf, MAX_PINNED_PUBKEY_SIZE);
+ curlx_dyn_init(&pbuf, MAX_PINNED_PUBKEY_SIZE);
begin_pos = strstr(pem, "-----BEGIN PUBLIC KEY-----");
if(!begin_pos)
@@ -1006,16 +691,19 @@ static CURLcode pubkey_pem_to_der(const char *pem,
*/
while(pem_count < pem_len) {
if('\n' != pem[pem_count] && '\r' != pem[pem_count]) {
- result = Curl_dyn_addn(&pbuf, &pem[pem_count], 1);
+ result = curlx_dyn_addn(&pbuf, &pem[pem_count], 1);
if(result)
return result;
}
++pem_count;
}
- result = Curl_base64_decode(Curl_dyn_ptr(&pbuf), der, der_len);
-
- Curl_dyn_free(&pbuf);
+ if(curlx_dyn_len(&pbuf)) {
+ result = curlx_base64_decode(curlx_dyn_ptr(&pbuf), der, der_len);
+ curlx_dyn_free(&pbuf);
+ }
+ else
+ result = CURLE_BAD_CONTENT_ENCODING;
return result;
}
@@ -1059,9 +747,9 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
sha256sumdigest, CURL_SHA256_DIGEST_LENGTH);
if(!encode)
- encode = Curl_base64_encode((char *)sha256sumdigest,
- CURL_SHA256_DIGEST_LENGTH, &encoded,
- &encodedlen);
+ encode = curlx_base64_encode((char *)sha256sumdigest,
+ CURL_SHA256_DIGEST_LENGTH, &encoded,
+ &encodedlen);
Curl_safefree(sha256sumdigest);
if(encode)
@@ -1080,8 +768,8 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
do {
end_pos = strstr(begin_pos, ";sha256//");
/*
- * if there is an end_pos, null terminate,
- * otherwise it will go to the end of the original string
+ * if there is an end_pos, null-terminate, otherwise it will go to the
+ * end of the original string
*/
if(end_pos)
end_pos[0] = '\0';
@@ -1116,7 +804,7 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
if(!fp)
return result;
- Curl_dyn_init(&buf, MAX_PINNED_PUBKEY_SIZE);
+ curlx_dyn_init(&buf, MAX_PINNED_PUBKEY_SIZE);
/* Determine the file's size */
if(fseek(fp, 0, SEEK_END))
@@ -1144,23 +832,23 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
size_t want = left > sizeof(buffer) ? sizeof(buffer) : left;
if(want != fread(buffer, 1, want, fp))
goto end;
- if(Curl_dyn_addn(&buf, buffer, want))
+ if(curlx_dyn_addn(&buf, buffer, want))
goto end;
left -= want;
} while(left);
/* If the sizes are the same, it cannot be base64 encoded, must be der */
if(pubkeylen == size) {
- if(!memcmp(pubkey, Curl_dyn_ptr(&buf), pubkeylen))
+ if(!memcmp(pubkey, curlx_dyn_ptr(&buf), pubkeylen))
result = CURLE_OK;
goto end;
}
/*
- * Otherwise we will assume it is PEM and try to decode it
- * after placing null terminator
+ * Otherwise we will assume it is PEM and try to decode it after placing
+ * null-terminator
*/
- pem_read = pubkey_pem_to_der(Curl_dyn_ptr(&buf), &pem_ptr, &pem_len);
+ pem_read = pubkey_pem_to_der(curlx_dyn_ptr(&buf), &pem_ptr, &pem_len);
/* if it was not read successfully, exit */
if(pem_read)
goto end;
@@ -1172,7 +860,7 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
if(pubkeylen == pem_len && !memcmp(pubkey, pem_ptr, pubkeylen))
result = CURLE_OK;
end:
- Curl_dyn_free(&buf);
+ curlx_dyn_free(&buf);
Curl_safefree(pem_ptr);
fclose(fp);
}
@@ -1185,96 +873,18 @@ end:
*/
bool Curl_ssl_cert_status_request(void)
{
- return Curl_ssl->cert_status_request();
+ if(Curl_ssl->cert_status_request)
+ return Curl_ssl->cert_status_request();
+ return FALSE;
}
/*
* Check whether the SSL backend supports false start.
*/
-bool Curl_ssl_false_start(struct Curl_easy *data)
-{
- (void)data;
- return Curl_ssl->false_start();
-}
-
-/*
- * Default implementations for unsupported functions.
- */
-
-int Curl_none_init(void)
-{
- return 1;
-}
-
-void Curl_none_cleanup(void)
-{ }
-
-CURLcode Curl_none_shutdown(struct Curl_cfilter *cf UNUSED_PARAM,
- struct Curl_easy *data UNUSED_PARAM,
- bool send_shutdown UNUSED_PARAM,
- bool *done)
-{
- (void)data;
- (void)cf;
- (void)send_shutdown;
- /* Every SSL backend should have a shutdown implementation. Until we
- * have implemented that, we put this fake in place. */
- *done = TRUE;
- return CURLE_OK;
-}
-
-int Curl_none_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *data)
-{
- (void)cf;
- (void)data;
- return -1;
-}
-
-void Curl_none_close_all(struct Curl_easy *data UNUSED_PARAM)
-{
- (void)data;
-}
-
-void Curl_none_session_free(void *ptr UNUSED_PARAM)
-{
- (void)ptr;
-}
-
-bool Curl_none_data_pending(struct Curl_cfilter *cf UNUSED_PARAM,
- const struct Curl_easy *data UNUSED_PARAM)
-{
- (void)cf;
- (void)data;
- return 0;
-}
-
-bool Curl_none_cert_status_request(void)
-{
- return FALSE;
-}
-
-CURLcode Curl_none_set_engine(struct Curl_easy *data UNUSED_PARAM,
- const char *engine UNUSED_PARAM)
-{
- (void)data;
- (void)engine;
- return CURLE_NOT_BUILT_IN;
-}
-
-CURLcode Curl_none_set_engine_default(struct Curl_easy *data UNUSED_PARAM)
-{
- (void)data;
- return CURLE_NOT_BUILT_IN;
-}
-
-struct curl_slist *Curl_none_engines_list(struct Curl_easy *data UNUSED_PARAM)
-{
- (void)data;
- return (struct curl_slist *)NULL;
-}
-
-bool Curl_none_false_start(void)
+bool Curl_ssl_false_start(void)
{
+ if(Curl_ssl->false_start)
+ return Curl_ssl->false_start();
return FALSE;
}
@@ -1282,24 +892,17 @@ static int multissl_init(void)
{
if(multissl_setup(NULL))
return 1;
- return Curl_ssl->init();
+ if(Curl_ssl->init)
+ return Curl_ssl->init();
+ return 1;
}
static CURLcode multissl_connect(struct Curl_cfilter *cf,
- struct Curl_easy *data)
+ struct Curl_easy *data, bool *done)
{
if(multissl_setup(NULL))
return CURLE_FAILED_INIT;
- return Curl_ssl->connect_blocking(cf, data);
-}
-
-static CURLcode multissl_connect_nonblocking(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool *done)
-{
- if(multissl_setup(NULL))
- return CURLE_FAILED_INIT;
- return Curl_ssl->connect_nonblocking(cf, data, done);
+ return Curl_ssl->do_connect(cf, data, done);
}
static void multissl_adjust_pollset(struct Curl_cfilter *cf,
@@ -1351,26 +954,22 @@ static const struct Curl_ssl Curl_ssl_multi = {
(size_t)-1, /* something insanely large to be on the safe side */
multissl_init, /* init */
- Curl_none_cleanup, /* cleanup */
+ NULL, /* cleanup */
multissl_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
- Curl_none_shutdown, /* shutdown */
- Curl_none_data_pending, /* data_pending */
+ NULL, /* shutdown */
+ NULL, /* data_pending */
NULL, /* random */
- Curl_none_cert_status_request, /* cert_status_request */
+ NULL, /* cert_status_request */
multissl_connect, /* connect */
- multissl_connect_nonblocking, /* connect_nonblocking */
- multissl_adjust_pollset, /* adjust_pollset */
+ multissl_adjust_pollset, /* adjust_pollset */
multissl_get_internals, /* get_internals */
multissl_close, /* close_one */
- Curl_none_close_all, /* close_all */
- Curl_none_set_engine, /* set_engine */
- Curl_none_set_engine_default, /* set_engine_default */
- Curl_none_engines_list, /* engines_list */
- Curl_none_false_start, /* false_start */
+ NULL, /* close_all */
+ NULL, /* set_engine */
+ NULL, /* set_engine_default */
+ NULL, /* engines_list */
+ NULL, /* false_start */
NULL, /* sha256sum */
- NULL, /* associate_connection */
- NULL, /* disassociate_connection */
multissl_recv_plain, /* recv decrypted data */
multissl_send_plain, /* send data to encrypt */
NULL, /* get_channel_binding */
@@ -1432,7 +1031,8 @@ void Curl_ssl_cleanup(void)
{
if(init_ssl) {
/* only cleanup if we did a previous init */
- Curl_ssl->cleanup();
+ if(Curl_ssl->cleanup)
+ Curl_ssl->cleanup();
#if defined(CURL_WITH_MULTI_SSL)
Curl_ssl = &Curl_ssl_multi;
#endif
@@ -1482,8 +1082,8 @@ static size_t multissl_version(char *buffer, size_t size)
static int multissl_setup(const struct Curl_ssl *backend)
{
- const char *env;
- char *env_tmp;
+ int i;
+ char *env;
if(Curl_ssl != &Curl_ssl_multi)
return 1;
@@ -1496,25 +1096,31 @@ static int multissl_setup(const struct Curl_ssl *backend)
if(!available_backends[0])
return 1;
- env = env_tmp = curl_getenv("CURL_SSL_BACKEND");
-#ifdef CURL_DEFAULT_SSL_BACKEND
- if(!env)
- env = CURL_DEFAULT_SSL_BACKEND;
-#endif
+ env = curl_getenv("CURL_SSL_BACKEND");
if(env) {
- int i;
for(i = 0; available_backends[i]; i++) {
if(strcasecompare(env, available_backends[i]->info.name)) {
Curl_ssl = available_backends[i];
- free(env_tmp);
+ free(env);
return 0;
}
}
}
+#ifdef CURL_DEFAULT_SSL_BACKEND
+ for(i = 0; available_backends[i]; i++) {
+ if(strcasecompare(CURL_DEFAULT_SSL_BACKEND,
+ available_backends[i]->info.name)) {
+ Curl_ssl = available_backends[i];
+ free(env);
+ return 0;
+ }
+ }
+#endif
+
/* Fall back to first available backend */
Curl_ssl = available_backends[0];
- free(env_tmp);
+ free(env);
return 0;
}
@@ -1565,11 +1171,12 @@ CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
void Curl_ssl_peer_cleanup(struct ssl_peer *peer)
{
+ Curl_safefree(peer->sni);
if(peer->dispname != peer->hostname)
free(peer->dispname);
- free(peer->sni);
- free(peer->hostname);
- peer->hostname = peer->sni = peer->dispname = NULL;
+ peer->dispname = NULL;
+ Curl_safefree(peer->hostname);
+ Curl_safefree(peer->scache_key);
peer->type = CURL_SSL_PEER_DNS;
}
@@ -1577,7 +1184,7 @@ static void cf_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct ssl_connect_data *connssl = cf->ctx;
if(connssl) {
- Curl_ssl->close(cf, data);
+ connssl->ssl_impl->close(cf, data);
connssl->state = ssl_connection_none;
Curl_ssl_peer_cleanup(&connssl->peer);
}
@@ -1592,10 +1199,10 @@ static ssl_peer_type get_peer_type(const char *hostname)
#else
struct in_addr addr;
#endif
- if(Curl_inet_pton(AF_INET, hostname, &addr))
+ if(curlx_inet_pton(AF_INET, hostname, &addr))
return CURL_SSL_PEER_IPV4;
#ifdef USE_IPV6
- else if(Curl_inet_pton(AF_INET6, hostname, &addr)) {
+ else if(curlx_inet_pton(AF_INET6, hostname, &addr)) {
return CURL_SSL_PEER_IPV6;
}
#endif
@@ -1603,7 +1210,9 @@ static ssl_peer_type get_peer_type(const char *hostname)
return CURL_SSL_PEER_DNS;
}
-CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf,
+CURLcode Curl_ssl_peer_init(struct ssl_peer *peer,
+ struct Curl_cfilter *cf,
+ const char *tls_id,
int transport)
{
const char *ehostname, *edispname;
@@ -1665,7 +1274,8 @@ CURLcode Curl_ssl_peer_init(struct ssl_peer *peer, struct Curl_cfilter *cf,
peer->sni[len] = 0;
}
}
- result = CURLE_OK;
+
+ result = Curl_ssl_peer_key_make(cf, peer, tls_id, &peer->scache_key);
out:
if(result)
@@ -1698,13 +1308,13 @@ static void ssl_cf_close(struct Curl_cfilter *cf,
static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
struct Curl_easy *data,
- bool blocking, bool *done)
+ bool *done)
{
struct ssl_connect_data *connssl = cf->ctx;
struct cf_call_data save;
CURLcode result;
- if(cf->connected) {
+ if(cf->connected && (connssl->state != ssl_connection_deferred)) {
*done = TRUE;
return CURLE_OK;
}
@@ -1715,38 +1325,41 @@ static CURLcode ssl_cf_connect(struct Curl_cfilter *cf,
}
if(!cf->next->connected) {
- result = cf->next->cft->do_connect(cf->next, data, blocking, done);
+ result = cf->next->cft->do_connect(cf->next, data, done);
if(result || !*done)
return result;
}
CF_DATA_SAVE(save, cf, data);
CURL_TRC_CF(data, cf, "cf_connect()");
- DEBUGASSERT(data->conn);
- DEBUGASSERT(data->conn == cf->conn);
DEBUGASSERT(connssl);
*done = FALSE;
if(!connssl->peer.hostname) {
- result = Curl_ssl_peer_init(&connssl->peer, cf, TRNSPRT_TCP);
+ char tls_id[80];
+ connssl->ssl_impl->version(tls_id, sizeof(tls_id) - 1);
+ result = Curl_ssl_peer_init(&connssl->peer, cf, tls_id, TRNSPRT_TCP);
if(result)
goto out;
}
- if(blocking) {
- result = ssl_connect(cf, data);
- *done = (result == CURLE_OK);
- }
- else {
- result = ssl_connect_nonblocking(cf, data, done);
+ if(!connssl->prefs_checked) {
+ if(!ssl_prefs_check(data))
+ return CURLE_SSL_CONNECT_ERROR;
+ connssl->prefs_checked = TRUE;
}
+ result = connssl->ssl_impl->do_connect(cf, data, done);
+
if(!result && *done) {
cf->connected = TRUE;
- connssl->handshake_done = Curl_now();
+ if(connssl->state == ssl_connection_complete)
+ connssl->handshake_done = curlx_now();
/* Connection can be deferred when sending early data */
DEBUGASSERT(connssl->state == ssl_connection_complete ||
connssl->state == ssl_connection_deferred);
+ DEBUGASSERT(connssl->state != ssl_connection_deferred ||
+ connssl->earlydata_state > ssl_earlydata_none);
}
out:
CURL_TRC_CF(data, cf, "cf_connect() -> %d, done=%d", result, *done);
@@ -1754,14 +1367,87 @@ out:
return result;
}
+static CURLcode ssl_cf_set_earlydata(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const void *buf, size_t blen)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ ssize_t nwritten = 0;
+ CURLcode result = CURLE_OK;
+
+ DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_await);
+ DEBUGASSERT(Curl_bufq_is_empty(&connssl->earlydata));
+ if(blen) {
+ if(blen > connssl->earlydata_max)
+ blen = connssl->earlydata_max;
+ nwritten = Curl_bufq_write(&connssl->earlydata, buf, blen, &result);
+ CURL_TRC_CF(data, cf, "ssl_cf_set_earlydata(len=%zu) -> %zd",
+ blen, nwritten);
+ if(nwritten < 0)
+ return result;
+ }
+ return CURLE_OK;
+}
+
+static CURLcode ssl_cf_connect_deferred(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const void *buf, size_t blen,
+ bool *done)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ CURLcode result = CURLE_OK;
+
+ DEBUGASSERT(connssl->state == ssl_connection_deferred);
+ *done = FALSE;
+ if(connssl->earlydata_state == ssl_earlydata_await) {
+ result = ssl_cf_set_earlydata(cf, data, buf, blen);
+ if(result)
+ return result;
+ /* we buffered any early data we'd like to send. Actually
+ * do the connect now which sends it and performs the handshake. */
+ connssl->earlydata_state = ssl_earlydata_sending;
+ connssl->earlydata_skip = Curl_bufq_len(&connssl->earlydata);
+ }
+
+ result = ssl_cf_connect(cf, data, done);
+
+ if(!result && *done) {
+ Curl_pgrsTimeWas(data, TIMER_APPCONNECT, connssl->handshake_done);
+ switch(connssl->earlydata_state) {
+ case ssl_earlydata_none:
+ break;
+ case ssl_earlydata_accepted:
+ if(!Curl_ssl_cf_is_proxy(cf))
+ Curl_pgrsEarlyData(data, (curl_off_t)connssl->earlydata_skip);
+ infof(data, "Server accepted %zu bytes of TLS early data.",
+ connssl->earlydata_skip);
+ break;
+ case ssl_earlydata_rejected:
+ if(!Curl_ssl_cf_is_proxy(cf))
+ Curl_pgrsEarlyData(data, -(curl_off_t)connssl->earlydata_skip);
+ infof(data, "Server rejected TLS early data.");
+ connssl->earlydata_skip = 0;
+ break;
+ default:
+ /* This should not happen. Either we do not use early data or we
+ * should know if it was accepted or not. */
+ DEBUGASSERT(NULL);
+ break;
+ }
+ }
+ return result;
+}
+
static bool ssl_cf_data_pending(struct Curl_cfilter *cf,
const struct Curl_easy *data)
{
+ struct ssl_connect_data *connssl = cf->ctx;
struct cf_call_data save;
bool result;
CF_DATA_SAVE(save, cf, data);
- if(Curl_ssl->data_pending(cf, data))
+ if(connssl->ssl_impl->data_pending &&
+ connssl->ssl_impl->data_pending(cf, data))
result = TRUE;
else
result = cf->next->cft->has_data_pending(cf->next, data);
@@ -1770,20 +1456,57 @@ static bool ssl_cf_data_pending(struct Curl_cfilter *cf,
}
static ssize_t ssl_cf_send(struct Curl_cfilter *cf,
- struct Curl_easy *data, const void *buf, size_t len,
+ struct Curl_easy *data,
+ const void *buf, size_t blen,
bool eos, CURLcode *err)
{
+ struct ssl_connect_data *connssl = cf->ctx;
struct cf_call_data save;
- ssize_t nwritten = 0;
+ ssize_t nwritten = 0, early_written = 0;
(void)eos;
- /* OpenSSL and maybe other TLS libs do not like 0-length writes. Skip. */
*err = CURLE_OK;
- if(len > 0) {
- CF_DATA_SAVE(save, cf, data);
- nwritten = Curl_ssl->send_plain(cf, data, buf, len, err);
- CF_DATA_RESTORE(cf, save);
+ CF_DATA_SAVE(save, cf, data);
+
+ if(connssl->state == ssl_connection_deferred) {
+ bool done = FALSE;
+ *err = ssl_cf_connect_deferred(cf, data, buf, blen, &done);
+ if(*err) {
+ nwritten = -1;
+ goto out;
+ }
+ else if(!done) {
+ *err = CURLE_AGAIN;
+ nwritten = -1;
+ goto out;
+ }
+ DEBUGASSERT(connssl->state == ssl_connection_complete);
+ }
+
+ if(connssl->earlydata_skip) {
+ if(connssl->earlydata_skip >= blen) {
+ connssl->earlydata_skip -= blen;
+ *err = CURLE_OK;
+ nwritten = (ssize_t)blen;
+ goto out;
+ }
+ else {
+ early_written = connssl->earlydata_skip;
+ buf = ((const char *)buf) + connssl->earlydata_skip;
+ blen -= connssl->earlydata_skip;
+ connssl->earlydata_skip = 0;
+ }
}
+
+ /* OpenSSL and maybe other TLS libs do not like 0-length writes. Skip. */
+ if(blen > 0)
+ nwritten = connssl->ssl_impl->send_plain(cf, data, buf, blen, err);
+
+ if(nwritten >= 0)
+ nwritten += early_written;
+
+out:
+ CF_DATA_RESTORE(cf, save);
return nwritten;
}
@@ -1791,12 +1514,28 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf,
struct Curl_easy *data, char *buf, size_t len,
CURLcode *err)
{
+ struct ssl_connect_data *connssl = cf->ctx;
struct cf_call_data save;
ssize_t nread;
CF_DATA_SAVE(save, cf, data);
*err = CURLE_OK;
- nread = Curl_ssl->recv_plain(cf, data, buf, len, err);
+ if(connssl->state == ssl_connection_deferred) {
+ bool done = FALSE;
+ *err = ssl_cf_connect_deferred(cf, data, NULL, 0, &done);
+ if(*err) {
+ nread = -1;
+ goto out;
+ }
+ else if(!done) {
+ *err = CURLE_AGAIN;
+ nread = -1;
+ goto out;
+ }
+ DEBUGASSERT(connssl->state == ssl_connection_complete);
+ }
+
+ nread = connssl->ssl_impl->recv_plain(cf, data, buf, len, err);
if(nread > 0) {
DEBUGASSERT((size_t)nread <= len);
}
@@ -1804,6 +1543,8 @@ static ssize_t ssl_cf_recv(struct Curl_cfilter *cf,
/* eof */
*err = CURLE_OK;
}
+
+out:
CURL_TRC_CF(data, cf, "cf_recv(len=%zu) -> %zd, %d", len,
nread, *err);
CF_DATA_RESTORE(cf, save);
@@ -1814,14 +1555,17 @@ static CURLcode ssl_cf_shutdown(struct Curl_cfilter *cf,
struct Curl_easy *data,
bool *done)
{
+ struct ssl_connect_data *connssl = cf->ctx;
CURLcode result = CURLE_OK;
*done = TRUE;
- if(!cf->shutdown) {
+ /* If we have done the SSL handshake, shut down the connection cleanly */
+ if(cf->connected && (connssl->state == ssl_connection_complete) &&
+ !cf->shutdown && Curl_ssl->shut_down) {
struct cf_call_data save;
CF_DATA_SAVE(save, cf, data);
- result = Curl_ssl->shut_down(cf, data, TRUE, done);
+ result = connssl->ssl_impl->shut_down(cf, data, TRUE, done);
CURL_TRC_CF(data, cf, "cf_shutdown -> %d, done=%d", result, *done);
CF_DATA_RESTORE(cf, save);
cf->shutdown = (result || *done);
@@ -1833,42 +1577,14 @@ static void ssl_cf_adjust_pollset(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct easy_pollset *ps)
{
+ struct ssl_connect_data *connssl = cf->ctx;
struct cf_call_data save;
CF_DATA_SAVE(save, cf, data);
- Curl_ssl->adjust_pollset(cf, data, ps);
+ connssl->ssl_impl->adjust_pollset(cf, data, ps);
CF_DATA_RESTORE(cf, save);
}
-static CURLcode ssl_cf_cntrl(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- int event, int arg1, void *arg2)
-{
- struct cf_call_data save;
-
- (void)arg1;
- (void)arg2;
- switch(event) {
- case CF_CTRL_DATA_ATTACH:
- if(Curl_ssl->attach_data) {
- CF_DATA_SAVE(save, cf, data);
- Curl_ssl->attach_data(cf, data);
- CF_DATA_RESTORE(cf, save);
- }
- break;
- case CF_CTRL_DATA_DETACH:
- if(Curl_ssl->detach_data) {
- CF_DATA_SAVE(save, cf, data);
- Curl_ssl->detach_data(cf, data);
- CF_DATA_RESTORE(cf, save);
- }
- break;
- default:
- break;
- }
- return CURLE_OK;
-}
-
static CURLcode ssl_cf_query(struct Curl_cfilter *cf,
struct Curl_easy *data,
int query, int *pres1, void *pres2)
@@ -1893,28 +1609,9 @@ static CURLcode ssl_cf_query(struct Curl_cfilter *cf,
static bool cf_ssl_is_alive(struct Curl_cfilter *cf, struct Curl_easy *data,
bool *input_pending)
{
- struct cf_call_data save;
- int result;
/*
* This function tries to determine connection status.
- *
- * Return codes:
- * 1 means the connection is still in place
- * 0 means the connection has been closed
- * -1 means the connection status is unknown
*/
- CF_DATA_SAVE(save, cf, data);
- result = Curl_ssl->check_cxn(cf, data);
- CF_DATA_RESTORE(cf, save);
- if(result > 0) {
- *input_pending = TRUE;
- return TRUE;
- }
- if(result == 0) {
- *input_pending = FALSE;
- return FALSE;
- }
- /* ssl backend does not know */
return cf->next ?
cf->next->cft->is_alive(cf->next, data, input_pending) :
FALSE; /* pessimistic in absence of data */
@@ -1933,7 +1630,7 @@ struct Curl_cftype Curl_cft_ssl = {
ssl_cf_data_pending,
ssl_cf_send,
ssl_cf_recv,
- ssl_cf_cntrl,
+ Curl_cf_def_cntrl,
cf_ssl_is_alive,
Curl_cf_def_conn_keep_alive,
ssl_cf_query,
@@ -1954,7 +1651,7 @@ struct Curl_cftype Curl_cft_ssl_proxy = {
ssl_cf_data_pending,
ssl_cf_send,
ssl_cf_recv,
- ssl_cf_cntrl,
+ Curl_cf_def_cntrl,
cf_ssl_is_alive,
Curl_cf_def_conn_keep_alive,
Curl_cf_def_query,
@@ -1972,8 +1669,14 @@ static CURLcode cf_ssl_create(struct Curl_cfilter **pcf,
DEBUGASSERT(data->conn);
- ctx = cf_ctx_new(data, alpn_get_spec(data->state.httpwant,
+#ifdef CURL_DISABLE_HTTP
+ /* We only support ALPN for HTTP so far. */
+ DEBUGASSERT(!conn->bits.tls_enable_alpn);
+ ctx = cf_ctx_new(data, NULL);
+#else
+ ctx = cf_ctx_new(data, alpn_get_spec(data->state.http_neg.wanted,
conn->bits.tls_enable_alpn));
+#endif
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
@@ -2023,16 +1726,16 @@ static CURLcode cf_ssl_proxy_create(struct Curl_cfilter **pcf,
struct ssl_connect_data *ctx;
CURLcode result;
bool use_alpn = conn->bits.tls_enable_alpn;
- int httpwant = CURL_HTTP_VERSION_1_1;
+ http_majors allowed = CURL_HTTP_V1x;
#ifdef USE_HTTP2
if(conn->http_proxy.proxytype == CURLPROXY_HTTPS2) {
use_alpn = TRUE;
- httpwant = CURL_HTTP_VERSION_2;
+ allowed = (CURL_HTTP_V1x|CURL_HTTP_V2x);
}
#endif
- ctx = cf_ctx_new(data, alpn_get_spec(httpwant, use_alpn));
+ ctx = cf_ctx_new(data, alpn_get_spec(allowed, use_alpn));
if(!ctx) {
result = CURLE_OUT_OF_MEMORY;
goto out;
@@ -2090,9 +1793,10 @@ void *Curl_ssl_get_internals(struct Curl_easy *data, int sockindex,
/* get first SSL filter in chain, if any is present */
cf = get_ssl_filter(data->conn->cfilter[sockindex]);
if(cf) {
+ struct ssl_connect_data *connssl = cf->ctx;
struct cf_call_data save;
CF_DATA_SAVE(save, cf, data);
- result = Curl_ssl->get_internals(cf->ctx, info);
+ result = connssl->ssl_impl->get_internals(cf->ctx, info);
CF_DATA_RESTORE(cf, save);
}
}
@@ -2125,7 +1829,7 @@ static CURLcode vtls_shutdown_blocking(struct Curl_cfilter *cf,
return CURLE_OPERATION_TIMEDOUT;
}
- result = Curl_ssl->shut_down(cf, data, send_shutdown, done);
+ result = connssl->ssl_impl->shut_down(cf, data, send_shutdown, done);
if(result ||*done)
goto out;
@@ -2163,7 +1867,7 @@ CURLcode Curl_ssl_cfilter_remove(struct Curl_easy *data,
if(cf->cft == &Curl_cft_ssl) {
bool done;
CURL_TRC_CF(data, cf, "shutdown and remove SSL, start");
- Curl_shutdown_start(data, sockindex, NULL);
+ Curl_shutdown_start(data, sockindex, 0, NULL);
result = vtls_shutdown_blocking(cf, data, send_shutdown, &done);
Curl_shutdown_clear(data, sockindex);
if(!result && !done) /* blocking failed? */
@@ -2261,6 +1965,24 @@ bool Curl_alpn_contains_proto(const struct alpn_spec *spec,
return FALSE;
}
+void Curl_alpn_restrict_to(struct alpn_spec *spec, const char *proto)
+{
+ size_t plen = strlen(proto);
+ DEBUGASSERT(plen < sizeof(spec->entries[0]));
+ if(plen < sizeof(spec->entries[0])) {
+ memcpy(spec->entries[0], proto, plen + 1);
+ spec->count = 1;
+ }
+}
+
+void Curl_alpn_copy(struct alpn_spec *dest, const struct alpn_spec *src)
+{
+ if(src)
+ memcpy(dest, src, sizeof(*dest));
+ else
+ memset(dest, 0, sizeof(*dest));
+}
+
CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_connect_data *connssl,
@@ -2277,28 +1999,28 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
#endif
;
- if(connssl->alpn_negotiated) {
+ if(connssl->negotiated.alpn) {
/* When we ask for a specific ALPN protocol, we need the confirmation
* of it by the server, as we have installed protocol handler and
* connection filter chain for exactly this protocol. */
if(!proto_len) {
failf(data, "ALPN: asked for '%s' from previous session, "
"but server did not confirm it. Refusing to continue.",
- connssl->alpn_negotiated);
+ connssl->negotiated.alpn);
result = CURLE_SSL_CONNECT_ERROR;
goto out;
}
- else if((strlen(connssl->alpn_negotiated) != proto_len) ||
- memcmp(connssl->alpn_negotiated, proto, proto_len)) {
+ else if((strlen(connssl->negotiated.alpn) != proto_len) ||
+ memcmp(connssl->negotiated.alpn, proto, proto_len)) {
failf(data, "ALPN: asked for '%s' from previous session, but server "
"selected '%.*s'. Refusing to continue.",
- connssl->alpn_negotiated, (int)proto_len, proto);
+ connssl->negotiated.alpn, (int)proto_len, proto);
result = CURLE_SSL_CONNECT_ERROR;
goto out;
}
/* ALPN is exactly what we asked for, done. */
infof(data, "ALPN: server confirmed to use '%s'",
- connssl->alpn_negotiated);
+ connssl->negotiated.alpn);
goto out;
}
@@ -2309,11 +2031,11 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
result = CURLE_SSL_CONNECT_ERROR;
goto out;
}
- connssl->alpn_negotiated = malloc(proto_len + 1);
- if(!connssl->alpn_negotiated)
+ connssl->negotiated.alpn = malloc(proto_len + 1);
+ if(!connssl->negotiated.alpn)
return CURLE_OUT_OF_MEMORY;
- memcpy(connssl->alpn_negotiated, proto, proto_len);
- connssl->alpn_negotiated[proto_len] = 0;
+ memcpy(connssl->negotiated.alpn, proto, proto_len);
+ connssl->negotiated.alpn[proto_len] = 0;
}
if(proto && proto_len) {
@@ -2336,8 +2058,8 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
else {
*palpn = CURL_HTTP_VERSION_NONE;
failf(data, "unsupported ALPN protocol: '%.*s'", (int)proto_len, proto);
- /* TODO: do we want to fail this? Previous code just ignored it and
- * some vtls backends even ignore the return code of this function. */
+ /* Previous code just ignored it and some vtls backends even ignore the
+ * return code of this function. */
/* return CURLE_NOT_BUILT_IN; */
goto out;
}
diff --git a/libs/libcurl/src/vtls/vtls.h b/libs/libcurl/src/vtls/vtls.h
index 171a5d8e55..0617758f92 100644
--- a/libs/libcurl/src/vtls/vtls.h
+++ b/libs/libcurl/src/vtls/vtls.h
@@ -23,12 +23,14 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
struct connectdata;
struct ssl_config_data;
struct ssl_primary_config;
-struct Curl_ssl_session;
+struct Curl_cfilter;
+struct Curl_easy;
+struct dynbuf;
#define SSLSUPP_CA_PATH (1<<0) /* supports CAPATH */
#define SSLSUPP_CERTINFO (1<<1) /* supports CURLOPT_CERTINFO */
@@ -40,6 +42,15 @@ struct Curl_ssl_session;
#define SSLSUPP_ECH (1<<7)
#define SSLSUPP_CA_CACHE (1<<8)
#define SSLSUPP_CIPHER_LIST (1<<9) /* supports TLS 1.0-1.2 ciphersuites */
+#define SSLSUPP_SIGNATURE_ALGORITHMS (1<<10) /* supports TLS sigalgs */
+
+#ifdef USE_ECH
+# include "../curlx/base64.h"
+# define ECH_ENABLED(__data__) \
+ (__data__->set.tls_ech && \
+ !(__data__->set.tls_ech & CURLECH_DISABLE)\
+ )
+#endif /* USE_ECH */
#define ALPN_ACCEPTED "ALPN: server accepted "
@@ -55,9 +66,31 @@ struct Curl_ssl_session;
#define VTLS_INFOF_ALPN_DEFERRED \
"ALPN: deferred handshake for early data using '%.*s'."
-/* Curl_multi SSL backend-specific data; declared differently by each SSL
- backend */
-struct Curl_cfilter;
+/* IETF defined version numbers used in TLS protocol negotiation */
+#define CURL_IETF_PROTO_UNKNOWN 0x0
+#define CURL_IETF_PROTO_SSL3 0x0300
+#define CURL_IETF_PROTO_TLS1 0x0301
+#define CURL_IETF_PROTO_TLS1_1 0x0302
+#define CURL_IETF_PROTO_TLS1_2 0x0303
+#define CURL_IETF_PROTO_TLS1_3 0x0304
+#define CURL_IETF_PROTO_DTLS1 0xFEFF
+#define CURL_IETF_PROTO_DTLS1_2 0xFEFD
+
+typedef enum {
+ CURL_SSL_PEER_DNS,
+ CURL_SSL_PEER_IPV4,
+ CURL_SSL_PEER_IPV6
+} ssl_peer_type;
+
+struct ssl_peer {
+ char *hostname; /* hostname for verification */
+ char *dispname; /* display version of hostname */
+ char *sni; /* SNI version of hostname or NULL if not usable */
+ char *scache_key; /* for lookups in session cache */
+ ssl_peer_type type; /* type of the peer information */
+ int port; /* port we are talking to */
+ int transport; /* one of TRNSPRT_* defines */
+};
CURLsslset Curl_init_sslset_nolock(curl_sslbackend id, const char *name,
const curl_ssl_backend ***avail);
@@ -113,7 +146,9 @@ void Curl_ssl_conn_config_update(struct Curl_easy *data, bool for_proxy);
* Init SSL peer information for filter. Can be called repeatedly.
*/
CURLcode Curl_ssl_peer_init(struct ssl_peer *peer,
- struct Curl_cfilter *cf, int transport);
+ struct Curl_cfilter *cf,
+ const char *tls_id,
+ int transport);
/**
* Free all allocated data and reset peer information.
*/
@@ -130,8 +165,6 @@ CURLcode Curl_ssl_set_engine(struct Curl_easy *data, const char *engine);
CURLcode Curl_ssl_set_engine_default(struct Curl_easy *data);
struct curl_slist *Curl_ssl_engines_list(struct Curl_easy *data);
-/* init the SSL session ID cache */
-CURLcode Curl_ssl_initsessions(struct Curl_easy *, size_t);
void Curl_ssl_version(char *buffer, size_t size);
/* Certificate information list handling. */
@@ -147,33 +180,6 @@ CURLcode Curl_ssl_push_certinfo(struct Curl_easy *data, int certnum,
/* Functions to be used by SSL library adaptation functions */
-/* Lock session cache mutex.
- * Call this before calling other Curl_ssl_*session* functions
- * Caller should unlock this mutex as soon as possible, as it may block
- * other SSL connection from making progress.
- * The purpose of explicitly locking SSL session cache data is to allow
- * individual SSL engines to manage session lifetime in their specific way.
- */
-void Curl_ssl_sessionid_lock(struct Curl_easy *data);
-
-/* Unlock session cache mutex */
-void Curl_ssl_sessionid_unlock(struct Curl_easy *data);
-
-/* Kill a single session ID entry in the cache
- * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
- * This will call engine-specific curlssl_session_free function, which must
- * take sessionid object ownership from sessionid cache
- * (e.g. decrement refcount).
- */
-void Curl_ssl_kill_session(struct Curl_ssl_session *session);
-/* delete a session from the cache
- * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
- * This will call engine-specific curlssl_session_free function, which must
- * take sessionid object ownership from sessionid cache
- * (e.g. decrement refcount).
- */
-void Curl_ssl_delsessionid(struct Curl_easy *data, void *ssl_sessionid);
-
/* get N random bytes into the buffer */
CURLcode Curl_ssl_random(struct Curl_easy *data, unsigned char *buffer,
size_t length);
@@ -184,7 +190,7 @@ CURLcode Curl_pin_peer_pubkey(struct Curl_easy *data,
bool Curl_ssl_cert_status_request(void);
-bool Curl_ssl_false_start(struct Curl_easy *data);
+bool Curl_ssl_false_start(void);
/* The maximum size of the SSL channel binding is 85 bytes, as defined in
* RFC 5929, Section 4.1. The 'tls-server-end-point:' prefix is 21 bytes long,
@@ -265,12 +271,10 @@ extern struct Curl_cftype Curl_cft_ssl_proxy;
#define Curl_ssl_set_engine(x,y) CURLE_NOT_BUILT_IN
#define Curl_ssl_set_engine_default(x) CURLE_NOT_BUILT_IN
#define Curl_ssl_engines_list(x) NULL
-#define Curl_ssl_initsessions(x,y) CURLE_OK
#define Curl_ssl_free_certinfo(x) Curl_nop_stmt
-#define Curl_ssl_kill_session(x) Curl_nop_stmt
#define Curl_ssl_random(x,y,z) ((void)x, CURLE_NOT_BUILT_IN)
#define Curl_ssl_cert_status_request() FALSE
-#define Curl_ssl_false_start(a) FALSE
+#define Curl_ssl_false_start() FALSE
#define Curl_ssl_get_internals(a,b,c,d) NULL
#define Curl_ssl_supports(a,b) FALSE
#define Curl_ssl_cfilter_add(a,b,c) CURLE_NOT_BUILT_IN
diff --git a/libs/libcurl/src/vtls/vtls_int.h b/libs/libcurl/src/vtls/vtls_int.h
index 250273ef9d..e4a9e35325 100644
--- a/libs/libcurl/src/vtls/vtls_int.h
+++ b/libs/libcurl/src/vtls/vtls_int.h
@@ -23,12 +23,14 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
-#include "cfilters.h"
-#include "urldata.h"
+#include "../curl_setup.h"
+#include "../cfilters.h"
+#include "../urldata.h"
+#include "vtls.h"
#ifdef USE_SSL
+struct Curl_ssl;
struct ssl_connect_data;
/* see https://www.iana.org/assignments/tls-extensiontype-values/ */
@@ -47,7 +49,7 @@ struct ssl_connect_data;
#define ALPN_PROTO_BUF_MAX (ALPN_ENTRIES_MAX * (ALPN_NAME_MAX + 1))
struct alpn_spec {
- const char entries[ALPN_ENTRIES_MAX][ALPN_NAME_MAX];
+ char entries[ALPN_ENTRIES_MAX][ALPN_NAME_MAX];
size_t count; /* number of entries */
};
@@ -60,6 +62,8 @@ CURLcode Curl_alpn_to_proto_buf(struct alpn_proto_buf *buf,
const struct alpn_spec *spec);
CURLcode Curl_alpn_to_proto_str(struct alpn_proto_buf *buf,
const struct alpn_spec *spec);
+void Curl_alpn_restrict_to(struct alpn_spec *spec, const char *proto);
+void Curl_alpn_copy(struct alpn_spec *dest, const struct alpn_spec *src);
CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
struct Curl_easy *data,
@@ -87,7 +91,7 @@ typedef enum {
typedef enum {
ssl_earlydata_none,
- ssl_earlydata_use,
+ ssl_earlydata_await,
ssl_earlydata_sending,
ssl_earlydata_sent,
ssl_earlydata_accepted,
@@ -103,12 +107,15 @@ typedef enum {
/* Information in each SSL cfilter context: cf->ctx */
struct ssl_connect_data {
- struct ssl_peer peer;
+ const struct Curl_ssl *ssl_impl; /* TLS backend for this filter */
+ struct ssl_peer peer; /* peer the filter talks to */
const struct alpn_spec *alpn; /* ALPN to use or NULL for none */
void *backend; /* vtls backend specific props */
struct cf_call_data call_data; /* data handle used in current call */
struct curltime handshake_done; /* time when handshake finished */
- char *alpn_negotiated; /* negotiated ALPN value or NULL */
+ struct {
+ char *alpn; /* ALPN value or NULL */
+ } negotiated;
struct bufq earlydata; /* earlydata to be send to peer */
size_t earlydata_max; /* max earlydata allowed by peer */
size_t earlydata_skip; /* sending bytes to skip when earlydata
@@ -119,6 +126,7 @@ struct ssl_connect_data {
int io_need; /* TLS signals special SEND/RECV needs */
BIT(use_alpn); /* if ALPN shall be used in handshake */
BIT(peer_closed); /* peer has closed connection */
+ BIT(prefs_checked); /* SSL preferences have been checked */
};
@@ -142,7 +150,6 @@ struct Curl_ssl {
void (*cleanup)(void);
size_t (*version)(char *buffer, size_t size);
- int (*check_cxn)(struct Curl_cfilter *cf, struct Curl_easy *data);
CURLcode (*shut_down)(struct Curl_cfilter *cf, struct Curl_easy *data,
bool send_shutdown, bool *done);
bool (*data_pending)(struct Curl_cfilter *cf,
@@ -153,11 +160,8 @@ struct Curl_ssl {
size_t length);
bool (*cert_status_request)(void);
- CURLcode (*connect_blocking)(struct Curl_cfilter *cf,
- struct Curl_easy *data);
- CURLcode (*connect_nonblocking)(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool *done);
+ CURLcode (*do_connect)(struct Curl_cfilter *cf, struct Curl_easy *data,
+ bool *done);
/* During handshake/shutdown, adjust the pollset to include the socket
* for POLLOUT or POLLIN as needed. Mandatory. */
@@ -174,10 +178,6 @@ struct Curl_ssl {
bool (*false_start)(void);
CURLcode (*sha256sum)(const unsigned char *input, size_t inputlen,
unsigned char *sha256sum, size_t sha256sumlen);
-
- bool (*attach_data)(struct Curl_cfilter *cf, struct Curl_easy *data);
- void (*detach_data)(struct Curl_cfilter *cf, struct Curl_easy *data);
-
ssize_t (*recv_plain)(struct Curl_cfilter *cf, struct Curl_easy *data,
char *buf, size_t len, CURLcode *code);
ssize_t (*send_plain)(struct Curl_cfilter *cf, struct Curl_easy *data,
@@ -190,66 +190,14 @@ struct Curl_ssl {
extern const struct Curl_ssl *Curl_ssl;
-
-int Curl_none_init(void);
-void Curl_none_cleanup(void);
-CURLcode Curl_none_shutdown(struct Curl_cfilter *cf, struct Curl_easy *data,
- bool send_shutdown, bool *done);
-int Curl_none_check_cxn(struct Curl_cfilter *cf, struct Curl_easy *data);
-void Curl_none_close_all(struct Curl_easy *data);
-void Curl_none_session_free(void *ptr);
-bool Curl_none_data_pending(struct Curl_cfilter *cf,
- const struct Curl_easy *data);
-bool Curl_none_cert_status_request(void);
-CURLcode Curl_none_set_engine(struct Curl_easy *data, const char *engine);
-CURLcode Curl_none_set_engine_default(struct Curl_easy *data);
-struct curl_slist *Curl_none_engines_list(struct Curl_easy *data);
-bool Curl_none_false_start(void);
void Curl_ssl_adjust_pollset(struct Curl_cfilter *cf, struct Curl_easy *data,
- struct easy_pollset *ps);
+ struct easy_pollset *ps);
/**
* Get the SSL filter below the given one or NULL if there is none.
*/
bool Curl_ssl_cf_is_proxy(struct Curl_cfilter *cf);
-/* extract a session ID
- * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
- * Caller must make sure that the ownership of returned sessionid object
- * is properly taken (e.g. its refcount is incremented
- * under sessionid mutex).
- * @param cf the connection filter wanting to use it
- * @param data the transfer involved
- * @param peer the peer the filter wants to talk to
- * @param sessionid on return the TLS session
- * @param idsize on return the size of the TLS session data
- * @param palpn on return the ALPN string used by the session,
- * set to NULL when not interested
- */
-bool Curl_ssl_getsessionid(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const struct ssl_peer *peer,
- void **ssl_sessionid,
- size_t *idsize, /* set 0 if unknown */
- char **palpn);
-
-/* Set a TLS session ID for `peer`. Replaces an existing session ID if
- * not already the same.
- * Sessionid mutex must be locked (see Curl_ssl_sessionid_lock).
- * Call takes ownership of `ssl_sessionid`, using `sessionid_free_cb`
- * to deallocate it. Is called in all outcomes, either right away or
- * later when the session cache is cleaned up.
- * Caller must ensure that it has properly shared ownership of this sessionid
- * object with cache (e.g. incrementing refcount on success)
- */
-CURLcode Curl_ssl_set_sessionid(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const struct ssl_peer *peer,
- const char *alpn,
- void *sessionid,
- size_t sessionid_size,
- Curl_ssl_sessionid_dtor *sessionid_free_cb);
-
#endif /* USE_SSL */
#endif /* HEADER_CURL_VTLS_INT_H */
diff --git a/libs/libcurl/src/vtls/vtls_scache.c b/libs/libcurl/src/vtls/vtls_scache.c
new file mode 100644
index 0000000000..3e1825d635
--- /dev/null
+++ b/libs/libcurl/src/vtls/vtls_scache.c
@@ -0,0 +1,1235 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "../curl_setup.h"
+
+#ifdef USE_SSL
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#include "../urldata.h"
+#include "../cfilters.h"
+
+#include "vtls.h" /* generic SSL protos etc */
+#include "vtls_int.h"
+#include "vtls_scache.h"
+#include "vtls_spack.h"
+
+#include "../strcase.h"
+#include "../url.h"
+#include "../llist.h"
+#include "../share.h"
+#include "../curl_trc.h"
+#include "../curl_sha256.h"
+#include "../rand.h"
+#include "../curlx/warnless.h"
+#include "../curl_printf.h"
+#include "../strdup.h"
+
+/* The last #include files should be: */
+#include "../curl_memory.h"
+#include "../memdebug.h"
+
+
+static bool cf_ssl_peer_key_is_global(const char *peer_key);
+
+/* a peer+tls-config we cache sessions for */
+struct Curl_ssl_scache_peer {
+ char *ssl_peer_key; /* id for peer + relevant TLS configuration */
+ char *clientcert;
+ char *srp_username;
+ char *srp_password;
+ struct Curl_llist sessions;
+ void *sobj; /* object instance or NULL */
+ Curl_ssl_scache_obj_dtor *sobj_free; /* free `sobj` callback */
+ unsigned char key_salt[CURL_SHA256_DIGEST_LENGTH]; /* for entry export */
+ unsigned char key_hmac[CURL_SHA256_DIGEST_LENGTH]; /* for entry export */
+ size_t max_sessions;
+ long age; /* just a number, the higher the more recent */
+ BIT(hmac_set); /* if key_salt and key_hmac are present */
+ BIT(exportable); /* sessions for this peer can be exported */
+};
+
+#define CURL_SCACHE_MAGIC 0x000e1551
+
+#define GOOD_SCACHE(x) ((x) && (x)->magic == CURL_SCACHE_MAGIC)
+
+struct Curl_ssl_scache {
+ unsigned int magic;
+ struct Curl_ssl_scache_peer *peers;
+ size_t peer_count;
+ int default_lifetime_secs;
+ long age;
+};
+
+static struct Curl_ssl_scache *cf_ssl_scache_get(struct Curl_easy *data)
+{
+ struct Curl_ssl_scache *scache = NULL;
+ /* If a share is present, its ssl_scache has preference over the multi */
+ if(data->share && data->share->ssl_scache)
+ scache = data->share->ssl_scache;
+ else if(data->multi && data->multi->ssl_scache)
+ scache = data->multi->ssl_scache;
+ if(scache && !GOOD_SCACHE(scache)) {
+ failf(data, "transfer would use an invalid scache at %p, denied",
+ (void *)scache);
+ DEBUGASSERT(0);
+ return NULL;
+ }
+ return scache;
+}
+
+static void cf_ssl_scache_session_ldestroy(void *udata, void *obj)
+{
+ struct Curl_ssl_session *s = obj;
+ (void)udata;
+ free(CURL_UNCONST(s->sdata));
+ free(CURL_UNCONST(s->quic_tp));
+ free((void *)s->alpn);
+ free(s);
+}
+
+CURLcode
+Curl_ssl_session_create(void *sdata, size_t sdata_len,
+ int ietf_tls_id, const char *alpn,
+ curl_off_t valid_until, size_t earlydata_max,
+ struct Curl_ssl_session **psession)
+{
+ return Curl_ssl_session_create2(sdata, sdata_len, ietf_tls_id, alpn,
+ valid_until, earlydata_max,
+ NULL, 0, psession);
+}
+
+CURLcode
+Curl_ssl_session_create2(void *sdata, size_t sdata_len,
+ int ietf_tls_id, const char *alpn,
+ curl_off_t valid_until, size_t earlydata_max,
+ unsigned char *quic_tp, size_t quic_tp_len,
+ struct Curl_ssl_session **psession)
+{
+ struct Curl_ssl_session *s;
+
+ if(!sdata || !sdata_len) {
+ free(sdata);
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+
+ *psession = NULL;
+ s = calloc(1, sizeof(*s));
+ if(!s) {
+ free(sdata);
+ free(quic_tp);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ s->ietf_tls_id = ietf_tls_id;
+ s->valid_until = valid_until;
+ s->earlydata_max = earlydata_max;
+ s->sdata = sdata;
+ s->sdata_len = sdata_len;
+ s->quic_tp = quic_tp;
+ s->quic_tp_len = quic_tp_len;
+ if(alpn) {
+ s->alpn = strdup(alpn);
+ if(!s->alpn) {
+ cf_ssl_scache_session_ldestroy(NULL, s);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+ *psession = s;
+ return CURLE_OK;
+}
+
+void Curl_ssl_session_destroy(struct Curl_ssl_session *s)
+{
+ if(s) {
+ /* if in the list, the list destructor takes care of it */
+ if(Curl_node_llist(&s->list))
+ Curl_node_remove(&s->list);
+ else {
+ cf_ssl_scache_session_ldestroy(NULL, s);
+ }
+ }
+}
+
+static void cf_ssl_scache_clear_peer(struct Curl_ssl_scache_peer *peer)
+{
+ Curl_llist_destroy(&peer->sessions, NULL);
+ if(peer->sobj) {
+ DEBUGASSERT(peer->sobj_free);
+ if(peer->sobj_free)
+ peer->sobj_free(peer->sobj);
+ peer->sobj = NULL;
+ }
+ peer->sobj_free = NULL;
+ Curl_safefree(peer->clientcert);
+#ifdef USE_TLS_SRP
+ Curl_safefree(peer->srp_username);
+ Curl_safefree(peer->srp_password);
+#endif
+ Curl_safefree(peer->ssl_peer_key);
+ peer->age = 0;
+ peer->hmac_set = FALSE;
+}
+
+static void cf_ssl_scache_peer_set_obj(struct Curl_ssl_scache_peer *peer,
+ void *sobj,
+ Curl_ssl_scache_obj_dtor *sobj_free)
+{
+ DEBUGASSERT(peer);
+ if(peer->sobj_free) {
+ peer->sobj_free(peer->sobj);
+ }
+ peer->sobj = sobj;
+ peer->sobj_free = sobj_free;
+}
+
+static void cf_ssl_cache_peer_update(struct Curl_ssl_scache_peer *peer)
+{
+ /* The sessions of this peer are exportable if
+ * - it has no confidential information
+ * - its peer key is not yet known, because sessions were
+ * imported using only the salt+hmac
+ * - the peer key is global, e.g. carrying no relative paths */
+ peer->exportable = (!peer->clientcert && !peer->srp_username &&
+ !peer->srp_password &&
+ (!peer->ssl_peer_key ||
+ cf_ssl_peer_key_is_global(peer->ssl_peer_key)));
+}
+
+static CURLcode
+cf_ssl_scache_peer_init(struct Curl_ssl_scache_peer *peer,
+ const char *ssl_peer_key,
+ const char *clientcert,
+ const char *srp_username,
+ const char *srp_password,
+ const unsigned char *salt,
+ const unsigned char *hmac)
+{
+ CURLcode result = CURLE_OUT_OF_MEMORY;
+
+ DEBUGASSERT(!peer->ssl_peer_key);
+ if(ssl_peer_key) {
+ peer->ssl_peer_key = strdup(ssl_peer_key);
+ if(!peer->ssl_peer_key)
+ goto out;
+ peer->hmac_set = FALSE;
+ }
+ else if(salt && hmac) {
+ memcpy(peer->key_salt, salt, sizeof(peer->key_salt));
+ memcpy(peer->key_hmac, hmac, sizeof(peer->key_hmac));
+ peer->hmac_set = TRUE;
+ }
+ else {
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
+ }
+ if(clientcert) {
+ peer->clientcert = strdup(clientcert);
+ if(!peer->clientcert)
+ goto out;
+ }
+ if(srp_username) {
+ peer->srp_username = strdup(srp_username);
+ if(!peer->srp_username)
+ goto out;
+ }
+ if(srp_password) {
+ peer->srp_password = strdup(srp_password);
+ if(!peer->srp_password)
+ goto out;
+ }
+
+ cf_ssl_cache_peer_update(peer);
+ result = CURLE_OK;
+out:
+ if(result)
+ cf_ssl_scache_clear_peer(peer);
+ return result;
+}
+
+static void cf_scache_session_remove(struct Curl_ssl_scache_peer *peer,
+ struct Curl_ssl_session *s)
+{
+ (void)peer;
+ DEBUGASSERT(Curl_node_llist(&s->list) == &peer->sessions);
+ Curl_ssl_session_destroy(s);
+}
+
+static bool cf_scache_session_expired(struct Curl_ssl_session *s,
+ curl_off_t now)
+{
+ return (s->valid_until > 0) && (s->valid_until < now);
+}
+
+static void cf_scache_peer_remove_expired(struct Curl_ssl_scache_peer *peer,
+ curl_off_t now)
+{
+ struct Curl_llist_node *n = Curl_llist_head(&peer->sessions);
+ while(n) {
+ struct Curl_ssl_session *s = Curl_node_elem(n);
+ n = Curl_node_next(n);
+ if(cf_scache_session_expired(s, now))
+ cf_scache_session_remove(peer, s);
+ }
+}
+
+static void cf_scache_peer_remove_non13(struct Curl_ssl_scache_peer *peer)
+{
+ struct Curl_llist_node *n = Curl_llist_head(&peer->sessions);
+ while(n) {
+ struct Curl_ssl_session *s = Curl_node_elem(n);
+ n = Curl_node_next(n);
+ if(s->ietf_tls_id != CURL_IETF_PROTO_TLS1_3)
+ cf_scache_session_remove(peer, s);
+ }
+}
+
+CURLcode Curl_ssl_scache_create(size_t max_peers,
+ size_t max_sessions_per_peer,
+ struct Curl_ssl_scache **pscache)
+{
+ struct Curl_ssl_scache *scache;
+ struct Curl_ssl_scache_peer *peers;
+ size_t i;
+
+ *pscache = NULL;
+ peers = calloc(max_peers, sizeof(*peers));
+ if(!peers)
+ return CURLE_OUT_OF_MEMORY;
+
+ scache = calloc(1, sizeof(*scache));
+ if(!scache) {
+ free(peers);
+ return CURLE_OUT_OF_MEMORY;
+ }
+
+ scache->magic = CURL_SCACHE_MAGIC;
+ scache->default_lifetime_secs = (24*60*60); /* 1 day */
+ scache->peer_count = max_peers;
+ scache->peers = peers;
+ scache->age = 1;
+ for(i = 0; i < scache->peer_count; ++i) {
+ scache->peers[i].max_sessions = max_sessions_per_peer;
+ Curl_llist_init(&scache->peers[i].sessions,
+ cf_ssl_scache_session_ldestroy);
+ }
+
+ *pscache = scache;
+ return CURLE_OK;
+}
+
+void Curl_ssl_scache_destroy(struct Curl_ssl_scache *scache)
+{
+ if(scache && GOOD_SCACHE(scache)) {
+ size_t i;
+ scache->magic = 0;
+ for(i = 0; i < scache->peer_count; ++i) {
+ cf_ssl_scache_clear_peer(&scache->peers[i]);
+ }
+ free(scache->peers);
+ free(scache);
+ }
+}
+
+/* Lock shared SSL session data */
+void Curl_ssl_scache_lock(struct Curl_easy *data)
+{
+ if(CURL_SHARE_ssl_scache(data))
+ Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
+}
+
+/* Unlock shared SSL session data */
+void Curl_ssl_scache_unlock(struct Curl_easy *data)
+{
+ if(CURL_SHARE_ssl_scache(data))
+ Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
+}
+
+static CURLcode cf_ssl_peer_key_add_path(struct dynbuf *buf,
+ const char *name,
+ char *path,
+ bool *is_local)
+{
+ if(path && path[0]) {
+ /* We try to add absolute paths, so that the session key can stay
+ * valid when used in another process with different CWD. However,
+ * when a path does not exist, this does not work. Then, we add
+ * the path as is. */
+#ifdef UNDER_CE
+ (void)is_local;
+ return curlx_dyn_addf(buf, ":%s-%s", name, path);
+#elif defined(_WIN32)
+ char abspath[_MAX_PATH];
+ if(_fullpath(abspath, path, _MAX_PATH))
+ return curlx_dyn_addf(buf, ":%s-%s", name, abspath);
+ *is_local = TRUE;
+#elif defined(HAVE_REALPATH)
+ if(path[0] != '/') {
+ char *abspath = realpath(path, NULL);
+ if(abspath) {
+ CURLcode r = curlx_dyn_addf(buf, ":%s-%s", name, abspath);
+ (free)(abspath); /* allocated by libc, free without memdebug */
+ return r;
+ }
+ *is_local = TRUE;
+ }
+#endif
+ return curlx_dyn_addf(buf, ":%s-%s", name, path);
+ }
+ return CURLE_OK;
+}
+
+static CURLcode cf_ssl_peer_key_add_hash(struct dynbuf *buf,
+ const char *name,
+ struct curl_blob *blob)
+{
+ CURLcode r = CURLE_OK;
+ if(blob && blob->len) {
+ unsigned char hash[CURL_SHA256_DIGEST_LENGTH];
+ size_t i;
+
+ r = curlx_dyn_addf(buf, ":%s-", name);
+ if(r)
+ goto out;
+ r = Curl_sha256it(hash, blob->data, blob->len);
+ if(r)
+ goto out;
+ for(i = 0; i < CURL_SHA256_DIGEST_LENGTH; ++i) {
+ r = curlx_dyn_addf(buf, "%02x", hash[i]);
+ if(r)
+ goto out;
+ }
+ }
+out:
+ return r;
+}
+
+#define CURL_SSLS_LOCAL_SUFFIX ":L"
+#define CURL_SSLS_GLOBAL_SUFFIX ":G"
+
+static bool cf_ssl_peer_key_is_global(const char *peer_key)
+{
+ size_t len = peer_key ? strlen(peer_key) : 0;
+ return (len > 2) &&
+ (peer_key[len - 1] == 'G') &&
+ (peer_key[len - 2] == ':');
+}
+
+CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
+ const struct ssl_peer *peer,
+ const char *tls_id,
+ char **ppeer_key)
+{
+ struct ssl_primary_config *ssl = Curl_ssl_cf_get_primary_config(cf);
+ struct dynbuf buf;
+ size_t key_len;
+ bool is_local = FALSE;
+ CURLcode r;
+
+ *ppeer_key = NULL;
+ curlx_dyn_init(&buf, 10 * 1024);
+
+ r = curlx_dyn_addf(&buf, "%s:%d", peer->hostname, peer->port);
+ if(r)
+ goto out;
+
+ switch(peer->transport) {
+ case TRNSPRT_TCP:
+ break;
+ case TRNSPRT_UDP:
+ r = curlx_dyn_add(&buf, ":UDP");
+ break;
+ case TRNSPRT_QUIC:
+ r = curlx_dyn_add(&buf, ":QUIC");
+ break;
+ case TRNSPRT_UNIX:
+ r = curlx_dyn_add(&buf, ":UNIX");
+ break;
+ default:
+ r = curlx_dyn_addf(&buf, ":TRNSPRT-%d", peer->transport);
+ break;
+ }
+ if(r)
+ goto out;
+
+ if(!ssl->verifypeer) {
+ r = curlx_dyn_add(&buf, ":NO-VRFY-PEER");
+ if(r)
+ goto out;
+ }
+ if(!ssl->verifyhost) {
+ r = curlx_dyn_add(&buf, ":NO-VRFY-HOST");
+ if(r)
+ goto out;
+ }
+ if(ssl->verifystatus) {
+ r = curlx_dyn_add(&buf, ":VRFY-STATUS");
+ if(r)
+ goto out;
+ }
+ if(!ssl->verifypeer || !ssl->verifyhost) {
+ if(cf->conn->bits.conn_to_host) {
+ r = curlx_dyn_addf(&buf, ":CHOST-%s", cf->conn->conn_to_host.name);
+ if(r)
+ goto out;
+ }
+ if(cf->conn->bits.conn_to_port) {
+ r = curlx_dyn_addf(&buf, ":CPORT-%d", cf->conn->conn_to_port);
+ if(r)
+ goto out;
+ }
+ }
+
+ if(ssl->version || ssl->version_max) {
+ r = curlx_dyn_addf(&buf, ":TLSVER-%d-%d", ssl->version,
+ (ssl->version_max >> 16));
+ if(r)
+ goto out;
+ }
+ if(ssl->ssl_options) {
+ r = curlx_dyn_addf(&buf, ":TLSOPT-%x", ssl->ssl_options);
+ if(r)
+ goto out;
+ }
+ if(ssl->cipher_list) {
+ r = curlx_dyn_addf(&buf, ":CIPHER-%s", ssl->cipher_list);
+ if(r)
+ goto out;
+ }
+ if(ssl->cipher_list13) {
+ r = curlx_dyn_addf(&buf, ":CIPHER13-%s", ssl->cipher_list13);
+ if(r)
+ goto out;
+ }
+ if(ssl->curves) {
+ r = curlx_dyn_addf(&buf, ":CURVES-%s", ssl->curves);
+ if(r)
+ goto out;
+ }
+ if(ssl->verifypeer) {
+ r = cf_ssl_peer_key_add_path(&buf, "CA", ssl->CAfile, &is_local);
+ if(r)
+ goto out;
+ r = cf_ssl_peer_key_add_path(&buf, "CApath", ssl->CApath, &is_local);
+ if(r)
+ goto out;
+ r = cf_ssl_peer_key_add_path(&buf, "CRL", ssl->CRLfile, &is_local);
+ if(r)
+ goto out;
+ r = cf_ssl_peer_key_add_path(&buf, "Issuer", ssl->issuercert, &is_local);
+ if(r)
+ goto out;
+ if(ssl->cert_blob) {
+ r = cf_ssl_peer_key_add_hash(&buf, "CertBlob", ssl->cert_blob);
+ if(r)
+ goto out;
+ }
+ if(ssl->ca_info_blob) {
+ r = cf_ssl_peer_key_add_hash(&buf, "CAInfoBlob", ssl->ca_info_blob);
+ if(r)
+ goto out;
+ }
+ if(ssl->issuercert_blob) {
+ r = cf_ssl_peer_key_add_hash(&buf, "IssuerBlob", ssl->issuercert_blob);
+ if(r)
+ goto out;
+ }
+ }
+ if(ssl->pinned_key && ssl->pinned_key[0]) {
+ r = curlx_dyn_addf(&buf, ":Pinned-%s", ssl->pinned_key);
+ if(r)
+ goto out;
+ }
+
+ if(ssl->clientcert && ssl->clientcert[0]) {
+ r = curlx_dyn_add(&buf, ":CCERT");
+ if(r)
+ goto out;
+ }
+#ifdef USE_TLS_SRP
+ if(ssl->username || ssl->password) {
+ r = curlx_dyn_add(&buf, ":SRP-AUTH");
+ if(r)
+ goto out;
+ }
+#endif
+
+ if(!tls_id || !tls_id[0]) {
+ r = CURLE_FAILED_INIT;
+ goto out;
+ }
+ r = curlx_dyn_addf(&buf, ":IMPL-%s", tls_id);
+ if(r)
+ goto out;
+
+ r = curlx_dyn_addf(&buf, is_local ?
+ CURL_SSLS_LOCAL_SUFFIX : CURL_SSLS_GLOBAL_SUFFIX);
+ if(r)
+ goto out;
+
+ *ppeer_key = curlx_dyn_take(&buf, &key_len);
+ /* we just added printable char, and dynbuf always null-terminates, no need
+ * to track length */
+
+out:
+ curlx_dyn_free(&buf);
+ return r;
+}
+
+static bool cf_ssl_scache_match_auth(struct Curl_ssl_scache_peer *peer,
+ struct ssl_primary_config *conn_config)
+{
+ if(!conn_config) {
+ if(peer->clientcert)
+ return FALSE;
+#ifdef USE_TLS_SRP
+ if(peer->srp_username || peer->srp_password)
+ return FALSE;
+#endif
+ return TRUE;
+ }
+ else if(!Curl_safecmp(peer->clientcert, conn_config->clientcert))
+ return FALSE;
+#ifdef USE_TLS_SRP
+ if(Curl_timestrcmp(peer->srp_username, conn_config->username) ||
+ Curl_timestrcmp(peer->srp_password, conn_config->password))
+ return FALSE;
+#endif
+ return TRUE;
+}
+
+static CURLcode
+cf_ssl_find_peer_by_key(struct Curl_easy *data,
+ struct Curl_ssl_scache *scache,
+ const char *ssl_peer_key,
+ struct ssl_primary_config *conn_config,
+ struct Curl_ssl_scache_peer **ppeer)
+{
+ size_t i, peer_key_len = 0;
+ CURLcode result = CURLE_OK;
+
+ *ppeer = NULL;
+ if(!GOOD_SCACHE(scache)) {
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ }
+
+ CURL_TRC_SSLS(data, "find peer slot for %s among %zu slots",
+ ssl_peer_key, scache->peer_count);
+
+ /* check for entries with known peer_key */
+ for(i = 0; scache && i < scache->peer_count; i++) {
+ if(scache->peers[i].ssl_peer_key &&
+ strcasecompare(ssl_peer_key, scache->peers[i].ssl_peer_key) &&
+ cf_ssl_scache_match_auth(&scache->peers[i], conn_config)) {
+ /* yes, we have a cached session for this! */
+ *ppeer = &scache->peers[i];
+ goto out;
+ }
+ }
+ /* check for entries with HMAC set but no known peer_key */
+ for(i = 0; scache && i < scache->peer_count; i++) {
+ if(!scache->peers[i].ssl_peer_key &&
+ scache->peers[i].hmac_set &&
+ cf_ssl_scache_match_auth(&scache->peers[i], conn_config)) {
+ /* possible entry with unknown peer_key, check hmac */
+ unsigned char my_hmac[CURL_SHA256_DIGEST_LENGTH];
+ if(!peer_key_len) /* we are lazy */
+ peer_key_len = strlen(ssl_peer_key);
+ result = Curl_hmacit(&Curl_HMAC_SHA256,
+ scache->peers[i].key_salt,
+ sizeof(scache->peers[i].key_salt),
+ (const unsigned char *)ssl_peer_key,
+ peer_key_len,
+ my_hmac);
+ if(result)
+ goto out;
+ if(!memcmp(scache->peers[i].key_hmac, my_hmac, sizeof(my_hmac))) {
+ /* remember peer_key for future lookups */
+ CURL_TRC_SSLS(data, "peer entry %zu key recovered: %s",
+ i, ssl_peer_key);
+ scache->peers[i].ssl_peer_key = strdup(ssl_peer_key);
+ if(!scache->peers[i].ssl_peer_key) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+ cf_ssl_cache_peer_update(&scache->peers[i]);
+ *ppeer = &scache->peers[i];
+ goto out;
+ }
+ }
+ }
+ CURL_TRC_SSLS(data, "peer not found for %s", ssl_peer_key);
+out:
+ return result;
+}
+
+static struct Curl_ssl_scache_peer *
+cf_ssl_get_free_peer(struct Curl_ssl_scache *scache)
+{
+ struct Curl_ssl_scache_peer *peer = NULL;
+ size_t i;
+
+ /* find empty or oldest peer */
+ for(i = 0; i < scache->peer_count; ++i) {
+ /* free peer entry? */
+ if(!scache->peers[i].ssl_peer_key && !scache->peers[i].hmac_set) {
+ peer = &scache->peers[i];
+ break;
+ }
+ /* peer without sessions and obj */
+ if(!scache->peers[i].sobj &&
+ !Curl_llist_count(&scache->peers[i].sessions)) {
+ peer = &scache->peers[i];
+ break;
+ }
+ /* remember "oldest" peer */
+ if(!peer || (scache->peers[i].age < peer->age)) {
+ peer = &scache->peers[i];
+ }
+ }
+ DEBUGASSERT(peer);
+ if(peer)
+ cf_ssl_scache_clear_peer(peer);
+ return peer;
+}
+
+static CURLcode
+cf_ssl_add_peer(struct Curl_easy *data,
+ struct Curl_ssl_scache *scache,
+ const char *ssl_peer_key,
+ struct ssl_primary_config *conn_config,
+ struct Curl_ssl_scache_peer **ppeer)
+{
+ struct Curl_ssl_scache_peer *peer = NULL;
+ CURLcode result = CURLE_OK;
+
+ *ppeer = NULL;
+ if(ssl_peer_key) {
+ result = cf_ssl_find_peer_by_key(data, scache, ssl_peer_key, conn_config,
+ &peer);
+ if(result || !scache->peer_count)
+ return result;
+ }
+
+ if(peer) {
+ *ppeer = peer;
+ return CURLE_OK;
+ }
+
+ peer = cf_ssl_get_free_peer(scache);
+ if(peer) {
+ const char *ccert = conn_config ? conn_config->clientcert : NULL;
+ const char *username = NULL, *password = NULL;
+#ifdef USE_TLS_SRP
+ username = conn_config ? conn_config->username : NULL;
+ password = conn_config ? conn_config->password : NULL;
+#endif
+ result = cf_ssl_scache_peer_init(peer, ssl_peer_key, ccert,
+ username, password, NULL, NULL);
+ if(result)
+ goto out;
+ /* all ready */
+ *ppeer = peer;
+ result = CURLE_OK;
+ }
+
+out:
+ if(result) {
+ cf_ssl_scache_clear_peer(peer);
+ }
+ return result;
+}
+
+static void cf_scache_peer_add_session(struct Curl_ssl_scache_peer *peer,
+ struct Curl_ssl_session *s,
+ curl_off_t now)
+{
+ /* A session not from TLSv1.3 replaces all other. */
+ if(s->ietf_tls_id != CURL_IETF_PROTO_TLS1_3) {
+ Curl_llist_destroy(&peer->sessions, NULL);
+ Curl_llist_append(&peer->sessions, s, &s->list);
+ }
+ else {
+ /* Expire existing, append, trim from head to obey max_sessions */
+ cf_scache_peer_remove_expired(peer, now);
+ cf_scache_peer_remove_non13(peer);
+ Curl_llist_append(&peer->sessions, s, &s->list);
+ while(Curl_llist_count(&peer->sessions) > peer->max_sessions) {
+ Curl_node_remove(Curl_llist_head(&peer->sessions));
+ }
+ }
+}
+
+static CURLcode cf_scache_add_session(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct Curl_ssl_scache *scache,
+ const char *ssl_peer_key,
+ struct Curl_ssl_session *s)
+{
+ struct Curl_ssl_scache_peer *peer = NULL;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ CURLcode result = CURLE_OUT_OF_MEMORY;
+ curl_off_t now = (curl_off_t)time(NULL);
+ curl_off_t max_lifetime;
+
+ if(!scache || !scache->peer_count) {
+ Curl_ssl_session_destroy(s);
+ return CURLE_OK;
+ }
+
+ if(s->valid_until <= 0)
+ s->valid_until = now + scache->default_lifetime_secs;
+
+ max_lifetime = (s->ietf_tls_id == CURL_IETF_PROTO_TLS1_3) ?
+ CURL_SCACHE_MAX_13_LIFETIME_SEC :
+ CURL_SCACHE_MAX_12_LIFETIME_SEC;
+ if(s->valid_until > (now + max_lifetime))
+ s->valid_until = now + max_lifetime;
+
+ if(cf_scache_session_expired(s, now)) {
+ CURL_TRC_SSLS(data, "add, session already expired");
+ Curl_ssl_session_destroy(s);
+ return CURLE_OK;
+ }
+
+ result = cf_ssl_add_peer(data, scache, ssl_peer_key, conn_config, &peer);
+ if(result || !peer) {
+ CURL_TRC_SSLS(data, "unable to add scache peer: %d", result);
+ Curl_ssl_session_destroy(s);
+ goto out;
+ }
+
+ cf_scache_peer_add_session(peer, s, now);
+
+out:
+ if(result) {
+ failf(data, "[SCACHE] failed to add session for %s, error=%d",
+ ssl_peer_key, result);
+ }
+ else
+ CURL_TRC_SSLS(data, "added session for %s [proto=0x%x, "
+ "valid_secs=%" FMT_OFF_T ", alpn=%s, earlydata=%zu, "
+ "quic_tp=%s], peer has %zu sessions now",
+ ssl_peer_key, s->ietf_tls_id, s->valid_until - now,
+ s->alpn, s->earlydata_max, s->quic_tp ? "yes" : "no",
+ peer ? Curl_llist_count(&peer->sessions) : 0);
+ return result;
+}
+
+CURLcode Curl_ssl_scache_put(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ struct Curl_ssl_session *s)
+{
+ struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ CURLcode result;
+ DEBUGASSERT(ssl_config);
+
+ if(!scache || !ssl_config->primary.cache_session) {
+ Curl_ssl_session_destroy(s);
+ return CURLE_OK;
+ }
+
+ Curl_ssl_scache_lock(data);
+ result = cf_scache_add_session(cf, data, scache, ssl_peer_key, s);
+ Curl_ssl_scache_unlock(data);
+ return result;
+}
+
+void Curl_ssl_scache_return(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ struct Curl_ssl_session *s)
+{
+ /* See RFC 8446 C.4:
+ * "Clients SHOULD NOT reuse a ticket for multiple connections." */
+ if(s && s->ietf_tls_id < 0x304)
+ (void)Curl_ssl_scache_put(cf, data, ssl_peer_key, s);
+ else
+ Curl_ssl_session_destroy(s);
+}
+
+CURLcode Curl_ssl_scache_take(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ struct Curl_ssl_session **ps)
+{
+ struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct Curl_ssl_scache_peer *peer = NULL;
+ struct Curl_llist_node *n;
+ struct Curl_ssl_session *s = NULL;
+ CURLcode result;
+
+ *ps = NULL;
+ if(!scache)
+ return CURLE_OK;
+
+ Curl_ssl_scache_lock(data);
+ result = cf_ssl_find_peer_by_key(data, scache, ssl_peer_key, conn_config,
+ &peer);
+ if(!result && peer) {
+ cf_scache_peer_remove_expired(peer, (curl_off_t)time(NULL));
+ n = Curl_llist_head(&peer->sessions);
+ if(n) {
+ s = Curl_node_take_elem(n);
+ (scache->age)++; /* increase general age */
+ peer->age = scache->age; /* set this as used in this age */
+ }
+ }
+ Curl_ssl_scache_unlock(data);
+ if(s) {
+ *ps = s;
+ CURL_TRC_SSLS(data, "took session for %s [proto=0x%x, "
+ "alpn=%s, earlydata=%zu, quic_tp=%s], %zu sessions remain",
+ ssl_peer_key, s->ietf_tls_id, s->alpn,
+ s->earlydata_max, s->quic_tp ? "yes" : "no",
+ Curl_llist_count(&peer->sessions));
+ }
+ else {
+ CURL_TRC_SSLS(data, "no cached session for %s", ssl_peer_key);
+ }
+ return result;
+}
+
+CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ void *sobj,
+ Curl_ssl_scache_obj_dtor *sobj_free)
+{
+ struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct Curl_ssl_scache_peer *peer = NULL;
+ CURLcode result;
+
+ DEBUGASSERT(sobj);
+ DEBUGASSERT(sobj_free);
+
+ if(!scache) {
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
+ }
+
+ result = cf_ssl_add_peer(data, scache, ssl_peer_key, conn_config, &peer);
+ if(result || !peer) {
+ CURL_TRC_SSLS(data, "unable to add scache peer: %d", result);
+ goto out;
+ }
+
+ cf_ssl_scache_peer_set_obj(peer, sobj, sobj_free);
+ sobj = NULL; /* peer took ownership */
+
+out:
+ if(sobj && sobj_free)
+ sobj_free(sobj);
+ return result;
+}
+
+void *Curl_ssl_scache_get_obj(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key)
+{
+ struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct Curl_ssl_scache_peer *peer = NULL;
+ CURLcode result;
+ void *sobj;
+
+ if(!scache)
+ return NULL;
+
+ result = cf_ssl_find_peer_by_key(data, scache, ssl_peer_key, conn_config,
+ &peer);
+ if(result)
+ return NULL;
+
+ sobj = peer ? peer->sobj : NULL;
+
+ CURL_TRC_SSLS(data, "%s cached session for '%s'",
+ sobj ? "Found" : "No", ssl_peer_key);
+ return sobj;
+}
+
+void Curl_ssl_scache_remove_all(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key)
+{
+ struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ struct Curl_ssl_scache_peer *peer = NULL;
+ CURLcode result;
+
+ (void)cf;
+ if(!scache)
+ return;
+
+ Curl_ssl_scache_lock(data);
+ result = cf_ssl_find_peer_by_key(data, scache, ssl_peer_key, conn_config,
+ &peer);
+ if(!result && peer)
+ cf_ssl_scache_clear_peer(peer);
+ Curl_ssl_scache_unlock(data);
+}
+
+#ifdef USE_SSLS_EXPORT
+
+#define CURL_SSL_TICKET_MAX (16*1024)
+
+static CURLcode cf_ssl_scache_peer_set_hmac(struct Curl_ssl_scache_peer *peer)
+{
+ CURLcode result;
+
+ DEBUGASSERT(peer);
+ if(!peer->ssl_peer_key)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ result = Curl_rand(NULL, peer->key_salt, sizeof(peer->key_salt));
+ if(result)
+ return result;
+
+ result = Curl_hmacit(&Curl_HMAC_SHA256,
+ peer->key_salt, sizeof(peer->key_salt),
+ (const unsigned char *)peer->ssl_peer_key,
+ strlen(peer->ssl_peer_key),
+ peer->key_hmac);
+ if(!result)
+ peer->hmac_set = TRUE;
+ return result;
+}
+
+static CURLcode
+cf_ssl_find_peer_by_hmac(struct Curl_ssl_scache *scache,
+ const unsigned char *salt,
+ const unsigned char *hmac,
+ struct Curl_ssl_scache_peer **ppeer)
+{
+ size_t i;
+ CURLcode result = CURLE_OK;
+
+ *ppeer = NULL;
+ if(!GOOD_SCACHE(scache))
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ /* look for an entry that matches salt+hmac exactly or has a known
+ * ssl_peer_key which salt+hmac's to the same. */
+ for(i = 0; scache && i < scache->peer_count; i++) {
+ struct Curl_ssl_scache_peer *peer = &scache->peers[i];
+ if(!cf_ssl_scache_match_auth(peer, NULL))
+ continue;
+ if(scache->peers[i].hmac_set &&
+ !memcmp(peer->key_salt, salt, sizeof(peer->key_salt)) &&
+ !memcmp(peer->key_hmac, hmac, sizeof(peer->key_hmac))) {
+ /* found exact match, return */
+ *ppeer = peer;
+ goto out;
+ }
+ else if(peer->ssl_peer_key) {
+ unsigned char my_hmac[CURL_SHA256_DIGEST_LENGTH];
+ /* compute hmac for the passed salt */
+ result = Curl_hmacit(&Curl_HMAC_SHA256,
+ salt, sizeof(peer->key_salt),
+ (const unsigned char *)peer->ssl_peer_key,
+ strlen(peer->ssl_peer_key),
+ my_hmac);
+ if(result)
+ goto out;
+ if(!memcmp(my_hmac, hmac, sizeof(my_hmac))) {
+ /* cryptohash match, take over salt+hmac if no set and return */
+ if(!peer->hmac_set) {
+ memcpy(peer->key_salt, salt, sizeof(peer->key_salt));
+ memcpy(peer->key_hmac, hmac, sizeof(peer->key_hmac));
+ peer->hmac_set = TRUE;
+ }
+ *ppeer = peer;
+ goto out;
+ }
+ }
+ }
+out:
+ return result;
+}
+
+CURLcode Curl_ssl_session_import(struct Curl_easy *data,
+ const char *ssl_peer_key,
+ const unsigned char *shmac, size_t shmac_len,
+ const void *sdata, size_t sdata_len)
+{
+ struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
+ struct Curl_ssl_scache_peer *peer = NULL;
+ struct Curl_ssl_session *s = NULL;
+ bool locked = FALSE;
+ CURLcode r;
+
+ if(!scache) {
+ r = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
+ }
+ if(!ssl_peer_key && (!shmac || !shmac_len)) {
+ r = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
+ }
+
+ r = Curl_ssl_session_unpack(data, sdata, sdata_len, &s);
+ if(r)
+ goto out;
+
+ Curl_ssl_scache_lock(data);
+ locked = TRUE;
+
+ if(ssl_peer_key) {
+ r = cf_ssl_add_peer(data, scache, ssl_peer_key, NULL, &peer);
+ if(r)
+ goto out;
+ }
+ else if(shmac_len != (sizeof(peer->key_salt) + sizeof(peer->key_hmac))) {
+ /* Either salt+hmac was garbled by caller or is from a curl version
+ * that does things differently */
+ r = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
+ }
+ else {
+ const unsigned char *salt = shmac;
+ const unsigned char *hmac = shmac + sizeof(peer->key_salt);
+
+ r = cf_ssl_find_peer_by_hmac(scache, salt, hmac, &peer);
+ if(r)
+ goto out;
+ if(!peer) {
+ peer = cf_ssl_get_free_peer(scache);
+ if(peer) {
+ r = cf_ssl_scache_peer_init(peer, ssl_peer_key, NULL,
+ NULL, NULL, salt, hmac);
+ if(r)
+ goto out;
+ }
+ }
+ }
+
+ if(peer) {
+ cf_scache_peer_add_session(peer, s, time(NULL));
+ s = NULL; /* peer is now owner */
+ CURL_TRC_SSLS(data, "successfully imported ticket for peer %s, now "
+ "with %zu tickets",
+ peer->ssl_peer_key ? peer->ssl_peer_key : "without key",
+ Curl_llist_count(&peer->sessions));
+ }
+
+out:
+ if(locked)
+ Curl_ssl_scache_unlock(data);
+ Curl_ssl_session_destroy(s);
+ return r;
+}
+
+CURLcode Curl_ssl_session_export(struct Curl_easy *data,
+ curl_ssls_export_cb *export_fn,
+ void *userptr)
+{
+ struct Curl_ssl_scache *scache = cf_ssl_scache_get(data);
+ struct Curl_ssl_scache_peer *peer;
+ struct dynbuf sbuf, hbuf;
+ struct Curl_llist_node *n;
+ size_t i, npeers = 0, ntickets = 0;
+ curl_off_t now = time(NULL);
+ CURLcode r = CURLE_OK;
+
+ if(!export_fn)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ if(!scache)
+ return CURLE_OK;
+
+ Curl_ssl_scache_lock(data);
+
+ curlx_dyn_init(&hbuf, (CURL_SHA256_DIGEST_LENGTH * 2) + 1);
+ curlx_dyn_init(&sbuf, CURL_SSL_TICKET_MAX);
+
+ for(i = 0; scache && i < scache->peer_count; i++) {
+ peer = &scache->peers[i];
+ if(!peer->ssl_peer_key && !peer->hmac_set)
+ continue; /* skip free entry */
+ if(!peer->exportable)
+ continue;
+
+ curlx_dyn_reset(&hbuf);
+ cf_scache_peer_remove_expired(peer, now);
+ n = Curl_llist_head(&peer->sessions);
+ if(n)
+ ++npeers;
+ while(n) {
+ struct Curl_ssl_session *s = Curl_node_elem(n);
+ if(!peer->hmac_set) {
+ r = cf_ssl_scache_peer_set_hmac(peer);
+ if(r)
+ goto out;
+ }
+ if(!curlx_dyn_len(&hbuf)) {
+ r = curlx_dyn_addn(&hbuf, peer->key_salt, sizeof(peer->key_salt));
+ if(r)
+ goto out;
+ r = curlx_dyn_addn(&hbuf, peer->key_hmac, sizeof(peer->key_hmac));
+ if(r)
+ goto out;
+ }
+ curlx_dyn_reset(&sbuf);
+ r = Curl_ssl_session_pack(data, s, &sbuf);
+ if(r)
+ goto out;
+
+ r = export_fn(data, userptr, peer->ssl_peer_key,
+ curlx_dyn_uptr(&hbuf), curlx_dyn_len(&hbuf),
+ curlx_dyn_uptr(&sbuf), curlx_dyn_len(&sbuf),
+ s->valid_until, s->ietf_tls_id,
+ s->alpn, s->earlydata_max);
+ if(r)
+ goto out;
+ ++ntickets;
+ n = Curl_node_next(n);
+ }
+
+ }
+ r = CURLE_OK;
+ CURL_TRC_SSLS(data, "exported %zu session tickets for %zu peers",
+ ntickets, npeers);
+
+out:
+ Curl_ssl_scache_unlock(data);
+ curlx_dyn_free(&hbuf);
+ curlx_dyn_free(&sbuf);
+ return r;
+}
+
+#endif /* USE_SSLS_EXPORT */
+
+#endif /* USE_SSL */
diff --git a/libs/libcurl/src/vtls/vtls_scache.h b/libs/libcurl/src/vtls/vtls_scache.h
new file mode 100644
index 0000000000..3f2a3978c8
--- /dev/null
+++ b/libs/libcurl/src/vtls/vtls_scache.h
@@ -0,0 +1,217 @@
+#ifndef HEADER_CURL_VTLS_SCACHE_H
+#define HEADER_CURL_VTLS_SCACHE_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "../curl_setup.h"
+#include "../cfilters.h"
+#include "../urldata.h"
+
+#ifdef USE_SSL
+
+struct Curl_cfilter;
+struct Curl_easy;
+struct Curl_ssl_scache;
+struct Curl_ssl_session;
+struct ssl_peer;
+
+/* RFC 8446 (TLSv1.3) restrict lifetime to one week max, for
+ * other, less secure versions, we restrict it to a day */
+#define CURL_SCACHE_MAX_13_LIFETIME_SEC (60*60*24*7)
+#define CURL_SCACHE_MAX_12_LIFETIME_SEC (60*60*24)
+
+/* Create a session cache for up to max_peers endpoints with a total
+ * of up to max_sessions SSL sessions per peer */
+CURLcode Curl_ssl_scache_create(size_t max_peers,
+ size_t max_sessions_per_peer,
+ struct Curl_ssl_scache **pscache);
+
+void Curl_ssl_scache_destroy(struct Curl_ssl_scache *scache);
+
+/* Create a key from peer and TLS configuration information that is
+ * unique for how the connection filter wants to establish a TLS
+ * connection to the peer.
+ * If the filter is a TLS proxy filter, it will use the proxy relevant
+ * information.
+ * @param cf the connection filter wanting to use it
+ * @param peer the peer the filter wants to talk to
+ * @param tls_id identifier of TLS implementation for sessions. Should
+ * include full version if session data from other versions
+ * is to be avoided.
+ * @param ppeer_key on successful return, the key generated
+ */
+CURLcode Curl_ssl_peer_key_make(struct Curl_cfilter *cf,
+ const struct ssl_peer *peer,
+ const char *tls_id,
+ char **ppeer_key);
+
+/* Lock session cache mutex.
+ * Call this before calling other Curl_ssl_*session* functions
+ * Caller should unlock this mutex as soon as possible, as it may block
+ * other SSL connection from making progress.
+ * The purpose of explicitly locking SSL session cache data is to allow
+ * individual SSL engines to manage session lifetime in their specific way.
+ */
+void Curl_ssl_scache_lock(struct Curl_easy *data);
+
+/* Unlock session cache mutex */
+void Curl_ssl_scache_unlock(struct Curl_easy *data);
+
+/* Get TLS session object from the cache for the ssl_peer_ey.
+ * scache mutex must be locked (see Curl_ssl_scache_lock).
+ * Caller must make sure that the ownership of returned session object
+ * is properly taken (e.g. its refcount is incremented
+ * under scache mutex).
+ * @param cf the connection filter wanting to use it
+ * @param data the transfer involved
+ * @param ssl_peer_key the key for lookup
+ * @retval sobj the object for the peer key or NULL
+ */
+void *Curl_ssl_scache_get_obj(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key);
+
+typedef void Curl_ssl_scache_obj_dtor(void *sobj);
+
+/* Add a TLS session related object to the cache.
+ * Replaces an existing object with the same peer_key.
+ * scache mutex must be locked (see Curl_ssl_scache_lock).
+ * Call takes ownership of `sobj`, using `sobj_dtor_cb`
+ * to deallocate it. Is called in all outcomes, either right away or
+ * later when the session cache is cleaned up.
+ * Caller must ensure that it has properly shared ownership of `sobj`
+ * with cache (e.g. incrementing refcount on success)
+ * @param cf the connection filter wanting to use it
+ * @param data the transfer involved
+ * @param ssl_peer_key the key for lookup
+ * @param sobj the TLS session object
+ * @param sobj_free_cb callback to free the session objectt
+ */
+CURLcode Curl_ssl_scache_add_obj(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ void *sobj,
+ Curl_ssl_scache_obj_dtor *sobj_dtor_cb);
+
+/* All about an SSL session ticket */
+struct Curl_ssl_session {
+ const void *sdata; /* session ticket data, plain bytes */
+ size_t sdata_len; /* number of bytes in sdata */
+ curl_off_t valid_until; /* seconds since EPOCH until ticket expires */
+ int ietf_tls_id; /* TLS protocol identifier negotiated */
+ char *alpn; /* APLN TLS negotiated protocol string */
+ size_t earlydata_max; /* max 0-RTT data supported by peer */
+ const unsigned char *quic_tp; /* Optional QUIC transport param bytes */
+ size_t quic_tp_len; /* number of bytes in quic_tp */
+ struct Curl_llist_node list; /* internal storage handling */
+};
+
+/* Create a `session` instance. Does NOT need locking.
+ * Takes ownership of `sdata` and `sobj` regardless of return code.
+ * @param sdata bytes of SSL session data or NULL (sobj then required)
+ * @param sdata_len amount of session data bytes
+ * @param ietf_tls_id IETF protocol version, e.g. 0x304 for TLSv1.3
+ * @param alpn ALPN protocol selected or NULL
+ * @param valid_until seconds since EPOCH when session expires, pass 0
+ * in case this is not known.
+ * @param psession on return the scached session instance created
+ */
+CURLcode
+Curl_ssl_session_create(void *sdata, size_t sdata_len,
+ int ietf_tls_id, const char *alpn,
+ curl_off_t valid_until,
+ size_t earlydata_max,
+ struct Curl_ssl_session **psession);
+
+/* Variation of session creation with quic transport parameter bytes,
+ * Takes ownership of `quic_tp` regardless of return code. */
+CURLcode
+Curl_ssl_session_create2(void *sdata, size_t sdata_len,
+ int ietf_tls_id, const char *alpn,
+ curl_off_t valid_until,
+ size_t earlydata_max,
+ unsigned char *quic_tp, size_t quic_tp_len,
+ struct Curl_ssl_session **psession);
+
+/* Destroy a `session` instance. Can be called with NULL.
+ * Does NOT need locking. */
+void Curl_ssl_session_destroy(struct Curl_ssl_session *s);
+
+/* Put the scache session into the cache. Does NOT need locking.
+ * Call takes ownership of `s` in all outcomes.
+ * @param cf the connection filter wanting to use it
+ * @param data the transfer involved
+ * @param ssl_peer_key the key for lookup
+ * @param s the scache session object
+ */
+CURLcode Curl_ssl_scache_put(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ struct Curl_ssl_session *s);
+
+/* Take a matching scache session from the cache. Does NOT need locking.
+ * @param cf the connection filter wanting to use it
+ * @param data the transfer involved
+ * @param ssl_peer_key the key for lookup
+ * @param s on return, the scache session object or NULL
+ */
+CURLcode Curl_ssl_scache_take(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ struct Curl_ssl_session **ps);
+
+/* Return a taken scache session to the cache. Does NOT need locking.
+ * Depending on TLS version and other criteria, it may cache it again
+ * or destroy it. Maybe called with a NULL session.
+ */
+void Curl_ssl_scache_return(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ struct Curl_ssl_session *s);
+
+/* Remove all sessions and obj for the peer_key. Does NOT need locking. */
+void Curl_ssl_scache_remove_all(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key);
+
+#ifdef USE_SSLS_EXPORT
+
+CURLcode Curl_ssl_session_import(struct Curl_easy *data,
+ const char *ssl_peer_key,
+ const unsigned char *shmac, size_t shmac_len,
+ const void *sdata, size_t sdata_len);
+
+CURLcode Curl_ssl_session_export(struct Curl_easy *data,
+ curl_ssls_export_cb *export_fn,
+ void *userptr);
+
+#endif /* USE_SSLS_EXPORT */
+
+#else /* USE_SSL */
+
+#define Curl_ssl_scache_create(x,y,z) ((void)x, CURLE_OK)
+#define Curl_ssl_scache_destroy(x) do {} while(0)
+
+#endif /* USE_SSL (else) */
+
+#endif /* HEADER_CURL_VTLS_SCACHE_H */
diff --git a/libs/libcurl/src/vtls/vtls_spack.c b/libs/libcurl/src/vtls/vtls_spack.c
new file mode 100644
index 0000000000..39555f226b
--- /dev/null
+++ b/libs/libcurl/src/vtls/vtls_spack.c
@@ -0,0 +1,346 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+
+#include "../curl_setup.h"
+
+#ifdef USE_SSLS_EXPORT
+
+#include "../urldata.h"
+#include "../curl_trc.h"
+#include "vtls_scache.h"
+#include "vtls_spack.h"
+#include "../strdup.h"
+
+/* The last #include files should be: */
+#include "../curl_memory.h"
+#include "../memdebug.h"
+
+#ifdef _MSC_VER
+#if _MSC_VER >= 1600
+#include <stdint.h>
+#else
+typedef unsigned char uint8_t;
+typedef unsigned __int16 uint16_t;
+typedef unsigned __int32 uint32_t;
+typedef unsigned __int64 uint64_t;
+#endif
+#endif /* _MSC_VER */
+
+#ifndef UINT16_MAX
+#define UINT16_MAX 0xffff
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX 0xffffffff
+#endif
+
+#define CURL_SPACK_VERSION 0x01
+#define CURL_SPACK_IETF_ID 0x02
+#define CURL_SPACK_VALID_UNTIL 0x03
+#define CURL_SPACK_TICKET 0x04
+#define CURL_SPACK_ALPN 0x05
+#define CURL_SPACK_EARLYDATA 0x06
+#define CURL_SPACK_QUICTP 0x07
+
+static CURLcode spack_enc8(struct dynbuf *buf, uint8_t b)
+{
+ return curlx_dyn_addn(buf, &b, 1);
+}
+
+static CURLcode
+spack_dec8(uint8_t *val, const uint8_t **src, const uint8_t *end)
+{
+ if(end - *src < 1)
+ return CURLE_READ_ERROR;
+ *val = **src;
+ *src += 1;
+ return CURLE_OK;
+}
+
+static CURLcode spack_enc16(struct dynbuf *buf, uint16_t val)
+{
+ uint8_t nval[2];
+ nval[0] = (uint8_t)(val >> 8);
+ nval[1] = (uint8_t)val;
+ return curlx_dyn_addn(buf, nval, sizeof(nval));
+}
+
+static CURLcode
+spack_dec16(uint16_t *val, const uint8_t **src, const uint8_t *end)
+{
+ if(end - *src < 2)
+ return CURLE_READ_ERROR;
+ *val = (uint16_t)((*src)[0] << 8 | (*src)[1]);
+ *src += 2;
+ return CURLE_OK;
+}
+
+static CURLcode spack_enc32(struct dynbuf *buf, uint32_t val)
+{
+ uint8_t nval[4];
+ nval[0] = (uint8_t)(val >> 24);
+ nval[1] = (uint8_t)(val >> 16);
+ nval[2] = (uint8_t)(val >> 8);
+ nval[3] = (uint8_t)val;
+ return curlx_dyn_addn(buf, nval, sizeof(nval));
+}
+
+static CURLcode
+spack_dec32(uint32_t *val, const uint8_t **src, const uint8_t *end)
+{
+ if(end - *src < 4)
+ return CURLE_READ_ERROR;
+ *val = (uint32_t)(*src)[0] << 24 | (uint32_t)(*src)[1] << 16 |
+ (uint32_t)(*src)[2] << 8 | (*src)[3];
+ *src += 4;
+ return CURLE_OK;
+}
+
+static CURLcode spack_enc64(struct dynbuf *buf, uint64_t val)
+{
+ uint8_t nval[8];
+ nval[0] = (uint8_t)(val >> 56);
+ nval[1] = (uint8_t)(val >> 48);
+ nval[2] = (uint8_t)(val >> 40);
+ nval[3] = (uint8_t)(val >> 32); \
+ nval[4] = (uint8_t)(val >> 24);
+ nval[5] = (uint8_t)(val >> 16);
+ nval[6] = (uint8_t)(val >> 8);
+ nval[7] = (uint8_t)val;
+ return curlx_dyn_addn(buf, nval, sizeof(nval));
+}
+
+static CURLcode
+spack_dec64(uint64_t *val, const uint8_t **src, const uint8_t *end)
+{
+ if(end - *src < 8)
+ return CURLE_READ_ERROR;
+ *val = (uint64_t)(*src)[0] << 56 | (uint64_t)(*src)[1] << 48 |
+ (uint64_t)(*src)[2] << 40 | (uint64_t)(*src)[3] << 32 |
+ (uint64_t)(*src)[4] << 24 | (uint64_t)(*src)[5] << 16 |
+ (uint64_t)(*src)[6] << 8 | (*src)[7];
+ *src += 8;
+ return CURLE_OK;
+}
+
+static CURLcode spack_encstr16(struct dynbuf *buf, const char *s)
+{
+ size_t slen = strlen(s);
+ CURLcode r;
+ if(slen > UINT16_MAX)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ r = spack_enc16(buf, (uint16_t)slen);
+ if(!r) {
+ r = curlx_dyn_addn(buf, s, slen);
+ }
+ return r;
+}
+
+static CURLcode
+spack_decstr16(char **val, const uint8_t **src, const uint8_t *end)
+{
+ uint16_t slen;
+ CURLcode r;
+
+ *val = NULL;
+ r = spack_dec16(&slen, src, end);
+ if(r)
+ return r;
+ if(end - *src < slen)
+ return CURLE_READ_ERROR;
+ *val = Curl_memdup0((const char *)(*src), slen);
+ *src += slen;
+ return *val ? CURLE_OK : CURLE_OUT_OF_MEMORY;
+}
+
+static CURLcode spack_encdata16(struct dynbuf *buf,
+ const uint8_t *data, size_t data_len)
+{
+ CURLcode r;
+ if(data_len > UINT16_MAX)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+ r = spack_enc16(buf, (uint16_t)data_len);
+ if(!r) {
+ r = curlx_dyn_addn(buf, data, data_len);
+ }
+ return r;
+}
+
+static CURLcode
+spack_decdata16(uint8_t **val, size_t *val_len,
+ const uint8_t **src, const uint8_t *end)
+{
+ uint16_t data_len;
+ CURLcode r;
+
+ *val = NULL;
+ r = spack_dec16(&data_len, src, end);
+ if(r)
+ return r;
+ if(end - *src < data_len)
+ return CURLE_READ_ERROR;
+ *val = Curl_memdup0((const char *)(*src), data_len);
+ *val_len = data_len;
+ *src += data_len;
+ return *val ? CURLE_OK : CURLE_OUT_OF_MEMORY;
+}
+
+CURLcode Curl_ssl_session_pack(struct Curl_easy *data,
+ struct Curl_ssl_session *s,
+ struct dynbuf *buf)
+{
+ CURLcode r;
+ DEBUGASSERT(s->sdata);
+ DEBUGASSERT(s->sdata_len);
+
+ if(s->valid_until < 0)
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+
+ r = spack_enc8(buf, CURL_SPACK_VERSION);
+ if(!r)
+ r = spack_enc8(buf, CURL_SPACK_TICKET);
+ if(!r)
+ r = spack_encdata16(buf, s->sdata, s->sdata_len);
+ if(!r)
+ r = spack_enc8(buf, CURL_SPACK_IETF_ID);
+ if(!r)
+ r = spack_enc16(buf, (uint16_t)s->ietf_tls_id);
+ if(!r)
+ r = spack_enc8(buf, CURL_SPACK_VALID_UNTIL);
+ if(!r)
+ r = spack_enc64(buf, (uint64_t)s->valid_until);
+ if(!r && s->alpn) {
+ r = spack_enc8(buf, CURL_SPACK_ALPN);
+ if(!r)
+ r = spack_encstr16(buf, s->alpn);
+ }
+ if(!r && s->earlydata_max) {
+ if(s->earlydata_max > UINT32_MAX)
+ r = CURLE_BAD_FUNCTION_ARGUMENT;
+ if(!r)
+ r = spack_enc8(buf, CURL_SPACK_EARLYDATA);
+ if(!r)
+ r = spack_enc32(buf, (uint32_t)s->earlydata_max);
+ }
+ if(!r && s->quic_tp && s->quic_tp_len) {
+ r = spack_enc8(buf, CURL_SPACK_QUICTP);
+ if(!r)
+ r = spack_encdata16(buf, s->quic_tp, s->quic_tp_len);
+ }
+
+ if(r)
+ CURL_TRC_SSLS(data, "error packing data: %d", r);
+ return r;
+}
+
+CURLcode Curl_ssl_session_unpack(struct Curl_easy *data,
+ const void *bufv, size_t buflen,
+ struct Curl_ssl_session **ps)
+{
+ struct Curl_ssl_session *s = NULL;
+ const unsigned char *buf = (const unsigned char *)bufv;
+ const unsigned char *end = buf + buflen;
+ uint8_t val8, *pval8;
+ uint16_t val16;
+ uint32_t val32;
+ uint64_t val64;
+ CURLcode r;
+
+ DEBUGASSERT(buf);
+ DEBUGASSERT(buflen);
+ *ps = NULL;
+
+ r = spack_dec8(&val8, &buf, end);
+ if(r)
+ goto out;
+ if(val8 != CURL_SPACK_VERSION) {
+ r = CURLE_READ_ERROR;
+ goto out;
+ }
+
+ s = calloc(1, sizeof(*s));
+ if(!s) {
+ r = CURLE_OUT_OF_MEMORY;
+ goto out;
+ }
+
+ while(buf < end) {
+ r = spack_dec8(&val8, &buf, end);
+ if(r)
+ goto out;
+
+ switch(val8) {
+ case CURL_SPACK_ALPN:
+ r = spack_decstr16(&s->alpn, &buf, end);
+ if(r)
+ goto out;
+ break;
+ case CURL_SPACK_EARLYDATA:
+ r = spack_dec32(&val32, &buf, end);
+ if(r)
+ goto out;
+ s->earlydata_max = val32;
+ break;
+ case CURL_SPACK_IETF_ID:
+ r = spack_dec16(&val16, &buf, end);
+ if(r)
+ goto out;
+ s->ietf_tls_id = val16;
+ break;
+ case CURL_SPACK_QUICTP: {
+ r = spack_decdata16(&pval8, &s->quic_tp_len, &buf, end);
+ if(r)
+ goto out;
+ s->quic_tp = pval8;
+ break;
+ }
+ case CURL_SPACK_TICKET: {
+ r = spack_decdata16(&pval8, &s->sdata_len, &buf, end);
+ if(r)
+ goto out;
+ s->sdata = pval8;
+ break;
+ }
+ case CURL_SPACK_VALID_UNTIL:
+ r = spack_dec64(&val64, &buf, end);
+ if(r)
+ goto out;
+ s->valid_until = (curl_off_t)val64;
+ break;
+ default: /* unknown tag */
+ r = CURLE_READ_ERROR;
+ goto out;
+ }
+ }
+
+out:
+ if(r) {
+ CURL_TRC_SSLS(data, "error unpacking data: %d", r);
+ Curl_ssl_session_destroy(s);
+ }
+ else
+ *ps = s;
+ return r;
+}
+
+#endif /* USE_SSLS_EXPORT */
diff --git a/libs/libcurl/src/vtls/vtls_spack.h b/libs/libcurl/src/vtls/vtls_spack.h
new file mode 100644
index 0000000000..e1dfc18200
--- /dev/null
+++ b/libs/libcurl/src/vtls/vtls_spack.h
@@ -0,0 +1,43 @@
+#ifndef HEADER_CURL_VTLS_SPACK_H
+#define HEADER_CURL_VTLS_SPACK_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at https://curl.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ * SPDX-License-Identifier: curl
+ *
+ ***************************************************************************/
+#include "../curl_setup.h"
+
+#ifdef USE_SSLS_EXPORT
+
+struct dynbuf;
+struct Curl_ssl_session;
+
+CURLcode Curl_ssl_session_pack(struct Curl_easy *data,
+ struct Curl_ssl_session *s,
+ struct dynbuf *buf);
+
+CURLcode Curl_ssl_session_unpack(struct Curl_easy *data,
+ const void *bufv, size_t buflen,
+ struct Curl_ssl_session **ps);
+
+#endif /* USE_SSLS_EXPORT */
+
+#endif /* HEADER_CURL_VTLS_SPACK_H */
diff --git a/libs/libcurl/src/vtls/wolfssl.c b/libs/libcurl/src/vtls/wolfssl.c
index 9a05f82946..0971895859 100644
--- a/libs/libcurl/src/vtls/wolfssl.c
+++ b/libs/libcurl/src/vtls/wolfssl.c
@@ -28,13 +28,14 @@
*
*/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_WOLFSSL
#define WOLFSSL_OPTIONS_IGNORE_SYS
-#include <wolfssl/version.h>
#include <wolfssl/options.h>
+#include <wolfssl/version.h>
+
#if LIBWOLFSSL_VERSION_HEX < 0x03004006 /* wolfSSL 3.4.6 (2015) */
#error "wolfSSL version should be at least 3.4.6"
@@ -55,36 +56,34 @@
#include <limits.h>
-#include "urldata.h"
-#include "sendf.h"
-#include "inet_pton.h"
+#include "../urldata.h"
+#include "../sendf.h"
+#include "../curlx/inet_pton.h"
#include "vtls.h"
#include "vtls_int.h"
+#include "vtls_scache.h"
#include "keylog.h"
-#include "parsedate.h"
-#include "connect.h" /* for the connect timeout */
-#include "select.h"
-#include "strcase.h"
+#include "../parsedate.h"
+#include "../connect.h" /* for the connect timeout */
+#include "../progress.h"
+#include "../select.h"
+#include "../strcase.h"
+#include "../strdup.h"
#include "x509asn1.h"
-#include "curl_printf.h"
-#include "multiif.h"
+#include "../curl_printf.h"
+#include "../multiif.h"
-#include <wolfssl/openssl/ssl.h>
#include <wolfssl/ssl.h>
#include <wolfssl/error-ssl.h>
#include "wolfssl.h"
/* The last #include files should be: */
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
-#ifdef USE_ECH
-# include "curl_base64.h"
-# define ECH_ENABLED(__data__) \
- (__data__->set.tls_ech && \
- !(__data__->set.tls_ech & CURLECH_DISABLE)\
- )
-#endif /* USE_ECH */
+#ifdef HAVE_WOLFSSL_CTX_GENERATEECHCONFIG
+#define USE_ECH_WOLFSSL
+#endif
/* KEEP_PEER_CERT is a product of the presence of build time symbol
OPENSSL_EXTRA without NO_CERTS, depending on the version. KEEP_PEER_CERT is
@@ -97,11 +96,11 @@
#endif
#endif
-#ifdef HAVE_WOLFSSL_BIO
+#ifdef HAVE_WOLFSSL_BIO_NEW
#define USE_BIO_CHAIN
-#ifdef HAVE_WOLFSSL_FULL_BIO
+#ifdef HAVE_WOLFSSL_BIO_SET_SHUTDOWN
#define USE_FULL_BIO
-#else /* HAVE_WOLFSSL_FULL_BIO */
+#else /* HAVE_WOLFSSL_BIO_SET_SHUTDOWN */
#undef USE_FULL_BIO
#endif
/* wolfSSL 5.7.4 and older do not have these symbols, but only the
@@ -115,10 +114,14 @@
#define wolfSSL_BIO_set_retry_read BIO_set_retry_read
#endif /* !WOLFSSL_BIO_CTRL_GET_CLOSE */
-#else /* HAVE_WOLFSSL_BIO */
+#else /* HAVE_WOLFSSL_BIO_NEW */
#undef USE_BIO_CHAIN
#endif
+static CURLcode wssl_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done);
+
#ifdef OPENSSL_EXTRA
/*
* Availability note:
@@ -126,13 +129,13 @@
* wolfSSL 4.4.0, but requires the -DHAVE_SECRET_CALLBACK build option. If that
* option is not set, then TLS 1.3 will not be logged.
* For TLS 1.2 and before, we use wolfSSL_get_keys().
- * SSL_get_client_random and wolfSSL_get_keys require OPENSSL_EXTRA
+ * wolfSSL_get_client_random and wolfSSL_get_keys require OPENSSL_EXTRA
* (--enable-opensslextra or --enable-all).
*/
#if defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13)
static int
-wolfssl_tls13_secret_callback(SSL *ssl, int id, const unsigned char *secret,
- int secretSz, void *ctx)
+wssl_tls13_secret_callback(SSL *ssl, int id, const unsigned char *secret,
+ int secretSz, void *ctx)
{
const char *label;
unsigned char client_random[SSL3_RANDOM_SIZE];
@@ -168,7 +171,7 @@ wolfssl_tls13_secret_callback(SSL *ssl, int id, const unsigned char *secret,
return 0;
}
- if(SSL_get_client_random(ssl, client_random, SSL3_RANDOM_SIZE) == 0) {
+ if(wolfSSL_get_client_random(ssl, client_random, SSL3_RANDOM_SIZE) == 0) {
/* Should never happen as wolfSSL_KeepArrays() was called before. */
return 0;
}
@@ -178,8 +181,7 @@ wolfssl_tls13_secret_callback(SSL *ssl, int id, const unsigned char *secret,
}
#endif /* defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13) */
-static void
-wolfssl_log_tls12_secret(WOLFSSL *ssl)
+static void wssl_log_tls12_secret(WOLFSSL *ssl)
{
unsigned char *ms, *sr, *cr;
unsigned int msLen, srLen, crLen, i, x = 0;
@@ -221,7 +223,7 @@ wolfssl_log_tls12_secret(WOLFSSL *ssl)
}
#endif /* OPENSSL_EXTRA */
-static int wolfssl_do_file_type(const char *type)
+static int wssl_do_file_type(const char *type)
{
if(!type || !type[0])
return WOLFSSL_FILETYPE_PEM;
@@ -239,19 +241,19 @@ struct group_name_map {
};
static const struct group_name_map gnm[] = {
- { WOLFSSL_KYBER_LEVEL1, "KYBER_LEVEL1" },
- { WOLFSSL_KYBER_LEVEL3, "KYBER_LEVEL3" },
- { WOLFSSL_KYBER_LEVEL5, "KYBER_LEVEL5" },
- { WOLFSSL_P256_KYBER_LEVEL1, "P256_KYBER_LEVEL1" },
- { WOLFSSL_P384_KYBER_LEVEL3, "P384_KYBER_LEVEL3" },
- { WOLFSSL_P521_KYBER_LEVEL5, "P521_KYBER_LEVEL5" },
+ { WOLFSSL_ML_KEM_512, "ML_KEM_512" },
+ { WOLFSSL_ML_KEM_768, "ML_KEM_768" },
+ { WOLFSSL_ML_KEM_1024, "ML_KEM_1024" },
+ { WOLFSSL_P256_ML_KEM_512, "P256_ML_KEM_512" },
+ { WOLFSSL_P384_ML_KEM_768, "P384_ML_KEM_768" },
+ { WOLFSSL_P521_ML_KEM_1024, "P521_ML_KEM_1024" },
{ 0, NULL }
};
#endif
#ifdef USE_BIO_CHAIN
-static int wolfssl_bio_cf_create(WOLFSSL_BIO *bio)
+static int wssl_bio_cf_create(WOLFSSL_BIO *bio)
{
#ifdef USE_FULL_BIO
wolfSSL_BIO_set_shutdown(bio, 1);
@@ -260,14 +262,14 @@ static int wolfssl_bio_cf_create(WOLFSSL_BIO *bio)
return 1;
}
-static int wolfssl_bio_cf_destroy(WOLFSSL_BIO *bio)
+static int wssl_bio_cf_destroy(WOLFSSL_BIO *bio)
{
if(!bio)
return 0;
return 1;
}
-static long wolfssl_bio_cf_ctrl(WOLFSSL_BIO *bio, int cmd, long num, void *ptr)
+static long wssl_bio_cf_ctrl(WOLFSSL_BIO *bio, int cmd, long num, void *ptr)
{
struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio);
long ret = 1;
@@ -297,9 +299,11 @@ static long wolfssl_bio_cf_ctrl(WOLFSSL_BIO *bio, int cmd, long num, void *ptr)
ret = 1;
break;
#ifdef WOLFSSL_BIO_CTRL_EOF
- case WOLFSSL_BIO_CTRL_EOF:
+ case WOLFSSL_BIO_CTRL_EOF: {
/* EOF has been reached on input? */
- return (!cf->next || !cf->next->connected);
+ struct ssl_connect_data *connssl = cf->ctx;
+ return connssl->peer_closed;
+ }
#endif
default:
ret = 0;
@@ -308,30 +312,29 @@ static long wolfssl_bio_cf_ctrl(WOLFSSL_BIO *bio, int cmd, long num, void *ptr)
return ret;
}
-static int wolfssl_bio_cf_out_write(WOLFSSL_BIO *bio,
- const char *buf, int blen)
+static int wssl_bio_cf_out_write(WOLFSSL_BIO *bio,
+ const char *buf, int blen)
{
struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio);
struct ssl_connect_data *connssl = cf->ctx;
- struct wolfssl_ctx *backend =
- (struct wolfssl_ctx *)connssl->backend;
+ struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t nwritten, skiplen = 0;
CURLcode result = CURLE_OK;
DEBUGASSERT(data);
- if(backend->shutting_down && backend->io_send_blocked_len &&
- (backend->io_send_blocked_len < blen)) {
+ if(wssl->shutting_down && wssl->io_send_blocked_len &&
+ (wssl->io_send_blocked_len < blen)) {
/* bug in wolfSSL: <https://github.com/wolfSSL/wolfssl/issues/7784>
* It adds the close notify message again every time we retry
* sending during shutdown. */
CURL_TRC_CF(data, cf, "bio_write, shutdown restrict send of %d"
- " to %d bytes", blen, backend->io_send_blocked_len);
- skiplen = (ssize_t)(blen - backend->io_send_blocked_len);
- blen = backend->io_send_blocked_len;
+ " to %d bytes", blen, wssl->io_send_blocked_len);
+ skiplen = (ssize_t)(blen - wssl->io_send_blocked_len);
+ blen = wssl->io_send_blocked_len;
}
nwritten = Curl_conn_cf_send(cf->next, data, buf, blen, FALSE, &result);
- backend->io_result = result;
+ wssl->io_result = result;
CURL_TRC_CF(data, cf, "bio_write(len=%d) -> %zd, %d",
blen, nwritten, result);
#ifdef USE_FULL_BIO
@@ -339,20 +342,19 @@ static int wolfssl_bio_cf_out_write(WOLFSSL_BIO *bio,
#endif
if(nwritten < 0 && CURLE_AGAIN == result) {
wolfSSL_BIO_set_retry_write(bio);
- if(backend->shutting_down && !backend->io_send_blocked_len)
- backend->io_send_blocked_len = blen;
+ if(wssl->shutting_down && !wssl->io_send_blocked_len)
+ wssl->io_send_blocked_len = blen;
}
else if(!result && skiplen)
nwritten += skiplen;
return (int)nwritten;
}
-static int wolfssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen)
+static int wssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen)
{
struct Curl_cfilter *cf = wolfSSL_BIO_get_data(bio);
struct ssl_connect_data *connssl = cf->ctx;
- struct wolfssl_ctx *backend =
- (struct wolfssl_ctx *)connssl->backend;
+ struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
struct Curl_easy *data = CF_DATA_CURRENT(cf);
ssize_t nread;
CURLcode result = CURLE_OK;
@@ -362,8 +364,20 @@ static int wolfssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen)
if(!buf)
return 0;
+ if((connssl->connecting_state == ssl_connect_2) &&
+ !wssl->x509_store_setup) {
+ /* During handshake, init the x509 store before receiving the
+ * server response. This allows sending of ClientHello without delay. */
+ result = Curl_wssl_setup_x509_store(cf, data, wssl);
+ if(result) {
+ CURL_TRC_CF(data, cf, "Curl_wssl_setup_x509_store() -> %d", result);
+ wssl->io_result = result;
+ return -1;
+ }
+ }
+
nread = Curl_conn_cf_recv(cf->next, data, buf, blen, &result);
- backend->io_result = result;
+ wssl->io_result = result;
CURL_TRC_CF(data, cf, "bio_read(len=%d) -> %zd, %d", blen, nread, result);
#ifdef USE_FULL_BIO
wolfSSL_BIO_clear_retry_flags(bio);
@@ -375,82 +389,93 @@ static int wolfssl_bio_cf_in_read(WOLFSSL_BIO *bio, char *buf, int blen)
return (int)nread;
}
-static WOLFSSL_BIO_METHOD *wolfssl_bio_cf_method = NULL;
+static WOLFSSL_BIO_METHOD *wssl_bio_cf_method = NULL;
-static void wolfssl_bio_cf_init_methods(void)
+static void wssl_bio_cf_init_methods(void)
{
- wolfssl_bio_cf_method = wolfSSL_BIO_meth_new(WOLFSSL_BIO_MEMORY,
+ wssl_bio_cf_method = wolfSSL_BIO_meth_new(WOLFSSL_BIO_MEMORY,
"wolfSSL CF BIO");
- wolfSSL_BIO_meth_set_write(wolfssl_bio_cf_method, &wolfssl_bio_cf_out_write);
- wolfSSL_BIO_meth_set_read(wolfssl_bio_cf_method, &wolfssl_bio_cf_in_read);
- wolfSSL_BIO_meth_set_ctrl(wolfssl_bio_cf_method, &wolfssl_bio_cf_ctrl);
- wolfSSL_BIO_meth_set_create(wolfssl_bio_cf_method, &wolfssl_bio_cf_create);
- wolfSSL_BIO_meth_set_destroy(wolfssl_bio_cf_method, &wolfssl_bio_cf_destroy);
+ wolfSSL_BIO_meth_set_write(wssl_bio_cf_method, &wssl_bio_cf_out_write);
+ wolfSSL_BIO_meth_set_read(wssl_bio_cf_method, &wssl_bio_cf_in_read);
+ wolfSSL_BIO_meth_set_ctrl(wssl_bio_cf_method, &wssl_bio_cf_ctrl);
+ wolfSSL_BIO_meth_set_create(wssl_bio_cf_method, &wssl_bio_cf_create);
+ wolfSSL_BIO_meth_set_destroy(wssl_bio_cf_method, &wssl_bio_cf_destroy);
}
-static void wolfssl_bio_cf_free_methods(void)
+static void wssl_bio_cf_free_methods(void)
{
- wolfSSL_BIO_meth_free(wolfssl_bio_cf_method);
+ wolfSSL_BIO_meth_free(wssl_bio_cf_method);
}
#else /* USE_BIO_CHAIN */
-#define wolfssl_bio_cf_init_methods() Curl_nop_stmt
-#define wolfssl_bio_cf_free_methods() Curl_nop_stmt
+#define wssl_bio_cf_init_methods() Curl_nop_stmt
+#define wssl_bio_cf_free_methods() Curl_nop_stmt
#endif /* !USE_BIO_CHAIN */
-static void wolfssl_session_free(void *sdata, size_t slen)
-{
- (void)slen;
- free(sdata);
-}
-
-CURLcode wssl_cache_session(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct ssl_peer *peer,
- WOLFSSL_SESSION *session)
+CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ WOLFSSL_SESSION *session,
+ int ietf_tls_id,
+ const char *alpn,
+ unsigned char *quic_tp,
+ size_t quic_tp_len)
{
CURLcode result = CURLE_OK;
- unsigned char *sdata = NULL;
- unsigned int slen;
+ struct Curl_ssl_session *sc_session = NULL;
+ unsigned char *sdata = NULL, *qtp_clone = NULL;
+ unsigned int sdata_len;
+ unsigned int earlydata_max = 0;
if(!session)
goto out;
- slen = wolfSSL_i2d_SSL_SESSION(session, NULL);
- if(slen <= 0) {
- CURL_TRC_CF(data, cf, "fail to assess session length: %u", slen);
+ sdata_len = wolfSSL_i2d_SSL_SESSION(session, NULL);
+ if(sdata_len <= 0) {
+ CURL_TRC_CF(data, cf, "fail to assess session length: %u", sdata_len);
result = CURLE_FAILED_INIT;
goto out;
}
- sdata = calloc(1, slen);
+ sdata = calloc(1, sdata_len);
if(!sdata) {
- failf(data, "unable to allocate session buffer of %u bytes", slen);
+ failf(data, "unable to allocate session buffer of %u bytes", sdata_len);
result = CURLE_OUT_OF_MEMORY;
goto out;
}
- slen = wolfSSL_i2d_SSL_SESSION(session, &sdata);
- if(slen <= 0) {
- CURL_TRC_CF(data, cf, "fail to serialize session: %u", slen);
+ sdata_len = wolfSSL_i2d_SSL_SESSION(session, &sdata);
+ if(sdata_len <= 0) {
+ CURL_TRC_CF(data, cf, "fail to serialize session: %u", sdata_len);
result = CURLE_FAILED_INIT;
goto out;
}
+ if(quic_tp && quic_tp_len) {
+ qtp_clone = Curl_memdup0((char *)quic_tp, quic_tp_len);
+ if(!qtp_clone) {
+ free(sdata);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ }
+#ifdef WOLFSSL_EARLY_DATA
+ earlydata_max = wolfSSL_SESSION_get_max_early_data(session);
+#endif
- Curl_ssl_sessionid_lock(data);
- result = Curl_ssl_set_sessionid(cf, data, peer, NULL,
- sdata, slen, wolfssl_session_free);
- Curl_ssl_sessionid_unlock(data);
- if(result)
- failf(data, "failed to add new ssl session to cache (%d)", result);
- else {
- CURL_TRC_CF(data, cf, "added new session to cache");
- sdata = NULL;
+ result = Curl_ssl_session_create2(sdata, sdata_len,
+ ietf_tls_id, alpn,
+ (curl_off_t)time(NULL) +
+ wolfSSL_SESSION_get_timeout(session),
+ earlydata_max, qtp_clone, quic_tp_len,
+ &sc_session);
+ sdata = NULL; /* took ownership of sdata */
+ if(!result) {
+ result = Curl_ssl_scache_put(cf, data, ssl_peer_key, sc_session);
+ /* took ownership of `sc_session` */
}
out:
free(sdata);
- return 0;
+ return result;
}
static int wssl_vtls_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session)
@@ -465,50 +490,122 @@ static int wssl_vtls_new_session_cb(WOLFSSL *ssl, WOLFSSL_SESSION *session)
DEBUGASSERT(connssl);
DEBUGASSERT(data);
if(connssl && data) {
- (void)wssl_cache_session(cf, data, &connssl->peer, session);
+ (void)Curl_wssl_cache_session(cf, data, connssl->peer.scache_key,
+ session, wolfSSL_version(ssl),
+ connssl->negotiated.alpn, NULL, 0);
}
}
return 0;
}
-CURLcode wssl_setup_session(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct wolfssl_ctx *wss,
- struct ssl_peer *peer)
+static CURLcode wssl_on_session_reuse(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct alpn_spec *alpns,
+ struct Curl_ssl_session *scs,
+ bool *do_early_data)
{
- void *psdata;
- const unsigned char *sdata = NULL;
- size_t slen = 0;
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
CURLcode result = CURLE_OK;
- Curl_ssl_sessionid_lock(data);
- if(!Curl_ssl_getsessionid(cf, data, peer, &psdata, &slen, NULL)) {
+ *do_early_data = FALSE;
+#ifdef WOLFSSL_EARLY_DATA
+ connssl->earlydata_max = wolfSSL_SESSION_get_max_early_data(
+ wolfSSL_get_session(wssl->ssl));
+#else
+ (void)wssl;
+ connssl->earlydata_max = 0;
+#endif
+
+ if(!connssl->earlydata_max) {
+ /* Seems to be GnuTLS way to signal no EarlyData in session */
+ CURL_TRC_CF(data, cf, "SSL session does not allow earlydata");
+ }
+ else if(!Curl_alpn_contains_proto(alpns, scs->alpn)) {
+ CURL_TRC_CF(data, cf, "SSL session has different ALPN, no early data");
+ }
+ else {
+ infof(data, "SSL session allows %zu bytes of early data, "
+ "reusing ALPN '%s'", connssl->earlydata_max, scs->alpn);
+ connssl->earlydata_state = ssl_earlydata_await;
+ connssl->state = ssl_connection_deferred;
+ result = Curl_alpn_set_negotiated(cf, data, connssl,
+ (const unsigned char *)scs->alpn,
+ scs->alpn ? strlen(scs->alpn) : 0);
+ *do_early_data = !result;
+ }
+ return result;
+}
+
+static CURLcode
+wssl_setup_session(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct wssl_ctx *wss,
+ struct alpn_spec *alpns,
+ const char *ssl_peer_key,
+ Curl_wssl_init_session_reuse_cb *sess_reuse_cb)
+{
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ struct Curl_ssl_session *scs = NULL;
+ CURLcode result;
+
+ result = Curl_ssl_scache_take(cf, data, ssl_peer_key, &scs);
+ if(!result && scs && scs->sdata && scs->sdata_len &&
+ (!scs->alpn || Curl_alpn_contains_proto(alpns, scs->alpn))) {
WOLFSSL_SESSION *session;
- sdata = psdata;
- session = wolfSSL_d2i_SSL_SESSION(NULL, &sdata, (long)slen);
+ /* wolfSSL changes the passed pointer for whatever reasons, yikes */
+ const unsigned char *sdata = scs->sdata;
+ session = wolfSSL_d2i_SSL_SESSION(NULL, &sdata, (long)scs->sdata_len);
if(session) {
- int ret = wolfSSL_set_session(wss->handle, session);
+ int ret = wolfSSL_set_session(wss->ssl, session);
if(ret != WOLFSSL_SUCCESS) {
- Curl_ssl_delsessionid(data, psdata);
- infof(data, "previous session not accepted (%d), "
+ Curl_ssl_session_destroy(scs);
+ scs = NULL;
+ infof(data, "cached session not accepted (%d), "
"removing from cache", ret);
}
- else
- infof(data, "SSL reusing session ID");
+ else {
+ infof(data, "SSL reusing session with ALPN '%s'",
+ scs->alpn ? scs->alpn : "-");
+ if(ssl_config->earlydata &&
+ !cf->conn->connect_only &&
+ !strcmp("TLSv1.3", wolfSSL_get_version(wss->ssl))) {
+ bool do_early_data = FALSE;
+ if(sess_reuse_cb) {
+ result = sess_reuse_cb(cf, data, alpns, scs, &do_early_data);
+ if(result)
+ goto out;
+ }
+#ifdef WOLFSSL_EARLY_DATA
+ if(do_early_data) {
+ unsigned int edmax = (scs->earlydata_max < UINT_MAX) ?
+ (unsigned int)scs->earlydata_max : UINT_MAX;
+ /* We only try the ALPN protocol the session used before,
+ * otherwise we might send early data for the wrong protocol */
+ Curl_alpn_restrict_to(alpns, scs->alpn);
+ wolfSSL_set_max_early_data(wss->ssl, edmax);
+ }
+#else
+ /* Should never enable when not supported */
+ DEBUGASSERT(!do_early_data);
+#endif
+ }
+ }
wolfSSL_SESSION_free(session);
}
else {
failf(data, "could not decode previous session");
}
}
- Curl_ssl_sessionid_unlock(data);
+out:
+ Curl_ssl_scache_return(cf, data, ssl_peer_key, scs);
return result;
}
-static CURLcode populate_x509_store(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- WOLFSSL_X509_STORE *store,
- struct wolfssl_ctx *wssl)
+static CURLcode wssl_populate_x509_store(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ WOLFSSL_X509_STORE *store,
+ struct wssl_ctx *wssl)
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
const struct curl_blob *ca_info_blob = conn_config->ca_info_blob;
@@ -518,57 +615,61 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf,
const char * const ssl_capath = conn_config->CApath;
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
bool imported_native_ca = FALSE;
+ bool imported_ca_info_blob = FALSE;
+
+ /* We do not want to do this again, no matter the outcome */
+ wssl->x509_store_setup = TRUE;
-#if !defined(NO_FILESYSTEM) && defined(WOLFSSL_SYS_CA_CERTS)
+#ifndef NO_FILESYSTEM
/* load native CA certificates */
if(ssl_config->native_ca_store) {
- if(wolfSSL_CTX_load_system_CA_certs(wssl->ctx) != WOLFSSL_SUCCESS) {
+#ifdef WOLFSSL_SYS_CA_CERTS
+ if(wolfSSL_CTX_load_system_CA_certs(wssl->ssl_ctx) != WOLFSSL_SUCCESS) {
infof(data, "error importing native CA store, continuing anyway");
}
else {
imported_native_ca = TRUE;
infof(data, "successfully imported native CA store");
- wssl->x509_store_setup = TRUE;
}
+#else
+ infof(data, "ignoring native CA option because wolfSSL was built without "
+ "native CA support");
+#endif
}
#endif /* !NO_FILESYSTEM */
/* load certificate blob */
if(ca_info_blob) {
- if(wolfSSL_CTX_load_verify_buffer(wssl->ctx, ca_info_blob->data,
+ if(wolfSSL_CTX_load_verify_buffer(wssl->ssl_ctx, ca_info_blob->data,
(long)ca_info_blob->len,
WOLFSSL_FILETYPE_PEM) !=
WOLFSSL_SUCCESS) {
- if(imported_native_ca) {
- infof(data, "error importing CA certificate blob, continuing anyway");
- }
- else {
- failf(data, "error importing CA certificate blob");
- return CURLE_SSL_CACERT_BADFILE;
- }
+ failf(data, "error importing CA certificate blob");
+ return CURLE_SSL_CACERT_BADFILE;
}
else {
+ imported_ca_info_blob = TRUE;
infof(data, "successfully imported CA certificate blob");
- wssl->x509_store_setup = TRUE;
}
}
#ifndef NO_FILESYSTEM
/* load trusted cacert from file if not blob */
- CURL_TRC_CF(data, cf, "populate_x509_store, path=%s, blob=%d",
+ CURL_TRC_CF(data, cf, "wssl_populate_x509_store, path=%s, blob=%d",
ssl_cafile ? ssl_cafile : "none", !!ca_info_blob);
if(!store)
return CURLE_OUT_OF_MEMORY;
- if((ssl_cafile || ssl_capath) && (!wssl->x509_store_setup)) {
+ if(ssl_cafile || ssl_capath) {
int rc =
- wolfSSL_CTX_load_verify_locations_ex(wssl->ctx,
+ wolfSSL_CTX_load_verify_locations_ex(wssl->ssl_ctx,
ssl_cafile,
ssl_capath,
WOLFSSL_LOAD_FLAG_IGNORE_ERR);
if(WOLFSSL_SUCCESS != rc) {
- if(conn_config->verifypeer) {
+ if(conn_config->verifypeer &&
+ !imported_native_ca && !imported_ca_info_blob) {
/* Fail if we insist on successfully verifying the server. */
failf(data, "error setting certificate verify locations:"
" CAfile: %s CApath: %s",
@@ -592,7 +693,6 @@ static CURLcode populate_x509_store(struct Curl_cfilter *cf,
}
#endif
(void)store;
- wssl->x509_store_setup = TRUE;
return CURLE_OK;
}
@@ -620,12 +720,12 @@ static void wssl_x509_share_free(void *key, size_t key_len, void *p)
}
static bool
-cached_x509_store_expired(const struct Curl_easy *data,
- const struct wssl_x509_share *mb)
+wssl_cached_x509_store_expired(const struct Curl_easy *data,
+ const struct wssl_x509_share *mb)
{
const struct ssl_general_config *cfg = &data->set.general_ssl;
- struct curltime now = Curl_now();
- timediff_t elapsed_ms = Curl_timediff(now, mb->time);
+ struct curltime now = curlx_now();
+ timediff_t elapsed_ms = curlx_timediff(now, mb->time);
timediff_t timeout_ms = cfg->ca_cache_timeout * (timediff_t)1000;
if(timeout_ms < 0)
@@ -635,8 +735,8 @@ cached_x509_store_expired(const struct Curl_easy *data,
}
static bool
-cached_x509_store_different(struct Curl_cfilter *cf,
- const struct wssl_x509_share *mb)
+wssl_cached_x509_store_different(struct Curl_cfilter *cf,
+ const struct wssl_x509_share *mb)
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
if(!mb->CAfile || !conn_config->CAfile)
@@ -645,8 +745,8 @@ cached_x509_store_different(struct Curl_cfilter *cf,
return strcmp(mb->CAfile, conn_config->CAfile);
}
-static WOLFSSL_X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf,
- const struct Curl_easy *data)
+static WOLFSSL_X509_STORE *wssl_get_cached_x509_store(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
{
struct Curl_multi *multi = data->multi;
struct wssl_x509_share *share;
@@ -654,20 +754,20 @@ static WOLFSSL_X509_STORE *get_cached_x509_store(struct Curl_cfilter *cf,
DEBUGASSERT(multi);
share = multi ? Curl_hash_pick(&multi->proto_hash,
- (void *)MPROTO_WSSL_X509_KEY,
+ CURL_UNCONST(MPROTO_WSSL_X509_KEY),
sizeof(MPROTO_WSSL_X509_KEY)-1) : NULL;
if(share && share->store &&
- !cached_x509_store_expired(data, share) &&
- !cached_x509_store_different(cf, share)) {
+ !wssl_cached_x509_store_expired(data, share) &&
+ !wssl_cached_x509_store_different(cf, share)) {
store = share->store;
}
return store;
}
-static void set_cached_x509_store(struct Curl_cfilter *cf,
- const struct Curl_easy *data,
- WOLFSSL_X509_STORE *store)
+static void wssl_set_cached_x509_store(struct Curl_cfilter *cf,
+ const struct Curl_easy *data,
+ WOLFSSL_X509_STORE *store)
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct Curl_multi *multi = data->multi;
@@ -677,7 +777,7 @@ static void set_cached_x509_store(struct Curl_cfilter *cf,
if(!multi)
return;
share = Curl_hash_pick(&multi->proto_hash,
- (void *)MPROTO_WSSL_X509_KEY,
+ CURL_UNCONST(MPROTO_WSSL_X509_KEY),
sizeof(MPROTO_WSSL_X509_KEY)-1);
if(!share) {
@@ -685,7 +785,7 @@ static void set_cached_x509_store(struct Curl_cfilter *cf,
if(!share)
return;
if(!Curl_hash_add2(&multi->proto_hash,
- (void *)MPROTO_WSSL_X509_KEY,
+ CURL_UNCONST(MPROTO_WSSL_X509_KEY),
sizeof(MPROTO_WSSL_X509_KEY)-1,
share, wssl_x509_share_free)) {
free(share);
@@ -709,7 +809,7 @@ static void set_cached_x509_store(struct Curl_cfilter *cf,
free(share->CAfile);
}
- share->time = Curl_now();
+ share->time = curlx_now();
share->store = store;
share->CAfile = CAfile;
}
@@ -717,7 +817,7 @@ static void set_cached_x509_store(struct Curl_cfilter *cf,
CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf,
struct Curl_easy *data,
- struct wolfssl_ctx *wssl)
+ struct wssl_ctx *wssl)
{
struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
@@ -735,12 +835,14 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf,
!ssl_config->primary.CRLfile &&
!ssl_config->native_ca_store;
- cached_store = cache_criteria_met ? get_cached_x509_store(cf, data) : NULL;
- if(cached_store && wolfSSL_CTX_get_cert_store(wssl->ctx) == cached_store) {
+ cached_store = cache_criteria_met ? wssl_get_cached_x509_store(cf, data)
+ : NULL;
+ if(cached_store &&
+ wolfSSL_CTX_get_cert_store(wssl->ssl_ctx) == cached_store) {
/* The cached store is already in use, do nothing. */
}
else if(cached_store && wolfSSL_X509_STORE_up_ref(cached_store)) {
- wolfSSL_CTX_set_cert_store(wssl->ctx, cached_store);
+ wolfSSL_CTX_set_cert_store(wssl->ssl_ctx, cached_store);
}
else if(cache_criteria_met) {
/* wolfSSL's initial store in CTX is not shareable by default.
@@ -750,17 +852,17 @@ CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf,
failf(data, "SSL: could not create a X509 store");
return CURLE_OUT_OF_MEMORY;
}
- wolfSSL_CTX_set_cert_store(wssl->ctx, store);
+ wolfSSL_CTX_set_cert_store(wssl->ssl_ctx, store);
- result = populate_x509_store(cf, data, store, wssl);
+ result = wssl_populate_x509_store(cf, data, store, wssl);
if(!result) {
- set_cached_x509_store(cf, data, store);
+ wssl_set_cached_x509_store(cf, data, store);
}
}
else {
/* We never share the CTX's store, use it. */
- WOLFSSL_X509_STORE *store = wolfSSL_CTX_get_cert_store(wssl->ctx);
- result = populate_x509_store(cf, data, store, wssl);
+ WOLFSSL_X509_STORE *store = wolfSSL_CTX_get_cert_store(wssl->ssl_ctx);
+ result = wssl_populate_x509_store(cf, data, store, wssl);
}
return result;
@@ -773,20 +875,20 @@ wssl_add_default_ciphers(bool tls13, struct dynbuf *buf)
int i;
char *str;
- for(i = 0; (str = wolfSSL_get_cipher_list(i)); i++) {
+ for(i = 0; (str = wolfSSL_get_cipher_list(i)) != NULL; i++) {
size_t n;
if((strncmp(str, "TLS13", 5) == 0) != tls13)
continue;
/* if there already is data in the string, add colon separator */
- if(Curl_dyn_len(buf)) {
- CURLcode result = Curl_dyn_addn(buf, ":", 1);
+ if(curlx_dyn_len(buf)) {
+ CURLcode result = curlx_dyn_addn(buf, ":", 1);
if(result)
return result;
}
n = strlen(str);
- if(Curl_dyn_addn(buf, str, n))
+ if(curlx_dyn_addn(buf, str, n))
return CURLE_OUT_OF_MEMORY;
}
@@ -834,30 +936,41 @@ wssl_legacy_CTX_set_max_proto_version(WOLFSSL_CTX* ctx, int version)
#define wolfSSL_CTX_set_max_proto_version wssl_legacy_CTX_set_max_proto_version
#endif
-/*
- * This function loads all the client/CA certificates and CRLs. Setup the TLS
- * layer and do all necessary magic.
- */
-static CURLcode
-wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
+#define QUIC_CIPHERS \
+ "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \
+ "POLY1305_SHA256:TLS_AES_128_CCM_SHA256"
+#define QUIC_GROUPS "P-256:P-384:P-521"
+
+CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx,
+ struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct ssl_peer *peer,
+ const struct alpn_spec *alpns_requested,
+ Curl_wssl_ctx_setup_cb *cb_setup,
+ void *cb_user_data,
+ void *ssl_user_data,
+ Curl_wssl_init_session_reuse_cb *sess_reuse_cb)
{
+ struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
+ struct ssl_primary_config *conn_config;
+ WOLFSSL_METHOD* req_method = NULL;
+ struct alpn_spec alpns;
int res;
char *curves;
- struct ssl_connect_data *connssl = cf->ctx;
- struct wolfssl_ctx *backend =
- (struct wolfssl_ctx *)connssl->backend;
- struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
- const struct ssl_config_data *ssl_config = Curl_ssl_cf_get_config(cf, data);
- WOLFSSL_METHOD* req_method = NULL;
#ifdef WOLFSSL_HAVE_KYBER
word16 pqkem = 0;
size_t idx = 0;
#endif
+ CURLcode result = CURLE_FAILED_INIT;
- DEBUGASSERT(backend);
-
- if(connssl->state == ssl_connection_complete)
- return CURLE_OK;
+ DEBUGASSERT(!wctx->ssl_ctx);
+ DEBUGASSERT(!wctx->ssl);
+ conn_config = Curl_ssl_cf_get_primary_config(cf);
+ if(!conn_config) {
+ result = CURLE_FAILED_INIT;
+ goto out;
+ }
+ Curl_alpn_copy(&alpns, alpns_requested);
#if LIBWOLFSSL_VERSION_HEX < 0x04002000 /* 4.2.0 (2019) */
req_method = wolfSSLv23_client_method();
@@ -866,58 +979,62 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
#endif
if(!req_method) {
failf(data, "wolfSSL: could not create a client method");
- return CURLE_OUT_OF_MEMORY;
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
}
- if(backend->ctx)
- wolfSSL_CTX_free(backend->ctx);
+ if(wctx->ssl_ctx)
+ wolfSSL_CTX_free(wctx->ssl_ctx);
- backend->ctx = wolfSSL_CTX_new(req_method);
- if(!backend->ctx) {
+ wctx->ssl_ctx = wolfSSL_CTX_new(req_method);
+ if(!wctx->ssl_ctx) {
failf(data, "wolfSSL: could not create a context");
- return CURLE_OUT_OF_MEMORY;
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
}
switch(conn_config->version) {
case CURL_SSLVERSION_DEFAULT:
case CURL_SSLVERSION_TLSv1:
case CURL_SSLVERSION_TLSv1_0:
- res = wolfSSL_CTX_set_min_proto_version(backend->ctx, TLS1_VERSION);
+ res = wolfSSL_CTX_set_min_proto_version(wctx->ssl_ctx, TLS1_VERSION);
break;
case CURL_SSLVERSION_TLSv1_1:
- res = wolfSSL_CTX_set_min_proto_version(backend->ctx, TLS1_1_VERSION);
+ res = wolfSSL_CTX_set_min_proto_version(wctx->ssl_ctx, TLS1_1_VERSION);
break;
case CURL_SSLVERSION_TLSv1_2:
- res = wolfSSL_CTX_set_min_proto_version(backend->ctx, TLS1_2_VERSION);
+ res = wolfSSL_CTX_set_min_proto_version(wctx->ssl_ctx, TLS1_2_VERSION);
break;
#ifdef WOLFSSL_TLS13
case CURL_SSLVERSION_TLSv1_3:
- res = wolfSSL_CTX_set_min_proto_version(backend->ctx, TLS1_3_VERSION);
+ res = wolfSSL_CTX_set_min_proto_version(wctx->ssl_ctx, TLS1_3_VERSION);
break;
#endif
default:
failf(data, "wolfSSL: unsupported minimum TLS version value");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
if(res != WOLFSSL_SUCCESS) {
failf(data, "wolfSSL: failed set the minimum TLS version");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
switch(conn_config->version_max) {
#ifdef WOLFSSL_TLS13
case CURL_SSLVERSION_MAX_TLSv1_3:
- res = wolfSSL_CTX_set_max_proto_version(backend->ctx, TLS1_3_VERSION);
+ res = wolfSSL_CTX_set_max_proto_version(wctx->ssl_ctx, TLS1_3_VERSION);
break;
#endif
case CURL_SSLVERSION_MAX_TLSv1_2:
- res = wolfSSL_CTX_set_max_proto_version(backend->ctx, TLS1_2_VERSION);
+ res = wolfSSL_CTX_set_max_proto_version(wctx->ssl_ctx, TLS1_2_VERSION);
break;
case CURL_SSLVERSION_MAX_TLSv1_1:
- res = wolfSSL_CTX_set_max_proto_version(backend->ctx, TLS1_1_VERSION);
+ res = wolfSSL_CTX_set_max_proto_version(wctx->ssl_ctx, TLS1_1_VERSION);
break;
case CURL_SSLVERSION_MAX_TLSv1_0:
- res = wolfSSL_CTX_set_max_proto_version(backend->ctx, TLS1_VERSION);
+ res = wolfSSL_CTX_set_max_proto_version(wctx->ssl_ctx, TLS1_VERSION);
break;
case CURL_SSLVERSION_MAX_DEFAULT:
case CURL_SSLVERSION_MAX_NONE:
@@ -925,20 +1042,23 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
break;
default:
failf(data, "wolfSSL: unsupported maximum TLS version value");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
if(res != WOLFSSL_SUCCESS) {
failf(data, "wolfSSL: failed set the maximum TLS version");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
#ifndef WOLFSSL_TLS13
{
char *ciphers = conn_config->cipher_list;
if(ciphers) {
- if(!SSL_CTX_set_cipher_list(backend->ctx, ciphers)) {
+ if(!SSL_CTX_set_cipher_list(wctx->ssl_ctx, ciphers)) {
failf(data, "failed setting cipher list: %s", ciphers);
- return CURLE_SSL_CIPHER;
+ result = CURLE_SSL_CIPHER;
+ goto out;
}
infof(data, "Cipher selection: %s", ciphers);
}
@@ -949,40 +1069,42 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
const char *ciphers12 = conn_config->cipher_list;
const char *ciphers13 = conn_config->cipher_list13;
struct dynbuf c;
- CURLcode result;
- Curl_dyn_init(&c, MAX_CIPHER_LEN);
+ curlx_dyn_init(&c, MAX_CIPHER_LEN);
if(ciphers13)
- result = Curl_dyn_add(&c, ciphers13);
+ result = curlx_dyn_add(&c, ciphers13);
else
result = wssl_add_default_ciphers(TRUE, &c);
if(!result) {
if(ciphers12) {
- if(Curl_dyn_len(&c))
- result = Curl_dyn_addn(&c, ":", 1);
+ if(curlx_dyn_len(&c))
+ result = curlx_dyn_addn(&c, ":", 1);
if(!result)
- result = Curl_dyn_add(&c, ciphers12);
+ result = curlx_dyn_add(&c, ciphers12);
}
else
result = wssl_add_default_ciphers(FALSE, &c);
}
if(result)
- return result;
+ goto out;
- if(!wolfSSL_CTX_set_cipher_list(backend->ctx, Curl_dyn_ptr(&c))) {
- failf(data, "failed setting cipher list: %s", Curl_dyn_ptr(&c));
- Curl_dyn_free(&c);
- return CURLE_SSL_CIPHER;
+ if(!wolfSSL_CTX_set_cipher_list(wctx->ssl_ctx, curlx_dyn_ptr(&c))) {
+ failf(data, "failed setting cipher list: %s", curlx_dyn_ptr(&c));
+ curlx_dyn_free(&c);
+ result = CURLE_SSL_CIPHER;
+ goto out;
}
- infof(data, "Cipher selection: %s", Curl_dyn_ptr(&c));
- Curl_dyn_free(&c);
+ infof(data, "Cipher selection: %s", curlx_dyn_ptr(&c));
+ curlx_dyn_free(&c);
}
#endif
curves = conn_config->curves;
- if(curves) {
+ if(!curves && cf->conn->transport == TRNSPRT_QUIC)
+ curves = (char *)CURL_UNCONST(QUIC_GROUPS);
+ if(curves) {
#ifdef WOLFSSL_HAVE_KYBER
for(idx = 0; gnm[idx].name != NULL; idx++) {
if(strncmp(curves, gnm[idx].name, strlen(gnm[idx].name)) == 0) {
@@ -994,9 +1116,10 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
if(pqkem == 0)
#endif
{
- if(!wolfSSL_CTX_set1_curves_list(backend->ctx, curves)) {
+ if(!wolfSSL_CTX_set1_curves_list(wctx->ssl_ctx, curves)) {
failf(data, "failed setting curves list: '%s'", curves);
- return CURLE_SSL_CIPHER;
+ result = CURLE_SSL_CIPHER;
+ goto out;
}
}
}
@@ -1008,30 +1131,32 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
const char *key_file = ssl_config->key;
const struct curl_blob *cert_blob = ssl_config->primary.cert_blob;
const struct curl_blob *key_blob = ssl_config->key_blob;
- int file_type = wolfssl_do_file_type(ssl_config->cert_type);
+ int file_type = wssl_do_file_type(ssl_config->cert_type);
int rc;
switch(file_type) {
case WOLFSSL_FILETYPE_PEM:
rc = cert_blob ?
- wolfSSL_CTX_use_certificate_chain_buffer(backend->ctx,
+ wolfSSL_CTX_use_certificate_chain_buffer(wctx->ssl_ctx,
cert_blob->data,
(long)cert_blob->len) :
- wolfSSL_CTX_use_certificate_chain_file(backend->ctx, cert_file);
+ wolfSSL_CTX_use_certificate_chain_file(wctx->ssl_ctx, cert_file);
break;
case WOLFSSL_FILETYPE_ASN1:
rc = cert_blob ?
- wolfSSL_CTX_use_certificate_buffer(backend->ctx, cert_blob->data,
+ wolfSSL_CTX_use_certificate_buffer(wctx->ssl_ctx, cert_blob->data,
(long)cert_blob->len, file_type) :
- wolfSSL_CTX_use_certificate_file(backend->ctx, cert_file, file_type);
+ wolfSSL_CTX_use_certificate_file(wctx->ssl_ctx, cert_file, file_type);
break;
default:
failf(data, "unknown cert type");
- return CURLE_BAD_FUNCTION_ARGUMENT;
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
}
if(rc != 1) {
failf(data, "unable to use client certificate");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
if(!key_blob && !key_file) {
@@ -1039,53 +1164,57 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
key_file = cert_file;
}
else
- file_type = wolfssl_do_file_type(ssl_config->key_type);
+ file_type = wssl_do_file_type(ssl_config->key_type);
rc = key_blob ?
- wolfSSL_CTX_use_PrivateKey_buffer(backend->ctx, key_blob->data,
+ wolfSSL_CTX_use_PrivateKey_buffer(wctx->ssl_ctx, key_blob->data,
(long)key_blob->len, file_type) :
- wolfSSL_CTX_use_PrivateKey_file(backend->ctx, key_file, file_type);
+ wolfSSL_CTX_use_PrivateKey_file(wctx->ssl_ctx, key_file, file_type);
if(rc != 1) {
failf(data, "unable to set private key");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
}
#else /* NO_FILESYSTEM */
if(ssl_config->primary.cert_blob) {
const struct curl_blob *cert_blob = ssl_config->primary.cert_blob;
const struct curl_blob *key_blob = ssl_config->key_blob;
- int file_type = wolfssl_do_file_type(ssl_config->cert_type);
+ int file_type = wssl_do_file_type(ssl_config->cert_type);
int rc;
switch(file_type) {
case WOLFSSL_FILETYPE_PEM:
- rc = wolfSSL_CTX_use_certificate_chain_buffer(backend->ctx,
+ rc = wolfSSL_CTX_use_certificate_chain_buffer(wctx->ssl_ctx,
cert_blob->data,
(long)cert_blob->len);
break;
case WOLFSSL_FILETYPE_ASN1:
- rc = wolfSSL_CTX_use_certificate_buffer(backend->ctx, cert_blob->data,
+ rc = wolfSSL_CTX_use_certificate_buffer(wctx->ssl_ctx, cert_blob->data,
(long)cert_blob->len, file_type);
break;
default:
failf(data, "unknown cert type");
- return CURLE_BAD_FUNCTION_ARGUMENT;
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
}
if(rc != 1) {
failf(data, "unable to use client certificate");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
if(!key_blob)
key_blob = cert_blob;
else
- file_type = wolfssl_do_file_type(ssl_config->key_type);
+ file_type = wssl_do_file_type(ssl_config->key_type);
- if(wolfSSL_CTX_use_PrivateKey_buffer(backend->ctx, key_blob->data,
+ if(wolfSSL_CTX_use_PrivateKey_buffer(wctx->ssl_ctx, key_blob->data,
(long)key_blob->len,
file_type) != 1) {
failf(data, "unable to set private key");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
}
#endif /* !NO_FILESYSTEM */
@@ -1094,37 +1223,49 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
* fail to connect if the verification fails, or if it should continue
* anyway. In the latter case the result of the verification is checked with
* SSL_get_verify_result() below. */
- wolfSSL_CTX_set_verify(backend->ctx,
+ wolfSSL_CTX_set_verify(wctx->ssl_ctx,
conn_config->verifypeer ? WOLFSSL_VERIFY_PEER :
WOLFSSL_VERIFY_NONE, NULL);
#ifdef HAVE_SNI
- if(connssl->peer.sni) {
- size_t sni_len = strlen(connssl->peer.sni);
+ if(peer->sni) {
+ size_t sni_len = strlen(peer->sni);
if((sni_len < USHRT_MAX)) {
- if(wolfSSL_CTX_UseSNI(backend->ctx, WOLFSSL_SNI_HOST_NAME,
- connssl->peer.sni,
- (unsigned short)sni_len) != 1) {
+ if(wolfSSL_CTX_UseSNI(wctx->ssl_ctx, WOLFSSL_SNI_HOST_NAME,
+ peer->sni, (unsigned short)sni_len) != 1) {
failf(data, "Failed to set SNI");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
+ CURL_TRC_CF(data, cf, "set SNI '%s'", peer->sni);
}
}
#endif
+ if(ssl_config->primary.cache_session &&
+ cf->conn->transport != TRNSPRT_QUIC) {
+ /* Register to get notified when a new session is received */
+ wolfSSL_CTX_sess_set_new_cb(wctx->ssl_ctx, wssl_vtls_new_session_cb);
+ }
+
+ if(cb_setup) {
+ result = cb_setup(cf, data, cb_user_data);
+ if(result)
+ goto out;
+ }
+
/* give application a chance to interfere with SSL set up. */
if(data->set.ssl.fsslctx) {
- CURLcode result;
- if(!backend->x509_store_setup) {
- result = Curl_wssl_setup_x509_store(cf, data, backend);
+ if(!wctx->x509_store_setup) {
+ result = Curl_wssl_setup_x509_store(cf, data, wctx);
if(result)
- return result;
+ goto out;
}
- result = (*data->set.ssl.fsslctx)(data, backend->ctx,
+ result = (*data->set.ssl.fsslctx)(data, wctx->ssl_ctx,
data->set.ssl.fsslctxp);
if(result) {
failf(data, "error signaled by ssl ctx callback");
- return result;
+ goto out;
}
}
#ifdef NO_FILESYSTEM
@@ -1133,82 +1274,87 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
" with \"no filesystem\". Either disable peer verification"
" (insecure) or if you are building an application with libcurl you"
" can load certificates via CURLOPT_SSL_CTX_FUNCTION.");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
#endif
/* Let's make an SSL structure */
- if(backend->handle)
- wolfSSL_free(backend->handle);
- backend->handle = wolfSSL_new(backend->ctx);
- if(!backend->handle) {
+ wctx->ssl = wolfSSL_new(wctx->ssl_ctx);
+ if(!wctx->ssl) {
failf(data, "SSL: could not create a handle");
- return CURLE_OUT_OF_MEMORY;
+ result = CURLE_OUT_OF_MEMORY;
+ goto out;
}
+ wolfSSL_set_app_data(wctx->ssl, ssl_user_data);
+#ifdef WOLFSSL_QUIC
+ if(cf->conn->transport == TRNSPRT_QUIC)
+ wolfSSL_set_quic_use_legacy_codepoint(wctx->ssl, 0);
+#endif
+
#ifdef WOLFSSL_HAVE_KYBER
if(pqkem) {
- if(wolfSSL_UseKeyShare(backend->handle, pqkem) != WOLFSSL_SUCCESS) {
+ if(wolfSSL_UseKeyShare(wctx->ssl, pqkem) != WOLFSSL_SUCCESS) {
failf(data, "unable to use PQ KEM");
}
}
#endif
+ /* Check if there is a cached ID we can/should use here! */
+ if(ssl_config->primary.cache_session) {
+ /* Set session from cache if there is one */
+ (void)wssl_setup_session(cf, data, wctx, &alpns,
+ peer->scache_key, sess_reuse_cb);
+ }
+
#ifdef HAVE_ALPN
- if(connssl->alpn) {
+ if(alpns.count) {
struct alpn_proto_buf proto;
- CURLcode result;
+ memset(&proto, 0, sizeof(proto));
+ Curl_alpn_to_proto_str(&proto, &alpns);
- result = Curl_alpn_to_proto_str(&proto, connssl->alpn);
- if(result ||
- wolfSSL_UseALPN(backend->handle,
- (char *)proto.data, (unsigned int)proto.len,
+ if(wolfSSL_UseALPN(wctx->ssl, (char *)proto.data,
+ (unsigned int)proto.len,
WOLFSSL_ALPN_CONTINUE_ON_MISMATCH) != WOLFSSL_SUCCESS) {
failf(data, "SSL: failed setting ALPN protocols");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
- infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
+ CURL_TRC_CF(data, cf, "set ALPN: %s", proto.data);
}
#endif /* HAVE_ALPN */
#ifdef OPENSSL_EXTRA
if(Curl_tls_keylog_enabled()) {
/* Ensure the Client Random is preserved. */
- wolfSSL_KeepArrays(backend->handle);
+ wolfSSL_KeepArrays(wctx->ssl);
#if defined(HAVE_SECRET_CALLBACK) && defined(WOLFSSL_TLS13)
- wolfSSL_set_tls13_secret_cb(backend->handle,
- wolfssl_tls13_secret_callback, NULL);
+ wolfSSL_set_tls13_secret_cb(wctx->ssl,
+ wssl_tls13_secret_callback, NULL);
#endif
}
#endif /* OPENSSL_EXTRA */
#ifdef HAVE_SECURE_RENEGOTIATION
- if(wolfSSL_UseSecureRenegotiation(backend->handle) != SSL_SUCCESS) {
+ if(wolfSSL_UseSecureRenegotiation(wctx->ssl) != SSL_SUCCESS) {
failf(data, "SSL: failed setting secure renegotiation");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
#endif /* HAVE_SECURE_RENEGOTIATION */
- /* Check if there is a cached ID we can/should use here! */
- if(ssl_config->primary.cache_session) {
- /* Set session from cache if there is one */
- (void)wssl_setup_session(cf, data, backend, &connssl->peer);
- /* Register to get notified when a new session is received */
- wolfSSL_set_app_data(backend->handle, cf);
- wolfSSL_CTX_sess_set_new_cb(backend->ctx, wssl_vtls_new_session_cb);
- }
-
-#ifdef USE_ECH
+#ifdef USE_ECH_WOLFSSL
if(ECH_ENABLED(data)) {
int trying_ech_now = 0;
if(data->set.str[STRING_ECH_PUBLIC]) {
infof(data, "ECH: outername not (yet) supported with wolfSSL");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
if(data->set.tls_ech == CURLECH_GREASE) {
- infof(data, "ECH: GREASE'd ECH not yet supported for wolfSSL");
- return CURLE_SSL_CONNECT_ERROR;
+ infof(data, "ECH: GREASE is done by default by wolfSSL: no need to ask");
}
if(data->set.tls_ech & CURLECH_CLA_CFG
&& data->set.str[STRING_ECH_CONFIG]) {
@@ -1217,10 +1363,12 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
b64len = (word32) strlen(b64val);
if(b64len
- && wolfSSL_SetEchConfigsBase64(backend->handle, b64val, b64len)
+ && wolfSSL_SetEchConfigsBase64(wctx->ssl, b64val, b64len)
!= WOLFSSL_SUCCESS) {
- if(data->set.tls_ech & CURLECH_HARD)
- return CURLE_SSL_CONNECT_ERROR;
+ if(data->set.tls_ech & CURLECH_HARD) {
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
+ }
}
else {
trying_ech_now = 1;
@@ -1228,13 +1376,17 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
}
}
else {
+ struct ssl_connect_data *connssl = cf->ctx;
struct Curl_dns_entry *dns = NULL;
- dns = Curl_fetch_addr(data, connssl->peer.hostname, connssl->peer.port);
+ dns = Curl_dnscache_get(data, connssl->peer.hostname, connssl->peer.port,
+ cf->conn->ip_version);
if(!dns) {
infof(data, "ECH: requested but no DNS info available");
- if(data->set.tls_ech & CURLECH_HARD)
- return CURLE_SSL_CONNECT_ERROR;
+ if(data->set.tls_ech & CURLECH_HARD) {
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
+ }
}
else {
struct Curl_https_rrinfo *rinfo = NULL;
@@ -1245,11 +1397,13 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
size_t elen = rinfo->echconfiglist_len;
infof(data, "ECH: ECHConfig from DoH HTTPS RR");
- if(wolfSSL_SetEchConfigs(backend->handle, ecl, (word32) elen) !=
+ if(wolfSSL_SetEchConfigs(wctx->ssl, ecl, (word32) elen) !=
WOLFSSL_SUCCESS) {
infof(data, "ECH: wolfSSL_SetEchConfigs failed");
- if(data->set.tls_ech & CURLECH_HARD)
- return CURLE_SSL_CONNECT_ERROR;
+ if(data->set.tls_ech & CURLECH_HARD) {
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
+ }
}
else {
trying_ech_now = 1;
@@ -1258,49 +1412,107 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
}
else {
infof(data, "ECH: requested but no ECHConfig available");
- if(data->set.tls_ech & CURLECH_HARD)
- return CURLE_SSL_CONNECT_ERROR;
+ if(data->set.tls_ech & CURLECH_HARD) {
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
+ }
}
Curl_resolv_unlink(data, &dns);
}
}
- if(trying_ech_now
- && SSL_set_min_proto_version(backend->handle, TLS1_3_VERSION) != 1) {
+ if(trying_ech_now && wolfSSL_set_min_proto_version(wctx->ssl,
+ TLS1_3_VERSION) != 1) {
infof(data, "ECH: cannot force TLSv1.3 [ERROR]");
- return CURLE_SSL_CONNECT_ERROR;
+ result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
}
-#endif /* USE_ECH */
+#endif /* USE_ECH_WOLFSSL */
+
+ result = CURLE_OK;
+
+out:
+ if(result && wctx->ssl) {
+ wolfSSL_free(wctx->ssl);
+ wctx->ssl = NULL;
+ }
+ if(result && wctx->ssl_ctx) {
+ wolfSSL_CTX_free(wctx->ssl_ctx);
+ wctx->ssl_ctx = NULL;
+ }
+ return result;
+}
+
+/*
+ * This function loads all the client/CA certificates and CRLs. Setup the TLS
+ * layer and do all necessary magic.
+ */
+static CURLcode
+wssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ CURLcode result;
+
+ DEBUGASSERT(wssl);
+
+ if(connssl->state == ssl_connection_complete)
+ return CURLE_OK;
+
+ result = Curl_wssl_ctx_init(wssl, cf, data, &connssl->peer,
+ connssl->alpn, NULL, NULL, cf,
+ wssl_on_session_reuse);
+ if(result)
+ return result;
+
+#ifdef HAVE_ALPN
+ if(connssl->alpn && (connssl->state != ssl_connection_deferred)) {
+ struct alpn_proto_buf proto;
+ memset(&proto, 0, sizeof(proto));
+ Curl_alpn_to_proto_str(&proto, connssl->alpn);
+ infof(data, VTLS_INFOF_ALPN_OFFER_1STR, proto.data);
+ }
+#endif
+
+ /* Enable RFC2818 checks */
+ if(conn_config->verifyhost) {
+ char *snihost = connssl->peer.sni ?
+ connssl->peer.sni : connssl->peer.hostname;
+ if(wolfSSL_check_domain_name(wssl->ssl, snihost) !=
+ WOLFSSL_SUCCESS) {
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
#ifdef USE_BIO_CHAIN
{
WOLFSSL_BIO *bio;
- bio = wolfSSL_BIO_new(wolfssl_bio_cf_method);
+ bio = wolfSSL_BIO_new(wssl_bio_cf_method);
if(!bio)
return CURLE_OUT_OF_MEMORY;
wolfSSL_BIO_set_data(bio, cf);
- wolfSSL_set_bio(backend->handle, bio, bio);
+ wolfSSL_set_bio(wssl->ssl, bio, bio);
}
#else /* USE_BIO_CHAIN */
/* pass the raw socket into the SSL layer */
- if(!wolfSSL_set_fd(backend->handle,
+ if(!wolfSSL_set_fd(wssl->ssl,
(int)Curl_conn_cf_get_socket(cf, data))) {
- failf(data, "SSL: SSL_set_fd failed");
+ failf(data, "SSL: wolfSSL_set_fd failed");
return CURLE_SSL_CONNECT_ERROR;
}
#endif /* !USE_BIO_CHAIN */
- connssl->connecting_state = ssl_connect_2;
return CURLE_OK;
}
-static char *wolfssl_strerror(unsigned long error, char *buf,
- unsigned long size)
+static char *wssl_strerror(unsigned long error, char *buf,
+ unsigned long size)
{
DEBUGASSERT(size > 40);
*buf = '\0';
@@ -1316,15 +1528,10 @@ static char *wolfssl_strerror(unsigned long error, char *buf,
return buf;
}
-
-static CURLcode
-wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
+CURLcode Curl_wssl_verify_pinned(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct wssl_ctx *wssl)
{
- int ret = -1;
- struct ssl_connect_data *connssl = cf->ctx;
- struct wolfssl_ctx *backend =
- (struct wolfssl_ctx *)connssl->backend;
- struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
#ifndef CURL_DISABLE_PROXY
const char * const pinnedpubkey = Curl_ssl_cf_is_proxy(cf) ?
data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] :
@@ -1333,29 +1540,143 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
const char * const pinnedpubkey = data->set.str[STRING_SSL_PINNEDPUBLICKEY];
#endif
- DEBUGASSERT(backend);
+ if(pinnedpubkey) {
+#ifdef KEEP_PEER_CERT
+ WOLFSSL_X509 *x509;
+ const char *x509_der;
+ int x509_der_len;
+ struct Curl_X509certificate x509_parsed;
+ struct Curl_asn1Element *pubkey;
+ CURLcode result;
+
+ x509 = wolfSSL_get_peer_certificate(wssl->ssl);
+ if(!x509) {
+ failf(data, "SSL: failed retrieving server certificate");
+ return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+ }
- wolfSSL_ERR_clear_error();
+ x509_der = (const char *)wolfSSL_X509_get_der(x509, &x509_der_len);
+ if(!x509_der) {
+ failf(data, "SSL: failed retrieving ASN.1 server certificate");
+ return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+ }
- /* Enable RFC2818 checks */
- if(conn_config->verifyhost) {
- char *snihost = connssl->peer.sni ?
- connssl->peer.sni : connssl->peer.hostname;
- if(wolfSSL_check_domain_name(backend->handle, snihost) == WOLFSSL_FAILURE)
- return CURLE_SSL_CONNECT_ERROR;
+ memset(&x509_parsed, 0, sizeof(x509_parsed));
+ if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len))
+ return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+
+ pubkey = &x509_parsed.subjectPublicKeyInfo;
+ if(!pubkey->header || pubkey->end <= pubkey->header) {
+ failf(data, "SSL: failed retrieving public key from server certificate");
+ return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
+ }
+
+ result = Curl_pin_peer_pubkey(data,
+ pinnedpubkey,
+ (const unsigned char *)pubkey->header,
+ (size_t)(pubkey->end - pubkey->header));
+ wolfSSL_FreeX509(x509);
+ if(result) {
+ failf(data, "SSL: public key does not match pinned public key");
+ return result;
+ }
+#else
+ failf(data, "Library lacks pinning support built-in");
+ return CURLE_NOT_BUILT_IN;
+#endif
}
+ return CURLE_OK;
+}
- if(!backend->x509_store_setup) {
- /* After having send off the ClientHello, we prepare the x509
- * store to verify the coming certificate from the server */
- CURLcode result;
- result = Curl_wssl_setup_x509_store(cf, data, backend);
+#ifdef WOLFSSL_EARLY_DATA
+static CURLcode wssl_send_earlydata(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
+ CURLcode result = CURLE_OK;
+ const unsigned char *buf;
+ size_t blen;
+
+ DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_sending);
+ wssl->io_result = CURLE_OK;
+ while(Curl_bufq_peek(&connssl->earlydata, &buf, &blen)) {
+ int nwritten = 0, rc;
+
+ wolfSSL_ERR_clear_error();
+ rc = wolfSSL_write_early_data(wssl->ssl, buf, (int)blen, &nwritten);
+ CURL_TRC_CF(data, cf, "wolfSSL_write_early_data(len=%zu) -> %d, %d",
+ blen, rc, nwritten);
+ if(rc < 0) {
+ int err = wolfSSL_get_error(wssl->ssl, rc);
+ switch(err) {
+ case WOLFSSL_ERROR_NONE: /* just did not get anything */
+ case WOLFSSL_ERROR_WANT_READ:
+ case WOLFSSL_ERROR_WANT_WRITE:
+ result = CURLE_AGAIN;
+ break;
+ default: {
+ char error_buffer[256];
+ int detail = wolfSSL_get_error(wssl->ssl, err);
+ CURL_TRC_CF(data, cf, "SSL send early data, error: '%s'(%d)",
+ wssl_strerror((unsigned long)err, error_buffer,
+ sizeof(error_buffer)),
+ detail);
+ result = CURLE_SEND_ERROR;
+ break;
+ }
+ }
+ goto out;
+ }
+
+ Curl_bufq_skip(&connssl->earlydata, (size_t)nwritten);
+ }
+ /* sent everything there was */
+ connssl->earlydata_state = ssl_earlydata_sent;
+ if(!Curl_ssl_cf_is_proxy(cf))
+ Curl_pgrsEarlyData(data, (curl_off_t)connssl->earlydata_skip);
+ infof(data, "SSL sending %zu bytes of early data", connssl->earlydata_skip);
+out:
+ return result;
+}
+#endif /* WOLFSSL_EARLY_DATA */
+
+static CURLcode wssl_handshake(struct Curl_cfilter *cf,
+ struct Curl_easy *data)
+{
+ struct ssl_connect_data *connssl = cf->ctx;
+ struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
+ struct ssl_primary_config *conn_config = Curl_ssl_cf_get_primary_config(cf);
+ int ret = -1, detail;
+ CURLcode result;
+
+ DEBUGASSERT(wssl);
+ connssl->io_need = CURL_SSL_IO_NEED_NONE;
+
+#ifdef WOLFSSL_EARLY_DATA
+ if(connssl->earlydata_state == ssl_earlydata_sending) {
+ result = wssl_send_earlydata(cf, data);
if(result)
return result;
}
+ DEBUGASSERT((connssl->earlydata_state == ssl_earlydata_none) ||
+ (connssl->earlydata_state == ssl_earlydata_sent));
+#else
+ DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_none);
+#endif /* WOLFSSL_EARLY_DATA */
- connssl->io_need = CURL_SSL_IO_NEED_NONE;
- ret = wolfSSL_connect(backend->handle);
+ wolfSSL_ERR_clear_error();
+ ret = wolfSSL_connect(wssl->ssl);
+
+ if(!wssl->x509_store_setup) {
+ /* After having send off the ClientHello, we prepare the x509
+ * store to verify the coming certificate from the server */
+ result = Curl_wssl_setup_x509_store(cf, data, wssl);
+ if(result) {
+ CURL_TRC_CF(data, cf, "Curl_wssl_setup_x509_store() -> %d", result);
+ return result;
+ }
+ }
#ifdef OPENSSL_EXTRA
if(Curl_tls_keylog_enabled()) {
@@ -1371,67 +1692,49 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
* changes, the worst case is that no key is logged on error.
*/
if(ret == WOLFSSL_SUCCESS ||
- (!wolfSSL_want_read(backend->handle) &&
- !wolfSSL_want_write(backend->handle))) {
- wolfssl_log_tls12_secret(backend->handle);
+ (!wolfSSL_want_read(wssl->ssl) &&
+ !wolfSSL_want_write(wssl->ssl))) {
+ wssl_log_tls12_secret(wssl->ssl);
/* Client Random and master secrets are no longer needed, erase these.
* Ignored while the handshake is still in progress. */
- wolfSSL_FreeArrays(backend->handle);
+ wolfSSL_FreeArrays(wssl->ssl);
}
}
#endif /* OPENSSL_EXTRA */
- if(ret != 1) {
- int detail = wolfSSL_get_error(backend->handle, ret);
+ detail = wolfSSL_get_error(wssl->ssl, ret);
+ CURL_TRC_CF(data, cf, "wolfSSL_connect() -> %d, detail=%d", ret, detail);
+ if(ret == WOLFSSL_SUCCESS) {
+ return CURLE_OK;
+ }
+ else {
if(WOLFSSL_ERROR_WANT_READ == detail) {
connssl->io_need = CURL_SSL_IO_NEED_RECV;
- return CURLE_OK;
+ return CURLE_AGAIN;
}
else if(WOLFSSL_ERROR_WANT_WRITE == detail) {
connssl->io_need = CURL_SSL_IO_NEED_SEND;
- return CURLE_OK;
+ return CURLE_AGAIN;
}
- /* There is no easy way to override only the CN matching.
- * This will enable the override of both mismatching SubjectAltNames
- * as also mismatching CN fields */
else if(DOMAIN_NAME_MISMATCH == detail) {
-#if 1
+ /* There is no easy way to override only the CN matching.
+ * This will enable the override of both mismatching SubjectAltNames
+ * as also mismatching CN fields */
failf(data, " subject alt name(s) or common name do not match \"%s\"",
connssl->peer.dispname);
return CURLE_PEER_FAILED_VERIFICATION;
-#else
- /* When the wolfssl_check_domain_name() is used and you desire to
- * continue on a DOMAIN_NAME_MISMATCH, i.e. 'ssl_config.verifyhost
- * == 0', CyaSSL version 2.4.0 will fail with an INCOMPLETE_DATA
- * error. The only way to do this is currently to switch the
- * Wolfssl_check_domain_name() in and out based on the
- * 'ssl_config.verifyhost' value. */
- if(conn_config->verifyhost) {
- failf(data,
- " subject alt name(s) or common name do not match \"%s\"\n",
- connssl->dispname);
- return CURLE_PEER_FAILED_VERIFICATION;
- }
- else {
- infof(data,
- " subject alt name(s) and/or common name do not match \"%s\"",
- connssl->dispname);
- return CURLE_OK;
- }
-#endif
}
else if(ASN_NO_SIGNER_E == detail) {
if(conn_config->verifypeer) {
failf(data, " CA signer not available for verification");
return CURLE_SSL_CACERT_BADFILE;
}
- else {
- /* Just continue with a warning if no strict certificate
- verification is required. */
- infof(data, "CA signer not available for verification, "
- "continuing anyway");
- }
+ /* Just continue with a warning if no strict certificate
+ verification is required. */
+ infof(data, "CA signer not available for verification, "
+ "continuing anyway");
+ return CURLE_OK;
}
else if(ASN_AFTER_DATE_E == detail) {
failf(data, "server verification failed: certificate has expired.");
@@ -1441,7 +1744,16 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
failf(data, "server verification failed: certificate not valid yet.");
return CURLE_PEER_FAILED_VERIFICATION;
}
-#ifdef USE_ECH
+ else if(wssl->io_result) {
+ switch(wssl->io_result) {
+ case CURLE_SEND_ERROR:
+ case CURLE_RECV_ERROR:
+ return CURLE_SSL_CONNECT_ERROR;
+ default:
+ return wssl->io_result;
+ }
+ }
+#ifdef USE_ECH_WOLFSSL
else if(-1 == detail) {
/* try access a retry_config ECHConfigList for tracing */
byte echConfigs[1000];
@@ -1449,7 +1761,7 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
int rv = 0;
/* this currently does not produce the retry_configs */
- rv = wolfSSL_GetEchConfigs(backend->handle, echConfigs,
+ rv = wolfSSL_GetEchConfigs(wssl->ssl, echConfigs,
&echConfigsLen);
if(rv != WOLFSSL_SUCCESS) {
infof(data, "Failed to get ECHConfigs");
@@ -1458,160 +1770,100 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
char *b64str = NULL;
size_t blen = 0;
- rv = Curl_base64_encode((const char *)echConfigs, echConfigsLen,
- &b64str, &blen);
- if(!rv && b64str)
+ result = curlx_base64_encode((const char *)echConfigs, echConfigsLen,
+ &b64str, &blen);
+ if(!result && b64str)
infof(data, "ECH: (not yet) retry_configs %s", b64str);
free(b64str);
}
+ return CURLE_SSL_CONNECT_ERROR;
}
#endif
- else if(backend->io_result == CURLE_AGAIN) {
- return CURLE_OK;
- }
else {
char error_buffer[256];
failf(data, "SSL_connect failed with error %d: %s", detail,
- wolfssl_strerror((unsigned long)detail, error_buffer,
- sizeof(error_buffer)));
- return CURLE_SSL_CONNECT_ERROR;
- }
- }
-
- if(pinnedpubkey) {
-#ifdef KEEP_PEER_CERT
- WOLFSSL_X509 *x509;
- const char *x509_der;
- int x509_der_len;
- struct Curl_X509certificate x509_parsed;
- struct Curl_asn1Element *pubkey;
- CURLcode result;
-
- x509 = wolfSSL_get_peer_certificate(backend->handle);
- if(!x509) {
- failf(data, "SSL: failed retrieving server certificate");
- return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
- }
-
- x509_der = (const char *)wolfSSL_X509_get_der(x509, &x509_der_len);
- if(!x509_der) {
- failf(data, "SSL: failed retrieving ASN.1 server certificate");
- return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
- }
-
- memset(&x509_parsed, 0, sizeof(x509_parsed));
- if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len))
- return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
-
- pubkey = &x509_parsed.subjectPublicKeyInfo;
- if(!pubkey->header || pubkey->end <= pubkey->header) {
- failf(data, "SSL: failed retrieving public key from server certificate");
- return CURLE_SSL_PINNEDPUBKEYNOTMATCH;
- }
-
- result = Curl_pin_peer_pubkey(data,
- pinnedpubkey,
- (const unsigned char *)pubkey->header,
- (size_t)(pubkey->end - pubkey->header));
- wolfSSL_FreeX509(x509);
- if(result) {
- failf(data, "SSL: public key does not match pinned public key");
- return result;
- }
-#else
- failf(data, "Library lacks pinning support built-in");
- return CURLE_NOT_BUILT_IN;
-#endif
- }
-
-#ifdef HAVE_ALPN
- if(connssl->alpn) {
- int rc;
- char *protocol = NULL;
- unsigned short protocol_len = 0;
-
- rc = wolfSSL_ALPN_GetProtocol(backend->handle, &protocol, &protocol_len);
-
- if(rc == WOLFSSL_SUCCESS) {
- Curl_alpn_set_negotiated(cf, data, connssl,
- (const unsigned char *)protocol, protocol_len);
- }
- else if(rc == WOLFSSL_ALPN_NOT_FOUND)
- Curl_alpn_set_negotiated(cf, data, connssl, NULL, 0);
- else {
- failf(data, "ALPN, failure getting protocol, error %d", rc);
+ wssl_strerror((unsigned long)detail, error_buffer,
+ sizeof(error_buffer)));
return CURLE_SSL_CONNECT_ERROR;
}
}
-#endif /* HAVE_ALPN */
-
- connssl->connecting_state = ssl_connect_3;
-#if (LIBWOLFSSL_VERSION_HEX >= 0x03009010)
- infof(data, "SSL connection using %s / %s",
- wolfSSL_get_version(backend->handle),
- wolfSSL_get_cipher_name(backend->handle));
-#else
- infof(data, "SSL connected");
-#endif
-
- return CURLE_OK;
}
-static ssize_t wolfssl_send(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- const void *mem,
- size_t len,
- CURLcode *curlcode)
+static ssize_t wssl_send(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const void *buf, size_t blen,
+ CURLcode *curlcode)
{
struct ssl_connect_data *connssl = cf->ctx;
- struct wolfssl_ctx *backend =
- (struct wolfssl_ctx *)connssl->backend;
- int memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
- int rc;
-
- DEBUGASSERT(backend);
+ struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
+ size_t total_written = 0;
+ ssize_t nwritten = -1;
+ DEBUGASSERT(wssl);
wolfSSL_ERR_clear_error();
- rc = wolfSSL_write(backend->handle, mem, memlen);
- if(rc <= 0) {
- int err = wolfSSL_get_error(backend->handle, rc);
+ if(blen) {
+ int memlen = (blen > (size_t)INT_MAX) ? INT_MAX : (int)blen;
+ int rc;
- switch(err) {
- case WOLFSSL_ERROR_WANT_READ:
- case WOLFSSL_ERROR_WANT_WRITE:
- /* there is data pending, re-invoke SSL_write() */
- CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len);
- *curlcode = CURLE_AGAIN;
- return -1;
- default:
- if(backend->io_result == CURLE_AGAIN) {
- CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> AGAIN", len);
+ rc = wolfSSL_write(wssl->ssl, buf, memlen);
+ if(rc <= 0) {
+ int err = wolfSSL_get_error(wssl->ssl, rc);
+
+ switch(err) {
+ case WOLFSSL_ERROR_WANT_READ:
+ case WOLFSSL_ERROR_WANT_WRITE:
+ /* there is data pending, re-invoke wolfSSL_write() */
+ if(total_written) {
+ *curlcode = CURLE_OK;
+ nwritten = total_written;
+ goto out;
+ }
*curlcode = CURLE_AGAIN;
- return -1;
- }
- CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> %d, %d", len, rc, err);
- {
- char error_buffer[256];
- failf(data, "SSL write: %s, errno %d",
- wolfssl_strerror((unsigned long)err, error_buffer,
- sizeof(error_buffer)),
- SOCKERRNO);
+ nwritten = -1;
+ goto out;
+
+ default:
+ if(wssl->io_result == CURLE_AGAIN) {
+ if(total_written) {
+ *curlcode = CURLE_OK;
+ nwritten = total_written;
+ goto out;
+ }
+ *curlcode = CURLE_AGAIN;
+ nwritten = -1;
+ goto out;
+ }
+ {
+ char error_buffer[256];
+ failf(data, "SSL write: %s, errno %d",
+ wssl_strerror((unsigned long)err, error_buffer,
+ sizeof(error_buffer)),
+ SOCKERRNO);
+ }
+ *curlcode = CURLE_SEND_ERROR;
+ nwritten = -1;
+ goto out;
}
- *curlcode = CURLE_SEND_ERROR;
- return -1;
}
+ else
+ total_written += rc;
}
- CURL_TRC_CF(data, cf, "wolfssl_send(len=%zu) -> %d", len, rc);
- return rc;
+
+ *curlcode = CURLE_OK;
+ nwritten = total_written;
+out:
+ CURL_TRC_CF(data, cf, "wssl_send(len=%zu) -> %" FMT_OFF_T ", %d",
+ blen, nwritten, *curlcode);
+ return nwritten;
}
-static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool send_shutdown, bool *done)
+static CURLcode wssl_shutdown(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool send_shutdown, bool *done)
{
struct ssl_connect_data *connssl = cf->ctx;
- struct wolfssl_ctx *wctx = (struct wolfssl_ctx *)connssl->backend;
+ struct wssl_ctx *wctx = (struct wssl_ctx *)connssl->backend;
CURLcode result = CURLE_OK;
char buf[1024];
char error_buffer[256];
@@ -1620,7 +1872,7 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
int detail;
DEBUGASSERT(wctx);
- if(!wctx->handle || cf->shutdown) {
+ if(!wctx->ssl || cf->shutdown) {
*done = TRUE;
goto out;
}
@@ -1628,12 +1880,12 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
wctx->shutting_down = TRUE;
connssl->io_need = CURL_SSL_IO_NEED_NONE;
*done = FALSE;
- if(!(wolfSSL_get_shutdown(wctx->handle) & WOLFSSL_SENT_SHUTDOWN)) {
+ if(!(wolfSSL_get_shutdown(wctx->ssl) & WOLFSSL_SENT_SHUTDOWN)) {
/* We have not started the shutdown from our side yet. Check
* if the server already sent us one. */
wolfSSL_ERR_clear_error();
- nread = wolfSSL_read(wctx->handle, buf, (int)sizeof(buf));
- err = wolfSSL_get_error(wctx->handle, nread);
+ nread = wolfSSL_read(wctx->ssl, buf, (int)sizeof(buf));
+ err = wolfSSL_get_error(wctx->ssl, nread);
CURL_TRC_CF(data, cf, "wolfSSL_read, nread=%d, err=%d", nread, err);
if(!nread && err == WOLFSSL_ERROR_ZERO_RETURN) {
bool input_pending;
@@ -1655,16 +1907,16 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
}
}
- /* SSL should now have started the shutdown from our side. Since it
+ /* wolfSSL should now have started the shutdown from our side. Since it
* was not complete, we are lacking the close notify from the server. */
if(send_shutdown) {
wolfSSL_ERR_clear_error();
- if(wolfSSL_shutdown(wctx->handle) == 1) {
+ if(wolfSSL_shutdown(wctx->ssl) == 1) {
CURL_TRC_CF(data, cf, "SSL shutdown finished");
*done = TRUE;
goto out;
}
- if(WOLFSSL_ERROR_WANT_WRITE == wolfSSL_get_error(wctx->handle, nread)) {
+ if(WOLFSSL_ERROR_WANT_WRITE == wolfSSL_get_error(wctx->ssl, nread)) {
CURL_TRC_CF(data, cf, "SSL shutdown still wants to send");
connssl->io_need = CURL_SSL_IO_NEED_SEND;
goto out;
@@ -1675,11 +1927,11 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
for(i = 0; i < 10; ++i) {
wolfSSL_ERR_clear_error();
- nread = wolfSSL_read(wctx->handle, buf, (int)sizeof(buf));
+ nread = wolfSSL_read(wctx->ssl, buf, (int)sizeof(buf));
if(nread <= 0)
break;
}
- err = wolfSSL_get_error(wctx->handle, nread);
+ err = wolfSSL_get_error(wctx->ssl, nread);
switch(err) {
case WOLFSSL_ERROR_ZERO_RETURN: /* no more data */
CURL_TRC_CF(data, cf, "SSL shutdown received");
@@ -1687,7 +1939,7 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
break;
case WOLFSSL_ERROR_NONE: /* just did not get anything */
case WOLFSSL_ERROR_WANT_READ:
- /* SSL has send its notify and now wants to read the reply
+ /* wolfSSL has send its notify and now wants to read the reply
* from the server. We are not really interested in that. */
CURL_TRC_CF(data, cf, "SSL shutdown sent, want receive");
connssl->io_need = CURL_SSL_IO_NEED_RECV;
@@ -1697,10 +1949,10 @@ static CURLcode wolfssl_shutdown(struct Curl_cfilter *cf,
connssl->io_need = CURL_SSL_IO_NEED_SEND;
break;
default:
- detail = wolfSSL_get_error(wctx->handle, err);
+ detail = wolfSSL_get_error(wctx->ssl, err);
CURL_TRC_CF(data, cf, "SSL shutdown, error: '%s'(%d)",
- wolfssl_strerror((unsigned long)err, error_buffer,
- sizeof(error_buffer)),
+ wssl_strerror((unsigned long)err, error_buffer,
+ sizeof(error_buffer)),
detail);
result = CURLE_RECV_ERROR;
break;
@@ -1711,92 +1963,90 @@ out:
return result;
}
-static void wolfssl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
+static void wssl_close(struct Curl_cfilter *cf, struct Curl_easy *data)
{
struct ssl_connect_data *connssl = cf->ctx;
- struct wolfssl_ctx *backend =
- (struct wolfssl_ctx *)connssl->backend;
+ struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
(void) data;
- DEBUGASSERT(backend);
+ DEBUGASSERT(wssl);
- if(backend->handle) {
- wolfSSL_free(backend->handle);
- backend->handle = NULL;
+ if(wssl->ssl) {
+ wolfSSL_free(wssl->ssl);
+ wssl->ssl = NULL;
}
- if(backend->ctx) {
- wolfSSL_CTX_free(backend->ctx);
- backend->ctx = NULL;
+ if(wssl->ssl_ctx) {
+ wolfSSL_CTX_free(wssl->ssl_ctx);
+ wssl->ssl_ctx = NULL;
}
}
-static ssize_t wolfssl_recv(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- char *buf, size_t blen,
- CURLcode *curlcode)
+static ssize_t wssl_recv(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ char *buf, size_t blen,
+ CURLcode *curlcode)
{
struct ssl_connect_data *connssl = cf->ctx;
- struct wolfssl_ctx *backend =
- (struct wolfssl_ctx *)connssl->backend;
+ struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
int buffsize = (blen > (size_t)INT_MAX) ? INT_MAX : (int)blen;
int nread;
- DEBUGASSERT(backend);
+ DEBUGASSERT(wssl);
wolfSSL_ERR_clear_error();
*curlcode = CURLE_OK;
- nread = wolfSSL_read(backend->handle, buf, buffsize);
+ nread = wolfSSL_read(wssl->ssl, buf, buffsize);
if(nread <= 0) {
- int err = wolfSSL_get_error(backend->handle, nread);
+ int err = wolfSSL_get_error(wssl->ssl, nread);
switch(err) {
case WOLFSSL_ERROR_ZERO_RETURN: /* no more data */
- CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> CLOSED", blen);
+ CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> CLOSED", blen);
*curlcode = CURLE_OK;
return 0;
case WOLFSSL_ERROR_NONE:
case WOLFSSL_ERROR_WANT_READ:
case WOLFSSL_ERROR_WANT_WRITE:
- if(!backend->io_result && connssl->peer_closed) {
- CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> CLOSED", blen);
+ if(!wssl->io_result && connssl->peer_closed) {
+ CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> CLOSED", blen);
*curlcode = CURLE_OK;
return 0;
}
/* there is data pending, re-invoke wolfSSL_read() */
- CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen);
+ CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> AGAIN", blen);
*curlcode = CURLE_AGAIN;
return -1;
default:
- if(backend->io_result == CURLE_AGAIN) {
- CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> AGAIN", blen);
+ if(wssl->io_result == CURLE_AGAIN) {
+ CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> AGAIN", blen);
*curlcode = CURLE_AGAIN;
return -1;
}
- else if(!backend->io_result && connssl->peer_closed) {
- CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> CLOSED", blen);
+ else if(!wssl->io_result && connssl->peer_closed) {
+ CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> CLOSED", blen);
*curlcode = CURLE_OK;
return 0;
}
else {
char error_buffer[256];
failf(data, "SSL read: %s, errno %d",
- wolfssl_strerror((unsigned long)err, error_buffer,
- sizeof(error_buffer)),
+ wssl_strerror((unsigned long)err, error_buffer,
+ sizeof(error_buffer)),
SOCKERRNO);
}
*curlcode = CURLE_RECV_ERROR;
return -1;
}
}
- CURL_TRC_CF(data, cf, "wolfssl_recv(len=%zu) -> %d", blen, nread);
+
+ CURL_TRC_CF(data, cf, "wssl_recv(len=%zu) -> %d", blen, nread);
return nread;
}
-
-static size_t wolfssl_version(char *buffer, size_t size)
+size_t Curl_wssl_version(char *buffer, size_t size)
{
#if LIBWOLFSSL_VERSION_HEX >= 0x03006000
return msnprintf(buffer, size, "wolfSSL/%s", wolfSSL_lib_version());
@@ -1806,7 +2056,7 @@ static size_t wolfssl_version(char *buffer, size_t size)
}
-static int wolfssl_init(void)
+static int wssl_init(void)
{
int ret;
@@ -1814,14 +2064,14 @@ static int wolfssl_init(void)
Curl_tls_keylog_open();
#endif
ret = (wolfSSL_Init() == WOLFSSL_SUCCESS);
- wolfssl_bio_cf_init_methods();
+ wssl_bio_cf_init_methods();
return ret;
}
-static void wolfssl_cleanup(void)
+static void wssl_cleanup(void)
{
- wolfssl_bio_cf_free_methods();
+ wssl_bio_cf_free_methods();
wolfSSL_Cleanup();
#ifdef OPENSSL_EXTRA
Curl_tls_keylog_close();
@@ -1829,31 +2079,29 @@ static void wolfssl_cleanup(void)
}
-static bool wolfssl_data_pending(struct Curl_cfilter *cf,
- const struct Curl_easy *data)
+static bool wssl_data_pending(struct Curl_cfilter *cf,
+ const struct Curl_easy *data)
{
struct ssl_connect_data *ctx = cf->ctx;
- struct wolfssl_ctx *backend;
+ struct wssl_ctx *wssl;
(void)data;
DEBUGASSERT(ctx && ctx->backend);
- backend = (struct wolfssl_ctx *)ctx->backend;
- if(backend->handle) /* SSL is in use */
- return wolfSSL_pending(backend->handle);
+ wssl = (struct wssl_ctx *)ctx->backend;
+ if(wssl->ssl) /* wolfSSL is in use */
+ return wolfSSL_pending(wssl->ssl);
else
return FALSE;
}
-static CURLcode
-wolfssl_connect_common(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool nonblocking,
- bool *done)
+static CURLcode wssl_connect(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ bool *done)
{
- CURLcode result;
struct ssl_connect_data *connssl = cf->ctx;
- curl_socket_t sockfd = Curl_conn_cf_get_socket(cf, data);
+ struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
+ CURLcode result = CURLE_OK;
/* check if the connection has already been established */
if(ssl_connection_complete == connssl->state) {
@@ -1861,116 +2109,108 @@ wolfssl_connect_common(struct Curl_cfilter *cf,
return CURLE_OK;
}
- if(ssl_connect_1 == connssl->connecting_state) {
- /* Find out how much more time we are allowed */
- const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
-
- if(timeout_ms < 0) {
- /* no need to continue if time already is up */
- failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
+ *done = FALSE;
+ connssl->io_need = CURL_SSL_IO_NEED_NONE;
- result = wolfssl_connect_step1(cf, data);
+ if(ssl_connect_1 == connssl->connecting_state) {
+ result = wssl_connect_step1(cf, data);
if(result)
return result;
+ connssl->connecting_state = ssl_connect_2;
}
- while(ssl_connect_2 == connssl->connecting_state) {
-
- /* check allowed time left */
- const timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
+ if(ssl_connect_2 == connssl->connecting_state) {
+ if(connssl->earlydata_state == ssl_earlydata_await) {
+ /* We defer the handshake until request data arrives. */
+ DEBUGASSERT(connssl->state == ssl_connection_deferred);
+ goto out;
+ }
+ result = wssl_handshake(cf, data);
+ if(result == CURLE_AGAIN)
+ goto out;
+ wssl->hs_result = result;
+ connssl->connecting_state = ssl_connect_3;
+ }
- if(timeout_ms < 0) {
- /* no need to continue if time already is up */
- failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
+ if(ssl_connect_3 == connssl->connecting_state) {
+ /* Once the handshake has errored, it stays in that state and will
+ * error again on every call. */
+ if(wssl->hs_result) {
+ result = wssl->hs_result;
+ goto out;
}
+ result = Curl_wssl_verify_pinned(cf, data, wssl);
+ if(result) {
+ wssl->hs_result = result;
+ goto out;
+ }
+ /* handhshake was done without errors */
+#ifdef HAVE_ALPN
+ if(connssl->alpn) {
+ int rc;
+ char *protocol = NULL;
+ unsigned short protocol_len = 0;
+
+ rc = wolfSSL_ALPN_GetProtocol(wssl->ssl, &protocol, &protocol_len);
- /* if ssl is expecting something, check if it is available. */
- if(connssl->io_need) {
- curl_socket_t writefd = (connssl->io_need & CURL_SSL_IO_NEED_SEND) ?
- sockfd : CURL_SOCKET_BAD;
- curl_socket_t readfd = (connssl->io_need & CURL_SSL_IO_NEED_RECV) ?
- sockfd : CURL_SOCKET_BAD;
- int what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
- nonblocking ? 0 : timeout_ms);
- if(what < 0) {
- /* fatal error */
- failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
- return CURLE_SSL_CONNECT_ERROR;
+ if(rc == WOLFSSL_SUCCESS) {
+ Curl_alpn_set_negotiated(cf, data, connssl,
+ (const unsigned char *)protocol,
+ protocol_len);
}
- else if(0 == what) {
- if(nonblocking) {
- *done = FALSE;
- return CURLE_OK;
- }
- else {
- /* timeout */
- failf(data, "SSL connection timeout");
- return CURLE_OPERATION_TIMEDOUT;
- }
+ else if(rc == WOLFSSL_ALPN_NOT_FOUND)
+ Curl_alpn_set_negotiated(cf, data, connssl, NULL, 0);
+ else {
+ failf(data, "ALPN, failure getting protocol, error %d", rc);
+ wssl->hs_result = result = CURLE_SSL_CONNECT_ERROR;
+ goto out;
}
- /* socket is readable or writable */
}
+#endif /* HAVE_ALPN */
- /* Run transaction, and return to the caller if it failed or if
- * this connection is part of a multi handle and this loop would
- * execute again. This permits the owner of a multi handle to
- * abort a connection attempt before step2 has completed while
- * ensuring that a client using select() or epoll() will always
- * have a valid fdset to wait on.
- */
- result = wolfssl_connect_step2(cf, data);
- if(result || (nonblocking && (ssl_connect_2 == connssl->connecting_state)))
- return result;
- } /* repeat step2 until all transactions are done. */
+#if (LIBWOLFSSL_VERSION_HEX >= 0x03009010)
+ infof(data, "SSL connection using %s / %s",
+ wolfSSL_get_version(wssl->ssl),
+ wolfSSL_get_cipher_name(wssl->ssl));
+#else
+ infof(data, "SSL connected");
+#endif
- if(ssl_connect_3 == connssl->connecting_state) {
- /* In other backends, this is where we verify the certificate, but
- * wolfSSL already does that as part of the handshake. */
connssl->connecting_state = ssl_connect_done;
+ connssl->state = ssl_connection_complete;
+
+#ifdef WOLFSSL_EARLY_DATA
+ if(connssl->earlydata_state > ssl_earlydata_none) {
+ /* We should be in this state by now */
+ DEBUGASSERT(connssl->earlydata_state == ssl_earlydata_sent);
+ connssl->earlydata_state =
+ (wolfSSL_get_early_data_status(wssl->ssl) ==
+ WOLFSSL_EARLY_DATA_REJECTED) ?
+ ssl_earlydata_rejected : ssl_earlydata_accepted;
+ }
+#endif /* WOLFSSL_EARLY_DATA */
}
- if(ssl_connect_done == connssl->connecting_state) {
- connssl->state = ssl_connection_complete;
+ if((connssl->connecting_state == ssl_connect_done) ||
+ (connssl->state == ssl_connection_deferred)) {
*done = TRUE;
}
- else
- *done = FALSE;
-
- /* Reset our connect state machine */
- connssl->connecting_state = ssl_connect_1;
-
- return CURLE_OK;
-}
-
-static CURLcode wolfssl_connect_nonblocking(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- bool *done)
-{
- return wolfssl_connect_common(cf, data, TRUE, done);
-}
-
-
-static CURLcode wolfssl_connect(struct Curl_cfilter *cf,
- struct Curl_easy *data)
-{
- CURLcode result;
- bool done = FALSE;
-
- result = wolfssl_connect_common(cf, data, FALSE, &done);
- if(result)
- return result;
-
- DEBUGASSERT(done);
-
- return CURLE_OK;
+out:
+ if(result) {
+ *done = FALSE;
+ if(result == CURLE_AGAIN)
+ return CURLE_OK;
+ }
+ else if((connssl->connecting_state == ssl_connect_done) ||
+ (connssl->state == ssl_connection_deferred)) {
+ *done = TRUE;
+ }
+ return result;
}
-static CURLcode wolfssl_random(struct Curl_easy *data,
- unsigned char *entropy, size_t length)
+static CURLcode wssl_random(struct Curl_easy *data,
+ unsigned char *entropy, size_t length)
{
WC_RNG rng;
(void)data;
@@ -1985,10 +2225,10 @@ static CURLcode wolfssl_random(struct Curl_easy *data,
return CURLE_OK;
}
-static CURLcode wolfssl_sha256sum(const unsigned char *tmp, /* input */
- size_t tmplen,
- unsigned char *sha256sum /* output */,
- size_t unused)
+static CURLcode wssl_sha256sum(const unsigned char *tmp, /* input */
+ size_t tmplen,
+ unsigned char *sha256sum /* output */,
+ size_t unused)
{
wc_Sha256 SHA256pw;
(void)unused;
@@ -1999,14 +2239,13 @@ static CURLcode wolfssl_sha256sum(const unsigned char *tmp, /* input */
return CURLE_OK;
}
-static void *wolfssl_get_internals(struct ssl_connect_data *connssl,
- CURLINFO info UNUSED_PARAM)
+static void *wssl_get_internals(struct ssl_connect_data *connssl,
+ CURLINFO info UNUSED_PARAM)
{
- struct wolfssl_ctx *backend =
- (struct wolfssl_ctx *)connssl->backend;
+ struct wssl_ctx *wssl = (struct wssl_ctx *)connssl->backend;
(void)info;
- DEBUGASSERT(backend);
- return backend->handle;
+ DEBUGASSERT(wssl);
+ return wssl->ssl;
}
const struct Curl_ssl Curl_ssl_wolfssl = {
@@ -2020,7 +2259,7 @@ const struct Curl_ssl Curl_ssl_wolfssl = {
#endif
SSLSUPP_CA_PATH |
SSLSUPP_CAINFO_BLOB |
-#ifdef USE_ECH
+#ifdef USE_ECH_WOLFSSL
SSLSUPP_ECH |
#endif
SSLSUPP_SSL_CTX |
@@ -2030,31 +2269,27 @@ const struct Curl_ssl Curl_ssl_wolfssl = {
SSLSUPP_CA_CACHE |
SSLSUPP_CIPHER_LIST,
- sizeof(struct wolfssl_ctx),
-
- wolfssl_init, /* init */
- wolfssl_cleanup, /* cleanup */
- wolfssl_version, /* version */
- Curl_none_check_cxn, /* check_cxn */
- wolfssl_shutdown, /* shutdown */
- wolfssl_data_pending, /* data_pending */
- wolfssl_random, /* random */
- Curl_none_cert_status_request, /* cert_status_request */
- wolfssl_connect, /* connect */
- wolfssl_connect_nonblocking, /* connect_nonblocking */
+ sizeof(struct wssl_ctx),
+
+ wssl_init, /* init */
+ wssl_cleanup, /* cleanup */
+ Curl_wssl_version, /* version */
+ wssl_shutdown, /* shutdown */
+ wssl_data_pending, /* data_pending */
+ wssl_random, /* random */
+ NULL, /* cert_status_request */
+ wssl_connect, /* connect */
Curl_ssl_adjust_pollset, /* adjust_pollset */
- wolfssl_get_internals, /* get_internals */
- wolfssl_close, /* close_one */
- Curl_none_close_all, /* close_all */
- Curl_none_set_engine, /* set_engine */
- Curl_none_set_engine_default, /* set_engine_default */
- Curl_none_engines_list, /* engines_list */
- Curl_none_false_start, /* false_start */
- wolfssl_sha256sum, /* sha256sum */
- NULL, /* associate_connection */
- NULL, /* disassociate_connection */
- wolfssl_recv, /* recv decrypted data */
- wolfssl_send, /* send data to encrypt */
+ wssl_get_internals, /* get_internals */
+ wssl_close, /* close_one */
+ NULL, /* close_all */
+ NULL, /* set_engine */
+ NULL, /* set_engine_default */
+ NULL, /* engines_list */
+ NULL, /* false_start */
+ wssl_sha256sum, /* sha256sum */
+ wssl_recv, /* recv decrypted data */
+ wssl_send, /* send data to encrypt */
NULL, /* get_channel_binding */
};
diff --git a/libs/libcurl/src/vtls/wolfssl.h b/libs/libcurl/src/vtls/wolfssl.h
index 0a4d253a58..f5f30b2018 100644
--- a/libs/libcurl/src/vtls/wolfssl.h
+++ b/libs/libcurl/src/vtls/wolfssl.h
@@ -23,43 +23,70 @@
* SPDX-License-Identifier: curl
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#ifdef USE_WOLFSSL
-#include "urldata.h"
+#include "../urldata.h"
+
+struct alpn_spec;
+struct ssl_peer;
+struct Curl_ssl_session;
struct WOLFSSL;
-typedef struct WOLFSSL WOLFSSL;
struct WOLFSSL_CTX;
-typedef struct WOLFSSL_CTX WOLFSSL_CTX;
struct WOLFSSL_SESSION;
-typedef struct WOLFSSL_SESSION WOLFSSL_SESSION;
extern const struct Curl_ssl Curl_ssl_wolfssl;
-struct wolfssl_ctx {
- WOLFSSL_CTX *ctx;
- WOLFSSL *handle;
+struct wssl_ctx {
+ struct WOLFSSL_CTX *ssl_ctx;
+ struct WOLFSSL *ssl;
CURLcode io_result; /* result of last BIO cfilter operation */
+ CURLcode hs_result; /* result of handshake */
int io_send_blocked_len; /* length of last BIO write that EAGAINed */
BIT(x509_store_setup); /* x509 store has been set up */
BIT(shutting_down); /* TLS is being shut down */
};
-CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct wolfssl_ctx *wssl);
+size_t Curl_wssl_version(char *buffer, size_t size);
-CURLcode wssl_setup_session(struct Curl_cfilter *cf,
- struct Curl_easy *data,
- struct wolfssl_ctx *wss,
- struct ssl_peer *peer);
+typedef CURLcode Curl_wssl_ctx_setup_cb(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ void *user_data);
+
+typedef CURLcode Curl_wssl_init_session_reuse_cb(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct alpn_spec *alpns,
+ struct Curl_ssl_session *scs,
+ bool *do_early_data);
-CURLcode wssl_cache_session(struct Curl_cfilter *cf,
+CURLcode Curl_wssl_ctx_init(struct wssl_ctx *wctx,
+ struct Curl_cfilter *cf,
struct Curl_easy *data,
struct ssl_peer *peer,
- WOLFSSL_SESSION *session);
+ const struct alpn_spec *alpns,
+ Curl_wssl_ctx_setup_cb *cb_setup,
+ void *cb_user_data,
+ void *ssl_user_data,
+ Curl_wssl_init_session_reuse_cb *sess_reuse_cb);
+
+CURLcode Curl_wssl_setup_x509_store(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct wssl_ctx *wssl);
+
+CURLcode Curl_wssl_cache_session(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ const char *ssl_peer_key,
+ struct WOLFSSL_SESSION *session,
+ int ietf_tls_id,
+ const char *alpn,
+ unsigned char *quic_tp,
+ size_t quic_tp_len);
+
+CURLcode Curl_wssl_verify_pinned(struct Curl_cfilter *cf,
+ struct Curl_easy *data,
+ struct wssl_ctx *wssl);
#endif /* USE_WOLFSSL */
diff --git a/libs/libcurl/src/vtls/x509asn1.c b/libs/libcurl/src/vtls/x509asn1.c
index 6e1fbfab49..1b4821484c 100644
--- a/libs/libcurl/src/vtls/x509asn1.c
+++ b/libs/libcurl/src/vtls/x509asn1.c
@@ -22,39 +22,39 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \
defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \
- defined(USE_MBEDTLS)
+ defined(USE_MBEDTLS) || defined(USE_RUSTLS)
-#if defined(USE_WOLFSSL) || defined(USE_SCHANNEL)
+#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \
+ defined(USE_MBEDTLS) || defined(USE_WOLFSSL) || defined(USE_RUSTLS)
#define WANT_PARSEX509 /* uses Curl_parseX509() */
#endif
#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \
- defined(USE_MBEDTLS)
+ defined(USE_MBEDTLS) || defined(USE_RUSTLS)
#define WANT_EXTRACT_CERTINFO /* uses Curl_extract_certinfo() */
-#define WANT_PARSEX509 /* ... uses Curl_parseX509() */
#endif
#include <curl/curl.h>
-#include "urldata.h"
-#include "strcase.h"
-#include "curl_ctype.h"
+#include "../urldata.h"
+#include "../strcase.h"
+#include "../curl_ctype.h"
#include "hostcheck.h"
-#include "vtls/vtls.h"
-#include "vtls/vtls_int.h"
-#include "sendf.h"
-#include "inet_pton.h"
-#include "curl_base64.h"
+#include "vtls.h"
+#include "vtls_int.h"
+#include "../sendf.h"
+#include "../curlx/inet_pton.h"
+#include "../curlx/base64.h"
#include "x509asn1.h"
-#include "dynbuf.h"
+#include "../curlx/dynbuf.h"
/* The last 3 #include files should be in this order */
-#include "curl_printf.h"
-#include "curl_memory.h"
-#include "memdebug.h"
+#include "../curl_printf.h"
+#include "../curl_memory.h"
+#include "../memdebug.h"
/*
* Constants.
@@ -64,10 +64,10 @@
#define CURL_ASN1_MAX ((size_t) 0x40000) /* 256K */
/* ASN.1 classes. */
-#define CURL_ASN1_UNIVERSAL 0
-#define CURL_ASN1_APPLICATION 1
-#define CURL_ASN1_CONTEXT_SPECIFIC 2
-#define CURL_ASN1_PRIVATE 3
+/* #define CURL_ASN1_UNIVERSAL 0 */
+/* #define CURL_ASN1_APPLICATION 1 */
+/* #define CURL_ASN1_CONTEXT_SPECIFIC 2 */
+/* #define CURL_ASN1_PRIVATE 3 */
/* ASN.1 types. */
#define CURL_ASN1_BOOLEAN 1
@@ -76,27 +76,27 @@
#define CURL_ASN1_OCTET_STRING 4
#define CURL_ASN1_NULL 5
#define CURL_ASN1_OBJECT_IDENTIFIER 6
-#define CURL_ASN1_OBJECT_DESCRIPTOR 7
-#define CURL_ASN1_INSTANCE_OF 8
-#define CURL_ASN1_REAL 9
+/* #define CURL_ASN1_OBJECT_DESCRIPTOR 7 */
+/* #define CURL_ASN1_INSTANCE_OF 8 */
+/* #define CURL_ASN1_REAL 9 */
#define CURL_ASN1_ENUMERATED 10
-#define CURL_ASN1_EMBEDDED 11
+/* #define CURL_ASN1_EMBEDDED 11 */
#define CURL_ASN1_UTF8_STRING 12
-#define CURL_ASN1_RELATIVE_OID 13
-#define CURL_ASN1_SEQUENCE 16
-#define CURL_ASN1_SET 17
+/* #define CURL_ASN1_RELATIVE_OID 13 */
+/* #define CURL_ASN1_SEQUENCE 16 */
+/* #define CURL_ASN1_SET 17 */
#define CURL_ASN1_NUMERIC_STRING 18
#define CURL_ASN1_PRINTABLE_STRING 19
#define CURL_ASN1_TELETEX_STRING 20
-#define CURL_ASN1_VIDEOTEX_STRING 21
+/* #define CURL_ASN1_VIDEOTEX_STRING 21 */
#define CURL_ASN1_IA5_STRING 22
#define CURL_ASN1_UTC_TIME 23
#define CURL_ASN1_GENERALIZED_TIME 24
-#define CURL_ASN1_GRAPHIC_STRING 25
+/* #define CURL_ASN1_GRAPHIC_STRING 25 */
#define CURL_ASN1_VISIBLE_STRING 26
-#define CURL_ASN1_GENERAL_STRING 27
+/* #define CURL_ASN1_GENERAL_STRING 27 */
#define CURL_ASN1_UNIVERSAL_STRING 28
-#define CURL_ASN1_CHARACTER_STRING 29
+/* #define CURL_ASN1_CHARACTER_STRING 29 */
#define CURL_ASN1_BMP_STRING 30
@@ -178,8 +178,11 @@ static const char *getASN1Element(struct Curl_asn1Element *elem,
const char *beg, const char *end)
WARN_UNUSED_RESULT;
-static const char *getASN1Element(struct Curl_asn1Element *elem,
- const char *beg, const char *end)
+#define CURL_ASN1_MAX_RECURSIONS 16
+
+static const char *getASN1Element_(struct Curl_asn1Element *elem,
+ const char *beg, const char *end,
+ size_t lvl)
{
unsigned char b;
size_t len;
@@ -190,7 +193,8 @@ static const char *getASN1Element(struct Curl_asn1Element *elem,
Returns a pointer in source string after the parsed element, or NULL
if an error occurs. */
if(!beg || !end || beg >= end || !*beg ||
- (size_t)(end - beg) > CURL_ASN1_MAX)
+ ((size_t)(end - beg) > CURL_ASN1_MAX) ||
+ lvl >= CURL_ASN1_MAX_RECURSIONS)
return NULL;
/* Process header byte. */
@@ -216,7 +220,7 @@ static const char *getASN1Element(struct Curl_asn1Element *elem,
return NULL;
elem->beg = beg;
while(beg < end && *beg) {
- beg = getASN1Element(&lelem, beg, end);
+ beg = getASN1Element_(&lelem, beg, end, lvl + 1);
if(!beg)
return NULL;
}
@@ -243,10 +247,16 @@ static const char *getASN1Element(struct Curl_asn1Element *elem,
return elem->end;
}
+static const char *getASN1Element(struct Curl_asn1Element *elem,
+ const char *beg, const char *end)
+{
+ return getASN1Element_(elem, beg, end, 0);
+}
+
#ifdef WANT_EXTRACT_CERTINFO
/*
- * Search the null terminated OID or OID identifier in local table.
+ * Search the null-terminated OID or OID identifier in local table.
* Return the table entry pointer or NULL if not found.
*/
static const struct Curl_OID *searchOID(const char *oid)
@@ -259,6 +269,17 @@ static const struct Curl_OID *searchOID(const char *oid)
return NULL;
}
+#ifdef UNITTESTS
+/* used by unit1657.c */
+CURLcode Curl_x509_getASN1Element(struct Curl_asn1Element *elem,
+ const char *beg, const char *end)
+{
+ if(getASN1Element(elem, beg, end))
+ return CURLE_OK;
+ return CURLE_BAD_FUNCTION_ARGUMENT;
+}
+#endif
+
/*
* Convert an ASN.1 Boolean value into its string representation.
*
@@ -270,7 +291,7 @@ static CURLcode bool2str(struct dynbuf *store,
{
if(end - beg != 1)
return CURLE_BAD_FUNCTION_ARGUMENT;
- return Curl_dyn_add(store, *beg ? "TRUE": "FALSE");
+ return curlx_dyn_add(store, *beg ? "TRUE": "FALSE");
}
/*
@@ -284,7 +305,7 @@ static CURLcode octet2str(struct dynbuf *store,
CURLcode result = CURLE_OK;
while(!result && beg < end)
- result = Curl_dyn_addf(store, "%02x:", (unsigned char) *beg++);
+ result = curlx_dyn_addf(store, "%02x:", (unsigned char) *beg++);
return result;
}
@@ -323,7 +344,7 @@ static CURLcode int2str(struct dynbuf *store,
do
val = (val << 8) | *(const unsigned char *) beg++;
while(beg < end);
- return Curl_dyn_addf(store, "%s%x", val >= 10 ? "0x" : "", val);
+ return curlx_dyn_addf(store, "%s%x", val >= 10 ? "0x" : "", val);
}
/*
@@ -366,7 +387,7 @@ utf8asn1str(struct dynbuf *to, int type, const char *from, const char *end)
if(type == CURL_ASN1_UTF8_STRING) {
/* Just copy. */
if(inlength)
- result = Curl_dyn_addn(to, from, inlength);
+ result = curlx_dyn_addn(to, from, inlength);
}
else {
while(!result && (from < end)) {
@@ -405,7 +426,7 @@ utf8asn1str(struct dynbuf *to, int type, const char *from, const char *end)
charsize++;
}
buf[0] = (char) wc;
- result = Curl_dyn_addn(to, buf, charsize);
+ result = curlx_dyn_addn(to, buf, charsize);
}
}
return result;
@@ -428,7 +449,7 @@ static CURLcode encodeOID(struct dynbuf *store,
x = y / 40;
y -= x * 40;
- result = Curl_dyn_addf(store, "%u.%u", x, y);
+ result = curlx_dyn_addf(store, "%u.%u", x, y);
if(result)
return result;
@@ -441,7 +462,7 @@ static CURLcode encodeOID(struct dynbuf *store,
y = *(const unsigned char *) beg++;
x = (x << 7) | (y & 0x7F);
} while(y & 0x80);
- result = Curl_dyn_addf(store, ".%u", x);
+ result = curlx_dyn_addf(store, ".%u", x);
}
return result;
}
@@ -459,16 +480,16 @@ static CURLcode OID2str(struct dynbuf *store,
if(beg < end) {
if(symbolic) {
struct dynbuf buf;
- Curl_dyn_init(&buf, CURL_X509_STR_MAX);
+ curlx_dyn_init(&buf, CURL_X509_STR_MAX);
result = encodeOID(&buf, beg, end);
if(!result) {
- const struct Curl_OID *op = searchOID(Curl_dyn_ptr(&buf));
+ const struct Curl_OID *op = searchOID(curlx_dyn_ptr(&buf));
if(op)
- result = Curl_dyn_add(store, op->textoid);
+ result = curlx_dyn_add(store, op->textoid);
else
- result = Curl_dyn_add(store, Curl_dyn_ptr(&buf));
- Curl_dyn_free(&buf);
+ result = curlx_dyn_add(store, curlx_dyn_ptr(&buf));
+ curlx_dyn_free(&buf);
}
}
else
@@ -547,12 +568,12 @@ static CURLcode GTime2str(struct dynbuf *store,
tzl = end - tzp;
}
- return Curl_dyn_addf(store,
- "%.4s-%.2s-%.2s %.2s:%.2s:%c%c%s%.*s%s%.*s",
- beg, beg + 4, beg + 6,
- beg + 8, beg + 10, sec1, sec2,
- fracl ? ".": "", (int)fracl, fracp,
- sep, (int)tzl, tzp);
+ return curlx_dyn_addf(store,
+ "%.4s-%.2s-%.2s %.2s:%.2s:%c%c%s%.*s%s%.*s",
+ beg, beg + 4, beg + 6,
+ beg + 8, beg + 10, sec1, sec2,
+ fracl ? ".": "", (int)fracl, fracp,
+ sep, (int)tzl, tzp);
}
#ifdef UNITTESTS
@@ -601,10 +622,10 @@ static CURLcode UTime2str(struct dynbuf *store,
tzp++;
tzl = end - tzp;
- return Curl_dyn_addf(store, "%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s",
- 20 - (*beg >= '5'), beg, beg + 2, beg + 4,
- beg + 6, beg + 8, sec,
- (int)tzl, tzp);
+ return curlx_dyn_addf(store, "%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s",
+ 20 - (*beg >= '5'), beg, beg + 2, beg + 4,
+ beg + 6, beg + 8, sec,
+ (int)tzl, tzp);
}
/*
@@ -637,7 +658,7 @@ static CURLcode ASN1tostr(struct dynbuf *store,
result = octet2str(store, elem->beg, elem->end);
break;
case CURL_ASN1_NULL:
- result = Curl_dyn_addn(store, "", 1);
+ result = curlx_dyn_addn(store, "", 1);
break;
case CURL_ASN1_OBJECT_IDENTIFIER:
result = OID2str(store, elem->beg, elem->end, TRUE);
@@ -681,7 +702,7 @@ static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn)
CURLcode result = CURLE_OK;
bool added = FALSE;
struct dynbuf temp;
- Curl_dyn_init(&temp, CURL_X509_STR_MAX);
+ curlx_dyn_init(&temp, CURL_X509_STR_MAX);
for(p1 = dn->beg; p1 < dn->end;) {
p1 = getASN1Element(&rdn, p1, dn->end);
@@ -704,12 +725,12 @@ static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn)
result = CURLE_BAD_FUNCTION_ARGUMENT;
goto error;
}
- Curl_dyn_reset(&temp);
+ curlx_dyn_reset(&temp);
result = ASN1tostr(&temp, &oid, 0);
if(result)
goto error;
- str = Curl_dyn_ptr(&temp);
+ str = curlx_dyn_ptr(&temp);
if(!str) {
result = CURLE_BAD_FUNCTION_ARGUMENT;
@@ -722,20 +743,20 @@ static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn)
;
if(added) {
if(p3 - str > 2)
- result = Curl_dyn_addn(store, "/", 1);
+ result = curlx_dyn_addn(store, "/", 1);
else
- result = Curl_dyn_addn(store, ", ", 2);
+ result = curlx_dyn_addn(store, ", ", 2);
if(result)
goto error;
}
/* Encode attribute name. */
- result = Curl_dyn_add(store, str);
+ result = curlx_dyn_add(store, str);
if(result)
goto error;
/* Generate equal sign. */
- result = Curl_dyn_addn(store, "=", 1);
+ result = curlx_dyn_addn(store, "=", 1);
if(result)
goto error;
@@ -743,12 +764,12 @@ static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn)
result = ASN1tostr(store, &value, 0);
if(result)
goto error;
- Curl_dyn_reset(&temp);
+ curlx_dyn_reset(&temp);
added = TRUE; /* use separator for next */
}
}
error:
- Curl_dyn_free(&temp);
+ curlx_dyn_free(&temp);
return result;
}
@@ -926,8 +947,8 @@ static CURLcode ssl_push_certinfo_dyn(struct Curl_easy *data,
const char *label,
struct dynbuf *ptr)
{
- size_t valuelen = Curl_dyn_len(ptr);
- char *value = Curl_dyn_ptr(ptr);
+ size_t valuelen = curlx_dyn_len(ptr);
+ char *value = curlx_dyn_ptr(ptr);
CURLcode result = Curl_ssl_push_certinfo_len(data, certnum, label,
value, valuelen);
@@ -945,7 +966,7 @@ static CURLcode do_pubkey_field(struct Curl_easy *data, int certnum,
CURLcode result;
struct dynbuf out;
- Curl_dyn_init(&out, CURL_X509_STR_MAX);
+ curlx_dyn_init(&out, CURL_X509_STR_MAX);
/* Generate a certificate information record for the public key. */
@@ -953,7 +974,7 @@ static CURLcode do_pubkey_field(struct Curl_easy *data, int certnum,
if(!result) {
if(data->set.ssl.certinfo)
result = ssl_push_certinfo_dyn(data, certnum, label, &out);
- Curl_dyn_free(&out);
+ curlx_dyn_free(&out);
}
return result;
}
@@ -1005,7 +1026,7 @@ static int do_pubkey(struct Curl_easy *data, int certnum,
len = ((elem.end - q) * 8);
if(len) {
unsigned int i;
- for(i = *(unsigned char *) q; !(i & 0x80); i <<= 1)
+ for(i = *(const unsigned char *) q; !(i & 0x80); i <<= 1)
len--;
}
if(len > 32)
@@ -1089,7 +1110,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
if(certnum)
return CURLE_OK;
- Curl_dyn_init(&out, CURL_X509_STR_MAX);
+ curlx_dyn_init(&out, CURL_X509_STR_MAX);
/* Prepare the certificate information for curl_easy_getinfo(). */
/* Extract the certificate ASN.1 elements. */
@@ -1105,7 +1126,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
if(result)
goto done;
}
- Curl_dyn_reset(&out);
+ curlx_dyn_reset(&out);
/* Issuer. */
result = DNtostr(&out, &cert.issuer);
@@ -1116,20 +1137,20 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
if(result)
goto done;
}
- Curl_dyn_reset(&out);
+ curlx_dyn_reset(&out);
/* Version (always fits in less than 32 bits). */
version = 0;
for(ptr = cert.version.beg; ptr < cert.version.end; ptr++)
version = (version << 8) | *(const unsigned char *) ptr;
if(data->set.ssl.certinfo) {
- result = Curl_dyn_addf(&out, "%x", version);
+ result = curlx_dyn_addf(&out, "%x", version);
if(result)
goto done;
result = ssl_push_certinfo_dyn(data, certnum, "Version", &out);
if(result)
goto done;
- Curl_dyn_reset(&out);
+ curlx_dyn_reset(&out);
}
/* Serial number. */
@@ -1141,7 +1162,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
if(result)
goto done;
}
- Curl_dyn_reset(&out);
+ curlx_dyn_reset(&out);
/* Signature algorithm .*/
result = dumpAlgo(&out, &param, cert.signatureAlgorithm.beg,
@@ -1154,7 +1175,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
if(result)
goto done;
}
- Curl_dyn_reset(&out);
+ curlx_dyn_reset(&out);
/* Start Date. */
result = ASN1tostr(&out, &cert.notBefore, 0);
@@ -1165,7 +1186,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
if(result)
goto done;
}
- Curl_dyn_reset(&out);
+ curlx_dyn_reset(&out);
/* Expire Date. */
result = ASN1tostr(&out, &cert.notAfter, 0);
@@ -1176,7 +1197,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
if(result)
goto done;
}
- Curl_dyn_reset(&out);
+ curlx_dyn_reset(&out);
/* Public Key Algorithm. */
result = dumpAlgo(&out, &param, cert.subjectPublicKeyAlgorithm.beg,
@@ -1190,13 +1211,13 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
goto done;
}
- rc = do_pubkey(data, certnum, Curl_dyn_ptr(&out),
+ rc = do_pubkey(data, certnum, curlx_dyn_ptr(&out),
&param, &cert.subjectPublicKey);
if(rc) {
result = CURLE_OUT_OF_MEMORY; /* the most likely error */
goto done;
}
- Curl_dyn_reset(&out);
+ curlx_dyn_reset(&out);
/* Signature. */
result = ASN1tostr(&out, &cert.signature, 0);
@@ -1207,12 +1228,12 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
if(result)
goto done;
}
- Curl_dyn_reset(&out);
+ curlx_dyn_reset(&out);
/* Generate PEM certificate. */
- result = Curl_base64_encode(cert.certificate.beg,
- cert.certificate.end - cert.certificate.beg,
- &certptr, &clen);
+ result = curlx_base64_encode(cert.certificate.beg,
+ cert.certificate.end - cert.certificate.beg,
+ &certptr, &clen);
if(result)
goto done;
@@ -1225,22 +1246,22 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
-----END CERTIFICATE-----\n
*/
- Curl_dyn_reset(&out);
+ curlx_dyn_reset(&out);
/* Build the certificate string. */
- result = Curl_dyn_add(&out, "-----BEGIN CERTIFICATE-----\n");
+ result = curlx_dyn_add(&out, "-----BEGIN CERTIFICATE-----\n");
if(!result) {
size_t j = 0;
while(!result && (j < clen)) {
size_t chunksize = (clen - j) > 64 ? 64 : (clen - j);
- result = Curl_dyn_addn(&out, &certptr[j], chunksize);
+ result = curlx_dyn_addn(&out, &certptr[j], chunksize);
if(!result)
- result = Curl_dyn_addn(&out, "\n", 1);
+ result = curlx_dyn_addn(&out, "\n", 1);
j += chunksize;
}
if(!result)
- result = Curl_dyn_add(&out, "-----END CERTIFICATE-----\n");
+ result = curlx_dyn_add(&out, "-----END CERTIFICATE-----\n");
}
free(certptr);
if(!result)
@@ -1250,10 +1271,11 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data,
done:
if(result)
failf(data, "Failed extracting certificate chain");
- Curl_dyn_free(&out);
+ curlx_dyn_free(&out);
return result;
}
#endif /* WANT_EXTRACT_CERTINFO */
-#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP */
+#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP
+ or USE_MBEDTLS or USE_RUSTLS */
diff --git a/libs/libcurl/src/vtls/x509asn1.h b/libs/libcurl/src/vtls/x509asn1.h
index b0155d11a2..3b933cb231 100644
--- a/libs/libcurl/src/vtls/x509asn1.h
+++ b/libs/libcurl/src/vtls/x509asn1.h
@@ -25,14 +25,14 @@
*
***************************************************************************/
-#include "curl_setup.h"
+#include "../curl_setup.h"
#if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || \
defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \
- defined(USE_MBEDTLS)
+ defined(USE_MBEDTLS) || defined(USE_RUSTLS)
-#include "cfilters.h"
-#include "urldata.h"
+#include "../cfilters.h"
+#include "../urldata.h"
/*
* Types.
@@ -45,7 +45,7 @@ struct Curl_asn1Element {
const char *end; /* Pointer to 1st byte after element. */
unsigned char class; /* ASN.1 element class. */
unsigned char tag; /* ASN.1 element tag. */
- bool constructed; /* Element is constructed. */
+ BIT(constructed); /* Element is constructed. */
};
/* X509 certificate: RFC 5280. */
@@ -80,13 +80,17 @@ CURLcode Curl_verifyhost(struct Curl_cfilter *cf, struct Curl_easy *data,
#ifdef UNITTESTS
#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_SECTRANSP) || \
- defined(USE_MBEDTLS)
+ defined(USE_MBEDTLS) || defined(USE_RUSTLS)
/* used by unit1656.c */
CURLcode Curl_x509_GTime2str(struct dynbuf *store,
const char *beg, const char *end);
+/* used by unit1657.c */
+CURLcode Curl_x509_getASN1Element(struct Curl_asn1Element *elem,
+ const char *beg, const char *end);
#endif
#endif
-#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP */
+#endif /* USE_GNUTLS or USE_WOLFSSL or USE_SCHANNEL or USE_SECTRANSP
+ or USE_MBEDTLS or USE_RUSTLS */
#endif /* HEADER_CURL_X509ASN1_H */
diff --git a/libs/libcurl/src/ws.c b/libs/libcurl/src/ws.c
index 656c54e3f2..e522c8941b 100644
--- a/libs/libcurl/src/ws.c
+++ b/libs/libcurl/src/ws.c
@@ -27,10 +27,11 @@
#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
#include "urldata.h"
+#include "url.h"
#include "bufq.h"
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
#include "rand.h"
-#include "curl_base64.h"
+#include "curlx/base64.h"
#include "connect.h"
#include "sendf.h"
#include "multiif.h"
@@ -38,7 +39,8 @@
#include "easyif.h"
#include "transfer.h"
#include "select.h"
-#include "nonblock.h"
+#include "curlx/nonblock.h"
+#include "curlx/strparse.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
@@ -46,12 +48,26 @@
#include "memdebug.h"
-#define WSBIT_FIN 0x80
-#define WSBIT_OPCODE_CONT 0
-#define WSBIT_OPCODE_TEXT (1)
-#define WSBIT_OPCODE_BIN (2)
-#define WSBIT_OPCODE_CLOSE (8)
-#define WSBIT_OPCODE_PING (9)
+/***
+ RFC 6455 Section 5.2
+
+ 0 1 2 3 4 5 6 7
+ +-+-+-+-+-------+
+ |F|R|R|R| opcode|
+ |I|S|S|S| (4) |
+ |N|V|V|V| |
+ | |1|2|3| |
+*/
+#define WSBIT_FIN (0x80)
+#define WSBIT_RSV1 (0x40)
+#define WSBIT_RSV2 (0x20)
+#define WSBIT_RSV3 (0x10)
+#define WSBIT_RSV_MASK (WSBIT_RSV1 | WSBIT_RSV2 | WSBIT_RSV3)
+#define WSBIT_OPCODE_CONT (0x0)
+#define WSBIT_OPCODE_TEXT (0x1)
+#define WSBIT_OPCODE_BIN (0x2)
+#define WSBIT_OPCODE_CLOSE (0x8)
+#define WSBIT_OPCODE_PING (0x9)
#define WSBIT_OPCODE_PONG (0xa)
#define WSBIT_OPCODE_MASK (0xf)
@@ -61,51 +77,206 @@
#define WS_CHUNK_SIZE 65535
#define WS_CHUNK_COUNT 2
-struct ws_frame_meta {
- char proto_opcode;
- int flags;
- const char *name;
+
+/* a client-side WS frame decoder, parsing frame headers and
+ * payload, keeping track of current position and stats */
+enum ws_dec_state {
+ WS_DEC_INIT,
+ WS_DEC_HEAD,
+ WS_DEC_PAYLOAD
+};
+
+struct ws_decoder {
+ int frame_age; /* zero */
+ int frame_flags; /* See the CURLWS_* defines */
+ curl_off_t payload_offset; /* the offset parsing is at */
+ curl_off_t payload_len;
+ unsigned char head[10];
+ int head_len, head_total;
+ enum ws_dec_state state;
+ int cont_flags;
+};
+
+/* a client-side WS frame encoder, generating frame headers and
+ * converting payloads, tracking remaining data in current frame */
+struct ws_encoder {
+ curl_off_t payload_len; /* payload length of current frame */
+ curl_off_t payload_remain; /* remaining payload of current */
+ unsigned int xori; /* xor index */
+ unsigned char mask[4]; /* 32-bit mask for this connection */
+ unsigned char firstbyte; /* first byte of frame we encode */
+ BIT(contfragment); /* set TRUE if the previous fragment sent was not final */
};
-static struct ws_frame_meta WS_FRAMES[] = {
- { WSBIT_OPCODE_CONT, CURLWS_CONT, "CONT" },
- { WSBIT_OPCODE_TEXT, CURLWS_TEXT, "TEXT" },
- { WSBIT_OPCODE_BIN, CURLWS_BINARY, "BIN" },
- { WSBIT_OPCODE_CLOSE, CURLWS_CLOSE, "CLOSE" },
- { WSBIT_OPCODE_PING, CURLWS_PING, "PING" },
- { WSBIT_OPCODE_PONG, CURLWS_PONG, "PONG" },
+/* A websocket connection with en- and decoder that treat frames
+ * and keep track of boundaries. */
+struct websocket {
+ struct Curl_easy *data; /* used for write callback handling */
+ struct ws_decoder dec; /* decode of we frames */
+ struct ws_encoder enc; /* decode of we frames */
+ struct bufq recvbuf; /* raw data from the server */
+ struct bufq sendbuf; /* raw data to be sent to the server */
+ struct curl_ws_frame frame; /* the current WS FRAME received */
+ size_t sendbuf_payload; /* number of payload bytes in sendbuf */
};
-static const char *ws_frame_name_of_op(unsigned char proto_opcode)
+
+static const char *ws_frame_name_of_op(unsigned char firstbyte)
{
- unsigned char opcode = proto_opcode & WSBIT_OPCODE_MASK;
- size_t i;
- for(i = 0; i < sizeof(WS_FRAMES)/sizeof(WS_FRAMES[0]); ++i) {
- if(WS_FRAMES[i].proto_opcode == opcode)
- return WS_FRAMES[i].name;
+ switch(firstbyte & WSBIT_OPCODE_MASK) {
+ case WSBIT_OPCODE_CONT:
+ return "CONT";
+ case WSBIT_OPCODE_TEXT:
+ return "TEXT";
+ case WSBIT_OPCODE_BIN:
+ return "BIN";
+ case WSBIT_OPCODE_CLOSE:
+ return "CLOSE";
+ case WSBIT_OPCODE_PING:
+ return "PING";
+ case WSBIT_OPCODE_PONG:
+ return "PONG";
+ default:
+ return "???";
}
- return "???";
}
-static int ws_frame_op2flags(unsigned char proto_opcode)
+static int ws_frame_firstbyte2flags(struct Curl_easy *data,
+ unsigned char firstbyte, int cont_flags)
{
- unsigned char opcode = proto_opcode & WSBIT_OPCODE_MASK;
- size_t i;
- for(i = 0; i < sizeof(WS_FRAMES)/sizeof(WS_FRAMES[0]); ++i) {
- if(WS_FRAMES[i].proto_opcode == opcode)
- return WS_FRAMES[i].flags;
+ switch(firstbyte) {
+ /* 0x00 - intermediate TEXT/BINARY fragment */
+ case WSBIT_OPCODE_CONT:
+ if(!(cont_flags & CURLWS_CONT)) {
+ failf(data, "[WS] no ongoing fragmented message to resume");
+ return 0;
+ }
+ return cont_flags | CURLWS_CONT;
+ /* 0x80 - final TEXT/BIN fragment */
+ case (WSBIT_OPCODE_CONT | WSBIT_FIN):
+ if(!(cont_flags & CURLWS_CONT)) {
+ failf(data, "[WS] no ongoing fragmented message to resume");
+ return 0;
+ }
+ return cont_flags & ~CURLWS_CONT;
+ /* 0x01 - first TEXT fragment */
+ case WSBIT_OPCODE_TEXT:
+ if(cont_flags & CURLWS_CONT) {
+ failf(data, "[WS] fragmented message interrupted by new TEXT msg");
+ return 0;
+ }
+ return CURLWS_TEXT | CURLWS_CONT;
+ /* 0x81 - unfragmented TEXT msg */
+ case (WSBIT_OPCODE_TEXT | WSBIT_FIN):
+ if(cont_flags & CURLWS_CONT) {
+ failf(data, "[WS] fragmented message interrupted by new TEXT msg");
+ return 0;
+ }
+ return CURLWS_TEXT;
+ /* 0x02 - first BINARY fragment */
+ case WSBIT_OPCODE_BIN:
+ if(cont_flags & CURLWS_CONT) {
+ failf(data, "[WS] fragmented message interrupted by new BINARY msg");
+ return 0;
+ }
+ return CURLWS_BINARY | CURLWS_CONT;
+ /* 0x82 - unfragmented BINARY msg */
+ case (WSBIT_OPCODE_BIN | WSBIT_FIN):
+ if(cont_flags & CURLWS_CONT) {
+ failf(data, "[WS] fragmented message interrupted by new BINARY msg");
+ return 0;
+ }
+ return CURLWS_BINARY;
+ /* 0x08 - first CLOSE fragment */
+ case WSBIT_OPCODE_CLOSE:
+ failf(data, "[WS] invalid fragmented CLOSE frame");
+ return 0;
+ /* 0x88 - unfragmented CLOSE */
+ case (WSBIT_OPCODE_CLOSE | WSBIT_FIN):
+ return CURLWS_CLOSE;
+ /* 0x09 - first PING fragment */
+ case WSBIT_OPCODE_PING:
+ failf(data, "[WS] invalid fragmented PING frame");
+ return 0;
+ /* 0x89 - unfragmented PING */
+ case (WSBIT_OPCODE_PING | WSBIT_FIN):
+ return CURLWS_PING;
+ /* 0x0a - first PONG fragment */
+ case WSBIT_OPCODE_PONG:
+ failf(data, "[WS] invalid fragmented PONG frame");
+ return 0;
+ /* 0x8a - unfragmented PONG */
+ case (WSBIT_OPCODE_PONG | WSBIT_FIN):
+ return CURLWS_PONG;
+ /* invalid first byte */
+ default:
+ if(firstbyte & WSBIT_RSV_MASK)
+ /* any of the reserved bits 0x40/0x20/0x10 are set */
+ failf(data, "[WS] invalid reserved bits: %02x", firstbyte);
+ else
+ /* any of the reserved opcodes 0x3-0x7 or 0xb-0xf is used */
+ failf(data, "[WS] invalid opcode: %02x", firstbyte);
+ return 0;
}
- return 0;
}
-static unsigned char ws_frame_flags2op(int flags)
+static unsigned char ws_frame_flags2firstbyte(struct Curl_easy *data,
+ unsigned int flags,
+ bool contfragment,
+ CURLcode *err)
{
- size_t i;
- for(i = 0; i < sizeof(WS_FRAMES)/sizeof(WS_FRAMES[0]); ++i) {
- if(WS_FRAMES[i].flags & flags)
- return (unsigned char)WS_FRAMES[i].proto_opcode;
+ switch(flags & ~CURLWS_OFFSET) {
+ case 0:
+ if(contfragment) {
+ infof(data, "[WS] no flags given; interpreting as continuation "
+ "fragment for compatibility");
+ return (WSBIT_OPCODE_CONT | WSBIT_FIN);
+ }
+ failf(data, "[WS] no flags given");
+ *err = CURLE_BAD_FUNCTION_ARGUMENT;
+ return 0xff;
+ case CURLWS_CONT:
+ if(contfragment) {
+ infof(data, "[WS] setting CURLWS_CONT flag without message type is "
+ "supported for compatibility but highly discouraged");
+ return WSBIT_OPCODE_CONT;
+ }
+ failf(data, "[WS] No ongoing fragmented message to continue");
+ *err = CURLE_BAD_FUNCTION_ARGUMENT;
+ return 0xff;
+ case CURLWS_TEXT:
+ return contfragment ? (WSBIT_OPCODE_CONT | WSBIT_FIN)
+ : (WSBIT_OPCODE_TEXT | WSBIT_FIN);
+ case (CURLWS_TEXT | CURLWS_CONT):
+ return contfragment ? WSBIT_OPCODE_CONT : WSBIT_OPCODE_TEXT;
+ case CURLWS_BINARY:
+ return contfragment ? (WSBIT_OPCODE_CONT | WSBIT_FIN)
+ : (WSBIT_OPCODE_BIN | WSBIT_FIN);
+ case (CURLWS_BINARY | CURLWS_CONT):
+ return contfragment ? WSBIT_OPCODE_CONT : WSBIT_OPCODE_BIN;
+ case CURLWS_CLOSE:
+ return WSBIT_OPCODE_CLOSE | WSBIT_FIN;
+ case (CURLWS_CLOSE | CURLWS_CONT):
+ failf(data, "[WS] CLOSE frame must not be fragmented");
+ *err = CURLE_BAD_FUNCTION_ARGUMENT;
+ return 0xff;
+ case CURLWS_PING:
+ return WSBIT_OPCODE_PING | WSBIT_FIN;
+ case (CURLWS_PING | CURLWS_CONT):
+ failf(data, "[WS] PING frame must not be fragmented");
+ *err = CURLE_BAD_FUNCTION_ARGUMENT;
+ return 0xff;
+ case CURLWS_PONG:
+ return WSBIT_OPCODE_PONG | WSBIT_FIN;
+ case (CURLWS_PONG | CURLWS_CONT):
+ failf(data, "[WS] PONG frame must not be fragmented");
+ *err = CURLE_BAD_FUNCTION_ARGUMENT;
+ return 0xff;
+ default:
+ failf(data, "[WS] unknown flags: %x", flags);
+ *err = CURLE_BAD_FUNCTION_ARGUMENT;
+ return 0xff;
}
- return 0;
}
static void ws_dec_info(struct ws_decoder *dec, struct Curl_easy *data,
@@ -115,23 +286,23 @@ static void ws_dec_info(struct ws_decoder *dec, struct Curl_easy *data,
case 0:
break;
case 1:
- CURL_TRC_WRITE(data, "websocket, decoded %s [%s%s]", msg,
- ws_frame_name_of_op(dec->head[0]),
- (dec->head[0] & WSBIT_FIN) ? "" : " NON-FINAL");
+ CURL_TRC_WS(data, "decoded %s [%s%s]", msg,
+ ws_frame_name_of_op(dec->head[0]),
+ (dec->head[0] & WSBIT_FIN) ? "" : " NON-FINAL");
break;
default:
if(dec->head_len < dec->head_total) {
- CURL_TRC_WRITE(data, "websocket, decoded %s [%s%s](%d/%d)", msg,
- ws_frame_name_of_op(dec->head[0]),
- (dec->head[0] & WSBIT_FIN) ? "" : " NON-FINAL",
- dec->head_len, dec->head_total);
+ CURL_TRC_WS(data, "decoded %s [%s%s](%d/%d)", msg,
+ ws_frame_name_of_op(dec->head[0]),
+ (dec->head[0] & WSBIT_FIN) ? "" : " NON-FINAL",
+ dec->head_len, dec->head_total);
}
else {
- CURL_TRC_WRITE(data, "websocket, decoded %s [%s%s payload=%"
- FMT_OFF_T "/%" FMT_OFF_T "]",
- msg, ws_frame_name_of_op(dec->head[0]),
- (dec->head[0] & WSBIT_FIN) ? "" : " NON-FINAL",
- dec->payload_offset, dec->payload_len);
+ CURL_TRC_WS(data, "decoded %s [%s%s payload=%"
+ FMT_OFF_T "/%" FMT_OFF_T "]",
+ msg, ws_frame_name_of_op(dec->head[0]),
+ (dec->head[0] & WSBIT_FIN) ? "" : " NON-FINAL",
+ dec->payload_offset, dec->payload_len);
}
break;
}
@@ -147,6 +318,16 @@ typedef ssize_t ws_write_payload(const unsigned char *buf, size_t buflen,
void *userp,
CURLcode *err);
+static void ws_dec_next_frame(struct ws_decoder *dec)
+{
+ dec->frame_age = 0;
+ dec->frame_flags = 0;
+ dec->payload_offset = 0;
+ dec->payload_len = 0;
+ dec->head_len = dec->head_total = 0;
+ dec->state = WS_DEC_INIT;
+ /* dec->cont_flags must be carried over to next frame */
+}
static void ws_dec_reset(struct ws_decoder *dec)
{
@@ -156,6 +337,7 @@ static void ws_dec_reset(struct ws_decoder *dec)
dec->payload_len = 0;
dec->head_len = dec->head_total = 0;
dec->state = WS_DEC_INIT;
+ dec->cont_flags = 0;
}
static void ws_dec_init(struct ws_decoder *dec)
@@ -175,12 +357,19 @@ static CURLcode ws_dec_read_head(struct ws_decoder *dec,
dec->head[0] = *inbuf;
Curl_bufq_skip(inraw, 1);
- dec->frame_flags = ws_frame_op2flags(dec->head[0]);
+ dec->frame_flags = ws_frame_firstbyte2flags(data, dec->head[0],
+ dec->cont_flags);
if(!dec->frame_flags) {
- failf(data, "WS: unknown opcode: %x", dec->head[0]);
ws_dec_reset(dec);
return CURLE_RECV_ERROR;
}
+
+ /* fragmentation only applies to data frames (text/binary);
+ * control frames (close/ping/pong) do not affect the CONT status */
+ if(dec->frame_flags & (CURLWS_TEXT | CURLWS_BINARY)) {
+ dec->cont_flags = dec->frame_flags;
+ }
+
dec->head_len = 1;
/* ws_dec_info(dec, data, "seeing opcode"); */
continue;
@@ -192,10 +381,30 @@ static CURLcode ws_dec_read_head(struct ws_decoder *dec,
if(dec->head[1] & WSBIT_MASK) {
/* A client MUST close a connection if it detects a masked frame. */
- failf(data, "WS: masked input frame");
+ failf(data, "[WS] masked input frame");
+ ws_dec_reset(dec);
+ return CURLE_RECV_ERROR;
+ }
+ if(dec->frame_flags & CURLWS_PING && dec->head[1] > 125) {
+ /* The maximum valid size of PING frames is 125 bytes.
+ Accepting overlong pings would mean sending equivalent pongs! */
+ failf(data, "[WS] received PING frame is too big");
ws_dec_reset(dec);
return CURLE_RECV_ERROR;
}
+ if(dec->frame_flags & CURLWS_PONG && dec->head[1] > 125) {
+ /* The maximum valid size of PONG frames is 125 bytes. */
+ failf(data, "[WS] received PONG frame is too big");
+ ws_dec_reset(dec);
+ return CURLE_RECV_ERROR;
+ }
+ if(dec->frame_flags & CURLWS_CLOSE && dec->head[1] > 125) {
+ /* The maximum valid size of CLOSE frames is 125 bytes. */
+ failf(data, "[WS] received CLOSE frame is too big");
+ ws_dec_reset(dec);
+ return CURLE_RECV_ERROR;
+ }
+
/* How long is the frame head? */
if(dec->head[1] == 126) {
dec->head_total = 4;
@@ -230,7 +439,7 @@ static CURLcode ws_dec_read_head(struct ws_decoder *dec,
break;
case 10:
if(dec->head[2] > 127) {
- failf(data, "WS: frame length longer than 64 signed not supported");
+ failf(data, "[WS] frame length longer than 64 signed not supported");
return CURLE_RECV_ERROR;
}
dec->payload_len = ((curl_off_t)dec->head[2] << 56) |
@@ -245,7 +454,7 @@ static CURLcode ws_dec_read_head(struct ws_decoder *dec,
default:
/* this should never happen */
DEBUGASSERT(0);
- failf(data, "WS: unexpected frame header length");
+ failf(data, "[WS] unexpected frame header length");
return CURLE_RECV_ERROR;
}
@@ -281,8 +490,8 @@ static CURLcode ws_dec_pass_payload(struct ws_decoder *dec,
Curl_bufq_skip(inraw, (size_t)nwritten);
dec->payload_offset += (curl_off_t)nwritten;
remain = dec->payload_len - dec->payload_offset;
- CURL_TRC_WRITE(data, "websocket, passed %zd bytes payload, %"
- FMT_OFF_T " remain", nwritten, remain);
+ CURL_TRC_WS(data, "passed %zd bytes payload, %"
+ FMT_OFF_T " remain", nwritten, remain);
}
return remain ? CURLE_AGAIN : CURLE_OK;
@@ -301,14 +510,14 @@ static CURLcode ws_dec_pass(struct ws_decoder *dec,
switch(dec->state) {
case WS_DEC_INIT:
- ws_dec_reset(dec);
+ ws_dec_next_frame(dec);
dec->state = WS_DEC_HEAD;
FALLTHROUGH();
case WS_DEC_HEAD:
result = ws_dec_read_head(dec, data, inraw);
if(result) {
if(result != CURLE_AGAIN) {
- infof(data, "WS: decode error %d", (int)result);
+ infof(data, "[WS] decode error %d", (int)result);
break; /* real error */
}
/* incomplete ws frame head */
@@ -350,11 +559,13 @@ static void update_meta(struct websocket *ws,
curl_off_t payload_len,
size_t cur_len)
{
+ curl_off_t bytesleft = (payload_len - payload_offset - cur_len);
+
ws->frame.age = frame_age;
ws->frame.flags = frame_flags;
ws->frame.offset = payload_offset;
ws->frame.len = cur_len;
- ws->frame.bytesleft = (payload_len - payload_offset - cur_len);
+ ws->frame.bytesleft = bytesleft;
}
/* WebSockets decoding client writer */
@@ -396,13 +607,15 @@ static ssize_t ws_cw_dec_next(const unsigned char *buf, size_t buflen,
struct ws_cw_dec_ctx *ctx = user_data;
struct Curl_easy *data = ctx->data;
struct websocket *ws = ctx->ws;
+ bool auto_pong = !data->set.ws_no_auto_pong;
curl_off_t remain = (payload_len - (payload_offset + buflen));
(void)frame_age;
- if((frame_flags & CURLWS_PING) && !remain) {
+
+ if(auto_pong && (frame_flags & CURLWS_PING) && !remain) {
/* auto-respond to PINGs, only works for single-frame payloads atm */
size_t bytes;
- infof(data, "WS: auto-respond to PING with a PONG");
+ infof(data, "[WS] auto-respond to PING with a PONG");
/* send back the exact same content as a PONG */
*err = curl_ws_send(data, buf, buflen, &bytes, 0, CURLWS_PONG);
if(*err)
@@ -433,9 +646,9 @@ static CURLcode ws_cw_write(struct Curl_easy *data,
if(!(type & CLIENTWRITE_BODY) || data->set.ws_raw_mode)
return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
- ws = data->conn->proto.ws;
+ ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN);
if(!ws) {
- failf(data, "WS: not a websocket transfer");
+ failf(data, "[WS] not a websocket transfer");
return CURLE_FAILED_INIT;
}
@@ -444,7 +657,7 @@ static CURLcode ws_cw_write(struct Curl_easy *data,
nwritten = Curl_bufq_write(&ctx->buf, (const unsigned char *)buf,
nbytes, &result);
if(nwritten < 0) {
- infof(data, "WS: error adding data to buffer %d", result);
+ infof(data, "[WS] error adding data to buffer %d", result);
return result;
}
}
@@ -460,17 +673,17 @@ static CURLcode ws_cw_write(struct Curl_easy *data,
if(result == CURLE_AGAIN) {
/* insufficient amount of data, keep it for later.
* we pretend to have written all since we have a copy */
- CURL_TRC_WRITE(data, "websocket, buffered incomplete frame head");
+ CURL_TRC_WS(data, "buffered incomplete frame head");
return CURLE_OK;
}
else if(result) {
- infof(data, "WS: decode error %d", (int)result);
+ infof(data, "[WS] decode error %d", (int)result);
return result;
}
}
if((type & CLIENTWRITE_EOS) && !Curl_bufq_is_empty(&ctx->buf)) {
- infof(data, "WS: decode ending with %zd frame bytes remaining",
+ failf(data, "[WS] decode ending with %zd frame bytes remaining",
Curl_bufq_len(&ctx->buf));
return CURLE_RECV_ERROR;
}
@@ -492,12 +705,11 @@ static const struct Curl_cwtype ws_cw_decode = {
static void ws_enc_info(struct ws_encoder *enc, struct Curl_easy *data,
const char *msg)
{
- infof(data, "WS-ENC: %s [%s%s%s payload=%" FMT_OFF_T "/%" FMT_OFF_T "]",
- msg, ws_frame_name_of_op(enc->firstbyte),
- (enc->firstbyte & WSBIT_OPCODE_MASK) == WSBIT_OPCODE_CONT ?
- " CONT" : "",
- (enc->firstbyte & WSBIT_FIN) ? "" : " NON-FIN",
- enc->payload_len - enc->payload_remain, enc->payload_len);
+ CURL_TRC_WS(data, "WS-ENC: %s [%s%s payload=%"
+ FMT_OFF_T "/%" FMT_OFF_T "]",
+ msg, ws_frame_name_of_op(enc->firstbyte),
+ (enc->firstbyte & WSBIT_FIN) ? "" : " NON-FIN",
+ enc->payload_len - enc->payload_remain, enc->payload_len);
}
static void ws_enc_reset(struct ws_encoder *enc)
@@ -543,13 +755,12 @@ static ssize_t ws_enc_write_head(struct Curl_easy *data,
CURLcode *err)
{
unsigned char firstbyte = 0;
- unsigned char opcode;
unsigned char head[14];
size_t hlen;
ssize_t n;
if(payload_len < 0) {
- failf(data, "WS: starting new frame with negative payload length %"
+ failf(data, "[WS] starting new frame with negative payload length %"
FMT_OFF_T, payload_len);
*err = CURLE_SEND_ERROR;
return -1;
@@ -557,38 +768,40 @@ static ssize_t ws_enc_write_head(struct Curl_easy *data,
if(enc->payload_remain > 0) {
/* trying to write a new frame before the previous one is finished */
- failf(data, "WS: starting new frame with %zd bytes from last one "
+ failf(data, "[WS] starting new frame with %zd bytes from last one "
"remaining to be sent", (ssize_t)enc->payload_remain);
*err = CURLE_SEND_ERROR;
return -1;
}
- opcode = ws_frame_flags2op((int)flags & ~CURLWS_CONT);
- if(!opcode) {
- failf(data, "WS: provided flags not recognized '%x'", flags);
- *err = CURLE_SEND_ERROR;
+ firstbyte = ws_frame_flags2firstbyte(data, flags, enc->contfragment, err);
+ if(*err) {
return -1;
}
- if(!(flags & CURLWS_CONT)) {
- if(!enc->contfragment)
- /* not marked as continuing, this is the final fragment */
- firstbyte |= WSBIT_FIN | opcode;
- else
- /* marked as continuing, this is the final fragment; set CONT
- opcode and FIN bit */
- firstbyte |= WSBIT_FIN | WSBIT_OPCODE_CONT;
+ /* fragmentation only applies to data frames (text/binary);
+ * control frames (close/ping/pong) do not affect the CONT status */
+ if(flags & (CURLWS_TEXT | CURLWS_BINARY)) {
+ enc->contfragment = (flags & CURLWS_CONT) ? (bit)TRUE : (bit)FALSE;
+ }
- enc->contfragment = FALSE;
+ if(flags & CURLWS_PING && payload_len > 125) {
+ /* The maximum valid size of PING frames is 125 bytes. */
+ failf(data, "[WS] given PING frame is too big");
+ *err = CURLE_TOO_LARGE;
+ return -1;
}
- else if(enc->contfragment) {
- /* the previous fragment was not a final one and this is not either, keep a
- CONT opcode and no FIN bit */
- firstbyte |= WSBIT_OPCODE_CONT;
+ if(flags & CURLWS_PONG && payload_len > 125) {
+ /* The maximum valid size of PONG frames is 125 bytes. */
+ failf(data, "[WS] given PONG frame is too big");
+ *err = CURLE_TOO_LARGE;
+ return -1;
}
- else {
- firstbyte = opcode;
- enc->contfragment = TRUE;
+ if(flags & CURLWS_CLOSE && payload_len > 125) {
+ /* The maximum valid size of CLOSE frames is 125 bytes. */
+ failf(data, "[WS] given CLOSE frame is too big");
+ *err = CURLE_TOO_LARGE;
+ return -1;
}
head[0] = enc->firstbyte = firstbyte;
@@ -676,7 +889,7 @@ struct wsfield {
const char *val;
};
-CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req)
+CURLcode Curl_ws_request(struct Curl_easy *data, struct dynbuf *req)
{
unsigned int i;
CURLcode result = CURLE_OK;
@@ -689,18 +902,18 @@ CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req)
{
/* The request MUST contain an |Upgrade| header field whose value
MUST include the "websocket" keyword. */
- "Upgrade:", "websocket"
+ "Upgrade", "websocket"
},
{
/* The request MUST contain a |Connection| header field whose value
MUST include the "Upgrade" token. */
- "Connection:", "Upgrade",
+ "Connection", "Upgrade",
},
{
/* The request MUST include a header field with the name
|Sec-WebSocket-Version|. The value of this header field MUST be
13. */
- "Sec-WebSocket-Version:", "13",
+ "Sec-WebSocket-Version", "13",
},
{
/* The request MUST include a header field with the name
@@ -708,7 +921,7 @@ CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req)
consisting of a randomly selected 16-byte value that has been
base64-encoded (see Section 4 of [RFC4648]). The nonce MUST be
selected randomly for each connection. */
- "Sec-WebSocket-Key:", NULL,
+ "Sec-WebSocket-Key", NULL,
}
};
heads[3].val = &keyval[0];
@@ -717,7 +930,7 @@ CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req)
result = Curl_rand(data, (unsigned char *)rand, sizeof(rand));
if(result)
return result;
- result = Curl_base64_encode((char *)rand, sizeof(rand), &randstr, &randlen);
+ result = curlx_base64_encode((char *)rand, sizeof(rand), &randstr, &randlen);
if(result)
return result;
DEBUGASSERT(randlen < sizeof(keyval));
@@ -727,24 +940,26 @@ CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req)
}
strcpy(keyval, randstr);
free(randstr);
- for(i = 0; !result && (i < sizeof(heads)/sizeof(heads[0])); i++) {
- if(!Curl_checkheaders(data, STRCONST(heads[i].name))) {
-#ifdef USE_HYPER
- char field[128];
- msnprintf(field, sizeof(field), "%s %s", heads[i].name,
- heads[i].val);
- result = Curl_hyper_header(data, req, field);
-#else
- (void)data;
- result = Curl_dyn_addf(req, "%s %s\r\n", heads[i].name,
- heads[i].val);
-#endif
+ for(i = 0; !result && (i < CURL_ARRAYSIZE(heads)); i++) {
+ if(!Curl_checkheaders(data, heads[i].name, strlen(heads[i].name))) {
+ result = curlx_dyn_addf(req, "%s: %s\r\n", heads[i].name,
+ heads[i].val);
}
}
k->upgr101 = UPGR101_WS;
return result;
}
+static void ws_conn_dtor(void *key, size_t klen, void *entry)
+{
+ struct websocket *ws = entry;
+ (void)key;
+ (void)klen;
+ Curl_bufq_free(&ws->recvbuf);
+ Curl_bufq_free(&ws->sendbuf);
+ free(ws);
+}
+
/*
* 'nread' is number of bytes of websocket data already in the buffer at
* 'mem'.
@@ -758,21 +973,19 @@ CURLcode Curl_ws_accept(struct Curl_easy *data,
CURLcode result;
DEBUGASSERT(data->conn);
- ws = data->conn->proto.ws;
+ ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN);
if(!ws) {
size_t chunk_size = WS_CHUNK_SIZE;
ws = calloc(1, sizeof(*ws));
if(!ws)
return CURLE_OUT_OF_MEMORY;
- data->conn->proto.ws = ws;
#ifdef DEBUGBUILD
{
- char *p = getenv("CURL_WS_CHUNK_SIZE");
+ const char *p = getenv("CURL_WS_CHUNK_SIZE");
if(p) {
- long l = strtol(p, NULL, 10);
- if(l > 0 && l <= (1*1024*1024)) {
+ curl_off_t l;
+ if(!curlx_str_number(&p, &l, 1*1024*1024))
chunk_size = (size_t)l;
- }
}
}
#endif
@@ -783,6 +996,10 @@ CURLcode Curl_ws_accept(struct Curl_easy *data,
BUFQ_OPT_SOFT_LIMIT);
ws_dec_init(&ws->dec);
ws_enc_init(&ws->enc);
+ result = Curl_conn_meta_set(data->conn, CURL_META_PROTO_WS_CONN,
+ ws, ws_conn_dtor);
+ if(result)
+ return result;
}
else {
Curl_bufq_reset(&ws->recvbuf);
@@ -814,7 +1031,14 @@ CURLcode Curl_ws_accept(struct Curl_easy *data,
sizeof(ws->enc.mask));
if(result)
return result;
- infof(data, "Received 101, switch to WebSocket; mask %02x%02x%02x%02x",
+
+#ifdef DEBUGBUILD
+ if(getenv("CURL_WS_FORCE_ZERO_MASK"))
+ /* force the bit mask to 0x00000000, effectively disabling masking */
+ memset(ws->enc.mask, 0, sizeof(ws->enc.mask));
+#endif
+
+ infof(data, "[WS] Received 101, switch to WebSocket; mask %02x%02x%02x%02x",
ws->enc.mask[0], ws->enc.mask[1], ws->enc.mask[2], ws->enc.mask[3]);
/* Install our client writer that decodes WS frames payload */
@@ -838,12 +1062,12 @@ CURLcode Curl_ws_accept(struct Curl_easy *data,
nread, &result);
if(nwritten < 0)
return result;
- infof(data, "%zu bytes websocket payload", nread);
+ CURL_TRC_WS(data, "%zu bytes payload", nread);
}
else { /* !connect_only */
/* And pass any additional data to the writers */
if(nread) {
- result = Curl_client_write(data, CLIENTWRITE_BODY, (char *)mem, nread);
+ result = Curl_client_write(data, CLIENTWRITE_BODY, mem, nread);
}
}
k->upgr101 = UPGR101_RECEIVED;
@@ -871,6 +1095,8 @@ static ssize_t ws_client_collect(const unsigned char *buf, size_t buflen,
CURLcode *err)
{
struct ws_collect *ctx = userp;
+ struct Curl_easy *data = ctx->data;
+ bool auto_pong = !data->set.ws_no_auto_pong;
size_t nwritten;
curl_off_t remain = (payload_len - (payload_offset + buflen));
@@ -882,10 +1108,10 @@ static ssize_t ws_client_collect(const unsigned char *buf, size_t buflen,
ctx->payload_len = payload_len;
}
- if((frame_flags & CURLWS_PING) && !remain) {
+ if(auto_pong && (frame_flags & CURLWS_PING) && !remain) {
/* auto-respond to PINGs, only works for single-frame payloads atm */
size_t bytes;
- infof(ctx->data, "WS: auto-respond to PING with a PONG");
+ infof(ctx->data, "[WS] auto-respond to PING with a PONG");
/* send back the exact same content as a PONG */
*err = curl_ws_send(ctx->data, buf, buflen, &bytes, 0, CURLWS_PONG);
if(*err)
@@ -939,19 +1165,19 @@ CURL_EXTERN CURLcode curl_ws_recv(CURL *d, void *buffer,
if(!conn) {
/* Unhappy hack with lifetimes of transfers and connection */
if(!data->set.connect_only) {
- failf(data, "CONNECT_ONLY is required");
+ failf(data, "[WS] CONNECT_ONLY is required");
return CURLE_UNSUPPORTED_PROTOCOL;
}
Curl_getconnectinfo(data, &conn);
if(!conn) {
- failf(data, "connection not found");
+ failf(data, "[WS] connection not found");
return CURLE_BAD_FUNCTION_ARGUMENT;
}
}
- ws = conn->proto.ws;
+ ws = Curl_conn_meta_get(conn, CURL_META_PROTO_WS_CONN);
if(!ws) {
- failf(data, "connection is not setup for websocket");
+ failf(data, "[WS] connection is not setup for websocket");
return CURLE_BAD_FUNCTION_ARGUMENT;
}
@@ -972,7 +1198,7 @@ CURL_EXTERN CURLcode curl_ws_recv(CURL *d, void *buffer,
}
else if(n == 0) {
/* connection closed */
- infof(data, "connection expectedly closed?");
+ infof(data, "[WS] connection expectedly closed?");
return CURLE_GOT_NOTHING;
}
CURL_TRC_WS(data, "curl_ws_recv, added %zu bytes from network",
@@ -1017,10 +1243,29 @@ static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws,
CURLcode result;
const unsigned char *out;
size_t outlen, n;
+#ifdef DEBUGBUILD
+ /* Simulate a blocking send after this chunk has been sent */
+ bool eagain_next = FALSE;
+ size_t chunk_egain = 0;
+ const char *p = getenv("CURL_WS_CHUNK_EAGAIN");
+ if(p) {
+ curl_off_t l;
+ if(!curlx_str_number(&p, &l, 1*1024*1024))
+ chunk_egain = (size_t)l;
+ }
+#endif
while(Curl_bufq_peek(&ws->sendbuf, &out, &outlen)) {
+#ifdef DEBUGBUILD
+ if(eagain_next)
+ return CURLE_AGAIN;
+ if(chunk_egain && (outlen > chunk_egain)) {
+ outlen = chunk_egain;
+ eagain_next = TRUE;
+ }
+#endif
if(blocking) {
- result = ws_send_raw_blocking(data, ws, (char *)out, outlen);
+ result = ws_send_raw_blocking(data, ws, (const char *)out, outlen);
n = result ? 0 : outlen;
}
else if(data->set.connect_only || Curl_is_in_callback(data))
@@ -1037,11 +1282,11 @@ static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws,
return result;
}
else if(result) {
- failf(data, "WS: flush, write error %d", result);
+ failf(data, "[WS] flush, write error %d", result);
return result;
}
else {
- infof(data, "WS: flushed %zu bytes", n);
+ CURL_TRC_WS(data, "flushed %zu bytes", n);
Curl_bufq_skip(&ws->sendbuf, n);
}
}
@@ -1073,7 +1318,7 @@ static CURLcode ws_send_raw_blocking(CURL *d, struct websocket *ws,
buflen);
left_ms = Curl_timeleft(data, NULL, FALSE);
if(left_ms < 0) {
- failf(data, "Timeout waiting for socket becoming writable");
+ failf(data, "[WS] Timeout waiting for socket becoming writable");
return CURLE_SEND_ERROR;
}
@@ -1083,7 +1328,7 @@ static CURLcode ws_send_raw_blocking(CURL *d, struct websocket *ws,
ev = Curl_socket_check(CURL_SOCKET_BAD, CURL_SOCKET_BAD, sock,
left_ms ? left_ms : 500);
if(ev < 0) {
- failf(data, "Error while waiting for socket becoming writable");
+ failf(data, "[WS] Error while waiting for socket becoming writable");
return CURLE_SEND_ERROR;
}
}
@@ -1094,11 +1339,12 @@ static CURLcode ws_send_raw_blocking(CURL *d, struct websocket *ws,
static CURLcode ws_send_raw(struct Curl_easy *data, const void *buffer,
size_t buflen, size_t *pnwritten)
{
- struct websocket *ws = data->conn->proto.ws;
+ struct websocket *ws;
CURLcode result;
+ ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN);
if(!ws) {
- failf(data, "Not a websocket transfer");
+ failf(data, "[WS] Not a websocket transfer");
return CURLE_SEND_ERROR;
}
if(!buflen)
@@ -1127,15 +1373,15 @@ static CURLcode ws_send_raw(struct Curl_easy *data, const void *buffer,
return result;
}
-CURL_EXTERN CURLcode curl_ws_send(CURL *d, const void *buffer,
+CURL_EXTERN CURLcode curl_ws_send(CURL *d, const void *buffer_arg,
size_t buflen, size_t *sent,
curl_off_t fragsize,
unsigned int flags)
{
struct websocket *ws;
+ const unsigned char *buffer = buffer_arg;
ssize_t n;
- size_t space, payload_added;
- CURLcode result;
+ CURLcode result = CURLE_OK;
struct Curl_easy *data = d;
CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T
@@ -1148,26 +1394,26 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *d, const void *buffer,
goto out;
}
if(!data->conn) {
- failf(data, "No associated connection");
+ failf(data, "[WS] No associated connection");
result = CURLE_SEND_ERROR;
goto out;
}
- if(!data->conn->proto.ws) {
- failf(data, "Not a websocket transfer");
+ ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN);
+ if(!ws) {
+ failf(data, "[WS] Not a websocket transfer");
result = CURLE_SEND_ERROR;
goto out;
}
- ws = data->conn->proto.ws;
-
- /* try flushing any content still waiting to be sent. */
- result = ws_flush(data, ws, FALSE);
- if(result)
- goto out;
if(data->set.ws_raw_mode) {
/* In raw mode, we write directly to the connection */
+ /* try flushing any content still waiting to be sent. */
+ result = ws_flush(data, ws, FALSE);
+ if(result)
+ goto out;
+
if(fragsize || flags) {
- failf(data, "ws_send, raw mode: fragsize and flags cannot be non-zero");
+ failf(data, "[WS] fragsize and flags must be zero in raw mode");
return CURLE_BAD_FUNCTION_ARGUMENT;
}
result = ws_send_raw(data, buffer, buflen, sent);
@@ -1175,87 +1421,91 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *d, const void *buffer,
}
/* Not RAW mode, buf we do the frame encoding */
- space = Curl_bufq_space(&ws->sendbuf);
- CURL_TRC_WS(data, "curl_ws_send(len=%zu), sendbuf=%zu space_left=%zu",
- buflen, Curl_bufq_len(&ws->sendbuf), space);
- if(space < 14) {
- result = CURLE_AGAIN;
- goto out;
- }
- if(flags & CURLWS_OFFSET) {
- if(fragsize) {
- /* a frame series 'fragsize' bytes big, this is the first */
- n = ws_enc_write_head(data, &ws->enc, flags, fragsize,
- &ws->sendbuf, &result);
- if(n < 0)
- goto out;
+ if(ws->enc.payload_remain || !Curl_bufq_is_empty(&ws->sendbuf)) {
+ /* a frame is ongoing with payload buffered or more payload
+ * that needs to be encoded into the buffer */
+ if(buflen < ws->sendbuf_payload) {
+ /* We have been called with LESS buffer data than before. This
+ * is not how it's supposed too work. */
+ failf(data, "[WS] curl_ws_send() called with smaller 'buflen' than "
+ "bytes already buffered in previous call, %zu vs %zu",
+ buflen, ws->sendbuf_payload);
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
}
- else {
- if((curl_off_t)buflen > ws->enc.payload_remain) {
- infof(data, "WS: unaligned frame size (sending %zu instead of %"
- FMT_OFF_T ")",
- buflen, ws->enc.payload_remain);
- }
+ if((curl_off_t)buflen >
+ (ws->enc.payload_remain + (curl_off_t)ws->sendbuf_payload)) {
+ /* too large buflen beyond payload length of frame */
+ failf(data, "[WS] unaligned frame size (sending %zu instead of %"
+ FMT_OFF_T ")",
+ buflen, ws->enc.payload_remain + ws->sendbuf_payload);
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ goto out;
}
}
- else if(!ws->enc.payload_remain) {
- n = ws_enc_write_head(data, &ws->enc, flags, (curl_off_t)buflen,
+ else {
+ /* starting a new frame, we want a clean sendbuf */
+ curl_off_t payload_len = (flags & CURLWS_OFFSET) ?
+ fragsize : (curl_off_t)buflen;
+ result = ws_flush(data, ws, Curl_is_in_callback(data));
+ if(result)
+ goto out;
+
+ n = ws_enc_write_head(data, &ws->enc, flags, payload_len,
&ws->sendbuf, &result);
if(n < 0)
goto out;
}
- n = ws_enc_write_payload(&ws->enc, data,
- buffer, buflen, &ws->sendbuf, &result);
- if(n < 0)
- goto out;
- payload_added = (size_t)n;
+ /* While there is either sendbuf to flush OR more payload to encode... */
+ while(!Curl_bufq_is_empty(&ws->sendbuf) || (buflen > ws->sendbuf_payload)) {
+ /* Try to add more payload to sendbuf */
+ if(buflen > ws->sendbuf_payload) {
+ size_t prev_len = Curl_bufq_len(&ws->sendbuf);
+ n = ws_enc_write_payload(&ws->enc, data,
+ buffer + ws->sendbuf_payload,
+ buflen - ws->sendbuf_payload,
+ &ws->sendbuf, &result);
+ if(n < 0 && (result != CURLE_AGAIN))
+ goto out;
+ ws->sendbuf_payload += Curl_bufq_len(&ws->sendbuf) - prev_len;
+ if(!ws->sendbuf_payload) {
+ result = CURLE_AGAIN;
+ goto out;
+ }
+ }
- while(!result && (buflen || !Curl_bufq_is_empty(&ws->sendbuf))) {
/* flush, blocking when in callback */
result = ws_flush(data, ws, Curl_is_in_callback(data));
- if(!result) {
- DEBUGASSERT(payload_added <= buflen);
- /* all buffered data sent. Try sending the rest if there is any. */
- *sent += payload_added;
- buffer = (const char *)buffer + payload_added;
- buflen -= payload_added;
- payload_added = 0;
- if(buflen) {
- n = ws_enc_write_payload(&ws->enc, data,
- buffer, buflen, &ws->sendbuf, &result);
- if(n < 0)
- goto out;
- payload_added = Curl_bufq_len(&ws->sendbuf);
- }
+ if(!result && ws->sendbuf_payload > 0) {
+ *sent += ws->sendbuf_payload;
+ buffer += ws->sendbuf_payload;
+ buflen -= ws->sendbuf_payload;
+ ws->sendbuf_payload = 0;
}
else if(result == CURLE_AGAIN) {
- /* partially sent. how much of the call data has been part of it? what
- * should we report to out caller so it can retry/send the rest? */
- if(payload_added < buflen) {
- /* We did not add everything the caller wanted. Return just
- * the partial write to our buffer. */
- *sent = payload_added;
+ if(ws->sendbuf_payload > Curl_bufq_len(&ws->sendbuf)) {
+ /* blocked, part of payload bytes remain, report length
+ * that we managed to send. */
+ size_t flushed = (ws->sendbuf_payload - Curl_bufq_len(&ws->sendbuf));
+ *sent += flushed;
+ ws->sendbuf_payload -= flushed;
result = CURLE_OK;
goto out;
}
- else if(!buflen) {
- /* We have no payload to report a partial write. EAGAIN would make
- * the caller repeat this and add the frame again.
- * Flush blocking seems the only way out of this. */
- *sent = (size_t)n;
- result = ws_flush(data, ws, TRUE);
+ else {
+ /* blocked before sending headers or 1st payload byte. We cannot report
+ * OK on 0-length send (caller counts only payload) and EAGAIN */
+ CURL_TRC_WS(data, "EAGAIN flushing sendbuf, payload_encoded: %zu/%zu",
+ ws->sendbuf_payload, buflen);
+ DEBUGASSERT(*sent == 0);
+ result = CURLE_AGAIN;
goto out;
}
- /* We added the complete data to our sendbuf. Report one byte less as
- * sent. This partial success should make the caller invoke us again
- * with the last byte. */
- *sent = payload_added - 1;
- result = Curl_bufq_unwrite(&ws->sendbuf, 1);
- if(!result)
- result = CURLE_AGAIN;
}
+ else
+ goto out; /* real error sending the data */
}
out:
@@ -1265,42 +1515,31 @@ out:
return result;
}
-static void ws_free(struct connectdata *conn)
-{
- if(conn && conn->proto.ws) {
- Curl_bufq_free(&conn->proto.ws->recvbuf);
- Curl_bufq_free(&conn->proto.ws->sendbuf);
- Curl_safefree(conn->proto.ws);
- }
-}
-
static CURLcode ws_setup_conn(struct Curl_easy *data,
struct connectdata *conn)
{
/* WebSockets is 1.1 only (for now) */
- data->state.httpwant = CURL_HTTP_VERSION_1_1;
+ data->state.http_neg.accept_09 = FALSE;
+ data->state.http_neg.only_10 = FALSE;
+ data->state.http_neg.wanted = CURL_HTTP_V1x;
+ data->state.http_neg.allowed = CURL_HTTP_V1x;
return Curl_http_setup_conn(data, conn);
}
-static CURLcode ws_disconnect(struct Curl_easy *data,
- struct connectdata *conn,
- bool dead_connection)
-{
- (void)data;
- (void)dead_connection;
- ws_free(conn);
- return CURLE_OK;
-}
-
CURL_EXTERN const struct curl_ws_frame *curl_ws_meta(CURL *d)
{
/* we only return something for websocket, called from within the callback
when not using raw mode */
struct Curl_easy *data = d;
- if(GOOD_EASY_HANDLE(data) && Curl_is_in_callback(data) && data->conn &&
- data->conn->proto.ws && !data->set.ws_raw_mode)
- return &data->conn->proto.ws->frame;
+ if(GOOD_EASY_HANDLE(data) && Curl_is_in_callback(data) &&
+ data->conn && !data->set.ws_raw_mode) {
+ struct websocket *ws;
+ ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN);
+ if(ws)
+ return &ws->frame;
+
+ }
return NULL;
}
@@ -1317,11 +1556,12 @@ const struct Curl_handler Curl_handler_ws = {
Curl_http_getsock_do, /* doing_getsock */
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
- ws_disconnect, /* disconnect */
+ ZERO_NULL, /* disconnect */
Curl_http_write_resp, /* write_resp */
Curl_http_write_resp_hd, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ Curl_http_follow, /* follow */
PORT_HTTP, /* defport */
CURLPROTO_WS, /* protocol */
CURLPROTO_HTTP, /* family */
@@ -1343,11 +1583,12 @@ const struct Curl_handler Curl_handler_wss = {
Curl_http_getsock_do, /* doing_getsock */
ZERO_NULL, /* domore_getsock */
ZERO_NULL, /* perform_getsock */
- ws_disconnect, /* disconnect */
+ ZERO_NULL, /* disconnect */
Curl_http_write_resp, /* write_resp */
Curl_http_write_resp_hd, /* write_resp_hd */
ZERO_NULL, /* connection_check */
ZERO_NULL, /* attach connection */
+ Curl_http_follow, /* follow */
PORT_HTTPS, /* defport */
CURLPROTO_WSS, /* protocol */
CURLPROTO_HTTP, /* family */
diff --git a/libs/libcurl/src/ws.h b/libs/libcurl/src/ws.h
index 1f14d4ec4d..9b456ae4f9 100644
--- a/libs/libcurl/src/ws.h
+++ b/libs/libcurl/src/ws.h
@@ -27,53 +27,10 @@
#if !defined(CURL_DISABLE_WEBSOCKETS) && !defined(CURL_DISABLE_HTTP)
-#ifdef USE_HYPER
-#define REQTYPE void
-#else
-#define REQTYPE struct dynbuf
-#endif
-
-/* a client-side WS frame decoder, parsing frame headers and
- * payload, keeping track of current position and stats */
-enum ws_dec_state {
- WS_DEC_INIT,
- WS_DEC_HEAD,
- WS_DEC_PAYLOAD
-};
-
-struct ws_decoder {
- int frame_age; /* zero */
- int frame_flags; /* See the CURLWS_* defines */
- curl_off_t payload_offset; /* the offset parsing is at */
- curl_off_t payload_len;
- unsigned char head[10];
- int head_len, head_total;
- enum ws_dec_state state;
-};
-
-/* a client-side WS frame encoder, generating frame headers and
- * converting payloads, tracking remaining data in current frame */
-struct ws_encoder {
- curl_off_t payload_len; /* payload length of current frame */
- curl_off_t payload_remain; /* remaining payload of current */
- unsigned int xori; /* xor index */
- unsigned char mask[4]; /* 32-bit mask for this connection */
- unsigned char firstbyte; /* first byte of frame we encode */
- bool contfragment; /* set TRUE if the previous fragment sent was not final */
-};
-
-/* A websocket connection with en- and decoder that treat frames
- * and keep track of boundaries. */
-struct websocket {
- struct Curl_easy *data; /* used for write callback handling */
- struct ws_decoder dec; /* decode of we frames */
- struct ws_encoder enc; /* decode of we frames */
- struct bufq recvbuf; /* raw data from the server */
- struct bufq sendbuf; /* raw data to be sent to the server */
- struct curl_ws_frame frame; /* the current WS FRAME received */
-};
+/* meta key for storing protocol meta at connection */
+#define CURL_META_PROTO_WS_CONN "meta:proto:ws:conn"
-CURLcode Curl_ws_request(struct Curl_easy *data, REQTYPE *req);
+CURLcode Curl_ws_request(struct Curl_easy *data, struct dynbuf *req);
CURLcode Curl_ws_accept(struct Curl_easy *data, const char *mem, size_t len);
extern const struct Curl_handler Curl_handler_ws;
diff --git a/libs/libjson/src/JSONChildren.cpp b/libs/libjson/src/JSONChildren.cpp
index 9d11dc0f66..5d2374d6b0 100644
--- a/libs/libjson/src/JSONChildren.cpp
+++ b/libs/libjson/src/JSONChildren.cpp
@@ -2,7 +2,7 @@
Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-25 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/libs/libjson/src/JSONDebug.cpp b/libs/libjson/src/JSONDebug.cpp
index 69a9aa0482..80da1b3460 100644
--- a/libs/libjson/src/JSONDebug.cpp
+++ b/libs/libjson/src/JSONDebug.cpp
@@ -2,7 +2,7 @@
Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-25 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/libs/libjson/src/JSONIterators.cpp b/libs/libjson/src/JSONIterators.cpp
index 108a2d6721..6d6d793ffb 100644
--- a/libs/libjson/src/JSONIterators.cpp
+++ b/libs/libjson/src/JSONIterators.cpp
@@ -2,7 +2,7 @@
Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-25 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/libs/libjson/src/JSONMemory.cpp b/libs/libjson/src/JSONMemory.cpp
index 5b3d686416..f593183ec4 100644
--- a/libs/libjson/src/JSONMemory.cpp
+++ b/libs/libjson/src/JSONMemory.cpp
@@ -2,7 +2,7 @@
Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-25 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/libs/libjson/src/JSONNode.cpp b/libs/libjson/src/JSONNode.cpp
index ded274c68c..ccaf95f5f6 100644
--- a/libs/libjson/src/JSONNode.cpp
+++ b/libs/libjson/src/JSONNode.cpp
@@ -2,7 +2,7 @@
Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-25 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/libs/libjson/src/JSONNode_Mutex.cpp b/libs/libjson/src/JSONNode_Mutex.cpp
index d5fb746347..10ac17c59c 100644
--- a/libs/libjson/src/JSONNode_Mutex.cpp
+++ b/libs/libjson/src/JSONNode_Mutex.cpp
@@ -2,7 +2,7 @@
Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-25 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/libs/libjson/src/JSONWorker.cpp b/libs/libjson/src/JSONWorker.cpp
index 353b032b4e..278acca532 100644
--- a/libs/libjson/src/JSONWorker.cpp
+++ b/libs/libjson/src/JSONWorker.cpp
@@ -2,7 +2,7 @@
Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-25 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/libs/libjson/src/JSONWriter.cpp b/libs/libjson/src/JSONWriter.cpp
index ca8e2419be..27f2c9fa85 100644
--- a/libs/libjson/src/JSONWriter.cpp
+++ b/libs/libjson/src/JSONWriter.cpp
@@ -2,7 +2,7 @@
Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-25 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/libs/libjson/src/internalJSONNode.cpp b/libs/libjson/src/internalJSONNode.cpp
index 7ab90d43d3..1ae15db03e 100644
--- a/libs/libjson/src/internalJSONNode.cpp
+++ b/libs/libjson/src/internalJSONNode.cpp
@@ -2,7 +2,7 @@
Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-25 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/libs/libjson/src/internalJSONNode.h b/libs/libjson/src/internalJSONNode.h
index 2ad616fbcd..133c0f4d0c 100644
--- a/libs/libjson/src/internalJSONNode.h
+++ b/libs/libjson/src/internalJSONNode.h
@@ -302,6 +302,8 @@ inline long internalJSONNode::as_int(void) const {
switch(type()) {
case JSON_NULL:
return 0;
+ case JSON_STRING:
+ return atoi(_string.c_str());
case JSON_BOOL:
return _value._bool ? 1 : 0;
}
@@ -317,6 +319,8 @@ inline json_number internalJSONNode::as_float(void) const {
switch(type()) {
case JSON_NULL:
return (json_number)0.0;
+ case JSON_STRING:
+ return atof(_string.c_str());
case JSON_BOOL:
return (json_number)(_value._bool ? 1.0 : 0.0);
}
diff --git a/libs/libjson/src/libJSON.cpp b/libs/libjson/src/libJSON.cpp
index bd654fa70d..a5218c560b 100644
--- a/libs/libjson/src/libJSON.cpp
+++ b/libs/libjson/src/libJSON.cpp
@@ -2,7 +2,7 @@
Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-25 Miranda NG team (https://miranda-ng.org),
Copyright (c) 2000-12 Miranda IM project,
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/libs/libjson/src/stdafx.cxx b/libs/libjson/src/stdafx.cxx
index 58c1046959..eac064fe7b 100644
--- a/libs/libjson/src/stdafx.cxx
+++ b/libs/libjson/src/stdafx.cxx
@@ -1,6 +1,6 @@
/*
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-25 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@@ -18,35 +18,20 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "stdafx.h"
-static void eraseOldValue(JSONNode &json, const char *pszName)
-{
- if (pszName && *pszName != 0) {
- auto n = json.find(pszName);
- if (n != json.end())
- json.erase(n);
- }
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////
-
LIBJSON_DLL(JSONNode&) operator<<(JSONNode &json, const INT_PARAM &param)
{
- eraseOldValue(json, param.szName);
json.push_back(JSONNode(param.szName, param.iValue));
return json;
}
LIBJSON_DLL(JSONNode&) operator<<(JSONNode &json, const INT64_PARAM &param)
{
- eraseOldValue(json, param.szName);
json.push_back(JSONNode(param.szName, param.iValue));
return json;
}
LIBJSON_DLL(JSONNode&) operator<<(JSONNode &json, const SINT64_PARAM &param)
{
- eraseOldValue(json, param.szName);
-
char str[40];
_i64toa(param.iValue, str, 10);
return json << CHAR_PARAM(param.szName, str);
@@ -54,15 +39,12 @@ LIBJSON_DLL(JSONNode&) operator<<(JSONNode &json, const SINT64_PARAM &param)
LIBJSON_DLL(JSONNode&) operator<<(JSONNode &json, const BOOL_PARAM &param)
{
- eraseOldValue(json, param.szName);
json.push_back(JSONNode(param.szName, param.bValue));
return json;
}
LIBJSON_DLL(JSONNode&) operator<<(JSONNode &json, const CHAR_PARAM &param)
{
- eraseOldValue(json, param.szName);
-
if (param.szValue == nullptr) {
JSONNode tmp(JSON_NULL); tmp.set_name(param.szName);
json.push_back(tmp);
@@ -78,15 +60,12 @@ LIBJSON_DLL(JSONNode&) operator<<(JSONNode &json, const CHAR_PARAM &param)
LIBJSON_DLL(JSONNode&) operator<<(JSONNode &json, const WCHAR_PARAM &param)
{
- eraseOldValue(json, param.szName);
json.push_back(JSONNode(param.szName, ptrA(mir_utf8encodeW(param.wszValue)).get()));
return json;
}
LIBJSON_DLL(JSONNode&) operator<<(JSONNode &json, const NULL_PARAM &param)
{
- eraseOldValue(json, param.szName);
-
JSONNode newOne(JSON_NULL);
newOne.set_name(param.szName);
json.push_back(newOne);
@@ -95,8 +74,6 @@ LIBJSON_DLL(JSONNode&) operator<<(JSONNode &json, const NULL_PARAM &param)
LIBJSON_DLL(JSONNode&) operator<<(JSONNode &json, const JSON_PARAM &param)
{
- eraseOldValue(json, param.szName);
-
JSONNode newOne(param.node);
newOne.set_name(param.szName);
json.push_back(newOne);
diff --git a/libs/libjson/src/stdafx.h b/libs/libjson/src/stdafx.h
index 5e22df85fd..f49f09f14e 100644
--- a/libs/libjson/src/stdafx.h
+++ b/libs/libjson/src/stdafx.h
@@ -2,7 +2,7 @@
Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-25 Miranda NG team (https://miranda-ng.org),
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/libs/libsignal/src/stdafx.cxx b/libs/libsignal/src/stdafx.cxx
index 07335ea48e..9baeae5952 100644
--- a/libs/libsignal/src/stdafx.cxx
+++ b/libs/libsignal/src/stdafx.cxx
@@ -1,6 +1,6 @@
/*
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-25 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/libs/libsignal/src/stdafx.h b/libs/libsignal/src/stdafx.h
index e264c4ef12..8c5389a205 100644
--- a/libs/libsignal/src/stdafx.h
+++ b/libs/libsignal/src/stdafx.h
@@ -2,7 +2,7 @@
Miranda NG: the free IM client for Microsoft* Windows*
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org),
+Copyright (C) 2012-25 Miranda NG team (https://miranda-ng.org),
all portions of this codebase are copyrighted to the people
listed in contributors.txt.
diff --git a/libs/litehtml/include/litehtml.h b/libs/litehtml/include/litehtml.h
index 2537aee839..5687703dff 100644
--- a/libs/litehtml/include/litehtml.h
+++ b/libs/litehtml/include/litehtml.h
@@ -7,5 +7,6 @@
#include <litehtml/stylesheet.h>
#include <litehtml/element.h>
#include <litehtml/utf8_strings.h>
+#include <litehtml/document_container.h>
#endif // LITEHTML_H
diff --git a/libs/litehtml/include/litehtml/codepoint.h b/libs/litehtml/include/litehtml/codepoint.h
index 52dd495559..3e06e86e00 100644
--- a/libs/litehtml/include/litehtml/codepoint.h
+++ b/libs/litehtml/include/litehtml/codepoint.h
@@ -30,10 +30,6 @@
#ifndef LITEHTML_CODEPOINT_H__
#define LITEHTML_CODEPOINT_H__
-#include <string>
-
-#include "os_types.h"
-
namespace litehtml {
bool is_ascii_codepoint(char c);
diff --git a/libs/litehtml/include/litehtml/css_parser.h b/libs/litehtml/include/litehtml/css_parser.h
index cbd88e3b84..eb49099fb9 100644
--- a/libs/litehtml/include/litehtml/css_parser.h
+++ b/libs/litehtml/include/litehtml/css_parser.h
@@ -3,6 +3,7 @@
#include "css_tokenizer.h"
#include "stylesheet.h"
+#include <functional>
namespace litehtml
{
diff --git a/libs/litehtml/include/litehtml/css_properties.h b/libs/litehtml/include/litehtml/css_properties.h
index b7f8e7e784..a904b9cced 100644
--- a/libs/litehtml/include/litehtml/css_properties.h
+++ b/libs/litehtml/include/litehtml/css_properties.h
@@ -1,7 +1,7 @@
#ifndef LITEHTML_CSS_PROPERTIES_H
#define LITEHTML_CSS_PROPERTIES_H
-#include "os_types.h"
+#include "string_id.h"
#include "types.h"
#include "css_margins.h"
#include "borders.h"
@@ -13,6 +13,19 @@ namespace litehtml
class html_tag;
class document;
+ template<class CssT, class CompT>
+ class css_property
+ {
+ public:
+ CssT css_value;
+ CompT computed_value;
+
+ css_property(const CssT& css_val, const CompT& computed_val) : css_value(css_val), computed_value(computed_val) {}
+ };
+
+ // CSS Properties types
+ using css_line_height_t = css_property<css_length, int>;
+
class css_properties
{
private:
@@ -22,6 +35,7 @@ namespace litehtml
white_space m_white_space;
style_display m_display;
visibility m_visibility;
+ appearance m_appearance;
box_sizing m_box_sizing;
css_length m_z_index;
vertical_align m_vertical_align;
@@ -39,7 +53,7 @@ namespace litehtml
css_offsets m_css_offsets;
css_length m_css_text_indent;
css_length m_css_line_height;
- int m_line_height;
+ css_line_height_t m_line_height {{}, 0};
list_style_type m_list_style_type;
list_style_position m_list_style_position;
string m_list_style_image;
@@ -50,7 +64,13 @@ namespace litehtml
string m_font_family;
css_length m_font_weight;
font_style m_font_style;
- string m_text_decoration;
+ int m_text_decoration_line = text_decoration_line_none;
+ text_decoration_style m_text_decoration_style = text_decoration_style_solid;
+ css_length m_text_decoration_thickness;
+ web_color m_text_decoration_color;
+ string m_text_emphasis_style;
+ web_color m_text_emphasis_color;
+ int m_text_emphasis_position;
font_metrics m_font_metrics;
text_transform m_text_transform;
web_color m_color;
@@ -88,6 +108,7 @@ namespace litehtml
m_white_space(white_space_normal),
m_display(display_inline),
m_visibility(visibility_visible),
+ m_appearance(appearance_none),
m_box_sizing(box_sizing_content_box),
m_z_index(0),
m_vertical_align(va_baseline),
@@ -105,7 +126,6 @@ namespace litehtml
m_css_offsets(),
m_css_text_indent(),
m_css_line_height(0),
- m_line_height(0),
m_list_style_type(list_style_type_none),
m_list_style_position(list_style_position_outside),
m_bg(),
@@ -148,6 +168,9 @@ namespace litehtml
visibility get_visibility() const;
void set_visibility(visibility mVisibility);
+ appearance get_appearance() const;
+ void set_appearance(appearance mAppearance);
+
box_sizing get_box_sizing() const;
void set_box_sizing(box_sizing mBoxSizing);
@@ -196,8 +219,8 @@ namespace litehtml
const css_length &get_text_indent() const;
void set_text_indent(const css_length &mCssTextIndent);
- int get_line_height() const;
- void set_line_height(int mLineHeight);
+ const css_line_height_t& line_height() const;
+ css_line_height_t& line_height_w();
list_style_type get_list_style_type() const;
void set_list_style_type(list_style_type mListStyleType);
@@ -205,10 +228,10 @@ namespace litehtml
list_style_position get_list_style_position() const;
void set_list_style_position(list_style_position mListStylePosition);
- string get_list_style_image() const;
+ const string& get_list_style_image() const;
void set_list_style_image(const string& url);
- string get_list_style_image_baseurl() const;
+ const string& get_list_style_image_baseurl() const;
void set_list_style_image_baseurl(const string& url);
const background &get_bg() const;
@@ -229,10 +252,10 @@ namespace litehtml
web_color get_color() const;
void set_color(web_color color);
- string get_cursor() const;
+ const string& get_cursor() const;
void set_cursor(const string& cursor);
- string get_content() const;
+ const string& get_content() const;
void set_content(const string& content);
border_collapse get_border_collapse() const;
@@ -259,6 +282,15 @@ namespace litehtml
int get_order() const;
void set_order(int order);
+
+ int get_text_decoration_line() const;
+ text_decoration_style get_text_decoration_style() const;
+ const css_length& get_text_decoration_thickness() const;
+ const web_color& get_text_decoration_color() const;
+
+ string get_text_emphasis_style() const;
+ web_color get_text_emphasis_color() const;
+ int get_text_emphasis_position() const;
};
inline element_position css_properties::get_position() const
@@ -321,6 +353,16 @@ namespace litehtml
m_visibility = mVisibility;
}
+ inline appearance css_properties::get_appearance() const
+ {
+ return m_appearance;
+ }
+
+ inline void css_properties::set_appearance(appearance mAppearance)
+ {
+ m_appearance = mAppearance;
+ }
+
inline box_sizing css_properties::get_box_sizing() const
{
return m_box_sizing;
@@ -481,14 +523,14 @@ namespace litehtml
m_css_text_indent = mCssTextIndent;
}
- inline int css_properties::get_line_height() const
+ inline const css_line_height_t& css_properties::line_height() const
{
return m_line_height;
}
- inline void css_properties::set_line_height(int mLineHeight)
+ inline css_line_height_t& css_properties::line_height_w()
{
- m_line_height = mLineHeight;
+ return m_line_height;
}
inline list_style_type css_properties::get_list_style_type() const
@@ -511,10 +553,10 @@ namespace litehtml
m_list_style_position = mListStylePosition;
}
- inline string css_properties::get_list_style_image() const { return m_list_style_image; }
+ inline const string& css_properties::get_list_style_image() const { return m_list_style_image; }
inline void css_properties::set_list_style_image(const string& url) { m_list_style_image = url; }
- inline string css_properties::get_list_style_image_baseurl() const { return m_list_style_image_baseurl; }
+ inline const string& css_properties::get_list_style_image_baseurl() const { return m_list_style_image_baseurl; }
inline void css_properties::set_list_style_image_baseurl(const string& url) { m_list_style_image_baseurl = url; }
inline const background &css_properties::get_bg() const
@@ -570,10 +612,10 @@ namespace litehtml
inline web_color css_properties::get_color() const { return m_color; }
inline void css_properties::set_color(web_color color) { m_color = color; }
- inline string css_properties::get_cursor() const { return m_cursor; }
+ inline const string& css_properties::get_cursor() const { return m_cursor; }
inline void css_properties::set_cursor(const string& cursor) { m_cursor = cursor; }
- inline string css_properties::get_content() const { return m_content; }
+ inline const string& css_properties::get_content() const { return m_content; }
inline void css_properties::set_content(const string& content) { m_content = content; }
inline border_collapse css_properties::get_border_collapse() const
@@ -669,6 +711,41 @@ namespace litehtml
{
m_order = order;
}
+
+ inline int css_properties::get_text_decoration_line() const
+ {
+ return m_text_decoration_line;
+ }
+
+ inline text_decoration_style css_properties::get_text_decoration_style() const
+ {
+ return m_text_decoration_style;
+ }
+
+ inline const css_length& css_properties::get_text_decoration_thickness() const
+ {
+ return m_text_decoration_thickness;
+ }
+
+ inline const web_color& css_properties::get_text_decoration_color() const
+ {
+ return m_text_decoration_color;
+ }
+
+ inline string css_properties::get_text_emphasis_style() const
+ {
+ return m_text_emphasis_style;
+ }
+
+ inline web_color css_properties::get_text_emphasis_color() const
+ {
+ return m_text_emphasis_color;
+ }
+
+ inline int css_properties::get_text_emphasis_position() const
+ {
+ return m_text_emphasis_position;
+ }
}
#endif //LITEHTML_CSS_PROPERTIES_H
diff --git a/libs/litehtml/include/litehtml/css_selector.h b/libs/litehtml/include/litehtml/css_selector.h
index 042f1691e4..57aa2846b5 100644
--- a/libs/litehtml/include/litehtml/css_selector.h
+++ b/libs/litehtml/include/litehtml/css_selector.h
@@ -1,6 +1,7 @@
#ifndef LH_CSS_SELECTOR_H
#define LH_CSS_SELECTOR_H
+#include "string_id.h"
#include "style.h"
#include "media_query.h"
#include "css_tokenizer.h"
@@ -154,7 +155,7 @@ namespace litehtml
string_id prefix; // [prefix|name]
string_id name; // .name, #name, [name], :name
string value; // [name=value], :lang(value)
-
+
attr_matcher matcher; // <attr-matcher> = ~= |= ^= $= *=
bool caseless_match; // value is matched ASCII case-insensitively
diff --git a/libs/litehtml/include/litehtml/css_tokenizer.h b/libs/litehtml/include/litehtml/css_tokenizer.h
index 119c925c79..f4a61af49c 100644
--- a/libs/litehtml/include/litehtml/css_tokenizer.h
+++ b/libs/litehtml/include/litehtml/css_tokenizer.h
@@ -1,6 +1,9 @@
#ifndef LH_CSS_TOKENIZER_H
#define LH_CSS_TOKENIZER_H
+#include "types.h"
+#include <cstdio>
+
namespace litehtml
{
@@ -11,7 +14,7 @@ namespace litehtml
enum css_token_type
{
WHITESPACE = ' ',
-
+
// Giving EOF and some chars explicit names to facilitate debugging and to get rid of warning C4063: case '41' is not a valid value for switch of enum 'litehtml::css_token_type'
_EOF = EOF,
LEFT_BRACE = '{',
@@ -26,7 +29,7 @@ enum css_token_type
BANG = '!',
DOT = '.',
AMPERSAND = '&',
-
+
IDENT = -20, // do not collide with any unicode chars
FUNCTION, // calc(
AT_KEYWORD, // @media
@@ -62,12 +65,12 @@ enum css_hash_type
};
// css_token: CSS token or component value ("fat" token)
-// Tokens exist in uncomponentized form only a short time after tokenization, most of the time they are "fat".
+// Tokens exist in uncomponentized form only a short time after tokenization, most of the time they are "fat".
// All functions in css_parser work regardless of whether tokens are fat or not, as per standard.
// All functions outside of css_parser that parse media queries, selectors, property values assume tokens are componentized.
struct css_token
{
- css_token(css_token_type type = css_token_type(),
+ css_token(css_token_type type = css_token_type(),
float number = 0, css_number_type number_type = css_number_integer, string str = "")
: type(type), str(str), n{number, number_type}
{
@@ -87,13 +90,13 @@ struct css_token
case HASH:
hash_type = token.hash_type;
break;
-
+
case NUMBER:
case PERCENTAGE:
case DIMENSION:
n = token.n;
break;
-
+
case CV_FUNCTION:
case CURLY_BLOCK:
case ROUND_BLOCK:
@@ -104,17 +107,17 @@ struct css_token
default:;
}
}
-
+
css_token& operator=(const css_token& token)
{
this->~css_token();
new(this) css_token(token);
return *this;
}
-
+
~css_token()
- {
- str.~string();
+ {
+ str.~string();
if (is_component_value()) value.~vector();
}
@@ -132,7 +135,7 @@ struct css_token
};
union {
string str; // STRING, URL
- string name; // HASH, IDENT, AT_KEYWORD, FUNCTION, CV_FUNCTION
+ string name; // HASH, IDENT, AT_KEYWORD, FUNCTION, CV_FUNCTION
string unit; // DIMENSION
};
struct number {
@@ -144,7 +147,7 @@ struct css_token
number n;
vector<css_token> value; // CV_FUNCTION, XXX_BLOCK
};
-
+
string repr; // https://www.w3.org/TR/css-syntax-3/#representation
};
@@ -155,7 +158,7 @@ class css_tokenizer
{
public:
css_tokenizer(const string& input) : str(input), index(0), current_char(0) {}
-
+
css_token_vector tokenize();
private:
@@ -188,7 +191,7 @@ private:
void consume_comments();
int consume_escaped_code_point();
css_token consume_string_token(int ending_code_point);
-
+
static bool would_start_ident_sequence(three_chars chars);
string consume_ident_sequence();
diff --git a/libs/litehtml/include/litehtml/document.h b/libs/litehtml/include/litehtml/document.h
index 7119636ffe..9be8465411 100644
--- a/libs/litehtml/include/litehtml/document.h
+++ b/libs/litehtml/include/litehtml/document.h
@@ -1,10 +1,12 @@
#ifndef LH_DOCUMENT_H
#define LH_DOCUMENT_H
-#include "style.h"
+#include "stylesheet.h"
#include "types.h"
#include "master_css.h"
#include "encodings.h"
+#include "font_description.h"
+
typedef struct GumboInternalOutput GumboOutput;
namespace litehtml
@@ -16,7 +18,7 @@ namespace litehtml
string text;
string baseurl;
string media;
-
+
css_text() = default;
css_text(const char* txt, const char* url, const char* media_str)
@@ -50,6 +52,7 @@ namespace litehtml
std::shared_ptr<element> m_root;
std::shared_ptr<render_item> m_root_render;
document_container* m_container;
+ fonts_map m_fonts;
css_text::vector m_css;
litehtml::css m_styles;
litehtml::web_color m_def_color;
@@ -58,7 +61,8 @@ namespace litehtml
litehtml::size m_size;
litehtml::size m_content_size;
position::vector m_fixed_boxes;
- element::ptr m_over_element;
+ std::shared_ptr<element> m_over_element;
+ std::shared_ptr<element> m_active_element;
std::list<shared_ptr<render_item>> m_tabular_elements;
media_query_list_list::vector m_media_lists;
media_features m_media;
@@ -72,7 +76,7 @@ namespace litehtml
document_container* container() { return m_container; }
document_mode mode() const { return m_mode; }
- uint_ptr get_font(const char* name, int size, const char* weight, const char* style, const char* decoration, font_metrics* fm);
+ uint_ptr get_font(const font_description& descr, font_metrics* fm);
int render(int max_width, render_type rt = render_all);
void draw(uint_ptr hdc, int x, int y, const position* clip);
web_color get_def_color() { return m_def_color; }
@@ -86,9 +90,10 @@ namespace litehtml
bool on_mouse_over(int x, int y, int client_x, int client_y, position::vector& redraw_boxes);
bool on_lbutton_down(int x, int y, int client_x, int client_y, position::vector& redraw_boxes);
bool on_lbutton_up(int x, int y, int client_x, int client_y, position::vector& redraw_boxes);
+ bool on_button_cancel(position::vector& redraw_boxes);
bool on_mouse_leave(position::vector& redraw_boxes);
- element::ptr create_element(const char* tag_name, const string_map& attributes);
- element::ptr root();
+ std::shared_ptr<element> create_element(const char* tag_name, const string_map& attributes);
+ std::shared_ptr<element> root();
std::shared_ptr<render_item> root_render();
void get_fixed_boxes(position::vector& fixed_boxes);
void add_fixed_box(const position& pos);
@@ -97,7 +102,7 @@ namespace litehtml
bool lang_changed();
bool match_lang(const string& lang);
void add_tabular(const std::shared_ptr<render_item>& el);
- element::const_ptr get_over_element() const { return m_over_element; }
+ std::shared_ptr<const element> get_over_element() const { return m_over_element; }
void append_children_from_string(element& parent, const char* str);
void dump(dumper& cout);
@@ -108,9 +113,9 @@ namespace litehtml
document_container* container,
const string& master_styles = litehtml::master_css,
const string& user_styles = "");
-
+
private:
- uint_ptr add_font(const char* name, int size, const char* weight, const char* style, const char* decoration, font_metrics* fm);
+ uint_ptr add_font(const font_description& descr, font_metrics* fm);
GumboOutput* parse_html(estring str);
void create_node(void* gnode, elements_list& elements, bool parseTextNode);
@@ -120,7 +125,7 @@ namespace litehtml
void fix_table_parent(const std::shared_ptr<render_item> & el_ptr, style_display disp, const char* disp_str);
};
- inline element::ptr document::root()
+ inline std::shared_ptr<element> document::root()
{
return m_root;
}
diff --git a/libs/litehtml/include/litehtml/document_container.h b/libs/litehtml/include/litehtml/document_container.h
index 95efe1eb6b..47cdc4b9d3 100644
--- a/libs/litehtml/include/litehtml/document_container.h
+++ b/libs/litehtml/include/litehtml/document_container.h
@@ -1,12 +1,12 @@
#ifndef LH_DOCUMENT_CONTAINER_H
#define LH_DOCUMENT_CONTAINER_H
-#include "os_types.h"
#include "types.h"
#include "web_color.h"
#include "background.h"
#include "borders.h"
#include "element.h"
+#include "font_description.h"
#include <memory>
#include <functional>
@@ -22,7 +22,7 @@ namespace litehtml
int index;
uint_ptr font;
};
-
+
enum mouse_event
{
mouse_event_enter,
@@ -35,7 +35,7 @@ namespace litehtml
public:
fonts_map m_fonts;
- virtual litehtml::uint_ptr create_font(const char* faceName, int size, int weight, litehtml::font_style italic, unsigned int decoration, litehtml::font_metrics* fm) = 0;
+ virtual litehtml::uint_ptr create_font(const font_description& descr, const document* doc, litehtml::font_metrics* fm) = 0;
virtual void delete_font(litehtml::uint_ptr hFont) = 0;
virtual int text_width(const char* text, litehtml::uint_ptr hFont) = 0;
virtual void draw_text(litehtml::uint_ptr hdc, const char* text, litehtml::uint_ptr hFont, litehtml::web_color color, const litehtml::position& pos) = 0;
@@ -56,13 +56,14 @@ namespace litehtml
virtual void set_base_url(const char* base_url) = 0;
virtual void link(const std::shared_ptr<litehtml::document>& doc, const litehtml::element::ptr& el) = 0;
virtual void on_anchor_click(const char* url, const litehtml::element::ptr& el) = 0;
+ virtual bool on_element_click(const litehtml::element::ptr& /*el*/) { return false; };
virtual void on_mouse_event(const litehtml::element::ptr& el, litehtml::mouse_event event) = 0;
virtual void set_cursor(const char* cursor) = 0;
virtual void transform_text(litehtml::string& text, litehtml::text_transform tt) = 0;
virtual void import_css(litehtml::string& text, const litehtml::string& url, litehtml::string& baseurl) = 0;
virtual void set_clip(const litehtml::position& pos, const litehtml::border_radiuses& bdr_radius) = 0;
virtual void del_clip() = 0;
- virtual void get_client_rect(litehtml::position& client) const = 0;
+ virtual void get_viewport(litehtml::position& viewport) const = 0;
virtual litehtml::element::ptr create_element( const char* tag_name,
const litehtml::string_map& attributes,
const std::shared_ptr<litehtml::document>& doc) = 0;
diff --git a/libs/litehtml/include/litehtml/el_cdata.h b/libs/litehtml/include/litehtml/el_cdata.h
index 838cd92b1e..3e9db43b88 100644
--- a/libs/litehtml/include/litehtml/el_cdata.h
+++ b/libs/litehtml/include/litehtml/el_cdata.h
@@ -1,7 +1,7 @@
#ifndef LH_EL_CDATA_H
#define LH_EL_CDATA_H
-#include "html_tag.h"
+#include "element.h"
namespace litehtml
{
diff --git a/libs/litehtml/include/litehtml/el_comment.h b/libs/litehtml/include/litehtml/el_comment.h
index 379318ffbe..fa8e72fc91 100644
--- a/libs/litehtml/include/litehtml/el_comment.h
+++ b/libs/litehtml/include/litehtml/el_comment.h
@@ -1,7 +1,7 @@
#ifndef LH_EL_COMMENT_H
#define LH_EL_COMMENT_H
-#include "html_tag.h"
+#include "element.h"
namespace litehtml
{
diff --git a/libs/litehtml/include/litehtml/el_image.h b/libs/litehtml/include/litehtml/el_image.h
index 2b73940815..b67de11941 100644
--- a/libs/litehtml/include/litehtml/el_image.h
+++ b/libs/litehtml/include/litehtml/el_image.h
@@ -2,6 +2,7 @@
#define LH_EL_IMAGE_H
#include "html_tag.h"
+#include "document.h"
namespace litehtml
{
diff --git a/libs/litehtml/include/litehtml/el_script.h b/libs/litehtml/include/litehtml/el_script.h
index e0a0374057..0353b15796 100644
--- a/libs/litehtml/include/litehtml/el_script.h
+++ b/libs/litehtml/include/litehtml/el_script.h
@@ -1,7 +1,7 @@
#ifndef LH_EL_SCRIPT_H
#define LH_EL_SCRIPT_H
-#include "html_tag.h"
+#include "element.h"
namespace litehtml
{
diff --git a/libs/litehtml/include/litehtml/el_space.h b/libs/litehtml/include/litehtml/el_space.h
index 53c170f13a..6d447f7103 100644
--- a/libs/litehtml/include/litehtml/el_space.h
+++ b/libs/litehtml/include/litehtml/el_space.h
@@ -1,7 +1,6 @@
#ifndef LH_EL_SPACE_H
#define LH_EL_SPACE_H
-#include "html_tag.h"
#include "el_text.h"
namespace litehtml
diff --git a/libs/litehtml/include/litehtml/el_style.h b/libs/litehtml/include/litehtml/el_style.h
index 62f1546259..60c0b50c67 100644
--- a/libs/litehtml/include/litehtml/el_style.h
+++ b/libs/litehtml/include/litehtml/el_style.h
@@ -1,7 +1,7 @@
#ifndef LH_EL_STYLE_H
#define LH_EL_STYLE_H
-#include "html_tag.h"
+#include "element.h"
namespace litehtml
{
@@ -13,6 +13,7 @@ namespace litehtml
void parse_attributes() override;
bool appendChild(const ptr &el) override;
+ void compute_styles(bool recursive) override;
string_id tag() const override;
const char* get_tagName() const override;
};
diff --git a/libs/litehtml/include/litehtml/el_text.h b/libs/litehtml/include/litehtml/el_text.h
index ea54cc9c66..f3da930825 100644
--- a/libs/litehtml/include/litehtml/el_text.h
+++ b/libs/litehtml/include/litehtml/el_text.h
@@ -1,7 +1,8 @@
#ifndef LH_EL_TEXT_H
#define LH_EL_TEXT_H
-#include "html_tag.h"
+#include "element.h"
+#include "document.h"
namespace litehtml
{
diff --git a/libs/litehtml/include/litehtml/element.h b/libs/litehtml/include/litehtml/element.h
index 67dd1ae251..d18976c9f0 100644
--- a/libs/litehtml/include/litehtml/element.h
+++ b/libs/litehtml/include/litehtml/element.h
@@ -1,12 +1,12 @@
#ifndef LH_ELEMENT_H
#define LH_ELEMENT_H
+#include <functional>
#include <memory>
#include <tuple>
#include <list>
+#include "types.h"
#include "stylesheet.h"
-#include "css_offsets.h"
-#include "css_margins.h"
#include "css_properties.h"
namespace litehtml
@@ -94,7 +94,7 @@ namespace litehtml
virtual bool on_mouse_over();
virtual bool on_mouse_leave();
virtual bool on_lbutton_down();
- virtual bool on_lbutton_up();
+ virtual bool on_lbutton_up(bool is_click = true);
virtual void on_click();
virtual bool set_pseudo_class(string_id cls, bool add);
virtual bool set_class(const char* pclass, bool add);
@@ -155,7 +155,9 @@ namespace litehtml
inline bool litehtml::element::in_normal_flow() const
{
- if(css().get_position() != element_position_absolute && css().get_display() != display_none)
+ if(css().get_position() != element_position_absolute &&
+ css().get_display() != display_none &&
+ css().get_position() != element_position_fixed)
{
return true;
}
@@ -207,8 +209,7 @@ namespace litehtml
if (css().get_display() == display_block ||
css().get_display() == display_flex ||
css().get_display() == display_table ||
- css().get_display() == display_list_item ||
- css().get_display() == display_flex)
+ css().get_display() == display_list_item)
{
return true;
}
diff --git a/libs/litehtml/include/litehtml/encodings.h b/libs/litehtml/include/litehtml/encodings.h
index 2dd9d5e0f5..ec5cb03e83 100644
--- a/libs/litehtml/include/litehtml/encodings.h
+++ b/libs/litehtml/include/litehtml/encodings.h
@@ -1,6 +1,7 @@
#ifndef LH_ENCODINGS_H
#define LH_ENCODINGS_H
+#include "types.h"
namespace litehtml
{
diff --git a/libs/litehtml/include/litehtml/flex_item.h b/libs/litehtml/include/litehtml/flex_item.h
index 0c4244ae2d..4ad53ac11a 100644
--- a/libs/litehtml/include/litehtml/flex_item.h
+++ b/libs/litehtml/include/litehtml/flex_item.h
@@ -1,8 +1,8 @@
#ifndef LITEHTML_FLEX_ITEM_H
#define LITEHTML_FLEX_ITEM_H
-#include <functional>
#include "formatting_context.h"
+#include "render_item.h"
namespace litehtml
{
diff --git a/libs/litehtml/include/litehtml/font_description.h b/libs/litehtml/include/litehtml/font_description.h
new file mode 100644
index 0000000000..c4c3ee7224
--- /dev/null
+++ b/libs/litehtml/include/litehtml/font_description.h
@@ -0,0 +1,45 @@
+#ifndef LITEHTML_FONT_DESCRIPTION
+#define LITEHTML_FONT_DESCRIPTION
+
+#include <string>
+#include "types.h"
+#include "css_length.h"
+#include "web_color.h"
+
+namespace litehtml
+{
+ struct font_description
+ {
+ std::string family; // Font Family
+ int size = 0; // Font size
+ font_style style = font_style_normal; // Font stype, see the enum font_style
+ int weight; // Font weight.
+ int decoration_line = text_decoration_line_none; // Decoration line. A bitset of flags of the enum text_decoration_line
+ css_length decoration_thickness; // Decoration line thickness in pixels. See predefined values in enumtext_decoration_thickness
+ text_decoration_style decoration_style = text_decoration_style_solid; // Decoration line style. See enum text_decoration_style
+ web_color decoration_color = web_color::current_color; // Decoration line color
+ std::string emphasis_style; // Text emphasis style
+ web_color emphasis_color = web_color::current_color; // Text emphasis color
+ int emphasis_position = text_emphasis_position_over; // Text emphasis position
+
+ std::string hash() const
+ {
+ std::string out;
+ out += family;
+ out += ":sz=" + std::to_string(size);
+ out += ":st=" + std::to_string(style);
+ out += ":w=" + std::to_string(weight);
+ out += ":dl=" + std::to_string(decoration_line);
+ out += ":dt=" + decoration_thickness.to_string();
+ out += ":ds=" + std::to_string(decoration_style);
+ out += ":dc=" + decoration_color.to_string();
+ out += ":ephs=" + emphasis_style;
+ out += ":ephc=" + emphasis_color.to_string();
+ out += ":ephp=" + std::to_string(emphasis_position);
+
+ return out;
+ }
+ };
+}
+
+#endif \ No newline at end of file
diff --git a/libs/litehtml/include/litehtml/gradient.h b/libs/litehtml/include/litehtml/gradient.h
index 6313eb75b2..1bfe77cde0 100644
--- a/libs/litehtml/include/litehtml/gradient.h
+++ b/libs/litehtml/include/litehtml/gradient.h
@@ -1,6 +1,11 @@
#ifndef LITEHTML_GRADIENT_H
#define LITEHTML_GRADIENT_H
+#include "css_length.h"
+#include "string_id.h"
+#include "types.h"
+#include "web_color.h"
+
namespace litehtml
{
enum gradient_side
@@ -129,7 +134,7 @@ namespace litehtml
explicit gradient(string_id type = empty_id)
{
m_type = type;
-
+
// linear gradient: m_side default is gradient_side_none (use angle)
// radial,conic gradient: m_side default is gradient_side_x_center | gradient_side_y_center (see parse_gradient)
m_side = gradient_side_none;
@@ -144,7 +149,7 @@ namespace litehtml
// <radial-size> https://drafts.csswg.org/css-images-3/#valdef-radial-gradient-radial-size
// if radius is specified radial_extent will be set to none, see parse_radial_size
- radial_extent = radial_extent_farthest_corner;
+ radial_extent = radial_extent_farthest_corner;
radial_radius_x.predef(0);
radial_radius_y.predef(0);
@@ -182,7 +187,7 @@ namespace litehtml
gradient m_gradient;
image() : type(type_none) {}
-
+
bool is_empty() const
{
switch (type)
diff --git a/libs/litehtml/include/litehtml/html.h b/libs/litehtml/include/litehtml/html.h
index cd0500aabe..9b725d9a66 100644
--- a/libs/litehtml/include/litehtml/html.h
+++ b/libs/litehtml/include/litehtml/html.h
@@ -6,19 +6,8 @@
#include <cctype>
#include <cstring>
#include <algorithm>
-#include <functional>
-#include "os_types.h"
#include "types.h"
-#include "string_id.h"
-#include "utf8_strings.h"
-#include "background.h"
-#include "borders.h"
-#include "web_color.h"
#include "media_query.h"
-#include "html_microsyntaxes.h"
-#include "html_tag.h"
-#include "document_container.h"
-#include "document.h"
namespace litehtml
{
@@ -36,15 +25,10 @@ namespace litehtml
double t_strtod(const char* string, char** endPtr = nullptr);
string get_escaped_string(const string& in_str);
- template<typename X, typename A>
- bool is_one_of(X x, A a)
+ template<typename T, typename... Opts>
+ bool is_one_of(T val, Opts ...opts)
{
- return x == a;
- }
- template<typename X, typename A, typename... AA>
- bool is_one_of(X x, A a, AA... aa)
- {
- return x == a || is_one_of(x, aa...);
+ return (... || (val == opts));
}
template<class T>
const T& at(const vector<T>& vec, int index /*may be negative*/)
@@ -155,7 +139,7 @@ namespace litehtml
return (c >= '0' && c <= '9');
}
const auto is_digit = t_isdigit;
-
+
inline bool is_hex_digit(int ch) {
return is_digit(ch) || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F');
}
diff --git a/libs/litehtml/include/litehtml/html_microsyntaxes.h b/libs/litehtml/include/litehtml/html_microsyntaxes.h
index 193dd20d62..04c7445ee5 100644
--- a/libs/litehtml/include/litehtml/html_microsyntaxes.h
+++ b/libs/litehtml/include/litehtml/html_microsyntaxes.h
@@ -1,20 +1,22 @@
#ifndef LH_HTML_MICROSYNTAXES_H
#define LH_HTML_MICROSYNTAXES_H
+#include "types.h"
+
namespace litehtml
{
-bool html_parse_integer(const string& str, int& val);
-bool html_parse_non_negative_integer(const string& str, int& val);
+ bool html_parse_integer(const string& str, int& val);
+ bool html_parse_non_negative_integer(const string& str, int& val);
-enum html_dimension_type
-{
- html_length,
- html_percentage
-};
+ enum html_dimension_type
+ {
+ html_length,
+ html_percentage
+ };
-bool html_parse_dimension_value(const string& str, float& val, html_dimension_type& type);
-bool html_parse_nonzero_dimension_value(const string& str, float& val, html_dimension_type& type);
+ bool html_parse_dimension_value(const string& str, float& val, html_dimension_type& type);
+ bool html_parse_nonzero_dimension_value(const string& str, float& val, html_dimension_type& type);
} // namespace litehtml
diff --git a/libs/litehtml/include/litehtml/html_tag.h b/libs/litehtml/include/litehtml/html_tag.h
index 63cd64498a..d8c9dfa40b 100644
--- a/libs/litehtml/include/litehtml/html_tag.h
+++ b/libs/litehtml/include/litehtml/html_tag.h
@@ -4,8 +4,6 @@
#include "element.h"
#include "style.h"
#include "background.h"
-#include "css_margins.h"
-#include "borders.h"
#include "css_selector.h"
#include "stylesheet.h"
#include "line_box.h"
@@ -61,7 +59,7 @@ namespace litehtml
bool on_mouse_over() override;
bool on_mouse_leave() override;
bool on_lbutton_down() override;
- bool on_lbutton_up() override;
+ bool on_lbutton_up(bool is_click) override;
void on_click() override;
bool set_pseudo_class(string_id cls, bool add) override;
bool set_class(const char* pclass, bool add) override;
diff --git a/libs/litehtml/include/litehtml/iterators.h b/libs/litehtml/include/litehtml/iterators.h
index b1a678d7b4..b7e1887c16 100644
--- a/libs/litehtml/include/litehtml/iterators.h
+++ b/libs/litehtml/include/litehtml/iterators.h
@@ -2,7 +2,6 @@
#define LH_ITERATORS_H
#include "types.h"
-#include <list>
#include <functional>
namespace litehtml
@@ -45,7 +44,7 @@ namespace litehtml
~elements_iterator() = default;
void process(const std::shared_ptr<render_item>& container, const std::function<void (std::shared_ptr<render_item>&, iterator_item_type)>& func);
-
+
private:
void next_idx();
};
diff --git a/libs/litehtml/include/litehtml/line_box.h b/libs/litehtml/include/litehtml/line_box.h
index 6e945c9fdc..eac00d13d3 100644
--- a/libs/litehtml/include/litehtml/line_box.h
+++ b/libs/litehtml/include/litehtml/line_box.h
@@ -1,9 +1,8 @@
#ifndef LH_LINE_BOX_H
#define LH_LINE_BOX_H
-#include <vector>
#include <memory>
-#include "os_types.h"
+#include "css_properties.h"
#include "types.h"
namespace litehtml
@@ -40,15 +39,16 @@ namespace litehtml
};
protected:
std::shared_ptr<render_item> m_element;
- int m_rendered_min_width;
+ int m_rendered_min_width = 0;
+ int m_items_top = 0;
+ int m_items_bottom = 0;
public:
- explicit line_box_item(const std::shared_ptr<render_item>& element) : m_element(element), m_rendered_min_width(0) {}
- line_box_item() : m_element(), m_rendered_min_width(0) {};
+ explicit line_box_item(const std::shared_ptr<render_item>& element) : m_element(element) {}
line_box_item(const line_box_item& el) = default;
line_box_item(line_box_item&&) = default;
virtual ~line_box_item();
- int height() const { return right() - left(); }
+ virtual int height() const;
const std::shared_ptr<render_item>& get_el() const { return m_element; }
virtual position& pos();
virtual void place_to(int x, int y);
@@ -60,6 +60,15 @@ namespace litehtml
virtual element_type get_type() const { return type_text_part; }
virtual int get_rendered_min_width() const { return m_rendered_min_width; }
virtual void set_rendered_min_width(int min_width) { m_rendered_min_width = min_width; }
+
+ void reset_items_height() { m_items_top = m_items_bottom = 0; }
+ void add_item_height(int item_top, int item_bottom)
+ {
+ m_items_top = std::min(m_items_top, item_top);
+ m_items_bottom = std::max(m_items_bottom, item_bottom);
+ }
+ int get_items_top() const { return m_items_top; }
+ int get_items_bottom() const { return m_items_bottom; }
};
class lbi_start : public line_box_item
@@ -68,9 +77,10 @@ namespace litehtml
position m_pos;
public:
explicit lbi_start(const std::shared_ptr<render_item>& element);
- virtual ~lbi_start() override;
+ ~lbi_start() override;
void place_to(int x, int y) override;
+ int height() const override;
int width() const override;
position& pos() override { return m_pos; }
int top() const override;
@@ -110,10 +120,10 @@ namespace litehtml
{
struct va_context
{
- int baseline;
+ int line_height = 0;
+ int baseline = 0;
font_metrics fm;
-
- va_context() : baseline(0) {}
+ line_box_item* start_lbi = nullptr;
};
int m_top;
@@ -121,21 +131,19 @@ namespace litehtml
int m_right;
int m_height;
int m_width;
- int m_line_height;
- int m_default_line_height;
+ css_line_height_t m_default_line_height;
font_metrics m_font_metrics;
int m_baseline;
text_align m_text_align;
int m_min_width;
std::list< std::unique_ptr<line_box_item> > m_items;
public:
- line_box(int top, int left, int right, int line_height, const font_metrics& fm, text_align align) :
+ line_box(int top, int left, int right, const css_line_height_t& line_height, const font_metrics& fm, text_align align) :
m_top(top),
m_left(left),
m_right(right),
m_height(0),
m_width(0),
- m_line_height(0),
m_default_line_height(line_height),
m_font_metrics(fm),
m_baseline(0),
diff --git a/libs/litehtml/include/litehtml/media_query.h b/libs/litehtml/include/litehtml/media_query.h
index 94ed205e52..163191a512 100644
--- a/libs/litehtml/include/litehtml/media_query.h
+++ b/libs/litehtml/include/litehtml/media_query.h
@@ -1,6 +1,10 @@
#ifndef LH_MEDIA_QUERY_H
#define LH_MEDIA_QUERY_H
+#include "css_tokenizer.h"
+#include "string_id.h"
+#include "types.h"
+
namespace litehtml
{
// https://drafts.csswg.org/mediaqueries/#evaluating
@@ -53,7 +57,7 @@ namespace litehtml
{
string_id op = _and_; // _not_ _and_ _or_
vector<media_in_parens> m_conditions;
-
+
trilean check(const media_features& features) const;
};
@@ -68,7 +72,7 @@ namespace litehtml
short op2 = 0;
bool verify_and_convert_units(string_id syntax, css_token val[2] = 0, css_token val2[2] = 0, shared_ptr<document> doc = 0);
-
+
bool compare(int x) const { return compare((float)x); }
bool compare(float x) const;
bool check(const media_features& features) const;
@@ -92,7 +96,7 @@ namespace litehtml
media_query_list parse_media_query_list(const css_token_vector& tokens, shared_ptr<document> doc);
media_query_list parse_media_query_list(const string& str, shared_ptr<document> doc);
-
+
class media_query_list_list
{
public:
diff --git a/libs/litehtml/include/litehtml/render_item.h b/libs/litehtml/include/litehtml/render_item.h
index d67fc42624..fedc1f3887 100644
--- a/libs/litehtml/include/litehtml/render_item.h
+++ b/libs/litehtml/include/litehtml/render_item.h
@@ -2,13 +2,14 @@
#define LH_RENDER_ITEM_H
#include <memory>
-#include <utility>
#include <list>
#include <tuple>
+#include "html.h"
#include "types.h"
#include "line_box.h"
#include "table.h"
#include "formatting_context.h"
+#include "element.h"
namespace litehtml
{
@@ -348,7 +349,8 @@ namespace litehtml
m_element->css().get_float() == float_none &&
m_margins.top >= 0 &&
!is_flex_item() &&
- !is_root();
+ !is_root() &&
+ !is_one_of(css().get_overflow(), overflow_hidden, overflow_scroll, overflow_auto);
}
bool collapse_bottom_margin() const
@@ -358,7 +360,8 @@ namespace litehtml
m_element->in_normal_flow() &&
m_element->css().get_float() == float_none &&
m_margins.bottom >= 0 &&
- !is_root();
+ !is_root() &&
+ !is_one_of(css().get_overflow(), overflow_hidden, overflow_scroll, overflow_auto);
}
bool is_visible() const
diff --git a/libs/litehtml/include/litehtml/string_id.h b/libs/litehtml/include/litehtml/string_id.h
index a488b32792..6373ec9e4d 100644
--- a/libs/litehtml/include/litehtml/string_id.h
+++ b/libs/litehtml/include/litehtml/string_id.h
@@ -1,6 +1,8 @@
#ifndef LH_STRING_ID_H
#define LH_STRING_ID_H
+#include <string>
+
namespace litehtml
{
@@ -252,6 +254,14 @@ STRING_ID(
_font_size_,
_line_height_,
_text_decoration_,
+ _text_decoration_style_,
+ _text_decoration_line_,
+ _text_decoration_color_,
+ _text_decoration_thickness_,
+ _text_emphasis_,
+ _text_emphasis_style_,
+ _text_emphasis_color_,
+ _text_emphasis_position_,
_white_space_,
_text_align_,
@@ -267,6 +277,7 @@ STRING_ID(
_overflow_,
_display_,
_visibility_,
+ _appearance_,
_box_sizing_,
_z_index_,
_float_,
@@ -346,8 +357,8 @@ STRING_ID(
extern const string_id empty_id; // _id("")
extern const string_id star_id; // _id("*")
-string_id _id(const string& str);
-const string& _s(string_id id);
+string_id _id(const std::string& str);
+const std::string& _s(string_id id);
} // namespace litehtml
diff --git a/libs/litehtml/include/litehtml/style.h b/libs/litehtml/include/litehtml/style.h
index 256fd8f3ca..e68a05b725 100644
--- a/libs/litehtml/include/litehtml/style.h
+++ b/libs/litehtml/include/litehtml/style.h
@@ -1,7 +1,12 @@
#ifndef LH_STYLE_H
#define LH_STYLE_H
+#include "background.h"
+#include "css_length.h"
+#include "css_position.h"
#include "css_tokenizer.h"
+#include "gradient.h"
+#include "web_color.h"
namespace litehtml
{
@@ -28,7 +33,7 @@ namespace litehtml
bool m_has_var = false; // css_token_vector, parsing is delayed because of var()
property_value() {}
- template<class T> property_value(const T& val, bool important, bool has_var = false)
+ template<class T> property_value(const T& val, bool important, bool has_var = false)
: base(val), m_important(important), m_has_var(has_var) {}
};
@@ -73,26 +78,33 @@ namespace litehtml
void parse_keyword_comma_list(string_id name, const css_token_vector& tokens, bool important);
void parse_background_position(const css_token_vector& tokens, bool important);
void parse_background_size(const css_token_vector& tokens, bool important);
-
+
void parse_border(const css_token_vector& tokens, bool important, document_container* container);
void parse_border_side(string_id name, const css_token_vector& tokens, bool important, document_container* container);
void parse_border_radius(const css_token_vector& tokens, bool important);
-
+
bool parse_list_style_image(const css_token& tok, string& url);
void parse_list_style(const css_token_vector& tokens, string baseurl, bool important);
void parse_font(css_token_vector tokens, bool important);
-
+ void parse_text_decoration(const css_token_vector& tokens, bool important, document_container* container);
+ bool parse_text_decoration_color(const css_token& token, bool important, document_container* container);
+ void parse_text_decoration_line(const css_token_vector& tokens, bool important);
+
+ void parse_text_emphasis(const css_token_vector& tokens, bool important, document_container* container);
+ bool parse_text_emphasis_color(const css_token& token, bool important, document_container* container);
+ void parse_text_emphasis_position(const css_token_vector& tokens, bool important);
+
void parse_flex_flow(const css_token_vector& tokens, bool important);
void parse_flex(const css_token_vector& tokens, bool important);
void parse_align_self(string_id name, const css_token_vector& tokens, bool important);
-
+
void add_parsed_property(string_id name, const property_value& propval);
void add_length_property(string_id name, css_token val, string keywords, int options, bool important);
template<class T> void add_four_properties(string_id top_name, T val[4], int n, bool important);
void remove_property(string_id name, bool important);
};
-
+
bool parse_url(const css_token& token, string& url);
bool parse_length(const css_token& tok, css_length& length, int options, string keywords = "");
bool parse_angle(const css_token& tok, float& angle, bool percents_allowed = false);
diff --git a/libs/litehtml/include/litehtml/stylesheet.h b/libs/litehtml/include/litehtml/stylesheet.h
index 179f2de69b..f81bac7121 100644
--- a/libs/litehtml/include/litehtml/stylesheet.h
+++ b/libs/litehtml/include/litehtml/stylesheet.h
@@ -1,7 +1,6 @@
#ifndef LH_STYLESHEET_H
#define LH_STYLESHEET_H
-#include "style.h"
#include "css_selector.h"
#include "css_tokenizer.h"
@@ -28,7 +27,7 @@ public:
using vector = std::vector<ptr>;
enum rule_type { qualified, at };
-
+
raw_rule(rule_type type, string name = "") : type(type), name(name) {}
rule_type type;
diff --git a/libs/litehtml/include/litehtml/tstring_view.h b/libs/litehtml/include/litehtml/tstring_view.h
index 74bf5d3241..9c3c74f5fe 100644
--- a/libs/litehtml/include/litehtml/tstring_view.h
+++ b/libs/litehtml/include/litehtml/tstring_view.h
@@ -33,8 +33,6 @@
#include <cstddef>
#include <ostream>
-#include "types.h"
-
namespace litehtml {
// tstring_view is a string reference type that provides a view into a string
diff --git a/libs/litehtml/include/litehtml/types.h b/libs/litehtml/include/litehtml/types.h
index c92998cd43..43cafc3bff 100644
--- a/libs/litehtml/include/litehtml/types.h
+++ b/libs/litehtml/include/litehtml/types.h
@@ -7,7 +7,6 @@
#include <vector>
#include <map>
#include <list>
-#include <set>
#include <variant>
#include <optional>
#include <algorithm>
@@ -50,10 +49,46 @@ namespace litehtml
limited_quirks_mode
};
- const unsigned int font_decoration_none = 0x00;
- const unsigned int font_decoration_underline = 0x01;
- const unsigned int font_decoration_linethrough = 0x02;
- const unsigned int font_decoration_overline = 0x04;
+ #define style_text_decoration_line_strings "none;underline;overline;line-through"
+
+ enum text_decoration_line
+ {
+ text_decoration_line_none = 0x00,
+ text_decoration_line_underline = 0x01,
+ text_decoration_line_overline = 0x02,
+ text_decoration_line_line_through = 0x04,
+ };
+
+ #define style_text_decoration_style_strings "solid;double;dotted;dashed;wavy"
+
+ enum text_decoration_style
+ {
+ text_decoration_style_solid,
+ text_decoration_style_double,
+ text_decoration_style_dotted,
+ text_decoration_style_dashed,
+ text_decoration_style_wavy,
+ text_decoration_style_max,
+ };
+
+ #define style_text_decoration_thickness_strings "auto;from-font"
+
+ enum text_decoration_thickness
+ {
+ text_decoration_thickness_auto,
+ text_decoration_thickness_from_font,
+ };
+
+ #define style_text_emphasis_position_strings "over;under;left;right"
+
+ enum text_emphasis_position
+ {
+ text_emphasis_position_over = 0x00,
+ text_emphasis_position_under = 0x01,
+ text_emphasis_position_left = 0x02,
+ text_emphasis_position_right = 0x04,
+ };
+
using byte = unsigned char;
using ucode_t = unsigned int;
@@ -103,23 +138,19 @@ namespace litehtml
{
using vector = std::vector<position>;
- int x;
- int y;
- int width;
- int height;
+ int x = 0;
+ int y = 0;
+ int width = 0;
+ int height = 0;
- position()
- {
- x = y = width = height = 0;
- }
+ position() = default;
- position(int x, int y, int width, int height)
- {
- this->x = x;
- this->y = y;
- this->width = width;
- this->height = height;
- }
+ position(int _x, int _y, int _width, int _height) :
+ x(_x),
+ y(_y),
+ width(_width),
+ height(_height)
+ {}
int right() const { return x + width; }
int bottom() const { return y + height; }
@@ -152,12 +183,18 @@ namespace litehtml
height = sz.height;
}
+ bool operator==(const position& val)
+ {
+ return x == val.x && y == val.y && width == val.width && height == val.height;
+ }
+
void move_to(int _x, int _y)
{
x = _x;
y = _y;
}
+ [[nodiscard]]
bool does_intersect(const position* val) const
{
if(!val) return true;
@@ -174,45 +211,56 @@ namespace litehtml
val->top() <= bottom() );
}
- bool empty() const
+ [[nodiscard]]
+ position intersect(const position& src) const
{
- if(!width && !height)
+ position dest;
+ int dest_x = std::max(src.x, x);
+ int dest_y = std::max(src.y, y);
+ int dest_x2 = std::min(src.right(), right());
+ int dest_y2 = std::min(src.bottom(), bottom());
+
+ if (dest_x2 > dest_x && dest_y2 > dest_y)
{
- return true;
+ dest.x = dest_x;
+ dest.y = dest_y;
+ dest.width = dest_x2 - dest_x;
+ dest.height = dest_y2 - dest_y;
}
- return false;
+ else
+ {
+ dest.width = 0;
+ dest.height = 0;
+ }
+
+ return dest;
}
+ [[nodiscard]]
+ bool empty() const
+ {
+ return !width && !height;
+ }
+
+ [[nodiscard]]
bool is_point_inside(int _x, int _y) const
{
- if(_x >= left() && _x < right() && _y >= top() && _y < bottom())
- {
- return true;
- }
- return false;
+ return (_x >= left() && _x < right() && _y >= top() && _y < bottom());
}
};
struct font_metrics
{
- int font_size;
- int height;
- int ascent;
- int descent;
- int x_height;
- int ch_width;
- bool draw_spaces;
-
- font_metrics()
- {
- font_size = 0;
- height = 0;
- ascent = 0;
- descent = 0;
- x_height = 0;
- ch_width = 0;
- draw_spaces = true;
- }
+ int font_size = 0; // Font size in pixels. The same as size argument of the create_font function
+ int height = 0; // Font height in pixels.
+ int ascent = 0; // The distance from the baseline to the top of a line of text.
+ int descent = 0; // The distance from the baseline to the bottom of a line of text.
+ int x_height = 0; // Height of the symbol x
+ int ch_width = 0; // Height of the symbol 0
+ bool draw_spaces = true; // True to call draw text function for spaces. If False, just use space width without draw.
+ int sub_shift = 0; // The baseline shift for subscripts.
+ int super_shift = 0; // The baseline shift for superscripts.
+
int base_line() const { return descent; }
};
@@ -830,6 +878,27 @@ namespace litehtml
_baseline_type m_type;
};
+#define appearance_strings "none;auto;menulist-button;textfield;button;checkbox;listbox;menulist;meter;progress-bar;push-button;radio;searchfield;slider-horizontal;square-button;textarea"
+
+ enum appearance
+ {
+ appearance_none,
+ appearance_auto,
+ appearance_menulist_button,
+ appearance_textfield,
+ appearance_button,
+ appearance_checkbox,
+ appearance_listbox,
+ appearance_menulist,
+ appearance_meter,
+ appearance_progress_bar,
+ appearance_push_button,
+ appearance_radio,
+ appearance_searchfield,
+ appearance_slider_horizontal,
+ appearance_square_button,
+ appearance_textarea,
+ };
#define box_sizing_strings "content-box;border-box"
diff --git a/libs/litehtml/include/litehtml/url.h b/libs/litehtml/include/litehtml/url.h
index 83f98a752e..fae3db0a01 100644
--- a/libs/litehtml/include/litehtml/url.h
+++ b/libs/litehtml/include/litehtml/url.h
@@ -30,8 +30,6 @@
#ifndef LITEHTML_URL_H__
#define LITEHTML_URL_H__
-#include <ostream>
-
#include "types.h"
// https://datatracker.ietf.org/doc/html/rfc3986
diff --git a/libs/litehtml/include/litehtml/url_path.h b/libs/litehtml/include/litehtml/url_path.h
index 6ba68b03b8..f936659186 100644
--- a/libs/litehtml/include/litehtml/url_path.h
+++ b/libs/litehtml/include/litehtml/url_path.h
@@ -30,8 +30,6 @@
#ifndef LITEHTML_URL_PATH_H__
#define LITEHTML_URL_PATH_H__
-#include <ostream>
-
#include "types.h"
namespace litehtml {
diff --git a/libs/litehtml/include/litehtml/web_color.h b/libs/litehtml/include/litehtml/web_color.h
index 9bc198dbcd..ba59ae66db 100644
--- a/libs/litehtml/include/litehtml/web_color.h
+++ b/libs/litehtml/include/litehtml/web_color.h
@@ -1,6 +1,9 @@
#ifndef LH_WEB_COLOR_H
#define LH_WEB_COLOR_H
+#include "css_tokenizer.h"
+#include "types.h"
+
namespace litehtml
{
class document_container;
diff --git a/libs/litehtml/src/background.cpp b/libs/litehtml/src/background.cpp
index 3ef69b862d..eaf115da72 100644
--- a/libs/litehtml/src/background.cpp
+++ b/libs/litehtml/src/background.cpp
@@ -1,8 +1,9 @@
#include <cmath>
-#include "html.h"
#include "background.h"
#include "render_item.h"
+#include "document.h"
+#include "document_container.h"
#ifndef M_PI
# define M_PI 3.14159265358979323846
@@ -807,7 +808,7 @@ bool litehtml::background_layer::gradient_base::prepare_color_points(float line_
{
if (!color_points.empty())
{
- color_points.back().hint = item.length ?
+ color_points.back().hint = item.length ?
normalize_length(*item.length, line_len) :
*item.angle / 360;
}
diff --git a/libs/litehtml/src/css_parser.cpp b/libs/litehtml/src/css_parser.cpp
index 24e49a78c8..3a78b00645 100644
--- a/libs/litehtml/src/css_parser.cpp
+++ b/libs/litehtml/src/css_parser.cpp
@@ -1,3 +1,4 @@
+#include "encodings.h"
#include "html.h"
#include "css_parser.h"
@@ -38,7 +39,48 @@ void filter_code_points(string& input)
input = result;
}
-void remove_whitespace(css_token_vector& tokens, keep_whitespace_fn keep_whitespace)
+static const size_t kLargeSize = 50;
+static void remove_whitespace_large(css_token_vector& tokens, keep_whitespace_fn keep_whitespace);
+static void remove_whitespace_small(css_token_vector& tokens, keep_whitespace_fn keep_whitespace);
+
+void remove_whitespace_large(css_token_vector& tokens, keep_whitespace_fn keep_whitespace)
+{
+ std::vector<int> keep_idx;
+ keep_idx.reserve(tokens.size());
+ for (int i = 0; i < static_cast<int>(tokens.size()); ++i)
+ {
+ auto &tok = tokens[i];
+ bool keep = true;
+ if (tok.type == ' ')
+ {
+ const auto &left = i > 0 ? tokens[i - 1] : css_token();
+ const auto &right = at(tokens, i + 1);
+ keep = keep_whitespace && keep_whitespace(left, right);
+ }
+ else if (tok.is_component_value())
+ {
+ if (tok.value.size() > kLargeSize)
+ remove_whitespace_large(tok.value, keep_whitespace);
+ else
+ remove_whitespace_small(tok.value, keep_whitespace);
+ }
+ if (keep)
+ keep_idx.push_back(i);
+ }
+
+ if (keep_idx.size() == tokens.size())
+ return;
+ else
+ {
+ css_token_vector tmp;
+ tmp.reserve(keep_idx.size());
+ for (auto idx : keep_idx)
+ tmp.push_back(tokens[idx]);
+ tokens.swap(tmp);
+ }
+}
+
+void remove_whitespace_small(css_token_vector& tokens, keep_whitespace_fn keep_whitespace)
{
for (int i = 0; i < (int)tokens.size(); i++)
{
@@ -48,13 +90,27 @@ void remove_whitespace(css_token_vector& tokens, keep_whitespace_fn keep_whitesp
const auto& left = i > 0 ? tokens[i - 1] : css_token();
const auto& right = at(tokens, i + 1);
bool keep = keep_whitespace && keep_whitespace(left, right);
- if (!keep) remove(tokens, i), i--;
+ if (!keep)
+ remove(tokens, i), i--;
}
else if (tok.is_component_value())
- remove_whitespace(tok.value, keep_whitespace);
+ {
+ if (tok.value.size() > kLargeSize)
+ remove_whitespace_large(tok.value, keep_whitespace);
+ else
+ remove_whitespace_small(tok.value, keep_whitespace);
+ }
}
}
+void remove_whitespace(css_token_vector& tokens, keep_whitespace_fn keep_whitespace)
+{
+ if (tokens.size() > kLargeSize)
+ remove_whitespace_large(tokens, keep_whitespace);
+ else
+ remove_whitespace_small(tokens, keep_whitespace);
+}
+
void componentize(css_token_vector& tokens)
{
css_parser parser(tokens);
@@ -85,17 +141,17 @@ css_token_vector normalize(string input, int options, keep_whitespace_fn keep_wh
}
// https://www.w3.org/TR/css-syntax-3/#parse-stylesheet
-// I don't create a stylesheet because its only perpose is to pass a list of rules to
+// I don't create a stylesheet because its only perpose is to pass a list of rules to
// parse_css_stylesheet. I just return the list of rules directly instead.
raw_rule::vector css_parser::parse_stylesheet(const string& input, bool top_level)
{
// 1. If input is a byte stream for stylesheet, decode bytes from input, and set input to the result.
// not implemented, utf-8 is always assumed
string str = decode(input, encoding::utf_8); // decoding potentially broken UTF-8 into valid UTF-8
-
+
// 2. Normalize input, and set input to the result.
auto tokens = normalize(str);
-
+
return parse_stylesheet(tokens, top_level);
}
raw_rule::vector css_parser::parse_stylesheet(const css_token_vector& input, bool top_level)
@@ -147,7 +203,7 @@ raw_rule::vector css_parser::consume_list_of_rules(bool top_level)
// If the top-level flag is set, do nothing.
if (top_level) break;
- // Otherwise, reconsume the current input token. Consume a qualified rule.
+ // Otherwise, reconsume the current input token. Consume a qualified rule.
// If anything is returned, append it to the list of rules.
m_index--;
rule = consume_qualified_rule();
@@ -208,7 +264,7 @@ raw_rule::ptr css_parser::consume_qualified_rule()
// https://www.w3.org/TR/css-syntax-3/#consume-at-rule
raw_rule::ptr css_parser::consume_at_rule()
{
- // Consume the next input token. Create a new at-rule with its name set to the value of the current input token,
+ // Consume the next input token. Create a new at-rule with its name set to the value of the current input token,
// its prelude initially set to an empty list, and its value initially set to nothing.
css_token token = next_token();
raw_rule::ptr rule = make_shared<raw_rule>(raw_rule::at, token.str);
@@ -251,7 +307,7 @@ css_token css_parser::consume_simple_block(char opening_bracket)
// Create a simple block with its associated token set to the current input token and with its value initially set to an empty list.
auto block_type = css_token_type(-100 - opening_bracket); // see css_token_type
css_token block(block_type);
-
+
char closing_bracket = mirror(opening_bracket);
while (true)
@@ -289,11 +345,11 @@ css_token css_parser::consume_component_value()
// If the current input token is a <{-token>, <[-token>, or <(-token>, consume a simple block and return it.
case '{': case '[': case '(':
return consume_simple_block((char)token.ch);
-
+
// Otherwise, if the current input token is a <function-token>, consume a function and return it.
case FUNCTION:
return consume_function(token.name);
-
+
// Otherwise, return the current input token.
default:
return token;
@@ -343,7 +399,7 @@ void trim_whitespace(css_token_vector& tokens)
// next token is guaranteed to be IDENT
raw_declaration css_parser::consume_declaration()
{
- // Consume the next input token. Create a new declaration with its name set to the value of
+ // Consume the next input token. Create a new declaration with its name set to the value of
// the current input token and its value initially set to an empty list.
css_token token = next_token();
raw_declaration decl = {token.name};
@@ -364,13 +420,13 @@ raw_declaration css_parser::consume_declaration()
// 3. While the next input token is a <whitespace-token>, consume the next input token.
while (peek_token().type == ' ') next_token();
- // 4. As long as the next input token is anything other than an <EOF-token>,
+ // 4. As long as the next input token is anything other than an <EOF-token>,
// consume a component value and append it to the declaration’s value.
while (peek_token().type != EOF)
value.push_back(consume_component_value());
- // 5. If the last two non-<whitespace-token>s in the declaration’s value are a <delim-token> with the value "!"
- // followed by an <ident-token> with a value that is an ASCII case-insensitive match for "important",
+ // 5. If the last two non-<whitespace-token>s in the declaration’s value are a <delim-token> with the value "!"
+ // followed by an <ident-token> with a value that is an ASCII case-insensitive match for "important",
// remove them from the declaration’s value and set the declaration’s important flag to true.
trim_whitespace(value); // deviation from standard: removing leading whitespace as well
@@ -417,7 +473,7 @@ void css_parser::consume_style_block_contents(/*out*/ raw_declaration::vector& d
case IDENT: {
// Initialize a temporary list initially filled with the current input token.
css_token_vector temp = { token };
- // As long as the next input token is anything other than a <semicolon-token> or <EOF-token>,
+ // As long as the next input token is anything other than a <semicolon-token> or <EOF-token>,
// consume a component value and append it to the temporary list.
while (!is_one_of(peek_token().type, ';', EOF))
temp.push_back(consume_component_value());
@@ -437,7 +493,7 @@ void css_parser::consume_style_block_contents(/*out*/ raw_declaration::vector& d
break;
}
default:
- // This is a parse error. Reconsume the current input token. As long as the next input token is
+ // This is a parse error. Reconsume the current input token. As long as the next input token is
// anything other than a <;> or <EOF>, consume a component value and throw away the returned value.
css_parse_error("unexpected token in a style block");
m_index--;
@@ -511,4 +567,4 @@ bool skip_whitespace(const css_token_vector& tokens, int& index)
return index != start;
}
-} // namespace litehtml \ No newline at end of file
+} // namespace litehtml
diff --git a/libs/litehtml/src/css_properties.cpp b/libs/litehtml/src/css_properties.cpp
index 9100a998e0..6014babb3b 100644
--- a/libs/litehtml/src/css_properties.cpp
+++ b/libs/litehtml/src/css_properties.cpp
@@ -1,13 +1,15 @@
#include "html.h"
#include "css_properties.h"
#include <cmath>
+#include "document.h"
+#include "html_tag.h"
+#include "document_container.h"
#define offset(member) ((uint_ptr)&this->member - (uint_ptr)this)
+//#define offset(func) [](const css_properties& css) { return css.func; }
void litehtml::css_properties::compute(const html_tag* el, const document::ptr& doc)
{
- compute_font(el, doc);
- int font_size = get_font_size();
m_color = el->get_property<web_color>(_color_, true, web_color::black, offset(m_color));
m_el_position = (element_position) el->get_property<int>( _position_, false, element_position_static, offset(m_el_position));
@@ -15,6 +17,7 @@ void litehtml::css_properties::compute(const html_tag* el, const document::ptr&
m_visibility = (visibility) el->get_property<int>( _visibility_, true, visibility_visible, offset(m_visibility));
m_float = (element_float) el->get_property<int>( _float_, false, float_none, offset(m_float));
m_clear = (element_clear) el->get_property<int>( _clear_, false, clear_none, offset(m_clear));
+ m_appearance = (appearance) el->get_property<int>( _appearance_, false, appearance_none, offset(m_appearance));
m_box_sizing = (box_sizing) el->get_property<int>( _box_sizing_, false, box_sizing_content_box, offset(m_box_sizing));
m_overflow = (overflow) el->get_property<int>( _overflow_, false, overflow_visible, offset(m_overflow));
m_text_align = (text_align) el->get_property<int>( _text_align_, true, text_align_left, offset(m_text_align));
@@ -97,10 +100,16 @@ void litehtml::css_properties::compute(const html_tag* el, const document::ptr&
{
m_display = display_block;
}
+ } else if(el->is_replaced() && m_display == display_inline)
+ {
+ m_display = display_inline_block;
}
}
// 5. Otherwise, the remaining 'display' property values apply as specified.
+ compute_font(el, doc);
+ int font_size = get_font_size();
+
const css_length _auto = css_length::predef_value(0);
const css_length none = _auto, normal = _auto;
@@ -217,17 +226,17 @@ void litehtml::css_properties::compute(const html_tag* el, const document::ptr&
m_css_text_indent = el->get_property<css_length>(_text_indent_, true, 0, offset(m_css_text_indent));
doc->cvt_units(m_css_text_indent, m_font_metrics, 0);
- m_css_line_height = el->get_property<css_length>(_line_height_, true, normal, offset(m_css_line_height));
- if(m_css_line_height.is_predefined())
+ m_line_height.css_value = el->get_property<css_length>(_line_height_, true, normal, offset(m_line_height.css_value));
+ if(m_line_height.css_value.is_predefined())
{
- m_line_height = m_font_metrics.height;
- } else if(m_css_line_height.units() == css_units_none)
+ m_line_height.computed_value = m_font_metrics.height;
+ } else if(m_line_height.css_value.units() == css_units_none)
{
- m_line_height = (int) std::nearbyint(m_css_line_height.val() * font_size);
+ m_line_height.computed_value = (int) std::nearbyint(m_line_height.css_value.val() * font_size);
} else
{
- m_line_height = doc->to_pixels(m_css_line_height, m_font_metrics, m_font_metrics.font_size);
- m_css_line_height = (float) m_line_height;
+ m_line_height.computed_value = doc->to_pixels(m_line_height.css_value, m_font_metrics, m_font_metrics.font_size);
+ m_line_height.css_value = (float) m_line_height.computed_value;
}
m_list_style_type = (list_style_type) el->get_property<int>(_list_style_type_, true, list_style_type_disc, offset(m_list_style_type));
@@ -281,7 +290,7 @@ void litehtml::css_properties::compute_font(const html_tag* el, const document::
{
parent_sz = doc_font_size;
}
-
+
int font_size = parent_sz;
if(sz.is_predefined())
@@ -347,22 +356,96 @@ void litehtml::css_properties::compute_font(const html_tag* el, const document::
font_size = doc->to_pixels(sz, fm, 0);
}
}
-
+
m_font_size = (float)font_size;
// initialize font
m_font_family = el->get_property<string>( _font_family_, true, doc->container()->get_default_font_name(), offset(m_font_family));
m_font_weight = el->get_property<css_length>(_font_weight_, true, css_length::predef_value(font_weight_normal), offset(m_font_weight));
m_font_style = (font_style) el->get_property<int>( _font_style_, true, font_style_normal, offset(m_font_style));
- m_text_decoration = el->get_property<string>( _text_decoration_, true, "none", offset(m_text_decoration));
-
- m_font = doc->get_font(
- m_font_family.c_str(),
- font_size,
- m_font_weight.is_predefined() ? index_value(m_font_weight.predef(), font_weight_strings).c_str() : std::to_string(m_font_weight.val()).c_str(),
- index_value(m_font_style, font_style_strings).c_str(),
- m_text_decoration.c_str(),
- &m_font_metrics);
+ bool propagate_decoration = !is_one_of(m_display, display_inline_block, display_inline_table, display_inline_flex) &&
+ m_float == float_none && !is_one_of(m_el_position, element_position_absolute, element_position_fixed);
+
+ m_text_decoration_line = el->get_property<int>(_text_decoration_line_, propagate_decoration, text_decoration_line_none, offset(m_text_decoration_line));
+
+ // Merge parent text decoration with child text decoration
+ if (propagate_decoration && el->parent())
+ {
+ m_text_decoration_line |= el->parent()->css().get_text_decoration_line();
+ }
+
+ if(m_text_decoration_line)
+ {
+ m_text_decoration_thickness = el->get_property<css_length>(_text_decoration_thickness_, propagate_decoration, css_length::predef_value(text_decoration_thickness_auto), offset(m_text_decoration_thickness));
+ m_text_decoration_style = (text_decoration_style) el->get_property<int>(_text_decoration_style_, propagate_decoration, text_decoration_style_solid, offset(m_text_decoration_style));
+ m_text_decoration_color = get_color_property(el, _text_decoration_color_, propagate_decoration, web_color::current_color, offset(m_text_decoration_color));
+ } else
+ {
+ m_text_decoration_thickness = css_length::predef_value(text_decoration_thickness_auto);
+ m_text_decoration_color = web_color::current_color;
+ }
+
+ // text-emphasis
+ m_text_emphasis_style = el->get_property<string>(_text_emphasis_style_, true, "", offset(m_text_emphasis_style));
+ m_text_emphasis_position = el->get_property<int>(_text_emphasis_position_, true, text_emphasis_position_over, offset(m_text_emphasis_position));
+ m_text_emphasis_color = get_color_property(el, _text_emphasis_color_, true, web_color::current_color, offset(m_text_emphasis_color));
+
+ if(el->parent())
+ {
+ if(m_text_emphasis_style.empty() || m_text_emphasis_style == "initial" || m_text_emphasis_style == "unset")
+ {
+ m_text_emphasis_style = el->parent()->css().get_text_emphasis_style();
+ }
+ if(m_text_emphasis_color == web_color::current_color)
+ {
+ m_text_emphasis_color = el->parent()->css().get_text_emphasis_color();
+ }
+ m_text_emphasis_position |= el->parent()->css().get_text_emphasis_position();
+ }
+
+ if(m_font_weight.is_predefined())
+ {
+ switch(m_font_weight.predef())
+ {
+ case font_weight_bold:
+ m_font_weight = 700;
+ break;
+ case font_weight_bolder:
+ {
+ const int inherited = (int) el->parent()->css().m_font_weight.val();
+ if(inherited < 400) m_font_weight = 400;
+ else if(inherited >= 400 && inherited < 600) m_font_weight = 700;
+ else m_font_weight = 900;
+ }
+ break;
+ case font_weight_lighter:
+ {
+ const int inherited = (int) el->parent()->css().m_font_weight.val();
+ if(inherited < 600) m_font_weight = 100;
+ else if(inherited >= 600 && inherited < 800) m_font_weight = 400;
+ else m_font_weight = 700;
+ }
+ break;
+ default:
+ m_font_weight = 400;
+ break;
+ }
+ }
+
+ font_description descr;
+ descr.family = m_font_family;
+ descr.size = font_size;
+ descr.style = m_font_style;
+ descr.weight = (int) m_font_weight.val();
+ descr.decoration_line = m_text_decoration_line;
+ descr.decoration_thickness = m_text_decoration_thickness;
+ descr.decoration_style = m_text_decoration_style;
+ descr.decoration_color = m_text_decoration_color;
+ descr.emphasis_style = m_text_emphasis_style;
+ descr.emphasis_color = m_text_emphasis_color;
+ descr.emphasis_position = m_text_emphasis_position;
+
+ m_font = doc->get_font(descr, &m_font_metrics);
}
void litehtml::css_properties::compute_background(const html_tag* el, const document::ptr& doc)
@@ -462,6 +545,7 @@ std::vector<std::tuple<litehtml::string, litehtml::string>> litehtml::css_proper
ret.emplace_back("overflow", index_value(m_overflow, overflow_strings));
ret.emplace_back("white_space", index_value(m_white_space, white_space_strings));
ret.emplace_back("visibility", index_value(m_visibility, visibility_strings));
+ ret.emplace_back("appearance", index_value(m_appearance, appearance_strings));
ret.emplace_back("box_sizing", index_value(m_box_sizing, box_sizing_strings));
ret.emplace_back("z_index", m_z_index.to_string());
ret.emplace_back("vertical_align", index_value(m_vertical_align, vertical_align_strings));
@@ -478,7 +562,7 @@ std::vector<std::tuple<litehtml::string, litehtml::string>> litehtml::css_proper
ret.emplace_back("max_height", m_css_max_width.to_string());
ret.emplace_back("offsets", m_css_offsets.to_string());
ret.emplace_back("text_indent", m_css_text_indent.to_string());
- ret.emplace_back("line_height", std::to_string(m_line_height));
+ ret.emplace_back("line_height", std::to_string(m_line_height.computed_value));
ret.emplace_back("list_style_type", index_value(m_list_style_type, list_style_type_strings));
ret.emplace_back("list_style_position", index_value(m_list_style_position, list_style_position_strings));
ret.emplace_back("border_spacing_x", m_css_border_spacing_x.to_string());
diff --git a/libs/litehtml/src/css_selector.cpp b/libs/litehtml/src/css_selector.cpp
index 43e5a5e33d..56a11e337d 100644
--- a/libs/litehtml/src/css_selector.cpp
+++ b/libs/litehtml/src/css_selector.cpp
@@ -2,6 +2,8 @@
#include "css_selector.h"
#include "css_parser.h"
#include "internal.h"
+#include "document.h"
+#include <set>
namespace litehtml
{
@@ -20,7 +22,7 @@ void css_selector::calc_specificity()
} else
{
m_specificity.c++;
- }
+ }
}
if(m_left)
{
@@ -273,17 +275,17 @@ bool to_int(string s, int& number)
const char* ptr = s.c_str();
char* end;
int n = strtol(ptr, &end, 10);
-
+
if (end != ptr + s.size())
return false;
-
+
number = n;
return true;
}
// https://www.w3.org/TR/css-syntax-3/#anb-syntax
// I don't use the formal grammar because it creates a lot of unnecessary complexity.
-// Deviations from the standard:
+// Deviations from the standard:
// * escapes are not allowed
// * comments are allowed inside numbers and identifiers: ev/**/en
an_b parse_an_b(string s)
@@ -336,9 +338,9 @@ int find_of_keyword(const css_token_vector& tokens)
}
// :nth-child(An+B [of S]?) https://www.w3.org/TR/selectors-4/#the-nth-child-pseudo
-// :nth-last-child(An+B [of S]?)
+// :nth-last-child(An+B [of S]?)
// where S is a forgiving <complex-selector-list>
-//
+//
// :nth-of-type(An+B) https://www.w3.org/TR/selectors-4/#the-nth-of-type-pseudo
// :nth-last-of-type(An+B)
//
@@ -356,7 +358,7 @@ css_attribute_selector parse_nth_child(const css_token& token, bool of_keyword,
// The standard doesn't specify if pseudo-elements are allowed in this selector list.
// But specifying them will make selector match nothing anyway because
- // "The structural pseudo-classes only apply to elements in the document tree;
+ // "The structural pseudo-classes only apply to elements in the document tree;
// they must never match pseudo-elements." https://www.w3.org/TR/selectors-4/#structural-pseudos
// So I parse as if they were not allowed.
selector.selector_list = parse_selector_list(selector_tokens, forgiving_mode + forbid_pseudo_elements, mode);
@@ -374,10 +376,10 @@ css_attribute_selector parse_nth_child(const css_token& token, bool of_keyword,
an_b x = parse_an_b(str);
if (!x.valid) return {};
-
+
selector.a = x.a;
selector.b = x.b;
-
+
return selector;
}
@@ -540,7 +542,7 @@ css_attribute_selector parse_pseudo_element(const css_token_vector& tokens, int&
{
if (!is_one_of(b.ident(), "before", "after")) // first-line/letter are not supported
return {};
-
+
index += 2;
return {select_pseudo_element, b.ident()};
}
@@ -575,7 +577,7 @@ css_element_selector::ptr parse_compound_selector(const css_token_vector& tokens
// <subclass-selector>*
while (css_attribute_selector sel = parse_subclass_selector(tokens, index, mode))
selector->m_attrs.push_back(sel);
-
+
// [ <pseudo-element-selector> <pseudo-class-selector>* ]*
while (true)
{
@@ -613,7 +615,7 @@ int parse_combinator(const css_token_vector& tokens, int& index)
skip_whitespace(tokens, index);
return tok.ch;
}
-
+
return ws ? ' ' : 0;
}
@@ -662,10 +664,10 @@ bool has_selector(const css_selector& selector, attr_select_type type, const str
if (sel.type == type && (name == "" || equal_i(_s(sel.name), name)))
return true;
}
-
+
if (selector.m_left)
return has_selector(*selector.m_left, type, name);
-
+
return false;
}
@@ -691,7 +693,7 @@ css_selector::vector parse_selector_list(const css_token_vector& tokens, int opt
// in forgiving mode, ignore the bad selector
if (options & forgiving_mode)
continue;
-
+
// in strict mode, entire selector-list fails to parse because of one bad selector
return {};
}
diff --git a/libs/litehtml/src/css_tokenizer.cpp b/libs/litehtml/src/css_tokenizer.cpp
index b2b8761fa4..3daf31f7d0 100644
--- a/libs/litehtml/src/css_tokenizer.cpp
+++ b/libs/litehtml/src/css_tokenizer.cpp
@@ -1,4 +1,5 @@
#include "html.h"
+#include "utf8_strings.h"
#include "css_tokenizer.h"
namespace litehtml
@@ -118,7 +119,7 @@ void css_tokenizer::consume_comments()
if (str[index] == '/' && str[index + 1] == '*')
{
int i = (int)str.find("*/", index + 2);
-
+
if (i != -1)
index = i + 2;
else
@@ -199,7 +200,7 @@ css_token css_tokenizer::consume_string_token(int ending_code_point)
// Otherwise, if the next input code point is a newline, consume it.
else if (str[index] == '\n')
index++;
- // Otherwise, (the stream starts with a valid escape) consume an escaped code point and
+ // Otherwise, (the stream starts with a valid escape) consume an escaped code point and
// append the returned code point to the <string-token>’s value.
else
append_char(token.str, consume_escaped_code_point());
@@ -224,7 +225,7 @@ bool css_tokenizer::would_start_ident_sequence(three_chars chars)
if (c1 == '-')
{
- // If the second code point is an ident-start code point or a U+002D HYPHEN-MINUS, or
+ // If the second code point is an ident-start code point or a U+002D HYPHEN-MINUS, or
// the second and third code points are a valid escape, return true. Otherwise, return false.
return is_ident_start_code_point(c2) || c2 == '-' || (c2 == '\\' && c3 != '\n');
}
@@ -291,13 +292,13 @@ double css_tokenizer::convert_string_to_number(const string& str)
// Divide the string into seven components, in order from left to right:
- // 1. A sign: a single U+002B (+) or U+002D (-), or the empty string.
+ // 1. A sign: a single U+002B (+) or U+002D (-), or the empty string.
// Let s be the number -1 if the sign is U+002D (-); otherwise, let s be the number 1.
double s = 1;
if (*p == '-') s = -1, p++;
else if (*p == '+') p++;
- // 2. An integer part: zero or more digits. If there is at least one digit, let i be the number formed by
+ // 2. An integer part: zero or more digits. If there is at least one digit, let i be the number formed by
// interpreting the digits as a base-10 integer; otherwise, let i be the number 0.
double i = 0;
while (is_digit(*p)) i = i * 10 + digit_value(*p++);
@@ -305,8 +306,8 @@ double css_tokenizer::convert_string_to_number(const string& str)
// 3. A decimal point: a single U+002E (.), or the empty string.
if (*p == '.') p++;
- // 4. A fractional part: zero or more digits. If there is at least one digit, let f be the number formed by
- // interpreting the digits as a base-10 integer and d be the number of digits;
+ // 4. A fractional part: zero or more digits. If there is at least one digit, let f be the number formed by
+ // interpreting the digits as a base-10 integer and d be the number of digits;
// otherwise, let f and d be the number 0.
double f = 0, d = 0;
while (is_digit(*p)) f = f * 10 + digit_value(*p++), d++;
@@ -314,13 +315,13 @@ double css_tokenizer::convert_string_to_number(const string& str)
// 5. An exponent indicator: a single U+0045 (E) or U+0065 (e), or the empty string.
if (*p == 'e' || *p == 'E') p++;
- // 6. An exponent sign: a single U+002B (+) or U+002D (-), or the empty string.
+ // 6. An exponent sign: a single U+002B (+) or U+002D (-), or the empty string.
// Let t be the number -1 if the sign is U+002D (-); otherwise, let t be the number 1.
double t = 1;
if (*p == '-') t = -1, p++;
else if (*p == '+') p++;
- // 7. An exponent: zero or more digits. If there is at least one digit, let e be the number formed by
+ // 7. An exponent: zero or more digits. If there is at least one digit, let e be the number formed by
// interpreting the digits as a base-10 integer; otherwise, let e be the number 0.
double e = 0;
while (is_digit(*p)) e = e * 10 + digit_value(*p++);
@@ -358,11 +359,11 @@ double css_tokenizer::consume_number(css_number_type& type)
append_char(repr, str[index++]);
}
- // 5. If the next 2 or 3 input code points are U+0045 (E) or U+0065 (e),
+ // 5. If the next 2 or 3 input code points are U+0045 (E) or U+0065 (e),
// optionally followed by U+002D (-) or U+002B (+), followed by a digit, then:
bool a = lowcase(str[index]) == 'e' && is_one_of(str[index+1], '+', '-') && is_digit(str[index+2]);
bool b = lowcase(str[index]) == 'e' && is_digit(str[index+1]);
-
+
if (a || b)
{
// 1. Consume them.
@@ -394,7 +395,7 @@ css_token css_tokenizer::consume_numeric_token()
// If the next 3 input code points would start an ident sequence, then:
if (would_start_ident_sequence(peek_chars()))
{
- // 1. Create a <dimension-token> with the same value and type flag as number, and
+ // 1. Create a <dimension-token> with the same value and type flag as number, and
// a unit set initially to the empty string.
css_token token(DIMENSION, number, type);
@@ -405,7 +406,7 @@ css_token css_tokenizer::consume_numeric_token()
return token;
}
- // Otherwise, if the next input code point is U+0025 (%), consume it.
+ // Otherwise, if the next input code point is U+0025 (%), consume it.
// Create a <percentage-token> with the same value as number, and return it.
if (str[index] == '%')
{
@@ -491,7 +492,7 @@ css_token css_tokenizer::consume_url_token()
return {BAD_URL};
case '\\':
- // If the stream starts with a valid escape, consume an escaped code point and
+ // If the stream starts with a valid escape, consume an escaped code point and
// append the returned code point to the <url-token>’s value.
if (str[index] != '\n')
append_char(token.str, consume_escaped_code_point());
@@ -522,7 +523,7 @@ css_token css_tokenizer::consume_ident_like_token()
// Consume an ident sequence, and let string be the result.
auto string = consume_ident_sequence();
- // If string’s value is an ASCII case-insensitive match for "url", and the next input code point is
+ // If string’s value is an ASCII case-insensitive match for "url", and the next input code point is
// U+0028 ((), consume it.
if (lowcase(string) == "url" && str[index] == '(')
{
@@ -543,7 +544,7 @@ css_token css_tokenizer::consume_ident_like_token()
}
}
- // Otherwise, if the next input code point is U+0028 ((), consume it.
+ // Otherwise, if the next input code point is U+0028 ((), consume it.
// Create a <function-token> with its value set to string and return it.
else if (str[index] == '(')
{
@@ -629,7 +630,7 @@ css_token css_tokenizer::consume_token()
index += 2;
token.type = CDC;
}
- // Otherwise, if the input stream starts with an ident sequence, reconsume the current input code point,
+ // Otherwise, if the input stream starts with an ident sequence, reconsume the current input code point,
// consume an ident-like token, and return it.
else if (would_start_ident_sequence({ ch, next._1, next._2 }))
{
@@ -654,7 +655,7 @@ css_token css_tokenizer::consume_token()
break;
case '@':
- // If the next 3 input code points would start an ident sequence, consume an ident sequence,
+ // If the next 3 input code points would start an ident sequence, consume an ident sequence,
// create an <at-keyword-token> with its value set to the returned value, and return it.
if (would_start_ident_sequence(peek_chars()))
{
@@ -667,7 +668,7 @@ css_token css_tokenizer::consume_token()
break;
case '\\':
- // If the input stream starts with a valid escape, reconsume the current input code point,
+ // If the input stream starts with a valid escape, reconsume the current input code point,
// consume an ident-like token, and return it.
if (str[index] != '\n')
{
diff --git a/libs/litehtml/src/document.cpp b/libs/litehtml/src/document.cpp
index 13bfe0e991..d498914081 100644
--- a/libs/litehtml/src/document.cpp
+++ b/libs/litehtml/src/document.cpp
@@ -25,6 +25,7 @@
#include "render_item.h"
#include "render_table.h"
#include "render_block.h"
+#include "document_container.h"
namespace litehtml
{
@@ -36,13 +37,20 @@ document::document(document_container* container)
document::~document()
{
- m_over_element = nullptr;
+ m_over_element = m_active_element = nullptr;
+ if(m_container)
+ {
+ for(auto& font : m_fonts)
+ {
+ m_container->delete_font(font.second.font);
+ }
+ }
}
document::ptr document::createFromString(
- const estring& str,
- document_container* container,
- const string& master_styles,
+ const estring& str,
+ document_container* container,
+ const string& master_styles,
const string& user_styles )
{
// Create litehtml::document
@@ -125,7 +133,7 @@ document::ptr document::createFromString(
doc->m_root_render = doc->m_root->create_render_item(nullptr);
// Now the m_tabular_elements is filled with tabular elements.
- // We have to check the tabular elements for missing table elements
+ // We have to check the tabular elements for missing table elements
// and create the anonymous boxes in visual table layout
doc->fix_tables_layout();
@@ -209,7 +217,7 @@ GumboOutput* document::parse_html(estring str)
{
// https://html.spec.whatwg.org/multipage/parsing.html#the-input-byte-stream
encoding_sniffing_algorithm(str);
- // cannot store output in local variable because gumbo keeps pointers into it,
+ // cannot store output in local variable because gumbo keeps pointers into it,
// which will be accessed later in gumbo_tag_from_original_text
if (str.encoding == encoding::utf_8)
m_text = str;
@@ -434,83 +442,18 @@ element::ptr document::create_element(const char* tag_name, const string_map& at
return newTag;
}
-uint_ptr document::add_font( const char* name, int size, const char* weight, const char* style, const char* decoration, font_metrics* fm )
+uint_ptr document::add_font( const font_description& descr, font_metrics* fm )
{
uint_ptr ret = 0;
- if(!name)
- {
- name = m_container->get_default_font_name();
- }
-
- char strSize[20];
- t_itoa(size, strSize, 20, 10);
+ std::string key = descr.hash();
- string key = name;
- key += ":";
- key += strSize;
- key += ":";
- key += weight;
- key += ":";
- key += style;
- key += ":";
- key += decoration;
-
- if(m_container->m_fonts.find(key) == m_container->m_fonts.end())
+ if(m_fonts.find(key) == m_fonts.end())
{
- font_style fs = (font_style) value_index(style, font_style_strings, font_style_normal);
- int fw = value_index(weight, font_weight_strings, -1);
- if(fw >= 0)
- {
- switch(fw)
- {
- case font_weight_bold:
- fw = 700;
- break;
- case font_weight_bolder:
- fw = 600;
- break;
- case font_weight_lighter:
- fw = 300;
- break;
- case font_weight_normal:
- fw = 400;
- break;
- }
- } else
- {
- fw = atoi(weight);
- if(fw < 100)
- {
- fw = 400;
- }
- }
-
- unsigned int decor = 0;
+ font_item fi = {0, {}};
- if(decoration)
- {
- std::vector<string> tokens;
- split_string(decoration, tokens, " ");
- for(auto & token : tokens)
- {
- if(!t_strcasecmp(token.c_str(), "underline"))
- {
- decor |= font_decoration_underline;
- } else if(!t_strcasecmp(token.c_str(), "line-through"))
- {
- decor |= font_decoration_linethrough;
- } else if(!t_strcasecmp(token.c_str(), "overline"))
- {
- decor |= font_decoration_overline;
- }
- }
- }
-
- font_item fi= {0, {}};
-
- fi.font = m_container->create_font(name, size, fw, fs, decor, &fi.metrics);
- m_container->m_fonts[key] = fi;
+ fi.font = m_container->create_font(descr, this, &fi.metrics);
+ m_fonts[key] = fi;
ret = fi.font;
if(fm)
{
@@ -520,33 +463,18 @@ uint_ptr document::add_font( const char* name, int size, const char* weight, con
return ret;
}
-uint_ptr document::get_font( const char* name, int size, const char* weight, const char* style, const char* decoration, font_metrics* fm )
+uint_ptr document::get_font( const font_description& descr, font_metrics* fm )
{
- if(!size)
+ if(!descr.size)
{
return 0;
}
- if(!name)
- {
- name = m_container->get_default_font_name();
- }
-
- char strSize[20];
- t_itoa(size, strSize, 20, 10);
- string key = name;
- key += ":";
- key += strSize;
- key += ":";
- key += weight;
- key += ":";
- key += style;
- key += ":";
- key += decoration;
+ auto key = descr.hash();
- auto el = m_container->m_fonts.find(key);
+ auto el = m_fonts.find(key);
- if(el != m_container->m_fonts.end())
+ if(el != m_fonts.end())
{
if(fm)
{
@@ -554,7 +482,7 @@ uint_ptr document::get_font( const char* name, int size, const char* weight, con
}
return el->second.font;
}
- return add_font(name, size, weight, style, decoration, fm);
+ return add_font(descr, fm);
}
int document::render( int max_width, render_type rt )
@@ -562,12 +490,12 @@ int document::render( int max_width, render_type rt )
int ret = 0;
if(m_root && m_root_render)
{
- position client_rc;
- m_container->get_client_rect(client_rc);
+ position viewport;
+ m_container->get_viewport(viewport);
containing_block_context cb_context;
cb_context.width = max_width;
cb_context.width.type = containing_block_context::cbc_value_type_absolute;
- cb_context.height = client_rc.height;
+ cb_context.height = viewport.height;
cb_context.height.type = containing_block_context::cbc_value_type_absolute;
if(rt == render_fixed_only)
@@ -737,9 +665,9 @@ bool document::on_mouse_over( int x, int y, int client_x, int client_y, position
}
cursor = m_over_element->css().get_cursor();
}
-
+
m_container->set_cursor(cursor.c_str());
-
+
if(state_was_changed)
{
m_container->on_mouse_event(m_over_element, mouse_event_enter);
@@ -773,6 +701,7 @@ bool document::on_lbutton_down( int x, int y, int client_x, int client_y, positi
}
element::ptr over_el = m_root_render->get_element_by_point(x, y, client_x, client_y);
+ m_active_element = over_el;
bool state_was_changed = false;
@@ -826,7 +755,7 @@ bool document::on_lbutton_up( int /*x*/, int /*y*/, int /*client_x*/, int /*clie
}
if(m_over_element)
{
- if(m_over_element->on_lbutton_up())
+ if(m_over_element->on_lbutton_up(m_active_element == m_over_element))
{
return m_root->find_styles_changes(redraw_boxes);
}
@@ -834,6 +763,26 @@ bool document::on_lbutton_up( int /*x*/, int /*y*/, int /*client_x*/, int /*clie
return false;
}
+bool document::on_button_cancel(position::vector& redraw_boxes) {
+ if(!m_root || !m_root_render)
+ {
+ return false;
+ }
+ m_active_element = nullptr;
+ if(m_over_element)
+ {
+ if(m_over_element->on_mouse_leave())
+ {
+ m_container->on_mouse_event(m_over_element, mouse_event_leave);
+ }
+ if(m_over_element->on_lbutton_up(false))
+ {
+ return m_root->find_styles_changes(redraw_boxes);
+ }
+ }
+ return false;
+}
+
void document::get_fixed_boxes( position::vector& fixed_boxes )
{
fixed_boxes = m_fixed_boxes;
@@ -1129,7 +1078,7 @@ void document::append_children_from_string(element& parent, const char* str)
child->compute_styles();
// Now the m_tabular_elements is filled with tabular elements.
- // We have to check the tabular elements for missing table elements
+ // We have to check the tabular elements for missing table elements
// and create the anonymous boxes in visual table layout
fix_tables_layout();
diff --git a/libs/litehtml/src/document_container.cpp b/libs/litehtml/src/document_container.cpp
index 8c3aea61bd..95f45c62cb 100644
--- a/libs/litehtml/src/document_container.cpp
+++ b/libs/litehtml/src/document_container.cpp
@@ -1,4 +1,4 @@
-#include "html.h"
+#include "utf8_strings.h"
#include "document_container.h"
void litehtml::document_container::split_text(const char* text, const std::function<void(const char*)>& on_word, const std::function<void(const char*)>& on_space)
diff --git a/libs/litehtml/src/el_anchor.cpp b/libs/litehtml/src/el_anchor.cpp
index 372e4afb50..2491c60033 100644
--- a/libs/litehtml/src/el_anchor.cpp
+++ b/libs/litehtml/src/el_anchor.cpp
@@ -1,6 +1,6 @@
-#include "html.h"
#include "el_anchor.h"
#include "document.h"
+#include "document_container.h"
litehtml::el_anchor::el_anchor(const std::shared_ptr<litehtml::document>& doc) : html_tag(doc)
{
diff --git a/libs/litehtml/src/el_base.cpp b/libs/litehtml/src/el_base.cpp
index baf0256539..b2db13e09f 100644
--- a/libs/litehtml/src/el_base.cpp
+++ b/libs/litehtml/src/el_base.cpp
@@ -1,10 +1,10 @@
-#include "html.h"
#include "el_base.h"
#include "document.h"
+#include "document_container.h"
litehtml::el_base::el_base(const std::shared_ptr<document>& doc) : html_tag(doc)
{
-
+
}
void litehtml::el_base::parse_attributes()
diff --git a/libs/litehtml/src/el_body.cpp b/libs/litehtml/src/el_body.cpp
index 3d35f8f758..899f27f041 100644
--- a/libs/litehtml/src/el_body.cpp
+++ b/libs/litehtml/src/el_body.cpp
@@ -1,4 +1,3 @@
-#include "html.h"
#include "el_body.h"
#include "document.h"
diff --git a/libs/litehtml/src/el_break.cpp b/libs/litehtml/src/el_break.cpp
index f13b2e3cf8..f190d06c1b 100644
--- a/libs/litehtml/src/el_break.cpp
+++ b/libs/litehtml/src/el_break.cpp
@@ -1,4 +1,3 @@
-#include "html.h"
#include "el_break.h"
litehtml::el_break::el_break(const std::shared_ptr<litehtml::document>& doc) : html_tag(doc)
diff --git a/libs/litehtml/src/el_cdata.cpp b/libs/litehtml/src/el_cdata.cpp
index e5948bff13..c3b431b3f7 100644
--- a/libs/litehtml/src/el_cdata.cpp
+++ b/libs/litehtml/src/el_cdata.cpp
@@ -1,4 +1,3 @@
-#include "html.h"
#include "el_cdata.h"
litehtml::el_cdata::el_cdata(const std::shared_ptr<document>& doc) : element(doc)
diff --git a/libs/litehtml/src/el_comment.cpp b/libs/litehtml/src/el_comment.cpp
index 1ec934d046..1a29a51662 100644
--- a/libs/litehtml/src/el_comment.cpp
+++ b/libs/litehtml/src/el_comment.cpp
@@ -1,4 +1,3 @@
-#include "html.h"
#include "el_comment.h"
litehtml::el_comment::el_comment(const std::shared_ptr<document>& doc) : element(doc)
diff --git a/libs/litehtml/src/el_div.cpp b/libs/litehtml/src/el_div.cpp
index 983d45ab2a..a23681cd1d 100644
--- a/libs/litehtml/src/el_div.cpp
+++ b/libs/litehtml/src/el_div.cpp
@@ -1,5 +1,5 @@
-#include "html.h"
#include "el_div.h"
+#include "document.h"
litehtml::el_div::el_div(const document::ptr& doc) : html_tag(doc)
diff --git a/libs/litehtml/src/el_font.cpp b/libs/litehtml/src/el_font.cpp
index 3d5f6d9f73..e8e1f7e231 100644
--- a/libs/litehtml/src/el_font.cpp
+++ b/libs/litehtml/src/el_font.cpp
@@ -1,5 +1,5 @@
-#include "html.h"
#include "el_font.h"
+#include "document.h"
litehtml::el_font::el_font(const std::shared_ptr<document>& doc) : html_tag(doc)
diff --git a/libs/litehtml/src/el_image.cpp b/libs/litehtml/src/el_image.cpp
index 0dee84e011..7ac81193bb 100644
--- a/libs/litehtml/src/el_image.cpp
+++ b/libs/litehtml/src/el_image.cpp
@@ -1,6 +1,6 @@
-#include "html.h"
#include "el_image.h"
#include "render_image.h"
+#include "document_container.h"
litehtml::el_image::el_image(const document::ptr& doc) : html_tag(doc)
{
@@ -82,4 +82,4 @@ std::shared_ptr<litehtml::render_item> litehtml::el_image::create_render_item(co
auto ret = std::make_shared<render_item_image>(shared_from_this());
ret->parent(parent_ri);
return ret;
-}
+} \ No newline at end of file
diff --git a/libs/litehtml/src/el_link.cpp b/libs/litehtml/src/el_link.cpp
index aa3bc76597..2783feafca 100644
--- a/libs/litehtml/src/el_link.cpp
+++ b/libs/litehtml/src/el_link.cpp
@@ -1,7 +1,7 @@
-#include "html.h"
#include "el_link.h"
#include "document.h"
-
+#include "document_container.h"
+#include <cstring>
litehtml::el_link::el_link(const std::shared_ptr<document>& doc) : litehtml::html_tag(doc)
{
diff --git a/libs/litehtml/src/el_para.cpp b/libs/litehtml/src/el_para.cpp
index 5d79151351..0362be9dfd 100644
--- a/libs/litehtml/src/el_para.cpp
+++ b/libs/litehtml/src/el_para.cpp
@@ -1,4 +1,3 @@
-#include "html.h"
#include "el_para.h"
#include "document.h"
diff --git a/libs/litehtml/src/el_script.cpp b/libs/litehtml/src/el_script.cpp
index 4b598a137e..857e47fc5f 100644
--- a/libs/litehtml/src/el_script.cpp
+++ b/libs/litehtml/src/el_script.cpp
@@ -1,4 +1,3 @@
-#include "html.h"
#include "el_script.h"
#include "document.h"
diff --git a/libs/litehtml/src/el_style.cpp b/libs/litehtml/src/el_style.cpp
index cc2f0bab6d..f0fbc66fa2 100644
--- a/libs/litehtml/src/el_style.cpp
+++ b/libs/litehtml/src/el_style.cpp
@@ -1,4 +1,3 @@
-#include "html.h"
#include "el_style.h"
#include "document.h"
@@ -21,8 +20,17 @@ void litehtml::el_style::parse_attributes()
bool litehtml::el_style::appendChild(const ptr &el)
{
- m_children.push_back(el);
- return true;
+ if(el && el->is_text())
+ {
+ m_children.push_back(el);
+ return true;
+ }
+ return false;
+}
+
+void litehtml::el_style::compute_styles(bool /* recursive */)
+{
+ css_w().set_display(display_none);
}
litehtml::string_id litehtml::el_style::tag() const
diff --git a/libs/litehtml/src/el_table.cpp b/libs/litehtml/src/el_table.cpp
index 82b02c6921..c3ce2a9ffc 100644
--- a/libs/litehtml/src/el_table.cpp
+++ b/libs/litehtml/src/el_table.cpp
@@ -1,58 +1,47 @@
-#include "html.h"
#include "el_table.h"
#include "document.h"
-#include "iterators.h"
namespace litehtml
{
-el_table::el_table(const shared_ptr<document>& doc) : html_tag(doc)
-{
-}
-
-bool el_table::appendChild(const element::ptr& el)
-{
- if(!el) return false;
- if( el->tag() == _tbody_ ||
- el->tag() == _thead_ ||
- el->tag() == _tfoot_ ||
- el->tag() == _caption_)
+ el_table::el_table(const shared_ptr<document>& doc) :
+ html_tag(doc)
{
- return html_tag::appendChild(el);
}
- return false;
-}
-void el_table::parse_attributes()
-{
- // https://html.spec.whatwg.org/multipage/rendering.html#tables-2:attr-table-width
- const char* str = get_attr("width");
- if (str)
- map_to_dimension_property_ignoring_zero(_width_, str);
-
- // https://html.spec.whatwg.org/multipage/rendering.html#tables-2:attr-table-height
- str = get_attr("height");
- if (str)
- map_to_dimension_property(_height_, str);
-
- // https://html.spec.whatwg.org/multipage/rendering.html#tables-2:attr-table-cellspacing
- str = get_attr("cellspacing");
- if (str)
- map_to_pixel_length_property(_border_spacing_, str);
-
- // https://html.spec.whatwg.org/multipage/rendering.html#tables-2:attr-table-border
- str = get_attr("border");
- if (str)
- map_to_pixel_length_property_with_default_value(_border_width_, str, 1);
-
- // https://html.spec.whatwg.org/multipage/rendering.html#tables-2:attr-background
- str = get_attr("bgcolor");
- if (str)
+ bool el_table::appendChild(const element::ptr& el)
{
- m_style.add_property(_background_color_, str, "", false, get_document()->container());
+ if(!el) return false;
+ if(el->tag() == _tbody_ || el->tag() == _thead_ || el->tag() == _tfoot_ || el->tag() == _caption_)
+ {
+ return html_tag::appendChild(el);
+ }
+ return false;
}
- html_tag::parse_attributes();
-}
+ void el_table::parse_attributes()
+ {
+ // https://html.spec.whatwg.org/multipage/rendering.html#tables-2:attr-table-width
+ const char* str = get_attr("width");
+ if(str) map_to_dimension_property_ignoring_zero(_width_, str);
+
+ // https://html.spec.whatwg.org/multipage/rendering.html#tables-2:attr-table-height
+ str = get_attr("height");
+ if(str) map_to_dimension_property(_height_, str);
+
+ // https://html.spec.whatwg.org/multipage/rendering.html#tables-2:attr-table-cellspacing
+ str = get_attr("cellspacing");
+ if(str) map_to_pixel_length_property(_border_spacing_, str);
+
+ // https://html.spec.whatwg.org/multipage/rendering.html#tables-2:attr-table-border
+ str = get_attr("border");
+ if(str) map_to_pixel_length_property_with_default_value(_border_width_, str, 1);
+
+ // https://html.spec.whatwg.org/multipage/rendering.html#tables-2:attr-background
+ str = get_attr("bgcolor");
+ if(str) { m_style.add_property(_background_color_, str, "", false, get_document()->container()); }
+
+ html_tag::parse_attributes();
+ }
-} // namespace litehtml \ No newline at end of file
+} // namespace litehtml
diff --git a/libs/litehtml/src/el_td.cpp b/libs/litehtml/src/el_td.cpp
index 659bfb1bc7..f665c7c095 100644
--- a/libs/litehtml/src/el_td.cpp
+++ b/libs/litehtml/src/el_td.cpp
@@ -1,5 +1,5 @@
-#include "html.h"
#include "el_td.h"
+#include "document.h"
namespace litehtml
{
diff --git a/libs/litehtml/src/el_text.cpp b/libs/litehtml/src/el_text.cpp
index 70ed82e6af..042da32651 100644
--- a/libs/litehtml/src/el_text.cpp
+++ b/libs/litehtml/src/el_text.cpp
@@ -1,6 +1,7 @@
#include "html.h"
#include "el_text.h"
#include "render_item.h"
+#include "document_container.h"
litehtml::el_text::el_text(const char* text, const document::ptr& doc) : element(doc)
{
@@ -28,7 +29,7 @@ void litehtml::el_text::compute_styles(bool /*recursive*/)
element::ptr el_parent = parent();
if (el_parent)
{
- css_w().set_line_height(el_parent->css().get_line_height());
+ css_w().line_height_w() = el_parent->css().line_height();
css_w().set_font(el_parent->css().get_font());
css_w().set_font_metrics(el_parent->css().get_font_metrics());
css_w().set_white_space(el_parent->css().get_white_space());
diff --git a/libs/litehtml/src/el_title.cpp b/libs/litehtml/src/el_title.cpp
index f0e42311b4..7752d49a82 100644
--- a/libs/litehtml/src/el_title.cpp
+++ b/libs/litehtml/src/el_title.cpp
@@ -1,6 +1,6 @@
-#include "html.h"
#include "el_title.h"
#include "document.h"
+#include "document_container.h"
litehtml::el_title::el_title(const std::shared_ptr<document>& doc) : html_tag(doc)
{
diff --git a/libs/litehtml/src/el_tr.cpp b/libs/litehtml/src/el_tr.cpp
index 5b80cc1221..78c53f4e80 100644
--- a/libs/litehtml/src/el_tr.cpp
+++ b/libs/litehtml/src/el_tr.cpp
@@ -1,5 +1,5 @@
-#include "html.h"
#include "el_tr.h"
+#include "document.h"
litehtml::el_tr::el_tr(const std::shared_ptr<document>& doc) : html_tag(doc)
diff --git a/libs/litehtml/src/element.cpp b/libs/litehtml/src/element.cpp
index fb4c2c2948..8672b9de7c 100644
--- a/libs/litehtml/src/element.cpp
+++ b/libs/litehtml/src/element.cpp
@@ -317,7 +317,7 @@ string litehtml::element::get_counters_value(const string_vector& parameters)
litehtml::trim(delims, "\"'");
string_vector values;
-
+
element::ptr current = shared_from_this();
while (current != nullptr)
{
@@ -362,7 +362,7 @@ bool litehtml::element::find_counter(const string_id& counter_name_id, std::map<
}
current = current->parent();
}
-
+
return false;
}
@@ -451,7 +451,7 @@ bool element::is_text() const LITEHTML_RETURN_FUNC(false)
bool element::on_mouse_over() LITEHTML_RETURN_FUNC(false)
bool element::on_mouse_leave() LITEHTML_RETURN_FUNC(false)
bool element::on_lbutton_down() LITEHTML_RETURN_FUNC(false)
-bool element::on_lbutton_up() LITEHTML_RETURN_FUNC(false)
+bool element::on_lbutton_up(const bool /*is_click*/) LITEHTML_RETURN_FUNC(false)
bool element::set_pseudo_class( string_id /*cls*/, bool /*add*/ ) LITEHTML_RETURN_FUNC(false)
bool element::set_class( const char* /*pclass*/, bool /*add*/ ) LITEHTML_RETURN_FUNC(false)
bool element::is_replaced() const LITEHTML_RETURN_FUNC(false)
diff --git a/libs/litehtml/src/encodings.cpp b/libs/litehtml/src/encodings.cpp
index 9cb64d821b..f56c0f10fe 100644
--- a/libs/litehtml/src/encodings.cpp
+++ b/libs/litehtml/src/encodings.cpp
@@ -1,4 +1,6 @@
#include "html.h"
+#include "os_types.h"
+#include "utf8_strings.h"
#include "encodings.h"
#include <cassert>
@@ -1888,7 +1890,7 @@ loop:
// 1.
index = (int)str.find_first_of(" \t\r\n\f>", index);
if (index == -1 || end_condition(index)) throw 0; // abort prescan
-
+
// 2.
string tmp;
while (prescan_get_attribute(str, index, tmp, tmp)) {}
diff --git a/libs/litehtml/src/flex_item.cpp b/libs/litehtml/src/flex_item.cpp
index b117c22415..687c7374e6 100644
--- a/libs/litehtml/src/flex_item.cpp
+++ b/libs/litehtml/src/flex_item.cpp
@@ -1,8 +1,6 @@
-#include "html.h"
+#include <cmath>
#include "flex_item.h"
-#include "render_item.h"
#include "flex_line.h"
-#include <cmath>
void litehtml::flex_item::init(const litehtml::containing_block_context &self_size,
litehtml::formatting_context *fmt_ctx, flex_align_items align_items)
diff --git a/libs/litehtml/src/flex_line.cpp b/libs/litehtml/src/flex_line.cpp
index ce45a06335..4e22d0aca8 100644
--- a/libs/litehtml/src/flex_line.cpp
+++ b/libs/litehtml/src/flex_line.cpp
@@ -1,7 +1,5 @@
-#include "html.h"
#include "flex_line.h"
#include "flex_item.h"
-#include "render_item.h"
void litehtml::flex_line::distribute_free_space(int container_main_size)
{
diff --git a/libs/litehtml/src/formatting_context.cpp b/libs/litehtml/src/formatting_context.cpp
index e85e990c9c..ab5e65a006 100644
--- a/libs/litehtml/src/formatting_context.cpp
+++ b/libs/litehtml/src/formatting_context.cpp
@@ -1,4 +1,3 @@
-#include "html.h"
#include "render_item.h"
#include "formatting_context.h"
diff --git a/libs/litehtml/src/gumbo/CMakeLists.txt b/libs/litehtml/src/gumbo/CMakeLists.txt
index 7282604893..9c0dc5b072 100644
--- a/libs/litehtml/src/gumbo/CMakeLists.txt
+++ b/libs/litehtml/src/gumbo/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required(VERSION 3.5)
+cmake_minimum_required(VERSION 3.11)
project(gumbo C)
diff --git a/libs/litehtml/src/gumbo/char_ref.c b/libs/litehtml/src/gumbo/char_ref.c
index a1d74fd5df..406afad032 100644
--- a/libs/litehtml/src/gumbo/char_ref.c
+++ b/libs/litehtml/src/gumbo/char_ref.c
@@ -136,7 +136,7 @@ static bool consume_numeric_ref(
return false;
}
- int codepoint = 0;
+ unsigned int codepoint = 0;
bool status = true;
do {
codepoint = (codepoint * (is_hex ? 16 : 10)) + digit;
diff --git a/libs/litehtml/src/gumbo/char_ref.rl b/libs/litehtml/src/gumbo/char_ref.rl
index 139a4bbd33..464e1690ba 100644
--- a/libs/litehtml/src/gumbo/char_ref.rl
+++ b/libs/litehtml/src/gumbo/char_ref.rl
@@ -162,7 +162,7 @@ static bool consume_numeric_ref(
return false;
}
- int codepoint = 0;
+ unsigned int codepoint = 0;
bool status = true;
do {
codepoint = (codepoint * (is_hex ? 16 : 10)) + digit;
diff --git a/libs/litehtml/src/gumbo/include/gumbo.h b/libs/litehtml/src/gumbo/include/gumbo.h
index f8137cf061..83cd22d5df 100644
--- a/libs/litehtml/src/gumbo/include/gumbo.h
+++ b/libs/litehtml/src/gumbo/include/gumbo.h
@@ -389,7 +389,10 @@ typedef enum {
*/
GUMBO_INSERTION_CONVERTED_FROM_END_TAG = 1 << 4,
- /** A flag for nodes that are converted from the parse of an <isindex> tag. */
+ /**
+ * Deprecated! Do not use!
+ * It's not used anymore since special handling for <isindex> is obsolete.
+ */
GUMBO_INSERTION_FROM_ISINDEX = 1 << 5,
/** A flag for <image> tags that are rewritten as <img>. */
diff --git a/libs/litehtml/src/gumbo/include/gumbo/tag_enum.h b/libs/litehtml/src/gumbo/include/gumbo/tag_enum.h
index 7a33d1e114..4237b1dec4 100644
--- a/libs/litehtml/src/gumbo/include/gumbo/tag_enum.h
+++ b/libs/litehtml/src/gumbo/include/gumbo/tag_enum.h
@@ -151,5 +151,6 @@ GUMBO_TAG_MULTICOL,
GUMBO_TAG_NOBR,
GUMBO_TAG_SPACER,
GUMBO_TAG_DIALOG,
+GUMBO_TAG_SEARCH,
GUMBO_TAG_TT,
GUMBO_TAG_RTC,
diff --git a/libs/litehtml/src/gumbo/include/gumbo/tag_gperf.h b/libs/litehtml/src/gumbo/include/gumbo/tag_gperf.h
index 525453946f..10691e473d 100644
--- a/libs/litehtml/src/gumbo/include/gumbo/tag_gperf.h
+++ b/libs/litehtml/src/gumbo/include/gumbo/tag_gperf.h
@@ -2,32 +2,32 @@ static unsigned int tag_hash(register const char *str, register size_t len)
{
static unsigned short asso_values[] =
{
- 274, 274, 274, 274, 274, 274, 274, 274, 274, 274,
- 274, 274, 274, 274, 274, 274, 274, 274, 274, 274,
- 274, 274, 274, 274, 274, 274, 274, 274, 274, 274,
- 274, 274, 274, 274, 274, 274, 274, 274, 274, 274,
- 274, 274, 274, 274, 274, 274, 274, 274, 274, 30,
- 27, 27, 9, 6, 3, 6, 6, 3, 274, 274,
- 274, 274, 274, 274, 274, 78, 3, 171, 12, 30,
- 39, 129, 12, 105, 24, 156, 9, 51, 60, 87,
- 12, 96, 3, 6, 18, 75, 99, 96, 36, 123,
- 274, 274, 274, 274, 274, 274, 274, 78, 3, 171,
- 12, 30, 39, 129, 12, 105, 24, 156, 9, 51,
- 60, 87, 12, 96, 3, 6, 18, 75, 99, 96,
- 36, 123, 274, 274, 274, 274, 274, 274, 274, 274,
- 274, 274, 274, 274, 274, 274, 274, 274, 274, 274,
- 274, 274, 274, 274, 274, 274, 274, 274, 274, 274,
- 274, 274, 274, 274, 274, 274, 274, 274, 274, 274,
- 274, 274, 274, 274, 274, 274, 274, 274, 274, 274,
- 274, 274, 274, 274, 274, 274, 274, 274, 274, 274,
- 274, 274, 274, 274, 274, 274, 274, 274, 274, 274,
- 274, 274, 274, 274, 274, 274, 274, 274, 274, 274,
- 274, 274, 274, 274, 274, 274, 274, 274, 274, 274,
- 274, 274, 274, 274, 274, 274, 274, 274, 274, 274,
- 274, 274, 274, 274, 274, 274, 274, 274, 274, 274,
- 274, 274, 274, 274, 274, 274, 274, 274, 274, 274,
- 274, 274, 274, 274, 274, 274, 274, 274, 274, 274,
- 274, 274, 274, 274, 274, 274, 274, 274, 274
+ 284, 284, 284, 284, 284, 284, 284, 284, 284, 284,
+ 284, 284, 284, 284, 284, 284, 284, 284, 284, 284,
+ 284, 284, 284, 284, 284, 284, 284, 284, 284, 284,
+ 284, 284, 284, 284, 284, 284, 284, 284, 284, 284,
+ 284, 284, 284, 284, 284, 284, 284, 284, 284, 9,
+ 5, 4, 2, 2, 1, 2, 1, 1, 284, 284,
+ 284, 284, 284, 284, 284, 56, 62, 156, 5, 14,
+ 47, 102, 3, 78, 32, 136, 10, 28, 36, 61,
+ 22, 108, 1, 2, 7, 55, 80, 123, 115, 76,
+ 284, 284, 284, 284, 284, 284, 284, 56, 62, 156,
+ 5, 14, 47, 102, 3, 78, 32, 136, 10, 28,
+ 36, 61, 22, 108, 1, 2, 7, 55, 80, 123,
+ 115, 76, 284, 284, 284, 284, 284, 284, 284, 284,
+ 284, 284, 284, 284, 284, 284, 284, 284, 284, 284,
+ 284, 284, 284, 284, 284, 284, 284, 284, 284, 284,
+ 284, 284, 284, 284, 284, 284, 284, 284, 284, 284,
+ 284, 284, 284, 284, 284, 284, 284, 284, 284, 284,
+ 284, 284, 284, 284, 284, 284, 284, 284, 284, 284,
+ 284, 284, 284, 284, 284, 284, 284, 284, 284, 284,
+ 284, 284, 284, 284, 284, 284, 284, 284, 284, 284,
+ 284, 284, 284, 284, 284, 284, 284, 284, 284, 284,
+ 284, 284, 284, 284, 284, 284, 284, 284, 284, 284,
+ 284, 284, 284, 284, 284, 284, 284, 284, 284, 284,
+ 284, 284, 284, 284, 284, 284, 284, 284, 284, 284,
+ 284, 284, 284, 284, 284, 284, 284, 284, 284, 284,
+ 284, 284, 284, 284, 284, 284, 284, 284, 284
};
register unsigned int hval = len;
@@ -49,239 +49,244 @@ static const unsigned char kGumboTagMap[] = {
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
- GUMBO_TAG_LAST,
- GUMBO_TAG_LAST,
- GUMBO_TAG_B,
- GUMBO_TAG_LAST,
- GUMBO_TAG_LAST,
- GUMBO_TAG_LAST,
- GUMBO_TAG_LAST,
- GUMBO_TAG_LAST,
GUMBO_TAG_S,
GUMBO_TAG_LAST,
- GUMBO_TAG_LAST,
- GUMBO_TAG_LAST,
- GUMBO_TAG_LAST,
- GUMBO_TAG_LAST,
- GUMBO_TAG_LAST,
GUMBO_TAG_H6,
- GUMBO_TAG_SPACER,
- GUMBO_TAG_LAST,
- GUMBO_TAG_RP,
- GUMBO_TAG_LAST,
- GUMBO_TAG_P,
GUMBO_TAG_H5,
- GUMBO_TAG_DIR,
- GUMBO_TAG_LAST,
GUMBO_TAG_H4,
- GUMBO_TAG_LAST,
- GUMBO_TAG_LAST,
- GUMBO_TAG_SMALL,
+ GUMBO_TAG_H3,
+ GUMBO_TAG_SPACER,
+ GUMBO_TAG_H2,
GUMBO_TAG_HEADER,
- GUMBO_TAG_SAMP,
- GUMBO_TAG_LABEL,
- GUMBO_TAG_DEL,
- GUMBO_TAG_DETAILS,
- GUMBO_TAG_RB,
- GUMBO_TAG_LEGEND,
+ GUMBO_TAG_SEARCH,
GUMBO_TAG_HEAD,
- GUMBO_TAG_BASEFONT,
+ GUMBO_TAG_H1,
+ GUMBO_TAG_DETAILS,
GUMBO_TAG_SELECT,
+ GUMBO_TAG_DIR,
+ GUMBO_TAG_LAST,
+ GUMBO_TAG_DEL,
GUMBO_TAG_LAST,
- GUMBO_TAG_H3,
GUMBO_TAG_SOURCE,
- GUMBO_TAG_BGSOUND,
- GUMBO_TAG_H2,
- GUMBO_TAG_SUB,
- GUMBO_TAG_BASE,
+ GUMBO_TAG_LEGEND,
GUMBO_TAG_DATALIST,
- GUMBO_TAG_FOOTER,
GUMBO_TAG_LAST,
- GUMBO_TAG_H1,
- GUMBO_TAG_HGROUP,
+ GUMBO_TAG_RP,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
- GUMBO_TAG_SUP,
- GUMBO_TAG_PICTURE,
- GUMBO_TAG_EMBED,
+ GUMBO_TAG_LABEL,
+ GUMBO_TAG_TABLE,
+ GUMBO_TAG_TEMPLATE,
+ GUMBO_TAG_SAMP,
GUMBO_TAG_LAST,
GUMBO_TAG_TIME,
GUMBO_TAG_TITLE,
- GUMBO_TAG_XMP,
- GUMBO_TAG_FONT,
- GUMBO_TAG_TABLE,
+ GUMBO_TAG_METER,
GUMBO_TAG_LAST,
+ GUMBO_TAG_SMALL,
+ GUMBO_TAG_MATH,
GUMBO_TAG_LAST,
- GUMBO_TAG_TEMPLATE,
- GUMBO_TAG_SCRIPT,
GUMBO_TAG_NOBR,
- GUMBO_TAG_METER,
- GUMBO_TAG_LAST,
- GUMBO_TAG_FOREIGNOBJECT,
- GUMBO_TAG_FIELDSET,
GUMBO_TAG_LAST,
GUMBO_TAG_SPAN,
+ GUMBO_TAG_P,
+ GUMBO_TAG_EMBED,
GUMBO_TAG_NOFRAMES,
- GUMBO_TAG_MAP,
- GUMBO_TAG_MATH,
- GUMBO_TAG_PARAM,
- GUMBO_TAG_LAST,
- GUMBO_TAG_NOEMBED,
- GUMBO_TAG_BR,
- GUMBO_TAG_FIGURE,
GUMBO_TAG_SECTION,
- GUMBO_TAG_LAST,
- GUMBO_TAG_LAST,
+ GUMBO_TAG_NOEMBED,
+ GUMBO_TAG_NEXTID,
GUMBO_TAG_LAST,
GUMBO_TAG_NOSCRIPT,
- GUMBO_TAG_NEXTID,
+ GUMBO_TAG_PICTURE,
+ GUMBO_TAG_MARQUEE,
+ GUMBO_TAG_FOOTER,
+ GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
+ GUMBO_TAG_MAP,
+ GUMBO_TAG_FONT,
+ GUMBO_TAG_PARAM,
GUMBO_TAG_HR,
- GUMBO_TAG_MGLYPH,
+ GUMBO_TAG_SCRIPT,
+ GUMBO_TAG_HGROUP,
GUMBO_TAG_LAST,
+ GUMBO_TAG_TR,
GUMBO_TAG_EM,
+ GUMBO_TAG_MENUITEM,
+ GUMBO_TAG_FOREIGNOBJECT,
+ GUMBO_TAG_MGLYPH,
+ GUMBO_TAG_DATA,
+ GUMBO_TAG_APPLET,
+ GUMBO_TAG_FIELDSET,
+ GUMBO_TAG_MAIN,
+ GUMBO_TAG_TEXTAREA,
+ GUMBO_TAG_ABBR,
GUMBO_TAG_LAST,
+ GUMBO_TAG_FIGURE,
+ GUMBO_TAG_DL,
+ GUMBO_TAG_RB,
GUMBO_TAG_FORM,
- GUMBO_TAG_TR,
GUMBO_TAG_LAST,
- GUMBO_TAG_MARQUEE,
+ GUMBO_TAG_BASEFONT,
+ GUMBO_TAG_LAST,
+ GUMBO_TAG_LAST,
+ GUMBO_TAG_BASE,
+ GUMBO_TAG_LAST,
GUMBO_TAG_PROGRESS,
+ GUMBO_TAG_OBJECT,
+ GUMBO_TAG_VAR,
+ GUMBO_TAG_MENU,
+ GUMBO_TAG_META,
+ GUMBO_TAG_MO,
+ GUMBO_TAG_OPTGROUP,
+ GUMBO_TAG_PRE,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
- GUMBO_TAG_MULTICOL,
- GUMBO_TAG_BUTTON,
- GUMBO_TAG_DATA,
+ GUMBO_TAG_TFOOT,
+ GUMBO_TAG_DIV,
+ GUMBO_TAG_PLAINTEXT,
+ GUMBO_TAG_LI,
GUMBO_TAG_LAST,
- GUMBO_TAG_APPLET,
+ GUMBO_TAG_TBODY,
+ GUMBO_TAG_FIGCAPTION,
GUMBO_TAG_LAST,
- GUMBO_TAG_DL,
+ GUMBO_TAG_OPTION,
+ GUMBO_TAG_BGSOUND,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
- GUMBO_TAG_OPTGROUP,
GUMBO_TAG_LAST,
- GUMBO_TAG_ABBR,
- GUMBO_TAG_TEXTAREA,
- GUMBO_TAG_VAR,
- GUMBO_TAG_FIGCAPTION,
+ GUMBO_TAG_LAST,
+ GUMBO_TAG_U,
+ GUMBO_TAG_MS,
+ GUMBO_TAG_A,
+ GUMBO_TAG_DD,
+ GUMBO_TAG_LAST,
+ GUMBO_TAG_TD,
+ GUMBO_TAG_FRAMESET,
+ GUMBO_TAG_MI,
+ GUMBO_TAG_IMAGE,
+ GUMBO_TAG_BR,
+ GUMBO_TAG_FRAME,
+ GUMBO_TAG_DFN,
+ GUMBO_TAG_DIALOG,
+ GUMBO_TAG_NAV,
+ GUMBO_TAG_B,
+ GUMBO_TAG_LAST,
+ GUMBO_TAG_LAST,
+ GUMBO_TAG_UL,
+ GUMBO_TAG_LISTING,
+ GUMBO_TAG_LAST,
+ GUMBO_TAG_LAST,
+ GUMBO_TAG_ARTICLE,
GUMBO_TAG_RT,
- GUMBO_TAG_PRE,
- GUMBO_TAG_HTML,
- GUMBO_TAG_MENUITEM,
- GUMBO_TAG_DIV,
+ GUMBO_TAG_OL,
+ GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
- GUMBO_TAG_LI,
- GUMBO_TAG_PLAINTEXT,
- GUMBO_TAG_MAIN,
GUMBO_TAG_DT,
+ GUMBO_TAG_ACRONYM,
+ GUMBO_TAG_TT,
+ GUMBO_TAG_HTML,
+ GUMBO_TAG_WBR,
+ GUMBO_TAG_SUP,
+ GUMBO_TAG_BODY,
+ GUMBO_TAG_STYLE,
+ GUMBO_TAG_STRIKE,
GUMBO_TAG_LAST,
GUMBO_TAG_BLOCKQUOTE,
+ GUMBO_TAG_TH,
GUMBO_TAG_LAST,
- GUMBO_TAG_WBR,
- GUMBO_TAG_BODY,
- GUMBO_TAG_TT,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
- GUMBO_TAG_STYLE,
- GUMBO_TAG_STRIKE,
GUMBO_TAG_LAST,
- GUMBO_TAG_FRAMESET,
- GUMBO_TAG_OBJECT,
- GUMBO_TAG_MENU,
- GUMBO_TAG_MO,
+ GUMBO_TAG_THEAD,
+ GUMBO_TAG_LAST,
+ GUMBO_TAG_ASIDE,
+ GUMBO_TAG_VIDEO,
+ GUMBO_TAG_I,
+ GUMBO_TAG_KBD,
+ GUMBO_TAG_LAST,
+ GUMBO_TAG_LINK,
+ GUMBO_TAG_MULTICOL,
+ GUMBO_TAG_XMP,
+ GUMBO_TAG_MTEXT,
+ GUMBO_TAG_LAST,
+ GUMBO_TAG_LAST,
+ GUMBO_TAG_CENTER,
+ GUMBO_TAG_ADDRESS,
+ GUMBO_TAG_DESC,
+ GUMBO_TAG_CANVAS,
+ GUMBO_TAG_COL,
+ GUMBO_TAG_AREA,
+ GUMBO_TAG_LAST,
+ GUMBO_TAG_MARK,
+ GUMBO_TAG_MN,
+ GUMBO_TAG_CODE,
+ GUMBO_TAG_IFRAME,
GUMBO_TAG_BIG,
- GUMBO_TAG_META,
- GUMBO_TAG_TFOOT,
- GUMBO_TAG_OUTPUT,
GUMBO_TAG_LAST,
- GUMBO_TAG_FRAME,
+ GUMBO_TAG_MALIGNMARK,
GUMBO_TAG_LAST,
- GUMBO_TAG_U,
- GUMBO_TAG_IMAGE,
+ GUMBO_TAG_KEYGEN,
+ GUMBO_TAG_SUB,
+ GUMBO_TAG_SVG,
+ GUMBO_TAG_CITE,
GUMBO_TAG_LAST,
- GUMBO_TAG_LISTING,
- GUMBO_TAG_DD,
- GUMBO_TAG_DIALOG,
- GUMBO_TAG_A,
- GUMBO_TAG_MS,
- GUMBO_TAG_OPTION,
GUMBO_TAG_LAST,
- GUMBO_TAG_TD,
+ GUMBO_TAG_COLGROUP,
+ GUMBO_TAG_ANNOTATION_XML,
+ GUMBO_TAG_OUTPUT,
GUMBO_TAG_LAST,
+ GUMBO_TAG_INS,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
- GUMBO_TAG_RUBY,
- GUMBO_TAG_MI,
GUMBO_TAG_LAST,
+ GUMBO_TAG_RUBY,
GUMBO_TAG_LAST,
- GUMBO_TAG_MTEXT,
+ GUMBO_TAG_INPUT,
GUMBO_TAG_LAST,
GUMBO_TAG_SUMMARY,
- GUMBO_TAG_UL,
- GUMBO_TAG_NAV,
- GUMBO_TAG_ACRONYM,
- GUMBO_TAG_TBODY,
GUMBO_TAG_LAST,
- GUMBO_TAG_LINK,
GUMBO_TAG_LAST,
- GUMBO_TAG_DFN,
+ GUMBO_TAG_TRACK,
+ GUMBO_TAG_CAPTION,
+ GUMBO_TAG_IMG,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
- GUMBO_TAG_OL,
- GUMBO_TAG_COL,
GUMBO_TAG_LAST,
- GUMBO_TAG_TH,
GUMBO_TAG_LAST,
- GUMBO_TAG_ARTICLE,
- GUMBO_TAG_THEAD,
- GUMBO_TAG_CENTER,
- GUMBO_TAG_Q,
- GUMBO_TAG_COLGROUP,
- GUMBO_TAG_CANVAS,
GUMBO_TAG_LAST,
- GUMBO_TAG_ANNOTATION_XML,
GUMBO_TAG_LAST,
- GUMBO_TAG_DESC,
- GUMBO_TAG_VIDEO,
- GUMBO_TAG_KBD,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
+ GUMBO_TAG_Q,
GUMBO_TAG_LAST,
- GUMBO_TAG_AUDIO,
+ GUMBO_TAG_BUTTON,
GUMBO_TAG_LAST,
- GUMBO_TAG_CODE,
- GUMBO_TAG_MN,
- GUMBO_TAG_INS,
- GUMBO_TAG_I,
- GUMBO_TAG_ASIDE,
GUMBO_TAG_LAST,
- GUMBO_TAG_CITE,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
- GUMBO_TAG_ADDRESS,
GUMBO_TAG_LAST,
GUMBO_TAG_BDO,
- GUMBO_TAG_MARK,
- GUMBO_TAG_INPUT,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
- GUMBO_TAG_MALIGNMARK,
+ GUMBO_TAG_STRONG,
+ GUMBO_TAG_LAST,
+ GUMBO_TAG_LAST,
+ GUMBO_TAG_LAST,
+ GUMBO_TAG_AUDIO,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
- GUMBO_TAG_KEYGEN,
- GUMBO_TAG_AREA,
GUMBO_TAG_LAST,
- GUMBO_TAG_STRONG,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
GUMBO_TAG_BDI,
@@ -290,22 +295,23 @@ static const unsigned char kGumboTagMap[] = {
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
- GUMBO_TAG_IFRAME,
- GUMBO_TAG_ISINDEX,
GUMBO_TAG_LAST,
- GUMBO_TAG_IMG,
- GUMBO_TAG_CAPTION,
- GUMBO_TAG_BLINK,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
- GUMBO_TAG_TRACK,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
- GUMBO_TAG_SVG,
+ GUMBO_TAG_LAST,
+ GUMBO_TAG_LAST,
+ GUMBO_TAG_LAST,
+ GUMBO_TAG_LAST,
+ GUMBO_TAG_BLINK,
+ GUMBO_TAG_LAST,
+ GUMBO_TAG_LAST,
+ GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
@@ -317,5 +323,9 @@ static const unsigned char kGumboTagMap[] = {
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
GUMBO_TAG_LAST,
+ GUMBO_TAG_LAST,
+ GUMBO_TAG_ISINDEX,
+ GUMBO_TAG_LAST,
+ GUMBO_TAG_LAST,
GUMBO_TAG_RTC
};
diff --git a/libs/litehtml/src/gumbo/include/gumbo/tag_sizes.h b/libs/litehtml/src/gumbo/include/gumbo/tag_sizes.h
index 5b93c22fa2..9dc9529995 100644
--- a/libs/litehtml/src/gumbo/include/gumbo/tag_sizes.h
+++ b/libs/litehtml/src/gumbo/include/gumbo/tag_sizes.h
@@ -1,4 +1,4 @@
// Generated via `gentags.py src/tag.in`.
// Do not edit; edit src/tag.in instead.
// clang-format off
-4, 4, 5, 4, 4, 4, 5, 6, 8, 8, 4, 7, 7, 3, 5, 2, 2, 2, 2, 2, 2, 6, 6, 6, 7, 1, 2, 3, 10, 2, 2, 2, 2, 2, 2, 6, 10, 4, 3, 1, 2, 6, 5, 1, 4, 1, 3, 4, 4, 4, 4, 3, 4, 3, 3, 3, 1, 1, 1, 4, 4, 2, 2, 3, 3, 4, 2, 3, 3, 3, 5, 3, 7, 6, 5, 6, 5, 5, 5, 6, 5, 6, 3, 4, 4, 2, 2, 2, 2, 5, 6, 10, 14, 3, 13, 4, 5, 7, 8, 3, 5, 5, 5, 2, 2, 2, 4, 8, 6, 5, 5, 6, 6, 8, 8, 6, 8, 6, 6, 8, 5, 7, 7, 4, 8, 6, 7, 7, 3, 5, 8, 8, 7, 7, 3, 6, 7, 9, 2, 6, 8, 3, 5, 6, 4, 7, 8, 4, 6, 6, 2, 3, \ No newline at end of file
+4, 4, 5, 4, 4, 4, 5, 6, 8, 8, 4, 7, 7, 3, 5, 2, 2, 2, 2, 2, 2, 6, 6, 6, 7, 1, 2, 3, 10, 2, 2, 2, 2, 2, 2, 6, 10, 4, 3, 1, 2, 6, 5, 1, 4, 1, 3, 4, 4, 4, 4, 3, 4, 3, 3, 3, 1, 1, 1, 4, 4, 2, 2, 3, 3, 4, 2, 3, 3, 3, 5, 3, 7, 6, 5, 6, 5, 5, 5, 6, 5, 6, 3, 4, 4, 2, 2, 2, 2, 5, 6, 10, 14, 3, 13, 4, 5, 7, 8, 3, 5, 5, 5, 2, 2, 2, 4, 8, 6, 5, 5, 6, 6, 8, 8, 6, 8, 6, 6, 8, 5, 7, 7, 4, 8, 6, 7, 7, 3, 5, 8, 8, 7, 7, 3, 6, 7, 9, 2, 6, 8, 3, 5, 6, 4, 7, 8, 4, 6, 6, 6, 2, 3, \ No newline at end of file
diff --git a/libs/litehtml/src/gumbo/include/gumbo/tag_strings.h b/libs/litehtml/src/gumbo/include/gumbo/tag_strings.h
index 03d793c05d..188bcda00b 100644
--- a/libs/litehtml/src/gumbo/include/gumbo/tag_strings.h
+++ b/libs/litehtml/src/gumbo/include/gumbo/tag_strings.h
@@ -151,5 +151,6 @@
"nobr",
"spacer",
"dialog",
+"search",
"tt",
"rtc",
diff --git a/libs/litehtml/src/gumbo/parser.c b/libs/litehtml/src/gumbo/parser.c
index 968fcc0f41..0ab3f92084 100644
--- a/libs/litehtml/src/gumbo/parser.c
+++ b/libs/litehtml/src/gumbo/parser.c
@@ -291,17 +291,16 @@ typedef struct _NamespacedAttributeReplacement {
static const NamespacedAttributeReplacement kForeignAttributeReplacements[] = {
{"xlink:actuate", "actuate", GUMBO_ATTR_NAMESPACE_XLINK},
- {"xlink:actuate", "actuate", GUMBO_ATTR_NAMESPACE_XLINK},
- {"xlink:href", "href", GUMBO_ATTR_NAMESPACE_XLINK},
- {"xlink:role", "role", GUMBO_ATTR_NAMESPACE_XLINK},
- {"xlink:show", "show", GUMBO_ATTR_NAMESPACE_XLINK},
- {"xlink:title", "title", GUMBO_ATTR_NAMESPACE_XLINK},
- {"xlink:type", "type", GUMBO_ATTR_NAMESPACE_XLINK},
- {"xml:base", "base", GUMBO_ATTR_NAMESPACE_XML},
- {"xml:lang", "lang", GUMBO_ATTR_NAMESPACE_XML},
- {"xml:space", "space", GUMBO_ATTR_NAMESPACE_XML},
- {"xmlns", "xmlns", GUMBO_ATTR_NAMESPACE_XMLNS},
- {"xmlns:xlink", "xlink", GUMBO_ATTR_NAMESPACE_XMLNS},
+ {"xlink:arcrole", "arcrole", GUMBO_ATTR_NAMESPACE_XLINK},
+ {"xlink:href", "href", GUMBO_ATTR_NAMESPACE_XLINK},
+ {"xlink:role", "role", GUMBO_ATTR_NAMESPACE_XLINK},
+ {"xlink:show", "show", GUMBO_ATTR_NAMESPACE_XLINK},
+ {"xlink:title", "title", GUMBO_ATTR_NAMESPACE_XLINK},
+ {"xlink:type", "type", GUMBO_ATTR_NAMESPACE_XLINK},
+ {"xml:lang", "lang", GUMBO_ATTR_NAMESPACE_XML},
+ {"xml:space", "space", GUMBO_ATTR_NAMESPACE_XML},
+ {"xmlns", "xmlns", GUMBO_ATTR_NAMESPACE_XMLNS},
+ {"xmlns:xlink", "xlink", GUMBO_ATTR_NAMESPACE_XMLNS},
};
// The "scope marker" for the list of active formatting elements. We use a
@@ -1564,12 +1563,12 @@ static bool is_special_node(const GumboNode* node) {
(gumbo_tagset){TAG(ADDRESS), TAG(APPLET), TAG(AREA), TAG(ARTICLE),
TAG(ASIDE), TAG(BASE), TAG(BASEFONT), TAG(BGSOUND), TAG(BLOCKQUOTE),
TAG(BODY), TAG(BR), TAG(BUTTON), TAG(CAPTION), TAG(CENTER), TAG(COL),
- TAG(COLGROUP), TAG(MENUITEM), TAG(DD), TAG(DETAILS), TAG(DIR),
+ TAG(COLGROUP), TAG(DD), TAG(DETAILS), TAG(DIR),
TAG(DIV), TAG(DL), TAG(DT), TAG(EMBED), TAG(FIELDSET),
TAG(FIGCAPTION), TAG(FIGURE), TAG(FOOTER), TAG(FORM), TAG(FRAME),
TAG(FRAMESET), TAG(H1), TAG(H2), TAG(H3), TAG(H4), TAG(H5), TAG(H6),
TAG(HEAD), TAG(HEADER), TAG(HGROUP), TAG(HR), TAG(HTML), TAG(IFRAME),
- TAG(IMG), TAG(INPUT), TAG(ISINDEX), TAG(LI), TAG(LINK), TAG(LISTING),
+ TAG(IMG), TAG(INPUT), TAG(LI), TAG(LINK), TAG(LISTING),
TAG(MARQUEE), TAG(MENU), TAG(META), TAG(NAV), TAG(NOEMBED),
TAG(NOFRAMES), TAG(NOSCRIPT), TAG(OBJECT), TAG(OL), TAG(P),
TAG(PARAM), TAG(PLAINTEXT), TAG(PRE), TAG(SCRIPT), TAG(SECTION),
@@ -2179,7 +2178,7 @@ static bool handle_in_head(GumboParser* parser, GumboToken* token) {
return handle_in_body(parser, token);
} else if (tag_in(token, kStartTag,
(gumbo_tagset){TAG(BASE), TAG(BASEFONT), TAG(BGSOUND),
- TAG(MENUITEM), TAG(LINK)})) {
+ TAG(LINK)})) {
insert_element_from_token(parser, token);
pop_current_node(parser);
acknowledge_self_closing_tag(parser);
@@ -2419,7 +2418,7 @@ static bool handle_in_body(GumboParser* parser, GumboToken* token) {
return false;
} else if (tag_in(token, kStartTag,
(gumbo_tagset){TAG(BASE), TAG(BASEFONT), TAG(BGSOUND),
- TAG(MENUITEM), TAG(LINK), TAG(META), TAG(NOFRAMES),
+ TAG(LINK), TAG(META), TAG(NOFRAMES),
TAG(SCRIPT), TAG(STYLE), TAG(TEMPLATE), TAG(TITLE)}) ||
tag_is(token, kEndTag, GUMBO_TAG_TEMPLATE)) {
return handle_in_head(parser, token);
@@ -2514,13 +2513,13 @@ static bool handle_in_body(GumboParser* parser, GumboToken* token) {
record_end_of_element(state->_current_token, &body->v.element);
}
return success;
- } else if (tag_in(token, kStartTag,
- (gumbo_tagset){TAG(ADDRESS), TAG(ARTICLE), TAG(ASIDE),
- TAG(BLOCKQUOTE), TAG(CENTER), TAG(DETAILS), TAG(DIALOG),
- TAG(DIR), TAG(DIV), TAG(DL), TAG(FIELDSET), TAG(FIGCAPTION),
- TAG(FIGURE), TAG(FOOTER), TAG(HEADER), TAG(HGROUP),
- TAG(MENU), TAG(MAIN), TAG(NAV), TAG(OL), TAG(P),
- TAG(SECTION), TAG(SUMMARY), TAG(UL)})) {
+ } else if (tag_in(token, kStartTag, (gumbo_tagset){
+ TAG(ADDRESS), TAG(ARTICLE), TAG(ASIDE), TAG(BLOCKQUOTE), TAG(CENTER),
+ TAG(DETAILS), TAG(DIALOG), TAG(DIR), TAG(DIV), TAG(DL), TAG(FIELDSET),
+ TAG(FIGCAPTION), TAG(FIGURE), TAG(FOOTER), TAG(HEADER), TAG(HGROUP),
+ TAG(MENU), TAG(MAIN), TAG(NAV), TAG(OL), TAG(P), TAG(SECTION),
+ TAG(SUMMARY), TAG(UL), TAG(SEARCH)}))
+ {
bool result = maybe_implicitly_close_p_tag(parser, token);
insert_element_from_token(parser, token);
return result;
@@ -2583,13 +2582,13 @@ static bool handle_in_body(GumboParser* parser, GumboToken* token) {
insert_element_from_token(parser, token);
state->_frameset_ok = false;
return true;
- } else if (tag_in(token, kEndTag,
- (gumbo_tagset){TAG(ADDRESS), TAG(ARTICLE), TAG(ASIDE),
- TAG(BLOCKQUOTE), TAG(BUTTON), TAG(CENTER), TAG(DETAILS),
- TAG(DIALOG), TAG(DIR), TAG(DIV), TAG(DL), TAG(FIELDSET),
- TAG(FIGCAPTION), TAG(FIGURE), TAG(FOOTER), TAG(HEADER),
- TAG(HGROUP), TAG(LISTING), TAG(MAIN), TAG(MENU), TAG(NAV),
- TAG(OL), TAG(PRE), TAG(SECTION), TAG(SUMMARY), TAG(UL)})) {
+ } else if (tag_in(token, kEndTag, (gumbo_tagset){
+ TAG(ADDRESS), TAG(ARTICLE), TAG(ASIDE), TAG(BLOCKQUOTE), TAG(BUTTON),
+ TAG(CENTER), TAG(DETAILS), TAG(DIALOG), TAG(DIR), TAG(DIV), TAG(DL),
+ TAG(FIELDSET), TAG(FIGCAPTION), TAG(FIGURE), TAG(FOOTER), TAG(HEADER),
+ TAG(HGROUP), TAG(LISTING), TAG(MAIN), TAG(MENU), TAG(NAV), TAG(OL),
+ TAG(PRE), TAG(SECTION), TAG(SUMMARY), TAG(UL), TAG(SEARCH)}))
+ {
GumboTag tag = token->v.end_tag;
if (!has_an_element_in_scope(parser, tag)) {
parser_add_parse_error(parser, token);
@@ -2820,100 +2819,13 @@ static bool handle_in_body(GumboParser* parser, GumboToken* token) {
set_frameset_not_ok(parser);
return result;
} else if (tag_is(token, kStartTag, GUMBO_TAG_ISINDEX)) {
- parser_add_parse_error(parser, token);
- if (parser->_parser_state->_form_element != NULL &&
- !has_open_element(parser, GUMBO_TAG_TEMPLATE)) {
- ignore_token(parser);
- return false;
- }
- acknowledge_self_closing_tag(parser);
- maybe_implicitly_close_p_tag(parser, token);
- set_frameset_not_ok(parser);
-
- GumboVector* token_attrs = &token->v.start_tag.attributes;
- GumboAttribute* prompt_attr = gumbo_get_attribute(token_attrs, "prompt");
- GumboAttribute* action_attr = gumbo_get_attribute(token_attrs, "action");
- GumboAttribute* name_attr = gumbo_get_attribute(token_attrs, "name");
-
- GumboNode* form = insert_element_of_tag_type(
- parser, GUMBO_TAG_FORM, GUMBO_INSERTION_FROM_ISINDEX);
- if (!has_open_element(parser, GUMBO_TAG_TEMPLATE)) {
- parser->_parser_state->_form_element = form;
- }
- if (action_attr) {
- gumbo_vector_add(parser, action_attr, &form->v.element.attributes);
- }
- insert_element_of_tag_type(
- parser, GUMBO_TAG_HR, GUMBO_INSERTION_FROM_ISINDEX);
- pop_current_node(parser); // <hr>
-
- insert_element_of_tag_type(
- parser, GUMBO_TAG_LABEL, GUMBO_INSERTION_FROM_ISINDEX);
- TextNodeBufferState* text_state = &parser->_parser_state->_text_node;
- text_state->_start_original_text = token->original_text.data;
- text_state->_start_position = token->position;
- text_state->_type = GUMBO_NODE_TEXT;
- if (prompt_attr) {
- int prompt_attr_length = strlen(prompt_attr->value);
- gumbo_string_buffer_destroy(parser, &text_state->_buffer);
- text_state->_buffer.data = gumbo_copy_stringz(parser, prompt_attr->value);
- text_state->_buffer.length = prompt_attr_length;
- text_state->_buffer.capacity = prompt_attr_length + 1;
- gumbo_destroy_attribute(parser, prompt_attr);
- } else {
- GumboStringPiece prompt_text =
- GUMBO_STRING("This is a searchable index. Enter search keywords: ");
- gumbo_string_buffer_append_string(
- parser, &prompt_text, &text_state->_buffer);
- }
-
- GumboNode* input = insert_element_of_tag_type(
- parser, GUMBO_TAG_INPUT, GUMBO_INSERTION_FROM_ISINDEX);
- for (unsigned int i = 0; i < token_attrs->length; ++i) {
- GumboAttribute* attr = token_attrs->data[i];
- if (attr != prompt_attr && attr != action_attr && attr != name_attr) {
- gumbo_vector_add(parser, attr, &input->v.element.attributes);
- }
- token_attrs->data[i] = NULL;
- }
-
- // All attributes have been successfully transferred and nulled out at this
- // point, so the call to ignore_token will free the memory for it without
- // touching the attributes.
- ignore_token(parser);
-
- // The name attribute, if present, should be destroyed since it's ignored
- // when copying over. The action attribute should be kept since it's moved
- // to the form.
- if (name_attr) {
- gumbo_destroy_attribute(parser, name_attr);
- }
-
- GumboAttribute* name =
- gumbo_parser_allocate(parser, sizeof(GumboAttribute));
- GumboStringPiece name_str = GUMBO_STRING("name");
- GumboStringPiece isindex_str = GUMBO_STRING("isindex");
- name->attr_namespace = GUMBO_ATTR_NAMESPACE_NONE;
- name->name = gumbo_copy_stringz(parser, "name");
- name->value = gumbo_copy_stringz(parser, "isindex");
- name->original_name = name_str;
- name->original_value = isindex_str;
- name->name_start = kGumboEmptySourcePosition;
- name->name_end = kGumboEmptySourcePosition;
- name->value_start = kGumboEmptySourcePosition;
- name->value_end = kGumboEmptySourcePosition;
- gumbo_vector_add(parser, name, &input->v.element.attributes);
-
- pop_current_node(parser); // <input>
- pop_current_node(parser); // <label>
- insert_element_of_tag_type(
- parser, GUMBO_TAG_HR, GUMBO_INSERTION_FROM_ISINDEX);
- pop_current_node(parser); // <hr>
- pop_current_node(parser); // <form>
- if (!has_open_element(parser, GUMBO_TAG_TEMPLATE)) {
- parser->_parser_state->_form_element = NULL;
+ reconstruct_active_formatting_elements(parser);
+ insert_element_from_token(parser, token);
+ if (token->v.start_tag.is_self_closing) {
+ pop_current_node(parser);
+ acknowledge_self_closing_tag(parser);
}
- return false;
+ return true;
} else if (tag_is(token, kStartTag, GUMBO_TAG_TEXTAREA)) {
run_generic_parsing_algorithm(parser, token, GUMBO_LEX_RCDATA);
parser->_parser_state->_ignore_next_linefeed = true;
@@ -3491,6 +3403,17 @@ static bool handle_in_select(GumboParser* parser, GumboToken* token) {
}
insert_element_from_token(parser, token);
return true;
+ } else if (tag_is(token, kStartTag, GUMBO_TAG_HR)) {
+ if (node_html_tag_is(get_current_node(parser), GUMBO_TAG_OPTION)) {
+ pop_current_node(parser);
+ }
+ if (node_html_tag_is(get_current_node(parser), GUMBO_TAG_OPTGROUP)) {
+ pop_current_node(parser);
+ }
+ insert_element_from_token(parser, token);
+ pop_current_node(parser);
+ acknowledge_self_closing_tag(parser);
+ return true;
} else if (tag_is(token, kEndTag, GUMBO_TAG_OPTGROUP)) {
GumboVector* open_elements = &parser->_parser_state->_open_elements;
if (node_html_tag_is(get_current_node(parser), GUMBO_TAG_OPTION) &&
@@ -3854,40 +3777,33 @@ static bool handle_in_foreign_content(GumboParser* parser, GumboToken* token) {
// Fall through to the if-statements below.
break;
}
- // Order matters for these clauses.
- if (tag_in(token, kStartTag,
- (gumbo_tagset){TAG(B), TAG(BIG), TAG(BLOCKQUOTE), TAG(BODY), TAG(BR),
- TAG(CENTER), TAG(CODE), TAG(DD), TAG(DIV), TAG(DL), TAG(DT),
- TAG(EM), TAG(EMBED), TAG(H1), TAG(H2), TAG(H3), TAG(H4), TAG(H5),
- TAG(H6), TAG(HEAD), TAG(HR), TAG(I), TAG(IMG), TAG(LI),
- TAG(LISTING), TAG(MENU), TAG(META), TAG(NOBR), TAG(OL), TAG(P),
- TAG(PRE), TAG(RUBY), TAG(S), TAG(SMALL), TAG(SPAN), TAG(STRONG),
- TAG(STRIKE), TAG(SUB), TAG(SUP), TAG(TABLE), TAG(TT), TAG(U),
- TAG(UL), TAG(VAR)}) ||
- (tag_is(token, kStartTag, GUMBO_TAG_FONT) &&
- (token_has_attribute(token, "color") ||
- token_has_attribute(token, "face") ||
- token_has_attribute(token, "size")))) {
+
+ if (tag_in(token, kStartTag, (gumbo_tagset){
+ TAG(B), TAG(BIG), TAG(BLOCKQUOTE), TAG(BODY), TAG(BR), TAG(CENTER),
+ TAG(CODE), TAG(DD), TAG(DIV), TAG(DL), TAG(DT), TAG(EM), TAG(EMBED),
+ TAG(H1), TAG(H2), TAG(H3), TAG(H4), TAG(H5), TAG(H6), TAG(HEAD),
+ TAG(HR), TAG(I), TAG(IMG), TAG(LI), TAG(LISTING), TAG(MENU), TAG(META),
+ TAG(NOBR), TAG(OL), TAG(P), TAG(PRE), TAG(RUBY), TAG(S), TAG(SMALL),
+ TAG(SPAN), TAG(STRONG), TAG(STRIKE), TAG(SUB), TAG(SUP), TAG(TABLE),
+ TAG(TT), TAG(U), TAG(UL), TAG(VAR)})
+ || tag_in(token, kEndTag, (gumbo_tagset){TAG(BR), TAG(P)})
+ || (tag_is(token, kStartTag, GUMBO_TAG_FONT)
+ && (token_has_attribute(token, "color")
+ || token_has_attribute(token, "face")
+ || token_has_attribute(token, "size"))))
+ {
/* Parse error */
parser_add_parse_error(parser, token);
- /*
- * Fragment case: If the parser was originally created for the HTML
- * fragment parsing algorithm, then act as described in the "any other
- * start tag" entry below.
- */
- if (!is_fragment_parser(parser)) {
- do {
- pop_current_node(parser);
- } while (!(is_mathml_integration_point(get_current_node(parser)) ||
- is_html_integration_point(get_current_node(parser)) ||
- get_current_node(parser)->v.element.tag_namespace ==
- GUMBO_NAMESPACE_HTML));
- parser->_parser_state->_reprocess_current_token = true;
- return false;
+ while (!is_mathml_integration_point(get_current_node(parser))
+ && !is_html_integration_point(get_current_node(parser))
+ && get_current_node(parser)->v.element.tag_namespace != GUMBO_NAMESPACE_HTML)
+ {
+ pop_current_node(parser);
}
- assert(token->type == GUMBO_TOKEN_START_TAG);
+ handle_html_content(parser, token);
+ return false;
}
if (token->type == GUMBO_TOKEN_START_TAG) {
diff --git a/libs/litehtml/src/html_microsyntaxes.cpp b/libs/litehtml/src/html_microsyntaxes.cpp
index f16bd622e0..d8ce01471f 100644
--- a/libs/litehtml/src/html_microsyntaxes.cpp
+++ b/libs/litehtml/src/html_microsyntaxes.cpp
@@ -1,3 +1,4 @@
+#include "html_microsyntaxes.h"
#include "html.h"
namespace litehtml
diff --git a/libs/litehtml/src/html_tag.cpp b/libs/litehtml/src/html_tag.cpp
index 54a8dd404a..08f289df9d 100644
--- a/libs/litehtml/src/html_tag.cpp
+++ b/libs/litehtml/src/html_tag.cpp
@@ -3,6 +3,7 @@
#include "html.h"
#include "html_tag.h"
#include "document.h"
+#include "html_microsyntaxes.h"
#include "iterators.h"
#include "stylesheet.h"
#include "table.h"
@@ -10,6 +11,7 @@
#include "line_box.h"
#include "render_item.h"
#include "internal.h"
+#include "document_container.h"
namespace litehtml
{
@@ -126,7 +128,7 @@ litehtml::elements_list litehtml::html_tag::select_all(const string& selector )
{
css_selector sel;
sel.parse(selector, get_document()->mode());
-
+
return select_all(sel);
}
@@ -143,7 +145,7 @@ void litehtml::html_tag::select_all(const css_selector& selector, elements_list&
{
res.push_back(shared_from_this());
}
-
+
for(auto& el : m_children)
{
el->select_all(selector, res);
@@ -295,7 +297,7 @@ void litehtml::html_tag::draw(uint_ptr hdc, int x, int y, const position *clip,
draw_background(hdc, x, y, clip, ri);
- if(m_css.get_display() == display_list_item &&
+ if(m_css.get_display() == display_list_item &&
(m_css.get_list_style_type() != list_style_type_none || m_css.get_list_style_image() != ""))
{
if(m_css.get_overflow() > overflow_visible)
@@ -816,7 +818,7 @@ bool litehtml::html_tag::on_lbutton_down()
return ret;
}
-bool litehtml::html_tag::on_lbutton_up()
+bool litehtml::html_tag::on_lbutton_up(bool is_click)
{
bool ret = false;
@@ -830,7 +832,7 @@ bool litehtml::html_tag::on_lbutton_up()
el = el->parent();
}
- on_click();
+ if (is_click) on_click();
return ret;
}
@@ -839,10 +841,13 @@ void litehtml::html_tag::on_click()
{
if (!is_root())
{
- element::ptr el_parent = parent();
- if (el_parent)
+ if(!get_document()->container()->on_element_click(shared_from_this()))
{
- el_parent->on_click();
+ element::ptr el_parent = parent();
+ if (el_parent)
+ {
+ el_parent->on_click();
+ }
}
}
}
@@ -935,7 +940,7 @@ void litehtml::html_tag::draw_background(uint_ptr hdc, int x, int y, const posit
bdr.radius.top_right_y = m_css.get_borders().radius.top_right_y;
}
-
+
bdr.top = m_css.get_borders().top;
bdr.bottom = m_css.get_borders().bottom;
if(box == boxes.begin())
@@ -954,7 +959,7 @@ void litehtml::html_tag::draw_background(uint_ptr hdc, int x, int y, const posit
{
background_layer layer;
if(!bg->get_layer(i, content_box, this, ri, layer)) continue;
- layer.border_radius = bdr.radius.calc_percents(layer.border_box.width, layer.border_box.width);
+ layer.border_radius = bdr.radius.calc_percents(box->width, box->height);
bg->draw_layer(hdc, i, layer, get_document()->container());
}
}
@@ -1057,7 +1062,7 @@ void litehtml::html_tag::draw_list_marker( uint_ptr hdc, const position& pos )
lm.baseurl = nullptr;
}
- int ln_height = css().get_line_height();
+ int ln_height = css().line_height().computed_value;
int sz_font = css().get_font_size();
lm.pos.x = pos.x;
lm.pos.width = sz_font - sz_font * 2 / 3;
@@ -1192,7 +1197,7 @@ bool html_tag::is_nth_child(const element::ptr& el, int num, int off, bool of_ty
{
if(child->css().get_display() != display_inline_text)
{
- if( (!of_type && selector_list.empty()) ||
+ if( (!of_type && selector_list.empty()) ||
(of_type && child->tag() == el->tag()) || child->select(selector_list) )
{
if(el == child)
@@ -1502,7 +1507,7 @@ const litehtml::background* litehtml::html_tag::get_background(bool own_only)
}
return nullptr;
}
-
+
if(is_body())
{
element::ptr el_parent = parent();
@@ -1578,7 +1583,7 @@ void html_tag::map_to_dimension_property_ignoring_zero(string_id prop_name, stri
tok = {DIMENSION, x, css_number_number, "px"};
else
tok = {PERCENTAGE, x, css_number_number};
-
+
m_style.add_property(prop_name, {tok});
}
diff --git a/libs/litehtml/src/iterators.cpp b/libs/litehtml/src/iterators.cpp
index f0a87555c2..3d48e463dd 100644
--- a/libs/litehtml/src/iterators.cpp
+++ b/libs/litehtml/src/iterators.cpp
@@ -1,8 +1,5 @@
-#include "html.h"
#include "iterators.h"
-#include "html_tag.h"
#include "render_item.h"
-#include <iterator>
litehtml::elements_iterator::elements_iterator(bool return_parents, iterator_selector* go_inside, iterator_selector* select) :
m_go_inside(go_inside),
diff --git a/libs/litehtml/src/line_box.cpp b/libs/litehtml/src/line_box.cpp
index fed0aa404d..20aed39fb3 100644
--- a/libs/litehtml/src/line_box.cpp
+++ b/libs/litehtml/src/line_box.cpp
@@ -45,6 +45,11 @@ int litehtml::line_box_item::left() const
return m_element->left();
}
+int litehtml::line_box_item::height() const
+{
+ return m_element->height();
+}
+
//////////////////////////////////////////////////////////////////////////////////////////
litehtml::lbi_start::lbi_start(const std::shared_ptr<render_item>& element) : line_box_item(element)
@@ -86,6 +91,11 @@ int litehtml::lbi_start::left() const
return m_pos.x - m_element->content_offset_left();
}
+int litehtml::lbi_start::height() const
+{
+ return m_pos.height;
+}
+
//////////////////////////////////////////////////////////////////////////////////////////
litehtml::lbi_end::lbi_end(const std::shared_ptr<render_item>& element) : lbi_start(element)
@@ -180,9 +190,9 @@ int litehtml::line_box::calc_va_baseline(const va_context& current, vertical_ali
switch(va)
{
case va_super:
- return current.baseline - current.fm.height / 3;
+ return current.baseline - current.fm.super_shift;
case va_sub:
- return current.baseline + current.fm.height / 3;
+ return current.baseline + current.fm.sub_shift;
case va_middle:
return current.baseline - current.fm.x_height / 2;
case va_text_top:
@@ -190,10 +200,10 @@ int litehtml::line_box::calc_va_baseline(const va_context& current, vertical_ali
new_font.height - new_font.base_line();
case va_text_bottom:
return current.baseline + current.fm.base_line() - new_font.base_line();
+ case va_bottom:
+ return bottom - new_font.base_line();
case va_top:
return top + new_font.height - new_font.base_line();
- case va_bottom:
- return bottom - new_font.height + new_font.base_line();
default:
return current.baseline;
}
@@ -275,130 +285,236 @@ std::list< std::unique_ptr<litehtml::line_box_item> > litehtml::line_box::finish
if( is_empty() || (!is_empty() && last_box && is_break_only()) )
{
- m_height = m_default_line_height;
+ m_height = m_default_line_height.computed_value;
m_baseline = m_font_metrics.base_line();
return ret_items;
}
- int spc_x = 0;
+ int spacing_x = 0; // Number of pixels to distribute between elements
+ int shift_x = 0; // Shift elements by X to apply the text-align
- int add_x = 0;
switch(m_text_align)
{
case text_align_right:
if(m_width < (m_right - m_left))
{
- add_x = (m_right - m_left) - m_width;
+ shift_x = (m_right - m_left) - m_width;
}
break;
case text_align_center:
if(m_width < (m_right - m_left))
{
- add_x = ((m_right - m_left) - m_width) / 2;
+ shift_x = ((m_right - m_left) - m_width) / 2;
}
break;
case text_align_justify:
if (m_width < (m_right - m_left))
{
- add_x = 0;
- spc_x = (m_right - m_left) - m_width;
- if (spc_x > m_width/4)
- spc_x = 0;
+ shift_x = 0;
+ spacing_x = (m_right - m_left) - m_width;
+ // don't justify for small lines
+ if (spacing_x > m_width / 4)
+ spacing_x = 0;
}
break;
default:
- add_x = 0;
+ shift_x = 0;
}
int counter = 0;
- float offj = float(spc_x) / std::max(1.f, float(m_items.size())-1.f);
+ float offj = float(spacing_x) / std::max(1.f, float(m_items.size()) - 1.f);
float cixx = 0.0f;
- int line_top = 0;
- int line_bottom = 0;
+ std::optional<int> line_height;
+
+ if(!m_default_line_height.css_value.is_predefined())
+ {
+ line_height = m_default_line_height.computed_value;
+ }
va_context current_context;
std::list<va_context> contexts;
current_context.baseline = 0;
current_context.fm = m_font_metrics;
+ current_context.start_lbi = nullptr;
+ current_context.line_height = m_default_line_height.computed_value;
m_min_width = 0;
+ struct items_dimensions
+ {
+ int top = 0;
+ int bottom = 0;
+ int count = 0;
+ int max_height = 0;
+
+ void add_item(const line_box_item* item)
+ {
+ top = std::min(top, item->top());
+ bottom = std::max(bottom, item->bottom());
+ max_height = std::max(max_height, item->height());
+ count++;
+ }
+ int height() const { return bottom - top; }
+ };
+
+ items_dimensions line_max_height;
+ items_dimensions top_aligned_max_height;
+ items_dimensions bottom_aligned_max_height;
+ items_dimensions inline_boxes_dims;
+
+ // First pass
+ // 1. Apply text-align-justify
+ // 1. Align all items by baseline
+ // 2. top/button aligned items are aligned by baseline
+ // 3. Calculate top and button of the linebox separately for items in baseline
+ // and for top and bottom aligned items
for (const auto& lbi : m_items)
- {
+ {
+ // Apply text-align-justify
m_min_width += lbi->get_rendered_min_width();
- { // start text_align_justify
- if (spc_x && counter)
+ if (spacing_x && counter)
+ {
+ cixx += offj;
+ if ((counter + 1) == int(m_items.size()))
+ cixx += 0.99f;
+ lbi->pos().x += int(cixx);
+ }
+ counter++;
+ if ((m_text_align == text_align_right || spacing_x) && counter == int(m_items.size()))
+ {
+ // Forcible justify the last element to the right side for text align right and justify;
+ lbi->pos().x = m_right - lbi->pos().width;
+ } else if (shift_x)
+ {
+ lbi->pos().x += shift_x;
+ }
+
+ // Calculate new baseline for inline start/continue
+ // Inline start/continue elements are inline containers like <span>
+ if (lbi->get_type() == line_box_item::type_inline_start || lbi->get_type() == line_box_item::type_inline_continue)
+ {
+ contexts.push_back(current_context);
+ if(is_one_of(lbi->get_el()->css().get_vertical_align(), va_top, va_bottom))
{
- cixx += offj;
- if ((counter + 1) == int(m_items.size()))
- cixx += 0.99f;
- lbi->pos().x += int(cixx);
- }
- counter++;
- if ((m_text_align == text_align_right || spc_x) && counter == int(m_items.size()))
+ // top/bottom aligned inline boxes are aligned by baseline == 0
+ current_context.baseline = 0;
+ current_context.start_lbi = lbi.get();
+ current_context.start_lbi->reset_items_height();
+ } else if(current_context.start_lbi)
{
- // Forcible justify the last element to the right side for text align right and justify;
- lbi->pos().x = m_right - lbi->pos().width;
- } else if (add_x)
+ current_context.baseline = calc_va_baseline(current_context,
+ lbi->get_el()->css().get_vertical_align(),
+ lbi->get_el()->css().get_font_metrics(),
+ current_context.start_lbi->top(), current_context.start_lbi->bottom());
+ } else
{
- lbi->pos().x += add_x;
+ current_context.start_lbi = nullptr;
+ current_context.baseline = calc_va_baseline(current_context,
+ lbi->get_el()->css().get_vertical_align(),
+ lbi->get_el()->css().get_font_metrics(),
+ line_max_height.top, line_max_height.bottom);
}
- } // end text_align_justify
+ current_context.fm = lbi->get_el()->css().get_font_metrics();
+ current_context.line_height = lbi->get_el()->css().line_height().computed_value;
+ }
- if (lbi->get_type() == line_box_item::type_inline_start || lbi->get_type() == line_box_item::type_inline_continue)
+ int bl = current_context.baseline;
+ int content_offset = 0;
+ bool is_top_bottom_box = false;
+ bool ignore = false;
+
+ // Align element by baseline
+ if(!is_one_of(lbi->get_el()->src_el()->css().get_display(), display_inline_text, display_inline))
{
- contexts.push_back(current_context);
- current_context.baseline = calc_va_baseline(current_context,
- lbi->get_el()->css().get_vertical_align(),
- lbi->get_el()->css().get_font_metrics(),
- line_top, line_bottom);
- current_context.fm = lbi->get_el()->css().get_font_metrics();
+ // Apply margins, paddings and border for inline boxes
+ content_offset = lbi->get_el()->content_offset_top();
+ switch (lbi->get_el()->css().get_vertical_align())
+ {
+ case va_bottom:
+ case va_top:
+ // Align by base line 0 all inline boxes with top and bottom vertical aling
+ bl = 0;
+ is_top_bottom_box = true;
+ break;
+
+ case va_text_bottom:
+ lbi->pos().y = bl + current_context.fm.base_line() - lbi->get_el()->height() + content_offset;
+ ignore = true;
+ break;
+
+ case va_text_top:
+ lbi->pos().y = bl - current_context.fm.ascent + content_offset;
+ ignore = true;
+ break;
+
+ case va_middle:
+ lbi->pos().y = bl - current_context.fm.x_height / 2 - lbi->get_el()->height() / 2 + content_offset;
+ ignore = true;
+ break;
+
+ default:
+ bl = calc_va_baseline(current_context,
+ lbi->get_el()->css().get_vertical_align(),
+ lbi->get_el()->css().get_font_metrics(),
+ line_max_height.top, line_max_height.bottom);
+ break;
+ }
+ }
+ if(!ignore)
+ {
+ lbi->pos().y = bl - lbi->get_el()->get_last_baseline() + content_offset;
}
- // Align elements vertically by baseline.
- if(lbi->get_el()->src_el()->css().get_display() == display_inline_text || lbi->get_el()->src_el()->css().get_display() == display_inline)
- {
- // inline elements and text are aligned by baseline only
- // at this point the baseline for text is properly aligned already
- lbi->pos().y = current_context.baseline - lbi->get_el()->css().get_font_metrics().height + lbi->get_el()->css().get_font_metrics().base_line();
- } else
- {
- switch(lbi->get_el()->css().get_vertical_align())
- {
- case va_sub:
- case va_super:
- {
- int bl = calc_va_baseline(current_context, lbi->get_el()->css().get_vertical_align(), current_context.fm, line_top, line_bottom);
- lbi->pos().y = bl - lbi->get_el()->get_last_baseline() +
- lbi->get_el()->content_offset_top();
- }
+ if(is_top_bottom_box)
+ {
+ switch (lbi->get_el()->css().get_vertical_align())
+ {
+ case va_top:
+ top_aligned_max_height.add_item(lbi.get());
break;
case va_bottom:
- lbi->pos().y = line_bottom - lbi->get_el()->height() + lbi->get_el()->content_offset_top();
+ bottom_aligned_max_height.add_item(lbi.get());
break;
+ default:
+ break;
+ }
+ } else if(current_context.start_lbi)
+ {
+ current_context.start_lbi->add_item_height(lbi->top(), lbi->bottom());
+ switch (current_context.start_lbi->get_el()->css().get_vertical_align())
+ {
case va_top:
- lbi->pos().y = line_top + lbi->get_el()->content_offset_top();
+ top_aligned_max_height.add_item(lbi.get());
break;
- case va_baseline:
- lbi->pos().y = current_context.baseline - lbi->get_el()->get_last_baseline() +
- lbi->get_el()->content_offset_top();
- break;
- case va_text_top:
- lbi->pos().y = current_context.baseline - current_context.fm.height + current_context.fm.base_line() +
- lbi->get_el()->content_offset_top();
- break;
- case va_text_bottom:
- lbi->pos().y = current_context.baseline + current_context.fm.base_line() - lbi->get_el()->height() +
- lbi->get_el()->content_offset_top();
+ case va_bottom:
+ bottom_aligned_max_height.add_item(lbi.get());
break;
- case va_middle:
- lbi->pos().y = current_context.baseline - current_context.fm.x_height / 2 - lbi->get_el()->height() / 2 +
- lbi->get_el()->content_offset_top();
- break;
- }
- }
+ default:
+ break;
+ }
+ } else
+ {
+ if(!lbi->get_el()->src_el()->is_inline_box())
+ {
+ line_max_height.add_item(lbi.get());
+ } else
+ {
+ inline_boxes_dims.add_item(lbi.get());
+ }
+ }
+
+ if(!lbi->get_el()->src_el()->is_inline_box() && !lbi->get_el()->css().line_height().css_value.is_predefined())
+ {
+ if(line_height.has_value())
+ {
+ line_height = std::max(line_height.value(), lbi->get_el()->css().line_height().computed_value);
+ } else
+ {
+ line_height = lbi->get_el()->css().line_height().computed_value;
+ }
+ }
if (lbi->get_type() == line_box_item::type_inline_end)
{
@@ -408,25 +524,68 @@ std::list< std::unique_ptr<litehtml::line_box_item> > litehtml::line_box::finish
contexts.pop_back();
}
}
+ }
- // calculate line height
- line_top = std::min(line_top, lbi->top());
- line_bottom = std::max(line_bottom, lbi->bottom());
-
- if(lbi->get_el()->src_el()->css().get_display() == display_inline_text)
+ int top_shift = 0;
+ if(line_height.has_value())
+ {
+ m_height = line_height.value();
+ if(line_max_height.count != 0)
+ {
+ // We have inline items
+ top_shift = std::abs(line_max_height.top);
+ const int top_shift_correction = (line_height.value() - line_max_height.height()) / 2;
+ m_baseline = line_max_height.bottom + top_shift_correction;
+ top_shift += top_shift_correction;
+ if(inline_boxes_dims.count)
+ {
+ const int diff2 = std::abs(inline_boxes_dims.top) - std::abs(top_shift);
+ if(diff2 > 0)
+ {
+ m_height += diff2;
+ top_shift += diff2;
+ m_baseline += diff2;
+ }
+ const int diff1 = inline_boxes_dims.bottom - (line_max_height.bottom + top_shift_correction);
+ if(diff1 > 0)
+ {
+ m_height += diff1;
+ }
+ }
+ } else if(inline_boxes_dims.count != 0)
+ {
+ // We have inline boxes only
+ m_height = inline_boxes_dims.height();
+ top_shift = std::abs(inline_boxes_dims.top);
+ m_baseline = inline_boxes_dims.bottom;
+ } else
{
- m_line_height = std::max(m_line_height, lbi->get_el()->css().get_line_height());
+ // We don't have inline items and inline boxes
+ top_shift = 0;
}
- }
- m_height = line_bottom - line_top;
- int top_shift = line_top;
- if(m_height < m_line_height)
+
+ const int top_down_height = std::max(top_aligned_max_height.max_height, bottom_aligned_max_height.max_height);
+ if(top_down_height > m_height)
+ {
+ if(bottom_aligned_max_height.count)
+ {
+ top_shift += bottom_aligned_max_height.height() - m_height;
+ }
+ m_height = top_down_height;
+ }
+ } else
{
- top_shift -= (m_line_height - m_height) / 2;
- m_height = m_line_height;
+ // Add inline boxes dimentions
+ line_max_height.top = std::min(line_max_height.top, inline_boxes_dims.top);
+ line_max_height.bottom = std::max(line_max_height.bottom, inline_boxes_dims.bottom);
+
+ // Height is maximum from inline elements height and top/bottom aligned elements height
+ m_height = std::max({line_max_height.height(), top_aligned_max_height.height(), bottom_aligned_max_height.height()});
+
+ top_shift = -std::min(line_max_height.top, line_max_height.bottom - bottom_aligned_max_height.height());
+ m_baseline = line_max_height.bottom;
}
- m_baseline = line_bottom;
struct inline_item_box
{
@@ -443,37 +602,30 @@ std::list< std::unique_ptr<litehtml::line_box_item> > litehtml::line_box::finish
current_context.baseline = 0;
current_context.fm = m_font_metrics;
- bool va_top_bottom = false;
+ current_context.start_lbi = nullptr;
+ //int va_top_bottom = 0;
+ // Second pass:
+ // 1. Vertical align top/bottom
+ // 2. Apply relative shift
+ // 3. Calculate inline boxes
for (const auto& lbi : m_items)
{
- // Calculate baseline. Now we calculate baseline for vertical alignment top and bottom
- if (lbi->get_type() == line_box_item::type_inline_start || lbi->get_type() == line_box_item::type_inline_continue)
+ if(is_one_of(lbi->get_type(), line_box_item::type_inline_start, line_box_item::type_inline_continue))
{
contexts.push_back(current_context);
- va_top_bottom = lbi->get_el()->css().get_vertical_align() == va_bottom || lbi->get_el()->css().get_vertical_align() == va_top;
- current_context.baseline = calc_va_baseline(current_context,
- lbi->get_el()->css().get_vertical_align(),
- lbi->get_el()->css().get_font_metrics(),
- top_shift, top_shift + m_height);
current_context.fm = lbi->get_el()->css().get_font_metrics();
- }
- // Align inlines and text by baseline if current vertical alignment is top or bottom
- if(va_top_bottom)
- {
- if (lbi->get_el()->src_el()->css().get_display() == display_inline_text ||
- lbi->get_el()->src_el()->css().get_display() == display_inline)
+ if(lbi->get_el()->css().get_vertical_align() == va_top)
{
- // inline elements and text are aligned by baseline only
- // at this point the baseline for text is properly aligned already
- lbi->pos().y = current_context.baseline - lbi->get_el()->css().get_font_metrics().height +
- lbi->get_el()->css().get_font_metrics().base_line();
+ current_context.baseline = m_top - lbi->get_items_top();
+ current_context.start_lbi = lbi.get();
+ } else if(lbi->get_el()->css().get_vertical_align() == va_bottom)
+ {
+ current_context.baseline = m_top + m_height - lbi->get_items_bottom();
+ current_context.start_lbi = lbi.get();
}
- }
-
- // Pop the prev context
- if (lbi->get_type() == line_box_item::type_inline_end)
+ } else if(lbi->get_type() == line_box_item::type_inline_end)
{
if(!contexts.empty())
{
@@ -482,20 +634,25 @@ std::list< std::unique_ptr<litehtml::line_box_item> > litehtml::line_box::finish
}
}
- // move element to the correct position
- lbi->pos().y += m_top - top_shift;
-
- // Perform vertical align top and bottom for inline boxes
- if(lbi->get_el()->css().get_display() != display_inline_text && lbi->get_el()->css().get_display() != display_inline)
- {
- if(lbi->get_el()->css().get_vertical_align() == va_top)
+ if(current_context.start_lbi)
+ {
+ lbi->pos().y = current_context.baseline - lbi->get_el()->get_last_baseline() +
+ lbi->get_el()->content_offset_top();
+ } else if(is_one_of(lbi->get_el()->css().get_vertical_align(), va_top, va_bottom) && lbi->get_type() == line_box_item::type_text_part)
+ {
+ if(lbi->get_el()->css().get_vertical_align() == va_top)
{
lbi->pos().y = m_top + lbi->get_el()->content_offset_top();
- } else if(lbi->get_el()->css().get_vertical_align() == va_bottom)
+ } else
{
- lbi->pos().y = m_top + m_height - lbi->get_el()->height() + lbi->get_el()->content_offset_top();
+ lbi->pos().y = m_top + m_height - (lbi->bottom() - lbi->top()) + lbi->get_el()->content_offset_bottom();
}
- }
+ } else
+ {
+ // move element to the correct position
+ lbi->pos().y += m_top + top_shift;
+ }
+
lbi->get_el()->apply_relative_shift(containing_block_size);
// Calculate and push inline box into the render item element
@@ -703,11 +860,9 @@ std::list< std::unique_ptr<litehtml::line_box_item> > litehtml::line_box::new_wi
{
remove_begin = i;
break;
- } else
- {
- (*i)->pos().x += add;
- m_width += (*i)->get_el()->width();
}
+ (*i)->pos().x += add;
+ m_width += (*i)->get_el()->width();
}
i++;
}
@@ -722,4 +877,3 @@ std::list< std::unique_ptr<litehtml::line_box_item> > litehtml::line_box::new_wi
}
return ret_items;
}
-
diff --git a/libs/litehtml/src/media_query.cpp b/libs/litehtml/src/media_query.cpp
index 8593f89f11..e2e17eb8a4 100644
--- a/libs/litehtml/src/media_query.cpp
+++ b/libs/litehtml/src/media_query.cpp
@@ -2,6 +2,8 @@
#include "media_query.h"
#include "css_parser.h"
#include <cassert>
+#include "document.h"
+#include "document_container.h"
#define UNE 8800 // u'≠'
#define ULE 10877 // u'⩽'
@@ -53,7 +55,7 @@ bool media_feature::check(const media_features& feat) const
return compare(feat.height >= feat.width ? _portrait_ : _landscape_);
case _aspect_ratio_:
// The standard calls 1/0 "a degenerate ratio" https://drafts.csswg.org/css-values-4/#ratio-value,
- // but it doesn't specify exactly how it behaves in this context: https://drafts.csswg.org/mediaqueries-5/#aspect-ratio.
+ // but it doesn't specify exactly how it behaves in this context: https://drafts.csswg.org/mediaqueries-5/#aspect-ratio.
// Chrome/Firefox work as expected with 1/0, for example when both width and height are nonzero
// (aspect-ratio < 1/0) evaluates to true. But they behave the same for 0/0, which is unexpected
// (0/0 is NaN, so any comparisons should evaluate to false).
@@ -79,12 +81,12 @@ trilean media_condition::check(const media_features& features) const
{
if (op == _not_)
return !m_conditions[0].check(features);
-
+
if (op == _and_)
{
// https://drafts.csswg.org/mediaqueries/#evaluating
- // The result is true if the <media-in-parens> child term and all of the <media-in-parens>
- // children of the <media-and> child terms are true, false if at least one of these
+ // The result is true if the <media-in-parens> child term and all of the <media-in-parens>
+ // children of the <media-and> child terms are true, false if at least one of these
// <media-in-parens> terms are false, and unknown otherwise.
trilean result = True;
for (const auto& condition : m_conditions)
@@ -96,8 +98,8 @@ trilean media_condition::check(const media_features& features) const
}
if (op == _or_)
{
- // The result is false if the <media-in-parens> child term and all of the <media-in-parens>
- // children of the <media-or> child terms are false, true if at least one of these
+ // The result is false if the <media-in-parens> child term and all of the <media-in-parens>
+ // children of the <media-or> child terms are false, true if at least one of these
// <media-in-parens> terms are true, and unknown otherwise.
trilean result = False;
for (const auto& condition : m_conditions)
@@ -129,7 +131,7 @@ trilean media_query::check(const media_features& features) const
result = False;
else if (m_media_type == media_type_all)
result = True;
- else
+ else
result = (trilean)(m_media_type == features.type);
if (result == True)
@@ -237,7 +239,7 @@ bool parse_media_query(const css_token_vector& tokens, media_query& mquery, docu
bool _not = false;
if (ident == "not") index++, _not = true;
else if (ident == "only") index++; // ignored https://drafts.csswg.org/mediaqueries-5/#mq-only
-
+
// <media-type> = <ident> except only, not, and, or, and layer
ident = at(tokens, index).ident();
if (is_one_of(ident, "", "only", "not", "and", "or", "layer"))
@@ -310,7 +312,7 @@ bool parse_media_in_parens(const css_token& token, media_in_parens& media_in_par
media_in_parens = unknown();
return true;
}
-
+
if (token.type != ROUND_BLOCK) return false;
const css_token_vector& tokens = token.value;
@@ -344,7 +346,7 @@ struct mf_info
string_id type = empty_id; // range, discrete
string_id value_type = empty_id; // length, ratio, resolution, integer, keyword
vector<string_id> keywords = {}; // default value is specified here to get rid of gcc warning "missing initializer for member"
-
+
operator bool() { return type != empty_id; }
};
@@ -407,7 +409,7 @@ bool convert_units(mf_info mfi, css_token val[2], document::ptr doc)
case _integer_:
// nothing to convert, just verify
return val[0].type == NUMBER && val[0].n.number_type == css_number_integer && val[1].type == 0;
-
+
case _length_:
{
if (val[1].type != 0) return false;
@@ -419,7 +421,7 @@ bool convert_units(mf_info mfi, css_token val[2], document::ptr doc)
val[0].n.number = length.val();
return true;
}
-
+
case _resolution_: // https://drafts.csswg.org/css-values-4/#resolution
if (val[1].type != 0) return false;
if (val[0].type == DIMENSION)
@@ -444,7 +446,7 @@ bool convert_units(mf_info mfi, css_token val[2], document::ptr doc)
}
// Note: <resolution> doesn't allow unitless zero
return false;
-
+
case _ratio_: // https://drafts.csswg.org/css-values-4/#ratio <ratio> = <number [0,∞]> [ / <number [0,∞]> ]?
if (val[0].type == NUMBER && val[0].n.number >= 0 &&
((val[1].type == NUMBER && val[1].n.number >= 0) || val[1].type == 0))
@@ -463,7 +465,7 @@ bool convert_units(mf_info mfi, css_token val[2], document::ptr doc)
val[0] = css_token(NUMBER, (float)ident);
return true;
}
-
+
default:
return false;
}
@@ -549,7 +551,7 @@ bool parse_media_feature(const css_token& token, media_feature& result, document
int index = 2;
if (!parse_mf_value(tokens, index, val) || index != (int)tokens.size())
return false;
-
+
media_feature mf = {tokens[0].ident()};
if (!mf.verify_and_convert_units(_plain_, val, nullptr, doc)) return false;
result = mf;
@@ -566,9 +568,9 @@ bool parse_mf_value(const css_token_vector& tokens, int& index, css_token val[2]
const css_token& a = at(tokens, index);
const css_token& b = at(tokens, index + 1);
const css_token& c = at(tokens, index + 2);
-
+
if (!is_one_of(a.type, NUMBER, DIMENSION, IDENT)) return false;
-
+
if (a.type == NUMBER && a.n.number >= 0 && b.ch == '/' &&
c.type == NUMBER && c.n.number >= 0)
{
diff --git a/libs/litehtml/src/render_block.cpp b/libs/litehtml/src/render_block.cpp
index 7e2867f35e..6fba641b03 100644
--- a/libs/litehtml/src/render_block.cpp
+++ b/libs/litehtml/src/render_block.cpp
@@ -1,8 +1,9 @@
-#include "html.h"
#include "render_block.h"
#include "render_inline_context.h"
#include "render_block_context.h"
#include "document.h"
+#include "document_container.h"
+#include "html_tag.h"
int litehtml::render_item_block::place_float(const std::shared_ptr<render_item> &el, int top, const containing_block_context &self_size, formatting_context* fmt_ctx)
{
@@ -199,7 +200,7 @@ int litehtml::render_item_block::_render(int x, int y, const containing_block_co
{
if(m_pos.height == 0)
{
- m_pos.height = css().get_line_height();
+ m_pos.height = css().line_height().computed_value;
}
}
diff --git a/libs/litehtml/src/render_block_context.cpp b/libs/litehtml/src/render_block_context.cpp
index 205485d478..d039985003 100644
--- a/libs/litehtml/src/render_block_context.cpp
+++ b/libs/litehtml/src/render_block_context.cpp
@@ -1,4 +1,3 @@
-#include "html.h"
#include "render_block_context.h"
#include "document.h"
diff --git a/libs/litehtml/src/render_flex.cpp b/libs/litehtml/src/render_flex.cpp
index 7b497cf312..ad8ff3a2fc 100644
--- a/libs/litehtml/src/render_flex.cpp
+++ b/libs/litehtml/src/render_flex.cpp
@@ -1,6 +1,6 @@
-#include "html.h"
#include "types.h"
#include "render_flex.h"
+#include "html_tag.h"
int litehtml::render_item_flex::_render_content(int x, int y, bool /*second_pass*/, const containing_block_context &self_size, formatting_context* fmt_ctx)
{
diff --git a/libs/litehtml/src/render_image.cpp b/libs/litehtml/src/render_image.cpp
index 23b059bd90..0f13216079 100644
--- a/libs/litehtml/src/render_image.cpp
+++ b/libs/litehtml/src/render_image.cpp
@@ -1,4 +1,3 @@
-#include "html.h"
#include "render_image.h"
#include "document.h"
@@ -19,7 +18,7 @@ int litehtml::render_item_image::_render(int x, int y, const containing_block_co
m_pos.width = sz.width;
m_pos.height = sz.height;
- src_el()->css_w().set_line_height(height());
+ src_el()->css_w().line_height_w().computed_value = height();
if(src_el()->css().get_height().is_predefined() && src_el()->css().get_width().is_predefined())
{
diff --git a/libs/litehtml/src/render_inline_context.cpp b/libs/litehtml/src/render_inline_context.cpp
index fcf23bf733..4c4d974800 100644
--- a/libs/litehtml/src/render_inline_context.cpp
+++ b/libs/litehtml/src/render_inline_context.cpp
@@ -1,4 +1,3 @@
-#include "html.h"
#include "render_inline_context.h"
#include "document.h"
#include "iterators.h"
@@ -150,7 +149,7 @@ void litehtml::render_item_inline_context::fix_line_width(element_float flt,
{
line_left += src_el()->css().get_text_indent().calc_percent(self_size.width);
}
-
+
}
auto items = m_line_boxes.back()->new_width(line_left, line_right);
@@ -227,7 +226,7 @@ int litehtml::render_item_inline_context::new_box(const std::unique_ptr<line_box
m_line_boxes.emplace_back(std::make_unique<line_box>(
line_ctx.top,
line_ctx.left + first_line_margin + text_indent, line_ctx.right,
- css().get_line_height(),
+ css().line_height(),
css().get_font_metrics(),
css().get_text_align()));
@@ -403,7 +402,7 @@ int litehtml::render_item_inline_context::get_last_baseline()
bl = line->bottom() - line->baseline() + content_offset_top();
} else
{
- bl = height() - margin_bottom();
+ bl = height();
}
return bl;
}
diff --git a/libs/litehtml/src/render_item.cpp b/libs/litehtml/src/render_item.cpp
index 5ec65ab049..3794807a4f 100644
--- a/libs/litehtml/src/render_item.cpp
+++ b/libs/litehtml/src/render_item.cpp
@@ -1,8 +1,8 @@
-#include "html.h"
#include "render_item.h"
#include "document.h"
#include <typeinfo>
-#include <utf8_strings.h>
+#include "document_container.h"
+#include "types.h"
litehtml::render_item::render_item(std::shared_ptr<element> _src_el) :
m_element(std::move(_src_el)),
@@ -46,7 +46,6 @@ int litehtml::render_item::render(int x, int y, const containing_block_context&
if(src_el()->is_block_formatting_context() || ! fmt_ctx)
{
formatting_context fmt;
- fmt.push_position(content_left, content_top);
ret = _render(x, y, containing_block_size, &fmt, second_pass);
fmt.apply_relative_shift(containing_block_size);
} else
@@ -243,8 +242,8 @@ bool litehtml::render_item::fetch_positioned()
void litehtml::render_item::render_positioned(render_type rt)
{
- position wnd_position;
- src_el()->get_document()->container()->get_client_rect(wnd_position);
+ position view_port;
+ src_el()->get_document()->container()->get_viewport(view_port);
element_position el_position;
bool process;
@@ -275,8 +274,8 @@ void litehtml::render_item::render_positioned(render_type rt)
containing_block_context containing_block_size;
if(el_position == element_position_fixed || (is_root() && !src_el()->is_positioned()))
{
- containing_block_size.height = wnd_position.height;
- containing_block_size.width = wnd_position.width;
+ containing_block_size.height = view_port.height;
+ containing_block_size.width = view_port.width;
} else
{
containing_block_size.height = m_pos.height + m_padding.height();
@@ -648,8 +647,8 @@ void litehtml::render_item::render_positioned(render_type rt)
if(el_position == element_position_fixed)
{
- position fixed_pos;
- el->get_redraw_box(fixed_pos);
+ position fixed_pos = el->pos();
+ el->get_redraw_box(fixed_pos);
src_el()->get_document()->add_fixed_box(fixed_pos);
}
}
@@ -832,12 +831,10 @@ void litehtml::render_item::draw_children(uint_ptr hdc, int x, int y, const posi
if (el->src_el()->is_positioned() && el->src_el()->css().get_z_index() == zindex)
{
if (el->src_el()->css().get_position() == element_position_fixed)
- {
- position browser_wnd;
- doc->container()->get_client_rect(browser_wnd);
-
- el->src_el()->draw(hdc, browser_wnd.x, browser_wnd.y, clip, el);
- el->draw_stacking_context(hdc, browser_wnd.x, browser_wnd.y, clip, true);
+ {
+ // Fixed elements position is always relative to the (0,0)
+ el->src_el()->draw(hdc, 0, 0, clip, el);
+ el->draw_stacking_context(hdc, 0, 0, clip, true);
}
else
{
@@ -1129,17 +1126,39 @@ void litehtml::render_item::get_rendering_boxes( position::vector& redraw_boxes)
if(src_el()->css().get_position() != element_position_fixed)
{
- auto cur_el = parent();
+ auto cur_el = parent();
+ int add_x = 0;
+ int add_y = 0;
+
while(cur_el)
- {
- for(auto& box : redraw_boxes)
- {
- box.x += cur_el->m_pos.x;
- box.y += cur_el->m_pos.y;
- }
+ {
+ if(cur_el->css().get_position() == element_position_fixed)
+ {
+ position view_port;
+ src_el()->get_document()->container()->get_viewport(view_port);
+ add_x += cur_el->m_pos.x + view_port.left();
+ add_y += cur_el->m_pos.y + view_port.top();
+ break;
+ }
+ add_x += cur_el->m_pos.x;
+ add_y += cur_el->m_pos.y;
cur_el = cur_el->parent();
}
- }
+ for(auto& box : redraw_boxes)
+ {
+ box.x += add_x;
+ box.y += add_y;
+ }
+ } else
+ {
+ position view_port;
+ src_el()->get_document()->container()->get_viewport(view_port);
+ for(auto& box : redraw_boxes)
+ {
+ box.x += view_port.left();
+ box.y += view_port.top();
+ }
+ }
}
void litehtml::render_item::dump(litehtml::dumper& cout)
diff --git a/libs/litehtml/src/render_table.cpp b/libs/litehtml/src/render_table.cpp
index 16a143eb43..4792f14972 100644
--- a/libs/litehtml/src/render_table.cpp
+++ b/libs/litehtml/src/render_table.cpp
@@ -1,4 +1,3 @@
-#include "html.h"
#include "render_table.h"
#include "document.h"
#include "iterators.h"
diff --git a/libs/litehtml/src/string_id.cpp b/libs/litehtml/src/string_id.cpp
index d197d24469..ac131962b7 100644
--- a/libs/litehtml/src/string_id.cpp
+++ b/libs/litehtml/src/string_id.cpp
@@ -2,10 +2,12 @@
#include "string_id.h"
#include <cassert>
+#include <Windows.h>
+#include <m_system.h>
+
#ifndef LITEHTML_NO_THREADS
- #include <mutex>
- static std::mutex mutex;
- #define lock_guard std::lock_guard<std::mutex> lock(mutex)
+ static mir_cs mutex;
+ #define lock_guard mir_cslock lock(mutex)
#else
#define lock_guard
#endif
diff --git a/libs/litehtml/src/style.cpp b/libs/litehtml/src/style.cpp
index 10768700a6..2707dd8bed 100644
--- a/libs/litehtml/src/style.cpp
+++ b/libs/litehtml/src/style.cpp
@@ -2,6 +2,9 @@
#include "style.h"
#include "css_parser.h"
#include "internal.h"
+#include <set>
+#include "html_tag.h"
+#include "document.h"
// All functions here assume that whitespace have been removed.
@@ -28,6 +31,7 @@ std::map<string_id, string> style::m_valid_values =
{ _float_, element_float_strings },
{ _clear_, element_clear_strings },
{ _overflow_, overflow_strings },
+ { _appearance_, appearance_strings },
{ _box_sizing_, box_sizing_strings },
{ _text_align_, text_align_strings },
@@ -62,6 +66,9 @@ std::map<string_id, string> style::m_valid_values =
{ _align_self_, flex_align_items_strings },
{ _caption_side_, caption_side_strings },
+
+ { _text_decoration_style_, style_text_decoration_style_strings },
+ { _text_emphasis_position_, style_text_emphasis_position_strings },
};
std::map<string_id, vector<string_id>> shorthands =
@@ -101,6 +108,9 @@ std::map<string_id, vector<string_id>> shorthands =
{ _flex_, {_flex_grow_, _flex_shrink_, _flex_basis_}},
{ _flex_flow_, {_flex_direction_, _flex_wrap_}},
+
+ { _text_decoration_, {_text_decoration_color_, _text_decoration_line_, _text_decoration_style_, _text_decoration_thickness_}},
+ { _text_emphasis_, {_text_emphasis_style_, _text_emphasis_color_}},
};
void style::add(const string& txt, const string& baseurl, document_container* container)
@@ -167,10 +177,10 @@ void style::add_property(string_id name, const css_token_vector& value, const st
// Note: empty value is a valid value for a custom property.
if (value.empty() && _s(name).substr(0, 2) != "--")
return;
-
+
if (has_var(value))
return add_parsed_property(name, property_value(value, important, true));
-
+
// valid only if value contains a single token
css_token val = value.size() == 1 ? value[0] : css_token();
// nonempty if value is a single identifier
@@ -193,6 +203,7 @@ void style::add_property(string_id name, const css_token_vector& value, const st
case _position_:
case _float_:
case _clear_:
+ case _appearance_:
case _box_sizing_:
case _overflow_:
@@ -203,6 +214,7 @@ void style::add_property(string_id name, const css_token_vector& value, const st
case _font_style_:
case _font_variant_:
+ case _text_decoration_style_:
case _list_style_type_:
case _list_style_position_:
@@ -233,7 +245,7 @@ void style::add_property(string_id name, const css_token_vector& value, const st
// <length-percentage> https://developer.mozilla.org/en-US/docs/Web/CSS/text-indent#formal_syntax
case _text_indent_:
return add_length_property(name, val, "", f_length_percentage, important);
-
+
// <length-percentage [0,∞]> https://developer.mozilla.org/en-US/docs/Web/CSS/padding-left
case _padding_left_:
case _padding_right_:
@@ -251,7 +263,7 @@ void style::add_property(string_id name, const css_token_vector& value, const st
case _margin_top_:
case _margin_bottom_:
return add_length_property(name, val, "auto", f_length_percentage, important);
-
+
// auto | min-content | max-content | fit-content | <length-percentage [0,∞]> https://developer.mozilla.org/en-US/docs/Web/CSS/width#formal_syntax
case _width_:
case _height_:
@@ -281,7 +293,7 @@ void style::add_property(string_id name, const css_token_vector& value, const st
if (int n = parse_1234_lengths(value, len, f_length_percentage | f_positive))
add_four_properties(_padding_top_, len, n, important);
break;
-
+
// ============================= COLOR =============================
case _color_:
@@ -370,8 +382,8 @@ void style::add_property(string_id name, const css_token_vector& value, const st
case _border_radius_x_:
case _border_radius_y_:
{
- string_id top_left = name == _border_radius_x_ ?
- _border_top_left_radius_x_ :
+ string_id top_left = name == _border_radius_x_ ?
+ _border_top_left_radius_x_ :
_border_top_left_radius_y_;
if (int n = parse_1234_lengths(value, len, f_length_percentage | f_positive))
@@ -424,10 +436,38 @@ void style::add_property(string_id name, const css_token_vector& value, const st
break;
case _text_decoration_:
+ parse_text_decoration(value, important, container);
+ break;
+
+ case _text_decoration_thickness_:
+ add_length_property(name, val, style_text_decoration_thickness_strings, f_length_percentage|f_positive, important);
+ break;
+
+ case _text_decoration_color_:
+ parse_text_decoration_color(val, important, container);
+ break;
+
+ case _text_decoration_line_:
+ parse_text_decoration_line(value, important);
+ break;
+
+ case _text_emphasis_:
+ parse_text_emphasis(value, important, container);
+ break;
+
+ case _text_emphasis_style_:
str = get_repr(value, 0, -1, true);
add_parsed_property(name, property_value(str, important));
break;
+ case _text_emphasis_color_:
+ parse_text_emphasis_color(val, important, container);
+ break;
+
+ case _text_emphasis_position_:
+ parse_text_emphasis_position(value, important);
+ break;
+
// ============================= FLEX =============================
case _flex_:
@@ -484,10 +524,10 @@ void style::add_property(string_id name, const css_token_vector& value, const st
break;
// ============================= CUSTOM PROPERTY =============================
-
+
// https://drafts.csswg.org/css-variables-2/#defining-variables
default:
- if (_s(name).substr(0, 2) == "--" && _s(name).size() >= 3 &&
+ if (_s(name).substr(0, 2) == "--" && _s(name).size() >= 3 &&
(value.empty() || is_declaration_value(value)))
add_parsed_property(name, property_value(value, important));
}
@@ -527,7 +567,7 @@ void style::parse_list_style(const css_token_vector& tokens, string baseurl, boo
for (const auto& token : tokens)
{
- // "...none is a valid value for both list-style-image and list-style-type. To resolve this ambiguity,
+ // "...none is a valid value for both list-style-image and list-style-type. To resolve this ambiguity,
// a value of none ... must be applied to whichever of the two properties aren’t otherwise set by the shorthand."
if (token.ident() == "none") {
none_count++;
@@ -566,12 +606,12 @@ void style::parse_list_style(const css_token_vector& tokens, string baseurl, boo
}
// https://developer.mozilla.org/en-US/docs/Web/CSS/border-radius
-// border-radius = <length-percentage [0,∞]>{1,4} [ / <length-percentage [0,∞]>{1,4} ]?
+// border-radius = <length-percentage [0,∞]>{1,4} [ / <length-percentage [0,∞]>{1,4} ]?
void style::parse_border_radius(const css_token_vector& tokens, bool important)
{
int i;
for (i = 0; i < (int)tokens.size() && tokens[i].ch != '/'; i++) {}
-
+
if (i == (int)tokens.size()) // no '/'
{
css_length len[4];
@@ -585,11 +625,11 @@ void style::parse_border_radius(const css_token_vector& tokens, bool important)
{
auto raduis_x = slice(tokens, 0, i);
auto raduis_y = slice(tokens, i + 1);
-
+
css_length rx[4], ry[4];
int n = parse_1234_lengths(raduis_x, rx, f_length_percentage | f_positive);
int m = parse_1234_lengths(raduis_y, ry, f_length_percentage | f_positive);
-
+
if (n && m)
{
add_four_properties(_border_top_left_radius_x_, rx, n, important);
@@ -603,10 +643,10 @@ bool parse_border_width(const css_token& token, css_length& w)
css_length width;
if (!width.from_token(token, f_length | f_positive, border_width_strings))
return false;
-
+
if (width.is_predefined())
width.set_value(border_width_values[width.predef()], css_units_px);
-
+
w = width;
return true;
}
@@ -650,7 +690,7 @@ void style::parse_border(const css_token_vector& tokens, bool important, documen
css_length width;
border_style style;
web_color color;
-
+
if (!parse_border_helper(tokens, container, width, style, color))
return;
@@ -724,7 +764,7 @@ int parse_1234_lengths(const css_token_vector& tokens, css_length len[4], int op
// This function implements the logic of the kind "if two values are specified, the first one applies to
// top and bottom, the second one to left and right". Works in conjunction with parse_1234_values.
-template<class T>
+template<class T>
void style::add_four_properties(string_id top_name, T val[4], int n, bool important)
{
// These always go in trbl order, see comment for "CSS property names" in string_id.
@@ -751,7 +791,7 @@ void style::parse_background(const css_token_vector& tokens, const string& baseu
if (layers.empty()) return;
web_color color;
- std::vector<image> images;
+ std::vector<image> images;
length_vector x_positions, y_positions;
size_vector sizes;
int_vector repeats, attachments, origins, clips;
@@ -761,7 +801,7 @@ void style::parse_background(const css_token_vector& tokens, const string& baseu
background bg;
if (!parse_bg_layer(layers[i], container, bg, i == layers.size() - 1))
return;
-
+
color = bg.m_color;
images.push_back(bg.m_image[0]);
x_positions.push_back(bg.m_position_x[0]);
@@ -821,7 +861,7 @@ bool style::parse_bg_layer(const css_token_vector& tokens, document_container* c
repeat_found = true;
else if (!attachment_found && parse_keyword(tokens[i], bg.m_attachment[0], background_attachment_strings))
attachment_found = true;
- // If one <visual-box> value is present then it sets both background-origin and background-clip to that value.
+ // If one <visual-box> value is present then it sets both background-origin and background-clip to that value.
// If two values are present, then the first sets background-origin and the second background-clip.
else if (!origin_found && parse_keyword(tokens[i], bg.m_origin[0], background_box_strings))
origin_found = true, bg.m_clip[0] = bg.m_origin[0];
@@ -841,7 +881,7 @@ bool parse_bg_position_size(const css_token_vector& tokens, int& index, css_leng
if (at(tokens, index).ch != '/')
return true; // no [ / <bg-size> ]
-
+
if (!parse_bg_size(tokens, ++index, size))
{
index--; // restore index to point to '/'
@@ -859,7 +899,7 @@ bool parse_bg_size(const css_token_vector& tokens, int& index, css_size& size)
if (!a.from_token(at(tokens, index), f_length_percentage | f_positive, background_size_strings))
return false;
-
+
// cover | contain
if (a.is_predefined() && a.predef() != background_size_auto)
{
@@ -900,7 +940,7 @@ bool parse_bg_position(const css_token_vector& tokens, int& index, css_length& x
bottom = background_position_bottom,
center = background_position_center
};
-
+
css_length a, b;
if (!a.from_token(at(tokens, index), f_length_percentage, background_position_strings))
@@ -924,7 +964,7 @@ bool parse_bg_position(const css_token_vector& tokens, int& index, css_length& x
if ((is_one_of_predef(a, top, bottom) && b.is_predefined()) ||
(a.is_predefined() && is_one_of_predef(b, left, right)))
swap(a, b);
-
+
// check for wrong order
if (is_one_of_predef(a, top, bottom) || is_one_of_predef(b, left, right))
return false;
@@ -939,7 +979,7 @@ bool parse_bg_position(const css_token_vector& tokens, int& index, css_length& x
if (b.is_predefined())
b.set_value(background_position_percentages[b.predef()], css_units_percentage);
}
-
+
x = a;
y = b;
return true;
@@ -949,7 +989,7 @@ void style::parse_background_image(const css_token_vector& tokens, const string&
{
auto layers = parse_comma_separated_list(tokens);
if (layers.empty()) return;
-
+
std::vector<image> images;
for (const auto& layer : layers)
@@ -1009,7 +1049,7 @@ bool parse_url(const css_token& tok, string& url)
url = trim(tok.value[0].str);
return true;
}
-
+
return false;
}
@@ -1231,6 +1271,121 @@ void style::parse_font(css_token_vector tokens, bool important)
add_parsed_property(_font_family_, property_value(font_family, important));
}
+void style::parse_text_decoration(const css_token_vector& tokens, bool important, document_container* container)
+{
+ css_length len;
+ css_token_vector line_tokens;
+ for(const auto& token : tokens)
+ {
+ if(parse_text_decoration_color(token, important, container)) continue;
+
+ if(parse_length(token, len, f_length_percentage|f_positive, style_text_decoration_thickness_strings))
+ {
+ add_parsed_property(_text_decoration_thickness_, property_value(len, important));
+ } else
+ {
+ if(token.type == IDENT)
+ {
+ int style = value_index(token.ident(), style_text_decoration_style_strings);
+ if(style >= 0)
+ {
+ add_parsed_property(_text_decoration_style_, property_value(style, important));
+ } else
+ {
+ line_tokens.push_back(token);
+ }
+ } else
+ {
+ line_tokens.push_back(token);
+ }
+ }
+ }
+ if(!line_tokens.empty())
+ {
+ parse_text_decoration_line(line_tokens, important);
+ }
+}
+
+bool style::parse_text_decoration_color(const css_token& token, bool important, document_container* container)
+{
+ web_color _color;
+ if(parse_color(token, _color, container))
+ {
+ add_parsed_property(_text_decoration_color_, property_value(_color, important));
+ return true;
+ }
+ if(token.type == IDENT && value_in_list(token.ident(), "auto;currentcolor"))
+ {
+ add_parsed_property(_text_decoration_color_, property_value(web_color::current_color, important));
+ return true;
+ }
+ return false;
+}
+
+void style::parse_text_decoration_line(const css_token_vector& tokens, bool important)
+{
+ int val = 0;
+ for(const auto& token : tokens)
+ {
+ if(token.type == IDENT)
+ {
+ int idx = value_index(token.ident(), style_text_decoration_line_strings);
+ if(idx >= 0)
+ {
+ val |= 1 << (idx - 1);
+ }
+ }
+ }
+ add_parsed_property(_text_decoration_line_, property_value(val, important));
+}
+
+void style::parse_text_emphasis(const css_token_vector& tokens, bool important, document_container *container) {
+ string style;
+ for(const auto& token : std::vector(tokens.rbegin(), tokens.rend()))
+ {
+ if(parse_text_emphasis_color(token, important, container)) continue;
+ style.insert(0, token.str + " ");
+ }
+ style = trim(style);
+ if (!style.empty())
+ {
+ add_parsed_property(_text_emphasis_style_, property_value(style, important));
+ }
+}
+
+bool style::parse_text_emphasis_color(const css_token &token, bool important, document_container *container)
+{
+ web_color _color;
+ if(parse_color(token, _color, container))
+ {
+ add_parsed_property(_text_emphasis_color_, property_value(_color, important));
+ return true;
+ }
+ if(token.type == IDENT && value_in_list(token.ident(), "auto;currentcolor"))
+ {
+ add_parsed_property(_text_emphasis_color_, property_value(web_color::current_color, important));
+ return true;
+ }
+ return false;
+}
+
+void style::parse_text_emphasis_position(const css_token_vector &tokens, bool important)
+{
+ int val = 0;
+ for(const auto& token : tokens)
+ {
+ if(token.type == IDENT)
+ {
+ int idx = value_index(token.ident(), style_text_emphasis_position_strings);
+ if(idx >= 0)
+ {
+ val |= 1 << (idx - 1);
+ }
+ }
+ }
+ add_parsed_property(_text_emphasis_position_, property_value(val, important));
+}
+
// https://developer.mozilla.org/en-US/docs/Web/CSS/flex
// https://drafts.csswg.org/css-flexbox/#flex-property
// flex = none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ]
@@ -1355,9 +1510,9 @@ void style::parse_align_self(string_id name, const css_token_vector& tokens, boo
return;
if (tokens[0].type != IDENT || (n == 2 && tokens[1].type != IDENT))
return;
-
+
string a = tokens[0].ident();
-
+
if (name == _align_items_ && a == "auto")
return;
@@ -1441,11 +1596,11 @@ const property_value& style::get_property(string_id name) const
bool check_var_syntax(const css_token_vector& args)
{
if (args.empty()) return false;
-
+
string name = args[0].ident();
if (name.substr(0, 2) != "--" || name.size() <= 2)
return false;
-
+
if (args.size() > 1 && args[1].ch != ',')
return false;
if (args.size() > 2 && !is_declaration_value(args, 2))
@@ -1467,11 +1622,11 @@ bool subst_var(css_token_vector& tokens, const html_tag* el, std::set<string_id>
{
auto args = tok.value; // copy is intentional
if (!check_var_syntax(args)) return false;
-
+
auto name = _id(args[0].name);
if (name in used_vars) return false; // dependency cycle https://drafts.csswg.org/css-variables/#cycles
used_vars.insert(name);
-
+
css_token_vector value;
if (el->get_custom_property(name, value))
{
diff --git a/libs/litehtml/src/stylesheet.cpp b/libs/litehtml/src/stylesheet.cpp
index bb428b1328..d6ddca5e8f 100644
--- a/libs/litehtml/src/stylesheet.cpp
+++ b/libs/litehtml/src/stylesheet.cpp
@@ -1,6 +1,8 @@
#include "html.h"
#include "stylesheet.h"
#include "css_parser.h"
+#include "document.h"
+#include "document_container.h"
namespace litehtml
{
@@ -12,12 +14,12 @@ void css::parse_css_stylesheet(const Input& input, string baseurl, document::ptr
if (doc && media)
doc->add_media_list(media);
- // To parse a CSS stylesheet, first parse a stylesheet.
+ // To parse a CSS stylesheet, first parse a stylesheet.
auto rules = css_parser::parse_stylesheet(input, top_level);
bool import_allowed = top_level;
// Interpret all of the resulting top-level qualified rules as style rules, defined below.
- // If any style rule is invalid, or any at-rule is not recognized or is invalid according
+ // If any style rule is invalid, or any at-rule is not recognized or is invalid according
// to its grammar or context, it's a parse error. Discard that rule.
for (auto rule : rules)
{
@@ -40,7 +42,7 @@ void css::parse_css_stylesheet(const Input& input, string baseurl, document::ptr
else
css_parse_error("incorrect placement of @import rule");
break;
-
+
// https://www.w3.org/TR/css-conditional-3/#at-media
// @media <media-query-list> { <stylesheet> }
case _media_:
@@ -58,7 +60,7 @@ void css::parse_css_stylesheet(const Input& input, string baseurl, document::ptr
import_allowed = false;
break;
}
-
+
default:
css_parse_error("unrecognized rule @" + rule->name);
}
@@ -115,7 +117,7 @@ bool css::parse_style_rule(raw_rule::ptr rule, string baseurl, document::ptr doc
}
style::ptr style = make_shared<litehtml::style>(); // style block
- // The content of the qualified rule's block is parsed as a style block's contents.
+ // The content of the qualified rule's block is parsed as a style block's contents.
style->add(rule->block.value, baseurl, doc->container());
for (auto sel : list)
diff --git a/libs/litehtml/src/url.cpp b/libs/litehtml/src/url.cpp
index caa8fbb9d3..dbae382ace 100644
--- a/libs/litehtml/src/url.cpp
+++ b/libs/litehtml/src/url.cpp
@@ -29,7 +29,6 @@
#include "url.h"
-#include <iostream>
#include <sstream>
#include <algorithm>
diff --git a/libs/litehtml/src/utf8_strings.cpp b/libs/litehtml/src/utf8_strings.cpp
index f720f6d26c..6a578e1b9e 100644
--- a/libs/litehtml/src/utf8_strings.cpp
+++ b/libs/litehtml/src/utf8_strings.cpp
@@ -1,4 +1,3 @@
-#include "html.h"
#include "utf8_strings.h"
namespace litehtml
@@ -13,7 +12,7 @@ char32_t read_utf8_char(const string& str, int& index)
if (!str[index]) return 0;
return str[index++];
};
-
+
byte b1 = getb();
// Determine whether we are dealing
diff --git a/libs/litehtml/src/web_color.cpp b/libs/litehtml/src/web_color.cpp
index 51c6e762f1..c9472432d7 100644
--- a/libs/litehtml/src/web_color.cpp
+++ b/libs/litehtml/src/web_color.cpp
@@ -1,6 +1,8 @@
#include "html.h"
#include "web_color.h"
#include "css_parser.h"
+#include "os_types.h"
+#include "document_container.h"
namespace litehtml
{
@@ -18,7 +20,7 @@ struct def_color
const char* rgb;
};
-def_color g_def_colors[] =
+def_color g_def_colors[] =
{
{"transparent","rgba(0, 0, 0, 0)"},
{"AliceBlue","#F0F8FF"},
@@ -268,7 +270,7 @@ bool parse_rgb_func(const css_token& tok, web_color& color)
int n = (int)list.size();
if (!is_one_of(n, 1, 3, 4))
return false;
-
+
css_length r, g, b, a(1, css_units_none);
// legacy syntax: <percentage>#{3} , <alpha-value>? | <number>#{3} , <alpha-value>?
if (n != 1)
@@ -354,18 +356,18 @@ bool parse_hsl_func(const css_token& tok, web_color& color)
float lit = l.val();
// For historical reasons, if the saturation is less than 0% it is clamped to 0% at parsed-value time, before being converted to an sRGB color.
if (sat < 0) sat = 0;
-
+
// Note: Chrome and Firefox treat invalid hsl values differently.
- //
- // Note: at this point, sat is not clamped at 100, and lit is not clamped at all. The standard
+ //
+ // Note: at this point, sat is not clamped at 100, and lit is not clamped at all. The standard
// mentions only clamping sat at 0. As a result, returning rgb values may not be inside [0,1].
float r, g, b;
hsl_to_rgb(hue, sat, lit, r, g, b);
-
+
r = clamp(r, 0, 1);
g = clamp(g, 0, 1);
b = clamp(b, 0, 1);
-
+
color = web_color(
(byte)round(r * 255),
(byte)round(g * 255),
@@ -411,9 +413,9 @@ bool parse_name_color(const css_token& tok, web_color& color, document_container
// https://drafts.csswg.org/css-color-5/#typedef-color
bool parse_color(const css_token& tok, web_color& color, document_container* container)
{
- return
- parse_hash_color(tok, color) ||
- parse_func_color(tok, color) ||
+ return
+ parse_hash_color(tok, color) ||
+ parse_func_color(tok, color) ||
parse_name_color(tok, color, container);
}
diff --git a/libs/mTextControl/src/stdafx.cxx b/libs/mTextControl/src/stdafx.cxx
index 13f28e1314..f111565f38 100644
--- a/libs/mTextControl/src/stdafx.cxx
+++ b/libs/mTextControl/src/stdafx.cxx
@@ -1,5 +1,5 @@
/*
-Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
+Copyright (C) 2012-25 Miranda NG team (https://miranda-ng.org)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
diff --git a/libs/sqlite3/src/sqlite3.c b/libs/sqlite3/src/sqlite3.c
index cea22e2cdc..9b6ef9131c 100644
--- a/libs/sqlite3/src/sqlite3.c
+++ b/libs/sqlite3/src/sqlite3.c
@@ -1,6 +1,6 @@
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
-** version 3.47.1. By combining all the individual C code files into this
+** version 3.50.0. By combining all the individual C code files into this
** single large file, the entire code can be compiled as a single translation
** unit. This allows many compilers to do optimizations that would not be
** possible if the files were compiled separately. Performance improvements
@@ -18,8 +18,11 @@
** separate file. This file contains only code for the core SQLite library.
**
** The content in this amalgamation comes from Fossil check-in
-** b95d11e958643b969c47a8e5857f3793b9e6.
+** dfc790f998f450d9c35e3ba1c8c89c17466c with changes in files:
+**
+**
*/
+#ifndef SQLITE_AMALGAMATION
#define SQLITE_CORE 1
#define SQLITE_AMALGAMATION 1
#ifndef SQLITE_PRIVATE
@@ -449,7 +452,7 @@ extern "C" {
**
** Since [version 3.6.18] ([dateof:3.6.18]),
** SQLite source code has been stored in the
-** <a href="http://www.fossil-scm.org/">Fossil configuration management
+** <a href="http://fossil-scm.org/">Fossil configuration management
** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to
** a string which identifies a particular check-in of SQLite
** within its configuration management system. ^The SQLITE_SOURCE_ID
@@ -462,9 +465,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.47.1"
-#define SQLITE_VERSION_NUMBER 3047001
-#define SQLITE_SOURCE_ID "2024-11-25 12:07:48 b95d11e958643b969c47a8e5857f3793b9e69700b8f1469371386369a26e577e"
+#define SQLITE_VERSION "3.50.0"
+#define SQLITE_VERSION_NUMBER 3050000
+#define SQLITE_SOURCE_ID "2025-05-29 14:26:00 dfc790f998f450d9c35e3ba1c8c89c17466cb559f87b0239e4aab9d34e28f742"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -1416,6 +1419,11 @@ struct sqlite3_io_methods {
** pointed to by the pArg argument. This capability is used during testing
** and only needs to be supported when SQLITE_TEST is defined.
**
+** <li>[[SQLITE_FCNTL_NULL_IO]]
+** The [SQLITE_FCNTL_NULL_IO] opcode sets the low-level file descriptor
+** or file handle for the [sqlite3_file] object such that it will no longer
+** read or write to the database file.
+**
** <li>[[SQLITE_FCNTL_WAL_BLOCK]]
** The [SQLITE_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might
** be advantageous to block on the next WAL lock if the lock is not immediately
@@ -1474,6 +1482,12 @@ struct sqlite3_io_methods {
** the value that M is to be set to. Before returning, the 32-bit signed
** integer is overwritten with the previous value of M.
**
+** <li>[[SQLITE_FCNTL_BLOCK_ON_CONNECT]]
+** The [SQLITE_FCNTL_BLOCK_ON_CONNECT] opcode is used to configure the
+** VFS to block when taking a SHARED lock to connect to a wal mode database.
+** This is used to implement the functionality associated with
+** SQLITE_SETLK_BLOCK_ON_CONNECT.
+**
** <li>[[SQLITE_FCNTL_DATA_VERSION]]
** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
** a database file. The argument is a pointer to a 32-bit unsigned integer.
@@ -1569,6 +1583,8 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_EXTERNAL_READER 40
#define SQLITE_FCNTL_CKSM_FILE 41
#define SQLITE_FCNTL_RESET_CACHE 42
+#define SQLITE_FCNTL_NULL_IO 43
+#define SQLITE_FCNTL_BLOCK_ON_CONNECT 44
/* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
@@ -2299,13 +2315,16 @@ struct sqlite3_mem_methods {
**
** [[SQLITE_CONFIG_LOOKASIDE]] <dt>SQLITE_CONFIG_LOOKASIDE</dt>
** <dd> ^(The SQLITE_CONFIG_LOOKASIDE option takes two arguments that determine
-** the default size of lookaside memory on each [database connection].
+** the default size of [lookaside memory] on each [database connection].
** The first argument is the
-** size of each lookaside buffer slot and the second is the number of
-** slots allocated to each database connection.)^ ^(SQLITE_CONFIG_LOOKASIDE
-** sets the <i>default</i> lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE]
-** option to [sqlite3_db_config()] can be used to change the lookaside
-** configuration on individual connections.)^ </dd>
+** size of each lookaside buffer slot ("sz") and the second is the number of
+** slots allocated to each database connection ("cnt").)^
+** ^(SQLITE_CONFIG_LOOKASIDE sets the <i>default</i> lookaside size.
+** The [SQLITE_DBCONFIG_LOOKASIDE] option to [sqlite3_db_config()] can
+** be used to change the lookaside configuration on individual connections.)^
+** The [-DSQLITE_DEFAULT_LOOKASIDE] option can be used to change the
+** default lookaside configuration at compile-time.
+** </dd>
**
** [[SQLITE_CONFIG_PCACHE2]] <dt>SQLITE_CONFIG_PCACHE2</dt>
** <dd> ^(The SQLITE_CONFIG_PCACHE2 option takes a single argument which is
@@ -2521,7 +2540,15 @@ struct sqlite3_mem_methods {
** CAPI3REF: Database Connection Configuration Options
**
** These constants are the available integer configuration options that
-** can be passed as the second argument to the [sqlite3_db_config()] interface.
+** can be passed as the second parameter to the [sqlite3_db_config()] interface.
+**
+** The [sqlite3_db_config()] interface is a var-args functions. It takes a
+** variable number of parameters, though always at least two. The number of
+** parameters passed into sqlite3_db_config() depends on which of these
+** constants is given as the second parameter. This documentation page
+** refers to parameters beyond the second as "arguments". Thus, when this
+** page says "the N-th argument" it means "the N-th parameter past the
+** configuration option" or "the (N+2)-th parameter to sqlite3_db_config()".
**
** New configuration options may be added in future releases of SQLite.
** Existing configuration options might be discontinued. Applications
@@ -2533,31 +2560,57 @@ struct sqlite3_mem_methods {
** <dl>
** [[SQLITE_DBCONFIG_LOOKASIDE]]
** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>
-** <dd> ^This option takes three additional arguments that determine the
-** [lookaside memory allocator] configuration for the [database connection].
-** ^The first argument (the third parameter to [sqlite3_db_config()] is a
+** <dd> The SQLITE_DBCONFIG_LOOKASIDE option is used to adjust the
+** configuration of the [lookaside memory allocator] within a database
+** connection.
+** The arguments to the SQLITE_DBCONFIG_LOOKASIDE option are <i>not</i>
+** in the [DBCONFIG arguments|usual format].
+** The SQLITE_DBCONFIG_LOOKASIDE option takes three arguments, not two,
+** so that a call to [sqlite3_db_config()] that uses SQLITE_DBCONFIG_LOOKASIDE
+** should have a total of five parameters.
+** <ol>
+** <li><p>The first argument ("buf") is a
** pointer to a memory buffer to use for lookaside memory.
-** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb
-** may be NULL in which case SQLite will allocate the
-** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the
-** size of each lookaside buffer slot. ^The third argument is the number of
-** slots. The size of the buffer in the first argument must be greater than
-** or equal to the product of the second and third arguments. The buffer
-** must be aligned to an 8-byte boundary. ^If the second argument to
-** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally
-** rounded down to the next smaller multiple of 8. ^(The lookaside memory
+** The first argument may be NULL in which case SQLite will allocate the
+** lookaside buffer itself using [sqlite3_malloc()].
+** <li><P>The second argument ("sz") is the
+** size of each lookaside buffer slot. Lookaside is disabled if "sz"
+** is less than 8. The "sz" argument should be a multiple of 8 less than
+** 65536. If "sz" does not meet this constraint, it is reduced in size until
+** it does.
+** <li><p>The third argument ("cnt") is the number of slots. Lookaside is disabled
+** if "cnt"is less than 1. The "cnt" value will be reduced, if necessary, so
+** that the product of "sz" and "cnt" does not exceed 2,147,418,112. The "cnt"
+** parameter is usually chosen so that the product of "sz" and "cnt" is less
+** than 1,000,000.
+** </ol>
+** <p>If the "buf" argument is not NULL, then it must
+** point to a memory buffer with a size that is greater than
+** or equal to the product of "sz" and "cnt".
+** The buffer must be aligned to an 8-byte boundary.
+** The lookaside memory
** configuration for a database connection can only be changed when that
** connection is not currently using lookaside memory, or in other words
-** when the "current value" returned by
-** [sqlite3_db_status](D,[SQLITE_DBSTATUS_LOOKASIDE_USED],...) is zero.
+** when the value returned by [SQLITE_DBSTATUS_LOOKASIDE_USED] is zero.
** Any attempt to change the lookaside memory configuration when lookaside
** memory is in use leaves the configuration unchanged and returns
-** [SQLITE_BUSY].)^</dd>
+** [SQLITE_BUSY].
+** If the "buf" argument is NULL and an attempt
+** to allocate memory based on "sz" and "cnt" fails, then
+** lookaside is silently disabled.
+** <p>
+** The [SQLITE_CONFIG_LOOKASIDE] configuration option can be used to set the
+** default lookaside configuration at initialization. The
+** [-DSQLITE_DEFAULT_LOOKASIDE] option can be used to set the default lookaside
+** configuration at compile-time. Typical values for lookaside are 1200 for
+** "sz" and 40 to 100 for "cnt".
+** </dd>
**
** [[SQLITE_DBCONFIG_ENABLE_FKEY]]
** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt>
** <dd> ^This option is used to enable or disable the enforcement of
-** [foreign key constraints]. There should be two additional arguments.
+** [foreign key constraints]. This is the same setting that is
+** enabled or disabled by the [PRAGMA foreign_keys] statement.
** The first argument is an integer which is 0 to disable FK enforcement,
** positive to enable FK enforcement or negative to leave FK enforcement
** unchanged. The second parameter is a pointer to an integer into which
@@ -2579,13 +2632,13 @@ struct sqlite3_mem_methods {
** <p>Originally this option disabled all triggers. ^(However, since
** SQLite version 3.35.0, TEMP triggers are still allowed even if
** this option is off. So, in other words, this option now only disables
-** triggers in the main database schema or in the schemas of ATTACH-ed
+** triggers in the main database schema or in the schemas of [ATTACH]-ed
** databases.)^ </dd>
**
** [[SQLITE_DBCONFIG_ENABLE_VIEW]]
** <dt>SQLITE_DBCONFIG_ENABLE_VIEW</dt>
** <dd> ^This option is used to enable or disable [CREATE VIEW | views].
-** There should be two additional arguments.
+** There must be two additional arguments.
** The first argument is an integer which is 0 to disable views,
** positive to enable views or negative to leave the setting unchanged.
** The second parameter is a pointer to an integer into which
@@ -2604,7 +2657,7 @@ struct sqlite3_mem_methods {
** <dd> ^This option is used to enable or disable the
** [fts3_tokenizer()] function which is part of the
** [FTS3] full-text search engine extension.
-** There should be two additional arguments.
+** There must be two additional arguments.
** The first argument is an integer which is 0 to disable fts3_tokenizer() or
** positive to enable fts3_tokenizer() or negative to leave the setting
** unchanged.
@@ -2619,7 +2672,7 @@ struct sqlite3_mem_methods {
** interface independently of the [load_extension()] SQL function.
** The [sqlite3_enable_load_extension()] API enables or disables both the
** C-API [sqlite3_load_extension()] and the SQL function [load_extension()].
-** There should be two additional arguments.
+** There must be two additional arguments.
** When the first argument to this interface is 1, then only the C-API is
** enabled and the SQL function remains disabled. If the first argument to
** this interface is 0, then both the C-API and the SQL function are disabled.
@@ -2633,23 +2686,30 @@ struct sqlite3_mem_methods {
**
** [[SQLITE_DBCONFIG_MAINDBNAME]] <dt>SQLITE_DBCONFIG_MAINDBNAME</dt>
** <dd> ^This option is used to change the name of the "main" database
-** schema. ^The sole argument is a pointer to a constant UTF8 string
-** which will become the new schema name in place of "main". ^SQLite
-** does not make a copy of the new main schema name string, so the application
-** must ensure that the argument passed into this DBCONFIG option is unchanged
-** until after the database connection closes.
+** schema. This option does not follow the
+** [DBCONFIG arguments|usual SQLITE_DBCONFIG argument format].
+** This option takes exactly one additional argument so that the
+** [sqlite3_db_config()] call has a total of three parameters. The
+** extra argument must be a pointer to a constant UTF8 string which
+** will become the new schema name in place of "main". ^SQLite does
+** not make a copy of the new main schema name string, so the application
+** must ensure that the argument passed into SQLITE_DBCONFIG MAINDBNAME
+** is unchanged until after the database connection closes.
** </dd>
**
** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]]
** <dt>SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE</dt>
-** <dd> Usually, when a database in wal mode is closed or detached from a
-** database handle, SQLite checks if this will mean that there are now no
-** connections at all to the database. If so, it performs a checkpoint
-** operation before closing the connection. This option may be used to
-** override this behavior. The first parameter passed to this operation
-** is an integer - positive to disable checkpoints-on-close, or zero (the
-** default) to enable them, and negative to leave the setting unchanged.
-** The second parameter is a pointer to an integer
+** <dd> Usually, when a database in [WAL mode] is closed or detached from a
+** database handle, SQLite checks if if there are other connections to the
+** same database, and if there are no other database connection (if the
+** connection being closed is the last open connection to the database),
+** then SQLite performs a [checkpoint] before closing the connection and
+** deletes the WAL file. The SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE option can
+** be used to override that behavior. The first argument passed to this
+** operation (the third parameter to [sqlite3_db_config()]) is an integer
+** which is positive to disable checkpoints-on-close, or zero (the default)
+** to enable them, and negative to leave the setting unchanged.
+** The second argument (the fourth parameter) is a pointer to an integer
** into which is written 0 or 1 to indicate whether checkpoints-on-close
** have been disabled - 0 if they are not disabled, 1 if they are.
** </dd>
@@ -2810,7 +2870,7 @@ struct sqlite3_mem_methods {
** statistics. For statistics to be collected, the flag must be set on
** the database handle both when the SQL statement is prepared and when it
** is stepped. The flag is set (collection of statistics is enabled)
-** by default. This option takes two arguments: an integer and a pointer to
+** by default. <p>This option takes two arguments: an integer and a pointer to
** an integer.. The first argument is 1, 0, or -1 to enable, disable, or
** leave unchanged the statement scanstatus option. If the second argument
** is not NULL, then the value of the statement scanstatus setting after
@@ -2824,7 +2884,7 @@ struct sqlite3_mem_methods {
** in which tables and indexes are scanned so that the scans start at the end
** and work toward the beginning rather than starting at the beginning and
** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the
-** same as setting [PRAGMA reverse_unordered_selects]. This option takes
+** same as setting [PRAGMA reverse_unordered_selects]. <p>This option takes
** two arguments which are an integer and a pointer to an integer. The first
** argument is 1, 0, or -1 to enable, disable, or leave unchanged the
** reverse scan order flag, respectively. If the second argument is not NULL,
@@ -2833,7 +2893,76 @@ struct sqlite3_mem_methods {
** first argument.
** </dd>
**
+** [[SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE]]
+** <dt>SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE</dt>
+** <dd>The SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE option enables or disables
+** the ability of the [ATTACH DATABASE] SQL command to create a new database
+** file if the database filed named in the ATTACH command does not already
+** exist. This ability of ATTACH to create a new database is enabled by
+** default. Applications can disable or reenable the ability for ATTACH to
+** create new database files using this DBCONFIG option.<p>
+** This option takes two arguments which are an integer and a pointer
+** to an integer. The first argument is 1, 0, or -1 to enable, disable, or
+** leave unchanged the attach-create flag, respectively. If the second
+** argument is not NULL, then 0 or 1 is written into the integer that the
+** second argument points to depending on if the attach-create flag is set
+** after processing the first argument.
+** </dd>
+**
+** [[SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE]]
+** <dt>SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE</dt>
+** <dd>The SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE option enables or disables the
+** ability of the [ATTACH DATABASE] SQL command to open a database for writing.
+** This capability is enabled by default. Applications can disable or
+** reenable this capability using the current DBCONFIG option. If the
+** the this capability is disabled, the [ATTACH] command will still work,
+** but the database will be opened read-only. If this option is disabled,
+** then the ability to create a new database using [ATTACH] is also disabled,
+** regardless of the value of the [SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE]
+** option.<p>
+** This option takes two arguments which are an integer and a pointer
+** to an integer. The first argument is 1, 0, or -1 to enable, disable, or
+** leave unchanged the ability to ATTACH another database for writing,
+** respectively. If the second argument is not NULL, then 0 or 1 is written
+** into the integer to which the second argument points, depending on whether
+** the ability to ATTACH a read/write database is enabled or disabled
+** after processing the first argument.
+** </dd>
+**
+** [[SQLITE_DBCONFIG_ENABLE_COMMENTS]]
+** <dt>SQLITE_DBCONFIG_ENABLE_COMMENTS</dt>
+** <dd>The SQLITE_DBCONFIG_ENABLE_COMMENTS option enables or disables the
+** ability to include comments in SQL text. Comments are enabled by default.
+** An application can disable or reenable comments in SQL text using this
+** DBCONFIG option.<p>
+** This option takes two arguments which are an integer and a pointer
+** to an integer. The first argument is 1, 0, or -1 to enable, disable, or
+** leave unchanged the ability to use comments in SQL text,
+** respectively. If the second argument is not NULL, then 0 or 1 is written
+** into the integer that the second argument points to depending on if
+** comments are allowed in SQL text after processing the first argument.
+** </dd>
+**
** </dl>
+**
+** [[DBCONFIG arguments]] <h3>Arguments To SQLITE_DBCONFIG Options</h3>
+**
+** <p>Most of the SQLITE_DBCONFIG options take two arguments, so that the
+** overall call to [sqlite3_db_config()] has a total of four parameters.
+** The first argument (the third parameter to sqlite3_db_config()) is a integer.
+** The second argument is a pointer to an integer. If the first argument is 1,
+** then the option becomes enabled. If the first integer argument is 0, then the
+** option is disabled. If the first argument is -1, then the option setting
+** is unchanged. The second argument, the pointer to an integer, may be NULL.
+** If the second argument is not NULL, then a value of 0 or 1 is written into
+** the integer to which the second argument points, depending on whether the
+** setting is disabled or enabled after applying any changes specified by
+** the first argument.
+**
+** <p>While most SQLITE_DBCONFIG options use the argument format
+** described in the previous paragraph, the [SQLITE_DBCONFIG_MAINDBNAME]
+** and [SQLITE_DBCONFIG_LOOKASIDE] options are different. See the
+** documentation of those exceptional options for details.
*/
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
@@ -2855,7 +2984,10 @@ struct sqlite3_mem_methods {
#define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */
#define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */
#define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */
-#define SQLITE_DBCONFIG_MAX 1019 /* Largest DBCONFIG */
+#define SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE 1020 /* int int* */
+#define SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE 1021 /* int int* */
+#define SQLITE_DBCONFIG_ENABLE_COMMENTS 1022 /* int int* */
+#define SQLITE_DBCONFIG_MAX 1022 /* Largest DBCONFIG */
/*
** CAPI3REF: Enable Or Disable Extended Result Codes
@@ -2947,10 +3079,14 @@ SQLITE_API void sqlite3_set_last_insert_rowid(sqlite3*,sqlite3_int64);
** deleted by the most recently completed INSERT, UPDATE or DELETE
** statement on the database connection specified by the only parameter.
** The two functions are identical except for the type of the return value
-** and that if the number of rows modified by the most recent INSERT, UPDATE
+** and that if the number of rows modified by the most recent INSERT, UPDATE,
** or DELETE is greater than the maximum value supported by type "int", then
** the return value of sqlite3_changes() is undefined. ^Executing any other
** type of SQL statement does not modify the value returned by these functions.
+** For the purposes of this interface, a CREATE TABLE AS SELECT statement
+** does not count as an INSERT, UPDATE or DELETE statement and hence the rows
+** added to the new table by the CREATE TABLE AS SELECT statement are not
+** counted.
**
** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are
** considered - auxiliary changes caused by [CREATE TRIGGER | triggers],
@@ -3206,6 +3342,44 @@ SQLITE_API int sqlite3_busy_handler(sqlite3*,int(*)(void*,int),void*);
SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms);
/*
+** CAPI3REF: Set the Setlk Timeout
+** METHOD: sqlite3
+**
+** This routine is only useful in SQLITE_ENABLE_SETLK_TIMEOUT builds. If
+** the VFS supports blocking locks, it sets the timeout in ms used by
+** eligible locks taken on wal mode databases by the specified database
+** handle. In non-SQLITE_ENABLE_SETLK_TIMEOUT builds, or if the VFS does
+** not support blocking locks, this function is a no-op.
+**
+** Passing 0 to this function disables blocking locks altogether. Passing
+** -1 to this function requests that the VFS blocks for a long time -
+** indefinitely if possible. The results of passing any other negative value
+** are undefined.
+**
+** Internally, each SQLite database handle store two timeout values - the
+** busy-timeout (used for rollback mode databases, or if the VFS does not
+** support blocking locks) and the setlk-timeout (used for blocking locks
+** on wal-mode databases). The sqlite3_busy_timeout() method sets both
+** values, this function sets only the setlk-timeout value. Therefore,
+** to configure separate busy-timeout and setlk-timeout values for a single
+** database handle, call sqlite3_busy_timeout() followed by this function.
+**
+** Whenever the number of connections to a wal mode database falls from
+** 1 to 0, the last connection takes an exclusive lock on the database,
+** then checkpoints and deletes the wal file. While it is doing this, any
+** new connection that tries to read from the database fails with an
+** SQLITE_BUSY error. Or, if the SQLITE_SETLK_BLOCK_ON_CONNECT flag is
+** passed to this API, the new connection blocks until the exclusive lock
+** has been released.
+*/
+SQLITE_API int sqlite3_setlk_timeout(sqlite3*, int ms, int flags);
+
+/*
+** CAPI3REF: Flags for sqlite3_setlk_timeout()
+*/
+#define SQLITE_SETLK_BLOCK_ON_CONNECT 0x01
+
+/*
** CAPI3REF: Convenience Routines For Running Queries
** METHOD: sqlite3
**
@@ -4510,11 +4684,22 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler
** to return an error (error code SQLITE_ERROR) if the statement uses
** any virtual tables.
+**
+** [[SQLITE_PREPARE_DONT_LOG]] <dt>SQLITE_PREPARE_DONT_LOG</dt>
+** <dd>The SQLITE_PREPARE_DONT_LOG flag prevents SQL compiler
+** errors from being sent to the error log defined by
+** [SQLITE_CONFIG_LOG]. This can be used, for example, to do test
+** compiles to see if some SQL syntax is well-formed, without generating
+** messages on the global error log when it is not. If the test compile
+** fails, the sqlite3_prepare_v3() call returns the same error indications
+** with or without this flag; it just omits the call to [sqlite3_log()] that
+** logs the error.
** </dl>
*/
#define SQLITE_PREPARE_PERSISTENT 0x01
#define SQLITE_PREPARE_NORMALIZE 0x02
#define SQLITE_PREPARE_NO_VTAB 0x04
+#define SQLITE_PREPARE_DONT_LOG 0x10
/*
** CAPI3REF: Compiling An SQL Statement
@@ -5309,7 +5494,7 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
** other than [SQLITE_ROW] before any subsequent invocation of
** sqlite3_step(). Failure to reset the prepared statement using
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
-** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1],
+** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]),
** sqlite3_step() began
** calling [sqlite3_reset()] automatically in this circumstance rather
** than returning [SQLITE_MISUSE]. This is not considered a compatibility
@@ -7205,6 +7390,8 @@ SQLITE_API int sqlite3_autovacuum_pages(
**
** ^The second argument is a pointer to the function to invoke when a
** row is updated, inserted or deleted in a rowid table.
+** ^The update hook is disabled by invoking sqlite3_update_hook()
+** with a NULL pointer as the second parameter.
** ^The first argument to the callback is a copy of the third argument
** to sqlite3_update_hook().
** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
@@ -11043,8 +11230,9 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c
/*
** CAPI3REF: Serialize a database
**
-** The sqlite3_serialize(D,S,P,F) interface returns a pointer to memory
-** that is a serialization of the S database on [database connection] D.
+** The sqlite3_serialize(D,S,P,F) interface returns a pointer to
+** memory that is a serialization of the S database on
+** [database connection] D. If S is a NULL pointer, the main database is used.
** If P is not a NULL pointer, then the size of the database in bytes
** is written into *P.
**
@@ -11205,7 +11393,7 @@ SQLITE_API int sqlite3_deserialize(
#if 0
} /* End of the 'extern "C"' block */
#endif
-#endif /* SQLITE3_H */
+/* #endif for SQLITE3_H will be added by mksqlite3.tcl */
/******** Begin file sqlite3rtree.h *********/
/*
@@ -11686,9 +11874,10 @@ SQLITE_API void sqlite3session_table_filter(
** is inserted while a session object is enabled, then later deleted while
** the same session object is disabled, no INSERT record will appear in the
** changeset, even though the delete took place while the session was disabled.
-** Or, if one field of a row is updated while a session is disabled, and
-** another field of the same row is updated while the session is enabled, the
-** resulting changeset will contain an UPDATE change that updates both fields.
+** Or, if one field of a row is updated while a session is enabled, and
+** then another field of the same row is updated while the session is disabled,
+** the resulting changeset will contain an UPDATE change that updates both
+** fields.
*/
SQLITE_API int sqlite3session_changeset(
sqlite3_session *pSession, /* Session object */
@@ -11760,8 +11949,9 @@ SQLITE_API sqlite3_int64 sqlite3session_changeset_size(sqlite3_session *pSession
** database zFrom the contents of the two compatible tables would be
** identical.
**
-** It an error if database zFrom does not exist or does not contain the
-** required compatible table.
+** Unless the call to this function is a no-op as described above, it is an
+** error if database zFrom does not exist or does not contain the required
+** compatible table.
**
** If the operation is successful, SQLITE_OK is returned. Otherwise, an SQLite
** error code. In this case, if argument pzErrMsg is not NULL, *pzErrMsg
@@ -11896,7 +12086,7 @@ SQLITE_API int sqlite3changeset_start_v2(
** The following flags may passed via the 4th parameter to
** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]:
**
-** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd>
+** <dt>SQLITE_CHANGESETSTART_INVERT <dd>
** Invert the changeset while iterating through it. This is equivalent to
** inverting a changeset using sqlite3changeset_invert() before applying it.
** It is an error to specify this flag with a patchset.
@@ -12211,19 +12401,6 @@ SQLITE_API int sqlite3changeset_concat(
void **ppOut /* OUT: Buffer containing output changeset */
);
-
-/*
-** CAPI3REF: Upgrade the Schema of a Changeset/Patchset
-*/
-SQLITE_API int sqlite3changeset_upgrade(
- sqlite3 *db,
- const char *zDb,
- int nIn, const void *pIn, /* Input changeset */
- int *pnOut, void **ppOut /* OUT: Inverse of input */
-);
-
-
-
/*
** CAPI3REF: Changegroup Handle
**
@@ -13456,14 +13633,29 @@ struct Fts5PhraseIter {
** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise,
** output variable (*ppToken) is set to point to a buffer containing the
** matching document token, and (*pnToken) to the size of that buffer in
-** bytes. This API is not available if the specified token matches a
-** prefix query term. In that case both output variables are always set
-** to 0.
+** bytes.
**
** The output text is not a copy of the document text that was tokenized.
** It is the output of the tokenizer module. For tokendata=1 tables, this
** includes any embedded 0x00 and trailing data.
**
+** This API may be slow in some cases if the token identified by parameters
+** iIdx and iToken matched a prefix token in the query. In most cases, the
+** first call to this API for each prefix token in the query is forced
+** to scan the portion of the full-text index that matches the prefix
+** token to collect the extra data required by this API. If the prefix
+** token matches a large number of token instances in the document set,
+** this may be a performance problem.
+**
+** If the user knows in advance that a query may use this API for a
+** prefix token, FTS5 may be configured to collect all required data as part
+** of the initial querying of the full-text index, avoiding the second scan
+** entirely. This also causes prefix queries that do not use this API to
+** run more slowly and use more memory. FTS5 may be configured in this way
+** either on a per-table basis using the [FTS5 insttoken | 'insttoken']
+** option, or on a per-query basis using the
+** [fts5_insttoken | fts5_insttoken()] user function.
+**
** This API can be quite slow if used with an FTS5 table created with the
** "detail=none" or "detail=column" option.
**
@@ -13897,6 +14089,7 @@ struct fts5_api {
#endif /* _FTS5_H */
/******** End of fts5.h *********/
+#endif /* SQLITE3_H */
/************** End of sqlite3.h *********************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
@@ -13942,6 +14135,7 @@ struct fts5_api {
#ifndef SQLITE_MAX_LENGTH
# define SQLITE_MAX_LENGTH 1000000000
#endif
+#define SQLITE_MIN_LENGTH 30 /* Minimum value for the length limit */
/*
** This is the maximum number of
@@ -13954,14 +14148,22 @@ struct fts5_api {
** * Terms in the GROUP BY or ORDER BY clauses of a SELECT statement.
** * Terms in the VALUES clause of an INSERT statement
**
-** The hard upper limit here is 32676. Most database people will
+** The hard upper limit here is 32767. Most database people will
** tell you that in a well-normalized database, you usually should
** not have more than a dozen or so columns in any table. And if
** that is the case, there is no point in having more than a few
** dozen values in any of the other situations described above.
+**
+** An index can only have SQLITE_MAX_COLUMN columns from the user
+** point of view, but the underlying b-tree that implements the index
+** might have up to twice as many columns in a WITHOUT ROWID table,
+** since must also store the primary key at the end. Hence the
+** column count for Index is u16 instead of i16.
*/
-#ifndef SQLITE_MAX_COLUMN
+#if !defined(SQLITE_MAX_COLUMN)
# define SQLITE_MAX_COLUMN 2000
+#elif SQLITE_MAX_COLUMN>32767
+# error SQLITE_MAX_COLUMN may not exceed 32767
#endif
/*
@@ -14007,9 +14209,13 @@ struct fts5_api {
/*
** The maximum number of arguments to an SQL function.
+**
+** This value has a hard upper limit of 32767 due to storage
+** constraints (it needs to fit inside a i16). We keep it
+** lower than that to prevent abuse.
*/
#ifndef SQLITE_MAX_FUNCTION_ARG
-# define SQLITE_MAX_FUNCTION_ARG 127
+# define SQLITE_MAX_FUNCTION_ARG 1000
#endif
/*
@@ -14609,6 +14815,7 @@ struct HashElem {
HashElem *next, *prev; /* Next and previous elements in the table */
void *data; /* Data associated with this element */
const char *pKey; /* Key associated with this element */
+ unsigned int h; /* hash for pKey */
};
/*
@@ -14832,7 +15039,8 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
#define TK_ERROR 182
#define TK_QNUMBER 183
#define TK_SPACE 184
-#define TK_ILLEGAL 185
+#define TK_COMMENT 185
+#define TK_ILLEGAL 186
/************** End of parse.h ***********************************************/
/************** Continuing where we left off in sqliteInt.h ******************/
@@ -14968,7 +15176,17 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash*);
** ourselves.
*/
#ifndef offsetof
-#define offsetof(STRUCTURE,FIELD) ((int)((char*)&((STRUCTURE*)0)->FIELD))
+#define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
+#endif
+
+/*
+** Work around C99 "flex-array" syntax for pre-C99 compilers, so as
+** to avoid complaints from -fsanitize=strict-bounds.
+*/
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+# define FLEXARRAY
+#else
+# define FLEXARRAY 1
#endif
/*
@@ -15046,6 +15264,11 @@ typedef INT16_TYPE i16; /* 2-byte signed integer */
typedef UINT8_TYPE u8; /* 1-byte unsigned integer */
typedef INT8_TYPE i8; /* 1-byte signed integer */
+/* A bitfield type for use inside of structures. Always follow with :N where
+** N is the number of bits.
+*/
+typedef unsigned bft; /* Bit Field Type */
+
/*
** SQLITE_MAX_U32 is a u64 constant that is the maximum u64 value
** that can be stored in a u32 without loss of data. The value
@@ -15084,6 +15307,8 @@ typedef u64 tRowcnt;
** 0.5 -> -10 0.1 -> -33 0.0625 -> -40
*/
typedef INT16_TYPE LogEst;
+#define LOGEST_MIN (-32768)
+#define LOGEST_MAX (32767)
/*
** Set the SQLITE_PTRSIZE macro to the number of bytes in a pointer
@@ -15213,6 +15438,14 @@ typedef INT16_TYPE LogEst;
#define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
/*
+** Macro SMXV(n) return the maximum value that can be held in variable n,
+** assuming n is a signed integer type. UMXV(n) is similar for unsigned
+** integer types.
+*/
+#define SMXV(n) ((((i64)1)<<(sizeof(n)-1))-1)
+#define UMXV(n) ((((i64)1)<<(sizeof(n)))-1)
+
+/*
** Round up a number to the next larger multiple of 8. This is used
** to force 8-byte alignment on 64-bit architectures.
**
@@ -15354,7 +15587,7 @@ SQLITE_PRIVATE u32 sqlite3WhereTrace;
** 0xFFFF---- Low-level debug messages
**
** 0x00000001 Code generation
-** 0x00000002 Solver
+** 0x00000002 Solver (Use 0x40000 for less detail)
** 0x00000004 Solver costs
** 0x00000008 WhereLoop inserts
**
@@ -15373,6 +15606,8 @@ SQLITE_PRIVATE u32 sqlite3WhereTrace;
**
** 0x00010000 Show more detail when printing WHERE terms
** 0x00020000 Show WHERE terms returned from whereScanNext()
+** 0x00040000 Solver overview messages
+** 0x00080000 Star-query heuristic
*/
@@ -16011,6 +16246,22 @@ typedef struct PgHdr DbPage;
#define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */
#define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */
+#define isWalMode(x) ((x)==PAGER_JOURNALMODE_WAL)
+
+/*
+** The argument to this macro is a file descriptor (type sqlite3_file*).
+** Return 0 if it is not open, or non-zero (but not 1) if it is.
+**
+** This is so that expressions can be written as:
+**
+** if( isOpen(pPager->jfd) ){ ...
+**
+** instead of
+**
+** if( pPager->jfd->pMethods ){ ...
+*/
+#define isOpen(pFd) ((pFd)->pMethods!=0)
+
/*
** Flags that make up the mask passed to sqlite3PagerGet().
*/
@@ -16649,6 +16900,7 @@ typedef struct SubrtnSig SubrtnSig;
*/
struct SubrtnSig {
int selId; /* SELECT-id for the SELECT statement on the RHS */
+ u8 bComplete; /* True if fully coded and available for reusable */
char *zAff; /* Affinity of the overall IN expression */
int iTable; /* Ephemeral table generated by the subroutine */
int iAddr; /* Subroutine entry address */
@@ -17036,7 +17288,7 @@ typedef struct VdbeOpList VdbeOpList;
** Additional non-public SQLITE_PREPARE_* flags
*/
#define SQLITE_PREPARE_SAVESQL 0x80 /* Preserve SQL text */
-#define SQLITE_PREPARE_MASK 0x0f /* Mask of public flags */
+#define SQLITE_PREPARE_MASK 0x1f /* Mask of public flags */
/*
** Prototypes for the VDBE interface. See comments on the implementation
@@ -17169,8 +17421,8 @@ SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context*);
SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3*);
#endif
-/* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on
-** each VDBE opcode.
+/* Use SQLITE_ENABLE_EXPLAIN_COMMENTS to enable generation of extra
+** comments on each VDBE opcode.
**
** Use the SQLITE_ENABLE_MODULE_COMMENTS macro to see some extra no-op
** comments in VDBE programs that show key decision points in the code
@@ -17751,47 +18003,11 @@ struct FuncDefHash {
};
#define SQLITE_FUNC_HASH(C,L) (((C)+(L))%SQLITE_FUNC_HASH_SZ)
-#if defined(SQLITE_USER_AUTHENTICATION)
-# warning "The SQLITE_USER_AUTHENTICATION extension is deprecated. \
- See ext/userauth/user-auth.txt for details."
-#endif
-#ifdef SQLITE_USER_AUTHENTICATION
-/*
-** Information held in the "sqlite3" database connection object and used
-** to manage user authentication.
-*/
-typedef struct sqlite3_userauth sqlite3_userauth;
-struct sqlite3_userauth {
- u8 authLevel; /* Current authentication level */
- int nAuthPW; /* Size of the zAuthPW in bytes */
- char *zAuthPW; /* Password used to authenticate */
- char *zAuthUser; /* User name used to authenticate */
-};
-
-/* Allowed values for sqlite3_userauth.authLevel */
-#define UAUTH_Unknown 0 /* Authentication not yet checked */
-#define UAUTH_Fail 1 /* User authentication failed */
-#define UAUTH_User 2 /* Authenticated as a normal user */
-#define UAUTH_Admin 3 /* Authenticated as an administrator */
-
-/* Functions used only by user authorization logic */
-SQLITE_PRIVATE int sqlite3UserAuthTable(const char*);
-SQLITE_PRIVATE int sqlite3UserAuthCheckLogin(sqlite3*,const char*,u8*);
-SQLITE_PRIVATE void sqlite3UserAuthInit(sqlite3*);
-SQLITE_PRIVATE void sqlite3CryptFunc(sqlite3_context*,int,sqlite3_value**);
-
-#endif /* SQLITE_USER_AUTHENTICATION */
-
/*
** typedef for the authorization callback function.
*/
-#ifdef SQLITE_USER_AUTHENTICATION
- typedef int (*sqlite3_xauth)(void*,int,const char*,const char*,const char*,
- const char*, const char*);
-#else
- typedef int (*sqlite3_xauth)(void*,int,const char*,const char*,const char*,
- const char*);
-#endif
+typedef int (*sqlite3_xauth)(void*,int,const char*,const char*,const char*,
+ const char*);
#ifndef SQLITE_OMIT_DEPRECATED
/* This is an extra SQLITE_TRACE macro that indicates "legacy" tracing
@@ -17929,6 +18145,10 @@ struct sqlite3 {
Savepoint *pSavepoint; /* List of active savepoints */
int nAnalysisLimit; /* Number of index rows to ANALYZE */
int busyTimeout; /* Busy handler timeout, in msec */
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ int setlkTimeout; /* Blocking lock timeout, in msec. -1 -> inf. */
+ int setlkFlags; /* Flags passed to setlk_timeout() */
+#endif
int nSavepoint; /* Number of non-transaction savepoints */
int nStatement; /* Number of nested statement-transactions */
i64 nDeferredCons; /* Net deferred constraints this transaction. */
@@ -17952,9 +18172,6 @@ struct sqlite3 {
void (*xUnlockNotify)(void **, int); /* Unlock notify callback */
sqlite3 *pNextBlocked; /* Next in list of all blocked connections */
#endif
-#ifdef SQLITE_USER_AUTHENTICATION
- sqlite3_userauth auth; /* User authentication information */
-#endif
};
/*
@@ -18018,6 +18235,9 @@ struct sqlite3 {
#define SQLITE_CorruptRdOnly HI(0x00002) /* Prohibit writes due to error */
#define SQLITE_ReadUncommit HI(0x00004) /* READ UNCOMMITTED in shared-cache */
#define SQLITE_FkNoAction HI(0x00008) /* Treat all FK as NO ACTION */
+#define SQLITE_AttachCreate HI(0x00010) /* ATTACH allowed to create new dbs */
+#define SQLITE_AttachWrite HI(0x00020) /* ATTACH allowed to open for write */
+#define SQLITE_Comments HI(0x00040) /* Enable SQL comments */
/* Flags used only if debugging */
#ifdef SQLITE_DEBUG
@@ -18077,6 +18297,7 @@ struct sqlite3 {
#define SQLITE_NullUnusedCols 0x04000000 /* NULL unused columns in subqueries */
#define SQLITE_OnePass 0x08000000 /* Single-pass DELETE and UPDATE */
#define SQLITE_OrderBySubq 0x10000000 /* ORDER BY in subquery helps outer */
+#define SQLITE_StarQuery 0x20000000 /* Heurists for star queries */
#define SQLITE_AllOpts 0xffffffff /* All optimizations */
/*
@@ -18113,7 +18334,7 @@ struct sqlite3 {
** field is used by per-connection app-def functions.
*/
struct FuncDef {
- i8 nArg; /* Number of arguments. -1 means unlimited */
+ i16 nArg; /* Number of arguments. -1 means unlimited */
u32 funcFlags; /* Some combination of SQLITE_FUNC_* */
void *pUserData; /* User data parameter */
FuncDef *pNext; /* Next function with same name */
@@ -18606,6 +18827,7 @@ struct Table {
} u;
Trigger *pTrigger; /* List of triggers on this object */
Schema *pSchema; /* Schema that contains this table */
+ u8 aHx[16]; /* Column aHt[K%sizeof(aHt)] might have hash K */
};
/*
@@ -18739,9 +18961,13 @@ struct FKey {
struct sColMap { /* Mapping of columns in pFrom to columns in zTo */
int iFrom; /* Index of column in pFrom */
char *zCol; /* Name of column in zTo. If NULL use PRIMARY KEY */
- } aCol[1]; /* One entry for each of nCol columns */
+ } aCol[FLEXARRAY]; /* One entry for each of nCol columns */
};
+/* The size (in bytes) of an FKey object holding N columns. The answer
+** does NOT include space to hold the zTo name. */
+#define SZ_FKEY(N) (offsetof(FKey,aCol)+(N)*sizeof(struct sColMap))
+
/*
** SQLite supports many different ways to resolve a constraint
** error. ROLLBACK processing means that a constraint violation
@@ -18803,9 +19029,12 @@ struct KeyInfo {
u16 nAllField; /* Total columns, including key plus others */
sqlite3 *db; /* The database connection */
u8 *aSortFlags; /* Sort order for each column. */
- CollSeq *aColl[1]; /* Collating sequence for each term of the key */
+ CollSeq *aColl[FLEXARRAY]; /* Collating sequence for each term of the key */
};
+/* The size (in bytes) of a KeyInfo object with up to N fields */
+#define SZ_KEYINFO(N) (offsetof(KeyInfo,aColl) + (N)*sizeof(CollSeq*))
+
/*
** Allowed bit values for entries in the KeyInfo.aSortFlags[] array.
*/
@@ -18925,7 +19154,7 @@ struct Index {
Pgno tnum; /* DB Page containing root of this index */
LogEst szIdxRow; /* Estimated average row size in bytes */
u16 nKeyCol; /* Number of columns forming the key */
- u16 nColumn; /* Number of columns stored in the index */
+ u16 nColumn; /* Nr columns in btree. Can be 2*Table.nCol */
u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
unsigned idxType:2; /* 0:Normal 1:UNIQUE, 2:PRIMARY KEY, 3:IPK */
unsigned bUnordered:1; /* Use this index for == or IN queries only */
@@ -18934,9 +19163,9 @@ struct Index {
unsigned isCovering:1; /* True if this is a covering index */
unsigned noSkipScan:1; /* Do not try to use skip-scan if true */
unsigned hasStat1:1; /* aiRowLogEst values come from sqlite_stat1 */
- unsigned bLowQual:1; /* sqlite_stat1 says this is a low-quality index */
unsigned bNoQuery:1; /* Do not use this index to optimize queries */
unsigned bAscKeyBug:1; /* True if the bba7b69f9849b5bf bug applies */
+ unsigned bIdxRowid:1; /* One or more of the index keys is the ROWID */
unsigned bHasVCol:1; /* Index references one or more VIRTUAL columns */
unsigned bHasExpr:1; /* Index contains an expression, either a literal
** expression, or a reference to a VIRTUAL column */
@@ -19263,10 +19492,10 @@ struct Expr {
/* Macros can be used to test, set, or clear bits in the
** Expr.flags field.
*/
-#define ExprHasProperty(E,P) (((E)->flags&(P))!=0)
-#define ExprHasAllProperty(E,P) (((E)->flags&(P))==(P))
-#define ExprSetProperty(E,P) (E)->flags|=(P)
-#define ExprClearProperty(E,P) (E)->flags&=~(P)
+#define ExprHasProperty(E,P) (((E)->flags&(u32)(P))!=0)
+#define ExprHasAllProperty(E,P) (((E)->flags&(u32)(P))==(u32)(P))
+#define ExprSetProperty(E,P) (E)->flags|=(u32)(P)
+#define ExprClearProperty(E,P) (E)->flags&=~(u32)(P)
#define ExprAlwaysTrue(E) (((E)->flags&(EP_OuterON|EP_IsTrue))==EP_IsTrue)
#define ExprAlwaysFalse(E) (((E)->flags&(EP_OuterON|EP_IsFalse))==EP_IsFalse)
#define ExprIsFullSize(E) (((E)->flags&(EP_Reduced|EP_TokenOnly))==0)
@@ -19378,9 +19607,14 @@ struct ExprList {
int iConstExprReg; /* Register in which Expr value is cached. Used only
** by Parse.pConstExpr */
} u;
- } a[1]; /* One slot for each expression in the list */
+ } a[FLEXARRAY]; /* One slot for each expression in the list */
};
+/* The size (in bytes) of an ExprList object that is big enough to hold
+** as many as N expressions. */
+#define SZ_EXPRLIST(N) \
+ (offsetof(ExprList,a) + (N)*sizeof(struct ExprList_item))
+
/*
** Allowed values for Expr.a.eEName
*/
@@ -19406,16 +19640,14 @@ struct ExprList {
*/
struct IdList {
int nId; /* Number of identifiers on the list */
- u8 eU4; /* Which element of a.u4 is valid */
struct IdList_item {
char *zName; /* Name of the identifier */
- union {
- int idx; /* Index in some Table.aCol[] of a column named zName */
- Expr *pExpr; /* Expr to implement a USING variable -- NOT USED */
- } u4;
- } a[1];
+ } a[FLEXARRAY];
};
+/* The size (in bytes) of an IdList object that can hold up to N IDs. */
+#define SZ_IDLIST(N) (offsetof(IdList,a)+(N)*sizeof(struct IdList_item))
+
/*
** Allowed values for IdList.eType, which determines which value of the a.u4
** is valid.
@@ -19535,11 +19767,19 @@ struct OnOrUsing {
**
*/
struct SrcList {
- int nSrc; /* Number of tables or subqueries in the FROM clause */
- u32 nAlloc; /* Number of entries allocated in a[] below */
- SrcItem a[1]; /* One entry for each identifier on the list */
+ int nSrc; /* Number of tables or subqueries in the FROM clause */
+ u32 nAlloc; /* Number of entries allocated in a[] below */
+ SrcItem a[FLEXARRAY]; /* One entry for each identifier on the list */
};
+/* Size (in bytes) of a SrcList object that can hold as many as N
+** SrcItem objects. */
+#define SZ_SRCLIST(N) (offsetof(SrcList,a)+(N)*sizeof(SrcItem))
+
+/* Size (in bytes( of a SrcList object that holds 1 SrcItem. This is a
+** special case of SZ_SRCITEM(1) that comes up often. */
+#define SZ_SRCLIST_1 (offsetof(SrcList,a)+sizeof(SrcItem))
+
/*
** Permitted values of the SrcList.a.jointype field
*/
@@ -20008,25 +20248,32 @@ struct Parse {
char *zErrMsg; /* An error message */
Vdbe *pVdbe; /* An engine for executing database bytecode */
int rc; /* Return code from execution */
- u8 colNamesSet; /* TRUE after OP_ColumnName has been issued to pVdbe */
- u8 checkSchema; /* Causes schema cookie check after an error */
+ LogEst nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */
u8 nested; /* Number of nested calls to the parser/code generator */
u8 nTempReg; /* Number of temporary registers in aTempReg[] */
u8 isMultiWrite; /* True if statement may modify/insert multiple rows */
u8 mayAbort; /* True if statement may throw an ABORT exception */
u8 hasCompound; /* Need to invoke convertCompoundSelectToSubquery() */
- u8 okConstFactor; /* OK to factor out constants */
u8 disableLookaside; /* Number of times lookaside has been disabled */
u8 prepFlags; /* SQLITE_PREPARE_* flags */
u8 withinRJSubrtn; /* Nesting level for RIGHT JOIN body subroutines */
- u8 bHasWith; /* True if statement contains WITH */
u8 mSubrtnSig; /* mini Bloom filter on available SubrtnSig.selId */
+ u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */
+ u8 bReturning; /* Coding a RETURNING trigger */
+ u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */
+ u8 disableTriggers; /* True to disable triggers */
#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
u8 earlyCleanup; /* OOM inside sqlite3ParserAddCleanup() */
#endif
#ifdef SQLITE_DEBUG
u8 ifNotExists; /* Might be true if IF NOT EXISTS. Assert()s only */
+ u8 isCreate; /* CREATE TABLE, INDEX, or VIEW (but not TRIGGER)
+ ** and ALTER TABLE ADD COLUMN. */
#endif
+ bft colNamesSet :1; /* TRUE after OP_ColumnName has been issued to pVdbe */
+ bft bHasWith :1; /* True if statement contains WITH */
+ bft okConstFactor :1; /* OK to factor out constants */
+ bft checkSchema :1; /* Causes schema cookie check after an error */
int nRangeReg; /* Size of the temporary register block */
int iRangeReg; /* First register in temporary register block */
int nErr; /* Number of errors seen */
@@ -20041,12 +20288,9 @@ struct Parse {
ExprList *pConstExpr;/* Constant expressions */
IndexedExpr *pIdxEpr;/* List of expressions used by active indexes */
IndexedExpr *pIdxPartExpr; /* Exprs constrained by index WHERE clauses */
- Token constraintName;/* Name of the constraint currently being parsed */
yDbMask writeMask; /* Start a write transaction on these databases */
yDbMask cookieMask; /* Bitmask of schema verified databases */
- int regRowid; /* Register holding rowid of CREATE TABLE entry */
- int regRoot; /* Register holding root page number for new objects */
- int nMaxArg; /* Max args passed to user function by sub-program */
+ int nMaxArg; /* Max args to xUpdate and xFilter vtab methods */
int nSelect; /* Number of SELECT stmts. Counter for Select.selId */
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
u32 nProgressSteps; /* xProgress steps taken during sqlite3_prepare() */
@@ -20060,17 +20304,6 @@ struct Parse {
Table *pTriggerTab; /* Table triggers are being coded for */
TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */
ParseCleanup *pCleanup; /* List of cleanup operations to run after parse */
- union {
- int addrCrTab; /* Address of OP_CreateBtree on CREATE TABLE */
- Returning *pReturning; /* The RETURNING clause */
- } u1;
- u32 oldmask; /* Mask of old.* columns referenced */
- u32 newmask; /* Mask of new.* columns referenced */
- LogEst nQueryLoop; /* Est number of iterations of a query (10*log2(N)) */
- u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */
- u8 bReturning; /* Coding a RETURNING trigger */
- u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */
- u8 disableTriggers; /* True to disable triggers */
/**************************************************************************
** Fields above must be initialized to zero. The fields that follow,
@@ -20082,6 +20315,19 @@ struct Parse {
int aTempReg[8]; /* Holding area for temporary registers */
Parse *pOuterParse; /* Outer Parse object when nested */
Token sNameToken; /* Token with unqualified schema object name */
+ u32 oldmask; /* Mask of old.* columns referenced */
+ u32 newmask; /* Mask of new.* columns referenced */
+ union {
+ struct { /* These fields available when isCreate is true */
+ int addrCrTab; /* Address of OP_CreateBtree on CREATE TABLE */
+ int regRowid; /* Register holding rowid of CREATE TABLE entry */
+ int regRoot; /* Register holding root page for new objects */
+ Token constraintName; /* Name of the constraint currently being parsed */
+ } cr;
+ struct { /* These fields available to all other statements */
+ Returning *pReturning; /* The RETURNING clause */
+ } d;
+ } u1;
/************************************************************************
** Above is constant between recursions. Below is reset before and after
@@ -20099,9 +20345,7 @@ struct Parse {
int nVtabLock; /* Number of virtual tables to lock */
#endif
int nHeight; /* Expression tree height of current sub-select */
-#ifndef SQLITE_OMIT_EXPLAIN
int addrExplain; /* Address of current OP_Explain opcode */
-#endif
VList *pVList; /* Mapping between variable names and numbers */
Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */
const char *zTail; /* All SQL text past the last semicolon parsed */
@@ -20599,9 +20843,13 @@ struct With {
int nCte; /* Number of CTEs in the WITH clause */
int bView; /* Belongs to the outermost Select of a view */
With *pOuter; /* Containing WITH clause, or NULL */
- Cte a[1]; /* For each CTE in the WITH clause.... */
+ Cte a[FLEXARRAY]; /* For each CTE in the WITH clause.... */
};
+/* The size (in bytes) of a With object that can hold as many
+** as N different CTEs. */
+#define SZ_WITH(N) (offsetof(With,a) + (N)*sizeof(Cte))
+
/*
** The Cte object is not guaranteed to persist for the entire duration
** of code generation. (The query flattener or other parser tree
@@ -20630,9 +20878,13 @@ struct DbClientData {
DbClientData *pNext; /* Next in a linked list */
void *pData; /* The data */
void (*xDestructor)(void*); /* Destructor. Might be NULL */
- char zName[1]; /* Name of this client data. MUST BE LAST */
+ char zName[FLEXARRAY]; /* Name of this client data. MUST BE LAST */
};
+/* The size (in bytes) of a DbClientData object that can has a name
+** that is N bytes long, including the zero-terminator. */
+#define SZ_DBCLIENTDATA(N) (offsetof(DbClientData,zName)+(N))
+
#ifdef SQLITE_DEBUG
/*
** An instance of the TreeView object is used for printing the content of
@@ -21075,7 +21327,7 @@ SQLITE_PRIVATE void sqlite3SubqueryColumnTypes(Parse*,Table*,Select*,char);
SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,Select*,char);
SQLITE_PRIVATE void sqlite3OpenSchemaTable(Parse *, int);
SQLITE_PRIVATE Index *sqlite3PrimaryKeyIndex(Table*);
-SQLITE_PRIVATE i16 sqlite3TableColumnToIndex(Index*, i16);
+SQLITE_PRIVATE int sqlite3TableColumnToIndex(Index*, int);
#ifdef SQLITE_OMIT_GENERATED_COLUMNS
# define sqlite3TableColumnToStorage(T,X) (X) /* No-op pass-through */
# define sqlite3StorageColumnToTable(T,X) (X) /* No-op pass-through */
@@ -21173,7 +21425,7 @@ SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse*, SrcList*);
SQLITE_PRIVATE void sqlite3IdListDelete(sqlite3*, IdList*);
SQLITE_PRIVATE void sqlite3ClearOnOrUsing(sqlite3*, OnOrUsing*);
SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3*, SrcList*);
-SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(sqlite3*,i16,int,char**);
+SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(sqlite3*,int,int,char**);
SQLITE_PRIVATE void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*,
Expr*, int, int, u8);
SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int);
@@ -21309,7 +21561,8 @@ SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3*,const Select*,int);
SQLITE_PRIVATE FuncDef *sqlite3FunctionSearch(int,const char*);
SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs(FuncDef*,int);
SQLITE_PRIVATE FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,u8,u8);
-SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum*,sqlite3_value*);
+SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum*,sqlite3_value*,int);
+SQLITE_PRIVATE int sqlite3AppendOneUtf8Character(char*, u32);
SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void);
SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void);
SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void);
@@ -22174,6 +22427,9 @@ static const char * const sqlite3azCompileOpt[] = {
#ifdef SQLITE_BUG_COMPATIBLE_20160819
"BUG_COMPATIBLE_20160819",
#endif
+#ifdef SQLITE_BUG_COMPATIBLE_20250510
+ "BUG_COMPATIBLE_20250510",
+#endif
#ifdef SQLITE_CASE_SENSITIVE_LIKE
"CASE_SENSITIVE_LIKE",
#endif
@@ -22410,6 +22666,9 @@ static const char * const sqlite3azCompileOpt[] = {
#ifdef SQLITE_ENABLE_SESSION
"ENABLE_SESSION",
#endif
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ "ENABLE_SETLK_TIMEOUT",
+#endif
#ifdef SQLITE_ENABLE_SNAPSHOT
"ENABLE_SNAPSHOT",
#endif
@@ -22464,6 +22723,9 @@ static const char * const sqlite3azCompileOpt[] = {
#ifdef SQLITE_EXTRA_INIT
"EXTRA_INIT=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT),
#endif
+#ifdef SQLITE_EXTRA_INIT_MUTEXED
+ "EXTRA_INIT_MUTEXED=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT_MUTEXED),
+#endif
#ifdef SQLITE_EXTRA_SHUTDOWN
"EXTRA_SHUTDOWN=" CTIMEOPT_VAL(SQLITE_EXTRA_SHUTDOWN),
#endif
@@ -22861,9 +23123,6 @@ static const char * const sqlite3azCompileOpt[] = {
#ifdef SQLITE_UNTESTABLE
"UNTESTABLE",
#endif
-#ifdef SQLITE_USER_AUTHENTICATION
- "USER_AUTHENTICATION",
-#endif
#ifdef SQLITE_USE_ALLOCA
"USE_ALLOCA",
#endif
@@ -23451,12 +23710,19 @@ struct VdbeCursor {
#endif
VdbeTxtBlbCache *pCache; /* Cache of large TEXT or BLOB values */
- /* 2*nField extra array elements allocated for aType[], beyond the one
- ** static element declared in the structure. nField total array slots for
- ** aType[] and nField+1 array slots for aOffset[] */
- u32 aType[1]; /* Type values record decode. MUST BE LAST */
+ /* Space is allocated for aType to hold at least 2*nField+1 entries:
+ ** nField slots for aType[] and nField+1 array slots for aOffset[] */
+ u32 aType[FLEXARRAY]; /* Type values record decode. MUST BE LAST */
};
+/*
+** The size (in bytes) of a VdbeCursor object that has an nField value of N
+** or less. The value of SZ_VDBECURSOR(n) is guaranteed to be a multiple
+** of 8.
+*/
+#define SZ_VDBECURSOR(N) \
+ (ROUND8(offsetof(VdbeCursor,aType)) + ((N)+1)*sizeof(u64))
+
/* Return true if P is a null-only cursor
*/
#define IsNullCursor(P) \
@@ -23562,6 +23828,7 @@ struct sqlite3_value {
#ifdef SQLITE_DEBUG
Mem *pScopyFrom; /* This Mem is a shallow copy of pScopyFrom */
u16 mScopyFlags; /* flags value immediately after the shallow copy */
+ u8 bScopy; /* The pScopyFrom of some other Mem *might* point here */
#endif
};
@@ -23711,14 +23978,17 @@ struct sqlite3_context {
int isError; /* Error code returned by the function. */
u8 enc; /* Encoding to use for results */
u8 skipFlag; /* Skip accumulator loading if true */
- u8 argc; /* Number of arguments */
- sqlite3_value *argv[1]; /* Argument set */
+ u16 argc; /* Number of arguments */
+ sqlite3_value *argv[FLEXARRAY]; /* Argument set */
};
-/* A bitfield type for use inside of structures. Always follow with :N where
-** N is the number of bits.
+/*
+** The size (in bytes) of an sqlite3_context object that holds N
+** argv[] arguments.
*/
-typedef unsigned bft; /* Bit Field Type */
+#define SZ_CONTEXT(N) \
+ (offsetof(sqlite3_context,argv)+(N)*sizeof(sqlite3_value*))
+
/* The ScanStatus object holds a single value for the
** sqlite3_stmt_scanstatus() interface.
@@ -23779,7 +24049,7 @@ struct Vdbe {
i64 nStmtDefCons; /* Number of def. constraints when stmt started */
i64 nStmtDefImmCons; /* Number of def. imm constraints when stmt started */
Mem *aMem; /* The memory locations */
- Mem **apArg; /* Arguments to currently executing user function */
+ Mem **apArg; /* Arguments xUpdate and xFilter vtab methods */
VdbeCursor **apCsr; /* One element of this array for each open cursor */
Mem *aVar; /* Values for the OP_Variable opcode. */
@@ -23799,6 +24069,7 @@ struct Vdbe {
#ifdef SQLITE_DEBUG
int rcApp; /* errcode set by sqlite3_result_error_code() */
u32 nWrite; /* Number of write operations that have occurred */
+ int napArg; /* Size of the apArg[] array */
#endif
u16 nResColumn; /* Number of columns in one row of the result set */
u16 nResAlloc; /* Column slots allocated to aColName[] */
@@ -23851,17 +24122,19 @@ struct PreUpdate {
VdbeCursor *pCsr; /* Cursor to read old values from */
int op; /* One of SQLITE_INSERT, UPDATE, DELETE */
u8 *aRecord; /* old.* database record */
- KeyInfo keyinfo;
+ KeyInfo *pKeyinfo; /* Key information */
UnpackedRecord *pUnpacked; /* Unpacked version of aRecord[] */
UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */
int iNewReg; /* Register for new.* values */
int iBlobWrite; /* Value returned by preupdate_blobwrite() */
i64 iKey1; /* First key value passed to hook */
i64 iKey2; /* Second key value passed to hook */
+ Mem oldipk; /* Memory cell holding "old" IPK value */
Mem *aNew; /* Array of new.* values */
Table *pTab; /* Schema object being updated */
Index *pPk; /* PK index if pTab is WITHOUT ROWID */
sqlite3_value **apDflt; /* Array of default values, if required */
+ u8 keyinfoSpace[SZ_KEYINFO(0)]; /* Space to hold pKeyinfo[0] content */
};
/*
@@ -24228,8 +24501,9 @@ SQLITE_PRIVATE int sqlite3LookasideUsed(sqlite3 *db, int *pHighwater){
nInit += countLookasideSlots(db->lookaside.pSmallInit);
nFree += countLookasideSlots(db->lookaside.pSmallFree);
#endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */
- if( pHighwater ) *pHighwater = db->lookaside.nSlot - nInit;
- return db->lookaside.nSlot - (nInit+nFree);
+ assert( db->lookaside.nSlot >= nInit+nFree );
+ if( pHighwater ) *pHighwater = (int)(db->lookaside.nSlot - nInit);
+ return (int)(db->lookaside.nSlot - (nInit+nFree));
}
/*
@@ -24282,7 +24556,7 @@ SQLITE_API int sqlite3_db_status(
assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)>=0 );
assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)<3 );
*pCurrent = 0;
- *pHighwater = db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT];
+ *pHighwater = (int)db->lookaside.anStat[op-SQLITE_DBSTATUS_LOOKASIDE_HIT];
if( resetFlag ){
db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT] = 0;
}
@@ -24659,6 +24933,9 @@ static int parseHhMmSs(const char *zDate, DateTime *p){
zDate++;
}
ms /= rScale;
+ /* Truncate to avoid problems with sub-milliseconds
+ ** rounding. https://sqlite.org/forum/forumpost/766a2c9231 */
+ if( ms>0.999 ) ms = 0.999;
}
}else{
s = 0;
@@ -25791,7 +26068,7 @@ static int daysAfterMonday(DateTime *pDate){
** In other words, return the day of the week according
** to this code:
**
-** 0=Sunday, 1=Monday, 2=Tues, ..., 6=Saturday
+** 0=Sunday, 1=Monday, 2=Tuesday, ..., 6=Saturday
*/
static int daysAfterSunday(DateTime *pDate){
assert( pDate->validJD );
@@ -25866,7 +26143,7 @@ static void strftimeFunc(
}
case 'f': { /* Fractional seconds. (Non-standard) */
double s = x.s;
- if( s>59.999 ) s = 59.999;
+ if( NEVER(s>59.999) ) s = 59.999;
sqlite3_str_appendf(&sRes, "%06.3f", s);
break;
}
@@ -30000,6 +30277,8 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
#ifdef __CYGWIN__
# include <sys/cygwin.h>
+# include <sys/stat.h> /* amalgamator: dontcache */
+# include <unistd.h> /* amalgamator: dontcache */
# include <errno.h> /* amalgamator: dontcache */
#endif
@@ -31394,17 +31673,17 @@ SQLITE_PRIVATE int sqlite3ApiExit(sqlite3* db, int rc){
#define etPERCENT 7 /* Percent symbol. %% */
#define etCHARX 8 /* Characters. %c */
/* The rest are extensions, not normally found in printf() */
-#define etSQLESCAPE 9 /* Strings with '\'' doubled. %q */
-#define etSQLESCAPE2 10 /* Strings with '\'' doubled and enclosed in '',
- NULL pointers replaced by SQL NULL. %Q */
-#define etTOKEN 11 /* a pointer to a Token structure */
-#define etSRCITEM 12 /* a pointer to a SrcItem */
-#define etPOINTER 13 /* The %p conversion */
-#define etSQLESCAPE3 14 /* %w -> Strings with '\"' doubled */
-#define etORDINAL 15 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */
-#define etDECIMAL 16 /* %d or %u, but not %x, %o */
+#define etESCAPE_q 9 /* Strings with '\'' doubled. %q */
+#define etESCAPE_Q 10 /* Strings with '\'' doubled and enclosed in '',
+ NULL pointers replaced by SQL NULL. %Q */
+#define etTOKEN 11 /* a pointer to a Token structure */
+#define etSRCITEM 12 /* a pointer to a SrcItem */
+#define etPOINTER 13 /* The %p conversion */
+#define etESCAPE_w 14 /* %w -> Strings with '\"' doubled */
+#define etORDINAL 15 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */
+#define etDECIMAL 16 /* %d or %u, but not %x, %o */
-#define etINVALID 17 /* Any unrecognized conversion type */
+#define etINVALID 17 /* Any unrecognized conversion type */
/*
@@ -31443,9 +31722,9 @@ static const et_info fmtinfo[] = {
{ 's', 0, 4, etSTRING, 0, 0 },
{ 'g', 0, 1, etGENERIC, 30, 0 },
{ 'z', 0, 4, etDYNSTRING, 0, 0 },
- { 'q', 0, 4, etSQLESCAPE, 0, 0 },
- { 'Q', 0, 4, etSQLESCAPE2, 0, 0 },
- { 'w', 0, 4, etSQLESCAPE3, 0, 0 },
+ { 'q', 0, 4, etESCAPE_q, 0, 0 },
+ { 'Q', 0, 4, etESCAPE_Q, 0, 0 },
+ { 'w', 0, 4, etESCAPE_w, 0, 0 },
{ 'c', 0, 0, etCHARX, 0, 0 },
{ 'o', 8, 0, etRADIX, 0, 2 },
{ 'u', 10, 0, etDECIMAL, 0, 0 },
@@ -32042,25 +32321,7 @@ SQLITE_API void sqlite3_str_vappendf(
}
}else{
unsigned int ch = va_arg(ap,unsigned int);
- if( ch<0x00080 ){
- buf[0] = ch & 0xff;
- length = 1;
- }else if( ch<0x00800 ){
- buf[0] = 0xc0 + (u8)((ch>>6)&0x1f);
- buf[1] = 0x80 + (u8)(ch & 0x3f);
- length = 2;
- }else if( ch<0x10000 ){
- buf[0] = 0xe0 + (u8)((ch>>12)&0x0f);
- buf[1] = 0x80 + (u8)((ch>>6) & 0x3f);
- buf[2] = 0x80 + (u8)(ch & 0x3f);
- length = 3;
- }else{
- buf[0] = 0xf0 + (u8)((ch>>18) & 0x07);
- buf[1] = 0x80 + (u8)((ch>>12) & 0x3f);
- buf[2] = 0x80 + (u8)((ch>>6) & 0x3f);
- buf[3] = 0x80 + (u8)(ch & 0x3f);
- length = 4;
- }
+ length = sqlite3AppendOneUtf8Character(buf, ch);
}
if( precision>1 ){
i64 nPrior = 1;
@@ -32140,22 +32401,31 @@ SQLITE_API void sqlite3_str_vappendf(
while( ii>=0 ) if( (bufpt[ii--] & 0xc0)==0x80 ) width++;
}
break;
- case etSQLESCAPE: /* %q: Escape ' characters */
- case etSQLESCAPE2: /* %Q: Escape ' and enclose in '...' */
- case etSQLESCAPE3: { /* %w: Escape " characters */
+ case etESCAPE_q: /* %q: Escape ' characters */
+ case etESCAPE_Q: /* %Q: Escape ' and enclose in '...' */
+ case etESCAPE_w: { /* %w: Escape " characters */
i64 i, j, k, n;
- int needQuote, isnull;
+ int needQuote = 0;
char ch;
- char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote character */
char *escarg;
+ char q;
if( bArgList ){
escarg = getTextArg(pArgList);
}else{
escarg = va_arg(ap,char*);
}
- isnull = escarg==0;
- if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)");
+ if( escarg==0 ){
+ escarg = (xtype==etESCAPE_Q ? "NULL" : "(NULL)");
+ }else if( xtype==etESCAPE_Q ){
+ needQuote = 1;
+ }
+ if( xtype==etESCAPE_w ){
+ q = '"';
+ flag_alternateform = 0;
+ }else{
+ q = '\'';
+ }
/* For %q, %Q, and %w, the precision is the number of bytes (or
** characters if the ! flags is present) to use from the input.
** Because of the extra quoting characters inserted, the number
@@ -32168,7 +32438,30 @@ SQLITE_API void sqlite3_str_vappendf(
while( (escarg[i+1]&0xc0)==0x80 ){ i++; }
}
}
- needQuote = !isnull && xtype==etSQLESCAPE2;
+ if( flag_alternateform ){
+ /* For %#q, do unistr()-style backslash escapes for
+ ** all control characters, and for backslash itself.
+ ** For %#Q, do the same but only if there is at least
+ ** one control character. */
+ u32 nBack = 0;
+ u32 nCtrl = 0;
+ for(k=0; k<i; k++){
+ if( escarg[k]=='\\' ){
+ nBack++;
+ }else if( ((u8*)escarg)[k]<=0x1f ){
+ nCtrl++;
+ }
+ }
+ if( nCtrl || xtype==etESCAPE_q ){
+ n += nBack + 5*nCtrl;
+ if( xtype==etESCAPE_Q ){
+ n += 10;
+ needQuote = 2;
+ }
+ }else{
+ flag_alternateform = 0;
+ }
+ }
n += i + 3;
if( n>etBUFSIZE ){
bufpt = zExtra = printfTempBuf(pAccum, n);
@@ -32177,13 +32470,41 @@ SQLITE_API void sqlite3_str_vappendf(
bufpt = buf;
}
j = 0;
- if( needQuote ) bufpt[j++] = q;
+ if( needQuote ){
+ if( needQuote==2 ){
+ memcpy(&bufpt[j], "unistr('", 8);
+ j += 8;
+ }else{
+ bufpt[j++] = '\'';
+ }
+ }
k = i;
- for(i=0; i<k; i++){
- bufpt[j++] = ch = escarg[i];
- if( ch==q ) bufpt[j++] = ch;
+ if( flag_alternateform ){
+ for(i=0; i<k; i++){
+ bufpt[j++] = ch = escarg[i];
+ if( ch==q ){
+ bufpt[j++] = ch;
+ }else if( ch=='\\' ){
+ bufpt[j++] = '\\';
+ }else if( ((unsigned char)ch)<=0x1f ){
+ bufpt[j-1] = '\\';
+ bufpt[j++] = 'u';
+ bufpt[j++] = '0';
+ bufpt[j++] = '0';
+ bufpt[j++] = ch>=0x10 ? '1' : '0';
+ bufpt[j++] = "0123456789abcdef"[ch&0xf];
+ }
+ }
+ }else{
+ for(i=0; i<k; i++){
+ bufpt[j++] = ch = escarg[i];
+ if( ch==q ) bufpt[j++] = ch;
+ }
+ }
+ if( needQuote ){
+ bufpt[j++] = '\'';
+ if( needQuote==2 ) bufpt[j++] = ')';
}
- if( needQuote ) bufpt[j++] = q;
bufpt[j] = 0;
length = j;
goto adjust_width_for_utf8;
@@ -32426,7 +32747,7 @@ SQLITE_API void sqlite3_str_appendall(sqlite3_str *p, const char *z){
static SQLITE_NOINLINE char *strAccumFinishRealloc(StrAccum *p){
char *zText;
assert( p->mxAlloc>0 && !isMalloced(p) );
- zText = sqlite3DbMallocRaw(p->db, p->nChar+1 );
+ zText = sqlite3DbMallocRaw(p->db, 1+(u64)p->nChar );
if( zText ){
memcpy(zText, p->zText, p->nChar+1);
p->printfFlags |= SQLITE_PRINTF_MALLOCED;
@@ -32671,6 +32992,15 @@ SQLITE_API char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){
return zBuf;
}
+/* Maximum size of an sqlite3_log() message. */
+#if defined(SQLITE_MAX_LOG_MESSAGE)
+ /* Leave the definition as supplied */
+#elif SQLITE_PRINT_BUF_SIZE*10>10000
+# define SQLITE_MAX_LOG_MESSAGE 10000
+#else
+# define SQLITE_MAX_LOG_MESSAGE (SQLITE_PRINT_BUF_SIZE*10)
+#endif
+
/*
** This is the routine that actually formats the sqlite3_log() message.
** We house it in a separate routine from sqlite3_log() to avoid using
@@ -32687,7 +33017,7 @@ SQLITE_API char *sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){
*/
static void renderLogMsg(int iErrCode, const char *zFormat, va_list ap){
StrAccum acc; /* String accumulator */
- char zMsg[SQLITE_PRINT_BUF_SIZE*3]; /* Complete log message */
+ char zMsg[SQLITE_MAX_LOG_MESSAGE]; /* Complete log message */
sqlite3StrAccumInit(&acc, 0, zMsg, sizeof(zMsg), 0);
sqlite3_str_vappendf(&acc, zFormat, ap);
@@ -33034,10 +33364,13 @@ SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc)
sqlite3_str_appendf(&x, " DDL");
}
if( pItem->fg.isCte ){
- sqlite3_str_appendf(&x, " CteUse=0x%p", pItem->u2.pCteUse);
+ static const char *aMat[] = {",MAT", "", ",NO-MAT"};
+ sqlite3_str_appendf(&x, " CteUse=%d%s",
+ pItem->u2.pCteUse->nUse,
+ aMat[pItem->u2.pCteUse->eM10d]);
}
if( pItem->fg.isOn || (pItem->fg.isUsing==0 && pItem->u3.pOn!=0) ){
- sqlite3_str_appendf(&x, " ON");
+ sqlite3_str_appendf(&x, " isOn");
}
if( pItem->fg.isTabFunc ) sqlite3_str_appendf(&x, " isTabFunc");
if( pItem->fg.isCorrelated ) sqlite3_str_appendf(&x, " isCorrelated");
@@ -33065,9 +33398,6 @@ SQLITE_PRIVATE void sqlite3TreeViewSrcList(TreeView *pView, const SrcList *pSrc)
sqlite3TreeViewColumnList(pView, pTab->aCol, pTab->nCol, 1);
}
assert( (int)pItem->fg.isNestedFrom == IsNestedFrom(pItem) );
- sqlite3TreeViewPush(&pView, 0);
- sqlite3TreeViewLine(pView, "SUBQUERY");
- sqlite3TreeViewPop(&pView);
sqlite3TreeViewSelect(pView, pItem->u4.pSubq->pSelect, 0);
}
if( pItem->fg.isTabFunc ){
@@ -33797,21 +34127,7 @@ SQLITE_PRIVATE void sqlite3TreeViewBareIdList(
if( zName==0 ) zName = "(null)";
sqlite3TreeViewPush(&pView, moreToFollow);
sqlite3TreeViewLine(pView, 0);
- if( pList->eU4==EU4_NONE ){
- fprintf(stdout, "%s\n", zName);
- }else if( pList->eU4==EU4_IDX ){
- fprintf(stdout, "%s (%d)\n", zName, pList->a[i].u4.idx);
- }else{
- assert( pList->eU4==EU4_EXPR );
- if( pList->a[i].u4.pExpr==0 ){
- fprintf(stdout, "%s (pExpr=NULL)\n", zName);
- }else{
- fprintf(stdout, "%s\n", zName);
- sqlite3TreeViewPush(&pView, i<pList->nId-1);
- sqlite3TreeViewExpr(pView, pList->a[i].u4.pExpr, 0);
- sqlite3TreeViewPop(&pView);
- }
- }
+ fprintf(stdout, "%s\n", zName);
sqlite3TreeViewPop(&pView);
}
}
@@ -34121,6 +34437,10 @@ SQLITE_PRIVATE void sqlite3TreeViewTrigger(
** accessible to the debugging, and to avoid warnings about unused
** functions. But these routines only exist in debugging builds, so they
** do not contaminate the interface.
+**
+** See Also:
+**
+** sqlite3ShowWhereTerm() in where.c
*/
SQLITE_PRIVATE void sqlite3ShowExpr(const Expr *p){ sqlite3TreeViewExpr(0,p,0); }
SQLITE_PRIVATE void sqlite3ShowExprList(const ExprList *p){ sqlite3TreeViewExprList(0,p,0,0);}
@@ -34693,6 +35013,35 @@ static const unsigned char sqlite3Utf8Trans1[] = {
}
/*
+** Write a single UTF8 character whose value is v into the
+** buffer starting at zOut. zOut must be sized to hold at
+** least four bytes. Return the number of bytes needed
+** to encode the new character.
+*/
+SQLITE_PRIVATE int sqlite3AppendOneUtf8Character(char *zOut, u32 v){
+ if( v<0x00080 ){
+ zOut[0] = (u8)(v & 0xff);
+ return 1;
+ }
+ if( v<0x00800 ){
+ zOut[0] = 0xc0 + (u8)((v>>6) & 0x1f);
+ zOut[1] = 0x80 + (u8)(v & 0x3f);
+ return 2;
+ }
+ if( v<0x10000 ){
+ zOut[0] = 0xe0 + (u8)((v>>12) & 0x0f);
+ zOut[1] = 0x80 + (u8)((v>>6) & 0x3f);
+ zOut[2] = 0x80 + (u8)(v & 0x3f);
+ return 3;
+ }
+ zOut[0] = 0xf0 + (u8)((v>>18) & 0x07);
+ zOut[1] = 0x80 + (u8)((v>>12) & 0x3f);
+ zOut[2] = 0x80 + (u8)((v>>6) & 0x3f);
+ zOut[3] = 0x80 + (u8)(v & 0x3f);
+ return 4;
+}
+
+/*
** Translate a single UTF-8 character. Return the unicode value.
**
** During translation, assume that the byte that zTerm points
@@ -35113,7 +35462,7 @@ SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *zIn, int nByte, int nChar){
int n = 0;
if( SQLITE_UTF16NATIVE==SQLITE_UTF16LE ) z++;
- while( n<nChar && ALWAYS(z<=zEnd) ){
+ while( n<nChar && z<=zEnd ){
c = z[0];
z += 2;
if( c>=0xd8 && c<0xdc && z<=zEnd && z[0]>=0xdc && z[0]<0xe0 ) z += 2;
@@ -35697,8 +36046,8 @@ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 en
int eValid = 1; /* True exponent is either not used or is well-formed */
int nDigit = 0; /* Number of digits processed */
int eType = 1; /* 1: pure integer, 2+: fractional -1 or less: bad UTF16 */
+ u64 s2; /* round-tripped significand */
double rr[2];
- u64 s2;
assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
*pResult = 0.0; /* Default return value, in case of an error */
@@ -35801,7 +36150,7 @@ do_atof_calc:
e = (e*esign) + d;
/* Try to adjust the exponent to make it smaller */
- while( e>0 && s<(LARGEST_UINT64/10) ){
+ while( e>0 && s<((LARGEST_UINT64-0x7ff)/10) ){
s *= 10;
e--;
}
@@ -35811,11 +36160,22 @@ do_atof_calc:
}
rr[0] = (double)s;
- s2 = (u64)rr[0];
-#if defined(_MSC_VER) && _MSC_VER<1700
- if( s2==0x8000000000000000LL ){ s2 = 2*(u64)(0.5*rr[0]); }
+ assert( sizeof(s2)==sizeof(rr[0]) );
+#ifdef SQLITE_DEBUG
+ rr[1] = 18446744073709549568.0;
+ memcpy(&s2, &rr[1], sizeof(s2));
+ assert( s2==0x43efffffffffffffLL );
#endif
- rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s);
+ /* Largest double that can be safely converted to u64
+ ** vvvvvvvvvvvvvvvvvvvvvv */
+ if( rr[0]<=18446744073709549568.0 ){
+ s2 = (u64)rr[0];
+ rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s);
+ }else{
+ rr[1] = 0.0;
+ }
+ assert( rr[1]<=1.0e-10*rr[0] ); /* Equal only when rr[0]==0.0 */
+
if( e>0 ){
while( e>=100 ){
e -= 100;
@@ -36277,7 +36637,11 @@ SQLITE_PRIVATE void sqlite3FpDecode(FpDecode *p, double r, int iRound, int mxRou
}
p->z = &p->zBuf[i+1];
assert( i+p->n < sizeof(p->zBuf) );
- while( ALWAYS(p->n>0) && p->z[p->n-1]=='0' ){ p->n--; }
+ assert( p->n>0 );
+ while( p->z[p->n-1]=='0' ){
+ p->n--;
+ assert( p->n>0 );
+ }
}
/*
@@ -36782,7 +37146,7 @@ SQLITE_PRIVATE int sqlite3MulInt64(i64 *pA, i64 iB){
}
/*
-** Compute the absolute value of a 32-bit signed integer, of possible. Or
+** Compute the absolute value of a 32-bit signed integer, if possible. Or
** if the integer has a value of -2147483648, return +2147483647
*/
SQLITE_PRIVATE int sqlite3AbsInt32(int x){
@@ -37063,12 +37427,19 @@ SQLITE_PRIVATE void sqlite3HashClear(Hash *pH){
*/
static unsigned int strHash(const char *z){
unsigned int h = 0;
- unsigned char c;
- while( (c = (unsigned char)*z++)!=0 ){ /*OPTIMIZATION-IF-TRUE*/
+ while( z[0] ){ /*OPTIMIZATION-IF-TRUE*/
/* Knuth multiplicative hashing. (Sorting & Searching, p. 510).
** 0x9e3779b1 is 2654435761 which is the closest prime number to
- ** (2**32)*golden_ratio, where golden_ratio = (sqrt(5) - 1)/2. */
- h += sqlite3UpperToLower[c];
+ ** (2**32)*golden_ratio, where golden_ratio = (sqrt(5) - 1)/2.
+ **
+ ** Only bits 0xdf for ASCII and bits 0xbf for EBCDIC each octet are
+ ** hashed since the omitted bits determine the upper/lower case difference.
+ */
+#ifdef SQLITE_EBCDIC
+ h += 0xbf & (unsigned char)*(z++);
+#else
+ h += 0xdf & (unsigned char)*(z++);
+#endif
h *= 0x9e3779b1;
}
return h;
@@ -37141,9 +37512,8 @@ static int rehash(Hash *pH, unsigned int new_size){
pH->htsize = new_size = sqlite3MallocSize(new_ht)/sizeof(struct _ht);
memset(new_ht, 0, new_size*sizeof(struct _ht));
for(elem=pH->first, pH->first=0; elem; elem = next_elem){
- unsigned int h = strHash(elem->pKey) % new_size;
next_elem = elem->next;
- insertElement(pH, &new_ht[h], elem);
+ insertElement(pH, &new_ht[elem->h % new_size], elem);
}
return 1;
}
@@ -37161,23 +37531,22 @@ static HashElem *findElementWithHash(
HashElem *elem; /* Used to loop thru the element list */
unsigned int count; /* Number of elements left to test */
unsigned int h; /* The computed hash */
- static HashElem nullElement = { 0, 0, 0, 0 };
+ static HashElem nullElement = { 0, 0, 0, 0, 0 };
+ h = strHash(pKey);
if( pH->ht ){ /*OPTIMIZATION-IF-TRUE*/
struct _ht *pEntry;
- h = strHash(pKey) % pH->htsize;
- pEntry = &pH->ht[h];
+ pEntry = &pH->ht[h % pH->htsize];
elem = pEntry->chain;
count = pEntry->count;
}else{
- h = 0;
elem = pH->first;
count = pH->count;
}
if( pHash ) *pHash = h;
while( count ){
assert( elem!=0 );
- if( sqlite3StrICmp(elem->pKey,pKey)==0 ){
+ if( h==elem->h && sqlite3StrICmp(elem->pKey,pKey)==0 ){
return elem;
}
elem = elem->next;
@@ -37189,10 +37558,9 @@ static HashElem *findElementWithHash(
/* Remove a single entry from the hash table given a pointer to that
** element and a hash on the element's key.
*/
-static void removeElementGivenHash(
+static void removeElement(
Hash *pH, /* The pH containing "elem" */
- HashElem* elem, /* The element to be removed from the pH */
- unsigned int h /* Hash value for the element */
+ HashElem *elem /* The element to be removed from the pH */
){
struct _ht *pEntry;
if( elem->prev ){
@@ -37204,7 +37572,7 @@ static void removeElementGivenHash(
elem->next->prev = elem->prev;
}
if( pH->ht ){
- pEntry = &pH->ht[h];
+ pEntry = &pH->ht[elem->h % pH->htsize];
if( pEntry->chain==elem ){
pEntry->chain = elem->next;
}
@@ -37255,7 +37623,7 @@ SQLITE_PRIVATE void *sqlite3HashInsert(Hash *pH, const char *pKey, void *data){
if( elem->data ){
void *old_data = elem->data;
if( data==0 ){
- removeElementGivenHash(pH,elem,h);
+ removeElement(pH,elem);
}else{
elem->data = data;
elem->pKey = pKey;
@@ -37266,15 +37634,13 @@ SQLITE_PRIVATE void *sqlite3HashInsert(Hash *pH, const char *pKey, void *data){
new_elem = (HashElem*)sqlite3Malloc( sizeof(HashElem) );
if( new_elem==0 ) return data;
new_elem->pKey = pKey;
+ new_elem->h = h;
new_elem->data = data;
pH->count++;
- if( pH->count>=10 && pH->count > 2*pH->htsize ){
- if( rehash(pH, pH->count*2) ){
- assert( pH->htsize>0 );
- h = strHash(pKey) % pH->htsize;
- }
+ if( pH->count>=5 && pH->count > 2*pH->htsize ){
+ rehash(pH, pH->count*3);
}
- insertElement(pH, pH->ht ? &pH->ht[h] : 0, new_elem);
+ insertElement(pH, pH->ht ? &pH->ht[new_elem->h % pH->htsize] : 0, new_elem);
return 0;
}
@@ -38686,7 +39052,7 @@ SQLITE_PRIVATE int sqlite3KvvfsInit(void){
# endif
#else /* !SQLITE_WASI */
# ifndef HAVE_FCHMOD
-# define HAVE_FCHMOD
+# define HAVE_FCHMOD 1
# endif
#endif /* SQLITE_WASI */
@@ -38757,6 +39123,7 @@ struct unixFile {
#endif
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
unsigned iBusyTimeout; /* Wait this many millisec on locks */
+ int bBlockOnConnect; /* True to block for SHARED locks */
#endif
#if OS_VXWORKS
struct vxworksFileId *pId; /* Unique file ID */
@@ -40137,7 +40504,7 @@ static int unixFileLock(unixFile *pFile, struct flock *pLock){
if( (pFile->ctrlFlags & (UNIXFILE_EXCL|UNIXFILE_RDONLY))==UNIXFILE_EXCL ){
if( pInode->bProcessLock==0 ){
struct flock lock;
- assert( pInode->nLock==0 );
+ /* assert( pInode->nLock==0 ); <-- Not true if unix-excl READONLY used */
lock.l_whence = SEEK_SET;
lock.l_start = SHARED_FIRST;
lock.l_len = SHARED_SIZE;
@@ -40150,6 +40517,13 @@ static int unixFileLock(unixFile *pFile, struct flock *pLock){
rc = 0;
}
}else{
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ if( pFile->bBlockOnConnect && pLock->l_type==F_RDLCK
+ && pLock->l_start==SHARED_FIRST && pLock->l_len==SHARED_SIZE
+ ){
+ rc = osFcntl(pFile->h, F_SETLKW, pLock);
+ }else
+#endif
rc = osSetPosixAdvisoryLock(pFile->h, pLock, pFile);
}
return rc;
@@ -42460,6 +42834,11 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
}
#endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */
+ case SQLITE_FCNTL_NULL_IO: {
+ osClose(pFile->h);
+ pFile->h = -1;
+ return SQLITE_OK;
+ }
case SQLITE_FCNTL_LOCKSTATE: {
*(int*)pArg = pFile->eFileLock;
return SQLITE_OK;
@@ -42506,8 +42885,9 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
case SQLITE_FCNTL_LOCK_TIMEOUT: {
int iOld = pFile->iBusyTimeout;
+ int iNew = *(int*)pArg;
#if SQLITE_ENABLE_SETLK_TIMEOUT==1
- pFile->iBusyTimeout = *(int*)pArg;
+ pFile->iBusyTimeout = iNew<0 ? 0x7FFFFFFF : (unsigned)iNew;
#elif SQLITE_ENABLE_SETLK_TIMEOUT==2
pFile->iBusyTimeout = !!(*(int*)pArg);
#else
@@ -42516,7 +42896,12 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){
*(int*)pArg = iOld;
return SQLITE_OK;
}
-#endif
+ case SQLITE_FCNTL_BLOCK_ON_CONNECT: {
+ int iNew = *(int*)pArg;
+ pFile->bBlockOnConnect = iNew;
+ return SQLITE_OK;
+ }
+#endif /* SQLITE_ENABLE_SETLK_TIMEOUT */
#if SQLITE_MAX_MMAP_SIZE>0
case SQLITE_FCNTL_MMAP_SIZE: {
i64 newLimit = *(i64*)pArg;
@@ -43499,7 +43884,7 @@ static int unixShmLock(
**
** It is not permitted to block on the RECOVER lock.
*/
-#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+#if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && defined(SQLITE_DEBUG)
{
u16 lockMask = (p->exclMask|p->sharedMask);
assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
@@ -45308,7 +45693,7 @@ static int unixSleep(sqlite3_vfs *NotUsed, int microseconds){
/* Almost all modern unix systems support nanosleep(). But if you are
** compiling for one of the rare exceptions, you can use
- ** -DHAVE_NANOSLEEP=0 (perhaps in conjuction with -DHAVE_USLEEP if
+ ** -DHAVE_NANOSLEEP=0 (perhaps in conjunction with -DHAVE_USLEEP if
** usleep() is available) in order to bypass the use of nanosleep() */
nanosleep(&sp, NULL);
@@ -47029,8 +47414,18 @@ struct winFile {
sqlite3_int64 mmapSize; /* Size of mapped region */
sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */
#endif
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ DWORD iBusyTimeout; /* Wait this many millisec on locks */
+ int bBlockOnConnect;
+#endif
};
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+# define winFileBusyTimeout(pDbFd) pDbFd->iBusyTimeout
+#else
+# define winFileBusyTimeout(pDbFd) 0
+#endif
+
/*
** The winVfsAppData structure is used for the pAppData member for all of the
** Win32 VFS variants.
@@ -47349,7 +47744,7 @@ static struct win_syscall {
{ "FileTimeToLocalFileTime", (SYSCALL)0, 0 },
#endif
-#define osFileTimeToLocalFileTime ((BOOL(WINAPI*)(CONST FILETIME*, \
+#define osFileTimeToLocalFileTime ((BOOL(WINAPI*)(const FILETIME*, \
LPFILETIME))aSyscall[11].pCurrent)
#if SQLITE_OS_WINCE
@@ -47358,7 +47753,7 @@ static struct win_syscall {
{ "FileTimeToSystemTime", (SYSCALL)0, 0 },
#endif
-#define osFileTimeToSystemTime ((BOOL(WINAPI*)(CONST FILETIME*, \
+#define osFileTimeToSystemTime ((BOOL(WINAPI*)(const FILETIME*, \
LPSYSTEMTIME))aSyscall[12].pCurrent)
{ "FlushFileBuffers", (SYSCALL)FlushFileBuffers, 0 },
@@ -47464,6 +47859,12 @@ static struct win_syscall {
#define osGetFullPathNameW ((DWORD(WINAPI*)(LPCWSTR,DWORD,LPWSTR, \
LPWSTR*))aSyscall[25].pCurrent)
+/*
+** For GetLastError(), MSDN says:
+**
+** Minimum supported client: Windows XP [desktop apps | UWP apps]
+** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps]
+*/
{ "GetLastError", (SYSCALL)GetLastError, 0 },
#define osGetLastError ((DWORD(WINAPI*)(VOID))aSyscall[26].pCurrent)
@@ -47632,7 +48033,7 @@ static struct win_syscall {
{ "LockFile", (SYSCALL)0, 0 },
#endif
-#ifndef osLockFile
+#if !defined(osLockFile) && defined(SQLITE_WIN32_HAS_ANSI)
#define osLockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
DWORD))aSyscall[47].pCurrent)
#endif
@@ -47696,7 +48097,7 @@ static struct win_syscall {
{ "SystemTimeToFileTime", (SYSCALL)SystemTimeToFileTime, 0 },
-#define osSystemTimeToFileTime ((BOOL(WINAPI*)(CONST SYSTEMTIME*, \
+#define osSystemTimeToFileTime ((BOOL(WINAPI*)(const SYSTEMTIME*, \
LPFILETIME))aSyscall[56].pCurrent)
#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
@@ -47705,7 +48106,7 @@ static struct win_syscall {
{ "UnlockFile", (SYSCALL)0, 0 },
#endif
-#ifndef osUnlockFile
+#if !defined(osUnlockFile) && defined(SQLITE_WIN32_HAS_ANSI)
#define osUnlockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
DWORD))aSyscall[57].pCurrent)
#endif
@@ -47746,11 +48147,13 @@ static struct win_syscall {
#define osCreateEventExW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,LPCWSTR, \
DWORD,DWORD))aSyscall[62].pCurrent)
-#if !SQLITE_OS_WINRT
+/*
+** For WaitForSingleObject(), MSDN says:
+**
+** Minimum supported client: Windows XP [desktop apps | UWP apps]
+** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps]
+*/
{ "WaitForSingleObject", (SYSCALL)WaitForSingleObject, 0 },
-#else
- { "WaitForSingleObject", (SYSCALL)0, 0 },
-#endif
#define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \
DWORD))aSyscall[63].pCurrent)
@@ -47897,6 +48300,97 @@ static struct win_syscall {
#define osFlushViewOfFile \
((BOOL(WINAPI*)(LPCVOID,SIZE_T))aSyscall[79].pCurrent)
+/*
+** If SQLITE_ENABLE_SETLK_TIMEOUT is defined, we require CreateEvent()
+** to implement blocking locks with timeouts. MSDN says:
+**
+** Minimum supported client: Windows XP [desktop apps | UWP apps]
+** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps]
+*/
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ { "CreateEvent", (SYSCALL)CreateEvent, 0 },
+#else
+ { "CreateEvent", (SYSCALL)0, 0 },
+#endif
+
+#define osCreateEvent ( \
+ (HANDLE(WINAPI*) (LPSECURITY_ATTRIBUTES,BOOL,BOOL,LPCSTR)) \
+ aSyscall[80].pCurrent \
+)
+
+/*
+** If SQLITE_ENABLE_SETLK_TIMEOUT is defined, we require CancelIo()
+** for the case where a timeout expires and a lock request must be
+** cancelled.
+**
+** Minimum supported client: Windows XP [desktop apps | UWP apps]
+** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps]
+*/
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ { "CancelIo", (SYSCALL)CancelIo, 0 },
+#else
+ { "CancelIo", (SYSCALL)0, 0 },
+#endif
+
+#define osCancelIo ((BOOL(WINAPI*)(HANDLE))aSyscall[81].pCurrent)
+
+#if defined(SQLITE_WIN32_HAS_WIDE) && defined(_WIN32)
+ { "GetModuleHandleW", (SYSCALL)GetModuleHandleW, 0 },
+#else
+ { "GetModuleHandleW", (SYSCALL)0, 0 },
+#endif
+
+#define osGetModuleHandleW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[82].pCurrent)
+
+#ifndef _WIN32
+ { "getenv", (SYSCALL)getenv, 0 },
+#else
+ { "getenv", (SYSCALL)0, 0 },
+#endif
+
+#define osGetenv ((const char *(*)(const char *))aSyscall[83].pCurrent)
+
+#ifndef _WIN32
+ { "getcwd", (SYSCALL)getcwd, 0 },
+#else
+ { "getcwd", (SYSCALL)0, 0 },
+#endif
+
+#define osGetcwd ((char*(*)(char*,size_t))aSyscall[84].pCurrent)
+
+#ifndef _WIN32
+ { "readlink", (SYSCALL)readlink, 0 },
+#else
+ { "readlink", (SYSCALL)0, 0 },
+#endif
+
+#define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[85].pCurrent)
+
+#ifndef _WIN32
+ { "lstat", (SYSCALL)lstat, 0 },
+#else
+ { "lstat", (SYSCALL)0, 0 },
+#endif
+
+#define osLstat ((int(*)(const char*,struct stat*))aSyscall[86].pCurrent)
+
+#ifndef _WIN32
+ { "__errno", (SYSCALL)__errno, 0 },
+#else
+ { "__errno", (SYSCALL)0, 0 },
+#endif
+
+#define osErrno (*((int*(*)(void))aSyscall[87].pCurrent)())
+
+#ifndef _WIN32
+ { "cygwin_conv_path", (SYSCALL)cygwin_conv_path, 0 },
+#else
+ { "cygwin_conv_path", (SYSCALL)0, 0 },
+#endif
+
+#define osCygwin_conv_path ((size_t(*)(unsigned int, \
+ const void *, void *, size_t))aSyscall[88].pCurrent)
+
}; /* End of the overrideable system calls */
/*
@@ -48070,6 +48564,7 @@ SQLITE_API int sqlite3_win32_reset_heap(){
}
#endif /* SQLITE_WIN32_MALLOC */
+#ifdef _WIN32
/*
** This function outputs the specified (ANSI) string to the Win32 debugger
** (if available).
@@ -48112,6 +48607,7 @@ SQLITE_API void sqlite3_win32_write_debug(const char *zBuf, int nBuf){
}
#endif
}
+#endif /* _WIN32 */
/*
** The following routine suspends the current thread for at least ms
@@ -48195,7 +48691,9 @@ SQLITE_API int sqlite3_win32_is_nt(void){
}
return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2;
#elif SQLITE_TEST
- return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2;
+ return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2
+ || osInterlockedCompareExchange(&sqlite3_os_type, 0, 0)==0
+ ;
#else
/*
** NOTE: All sub-platforms where the GetVersionEx[AW] functions are
@@ -48410,6 +48908,7 @@ SQLITE_PRIVATE void sqlite3MemSetDefault(void){
}
#endif /* SQLITE_WIN32_MALLOC */
+#ifdef _WIN32
/*
** Convert a UTF-8 string to Microsoft Unicode.
**
@@ -48435,6 +48934,7 @@ static LPWSTR winUtf8ToUnicode(const char *zText){
}
return zWideText;
}
+#endif /* _WIN32 */
/*
** Convert a Microsoft Unicode string to UTF-8.
@@ -48469,28 +48969,29 @@ static char *winUnicodeToUtf8(LPCWSTR zWideText){
** Space to hold the returned string is obtained from sqlite3_malloc().
*/
static LPWSTR winMbcsToUnicode(const char *zText, int useAnsi){
- int nByte;
+ int nWideChar;
LPWSTR zMbcsText;
int codepage = useAnsi ? CP_ACP : CP_OEMCP;
- nByte = osMultiByteToWideChar(codepage, 0, zText, -1, NULL,
- 0)*sizeof(WCHAR);
- if( nByte==0 ){
+ nWideChar = osMultiByteToWideChar(codepage, 0, zText, -1, NULL,
+ 0);
+ if( nWideChar==0 ){
return 0;
}
- zMbcsText = sqlite3MallocZero( nByte*sizeof(WCHAR) );
+ zMbcsText = sqlite3MallocZero( nWideChar*sizeof(WCHAR) );
if( zMbcsText==0 ){
return 0;
}
- nByte = osMultiByteToWideChar(codepage, 0, zText, -1, zMbcsText,
- nByte);
- if( nByte==0 ){
+ nWideChar = osMultiByteToWideChar(codepage, 0, zText, -1, zMbcsText,
+ nWideChar);
+ if( nWideChar==0 ){
sqlite3_free(zMbcsText);
zMbcsText = 0;
}
return zMbcsText;
}
+#ifdef _WIN32
/*
** Convert a Microsoft Unicode string to a multi-byte character string,
** using the ANSI or OEM code page.
@@ -48518,6 +49019,7 @@ static char *winUnicodeToMbcs(LPCWSTR zWideText, int useAnsi){
}
return zText;
}
+#endif /* _WIN32 */
/*
** Convert a multi-byte character string to UTF-8.
@@ -48537,6 +49039,7 @@ static char *winMbcsToUtf8(const char *zText, int useAnsi){
return zTextUtf8;
}
+#ifdef _WIN32
/*
** Convert a UTF-8 string to a multi-byte character string.
**
@@ -48586,6 +49089,7 @@ SQLITE_API char *sqlite3_win32_unicode_to_utf8(LPCWSTR zWideText){
#endif
return winUnicodeToUtf8(zWideText);
}
+#endif /* _WIN32 */
/*
** This is a public wrapper for the winMbcsToUtf8() function.
@@ -48603,6 +49107,7 @@ SQLITE_API char *sqlite3_win32_mbcs_to_utf8(const char *zText){
return winMbcsToUtf8(zText, osAreFileApisANSI());
}
+#ifdef _WIN32
/*
** This is a public wrapper for the winMbcsToUtf8() function.
*/
@@ -48727,6 +49232,7 @@ SQLITE_API int sqlite3_win32_set_directory(
){
return sqlite3_win32_set_directory16(type, zValue);
}
+#endif /* _WIN32 */
/*
** The return value of winGetLastErrorMsg
@@ -49275,14 +49781,95 @@ static BOOL winLockFile(
ovlp.Offset = offsetLow;
ovlp.OffsetHigh = offsetHigh;
return osLockFileEx(*phFile, flags, 0, numBytesLow, numBytesHigh, &ovlp);
+#ifdef SQLITE_WIN32_HAS_ANSI
}else{
return osLockFile(*phFile, offsetLow, offsetHigh, numBytesLow,
numBytesHigh);
+#endif
}
#endif
}
/*
+** Lock a region of nByte bytes starting at offset offset of file hFile.
+** Take an EXCLUSIVE lock if parameter bExclusive is true, or a SHARED lock
+** otherwise. If nMs is greater than zero and the lock cannot be obtained
+** immediately, block for that many ms before giving up.
+**
+** This function returns SQLITE_OK if the lock is obtained successfully. If
+** some other process holds the lock, SQLITE_BUSY is returned if nMs==0, or
+** SQLITE_BUSY_TIMEOUT otherwise. Or, if an error occurs, SQLITE_IOERR.
+*/
+static int winHandleLockTimeout(
+ HANDLE hFile,
+ DWORD offset,
+ DWORD nByte,
+ int bExcl,
+ DWORD nMs
+){
+ DWORD flags = LOCKFILE_FAIL_IMMEDIATELY | (bExcl?LOCKFILE_EXCLUSIVE_LOCK:0);
+ int rc = SQLITE_OK;
+ BOOL ret;
+
+ if( !osIsNT() ){
+ ret = winLockFile(&hFile, flags, offset, 0, nByte, 0);
+ }else{
+ OVERLAPPED ovlp;
+ memset(&ovlp, 0, sizeof(OVERLAPPED));
+ ovlp.Offset = offset;
+
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ if( nMs!=0 ){
+ flags &= ~LOCKFILE_FAIL_IMMEDIATELY;
+ }
+ ovlp.hEvent = osCreateEvent(NULL, TRUE, FALSE, NULL);
+ if( ovlp.hEvent==NULL ){
+ return SQLITE_IOERR_LOCK;
+ }
+#endif
+
+ ret = osLockFileEx(hFile, flags, 0, nByte, 0, &ovlp);
+
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ /* If SQLITE_ENABLE_SETLK_TIMEOUT is defined, then the file-handle was
+ ** opened with FILE_FLAG_OVERHEAD specified. In this case, the call to
+ ** LockFileEx() may fail because the request is still pending. This can
+ ** happen even if LOCKFILE_FAIL_IMMEDIATELY was specified.
+ **
+ ** If nMs is 0, then LOCKFILE_FAIL_IMMEDIATELY was set in the flags
+ ** passed to LockFileEx(). In this case, if the operation is pending,
+ ** block indefinitely until it is finished.
+ **
+ ** Otherwise, wait for up to nMs ms for the operation to finish. nMs
+ ** may be set to INFINITE.
+ */
+ if( !ret && GetLastError()==ERROR_IO_PENDING ){
+ DWORD nDelay = (nMs==0 ? INFINITE : nMs);
+ DWORD res = osWaitForSingleObject(ovlp.hEvent, nDelay);
+ if( res==WAIT_OBJECT_0 ){
+ ret = TRUE;
+ }else if( res==WAIT_TIMEOUT ){
+ rc = SQLITE_BUSY_TIMEOUT;
+ }else{
+ /* Some other error has occurred */
+ rc = SQLITE_IOERR_LOCK;
+ }
+
+ /* If it is still pending, cancel the LockFileEx() call. */
+ osCancelIo(hFile);
+ }
+
+ osCloseHandle(ovlp.hEvent);
+#endif
+ }
+
+ if( rc==SQLITE_OK && !ret ){
+ rc = SQLITE_BUSY;
+ }
+ return rc;
+}
+
+/*
** Unlock a file region.
*/
static BOOL winUnlockFile(
@@ -49306,13 +49893,23 @@ static BOOL winUnlockFile(
ovlp.Offset = offsetLow;
ovlp.OffsetHigh = offsetHigh;
return osUnlockFileEx(*phFile, 0, numBytesLow, numBytesHigh, &ovlp);
+#ifdef SQLITE_WIN32_HAS_ANSI
}else{
return osUnlockFile(*phFile, offsetLow, offsetHigh, numBytesLow,
numBytesHigh);
+#endif
}
#endif
}
+/*
+** Remove an nByte lock starting at offset iOff from HANDLE h.
+*/
+static int winHandleUnlock(HANDLE h, int iOff, int nByte){
+ BOOL ret = winUnlockFile(&h, iOff, 0, nByte, 0);
+ return (ret ? SQLITE_OK : SQLITE_IOERR_UNLOCK);
+}
+
/*****************************************************************************
** The next group of routines implement the I/O methods specified
** by the sqlite3_io_methods object.
@@ -49326,66 +49923,70 @@ static BOOL winUnlockFile(
#endif
/*
-** Move the current position of the file handle passed as the first
-** argument to offset iOffset within the file. If successful, return 0.
-** Otherwise, set pFile->lastErrno and return non-zero.
+** Seek the file handle h to offset nByte of the file.
+**
+** If successful, return SQLITE_OK. Or, if an error occurs, return an SQLite
+** error code.
*/
-static int winSeekFile(winFile *pFile, sqlite3_int64 iOffset){
+static int winHandleSeek(HANDLE h, sqlite3_int64 iOffset){
+ int rc = SQLITE_OK; /* Return value */
+
#if !SQLITE_OS_WINRT
LONG upperBits; /* Most sig. 32 bits of new offset */
LONG lowerBits; /* Least sig. 32 bits of new offset */
DWORD dwRet; /* Value returned by SetFilePointer() */
- DWORD lastErrno; /* Value returned by GetLastError() */
-
- OSTRACE(("SEEK file=%p, offset=%lld\n", pFile->h, iOffset));
upperBits = (LONG)((iOffset>>32) & 0x7fffffff);
lowerBits = (LONG)(iOffset & 0xffffffff);
+ dwRet = osSetFilePointer(h, lowerBits, &upperBits, FILE_BEGIN);
+
/* API oddity: If successful, SetFilePointer() returns a dword
** containing the lower 32-bits of the new file-offset. Or, if it fails,
** it returns INVALID_SET_FILE_POINTER. However according to MSDN,
** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine
** whether an error has actually occurred, it is also necessary to call
- ** GetLastError().
- */
- dwRet = osSetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN);
-
- if( (dwRet==INVALID_SET_FILE_POINTER
- && ((lastErrno = osGetLastError())!=NO_ERROR)) ){
- pFile->lastErrno = lastErrno;
- winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno,
- "winSeekFile", pFile->zPath);
- OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h));
- return 1;
+ ** GetLastError(). */
+ if( dwRet==INVALID_SET_FILE_POINTER ){
+ DWORD lastErrno = osGetLastError();
+ if( lastErrno!=NO_ERROR ){
+ rc = SQLITE_IOERR_SEEK;
+ }
}
-
- OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h));
- return 0;
#else
- /*
- ** Same as above, except that this implementation works for WinRT.
- */
-
+ /* This implementation works for WinRT. */
LARGE_INTEGER x; /* The new offset */
BOOL bRet; /* Value returned by SetFilePointerEx() */
x.QuadPart = iOffset;
- bRet = osSetFilePointerEx(pFile->h, x, 0, FILE_BEGIN);
+ bRet = osSetFilePointerEx(h, x, 0, FILE_BEGIN);
if(!bRet){
- pFile->lastErrno = osGetLastError();
- winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno,
- "winSeekFile", pFile->zPath);
- OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h));
- return 1;
+ rc = SQLITE_IOERR_SEEK;
}
-
- OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h));
- return 0;
#endif
+
+ OSTRACE(("SEEK file=%p, offset=%lld rc=%s\n", h, iOffset, sqlite3ErrName(rc)));
+ return rc;
+}
+
+/*
+** Move the current position of the file handle passed as the first
+** argument to offset iOffset within the file. If successful, return 0.
+** Otherwise, set pFile->lastErrno and return non-zero.
+*/
+static int winSeekFile(winFile *pFile, sqlite3_int64 iOffset){
+ int rc;
+
+ rc = winHandleSeek(pFile->h, iOffset);
+ if( rc!=SQLITE_OK ){
+ pFile->lastErrno = osGetLastError();
+ winLogError(rc, pFile->lastErrno, "winSeekFile", pFile->zPath);
+ }
+ return rc;
}
+
#if SQLITE_MAX_MMAP_SIZE>0
/* Forward references to VFS helper methods used for memory mapped files */
static int winMapfile(winFile*, sqlite3_int64);
@@ -49646,6 +50247,60 @@ static int winWrite(
}
/*
+** Truncate the file opened by handle h to nByte bytes in size.
+*/
+static int winHandleTruncate(HANDLE h, sqlite3_int64 nByte){
+ int rc = SQLITE_OK; /* Return code */
+ rc = winHandleSeek(h, nByte);
+ if( rc==SQLITE_OK ){
+ if( 0==osSetEndOfFile(h) ){
+ rc = SQLITE_IOERR_TRUNCATE;
+ }
+ }
+ return rc;
+}
+
+/*
+** Determine the size in bytes of the file opened by the handle passed as
+** the first argument.
+*/
+static int winHandleSize(HANDLE h, sqlite3_int64 *pnByte){
+ int rc = SQLITE_OK;
+
+#if SQLITE_OS_WINRT
+ FILE_STANDARD_INFO info;
+ BOOL b;
+ b = osGetFileInformationByHandleEx(h, FileStandardInfo, &info, sizeof(info));
+ if( b ){
+ *pnByte = info.EndOfFile.QuadPart;
+ }else{
+ rc = SQLITE_IOERR_FSTAT;
+ }
+#else
+ DWORD upperBits = 0;
+ DWORD lowerBits = 0;
+
+ assert( pnByte );
+ lowerBits = osGetFileSize(h, &upperBits);
+ *pnByte = (((sqlite3_int64)upperBits)<<32) + lowerBits;
+ if( lowerBits==INVALID_FILE_SIZE && osGetLastError()!=NO_ERROR ){
+ rc = SQLITE_IOERR_FSTAT;
+ }
+#endif
+
+ return rc;
+}
+
+/*
+** Close the handle passed as the only argument.
+*/
+static void winHandleClose(HANDLE h){
+ if( h!=INVALID_HANDLE_VALUE ){
+ osCloseHandle(h);
+ }
+}
+
+/*
** Truncate an open file to a specified size
*/
static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
@@ -49900,8 +50555,9 @@ static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){
** Different API routines are called depending on whether or not this
** is Win9x or WinNT.
*/
-static int winGetReadLock(winFile *pFile){
+static int winGetReadLock(winFile *pFile, int bBlock){
int res;
+ DWORD mask = ~(bBlock ? LOCKFILE_FAIL_IMMEDIATELY : 0);
OSTRACE(("READ-LOCK file=%p, lock=%d\n", pFile->h, pFile->locktype));
if( osIsNT() ){
#if SQLITE_OS_WINCE
@@ -49911,7 +50567,7 @@ static int winGetReadLock(winFile *pFile){
*/
res = winceLockFile(&pFile->h, SHARED_FIRST, 0, 1, 0);
#else
- res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS, SHARED_FIRST, 0,
+ res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS&mask, SHARED_FIRST, 0,
SHARED_SIZE, 0);
#endif
}
@@ -49920,7 +50576,7 @@ static int winGetReadLock(winFile *pFile){
int lk;
sqlite3_randomness(sizeof(lk), &lk);
pFile->sharedLockByte = (short)((lk & 0x7fffffff)%(SHARED_SIZE - 1));
- res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS,
+ res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS&mask,
SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0);
}
#endif
@@ -50015,46 +50671,62 @@ static int winLock(sqlite3_file *id, int locktype){
assert( locktype!=PENDING_LOCK );
assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK );
- /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or
+ /* Lock the PENDING_LOCK byte if we need to acquire an EXCLUSIVE lock or
** a SHARED lock. If we are acquiring a SHARED lock, the acquisition of
** the PENDING_LOCK byte is temporary.
*/
newLocktype = pFile->locktype;
- if( pFile->locktype==NO_LOCK
- || (locktype==EXCLUSIVE_LOCK && pFile->locktype<=RESERVED_LOCK)
+ if( locktype==SHARED_LOCK
+ || (locktype==EXCLUSIVE_LOCK && pFile->locktype==RESERVED_LOCK)
){
int cnt = 3;
- while( cnt-->0 && (res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS,
- PENDING_BYTE, 0, 1, 0))==0 ){
+
+ /* Flags for the LockFileEx() call. This should be an exclusive lock if
+ ** this call is to obtain EXCLUSIVE, or a shared lock if this call is to
+ ** obtain SHARED. */
+ int flags = LOCKFILE_FAIL_IMMEDIATELY;
+ if( locktype==EXCLUSIVE_LOCK ){
+ flags |= LOCKFILE_EXCLUSIVE_LOCK;
+ }
+ while( cnt>0 ){
/* Try 3 times to get the pending lock. This is needed to work
** around problems caused by indexing and/or anti-virus software on
** Windows systems.
+ **
** If you are using this code as a model for alternative VFSes, do not
- ** copy this retry logic. It is a hack intended for Windows only.
- */
+ ** copy this retry logic. It is a hack intended for Windows only. */
+ res = winLockFile(&pFile->h, flags, PENDING_BYTE, 0, 1, 0);
+ if( res ) break;
+
lastErrno = osGetLastError();
OSTRACE(("LOCK-PENDING-FAIL file=%p, count=%d, result=%d\n",
- pFile->h, cnt, res));
+ pFile->h, cnt, res
+ ));
+
if( lastErrno==ERROR_INVALID_HANDLE ){
pFile->lastErrno = lastErrno;
rc = SQLITE_IOERR_LOCK;
OSTRACE(("LOCK-FAIL file=%p, count=%d, rc=%s\n",
- pFile->h, cnt, sqlite3ErrName(rc)));
+ pFile->h, cnt, sqlite3ErrName(rc)
+ ));
return rc;
}
- if( cnt ) sqlite3_win32_sleep(1);
+
+ cnt--;
+ if( cnt>0 ) sqlite3_win32_sleep(1);
}
gotPendingLock = res;
- if( !res ){
- lastErrno = osGetLastError();
- }
}
/* Acquire a shared lock
*/
if( locktype==SHARED_LOCK && res ){
assert( pFile->locktype==NO_LOCK );
- res = winGetReadLock(pFile);
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ res = winGetReadLock(pFile, pFile->bBlockOnConnect);
+#else
+ res = winGetReadLock(pFile, 0);
+#endif
if( res ){
newLocktype = SHARED_LOCK;
}else{
@@ -50092,7 +50764,7 @@ static int winLock(sqlite3_file *id, int locktype){
newLocktype = EXCLUSIVE_LOCK;
}else{
lastErrno = osGetLastError();
- winGetReadLock(pFile);
+ winGetReadLock(pFile, 0);
}
}
@@ -50172,7 +50844,7 @@ static int winUnlock(sqlite3_file *id, int locktype){
type = pFile->locktype;
if( type>=EXCLUSIVE_LOCK ){
winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
- if( locktype==SHARED_LOCK && !winGetReadLock(pFile) ){
+ if( locktype==SHARED_LOCK && !winGetReadLock(pFile, 0) ){
/* This should never happen. We should always be able to
** reacquire the read lock */
rc = winLogError(SQLITE_IOERR_UNLOCK, osGetLastError(),
@@ -50341,6 +51013,11 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
return SQLITE_OK;
}
#endif
+ case SQLITE_FCNTL_NULL_IO: {
+ (void)osCloseHandle(pFile->h);
+ pFile->h = NULL;
+ return SQLITE_OK;
+ }
case SQLITE_FCNTL_TEMPFILENAME: {
char *zTFile = 0;
int rc = winGetTempname(pFile->pVfs, &zTFile);
@@ -50377,6 +51054,28 @@ static int winFileControl(sqlite3_file *id, int op, void *pArg){
return rc;
}
#endif
+
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ case SQLITE_FCNTL_LOCK_TIMEOUT: {
+ int iOld = pFile->iBusyTimeout;
+ int iNew = *(int*)pArg;
+#if SQLITE_ENABLE_SETLK_TIMEOUT==1
+ pFile->iBusyTimeout = (iNew < 0) ? INFINITE : (DWORD)iNew;
+#elif SQLITE_ENABLE_SETLK_TIMEOUT==2
+ pFile->iBusyTimeout = (DWORD)(!!iNew);
+#else
+# error "SQLITE_ENABLE_SETLK_TIMEOUT must be set to 1 or 2"
+#endif
+ *(int*)pArg = iOld;
+ return SQLITE_OK;
+ }
+ case SQLITE_FCNTL_BLOCK_ON_CONNECT: {
+ int iNew = *(int*)pArg;
+ pFile->bBlockOnConnect = iNew;
+ return SQLITE_OK;
+ }
+#endif /* SQLITE_ENABLE_SETLK_TIMEOUT */
+
}
OSTRACE(("FCNTL file=%p, rc=SQLITE_NOTFOUND\n", pFile->h));
return SQLITE_NOTFOUND;
@@ -50457,23 +51156,27 @@ static int winShmMutexHeld(void) {
**
** The following fields are read-only after the object is created:
**
-** fid
** zFilename
**
** Either winShmNode.mutex must be held or winShmNode.nRef==0 and
** winShmMutexHeld() is true when reading or writing any other field
** in this structure.
**
+** File-handle hSharedShm is used to (a) take the DMS lock, (b) truncate
+** the *-shm file if the DMS-locking protocol demands it, and (c) map
+** regions of the *-shm file into memory using MapViewOfFile() or
+** similar. Other locks are taken by individual clients using the
+** winShm.hShm handles.
*/
struct winShmNode {
sqlite3_mutex *mutex; /* Mutex to access this object */
char *zFilename; /* Name of the file */
- winFile hFile; /* File handle from winOpen */
+ HANDLE hSharedShm; /* File handle open on zFilename */
+ int isUnlocked; /* DMS lock has not yet been obtained */
+ int isReadonly; /* True if read-only */
int szRegion; /* Size of shared-memory regions */
int nRegion; /* Size of array apRegion */
- u8 isReadonly; /* True if read-only */
- u8 isUnlocked; /* True if no DMS lock held */
struct ShmRegion {
HANDLE hMap; /* File handle from CreateFileMapping */
@@ -50482,7 +51185,6 @@ struct winShmNode {
DWORD lastErrno; /* The Windows errno from the last I/O error */
int nRef; /* Number of winShm objects pointing to this */
- winShm *pFirst; /* All winShm objects pointing to this */
winShmNode *pNext; /* Next in list of all winShmNode objects */
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
u8 nextShmId; /* Next available winShm.id value */
@@ -50498,23 +51200,15 @@ static winShmNode *winShmNodeList = 0;
/*
** Structure used internally by this VFS to record the state of an
-** open shared memory connection.
-**
-** The following fields are initialized when this object is created and
-** are read-only thereafter:
-**
-** winShm.pShmNode
-** winShm.id
-**
-** All other fields are read/write. The winShm.pShmNode->mutex must be held
-** while accessing any read/write fields.
+** open shared memory connection. There is one such structure for each
+** winFile open on a wal mode database.
*/
struct winShm {
winShmNode *pShmNode; /* The underlying winShmNode object */
- winShm *pNext; /* Next winShm with the same winShmNode */
- u8 hasMutex; /* True if holding the winShmNode mutex */
u16 sharedMask; /* Mask of shared locks held */
u16 exclMask; /* Mask of exclusive locks held */
+ HANDLE hShm; /* File-handle on *-shm file. For locking. */
+ int bReadonly; /* True if hShm is opened read-only */
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
u8 id; /* Id of this connection with its winShmNode */
#endif
@@ -50526,50 +51220,6 @@ struct winShm {
#define WIN_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */
#define WIN_SHM_DMS (WIN_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */
-/*
-** Apply advisory locks for all n bytes beginning at ofst.
-*/
-#define WINSHM_UNLCK 1
-#define WINSHM_RDLCK 2
-#define WINSHM_WRLCK 3
-static int winShmSystemLock(
- winShmNode *pFile, /* Apply locks to this open shared-memory segment */
- int lockType, /* WINSHM_UNLCK, WINSHM_RDLCK, or WINSHM_WRLCK */
- int ofst, /* Offset to first byte to be locked/unlocked */
- int nByte /* Number of bytes to lock or unlock */
-){
- int rc = 0; /* Result code form Lock/UnlockFileEx() */
-
- /* Access to the winShmNode object is serialized by the caller */
- assert( pFile->nRef==0 || sqlite3_mutex_held(pFile->mutex) );
-
- OSTRACE(("SHM-LOCK file=%p, lock=%d, offset=%d, size=%d\n",
- pFile->hFile.h, lockType, ofst, nByte));
-
- /* Release/Acquire the system-level lock */
- if( lockType==WINSHM_UNLCK ){
- rc = winUnlockFile(&pFile->hFile.h, ofst, 0, nByte, 0);
- }else{
- /* Initialize the locking parameters */
- DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY;
- if( lockType == WINSHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK;
- rc = winLockFile(&pFile->hFile.h, dwFlags, ofst, 0, nByte, 0);
- }
-
- if( rc!= 0 ){
- rc = SQLITE_OK;
- }else{
- pFile->lastErrno = osGetLastError();
- rc = SQLITE_BUSY;
- }
-
- OSTRACE(("SHM-LOCK file=%p, func=%s, errno=%lu, rc=%s\n",
- pFile->hFile.h, (lockType == WINSHM_UNLCK) ? "winUnlockFile" :
- "winLockFile", pFile->lastErrno, sqlite3ErrName(rc)));
-
- return rc;
-}
-
/* Forward references to VFS methods */
static int winOpen(sqlite3_vfs*,const char*,sqlite3_file*,int,int*);
static int winDelete(sqlite3_vfs *,const char*,int);
@@ -50601,11 +51251,7 @@ static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
UNUSED_VARIABLE_VALUE(bRc);
}
- if( p->hFile.h!=NULL && p->hFile.h!=INVALID_HANDLE_VALUE ){
- SimulateIOErrorBenign(1);
- winClose((sqlite3_file *)&p->hFile);
- SimulateIOErrorBenign(0);
- }
+ winHandleClose(p->hSharedShm);
if( deleteFlag ){
SimulateIOErrorBenign(1);
sqlite3BeginBenignMalloc();
@@ -50623,42 +51269,239 @@ static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
}
/*
-** The DMS lock has not yet been taken on shm file pShmNode. Attempt to
-** take it now. Return SQLITE_OK if successful, or an SQLite error
-** code otherwise.
-**
-** If the DMS cannot be locked because this is a readonly_shm=1
-** connection and no other process already holds a lock, return
-** SQLITE_READONLY_CANTINIT and set pShmNode->isUnlocked=1.
+** The DMS lock has not yet been taken on the shm file associated with
+** pShmNode. Take the lock. Truncate the *-shm file if required.
+** Return SQLITE_OK if successful, or an SQLite error code otherwise.
*/
-static int winLockSharedMemory(winShmNode *pShmNode){
- int rc = winShmSystemLock(pShmNode, WINSHM_WRLCK, WIN_SHM_DMS, 1);
+static int winLockSharedMemory(winShmNode *pShmNode, DWORD nMs){
+ HANDLE h = pShmNode->hSharedShm;
+ int rc = SQLITE_OK;
+ assert( sqlite3_mutex_held(pShmNode->mutex) );
+ rc = winHandleLockTimeout(h, WIN_SHM_DMS, 1, 1, 0);
if( rc==SQLITE_OK ){
+ /* We have an EXCLUSIVE lock on the DMS byte. This means that this
+ ** is the first process to open the file. Truncate it to zero bytes
+ ** in this case. */
if( pShmNode->isReadonly ){
- pShmNode->isUnlocked = 1;
- winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
- return SQLITE_READONLY_CANTINIT;
- }else if( winTruncate((sqlite3_file*)&pShmNode->hFile, 0) ){
- winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
- return winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(),
- "winLockSharedMemory", pShmNode->zFilename);
+ rc = SQLITE_READONLY_CANTINIT;
+ }else{
+ rc = winHandleTruncate(h, 0);
}
+
+ /* Release the EXCLUSIVE lock acquired above. */
+ winUnlockFile(&h, WIN_SHM_DMS, 0, 1, 0);
+ }else if( (rc & 0xFF)==SQLITE_BUSY ){
+ rc = SQLITE_OK;
}
if( rc==SQLITE_OK ){
- winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
+ /* Take a SHARED lock on the DMS byte. */
+ rc = winHandleLockTimeout(h, WIN_SHM_DMS, 1, 0, nMs);
+ if( rc==SQLITE_OK ){
+ pShmNode->isUnlocked = 0;
+ }
}
- return winShmSystemLock(pShmNode, WINSHM_RDLCK, WIN_SHM_DMS, 1);
+ return rc;
}
+
/*
-** Open the shared-memory area associated with database file pDbFd.
+** Convert a UTF-8 filename into whatever form the underlying
+** operating system wants filenames in. Space to hold the result
+** is obtained from malloc and must be freed by the calling
+** function
+**
+** On Cygwin, 3 possible input forms are accepted:
+** - If the filename starts with "<drive>:/" or "<drive>:\",
+** it is converted to UTF-16 as-is.
+** - If the filename contains '/', it is assumed to be a
+** Cygwin absolute path, it is converted to a win32
+** absolute path in UTF-16.
+** - Otherwise it must be a filename only, the win32 filename
+** is returned in UTF-16.
+** Note: If the function cygwin_conv_path() fails, only
+** UTF-8 -> UTF-16 conversion will be done. This can only
+** happen when the file path >32k, in which case winUtf8ToUnicode()
+** will fail too.
+*/
+static void *winConvertFromUtf8Filename(const char *zFilename){
+ void *zConverted = 0;
+ if( osIsNT() ){
+#ifdef __CYGWIN__
+ int nChar;
+ LPWSTR zWideFilename;
+
+ if( osCygwin_conv_path && !(winIsDriveLetterAndColon(zFilename)
+ && winIsDirSep(zFilename[2])) ){
+ i64 nByte;
+ int convertflag = CCP_POSIX_TO_WIN_W;
+ if( !strchr(zFilename, '/') ) convertflag |= CCP_RELATIVE;
+ nByte = (i64)osCygwin_conv_path(convertflag,
+ zFilename, 0, 0);
+ if( nByte>0 ){
+ zConverted = sqlite3MallocZero(12+(u64)nByte);
+ if ( zConverted==0 ){
+ return zConverted;
+ }
+ zWideFilename = zConverted;
+ /* Filenames should be prefixed, except when converted
+ * full path already starts with "\\?\". */
+ if( osCygwin_conv_path(convertflag, zFilename,
+ zWideFilename+4, nByte)==0 ){
+ if( (convertflag&CCP_RELATIVE) ){
+ memmove(zWideFilename, zWideFilename+4, nByte);
+ }else if( memcmp(zWideFilename+4, L"\\\\", 4) ){
+ memcpy(zWideFilename, L"\\\\?\\", 8);
+ }else if( zWideFilename[6]!='?' ){
+ memmove(zWideFilename+6, zWideFilename+4, nByte);
+ memcpy(zWideFilename, L"\\\\?\\UNC", 14);
+ }else{
+ memmove(zWideFilename, zWideFilename+4, nByte);
+ }
+ return zConverted;
+ }
+ sqlite3_free(zConverted);
+ }
+ }
+ nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0);
+ if( nChar==0 ){
+ return 0;
+ }
+ zWideFilename = sqlite3MallocZero( nChar*sizeof(WCHAR)+12 );
+ if( zWideFilename==0 ){
+ return 0;
+ }
+ nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1,
+ zWideFilename, nChar);
+ if( nChar==0 ){
+ sqlite3_free(zWideFilename);
+ zWideFilename = 0;
+ }else if( nChar>MAX_PATH
+ && winIsDriveLetterAndColon(zFilename)
+ && winIsDirSep(zFilename[2]) ){
+ memmove(zWideFilename+4, zWideFilename, nChar*sizeof(WCHAR));
+ zWideFilename[2] = '\\';
+ memcpy(zWideFilename, L"\\\\?\\", 8);
+ }else if( nChar>MAX_PATH
+ && winIsDirSep(zFilename[0]) && winIsDirSep(zFilename[1])
+ && zFilename[2] != '?' ){
+ memmove(zWideFilename+6, zWideFilename, nChar*sizeof(WCHAR));
+ memcpy(zWideFilename, L"\\\\?\\UNC", 14);
+ }
+ zConverted = zWideFilename;
+#else
+ zConverted = winUtf8ToUnicode(zFilename);
+#endif /* __CYGWIN__ */
+ }
+#if defined(SQLITE_WIN32_HAS_ANSI) && defined(_WIN32)
+ else{
+ zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI());
+ }
+#endif
+ /* caller will handle out of memory */
+ return zConverted;
+}
+
+/*
+** This function is used to open a handle on a *-shm file.
**
-** When opening a new shared-memory file, if no other instances of that
-** file are currently open, in this process or in other processes, then
-** the file must be truncated to zero length or have its header cleared.
+** If SQLITE_ENABLE_SETLK_TIMEOUT is defined at build time, then the file
+** is opened with FILE_FLAG_OVERLAPPED specified. If not, it is not.
+*/
+static int winHandleOpen(
+ const char *zUtf8, /* File to open */
+ int *pbReadonly, /* IN/OUT: True for readonly handle */
+ HANDLE *ph /* OUT: New HANDLE for file */
+){
+ int rc = SQLITE_OK;
+ void *zConverted = 0;
+ int bReadonly = *pbReadonly;
+ HANDLE h = INVALID_HANDLE_VALUE;
+
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ const DWORD flag_overlapped = FILE_FLAG_OVERLAPPED;
+#else
+ const DWORD flag_overlapped = 0;
+#endif
+
+ /* Convert the filename to the system encoding. */
+ zConverted = winConvertFromUtf8Filename(zUtf8);
+ if( zConverted==0 ){
+ OSTRACE(("OPEN name=%s, rc=SQLITE_IOERR_NOMEM", zUtf8));
+ rc = SQLITE_IOERR_NOMEM_BKPT;
+ goto winopenfile_out;
+ }
+
+ /* Ensure the file we are trying to open is not actually a directory. */
+ if( winIsDir(zConverted) ){
+ OSTRACE(("OPEN name=%s, rc=SQLITE_CANTOPEN_ISDIR", zUtf8));
+ rc = SQLITE_CANTOPEN_ISDIR;
+ goto winopenfile_out;
+ }
+
+ /* TODO: platforms.
+ ** TODO: retry-on-ioerr.
+ */
+ if( osIsNT() ){
+#if SQLITE_OS_WINRT
+ CREATEFILE2_EXTENDED_PARAMETERS extendedParameters;
+ memset(&extendedParameters, 0, sizeof(extendedParameters));
+ extendedParameters.dwSize = sizeof(extendedParameters);
+ extendedParameters.dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
+ extendedParameters.dwFileFlags = flag_overlapped;
+ extendedParameters.dwSecurityQosFlags = SECURITY_ANONYMOUS;
+ h = osCreateFile2((LPCWSTR)zConverted,
+ (GENERIC_READ | (bReadonly ? 0 : GENERIC_WRITE)),/* dwDesiredAccess */
+ FILE_SHARE_READ | FILE_SHARE_WRITE, /* dwShareMode */
+ OPEN_ALWAYS, /* dwCreationDisposition */
+ &extendedParameters
+ );
+#else
+ h = osCreateFileW((LPCWSTR)zConverted, /* lpFileName */
+ (GENERIC_READ | (bReadonly ? 0 : GENERIC_WRITE)), /* dwDesiredAccess */
+ FILE_SHARE_READ | FILE_SHARE_WRITE, /* dwShareMode */
+ NULL, /* lpSecurityAttributes */
+ OPEN_ALWAYS, /* dwCreationDisposition */
+ FILE_ATTRIBUTE_NORMAL|flag_overlapped,
+ NULL
+ );
+#endif
+ }else{
+ /* Due to pre-processor directives earlier in this file,
+ ** SQLITE_WIN32_HAS_ANSI is always defined if osIsNT() is false. */
+#ifdef SQLITE_WIN32_HAS_ANSI
+ h = osCreateFileA((LPCSTR)zConverted,
+ (GENERIC_READ | (bReadonly ? 0 : GENERIC_WRITE)), /* dwDesiredAccess */
+ FILE_SHARE_READ | FILE_SHARE_WRITE, /* dwShareMode */
+ NULL, /* lpSecurityAttributes */
+ OPEN_ALWAYS, /* dwCreationDisposition */
+ FILE_ATTRIBUTE_NORMAL|flag_overlapped,
+ NULL
+ );
+#endif
+ }
+
+ if( h==INVALID_HANDLE_VALUE ){
+ if( bReadonly==0 ){
+ bReadonly = 1;
+ rc = winHandleOpen(zUtf8, &bReadonly, &h);
+ }else{
+ rc = SQLITE_CANTOPEN_BKPT;
+ }
+ }
+
+ winopenfile_out:
+ sqlite3_free(zConverted);
+ *pbReadonly = bReadonly;
+ *ph = h;
+ return rc;
+}
+
+
+/*
+** Open the shared-memory area associated with database file pDbFd.
*/
static int winOpenSharedMemory(winFile *pDbFd){
struct winShm *p; /* The connection to be opened */
@@ -50670,98 +51513,83 @@ static int winOpenSharedMemory(winFile *pDbFd){
assert( pDbFd->pShm==0 ); /* Not previously opened */
/* Allocate space for the new sqlite3_shm object. Also speculatively
- ** allocate space for a new winShmNode and filename.
- */
+ ** allocate space for a new winShmNode and filename. */
p = sqlite3MallocZero( sizeof(*p) );
if( p==0 ) return SQLITE_IOERR_NOMEM_BKPT;
nName = sqlite3Strlen30(pDbFd->zPath);
- pNew = sqlite3MallocZero( sizeof(*pShmNode) + nName + 17 );
+ pNew = sqlite3MallocZero( sizeof(*pShmNode) + (i64)nName + 17 );
if( pNew==0 ){
sqlite3_free(p);
return SQLITE_IOERR_NOMEM_BKPT;
}
pNew->zFilename = (char*)&pNew[1];
+ pNew->hSharedShm = INVALID_HANDLE_VALUE;
+ pNew->isUnlocked = 1;
sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath);
sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename);
+ /* Open a file-handle on the *-shm file for this connection. This file-handle
+ ** is only used for locking. The mapping of the *-shm file is created using
+ ** the shared file handle in winShmNode.hSharedShm. */
+ p->bReadonly = sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0);
+ rc = winHandleOpen(pNew->zFilename, &p->bReadonly, &p->hShm);
+
/* Look to see if there is an existing winShmNode that can be used.
- ** If no matching winShmNode currently exists, create a new one.
- */
+ ** If no matching winShmNode currently exists, then create a new one. */
winShmEnterMutex();
for(pShmNode = winShmNodeList; pShmNode; pShmNode=pShmNode->pNext){
/* TBD need to come up with better match here. Perhaps
- ** use FILE_ID_BOTH_DIR_INFO Structure.
- */
+ ** use FILE_ID_BOTH_DIR_INFO Structure. */
if( sqlite3StrICmp(pShmNode->zFilename, pNew->zFilename)==0 ) break;
}
- if( pShmNode ){
- sqlite3_free(pNew);
- }else{
- int inFlags = SQLITE_OPEN_WAL;
- int outFlags = 0;
-
+ if( pShmNode==0 ){
pShmNode = pNew;
- pNew = 0;
- ((winFile*)(&pShmNode->hFile))->h = INVALID_HANDLE_VALUE;
- pShmNode->pNext = winShmNodeList;
- winShmNodeList = pShmNode;
+ /* Allocate a mutex for this winShmNode object, if one is required. */
if( sqlite3GlobalConfig.bCoreMutex ){
pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
- if( pShmNode->mutex==0 ){
- rc = SQLITE_IOERR_NOMEM_BKPT;
- goto shm_open_err;
- }
+ if( pShmNode->mutex==0 ) rc = SQLITE_IOERR_NOMEM_BKPT;
}
- if( 0==sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){
- inFlags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
- }else{
- inFlags |= SQLITE_OPEN_READONLY;
- }
- rc = winOpen(pDbFd->pVfs, pShmNode->zFilename,
- (sqlite3_file*)&pShmNode->hFile,
- inFlags, &outFlags);
- if( rc!=SQLITE_OK ){
- rc = winLogError(rc, osGetLastError(), "winOpenShm",
- pShmNode->zFilename);
- goto shm_open_err;
+ /* Open a file-handle to use for mappings, and for the DMS lock. */
+ if( rc==SQLITE_OK ){
+ HANDLE h = INVALID_HANDLE_VALUE;
+ pShmNode->isReadonly = p->bReadonly;
+ rc = winHandleOpen(pNew->zFilename, &pShmNode->isReadonly, &h);
+ pShmNode->hSharedShm = h;
}
- if( outFlags==SQLITE_OPEN_READONLY ) pShmNode->isReadonly = 1;
- rc = winLockSharedMemory(pShmNode);
- if( rc!=SQLITE_OK && rc!=SQLITE_READONLY_CANTINIT ) goto shm_open_err;
+ /* If successful, link the new winShmNode into the global list. If an
+ ** error occurred, free the object. */
+ if( rc==SQLITE_OK ){
+ pShmNode->pNext = winShmNodeList;
+ winShmNodeList = pShmNode;
+ pNew = 0;
+ }else{
+ sqlite3_mutex_free(pShmNode->mutex);
+ if( pShmNode->hSharedShm!=INVALID_HANDLE_VALUE ){
+ osCloseHandle(pShmNode->hSharedShm);
+ }
+ }
}
- /* Make the new connection a child of the winShmNode */
- p->pShmNode = pShmNode;
+ /* If no error has occurred, link the winShm object to the winShmNode and
+ ** the winShm to pDbFd. */
+ if( rc==SQLITE_OK ){
+ p->pShmNode = pShmNode;
+ pShmNode->nRef++;
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
- p->id = pShmNode->nextShmId++;
+ p->id = pShmNode->nextShmId++;
#endif
- pShmNode->nRef++;
- pDbFd->pShm = p;
- winShmLeaveMutex();
-
- /* The reference count on pShmNode has already been incremented under
- ** the cover of the winShmEnterMutex() mutex and the pointer from the
- ** new (struct winShm) object to the pShmNode has been set. All that is
- ** left to do is to link the new object into the linked list starting
- ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex
- ** mutex.
- */
- sqlite3_mutex_enter(pShmNode->mutex);
- p->pNext = pShmNode->pFirst;
- pShmNode->pFirst = p;
- sqlite3_mutex_leave(pShmNode->mutex);
- return rc;
+ pDbFd->pShm = p;
+ }else if( p ){
+ winHandleClose(p->hShm);
+ sqlite3_free(p);
+ }
- /* Jump here on any error */
-shm_open_err:
- winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1);
- winShmPurge(pDbFd->pVfs, 0); /* This call frees pShmNode if required */
- sqlite3_free(p);
- sqlite3_free(pNew);
+ assert( rc!=SQLITE_OK || pShmNode->isUnlocked==0 || pShmNode->nRegion==0 );
winShmLeaveMutex();
+ sqlite3_free(pNew);
return rc;
}
@@ -50776,27 +51604,19 @@ static int winShmUnmap(
winFile *pDbFd; /* Database holding shared-memory */
winShm *p; /* The connection to be closed */
winShmNode *pShmNode; /* The underlying shared-memory file */
- winShm **pp; /* For looping over sibling connections */
pDbFd = (winFile*)fd;
p = pDbFd->pShm;
if( p==0 ) return SQLITE_OK;
- pShmNode = p->pShmNode;
-
- /* Remove connection p from the set of connections associated
- ** with pShmNode */
- sqlite3_mutex_enter(pShmNode->mutex);
- for(pp=&pShmNode->pFirst; (*pp)!=p; pp = &(*pp)->pNext){}
- *pp = p->pNext;
+ if( p->hShm!=INVALID_HANDLE_VALUE ){
+ osCloseHandle(p->hShm);
+ }
- /* Free the connection p */
- sqlite3_free(p);
- pDbFd->pShm = 0;
- sqlite3_mutex_leave(pShmNode->mutex);
+ pShmNode = p->pShmNode;
+ winShmEnterMutex();
/* If pShmNode->nRef has reached 0, then close the underlying
- ** shared-memory file, too */
- winShmEnterMutex();
+ ** shared-memory file, too. */
assert( pShmNode->nRef>0 );
pShmNode->nRef--;
if( pShmNode->nRef==0 ){
@@ -50804,6 +51624,9 @@ static int winShmUnmap(
}
winShmLeaveMutex();
+ /* Free the connection p */
+ sqlite3_free(p);
+ pDbFd->pShm = 0;
return SQLITE_OK;
}
@@ -50818,10 +51641,9 @@ static int winShmLock(
){
winFile *pDbFd = (winFile*)fd; /* Connection holding shared memory */
winShm *p = pDbFd->pShm; /* The shared memory being locked */
- winShm *pX; /* For looping over all siblings */
winShmNode *pShmNode;
int rc = SQLITE_OK; /* Result code */
- u16 mask; /* Mask of locks to take or release */
+ u16 mask = (u16)((1U<<(ofst+n)) - (1U<<ofst)); /* Mask of locks to [un]take */
if( p==0 ) return SQLITE_IOERR_SHMLOCK;
pShmNode = p->pShmNode;
@@ -50835,85 +51657,82 @@ static int winShmLock(
|| flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) );
assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 );
- mask = (u16)((1U<<(ofst+n)) - (1U<<ofst));
- assert( n>1 || mask==(1<<ofst) );
- sqlite3_mutex_enter(pShmNode->mutex);
- if( flags & SQLITE_SHM_UNLOCK ){
- u16 allMask = 0; /* Mask of locks held by siblings */
-
- /* See if any siblings hold this same lock */
- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
- if( pX==p ) continue;
- assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 );
- allMask |= pX->sharedMask;
- }
+ /* Check that, if this to be a blocking lock, no locks that occur later
+ ** in the following list than the lock being obtained are already held:
+ **
+ ** 1. Checkpointer lock (ofst==1).
+ ** 2. Write lock (ofst==0).
+ ** 3. Read locks (ofst>=3 && ofst<SQLITE_SHM_NLOCK).
+ **
+ ** In other words, if this is a blocking lock, none of the locks that
+ ** occur later in the above list than the lock being obtained may be
+ ** held.
+ **
+ ** It is not permitted to block on the RECOVER lock.
+ */
+#if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && defined(SQLITE_DEBUG)
+ {
+ u16 lockMask = (p->exclMask|p->sharedMask);
+ assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
+ (ofst!=2) /* not RECOVER */
+ && (ofst!=1 || lockMask==0 || lockMask==2)
+ && (ofst!=0 || lockMask<3)
+ && (ofst<3 || lockMask<(1<<ofst))
+ ));
+ }
+#endif
- /* Unlock the system-level locks */
- if( (mask & allMask)==0 ){
- rc = winShmSystemLock(pShmNode, WINSHM_UNLCK, ofst+WIN_SHM_BASE, n);
- }else{
- rc = SQLITE_OK;
- }
+ /* Check if there is any work to do. There are three cases:
+ **
+ ** a) An unlock operation where there are locks to unlock,
+ ** b) An shared lock where the requested lock is not already held
+ ** c) An exclusive lock where the requested lock is not already held
+ **
+ ** The SQLite core never requests an exclusive lock that it already holds.
+ ** This is assert()ed immediately below. */
+ assert( flags!=(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK)
+ || 0==(p->exclMask & mask)
+ );
+ if( ((flags & SQLITE_SHM_UNLOCK) && ((p->exclMask|p->sharedMask) & mask))
+ || (flags==(SQLITE_SHM_SHARED|SQLITE_SHM_LOCK) && 0==(p->sharedMask & mask))
+ || (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK))
+ ){
- /* Undo the local locks */
- if( rc==SQLITE_OK ){
- p->exclMask &= ~mask;
- p->sharedMask &= ~mask;
- }
- }else if( flags & SQLITE_SHM_SHARED ){
- u16 allShared = 0; /* Union of locks held by connections other than "p" */
+ if( flags & SQLITE_SHM_UNLOCK ){
+ /* Case (a) - unlock. */
- /* Find out which shared locks are already held by sibling connections.
- ** If any sibling already holds an exclusive lock, go ahead and return
- ** SQLITE_BUSY.
- */
- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
- if( (pX->exclMask & mask)!=0 ){
- rc = SQLITE_BUSY;
- break;
- }
- allShared |= pX->sharedMask;
- }
+ assert( (p->exclMask & p->sharedMask)==0 );
+ assert( !(flags & SQLITE_SHM_EXCLUSIVE) || (p->exclMask & mask)==mask );
+ assert( !(flags & SQLITE_SHM_SHARED) || (p->sharedMask & mask)==mask );
- /* Get shared locks at the system level, if necessary */
- if( rc==SQLITE_OK ){
- if( (allShared & mask)==0 ){
- rc = winShmSystemLock(pShmNode, WINSHM_RDLCK, ofst+WIN_SHM_BASE, n);
- }else{
- rc = SQLITE_OK;
- }
- }
+ rc = winHandleUnlock(p->hShm, ofst+WIN_SHM_BASE, n);
- /* Get the local shared locks */
- if( rc==SQLITE_OK ){
- p->sharedMask |= mask;
- }
- }else{
- /* Make sure no sibling connections hold locks that will block this
- ** lock. If any do, return SQLITE_BUSY right away.
- */
- for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
- if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){
- rc = SQLITE_BUSY;
- break;
+ /* If successful, also clear the bits in sharedMask/exclMask */
+ if( rc==SQLITE_OK ){
+ p->exclMask = (p->exclMask & ~mask);
+ p->sharedMask = (p->sharedMask & ~mask);
}
- }
-
- /* Get the exclusive locks at the system level. Then if successful
- ** also mark the local connection as being locked.
- */
- if( rc==SQLITE_OK ){
- rc = winShmSystemLock(pShmNode, WINSHM_WRLCK, ofst+WIN_SHM_BASE, n);
+ }else{
+ int bExcl = ((flags & SQLITE_SHM_EXCLUSIVE) ? 1 : 0);
+ DWORD nMs = winFileBusyTimeout(pDbFd);
+ rc = winHandleLockTimeout(p->hShm, ofst+WIN_SHM_BASE, n, bExcl, nMs);
if( rc==SQLITE_OK ){
- assert( (p->sharedMask & mask)==0 );
- p->exclMask |= mask;
+ if( bExcl ){
+ p->exclMask = (p->exclMask | mask);
+ }else{
+ p->sharedMask = (p->sharedMask | mask);
+ }
}
}
}
- sqlite3_mutex_leave(pShmNode->mutex);
- OSTRACE(("SHM-LOCK pid=%lu, id=%d, sharedMask=%03x, exclMask=%03x, rc=%s\n",
- osGetCurrentProcessId(), p->id, p->sharedMask, p->exclMask,
- sqlite3ErrName(rc)));
+
+ OSTRACE((
+ "SHM-LOCK(%d,%d,%d) pid=%lu, id=%d, sharedMask=%03x, exclMask=%03x,"
+ " rc=%s\n",
+ ofst, n, flags,
+ osGetCurrentProcessId(), p->id, p->sharedMask, p->exclMask,
+ sqlite3ErrName(rc))
+ );
return rc;
}
@@ -50975,13 +51794,15 @@ static int winShmMap(
sqlite3_mutex_enter(pShmNode->mutex);
if( pShmNode->isUnlocked ){
- rc = winLockSharedMemory(pShmNode);
+ /* Take the DMS lock. */
+ assert( pShmNode->nRegion==0 );
+ rc = winLockSharedMemory(pShmNode, winFileBusyTimeout(pDbFd));
if( rc!=SQLITE_OK ) goto shmpage_out;
- pShmNode->isUnlocked = 0;
}
- assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 );
+ assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 );
if( pShmNode->nRegion<=iRegion ){
+ HANDLE hShared = pShmNode->hSharedShm;
struct ShmRegion *apNew; /* New aRegion[] array */
int nByte = (iRegion+1)*szRegion; /* Minimum required file size */
sqlite3_int64 sz; /* Current size of wal-index file */
@@ -50992,10 +51813,9 @@ static int winShmMap(
** Check to see if it has been allocated (i.e. if the wal-index file is
** large enough to contain the requested region).
*/
- rc = winFileSize((sqlite3_file *)&pShmNode->hFile, &sz);
+ rc = winHandleSize(hShared, &sz);
if( rc!=SQLITE_OK ){
- rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(),
- "winShmMap1", pDbFd->zPath);
+ rc = winLogError(rc, osGetLastError(), "winShmMap1", pDbFd->zPath);
goto shmpage_out;
}
@@ -51004,19 +51824,17 @@ static int winShmMap(
** zero, exit early. *pp will be set to NULL and SQLITE_OK returned.
**
** Alternatively, if isWrite is non-zero, use ftruncate() to allocate
- ** the requested memory region.
- */
+ ** the requested memory region. */
if( !isWrite ) goto shmpage_out;
- rc = winTruncate((sqlite3_file *)&pShmNode->hFile, nByte);
+ rc = winHandleTruncate(hShared, nByte);
if( rc!=SQLITE_OK ){
- rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(),
- "winShmMap2", pDbFd->zPath);
+ rc = winLogError(rc, osGetLastError(), "winShmMap2", pDbFd->zPath);
goto shmpage_out;
}
}
/* Map the requested memory region into this processes address space. */
- apNew = (struct ShmRegion *)sqlite3_realloc64(
+ apNew = (struct ShmRegion*)sqlite3_realloc64(
pShmNode->aRegion, (iRegion+1)*sizeof(apNew[0])
);
if( !apNew ){
@@ -51035,18 +51853,13 @@ static int winShmMap(
void *pMap = 0; /* Mapped memory region */
#if SQLITE_OS_WINRT
- hMap = osCreateFileMappingFromApp(pShmNode->hFile.h,
- NULL, protect, nByte, NULL
- );
+ hMap = osCreateFileMappingFromApp(hShared, NULL, protect, nByte, NULL);
#elif defined(SQLITE_WIN32_HAS_WIDE)
- hMap = osCreateFileMappingW(pShmNode->hFile.h,
- NULL, protect, 0, nByte, NULL
- );
+ hMap = osCreateFileMappingW(hShared, NULL, protect, 0, nByte, NULL);
#elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA
- hMap = osCreateFileMappingA(pShmNode->hFile.h,
- NULL, protect, 0, nByte, NULL
- );
+ hMap = osCreateFileMappingA(hShared, NULL, protect, 0, nByte, NULL);
#endif
+
OSTRACE(("SHM-MAP-CREATE pid=%lu, region=%d, size=%d, rc=%s\n",
osGetCurrentProcessId(), pShmNode->nRegion, nByte,
hMap ? "ok" : "failed"));
@@ -51089,7 +51902,9 @@ shmpage_out:
}else{
*pp = 0;
}
- if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY;
+ if( pShmNode->isReadonly && rc==SQLITE_OK ){
+ rc = SQLITE_READONLY;
+ }
sqlite3_mutex_leave(pShmNode->mutex);
return rc;
}
@@ -51409,47 +52224,6 @@ static winVfsAppData winNolockAppData = {
** sqlite3_vfs object.
*/
-#if defined(__CYGWIN__)
-/*
-** Convert a filename from whatever the underlying operating system
-** supports for filenames into UTF-8. Space to hold the result is
-** obtained from malloc and must be freed by the calling function.
-*/
-static char *winConvertToUtf8Filename(const void *zFilename){
- char *zConverted = 0;
- if( osIsNT() ){
- zConverted = winUnicodeToUtf8(zFilename);
- }
-#ifdef SQLITE_WIN32_HAS_ANSI
- else{
- zConverted = winMbcsToUtf8(zFilename, osAreFileApisANSI());
- }
-#endif
- /* caller will handle out of memory */
- return zConverted;
-}
-#endif
-
-/*
-** Convert a UTF-8 filename into whatever form the underlying
-** operating system wants filenames in. Space to hold the result
-** is obtained from malloc and must be freed by the calling
-** function.
-*/
-static void *winConvertFromUtf8Filename(const char *zFilename){
- void *zConverted = 0;
- if( osIsNT() ){
- zConverted = winUtf8ToUnicode(zFilename);
- }
-#ifdef SQLITE_WIN32_HAS_ANSI
- else{
- zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI());
- }
-#endif
- /* caller will handle out of memory */
- return zConverted;
-}
-
/*
** This function returns non-zero if the specified UTF-8 string buffer
** ends with a directory separator character or one was successfully
@@ -51462,7 +52236,14 @@ static int winMakeEndInDirSep(int nBuf, char *zBuf){
if( winIsDirSep(zBuf[nLen-1]) ){
return 1;
}else if( nLen+1<nBuf ){
- zBuf[nLen] = winGetDirSep();
+ if( !osGetenv ){
+ zBuf[nLen] = winGetDirSep();
+ }else if( winIsDriveLetterAndColon(zBuf) && winIsDirSep(zBuf[2]) ){
+ zBuf[nLen] = '\\';
+ zBuf[2]='\\';
+ }else{
+ zBuf[nLen] = '/';
+ }
zBuf[nLen+1] = '\0';
return 1;
}
@@ -51489,14 +52270,14 @@ static int winTempDirDefined(void){
** The pointer returned in pzBuf must be freed via sqlite3_free().
*/
static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
- static char zChars[] =
+ static const char zChars[] =
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789";
size_t i, j;
DWORD pid;
int nPre = sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX);
- int nMax, nBuf, nDir, nLen;
+ i64 nMax, nBuf, nDir, nLen;
char *zBuf;
/* It's odd to simulate an io-error here, but really this is just
@@ -51508,7 +52289,8 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
/* Allocate a temporary buffer to store the fully qualified file
** name for the temporary file. If this fails, we cannot continue.
*/
- nMax = pVfs->mxPathname; nBuf = nMax + 2;
+ nMax = pVfs->mxPathname;
+ nBuf = 2 + (i64)nMax;
zBuf = sqlite3MallocZero( nBuf );
if( !zBuf ){
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
@@ -51539,7 +52321,7 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
}
#if defined(__CYGWIN__)
- else{
+ else if( osGetenv!=NULL ){
static const char *azDirs[] = {
0, /* getenv("SQLITE_TMPDIR") */
0, /* getenv("TMPDIR") */
@@ -51555,11 +52337,11 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
unsigned int i;
const char *zDir = 0;
- if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR");
- if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR");
- if( !azDirs[2] ) azDirs[2] = getenv("TMP");
- if( !azDirs[3] ) azDirs[3] = getenv("TEMP");
- if( !azDirs[4] ) azDirs[4] = getenv("USERPROFILE");
+ if( !azDirs[0] ) azDirs[0] = osGetenv("SQLITE_TMPDIR");
+ if( !azDirs[1] ) azDirs[1] = osGetenv("TMPDIR");
+ if( !azDirs[2] ) azDirs[2] = osGetenv("TMP");
+ if( !azDirs[3] ) azDirs[3] = osGetenv("TEMP");
+ if( !azDirs[4] ) azDirs[4] = osGetenv("USERPROFILE");
for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){
void *zConverted;
if( zDir==0 ) continue;
@@ -51568,7 +52350,7 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
** it must be converted to a native Win32 path via the Cygwin API
** prior to using it.
*/
- if( winIsDriveLetterAndColon(zDir) ){
+ {
zConverted = winConvertFromUtf8Filename(zDir);
if( !zConverted ){
sqlite3_free(zBuf);
@@ -51581,44 +52363,12 @@ static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
break;
}
sqlite3_free(zConverted);
- }else{
- zConverted = sqlite3MallocZero( nMax+1 );
- if( !zConverted ){
- sqlite3_free(zBuf);
- OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
- return SQLITE_IOERR_NOMEM_BKPT;
- }
- if( cygwin_conv_path(
- osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A, zDir,
- zConverted, nMax+1)<0 ){
- sqlite3_free(zConverted);
- sqlite3_free(zBuf);
- OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_CONVPATH\n"));
- return winLogError(SQLITE_IOERR_CONVPATH, (DWORD)errno,
- "winGetTempname2", zDir);
- }
- if( winIsDir(zConverted) ){
- /* At this point, we know the candidate directory exists and should
- ** be used. However, we may need to convert the string containing
- ** its name into UTF-8 (i.e. if it is UTF-16 right now).
- */
- char *zUtf8 = winConvertToUtf8Filename(zConverted);
- if( !zUtf8 ){
- sqlite3_free(zConverted);
- sqlite3_free(zBuf);
- OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
- return SQLITE_IOERR_NOMEM_BKPT;
- }
- sqlite3_snprintf(nMax, zBuf, "%s", zUtf8);
- sqlite3_free(zUtf8);
- sqlite3_free(zConverted);
- break;
- }
- sqlite3_free(zConverted);
}
}
}
-#elif !SQLITE_OS_WINRT && !defined(__CYGWIN__)
+#endif
+
+#if !SQLITE_OS_WINRT && defined(_WIN32)
else if( osIsNT() ){
char *zMulti;
LPWSTR zWidePath = sqlite3MallocZero( nMax*sizeof(WCHAR) );
@@ -51742,7 +52492,7 @@ static int winIsDir(const void *zConverted){
return 0; /* Invalid name? */
}
attr = sAttrData.dwFileAttributes;
-#if SQLITE_OS_WINCE==0
+#if SQLITE_OS_WINCE==0 && defined(SQLITE_WIN32_HAS_ANSI)
}else{
attr = osGetFileAttributesA((char*)zConverted);
#endif
@@ -51759,6 +52509,12 @@ static int winAccess(
);
/*
+** The Windows version of xAccess() accepts an extra bit in the flags
+** parameter that prevents an anti-virus retry loop.
+*/
+#define NORETRY 0x4000
+
+/*
** Open a file.
*/
static int winOpen(
@@ -51782,6 +52538,7 @@ static int winOpen(
void *zConverted; /* Filename in OS encoding */
const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
int cnt = 0;
+ int isRO = 0; /* file is known to be accessible readonly */
/* If argument zPath is a NULL pointer, this function is required to open
** a temporary file. Use this buffer to store the file name in.
@@ -51946,9 +52703,9 @@ static int winOpen(
&extendedParameters);
if( h!=INVALID_HANDLE_VALUE ) break;
if( isReadWrite ){
- int rc2, isRO = 0;
+ int rc2;
sqlite3BeginBenignMalloc();
- rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ, &isRO);
+ rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ|NORETRY, &isRO);
sqlite3EndBenignMalloc();
if( rc2==SQLITE_OK && isRO ) break;
}
@@ -51963,9 +52720,9 @@ static int winOpen(
NULL);
if( h!=INVALID_HANDLE_VALUE ) break;
if( isReadWrite ){
- int rc2, isRO = 0;
+ int rc2;
sqlite3BeginBenignMalloc();
- rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ, &isRO);
+ rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ|NORETRY, &isRO);
sqlite3EndBenignMalloc();
if( rc2==SQLITE_OK && isRO ) break;
}
@@ -51983,9 +52740,9 @@ static int winOpen(
NULL);
if( h!=INVALID_HANDLE_VALUE ) break;
if( isReadWrite ){
- int rc2, isRO = 0;
+ int rc2;
sqlite3BeginBenignMalloc();
- rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ, &isRO);
+ rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ|NORETRY, &isRO);
sqlite3EndBenignMalloc();
if( rc2==SQLITE_OK && isRO ) break;
}
@@ -52000,7 +52757,7 @@ static int winOpen(
if( h==INVALID_HANDLE_VALUE ){
sqlite3_free(zConverted);
sqlite3_free(zTmpname);
- if( isReadWrite && !isExclusive ){
+ if( isReadWrite && isRO && !isExclusive ){
return winOpen(pVfs, zName, id,
((flags|SQLITE_OPEN_READONLY) &
~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)),
@@ -52202,8 +52959,14 @@ static int winAccess(
int rc = 0;
DWORD lastErrno = 0;
void *zConverted;
+ int noRetry = 0; /* Do not use winRetryIoerr() */
UNUSED_PARAMETER(pVfs);
+ if( (flags & NORETRY)!=0 ){
+ noRetry = 1;
+ flags &= ~NORETRY;
+ }
+
SimulateIOError( return SQLITE_IOERR_ACCESS; );
OSTRACE(("ACCESS name=%s, flags=%x, pResOut=%p\n",
zFilename, flags, pResOut));
@@ -52226,7 +52989,10 @@ static int winAccess(
memset(&sAttrData, 0, sizeof(sAttrData));
while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted,
GetFileExInfoStandard,
- &sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){}
+ &sAttrData))
+ && !noRetry
+ && winRetryIoerr(&cnt, &lastErrno)
+ ){ /* Loop until true */}
if( rc ){
/* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file
** as if it does not exist.
@@ -52294,6 +53060,7 @@ static BOOL winIsDriveLetterAndColon(
return ( sqlite3Isalpha(zPathname[0]) && zPathname[1]==':' );
}
+#ifdef _WIN32
/*
** Returns non-zero if the specified path name should be used verbatim. If
** non-zero is returned from this function, the calling function must simply
@@ -52330,6 +53097,70 @@ static BOOL winIsVerbatimPathname(
*/
return FALSE;
}
+#endif /* _WIN32 */
+
+#ifdef __CYGWIN__
+/*
+** Simplify a filename into its canonical form
+** by making the following changes:
+**
+** * convert any '/' to '\' (win32) or reverse (Cygwin)
+** * removing any trailing and duplicate / (except for UNC paths)
+** * convert /./ into just /
+**
+** Changes are made in-place. Return the new name length.
+**
+** The original filename is in z[0..]. If the path is shortened,
+** no-longer used bytes will be written by '\0'.
+*/
+static void winSimplifyName(char *z){
+ int i, j;
+ for(i=j=0; z[i]; ++i){
+ if( winIsDirSep(z[i]) ){
+#if !defined(SQLITE_TEST)
+ /* Some test-cases assume that "./foo" and "foo" are different */
+ if( z[i+1]=='.' && winIsDirSep(z[i+2]) ){
+ ++i;
+ continue;
+ }
+#endif
+ if( !z[i+1] || (winIsDirSep(z[i+1]) && (i!=0)) ){
+ continue;
+ }
+ z[j++] = osGetenv?'/':'\\';
+ }else{
+ z[j++] = z[i];
+ }
+ }
+ while(j<i) z[j++] = '\0';
+}
+
+#define SQLITE_MAX_SYMLINKS 100
+
+static int mkFullPathname(
+ const char *zPath, /* Input path */
+ char *zOut, /* Output buffer */
+ int nOut /* Allocated size of buffer zOut */
+){
+ int nPath = sqlite3Strlen30(zPath);
+ int iOff = 0;
+ if( zPath[0]!='/' ){
+ if( osGetcwd(zOut, nOut-2)==0 ){
+ return winLogError(SQLITE_CANTOPEN_BKPT, (DWORD)osErrno, "getcwd", zPath);
+ }
+ iOff = sqlite3Strlen30(zOut);
+ zOut[iOff++] = '/';
+ }
+ if( (iOff+nPath+1)>nOut ){
+ /* SQLite assumes that xFullPathname() nul-terminates the output buffer
+ ** even if it returns an error. */
+ zOut[iOff] = '\0';
+ return SQLITE_CANTOPEN_BKPT;
+ }
+ sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath);
+ return SQLITE_OK;
+}
+#endif /* __CYGWIN__ */
/*
** Turn a relative pathname into a full pathname. Write the full
@@ -52342,8 +53173,8 @@ static int winFullPathnameNoMutex(
int nFull, /* Size of output buffer in bytes */
char *zFull /* Output buffer */
){
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(__CYGWIN__)
- DWORD nByte;
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
+ int nByte;
void *zConverted;
char *zOut;
#endif
@@ -52356,64 +53187,82 @@ static int winFullPathnameNoMutex(
zRelative++;
}
-#if defined(__CYGWIN__)
SimulateIOError( return SQLITE_ERROR );
- UNUSED_PARAMETER(nFull);
- assert( nFull>=pVfs->mxPathname );
- if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){
- /*
- ** NOTE: We are dealing with a relative path name and the data
- ** directory has been set. Therefore, use it as the basis
- ** for converting the relative path name to an absolute
- ** one by prepending the data directory and a slash.
- */
- char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 );
- if( !zOut ){
- return SQLITE_IOERR_NOMEM_BKPT;
- }
- if( cygwin_conv_path(
- (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A) |
- CCP_RELATIVE, zRelative, zOut, pVfs->mxPathname+1)<0 ){
- sqlite3_free(zOut);
- return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno,
- "winFullPathname1", zRelative);
- }else{
- char *zUtf8 = winConvertToUtf8Filename(zOut);
- if( !zUtf8 ){
- sqlite3_free(zOut);
- return SQLITE_IOERR_NOMEM_BKPT;
- }
- sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s",
- sqlite3_data_directory, winGetDirSep(), zUtf8);
- sqlite3_free(zUtf8);
- sqlite3_free(zOut);
- }
- }else{
- char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 );
- if( !zOut ){
- return SQLITE_IOERR_NOMEM_BKPT;
- }
- if( cygwin_conv_path(
- (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A),
- zRelative, zOut, pVfs->mxPathname+1)<0 ){
- sqlite3_free(zOut);
- return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno,
- "winFullPathname2", zRelative);
- }else{
- char *zUtf8 = winConvertToUtf8Filename(zOut);
- if( !zUtf8 ){
- sqlite3_free(zOut);
- return SQLITE_IOERR_NOMEM_BKPT;
- }
- sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zUtf8);
- sqlite3_free(zUtf8);
- sqlite3_free(zOut);
+
+#ifdef __CYGWIN__
+ if( osGetcwd ){
+ zFull[nFull-1] = '\0';
+ if( !winIsDriveLetterAndColon(zRelative) || !winIsDirSep(zRelative[2]) ){
+ int rc = SQLITE_OK;
+ int nLink = 1; /* Number of symbolic links followed so far */
+ const char *zIn = zRelative; /* Input path for each iteration of loop */
+ char *zDel = 0;
+ struct stat buf;
+
+ UNUSED_PARAMETER(pVfs);
+
+ do {
+ /* Call lstat() on path zIn. Set bLink to true if the path is a symbolic
+ ** link, or false otherwise. */
+ int bLink = 0;
+ if( osLstat && osReadlink ) {
+ if( osLstat(zIn, &buf)!=0 ){
+ int myErrno = osErrno;
+ if( myErrno!=ENOENT ){
+ rc = winLogError(SQLITE_CANTOPEN_BKPT, (DWORD)myErrno, "lstat", zIn);
+ }
+ }else{
+ bLink = ((buf.st_mode & 0170000) == 0120000);
+ }
+
+ if( bLink ){
+ if( zDel==0 ){
+ zDel = sqlite3MallocZero(nFull);
+ if( zDel==0 ) rc = SQLITE_NOMEM;
+ }else if( ++nLink>SQLITE_MAX_SYMLINKS ){
+ rc = SQLITE_CANTOPEN_BKPT;
+ }
+
+ if( rc==SQLITE_OK ){
+ nByte = osReadlink(zIn, zDel, nFull-1);
+ if( nByte ==(DWORD)-1 ){
+ rc = winLogError(SQLITE_CANTOPEN_BKPT, (DWORD)osErrno, "readlink", zIn);
+ }else{
+ if( zDel[0]!='/' ){
+ int n;
+ for(n = sqlite3Strlen30(zIn); n>0 && zIn[n-1]!='/'; n--);
+ if( nByte+n+1>nFull ){
+ rc = SQLITE_CANTOPEN_BKPT;
+ }else{
+ memmove(&zDel[n], zDel, nByte+1);
+ memcpy(zDel, zIn, n);
+ nByte += n;
+ }
+ }
+ zDel[nByte] = '\0';
+ }
+ }
+
+ zIn = zDel;
+ }
+ }
+
+ assert( rc!=SQLITE_OK || zIn!=zFull || zIn[0]=='/' );
+ if( rc==SQLITE_OK && zIn!=zFull ){
+ rc = mkFullPathname(zIn, zFull, nFull);
+ }
+ if( bLink==0 ) break;
+ zIn = zFull;
+ }while( rc==SQLITE_OK );
+
+ sqlite3_free(zDel);
+ winSimplifyName(zFull);
+ return rc;
}
}
- return SQLITE_OK;
-#endif
+#endif /* __CYGWIN__ */
-#if (SQLITE_OS_WINCE || SQLITE_OS_WINRT) && !defined(__CYGWIN__)
+#if (SQLITE_OS_WINCE || SQLITE_OS_WINRT) && defined(_WIN32)
SimulateIOError( return SQLITE_ERROR );
/* WinCE has no concept of a relative pathname, or so I am told. */
/* WinRT has no way to convert a relative path to an absolute one. */
@@ -52432,7 +53281,8 @@ static int winFullPathnameNoMutex(
return SQLITE_OK;
#endif
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(__CYGWIN__)
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
+#if defined(_WIN32)
/* It's odd to simulate an io-error here, but really this is just
** using the io-error infrastructure to test that SQLite handles this
** function failing. This function could fail if, for example, the
@@ -52450,6 +53300,7 @@ static int winFullPathnameNoMutex(
sqlite3_data_directory, winGetDirSep(), zRelative);
return SQLITE_OK;
}
+#endif
zConverted = winConvertFromUtf8Filename(zRelative);
if( zConverted==0 ){
return SQLITE_IOERR_NOMEM_BKPT;
@@ -52488,13 +53339,12 @@ static int winFullPathnameNoMutex(
return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
"winFullPathname3", zRelative);
}
- nByte += 3;
- zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) );
+ zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) + 3*sizeof(zTemp[0]) );
if( zTemp==0 ){
sqlite3_free(zConverted);
return SQLITE_IOERR_NOMEM_BKPT;
}
- nByte = osGetFullPathNameA((char*)zConverted, nByte, zTemp, 0);
+ nByte = osGetFullPathNameA((char*)zConverted, nByte+3, zTemp, 0);
if( nByte==0 ){
sqlite3_free(zConverted);
sqlite3_free(zTemp);
@@ -52507,7 +53357,26 @@ static int winFullPathnameNoMutex(
}
#endif
if( zOut ){
+#ifdef __CYGWIN__
+ if( memcmp(zOut, "\\\\?\\", 4) ){
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut);
+ }else if( memcmp(zOut+4, "UNC\\", 4) ){
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut+4);
+ }else{
+ char *p = zOut+6;
+ *p = '\\';
+ if( osGetcwd ){
+ /* On Cygwin, UNC paths use forward slashes */
+ while( *p ){
+ if( *p=='\\' ) *p = '/';
+ ++p;
+ }
+ }
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut+6);
+ }
+#else
sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut);
+#endif /* __CYGWIN__ */
sqlite3_free(zOut);
return SQLITE_OK;
}else{
@@ -52537,25 +53406,8 @@ static int winFullPathname(
*/
static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){
HANDLE h;
-#if defined(__CYGWIN__)
- int nFull = pVfs->mxPathname+1;
- char *zFull = sqlite3MallocZero( nFull );
- void *zConverted = 0;
- if( zFull==0 ){
- OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0));
- return 0;
- }
- if( winFullPathname(pVfs, zFilename, nFull, zFull)!=SQLITE_OK ){
- sqlite3_free(zFull);
- OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0));
- return 0;
- }
- zConverted = winConvertFromUtf8Filename(zFull);
- sqlite3_free(zFull);
-#else
void *zConverted = winConvertFromUtf8Filename(zFilename);
UNUSED_PARAMETER(pVfs);
-#endif
if( zConverted==0 ){
OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0));
return 0;
@@ -52904,7 +53756,7 @@ SQLITE_API int sqlite3_os_init(void){
/* Double-check that the aSyscall[] array has been constructed
** correctly. See ticket [bb3a86e890c8e96ab] */
- assert( ArraySize(aSyscall)==80 );
+ assert( ArraySize(aSyscall)==89 );
/* get memory map allocation granularity */
memset(&winSysInfo, 0, sizeof(SYSTEM_INFO));
@@ -53523,13 +54375,13 @@ static int memdbOpen(
}
if( p==0 ){
MemStore **apNew;
- p = sqlite3Malloc( sizeof(*p) + szName + 3 );
+ p = sqlite3Malloc( sizeof(*p) + (i64)szName + 3 );
if( p==0 ){
sqlite3_mutex_leave(pVfsMutex);
return SQLITE_NOMEM;
}
apNew = sqlite3Realloc(memdb_g.apMemStore,
- sizeof(apNew[0])*(memdb_g.nMemStore+1) );
+ sizeof(apNew[0])*(1+(i64)memdb_g.nMemStore) );
if( apNew==0 ){
sqlite3_free(p);
sqlite3_mutex_leave(pVfsMutex);
@@ -53962,7 +54814,7 @@ SQLITE_PRIVATE int sqlite3MemdbInit(void){
** no fewer collisions than the no-op *1. */
#define BITVEC_HASH(X) (((X)*1)%BITVEC_NINT)
-#define BITVEC_NPTR (BITVEC_USIZE/sizeof(Bitvec *))
+#define BITVEC_NPTR ((u32)(BITVEC_USIZE/sizeof(Bitvec *)))
/*
@@ -54145,7 +54997,7 @@ SQLITE_PRIVATE void sqlite3BitvecClear(Bitvec *p, u32 i, void *pBuf){
}
}
if( p->iSize<=BITVEC_NBIT ){
- p->u.aBitmap[i/BITVEC_SZELEM] &= ~(1 << (i&(BITVEC_SZELEM-1)));
+ p->u.aBitmap[i/BITVEC_SZELEM] &= ~(BITVEC_TELEM)(1<<(i&(BITVEC_SZELEM-1)));
}else{
unsigned int j;
u32 *aiValues = pBuf;
@@ -54196,7 +55048,7 @@ SQLITE_PRIVATE u32 sqlite3BitvecSize(Bitvec *p){
** individual bits within V.
*/
#define SETBIT(V,I) V[I>>3] |= (1<<(I&7))
-#define CLEARBIT(V,I) V[I>>3] &= ~(1<<(I&7))
+#define CLEARBIT(V,I) V[I>>3] &= ~(BITVEC_TELEM)(1<<(I&7))
#define TESTBIT(V,I) (V[I>>3]&(1<<(I&7)))!=0
/*
@@ -54239,7 +55091,7 @@ SQLITE_PRIVATE int sqlite3BitvecBuiltinTest(int sz, int *aOp){
/* Allocate the Bitvec to be tested and a linear array of
** bits to act as the reference */
pBitvec = sqlite3BitvecCreate( sz );
- pV = sqlite3MallocZero( (sz+7)/8 + 1 );
+ pV = sqlite3MallocZero( (7+(i64)sz)/8 + 1 );
pTmpSpace = sqlite3_malloc64(BITVEC_SZ);
if( pBitvec==0 || pV==0 || pTmpSpace==0 ) goto bitvec_end;
@@ -55480,10 +56332,6 @@ static SQLITE_WSD struct PCacheGlobal {
sqlite3_mutex *mutex; /* Mutex for accessing the following: */
PgFreeslot *pFree; /* Free page blocks */
int nFreeSlot; /* Number of unused pcache slots */
- /* The following value requires a mutex to change. We skip the mutex on
- ** reading because (1) most platforms read a 32-bit integer atomically and
- ** (2) even if an incorrect value is read, no great harm is done since this
- ** is really just an optimization. */
int bUnderPressure; /* True if low on PAGECACHE memory */
} pcache1_g;
@@ -55531,7 +56379,7 @@ SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){
pcache1.nReserve = n>90 ? 10 : (n/10 + 1);
pcache1.pStart = pBuf;
pcache1.pFree = 0;
- pcache1.bUnderPressure = 0;
+ AtomicStore(&pcache1.bUnderPressure,0);
while( n-- ){
p = (PgFreeslot*)pBuf;
p->pNext = pcache1.pFree;
@@ -55599,7 +56447,7 @@ static void *pcache1Alloc(int nByte){
if( p ){
pcache1.pFree = pcache1.pFree->pNext;
pcache1.nFreeSlot--;
- pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve;
+ AtomicStore(&pcache1.bUnderPressure,pcache1.nFreeSlot<pcache1.nReserve);
assert( pcache1.nFreeSlot>=0 );
sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_USED, 1);
@@ -55638,7 +56486,7 @@ static void pcache1Free(void *p){
pSlot->pNext = pcache1.pFree;
pcache1.pFree = pSlot;
pcache1.nFreeSlot++;
- pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve;
+ AtomicStore(&pcache1.bUnderPressure,pcache1.nFreeSlot<pcache1.nReserve);
assert( pcache1.nFreeSlot<=pcache1.nSlot );
sqlite3_mutex_leave(pcache1.mutex);
}else{
@@ -55769,7 +56617,7 @@ SQLITE_PRIVATE void sqlite3PageFree(void *p){
*/
static int pcache1UnderMemoryPressure(PCache1 *pCache){
if( pcache1.nSlot && (pCache->szPage+pCache->szExtra)<=pcache1.szSlot ){
- return pcache1.bUnderPressure;
+ return AtomicLoad(&pcache1.bUnderPressure);
}else{
return sqlite3HeapNearlyFull();
}
@@ -55786,12 +56634,12 @@ static int pcache1UnderMemoryPressure(PCache1 *pCache){
*/
static void pcache1ResizeHash(PCache1 *p){
PgHdr1 **apNew;
- unsigned int nNew;
- unsigned int i;
+ u64 nNew;
+ u32 i;
assert( sqlite3_mutex_held(p->pGroup->mutex) );
- nNew = p->nHash*2;
+ nNew = 2*(u64)p->nHash;
if( nNew<256 ){
nNew = 256;
}
@@ -56014,7 +56862,7 @@ static void pcache1Destroy(sqlite3_pcache *p);
static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){
PCache1 *pCache; /* The newly created page cache */
PGroup *pGroup; /* The group the new page cache will belong to */
- int sz; /* Bytes of memory required to allocate the new cache */
+ i64 sz; /* Bytes of memory required to allocate the new cache */
assert( (szPage & (szPage-1))==0 && szPage>=512 && szPage<=65536 );
assert( szExtra < 300 );
@@ -57991,20 +58839,6 @@ static const unsigned char aJournalMagic[] = {
# define USEFETCH(x) 0
#endif
-/*
-** The argument to this macro is a file descriptor (type sqlite3_file*).
-** Return 0 if it is not open, or non-zero (but not 1) if it is.
-**
-** This is so that expressions can be written as:
-**
-** if( isOpen(pPager->jfd) ){ ...
-**
-** instead of
-**
-** if( pPager->jfd->pMethods ){ ...
-*/
-#define isOpen(pFd) ((pFd)->pMethods!=0)
-
#ifdef SQLITE_DIRECT_OVERFLOW_READ
/*
** Return true if page pgno can be read directly from the database file
@@ -58019,19 +58853,19 @@ SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){
assert( pPager!=0 );
assert( pPager->fd!=0 );
if( pPager->fd->pMethods==0 ) return 0; /* Case (1) */
- assert( pPager->fd->pMethods->xDeviceCharacteristics!=0 );
- if( (pPager->fd->pMethods->xDeviceCharacteristics(pPager->fd)
- & SQLITE_IOCAP_SUBPAGE_READ)==0 ){
- return 0; /* Case (2) */
- }
if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0; /* Failed (3) */
#ifndef SQLITE_OMIT_WAL
if( pPager->pWal ){
u32 iRead = 0;
(void)sqlite3WalFindFrame(pPager->pWal, pgno, &iRead);
- return iRead==0; /* Condition (4) */
+ if( iRead ) return 0; /* Case (4) */
}
#endif
+ assert( pPager->fd->pMethods->xDeviceCharacteristics!=0 );
+ if( (pPager->fd->pMethods->xDeviceCharacteristics(pPager->fd)
+ & SQLITE_IOCAP_SUBPAGE_READ)==0 ){
+ return 0; /* Case (2) */
+ }
return 1;
}
#endif
@@ -58507,7 +59341,7 @@ static void checkPage(PgHdr *pPg){
** If an error occurs while reading from the journal file, an SQLite
** error code is returned.
*/
-static int readSuperJournal(sqlite3_file *pJrnl, char *zSuper, u32 nSuper){
+static int readSuperJournal(sqlite3_file *pJrnl, char *zSuper, u64 nSuper){
int rc; /* Return code */
u32 len; /* Length in bytes of super-journal name */
i64 szJ; /* Total size in bytes of journal file pJrnl */
@@ -59062,6 +59896,15 @@ static void pager_unlock(Pager *pPager){
if( pagerUseWal(pPager) ){
assert( !isOpen(pPager->jfd) );
+ if( pPager->eState==PAGER_ERROR ){
+ /* If an IO error occurs in wal.c while attempting to wrap the wal file,
+ ** then the Wal object may be holding a write-lock but no read-lock.
+ ** This call ensures that the write-lock is dropped as well. We cannot
+ ** have sqlite3WalEndReadTransaction() drop the write-lock, as it once
+ ** did, because this would break "BEGIN EXCLUSIVE" handling for
+ ** SQLITE_ENABLE_SETLK_TIMEOUT builds. */
+ sqlite3WalEndWriteTransaction(pPager->pWal);
+ }
sqlite3WalEndReadTransaction(pPager->pWal);
pPager->eState = PAGER_OPEN;
}else if( !pPager->exclusiveMode ){
@@ -59290,7 +60133,7 @@ static int pager_end_transaction(Pager *pPager, int hasSuper, int bCommit){
}
pPager->journalOff = 0;
}else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST
- || (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL)
+ || (pPager->exclusiveMode && pPager->journalMode<PAGER_JOURNALMODE_WAL)
){
rc = zeroJournalHdr(pPager, hasSuper||pPager->tempFile);
pPager->journalOff = 0;
@@ -59743,12 +60586,12 @@ static int pager_delsuper(Pager *pPager, const char *zSuper){
char *zJournal; /* Pointer to one journal within MJ file */
char *zSuperPtr; /* Space to hold super-journal filename */
char *zFree = 0; /* Free this buffer */
- int nSuperPtr; /* Amount of space allocated to zSuperPtr[] */
+ i64 nSuperPtr; /* Amount of space allocated to zSuperPtr[] */
/* Allocate space for both the pJournal and pSuper file descriptors.
** If successful, open the super-journal file for reading.
*/
- pSuper = (sqlite3_file *)sqlite3MallocZero(pVfs->szOsFile * 2);
+ pSuper = (sqlite3_file *)sqlite3MallocZero(2 * (i64)pVfs->szOsFile);
if( !pSuper ){
rc = SQLITE_NOMEM_BKPT;
pJournal = 0;
@@ -59766,11 +60609,14 @@ static int pager_delsuper(Pager *pPager, const char *zSuper){
*/
rc = sqlite3OsFileSize(pSuper, &nSuperJournal);
if( rc!=SQLITE_OK ) goto delsuper_out;
- nSuperPtr = pVfs->mxPathname+1;
+ nSuperPtr = 1 + (i64)pVfs->mxPathname;
+ assert( nSuperJournal>=0 && nSuperPtr>0 );
zFree = sqlite3Malloc(4 + nSuperJournal + nSuperPtr + 2);
if( !zFree ){
rc = SQLITE_NOMEM_BKPT;
goto delsuper_out;
+ }else{
+ assert( nSuperJournal<=0x7fffffff );
}
zFree[0] = zFree[1] = zFree[2] = zFree[3] = 0;
zSuperJournal = &zFree[4];
@@ -60031,7 +60877,7 @@ static int pager_playback(Pager *pPager, int isHot){
** for pageSize.
*/
zSuper = pPager->pTmpSpace;
- rc = readSuperJournal(pPager->jfd, zSuper, pPager->pVfs->mxPathname+1);
+ rc = readSuperJournal(pPager->jfd, zSuper, 1+(i64)pPager->pVfs->mxPathname);
if( rc==SQLITE_OK && zSuper[0] ){
rc = sqlite3OsAccess(pVfs, zSuper, SQLITE_ACCESS_EXISTS, &res);
}
@@ -60170,7 +61016,7 @@ end_playback:
** which case it requires 4 0x00 bytes in memory immediately before
** the filename. */
zSuper = &pPager->pTmpSpace[4];
- rc = readSuperJournal(pPager->jfd, zSuper, pPager->pVfs->mxPathname+1);
+ rc = readSuperJournal(pPager->jfd, zSuper, 1+(i64)pPager->pVfs->mxPathname);
testcase( rc!=SQLITE_OK );
}
if( rc==SQLITE_OK
@@ -61941,6 +62787,7 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
const char *zUri = 0; /* URI args to copy */
int nUriByte = 1; /* Number of bytes of URI args at *zUri */
+
/* Figure out how much space is required for each journal file-handle
** (there are two of them, the main journal and the sub-journal). */
journalFileSize = ROUND8(sqlite3JournalSize(pVfs));
@@ -61966,8 +62813,8 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
*/
if( zFilename && zFilename[0] ){
const char *z;
- nPathname = pVfs->mxPathname+1;
- zPathname = sqlite3DbMallocRaw(0, nPathname*2);
+ nPathname = pVfs->mxPathname + 1;
+ zPathname = sqlite3DbMallocRaw(0, 2*(i64)nPathname);
if( zPathname==0 ){
return SQLITE_NOMEM_BKPT;
}
@@ -62054,14 +62901,14 @@ SQLITE_PRIVATE int sqlite3PagerOpen(
ROUND8(sizeof(*pPager)) + /* Pager structure */
ROUND8(pcacheSize) + /* PCache object */
ROUND8(pVfs->szOsFile) + /* The main db file */
- journalFileSize * 2 + /* The two journal files */
+ (u64)journalFileSize * 2 + /* The two journal files */
SQLITE_PTRSIZE + /* Space to hold a pointer */
4 + /* Database prefix */
- nPathname + 1 + /* database filename */
- nUriByte + /* query parameters */
- nPathname + 8 + 1 + /* Journal filename */
+ (u64)nPathname + 1 + /* database filename */
+ (u64)nUriByte + /* query parameters */
+ (u64)nPathname + 8 + 1 + /* Journal filename */
#ifndef SQLITE_OMIT_WAL
- nPathname + 4 + 1 + /* WAL filename */
+ (u64)nPathname + 4 + 1 + /* WAL filename */
#endif
3 /* Terminator */
);
@@ -65516,6 +66363,11 @@ struct WalCkptInfo {
/*
** An open write-ahead log file is represented by an instance of the
** following object.
+**
+** writeLock:
+** This is usually set to 1 whenever the WRITER lock is held. However,
+** if it is set to 2, then the WRITER lock is held but must be released
+** by walHandleException() if a SEH exception is thrown.
*/
struct Wal {
sqlite3_vfs *pVfs; /* The VFS used to create pDbFd */
@@ -65606,9 +66458,13 @@ struct WalIterator {
u32 *aPgno; /* Array of page numbers. */
int nEntry; /* Nr. of entries in aPgno[] and aIndex[] */
int iZero; /* Frame number associated with aPgno[0] */
- } aSegment[1]; /* One for every 32KB page in the wal-index */
+ } aSegment[FLEXARRAY]; /* One for every 32KB page in the wal-index */
};
+/* Size (in bytes) of a WalIterator object suitable for N or fewer segments */
+#define SZ_WALITERATOR(N) \
+ (offsetof(WalIterator,aSegment)*(N)*sizeof(struct WalSegment))
+
/*
** Define the parameters of the hash tables in the wal-index file. There
** is a hash-table following every HASHTABLE_NPAGE page numbers in the
@@ -65767,7 +66623,7 @@ static SQLITE_NOINLINE int walIndexPageRealloc(
/* Enlarge the pWal->apWiData[] array if required */
if( pWal->nWiData<=iPage ){
- sqlite3_int64 nByte = sizeof(u32*)*(iPage+1);
+ sqlite3_int64 nByte = sizeof(u32*)*(1+(i64)iPage);
volatile u32 **apNew;
apNew = (volatile u32 **)sqlite3Realloc((void *)pWal->apWiData, nByte);
if( !apNew ){
@@ -65876,10 +66732,8 @@ static void walChecksumBytes(
s1 = s2 = 0;
}
- assert( nByte>=8 );
- assert( (nByte&0x00000007)==0 );
- assert( nByte<=65536 );
- assert( nByte%4==0 );
+ /* nByte is a multiple of 8 between 8 and 65536 */
+ assert( nByte>=8 && (nByte&7)==0 && nByte<=65536 );
if( !nativeCksum ){
do {
@@ -66969,8 +67823,7 @@ static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){
/* Allocate space for the WalIterator object. */
nSegment = walFramePage(iLast) + 1;
- nByte = sizeof(WalIterator)
- + (nSegment-1)*sizeof(struct WalSegment)
+ nByte = SZ_WALITERATOR(nSegment)
+ iLast*sizeof(ht_slot);
p = (WalIterator *)sqlite3_malloc64(nByte
+ sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast)
@@ -67041,7 +67894,7 @@ static int walEnableBlockingMs(Wal *pWal, int nMs){
static int walEnableBlocking(Wal *pWal){
int res = 0;
if( pWal->db ){
- int tmout = pWal->db->busyTimeout;
+ int tmout = pWal->db->setlkTimeout;
if( tmout ){
res = walEnableBlockingMs(pWal, tmout);
}
@@ -67427,7 +68280,9 @@ static int walHandleException(Wal *pWal){
static const int S = 1;
static const int E = (1<<SQLITE_SHM_NLOCK);
int ii;
- u32 mUnlock = pWal->lockMask & ~(
+ u32 mUnlock;
+ if( pWal->writeLock==2 ) pWal->writeLock = 0;
+ mUnlock = pWal->lockMask & ~(
(pWal->readLock<0 ? 0 : (S << WAL_READ_LOCK(pWal->readLock)))
| (pWal->writeLock ? (E << WAL_WRITE_LOCK) : 0)
| (pWal->ckptLock ? (E << WAL_CKPT_LOCK) : 0)
@@ -67699,7 +68554,12 @@ static int walIndexReadHdr(Wal *pWal, int *pChanged){
if( bWriteLock
|| SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1))
){
- pWal->writeLock = 1;
+ /* If the write-lock was just obtained, set writeLock to 2 instead of
+ ** the usual 1. This causes walIndexPage() to behave as if the
+ ** write-lock were held (so that it allocates new pages as required),
+ ** and walHandleException() to unlock the write-lock if a SEH exception
+ ** is thrown. */
+ if( !bWriteLock ) pWal->writeLock = 2;
if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){
badHdr = walIndexTryHdr(pWal, pChanged);
if( badHdr ){
@@ -68000,11 +68860,7 @@ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){
*/
static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){
volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */
- u32 mxReadMark; /* Largest aReadMark[] value */
- int mxI; /* Index of largest aReadMark[] value */
- int i; /* Loop counter */
int rc = SQLITE_OK; /* Return code */
- u32 mxFrame; /* Wal frame to lock to */
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
int nBlockTmout = 0;
#endif
@@ -68110,141 +68966,147 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){
assert( pWal->apWiData[0]!=0 );
pInfo = walCkptInfo(pWal);
SEH_INJECT_FAULT;
- if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame
+ {
+ u32 mxReadMark; /* Largest aReadMark[] value */
+ int mxI; /* Index of largest aReadMark[] value */
+ int i; /* Loop counter */
+ u32 mxFrame; /* Wal frame to lock to */
+ if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame
#ifdef SQLITE_ENABLE_SNAPSHOT
- && ((pWal->bGetSnapshot==0 && pWal->pSnapshot==0) || pWal->hdr.mxFrame==0)
+ && ((pWal->bGetSnapshot==0 && pWal->pSnapshot==0) || pWal->hdr.mxFrame==0)
#endif
- ){
- /* The WAL has been completely backfilled (or it is empty).
- ** and can be safely ignored.
- */
- rc = walLockShared(pWal, WAL_READ_LOCK(0));
- walShmBarrier(pWal);
- if( rc==SQLITE_OK ){
- if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){
- /* It is not safe to allow the reader to continue here if frames
- ** may have been appended to the log before READ_LOCK(0) was obtained.
- ** When holding READ_LOCK(0), the reader ignores the entire log file,
- ** which implies that the database file contains a trustworthy
- ** snapshot. Since holding READ_LOCK(0) prevents a checkpoint from
- ** happening, this is usually correct.
- **
- ** However, if frames have been appended to the log (or if the log
- ** is wrapped and written for that matter) before the READ_LOCK(0)
- ** is obtained, that is not necessarily true. A checkpointer may
- ** have started to backfill the appended frames but crashed before
- ** it finished. Leaving a corrupt image in the database file.
- */
- walUnlockShared(pWal, WAL_READ_LOCK(0));
- return WAL_RETRY;
+ ){
+ /* The WAL has been completely backfilled (or it is empty).
+ ** and can be safely ignored.
+ */
+ rc = walLockShared(pWal, WAL_READ_LOCK(0));
+ walShmBarrier(pWal);
+ if( rc==SQLITE_OK ){
+ if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr,sizeof(WalIndexHdr)) ){
+ /* It is not safe to allow the reader to continue here if frames
+ ** may have been appended to the log before READ_LOCK(0) was obtained.
+ ** When holding READ_LOCK(0), the reader ignores the entire log file,
+ ** which implies that the database file contains a trustworthy
+ ** snapshot. Since holding READ_LOCK(0) prevents a checkpoint from
+ ** happening, this is usually correct.
+ **
+ ** However, if frames have been appended to the log (or if the log
+ ** is wrapped and written for that matter) before the READ_LOCK(0)
+ ** is obtained, that is not necessarily true. A checkpointer may
+ ** have started to backfill the appended frames but crashed before
+ ** it finished. Leaving a corrupt image in the database file.
+ */
+ walUnlockShared(pWal, WAL_READ_LOCK(0));
+ return WAL_RETRY;
+ }
+ pWal->readLock = 0;
+ return SQLITE_OK;
+ }else if( rc!=SQLITE_BUSY ){
+ return rc;
}
- pWal->readLock = 0;
- return SQLITE_OK;
- }else if( rc!=SQLITE_BUSY ){
- return rc;
}
- }
- /* If we get this far, it means that the reader will want to use
- ** the WAL to get at content from recent commits. The job now is
- ** to select one of the aReadMark[] entries that is closest to
- ** but not exceeding pWal->hdr.mxFrame and lock that entry.
- */
- mxReadMark = 0;
- mxI = 0;
- mxFrame = pWal->hdr.mxFrame;
+ /* If we get this far, it means that the reader will want to use
+ ** the WAL to get at content from recent commits. The job now is
+ ** to select one of the aReadMark[] entries that is closest to
+ ** but not exceeding pWal->hdr.mxFrame and lock that entry.
+ */
+ mxReadMark = 0;
+ mxI = 0;
+ mxFrame = pWal->hdr.mxFrame;
#ifdef SQLITE_ENABLE_SNAPSHOT
- if( pWal->pSnapshot && pWal->pSnapshot->mxFrame<mxFrame ){
- mxFrame = pWal->pSnapshot->mxFrame;
- }
-#endif
- for(i=1; i<WAL_NREADER; i++){
- u32 thisMark = AtomicLoad(pInfo->aReadMark+i); SEH_INJECT_FAULT;
- if( mxReadMark<=thisMark && thisMark<=mxFrame ){
- assert( thisMark!=READMARK_NOT_USED );
- mxReadMark = thisMark;
- mxI = i;
+ if( pWal->pSnapshot && pWal->pSnapshot->mxFrame<mxFrame ){
+ mxFrame = pWal->pSnapshot->mxFrame;
}
- }
- if( (pWal->readOnly & WAL_SHM_RDONLY)==0
- && (mxReadMark<mxFrame || mxI==0)
- ){
+#endif
for(i=1; i<WAL_NREADER; i++){
- rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
- if( rc==SQLITE_OK ){
- AtomicStore(pInfo->aReadMark+i,mxFrame);
- mxReadMark = mxFrame;
+ u32 thisMark = AtomicLoad(pInfo->aReadMark+i); SEH_INJECT_FAULT;
+ if( mxReadMark<=thisMark && thisMark<=mxFrame ){
+ assert( thisMark!=READMARK_NOT_USED );
+ mxReadMark = thisMark;
mxI = i;
- walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
- break;
- }else if( rc!=SQLITE_BUSY ){
- return rc;
}
}
- }
- if( mxI==0 ){
- assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 );
- return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT;
- }
+ if( (pWal->readOnly & WAL_SHM_RDONLY)==0
+ && (mxReadMark<mxFrame || mxI==0)
+ ){
+ for(i=1; i<WAL_NREADER; i++){
+ rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
+ if( rc==SQLITE_OK ){
+ AtomicStore(pInfo->aReadMark+i,mxFrame);
+ mxReadMark = mxFrame;
+ mxI = i;
+ walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
+ break;
+ }else if( rc!=SQLITE_BUSY ){
+ return rc;
+ }
+ }
+ }
+ if( mxI==0 ){
+ assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 );
+ return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT;
+ }
- (void)walEnableBlockingMs(pWal, nBlockTmout);
- rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
- walDisableBlocking(pWal);
- if( rc ){
+ (void)walEnableBlockingMs(pWal, nBlockTmout);
+ rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
+ walDisableBlocking(pWal);
+ if( rc ){
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
- if( rc==SQLITE_BUSY_TIMEOUT ){
- *pCnt |= WAL_RETRY_BLOCKED_MASK;
- }
+ if( rc==SQLITE_BUSY_TIMEOUT ){
+ *pCnt |= WAL_RETRY_BLOCKED_MASK;
+ }
#else
- assert( rc!=SQLITE_BUSY_TIMEOUT );
+ assert( rc!=SQLITE_BUSY_TIMEOUT );
#endif
- assert( (rc&0xFF)!=SQLITE_BUSY||rc==SQLITE_BUSY||rc==SQLITE_BUSY_TIMEOUT );
- return (rc&0xFF)==SQLITE_BUSY ? WAL_RETRY : rc;
- }
- /* Now that the read-lock has been obtained, check that neither the
- ** value in the aReadMark[] array or the contents of the wal-index
- ** header have changed.
- **
- ** It is necessary to check that the wal-index header did not change
- ** between the time it was read and when the shared-lock was obtained
- ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility
- ** that the log file may have been wrapped by a writer, or that frames
- ** that occur later in the log than pWal->hdr.mxFrame may have been
- ** copied into the database by a checkpointer. If either of these things
- ** happened, then reading the database with the current value of
- ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry
- ** instead.
- **
- ** Before checking that the live wal-index header has not changed
- ** since it was read, set Wal.minFrame to the first frame in the wal
- ** file that has not yet been checkpointed. This client will not need
- ** to read any frames earlier than minFrame from the wal file - they
- ** can be safely read directly from the database file.
- **
- ** Because a ShmBarrier() call is made between taking the copy of
- ** nBackfill and checking that the wal-header in shared-memory still
- ** matches the one cached in pWal->hdr, it is guaranteed that the
- ** checkpointer that set nBackfill was not working with a wal-index
- ** header newer than that cached in pWal->hdr. If it were, that could
- ** cause a problem. The checkpointer could omit to checkpoint
- ** a version of page X that lies before pWal->minFrame (call that version
- ** A) on the basis that there is a newer version (version B) of the same
- ** page later in the wal file. But if version B happens to like past
- ** frame pWal->hdr.mxFrame - then the client would incorrectly assume
- ** that it can read version A from the database file. However, since
- ** we can guarantee that the checkpointer that set nBackfill could not
- ** see any pages past pWal->hdr.mxFrame, this problem does not come up.
- */
- pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1; SEH_INJECT_FAULT;
- walShmBarrier(pWal);
- if( AtomicLoad(pInfo->aReadMark+mxI)!=mxReadMark
- || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr))
- ){
- walUnlockShared(pWal, WAL_READ_LOCK(mxI));
- return WAL_RETRY;
- }else{
- assert( mxReadMark<=pWal->hdr.mxFrame );
- pWal->readLock = (i16)mxI;
+ assert((rc&0xFF)!=SQLITE_BUSY||rc==SQLITE_BUSY||rc==SQLITE_BUSY_TIMEOUT);
+ return (rc&0xFF)==SQLITE_BUSY ? WAL_RETRY : rc;
+ }
+ /* Now that the read-lock has been obtained, check that neither the
+ ** value in the aReadMark[] array or the contents of the wal-index
+ ** header have changed.
+ **
+ ** It is necessary to check that the wal-index header did not change
+ ** between the time it was read and when the shared-lock was obtained
+ ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility
+ ** that the log file may have been wrapped by a writer, or that frames
+ ** that occur later in the log than pWal->hdr.mxFrame may have been
+ ** copied into the database by a checkpointer. If either of these things
+ ** happened, then reading the database with the current value of
+ ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry
+ ** instead.
+ **
+ ** Before checking that the live wal-index header has not changed
+ ** since it was read, set Wal.minFrame to the first frame in the wal
+ ** file that has not yet been checkpointed. This client will not need
+ ** to read any frames earlier than minFrame from the wal file - they
+ ** can be safely read directly from the database file.
+ **
+ ** Because a ShmBarrier() call is made between taking the copy of
+ ** nBackfill and checking that the wal-header in shared-memory still
+ ** matches the one cached in pWal->hdr, it is guaranteed that the
+ ** checkpointer that set nBackfill was not working with a wal-index
+ ** header newer than that cached in pWal->hdr. If it were, that could
+ ** cause a problem. The checkpointer could omit to checkpoint
+ ** a version of page X that lies before pWal->minFrame (call that version
+ ** A) on the basis that there is a newer version (version B) of the same
+ ** page later in the wal file. But if version B happens to like past
+ ** frame pWal->hdr.mxFrame - then the client would incorrectly assume
+ ** that it can read version A from the database file. However, since
+ ** we can guarantee that the checkpointer that set nBackfill could not
+ ** see any pages past pWal->hdr.mxFrame, this problem does not come up.
+ */
+ pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1; SEH_INJECT_FAULT;
+ walShmBarrier(pWal);
+ if( AtomicLoad(pInfo->aReadMark+mxI)!=mxReadMark
+ || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr))
+ ){
+ walUnlockShared(pWal, WAL_READ_LOCK(mxI));
+ return WAL_RETRY;
+ }else{
+ assert( mxReadMark<=pWal->hdr.mxFrame );
+ pWal->readLock = (i16)mxI;
+ }
}
return rc;
}
@@ -68482,8 +69344,11 @@ SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
** read-lock.
*/
SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal){
- sqlite3WalEndWriteTransaction(pWal);
+#ifndef SQLITE_ENABLE_SETLK_TIMEOUT
+ assert( pWal->writeLock==0 || pWal->readLock<0 );
+#endif
if( pWal->readLock>=0 ){
+ sqlite3WalEndWriteTransaction(pWal);
walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock));
pWal->readLock = -1;
}
@@ -68676,7 +69541,7 @@ SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal){
** read-transaction was even opened, making this call a no-op.
** Return early. */
if( pWal->writeLock ){
- assert( !memcmp(&pWal->hdr,(void *)walIndexHdr(pWal),sizeof(WalIndexHdr)) );
+ assert( !memcmp(&pWal->hdr,(void*)pWal->apWiData[0],sizeof(WalIndexHdr)) );
return SQLITE_OK;
}
#endif
@@ -70126,6 +70991,12 @@ struct CellInfo {
#define BTCURSOR_MAX_DEPTH 20
/*
+** Maximum amount of storage local to a database page, regardless of
+** page size.
+*/
+#define BT_MAX_LOCAL 65501 /* 65536 - 35 */
+
+/*
** A cursor is a pointer to a particular entry within a particular
** b-tree within a database file.
**
@@ -70533,7 +71404,7 @@ SQLITE_PRIVATE int sqlite3BtreeHoldsMutex(Btree *p){
*/
static void SQLITE_NOINLINE btreeEnterAll(sqlite3 *db){
int i;
- int skipOk = 1;
+ u8 skipOk = 1;
Btree *p;
assert( sqlite3_mutex_held(db->mutex) );
for(i=0; i<db->nDb; i++){
@@ -71389,7 +72260,7 @@ static int saveCursorKey(BtCursor *pCur){
** below. */
void *pKey;
pCur->nKey = sqlite3BtreePayloadSize(pCur);
- pKey = sqlite3Malloc( pCur->nKey + 9 + 8 );
+ pKey = sqlite3Malloc( ((i64)pCur->nKey) + 9 + 8 );
if( pKey ){
rc = sqlite3BtreePayload(pCur, 0, (int)pCur->nKey, pKey);
if( rc==SQLITE_OK ){
@@ -71679,7 +72550,7 @@ SQLITE_PRIVATE void sqlite3BtreeCursorHint(BtCursor *pCur, int eHintType, ...){
*/
SQLITE_PRIVATE void sqlite3BtreeCursorHintFlags(BtCursor *pCur, unsigned x){
assert( x==BTREE_SEEK_EQ || x==BTREE_BULKLOAD || x==0 );
- pCur->hints = x;
+ pCur->hints = (u8)x;
}
@@ -71873,14 +72744,15 @@ static SQLITE_NOINLINE void btreeParseCellAdjustSizeForOverflow(
static int btreePayloadToLocal(MemPage *pPage, i64 nPayload){
int maxLocal; /* Maximum amount of payload held locally */
maxLocal = pPage->maxLocal;
+ assert( nPayload>=0 );
if( nPayload<=maxLocal ){
- return nPayload;
+ return (int)nPayload;
}else{
int minLocal; /* Minimum amount of payload held locally */
int surplus; /* Overflow payload available for local storage */
minLocal = pPage->minLocal;
- surplus = minLocal + (nPayload - minLocal)%(pPage->pBt->usableSize-4);
- return ( surplus <= maxLocal ) ? surplus : minLocal;
+ surplus = (int)(minLocal +(nPayload - minLocal)%(pPage->pBt->usableSize-4));
+ return (surplus <= maxLocal) ? surplus : minLocal;
}
}
@@ -71990,11 +72862,13 @@ static void btreeParseCellPtr(
pInfo->pPayload = pIter;
testcase( nPayload==pPage->maxLocal );
testcase( nPayload==(u32)pPage->maxLocal+1 );
+ assert( nPayload>=0 );
+ assert( pPage->maxLocal <= BT_MAX_LOCAL );
if( nPayload<=pPage->maxLocal ){
/* This is the (easy) common case where the entire payload fits
** on the local page. No overflow is required.
*/
- pInfo->nSize = nPayload + (u16)(pIter - pCell);
+ pInfo->nSize = (u16)nPayload + (u16)(pIter - pCell);
if( pInfo->nSize<4 ) pInfo->nSize = 4;
pInfo->nLocal = (u16)nPayload;
}else{
@@ -72027,11 +72901,13 @@ static void btreeParseCellPtrIndex(
pInfo->pPayload = pIter;
testcase( nPayload==pPage->maxLocal );
testcase( nPayload==(u32)pPage->maxLocal+1 );
+ assert( nPayload>=0 );
+ assert( pPage->maxLocal <= BT_MAX_LOCAL );
if( nPayload<=pPage->maxLocal ){
/* This is the (easy) common case where the entire payload fits
** on the local page. No overflow is required.
*/
- pInfo->nSize = nPayload + (u16)(pIter - pCell);
+ pInfo->nSize = (u16)nPayload + (u16)(pIter - pCell);
if( pInfo->nSize<4 ) pInfo->nSize = 4;
pInfo->nLocal = (u16)nPayload;
}else{
@@ -72570,14 +73446,14 @@ static SQLITE_INLINE int allocateSpace(MemPage *pPage, int nByte, int *pIdx){
** at the end of the page. So do additional corruption checks inside this
** routine and return SQLITE_CORRUPT if any problems are found.
*/
-static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
- u16 iPtr; /* Address of ptr to next freeblock */
- u16 iFreeBlk; /* Address of the next freeblock */
+static int freeSpace(MemPage *pPage, int iStart, int iSize){
+ int iPtr; /* Address of ptr to next freeblock */
+ int iFreeBlk; /* Address of the next freeblock */
u8 hdr; /* Page header size. 0 or 100 */
- u8 nFrag = 0; /* Reduction in fragmentation */
- u16 iOrigSize = iSize; /* Original value of iSize */
- u16 x; /* Offset to cell content area */
- u32 iEnd = iStart + iSize; /* First byte past the iStart buffer */
+ int nFrag = 0; /* Reduction in fragmentation */
+ int iOrigSize = iSize; /* Original value of iSize */
+ int x; /* Offset to cell content area */
+ int iEnd = iStart + iSize; /* First byte past the iStart buffer */
unsigned char *data = pPage->aData; /* Page content */
u8 *pTmp; /* Temporary ptr into data[] */
@@ -72604,7 +73480,7 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
}
iPtr = iFreeBlk;
}
- if( iFreeBlk>pPage->pBt->usableSize-4 ){ /* TH3: corrupt081.100 */
+ if( iFreeBlk>(int)pPage->pBt->usableSize-4 ){ /* TH3: corrupt081.100 */
return SQLITE_CORRUPT_PAGE(pPage);
}
assert( iFreeBlk>iPtr || iFreeBlk==0 || CORRUPT_DB );
@@ -72619,7 +73495,7 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
nFrag = iFreeBlk - iEnd;
if( iEnd>iFreeBlk ) return SQLITE_CORRUPT_PAGE(pPage);
iEnd = iFreeBlk + get2byte(&data[iFreeBlk+2]);
- if( iEnd > pPage->pBt->usableSize ){
+ if( iEnd > (int)pPage->pBt->usableSize ){
return SQLITE_CORRUPT_PAGE(pPage);
}
iSize = iEnd - iStart;
@@ -72640,7 +73516,7 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
}
}
if( nFrag>data[hdr+7] ) return SQLITE_CORRUPT_PAGE(pPage);
- data[hdr+7] -= nFrag;
+ data[hdr+7] -= (u8)nFrag;
}
pTmp = &data[hdr+5];
x = get2byte(pTmp);
@@ -72661,7 +73537,8 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){
/* Insert the new freeblock into the freelist */
put2byte(&data[iPtr], iStart);
put2byte(&data[iStart], iFreeBlk);
- put2byte(&data[iStart+2], iSize);
+ assert( iSize>=0 && iSize<=0xffff );
+ put2byte(&data[iStart+2], (u16)iSize);
}
pPage->nFree += iOrigSize;
return SQLITE_OK;
@@ -72887,7 +73764,7 @@ static int btreeInitPage(MemPage *pPage){
assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
pPage->maskPage = (u16)(pBt->pageSize - 1);
pPage->nOverflow = 0;
- pPage->cellOffset = pPage->hdrOffset + 8 + pPage->childPtrSize;
+ pPage->cellOffset = (u16)(pPage->hdrOffset + 8 + pPage->childPtrSize);
pPage->aCellIdx = data + pPage->childPtrSize + 8;
pPage->aDataEnd = pPage->aData + pBt->pageSize;
pPage->aDataOfst = pPage->aData + pPage->childPtrSize;
@@ -72921,8 +73798,8 @@ static int btreeInitPage(MemPage *pPage){
static void zeroPage(MemPage *pPage, int flags){
unsigned char *data = pPage->aData;
BtShared *pBt = pPage->pBt;
- u8 hdr = pPage->hdrOffset;
- u16 first;
+ int hdr = pPage->hdrOffset;
+ int first;
assert( sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno || CORRUPT_DB );
assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage );
@@ -72939,7 +73816,7 @@ static void zeroPage(MemPage *pPage, int flags){
put2byte(&data[hdr+5], pBt->usableSize);
pPage->nFree = (u16)(pBt->usableSize - first);
decodeFlags(pPage, flags);
- pPage->cellOffset = first;
+ pPage->cellOffset = (u16)first;
pPage->aDataEnd = &data[pBt->pageSize];
pPage->aCellIdx = &data[first];
pPage->aDataOfst = &data[pPage->childPtrSize];
@@ -73725,7 +74602,7 @@ SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve,
BtShared *pBt = p->pBt;
assert( nReserve>=0 && nReserve<=255 );
sqlite3BtreeEnter(p);
- pBt->nReserveWanted = nReserve;
+ pBt->nReserveWanted = (u8)nReserve;
x = pBt->pageSize - pBt->usableSize;
if( nReserve<x ) nReserve = x;
if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){
@@ -73831,7 +74708,7 @@ SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree *p, int newFlag){
assert( BTS_FAST_SECURE==(BTS_OVERWRITE|BTS_SECURE_DELETE) );
if( newFlag>=0 ){
p->pBt->btsFlags &= ~BTS_FAST_SECURE;
- p->pBt->btsFlags |= BTS_SECURE_DELETE*newFlag;
+ p->pBt->btsFlags |= (u16)(BTS_SECURE_DELETE*newFlag);
}
b = (p->pBt->btsFlags & BTS_FAST_SECURE)/BTS_SECURE_DELETE;
sqlite3BtreeLeave(p);
@@ -76760,7 +77637,7 @@ bypass_moveto_root:
rc = SQLITE_CORRUPT_PAGE(pPage);
goto moveto_index_finish;
}
- pCellKey = sqlite3Malloc( nCell+nOverrun );
+ pCellKey = sqlite3Malloc( (u64)nCell+(u64)nOverrun );
if( pCellKey==0 ){
rc = SQLITE_NOMEM_BKPT;
goto moveto_index_finish;
@@ -78279,7 +79156,8 @@ static int rebuildPage(
}
/* The pPg->nFree field is now set incorrectly. The caller will fix it. */
- pPg->nCell = nCell;
+ assert( nCell < 10922 );
+ pPg->nCell = (u16)nCell;
pPg->nOverflow = 0;
put2byte(&aData[hdr+1], 0);
@@ -78526,9 +79404,13 @@ static int editPage(
if( pageInsertArray(
pPg, pBegin, &pData, pCellptr,
iNew+nCell, nNew-nCell, pCArray
- ) ) goto editpage_fail;
+ )
+ ){
+ goto editpage_fail;
+ }
- pPg->nCell = nNew;
+ assert( nNew < 10922 );
+ pPg->nCell = (u16)nNew;
pPg->nOverflow = 0;
put2byte(&aData[hdr+3], pPg->nCell);
@@ -78837,7 +79719,7 @@ static int balance_nonroot(
int pageFlags; /* Value of pPage->aData[0] */
int iSpace1 = 0; /* First unused byte of aSpace1[] */
int iOvflSpace = 0; /* First unused byte of aOvflSpace[] */
- int szScratch; /* Size of scratch memory requested */
+ u64 szScratch; /* Size of scratch memory requested */
MemPage *apOld[NB]; /* pPage and up to two siblings */
MemPage *apNew[NB+2]; /* pPage and up to NB siblings after balancing */
u8 *pRight; /* Location in parent of right-sibling pointer */
@@ -80122,7 +81004,7 @@ SQLITE_PRIVATE int sqlite3BtreeInsert(
if( pCur->info.nKey==pX->nKey ){
BtreePayload x2;
x2.pData = pX->pKey;
- x2.nData = pX->nKey;
+ x2.nData = (int)pX->nKey; assert( pX->nKey<=0x7fffffff );
x2.nZero = 0;
return btreeOverwriteCell(pCur, &x2);
}
@@ -80303,7 +81185,7 @@ SQLITE_PRIVATE int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64
getCellInfo(pSrc);
if( pSrc->info.nPayload<0x80 ){
- *(aOut++) = pSrc->info.nPayload;
+ *(aOut++) = (u8)pSrc->info.nPayload;
}else{
aOut += sqlite3PutVarint(aOut, pSrc->info.nPayload);
}
@@ -80316,7 +81198,7 @@ SQLITE_PRIVATE int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64
nRem = pSrc->info.nPayload;
if( nIn==nRem && nIn<pDest->pPage->maxLocal ){
memcpy(aOut, aIn, nIn);
- pBt->nPreformatSize = nIn + (aOut - pBt->pTmpSpace);
+ pBt->nPreformatSize = nIn + (int)(aOut - pBt->pTmpSpace);
return SQLITE_OK;
}else{
int rc = SQLITE_OK;
@@ -80328,7 +81210,7 @@ SQLITE_PRIVATE int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64
u32 nOut; /* Size of output buffer aOut[] */
nOut = btreePayloadToLocal(pDest->pPage, pSrc->info.nPayload);
- pBt->nPreformatSize = nOut + (aOut - pBt->pTmpSpace);
+ pBt->nPreformatSize = (int)nOut + (int)(aOut - pBt->pTmpSpace);
if( nOut<pSrc->info.nPayload ){
pPgnoOut = &aOut[nOut];
pBt->nPreformatSize += 4;
@@ -81949,6 +82831,7 @@ SQLITE_PRIVATE int sqlite3BtreeIsInBackup(Btree *p){
*/
SQLITE_PRIVATE void *sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){
BtShared *pBt = p->pBt;
+ assert( nBytes==0 || nBytes==sizeof(Schema) );
sqlite3BtreeEnter(p);
if( !pBt->pSchema && nBytes ){
pBt->pSchema = sqlite3DbMallocZero(0, nBytes);
@@ -83065,7 +83948,7 @@ static void vdbeMemRenderNum(int sz, char *zBuf, Mem *p){
** corresponding string value, then it is important that the string be
** derived from the numeric value, not the other way around, to ensure
** that the index and table are consistent. See ticket
-** https://www.sqlite.org/src/info/343634942dd54ab (2018-01-31) for
+** https://sqlite.org/src/info/343634942dd54ab (2018-01-31) for
** an example.
**
** This routine looks at pMem to verify that if it has both a numeric
@@ -83251,7 +84134,7 @@ SQLITE_PRIVATE void sqlite3VdbeMemZeroTerminateIfAble(Mem *pMem){
return;
}
if( pMem->enc!=SQLITE_UTF8 ) return;
- if( NEVER(pMem->z==0) ) return;
+ assert( pMem->z!=0 );
if( pMem->flags & MEM_Dyn ){
if( pMem->xDel==sqlite3_free
&& sqlite3_msize(pMem->z) >= (u64)(pMem->n+1)
@@ -83970,27 +84853,30 @@ SQLITE_PRIVATE int sqlite3VdbeMemTooBig(Mem *p){
SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe *pVdbe, Mem *pMem){
int i;
Mem *pX;
- for(i=1, pX=pVdbe->aMem+1; i<pVdbe->nMem; i++, pX++){
- if( pX->pScopyFrom==pMem ){
- u16 mFlags;
- if( pVdbe->db->flags & SQLITE_VdbeTrace ){
- sqlite3DebugPrintf("Invalidate R[%d] due to change in R[%d]\n",
- (int)(pX - pVdbe->aMem), (int)(pMem - pVdbe->aMem));
- }
- /* If pX is marked as a shallow copy of pMem, then try to verify that
- ** no significant changes have been made to pX since the OP_SCopy.
- ** A significant change would indicated a missed call to this
- ** function for pX. Minor changes, such as adding or removing a
- ** dual type, are allowed, as long as the underlying value is the
- ** same. */
- mFlags = pMem->flags & pX->flags & pX->mScopyFlags;
- assert( (mFlags&(MEM_Int|MEM_IntReal))==0 || pMem->u.i==pX->u.i );
-
- /* pMem is the register that is changing. But also mark pX as
- ** undefined so that we can quickly detect the shallow-copy error */
- pX->flags = MEM_Undefined;
- pX->pScopyFrom = 0;
- }
+ if( pMem->bScopy ){
+ for(i=1, pX=pVdbe->aMem+1; i<pVdbe->nMem; i++, pX++){
+ if( pX->pScopyFrom==pMem ){
+ u16 mFlags;
+ if( pVdbe->db->flags & SQLITE_VdbeTrace ){
+ sqlite3DebugPrintf("Invalidate R[%d] due to change in R[%d]\n",
+ (int)(pX - pVdbe->aMem), (int)(pMem - pVdbe->aMem));
+ }
+ /* If pX is marked as a shallow copy of pMem, then try to verify that
+ ** no significant changes have been made to pX since the OP_SCopy.
+ ** A significant change would indicated a missed call to this
+ ** function for pX. Minor changes, such as adding or removing a
+ ** dual type, are allowed, as long as the underlying value is the
+ ** same. */
+ mFlags = pMem->flags & pX->flags & pX->mScopyFlags;
+ assert( (mFlags&(MEM_Int|MEM_IntReal))==0 || pMem->u.i==pX->u.i );
+
+ /* pMem is the register that is changing. But also mark pX as
+ ** undefined so that we can quickly detect the shallow-copy error */
+ pX->flags = MEM_Undefined;
+ pX->pScopyFrom = 0;
+ }
+ }
+ pMem->bScopy = 0;
}
pMem->pScopyFrom = 0;
}
@@ -84361,7 +85247,7 @@ static sqlite3_value *valueNew(sqlite3 *db, struct ValueNewStat4Ctx *p){
if( pRec==0 ){
Index *pIdx = p->pIdx; /* Index being probed */
- int nByte; /* Bytes of space to allocate */
+ i64 nByte; /* Bytes of space to allocate */
int i; /* Counter variable */
int nCol = pIdx->nColumn; /* Number of index columns including rowid */
@@ -84427,7 +85313,7 @@ static int valueFromFunction(
){
sqlite3_context ctx; /* Context object for function invocation */
sqlite3_value **apVal = 0; /* Function arguments */
- int nVal = 0; /* Size of apVal[] array */
+ int nVal = 0; /* Number of function arguments */
FuncDef *pFunc = 0; /* Function definition */
sqlite3_value *pVal = 0; /* New value */
int rc = SQLITE_OK; /* Return code */
@@ -85425,12 +86311,10 @@ SQLITE_PRIVATE int sqlite3VdbeAddFunctionCall(
int eCallCtx /* Calling context */
){
Vdbe *v = pParse->pVdbe;
- int nByte;
int addr;
sqlite3_context *pCtx;
assert( v );
- nByte = sizeof(*pCtx) + (nArg-1)*sizeof(sqlite3_value*);
- pCtx = sqlite3DbMallocRawNN(pParse->db, nByte);
+ pCtx = sqlite3DbMallocRawNN(pParse->db, SZ_CONTEXT(nArg));
if( pCtx==0 ){
assert( pParse->db->mallocFailed );
freeEphemeralFunction(pParse->db, (FuncDef*)pFunc);
@@ -85706,7 +86590,7 @@ static Op *opIterNext(VdbeOpIter *p){
}
if( pRet->p4type==P4_SUBPROGRAM ){
- int nByte = (p->nSub+1)*sizeof(SubProgram*);
+ i64 nByte = (1+(u64)p->nSub)*sizeof(SubProgram*);
int j;
for(j=0; j<p->nSub; j++){
if( p->apSub[j]==pRet->p4.pProgram ) break;
@@ -85836,8 +86720,8 @@ SQLITE_PRIVATE void sqlite3VdbeAssertAbortable(Vdbe *p){
** (1) For each jump instruction with a negative P2 value (a label)
** resolve the P2 value to an actual address.
**
-** (2) Compute the maximum number of arguments used by any SQL function
-** and store that value in *pMaxFuncArgs.
+** (2) Compute the maximum number of arguments used by the xUpdate/xFilter
+** methods of any virtual table and store that value in *pMaxVtabArgs.
**
** (3) Update the Vdbe.readOnly and Vdbe.bIsReader flags to accurately
** indicate what the prepared statement actually does.
@@ -85850,8 +86734,8 @@ SQLITE_PRIVATE void sqlite3VdbeAssertAbortable(Vdbe *p){
** script numbers the opcodes correctly. Changes to this routine must be
** coordinated with changes to mkopcodeh.tcl.
*/
-static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
- int nMaxArgs = *pMaxFuncArgs;
+static void resolveP2Values(Vdbe *p, int *pMaxVtabArgs){
+ int nMaxVtabArgs = *pMaxVtabArgs;
Op *pOp;
Parse *pParse = p->pParse;
int *aLabel = pParse->aLabel;
@@ -85896,15 +86780,19 @@ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){
}
#ifndef SQLITE_OMIT_VIRTUALTABLE
case OP_VUpdate: {
- if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2;
+ if( pOp->p2>nMaxVtabArgs ) nMaxVtabArgs = pOp->p2;
break;
}
case OP_VFilter: {
int n;
+ /* The instruction immediately prior to VFilter will be an
+ ** OP_Integer that sets the "argc" value for the VFilter. See
+ ** the code where OP_VFilter is generated at tag-20250207a. */
assert( (pOp - p->aOp) >= 3 );
assert( pOp[-1].opcode==OP_Integer );
+ assert( pOp[-1].p2==pOp->p3+1 );
n = pOp[-1].p1;
- if( n>nMaxArgs ) nMaxArgs = n;
+ if( n>nMaxVtabArgs ) nMaxVtabArgs = n;
/* Fall through into the default case */
/* no break */ deliberate_fall_through
}
@@ -85945,7 +86833,7 @@ resolve_p2_values_loop_exit:
pParse->aLabel = 0;
}
pParse->nLabel = 0;
- *pMaxFuncArgs = nMaxArgs;
+ *pMaxVtabArgs = nMaxVtabArgs;
assert( p->bIsReader!=0 || DbMaskAllZero(p->btreeMask) );
}
@@ -86174,7 +87062,7 @@ SQLITE_PRIVATE void sqlite3VdbeScanStatus(
const char *zName /* Name of table or index being scanned */
){
if( IS_STMT_SCANSTATUS(p->db) ){
- sqlite3_int64 nByte = (p->nScan+1) * sizeof(ScanStatus);
+ i64 nByte = (1+(i64)p->nScan) * sizeof(ScanStatus);
ScanStatus *aNew;
aNew = (ScanStatus*)sqlite3DbRealloc(p->db, p->aScan, nByte);
if( aNew ){
@@ -86284,6 +87172,9 @@ SQLITE_PRIVATE void sqlite3VdbeChangeP5(Vdbe *p, u16 p5){
*/
SQLITE_PRIVATE void sqlite3VdbeTypeofColumn(Vdbe *p, int iDest){
VdbeOp *pOp = sqlite3VdbeGetLastOp(p);
+#ifdef SQLITE_DEBUG
+ while( pOp->opcode==OP_ReleaseReg ) pOp--;
+#endif
if( pOp->p3==iDest && pOp->opcode==OP_Column ){
pOp->p5 |= OPFLAG_TYPEOFARG;
}
@@ -87124,6 +88015,7 @@ SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE *pOut, int pc, VdbeOp *pOp){
** will be initialized before use.
*/
static void initMemArray(Mem *p, int N, sqlite3 *db, u16 flags){
+ assert( db!=0 );
if( N>0 ){
do{
p->flags = flags;
@@ -87131,6 +88023,7 @@ static void initMemArray(Mem *p, int N, sqlite3 *db, u16 flags){
p->szMalloc = 0;
#ifdef SQLITE_DEBUG
p->pScopyFrom = 0;
+ p->bScopy = 0;
#endif
p++;
}while( (--N)>0 );
@@ -87149,6 +88042,7 @@ static void releaseMemArray(Mem *p, int N){
if( p && N ){
Mem *pEnd = &p[N];
sqlite3 *db = p->db;
+ assert( db!=0 );
if( db->pnBytesFreed ){
do{
if( p->szMalloc ) sqlite3DbFree(db, p->zMalloc);
@@ -87620,7 +88514,7 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady(
int nVar; /* Number of parameters */
int nMem; /* Number of VM memory registers */
int nCursor; /* Number of cursors required */
- int nArg; /* Number of arguments in subprograms */
+ int nArg; /* Max number args to xFilter or xUpdate */
int n; /* Loop counter */
struct ReusableSpace x; /* Reusable bulk memory */
@@ -87629,6 +88523,7 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady(
assert( pParse!=0 );
assert( p->eVdbeState==VDBE_INIT_STATE );
assert( pParse==p->pParse );
+ assert( pParse->db==p->db );
p->pVList = pParse->pVList;
pParse->pVList = 0;
db = p->db;
@@ -87691,6 +88586,9 @@ SQLITE_PRIVATE void sqlite3VdbeMakeReady(
p->apCsr = allocSpace(&x, p->apCsr, nCursor*sizeof(VdbeCursor*));
}
}
+#ifdef SQLITE_DEBUG
+ p->napArg = nArg;
+#endif
if( db->mallocFailed ){
p->nVar = 0;
@@ -89188,6 +90086,7 @@ SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(
){
UnpackedRecord *p; /* Unpacked record to return */
int nByte; /* Number of bytes required for *p */
+ assert( sizeof(UnpackedRecord) + sizeof(Mem)*65536 < 0x7fffffff );
nByte = ROUND8P(sizeof(UnpackedRecord)) + sizeof(Mem)*(pKeyInfo->nKeyField+1);
p = (UnpackedRecord *)sqlite3DbMallocRaw(pKeyInfo->db, nByte);
if( !p ) return 0;
@@ -90494,10 +91393,11 @@ SQLITE_PRIVATE void sqlite3VdbePreUpdateHook(
preupdate.pCsr = pCsr;
preupdate.op = op;
preupdate.iNewReg = iReg;
- preupdate.keyinfo.db = db;
- preupdate.keyinfo.enc = ENC(db);
- preupdate.keyinfo.nKeyField = pTab->nCol;
- preupdate.keyinfo.aSortFlags = (u8*)&fakeSortOrder;
+ preupdate.pKeyinfo = (KeyInfo*)&preupdate.keyinfoSpace;
+ preupdate.pKeyinfo->db = db;
+ preupdate.pKeyinfo->enc = ENC(db);
+ preupdate.pKeyinfo->nKeyField = pTab->nCol;
+ preupdate.pKeyinfo->aSortFlags = (u8*)&fakeSortOrder;
preupdate.iKey1 = iKey1;
preupdate.iKey2 = iKey2;
preupdate.pTab = pTab;
@@ -90507,8 +91407,9 @@ SQLITE_PRIVATE void sqlite3VdbePreUpdateHook(
db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2);
db->pPreUpdate = 0;
sqlite3DbFree(db, preupdate.aRecord);
- vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pUnpacked);
- vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pNewUnpacked);
+ vdbeFreeUnpacked(db, preupdate.pKeyinfo->nKeyField+1,preupdate.pUnpacked);
+ vdbeFreeUnpacked(db, preupdate.pKeyinfo->nKeyField+1,preupdate.pNewUnpacked);
+ sqlite3VdbeMemRelease(&preupdate.oldipk);
if( preupdate.aNew ){
int i;
for(i=0; i<pCsr->nField; i++){
@@ -90593,7 +91494,6 @@ static SQLITE_NOINLINE void invokeProfileCallback(sqlite3 *db, Vdbe *p){
sqlite3_int64 iNow;
sqlite3_int64 iElapse;
assert( p->startTime>0 );
- assert( (db->mTrace & (SQLITE_TRACE_PROFILE|SQLITE_TRACE_XPROFILE))!=0 );
assert( db->init.busy==0 );
assert( p->zSql!=0 );
sqlite3OsCurrentTimeInt64(db->pVfs, &iNow);
@@ -91313,7 +92213,7 @@ static int sqlite3Step(Vdbe *p){
}
assert( db->nVdbeWrite>0 || db->autoCommit==0
- || (db->nDeferredCons==0 && db->nDeferredImmCons==0)
+ || ((db->nDeferredCons + db->nDeferredImmCons)==0)
);
#ifndef SQLITE_OMIT_TRACE
@@ -91824,6 +92724,7 @@ static const Mem *columnNullValue(void){
#ifdef SQLITE_DEBUG
/* .pScopyFrom = */ (Mem*)0,
/* .mScopyFlags= */ 0,
+ /* .bScopy = */ 0,
#endif
};
return &nullMem;
@@ -91865,7 +92766,7 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){
** sqlite3_column_int64()
** sqlite3_column_text()
** sqlite3_column_text16()
-** sqlite3_column_real()
+** sqlite3_column_double()
** sqlite3_column_bytes()
** sqlite3_column_bytes16()
** sqlite3_column_blob()
@@ -92338,7 +93239,7 @@ SQLITE_API int sqlite3_bind_text64(
assert( xDel!=SQLITE_DYNAMIC );
if( enc!=SQLITE_UTF8 ){
if( enc==SQLITE_UTF16 ) enc = SQLITE_UTF16NATIVE;
- nData &= ~(u16)1;
+ nData &= ~(u64)1;
}
return bindText(pStmt, i, zData, nData, xDel, enc);
}
@@ -92706,6 +93607,7 @@ SQLITE_API int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppVa
PreUpdate *p;
Mem *pMem;
int rc = SQLITE_OK;
+ int iStore = 0;
#ifdef SQLITE_ENABLE_API_ARMOR
if( db==0 || ppValue==0 ){
@@ -92720,67 +93622,75 @@ SQLITE_API int sqlite3_preupdate_old(sqlite3 *db, int iIdx, sqlite3_value **ppVa
goto preupdate_old_out;
}
if( p->pPk ){
- iIdx = sqlite3TableColumnToIndex(p->pPk, iIdx);
+ iStore = sqlite3TableColumnToIndex(p->pPk, iIdx);
+ }else{
+ iStore = sqlite3TableColumnToStorage(p->pTab, iIdx);
}
- if( iIdx>=p->pCsr->nField || iIdx<0 ){
+ if( iStore>=p->pCsr->nField || iStore<0 ){
rc = SQLITE_RANGE;
goto preupdate_old_out;
}
- /* If the old.* record has not yet been loaded into memory, do so now. */
- if( p->pUnpacked==0 ){
- u32 nRec;
- u8 *aRec;
+ if( iIdx==p->pTab->iPKey ){
+ *ppValue = pMem = &p->oldipk;
+ sqlite3VdbeMemSetInt64(pMem, p->iKey1);
+ }else{
- assert( p->pCsr->eCurType==CURTYPE_BTREE );
- nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor);
- aRec = sqlite3DbMallocRaw(db, nRec);
- if( !aRec ) goto preupdate_old_out;
- rc = sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec);
- if( rc==SQLITE_OK ){
- p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec);
- if( !p->pUnpacked ) rc = SQLITE_NOMEM;
- }
- if( rc!=SQLITE_OK ){
- sqlite3DbFree(db, aRec);
- goto preupdate_old_out;
+ /* If the old.* record has not yet been loaded into memory, do so now. */
+ if( p->pUnpacked==0 ){
+ u32 nRec;
+ u8 *aRec;
+
+ assert( p->pCsr->eCurType==CURTYPE_BTREE );
+ nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor);
+ aRec = sqlite3DbMallocRaw(db, nRec);
+ if( !aRec ) goto preupdate_old_out;
+ rc = sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec);
+ if( rc==SQLITE_OK ){
+ p->pUnpacked = vdbeUnpackRecord(p->pKeyinfo, nRec, aRec);
+ if( !p->pUnpacked ) rc = SQLITE_NOMEM;
+ }
+ if( rc!=SQLITE_OK ){
+ sqlite3DbFree(db, aRec);
+ goto preupdate_old_out;
+ }
+ p->aRecord = aRec;
}
- p->aRecord = aRec;
- }
- pMem = *ppValue = &p->pUnpacked->aMem[iIdx];
- if( iIdx==p->pTab->iPKey ){
- sqlite3VdbeMemSetInt64(pMem, p->iKey1);
- }else if( iIdx>=p->pUnpacked->nField ){
- /* This occurs when the table has been extended using ALTER TABLE
- ** ADD COLUMN. The value to return is the default value of the column. */
- Column *pCol = &p->pTab->aCol[iIdx];
- if( pCol->iDflt>0 ){
- if( p->apDflt==0 ){
- int nByte = sizeof(sqlite3_value*)*p->pTab->nCol;
- p->apDflt = (sqlite3_value**)sqlite3DbMallocZero(db, nByte);
- if( p->apDflt==0 ) goto preupdate_old_out;
- }
- if( p->apDflt[iIdx]==0 ){
- sqlite3_value *pVal = 0;
- Expr *pDflt;
- assert( p->pTab!=0 && IsOrdinaryTable(p->pTab) );
- pDflt = p->pTab->u.tab.pDfltList->a[pCol->iDflt-1].pExpr;
- rc = sqlite3ValueFromExpr(db, pDflt, ENC(db), pCol->affinity, &pVal);
- if( rc==SQLITE_OK && pVal==0 ){
- rc = SQLITE_CORRUPT_BKPT;
+ pMem = *ppValue = &p->pUnpacked->aMem[iStore];
+ if( iStore>=p->pUnpacked->nField ){
+ /* This occurs when the table has been extended using ALTER TABLE
+ ** ADD COLUMN. The value to return is the default value of the column. */
+ Column *pCol = &p->pTab->aCol[iIdx];
+ if( pCol->iDflt>0 ){
+ if( p->apDflt==0 ){
+ int nByte;
+ assert( sizeof(sqlite3_value*)*UMXV(p->pTab->nCol) < 0x7fffffff );
+ nByte = sizeof(sqlite3_value*)*p->pTab->nCol;
+ p->apDflt = (sqlite3_value**)sqlite3DbMallocZero(db, nByte);
+ if( p->apDflt==0 ) goto preupdate_old_out;
}
- p->apDflt[iIdx] = pVal;
+ if( p->apDflt[iIdx]==0 ){
+ sqlite3_value *pVal = 0;
+ Expr *pDflt;
+ assert( p->pTab!=0 && IsOrdinaryTable(p->pTab) );
+ pDflt = p->pTab->u.tab.pDfltList->a[pCol->iDflt-1].pExpr;
+ rc = sqlite3ValueFromExpr(db, pDflt, ENC(db), pCol->affinity, &pVal);
+ if( rc==SQLITE_OK && pVal==0 ){
+ rc = SQLITE_CORRUPT_BKPT;
+ }
+ p->apDflt[iIdx] = pVal;
+ }
+ *ppValue = p->apDflt[iIdx];
+ }else{
+ *ppValue = (sqlite3_value *)columnNullValue();
+ }
+ }else if( p->pTab->aCol[iIdx].affinity==SQLITE_AFF_REAL ){
+ if( pMem->flags & (MEM_Int|MEM_IntReal) ){
+ testcase( pMem->flags & MEM_Int );
+ testcase( pMem->flags & MEM_IntReal );
+ sqlite3VdbeMemRealify(pMem);
}
- *ppValue = p->apDflt[iIdx];
- }else{
- *ppValue = (sqlite3_value *)columnNullValue();
- }
- }else if( p->pTab->aCol[iIdx].affinity==SQLITE_AFF_REAL ){
- if( pMem->flags & (MEM_Int|MEM_IntReal) ){
- testcase( pMem->flags & MEM_Int );
- testcase( pMem->flags & MEM_IntReal );
- sqlite3VdbeMemRealify(pMem);
}
}
@@ -92802,7 +93712,7 @@ SQLITE_API int sqlite3_preupdate_count(sqlite3 *db){
#else
p = db->pPreUpdate;
#endif
- return (p ? p->keyinfo.nKeyField : 0);
+ return (p ? p->pKeyinfo->nKeyField : 0);
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
@@ -92854,6 +93764,7 @@ SQLITE_API int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppVa
PreUpdate *p;
int rc = SQLITE_OK;
Mem *pMem;
+ int iStore = 0;
#ifdef SQLITE_ENABLE_API_ARMOR
if( db==0 || ppValue==0 ){
@@ -92866,9 +93777,12 @@ SQLITE_API int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppVa
goto preupdate_new_out;
}
if( p->pPk && p->op!=SQLITE_UPDATE ){
- iIdx = sqlite3TableColumnToIndex(p->pPk, iIdx);
+ iStore = sqlite3TableColumnToIndex(p->pPk, iIdx);
+ }else{
+ iStore = sqlite3TableColumnToStorage(p->pTab, iIdx);
}
- if( iIdx>=p->pCsr->nField || iIdx<0 ){
+
+ if( iStore>=p->pCsr->nField || iStore<0 ){
rc = SQLITE_RANGE;
goto preupdate_new_out;
}
@@ -92881,40 +93795,41 @@ SQLITE_API int sqlite3_preupdate_new(sqlite3 *db, int iIdx, sqlite3_value **ppVa
Mem *pData = &p->v->aMem[p->iNewReg];
rc = ExpandBlob(pData);
if( rc!=SQLITE_OK ) goto preupdate_new_out;
- pUnpack = vdbeUnpackRecord(&p->keyinfo, pData->n, pData->z);
+ pUnpack = vdbeUnpackRecord(p->pKeyinfo, pData->n, pData->z);
if( !pUnpack ){
rc = SQLITE_NOMEM;
goto preupdate_new_out;
}
p->pNewUnpacked = pUnpack;
}
- pMem = &pUnpack->aMem[iIdx];
+ pMem = &pUnpack->aMem[iStore];
if( iIdx==p->pTab->iPKey ){
sqlite3VdbeMemSetInt64(pMem, p->iKey2);
- }else if( iIdx>=pUnpack->nField ){
+ }else if( iStore>=pUnpack->nField ){
pMem = (sqlite3_value *)columnNullValue();
}
}else{
- /* For an UPDATE, memory cell (p->iNewReg+1+iIdx) contains the required
+ /* For an UPDATE, memory cell (p->iNewReg+1+iStore) contains the required
** value. Make a copy of the cell contents and return a pointer to it.
** It is not safe to return a pointer to the memory cell itself as the
** caller may modify the value text encoding.
*/
assert( p->op==SQLITE_UPDATE );
if( !p->aNew ){
- p->aNew = (Mem *)sqlite3DbMallocZero(db, sizeof(Mem) * p->pCsr->nField);
+ assert( sizeof(Mem)*UMXV(p->pCsr->nField) < 0x7fffffff );
+ p->aNew = (Mem *)sqlite3DbMallocZero(db, sizeof(Mem)*p->pCsr->nField);
if( !p->aNew ){
rc = SQLITE_NOMEM;
goto preupdate_new_out;
}
}
- assert( iIdx>=0 && iIdx<p->pCsr->nField );
- pMem = &p->aNew[iIdx];
+ assert( iStore>=0 && iStore<p->pCsr->nField );
+ pMem = &p->aNew[iStore];
if( pMem->flags==0 ){
if( iIdx==p->pTab->iPKey ){
sqlite3VdbeMemSetInt64(pMem, p->iKey2);
}else{
- rc = sqlite3VdbeMemCopy(pMem, &p->v->aMem[p->iNewReg+1+iIdx]);
+ rc = sqlite3VdbeMemCopy(pMem, &p->v->aMem[p->iNewReg+1+iStore]);
if( rc!=SQLITE_OK ) goto preupdate_new_out;
}
}
@@ -93672,11 +94587,11 @@ static VdbeCursor *allocateCursor(
*/
Mem *pMem = iCur>0 ? &p->aMem[p->nMem-iCur] : p->aMem;
- int nByte;
+ i64 nByte;
VdbeCursor *pCx = 0;
- nByte =
- ROUND8P(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField +
- (eCurType==CURTYPE_BTREE?sqlite3BtreeCursorSize():0);
+ nByte = SZ_VDBECURSOR(nField);
+ assert( ROUND8(nByte)==nByte );
+ if( eCurType==CURTYPE_BTREE ) nByte += sqlite3BtreeCursorSize();
assert( iCur>=0 && iCur<p->nCursor );
if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/
@@ -93700,7 +94615,7 @@ static VdbeCursor *allocateCursor(
pMem->szMalloc = 0;
return 0;
}
- pMem->szMalloc = nByte;
+ pMem->szMalloc = (int)nByte;
}
p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->zMalloc;
@@ -93709,8 +94624,8 @@ static VdbeCursor *allocateCursor(
pCx->nField = nField;
pCx->aOffset = &pCx->aType[nField];
if( eCurType==CURTYPE_BTREE ){
- pCx->uc.pCursor = (BtCursor*)
- &pMem->z[ROUND8P(sizeof(VdbeCursor))+2*sizeof(u32)*nField];
+ assert( ROUND8(SZ_VDBECURSOR(nField))==SZ_VDBECURSOR(nField) );
+ pCx->uc.pCursor = (BtCursor*)&pMem->z[SZ_VDBECURSOR(nField)];
sqlite3BtreeCursorZero(pCx->uc.pCursor);
}
return pCx;
@@ -94003,6 +94918,7 @@ static void registerTrace(int iReg, Mem *p){
printf("R[%d] = ", iReg);
memTracePrint(p);
if( p->pScopyFrom ){
+ assert( p->pScopyFrom->bScopy );
printf(" <== R[%d]", (int)(p->pScopyFrom - &p[-iReg]));
}
printf("\n");
@@ -94713,7 +95629,7 @@ case OP_Halt: {
sqlite3VdbeError(p, "%s", pOp->p4.z);
}
pcx = (int)(pOp - aOp);
- sqlite3_log(pOp->p1, "abort at %d in [%s]: %s", pcx, p->zSql, p->zErrMsg);
+ sqlite3_log(pOp->p1, "abort at %d: %s; [%s]", pcx, p->zErrMsg, p->zSql);
}
rc = sqlite3VdbeHalt(p);
assert( rc==SQLITE_BUSY || rc==SQLITE_OK || rc==SQLITE_ERROR );
@@ -94986,6 +95902,7 @@ case OP_Move: {
{ int i;
for(i=1; i<p->nMem; i++){
if( aMem[i].pScopyFrom==pIn1 ){
+ assert( aMem[i].bScopy );
aMem[i].pScopyFrom = pOut;
}
}
@@ -95058,6 +95975,7 @@ case OP_SCopy: { /* out2 */
#ifdef SQLITE_DEBUG
pOut->pScopyFrom = pIn1;
pOut->mScopyFlags = pIn1->flags;
+ pIn1->bScopy = 1;
#endif
break;
}
@@ -96037,7 +96955,7 @@ case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */
break;
}
-/* Opcode: Once P1 P2 * * *
+/* Opcode: Once P1 P2 P3 * *
**
** Fall through to the next instruction the first time this opcode is
** encountered on each invocation of the byte-code program. Jump to P2
@@ -96053,6 +96971,12 @@ case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */
** whether or not the jump should be taken. The bitmask is necessary
** because the self-altering code trick does not work for recursive
** triggers.
+**
+** The P3 operand is not used directly by this opcode. However P3 is
+** used by the code generator as follows: If this opcode is the start
+** of a subroutine and that subroutine uses a Bloom filter, then P3 will
+** be the register that holds that Bloom filter. See tag-202407032019
+** in the source code for implementation details.
*/
case OP_Once: { /* jump */
u32 iAddr; /* Address of this instruction */
@@ -97098,6 +98022,7 @@ case OP_MakeRecord: {
zHdr += sqlite3PutVarint(zHdr, serial_type);
if( pRec->n ){
assert( pRec->z!=0 );
+ assert( pRec->z!=(const char*)sqlite3CtypeMap );
memcpy(zPayload, pRec->z, pRec->n);
zPayload += pRec->n;
}
@@ -97934,9 +98859,11 @@ case OP_OpenEphemeral: { /* ncycle */
}
}
pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED);
+ assert( p->apCsr[pOp->p1]==pCx );
if( rc ){
assert( !sqlite3BtreeClosesWithCursor(pCx->ub.pBtx, pCx->uc.pCursor) );
sqlite3BtreeClose(pCx->ub.pBtx);
+ p->apCsr[pOp->p1] = 0; /* Not required; helps with static analysis */
}else{
assert( sqlite3BtreeClosesWithCursor(pCx->ub.pBtx, pCx->uc.pCursor) );
}
@@ -99447,7 +100374,7 @@ case OP_RowData: {
/* The OP_RowData opcodes always follow OP_NotExists or
** OP_SeekRowid or OP_Rewind/Op_Next with no intervening instructions
** that might invalidate the cursor.
- ** If this where not the case, on of the following assert()s
+ ** If this were not the case, one of the following assert()s
** would fail. Should this ever change (because of changes in the code
** generator) then the fix would be to insert a call to
** sqlite3VdbeCursorMoveto().
@@ -100716,7 +101643,7 @@ case OP_RowSetTest: { /* jump, in1, in3 */
*/
case OP_Program: { /* jump0 */
int nMem; /* Number of memory registers for sub-program */
- int nByte; /* Bytes of runtime space required for sub-program */
+ i64 nByte; /* Bytes of runtime space required for sub-program */
Mem *pRt; /* Register to allocate runtime space */
Mem *pMem; /* Used to iterate through memory cells */
Mem *pEnd; /* Last memory cell in new array */
@@ -100767,7 +101694,7 @@ case OP_Program: { /* jump0 */
nByte = ROUND8(sizeof(VdbeFrame))
+ nMem * sizeof(Mem)
+ pProgram->nCsr * sizeof(VdbeCursor*)
- + (pProgram->nOp + 7)/8;
+ + (7 + (i64)pProgram->nOp)/8;
pFrame = sqlite3DbMallocZero(db, nByte);
if( !pFrame ){
goto no_mem;
@@ -100775,7 +101702,7 @@ case OP_Program: { /* jump0 */
sqlite3VdbeMemRelease(pRt);
pRt->flags = MEM_Blob|MEM_Dyn;
pRt->z = (char*)pFrame;
- pRt->n = nByte;
+ pRt->n = (int)nByte;
pRt->xDel = sqlite3VdbeFrameMemDel;
pFrame->v = p;
@@ -100874,12 +101801,14 @@ case OP_Param: { /* out2 */
** statement counter is incremented (immediate foreign key constraints).
*/
case OP_FkCounter: {
- if( db->flags & SQLITE_DeferFKs ){
- db->nDeferredImmCons += pOp->p2;
- }else if( pOp->p1 ){
+ if( pOp->p1 ){
db->nDeferredCons += pOp->p2;
}else{
- p->nFkConstraint += pOp->p2;
+ if( db->flags & SQLITE_DeferFKs ){
+ db->nDeferredImmCons += pOp->p2;
+ }else{
+ p->nFkConstraint += pOp->p2;
+ }
}
break;
}
@@ -101094,7 +102023,7 @@ case OP_AggStep: {
**
** Note: We could avoid this by using a regular memory cell from aMem[] for
** the accumulator, instead of allocating one here. */
- nAlloc = ROUND8P( sizeof(pCtx[0]) + (n-1)*sizeof(sqlite3_value*) );
+ nAlloc = ROUND8P( SZ_CONTEXT(n) );
pCtx = sqlite3DbMallocRawNN(db, nAlloc + sizeof(Mem));
if( pCtx==0 ) goto no_mem;
pCtx->pOut = (Mem*)((u8*)pCtx + nAlloc);
@@ -101754,6 +102683,7 @@ case OP_VFilter: { /* jump, ncycle */
/* Invoke the xFilter method */
apArg = p->apArg;
+ assert( nArg<=p->napArg );
for(i = 0; i<nArg; i++){
apArg[i] = &pArgc[i+1];
}
@@ -101964,6 +102894,7 @@ case OP_VUpdate: {
u8 vtabOnConflict = db->vtabOnConflict;
apArg = p->apArg;
pX = &aMem[pOp->p3];
+ assert( nArg<=p->napArg );
for(i=0; i<nArg; i++){
assert( memIsValid(pX) );
memAboutToChange(p, pX);
@@ -102540,8 +103471,8 @@ abort_due_to_error:
p->rc = rc;
sqlite3SystemError(db, rc);
testcase( sqlite3GlobalConfig.xLog!=0 );
- sqlite3_log(rc, "statement aborts at %d: [%s] %s",
- (int)(pOp - aOp), p->zSql, p->zErrMsg);
+ sqlite3_log(rc, "statement aborts at %d: %s; [%s]",
+ (int)(pOp - aOp), p->zErrMsg, p->zSql);
if( p->eVdbeState==VDBE_RUN_STATE ) sqlite3VdbeHalt(p);
if( rc==SQLITE_IOERR_NOMEM ) sqlite3OomFault(db);
if( rc==SQLITE_CORRUPT && db->autoCommit==0 ){
@@ -102750,6 +103681,7 @@ SQLITE_API int sqlite3_blob_open(
char *zErr = 0;
Table *pTab;
Incrblob *pBlob = 0;
+ int iDb;
Parse sParse;
#ifdef SQLITE_ENABLE_API_ARMOR
@@ -102795,7 +103727,10 @@ SQLITE_API int sqlite3_blob_open(
sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable);
}
#endif
- if( !pTab ){
+ if( pTab==0
+ || ((iDb = sqlite3SchemaToIndex(db, pTab->pSchema))==1 &&
+ sqlite3OpenTempDatabase(&sParse))
+ ){
if( sParse.zErrMsg ){
sqlite3DbFree(db, zErr);
zErr = sParse.zErrMsg;
@@ -102806,15 +103741,11 @@ SQLITE_API int sqlite3_blob_open(
goto blob_open_out;
}
pBlob->pTab = pTab;
- pBlob->zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName;
+ pBlob->zDb = db->aDb[iDb].zDbSName;
/* Now search pTab for the exact column. */
- for(iCol=0; iCol<pTab->nCol; iCol++) {
- if( sqlite3StrICmp(pTab->aCol[iCol].zCnName, zColumn)==0 ){
- break;
- }
- }
- if( iCol==pTab->nCol ){
+ iCol = sqlite3ColumnIndex(pTab, zColumn);
+ if( iCol<0 ){
sqlite3DbFree(db, zErr);
zErr = sqlite3MPrintf(db, "no such column: \"%s\"", zColumn);
rc = SQLITE_ERROR;
@@ -102894,7 +103825,6 @@ SQLITE_API int sqlite3_blob_open(
{OP_Halt, 0, 0, 0}, /* 5 */
};
Vdbe *v = (Vdbe *)pBlob->pStmt;
- int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
VdbeOp *aOp;
sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, wrFlag,
@@ -103472,9 +104402,12 @@ struct VdbeSorter {
u8 iPrev; /* Previous thread used to flush PMA */
u8 nTask; /* Size of aTask[] array */
u8 typeMask;
- SortSubtask aTask[1]; /* One or more subtasks */
+ SortSubtask aTask[FLEXARRAY]; /* One or more subtasks */
};
+/* Size (in bytes) of a VdbeSorter object that works with N or fewer subtasks */
+#define SZ_VDBESORTER(N) (offsetof(VdbeSorter,aTask)+(N)*sizeof(SortSubtask))
+
#define SORTER_TYPE_INTEGER 0x01
#define SORTER_TYPE_TEXT 0x02
@@ -104076,7 +105009,7 @@ SQLITE_PRIVATE int sqlite3VdbeSorterInit(
VdbeSorter *pSorter; /* The new sorter */
KeyInfo *pKeyInfo; /* Copy of pCsr->pKeyInfo with db==0 */
int szKeyInfo; /* Size of pCsr->pKeyInfo in bytes */
- int sz; /* Size of pSorter in bytes */
+ i64 sz; /* Size of pSorter in bytes */
int rc = SQLITE_OK;
#if SQLITE_MAX_WORKER_THREADS==0
# define nWorker 0
@@ -104104,8 +105037,10 @@ SQLITE_PRIVATE int sqlite3VdbeSorterInit(
assert( pCsr->pKeyInfo );
assert( !pCsr->isEphemeral );
assert( pCsr->eCurType==CURTYPE_SORTER );
- szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nKeyField-1)*sizeof(CollSeq*);
- sz = sizeof(VdbeSorter) + nWorker * sizeof(SortSubtask);
+ assert( sizeof(KeyInfo) + UMXV(pCsr->pKeyInfo->nKeyField)*sizeof(CollSeq*)
+ < 0x7fffffff );
+ szKeyInfo = SZ_KEYINFO(pCsr->pKeyInfo->nKeyField+1);
+ sz = SZ_VDBESORTER(nWorker+1);
pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo);
pCsr->uc.pSorter = pSorter;
@@ -104317,7 +105252,7 @@ static int vdbeSorterJoinAll(VdbeSorter *pSorter, int rcin){
*/
static MergeEngine *vdbeMergeEngineNew(int nReader){
int N = 2; /* Smallest power of two >= nReader */
- int nByte; /* Total bytes of space to allocate */
+ i64 nByte; /* Total bytes of space to allocate */
MergeEngine *pNew; /* Pointer to allocated object to return */
assert( nReader<=SORTER_MAX_MERGE_COUNT );
@@ -104569,6 +105504,10 @@ static int vdbeSorterSort(SortSubtask *pTask, SorterList *pList){
p->u.pNext = 0;
for(i=0; aSlot[i]; i++){
p = vdbeSorterMerge(pTask, p, aSlot[i]);
+ /* ,--Each aSlot[] holds twice as much as the previous. So we cannot use
+ ** | up all 64 aSlots[] with only a 64-bit address space.
+ ** v */
+ assert( i<ArraySize(aSlot) );
aSlot[i] = 0;
}
aSlot[i] = p;
@@ -107360,7 +108299,6 @@ static int lookupName(
Schema *pSchema = 0; /* Schema of the expression */
int eNewExprOp = TK_COLUMN; /* New value for pExpr->op on success */
Table *pTab = 0; /* Table holding the row */
- Column *pCol; /* A column of pTab */
ExprList *pFJMatch = 0; /* Matches for FULL JOIN .. USING */
const char *zCol = pRight->u.zToken;
@@ -107411,7 +108349,6 @@ static int lookupName(
if( pSrcList ){
for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){
- u8 hCol;
pTab = pItem->pSTab;
assert( pTab!=0 && pTab->zName!=0 );
assert( pTab->nCol>0 || pParse->nErr );
@@ -107499,43 +108436,38 @@ static int lookupName(
sqlite3RenameTokenRemap(pParse, 0, (void*)&pExpr->y.pTab);
}
}
- hCol = sqlite3StrIHash(zCol);
- for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){
- if( pCol->hName==hCol
- && sqlite3StrICmp(pCol->zCnName, zCol)==0
- ){
- if( cnt>0 ){
- if( pItem->fg.isUsing==0
- || sqlite3IdListIndex(pItem->u3.pUsing, zCol)<0
- ){
- /* Two or more tables have the same column name which is
- ** not joined by USING. This is an error. Signal as much
- ** by clearing pFJMatch and letting cnt go above 1. */
- sqlite3ExprListDelete(db, pFJMatch);
- pFJMatch = 0;
- }else
- if( (pItem->fg.jointype & JT_RIGHT)==0 ){
- /* An INNER or LEFT JOIN. Use the left-most table */
- continue;
- }else
- if( (pItem->fg.jointype & JT_LEFT)==0 ){
- /* A RIGHT JOIN. Use the right-most table */
- cnt = 0;
- sqlite3ExprListDelete(db, pFJMatch);
- pFJMatch = 0;
- }else{
- /* For a FULL JOIN, we must construct a coalesce() func */
- extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn);
- }
- }
- cnt++;
- pMatch = pItem;
- /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */
- pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j;
- if( pItem->fg.isNestedFrom ){
- sqlite3SrcItemColumnUsed(pItem, j);
+ j = sqlite3ColumnIndex(pTab, zCol);
+ if( j>=0 ){
+ if( cnt>0 ){
+ if( pItem->fg.isUsing==0
+ || sqlite3IdListIndex(pItem->u3.pUsing, zCol)<0
+ ){
+ /* Two or more tables have the same column name which is
+ ** not joined by USING. This is an error. Signal as much
+ ** by clearing pFJMatch and letting cnt go above 1. */
+ sqlite3ExprListDelete(db, pFJMatch);
+ pFJMatch = 0;
+ }else
+ if( (pItem->fg.jointype & JT_RIGHT)==0 ){
+ /* An INNER or LEFT JOIN. Use the left-most table */
+ continue;
+ }else
+ if( (pItem->fg.jointype & JT_LEFT)==0 ){
+ /* A RIGHT JOIN. Use the right-most table */
+ cnt = 0;
+ sqlite3ExprListDelete(db, pFJMatch);
+ pFJMatch = 0;
+ }else{
+ /* For a FULL JOIN, we must construct a coalesce() func */
+ extendFJMatch(pParse, &pFJMatch, pMatch, pExpr->iColumn);
}
- break;
+ }
+ cnt++;
+ pMatch = pItem;
+ /* Substitute the rowid (column -1) for the INTEGER PRIMARY KEY */
+ pExpr->iColumn = j==pTab->iPKey ? -1 : (i16)j;
+ if( pItem->fg.isNestedFrom ){
+ sqlite3SrcItemColumnUsed(pItem, j);
}
}
if( 0==cnt && VisibleRowid(pTab) ){
@@ -107625,23 +108557,18 @@ static int lookupName(
if( pTab ){
int iCol;
- u8 hCol = sqlite3StrIHash(zCol);
pSchema = pTab->pSchema;
cntTab++;
- for(iCol=0, pCol=pTab->aCol; iCol<pTab->nCol; iCol++, pCol++){
- if( pCol->hName==hCol
- && sqlite3StrICmp(pCol->zCnName, zCol)==0
- ){
- if( iCol==pTab->iPKey ){
- iCol = -1;
- }
- break;
+ iCol = sqlite3ColumnIndex(pTab, zCol);
+ if( iCol>=0 ){
+ if( pTab->iPKey==iCol ) iCol = -1;
+ }else{
+ if( sqlite3IsRowid(zCol) && VisibleRowid(pTab) ){
+ iCol = -1;
+ }else{
+ iCol = pTab->nCol;
}
}
- if( iCol>=pTab->nCol && sqlite3IsRowid(zCol) && VisibleRowid(pTab) ){
- /* IMP: R-51414-32910 */
- iCol = -1;
- }
if( iCol<pTab->nCol ){
cnt++;
pMatch = 0;
@@ -108280,13 +109207,12 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
** sqlite_version() that might change over time cannot be used
** in an index or generated column. Curiously, they can be used
** in a CHECK constraint. SQLServer, MySQL, and PostgreSQL all
- ** all this. */
+ ** allow this. */
sqlite3ResolveNotValid(pParse, pNC, "non-deterministic functions",
NC_IdxExpr|NC_PartIdx|NC_GenCol, 0, pExpr);
}else{
assert( (NC_SelfRef & 0xff)==NC_SelfRef ); /* Must fit in 8 bits */
pExpr->op2 = pNC->ncFlags & NC_SelfRef;
- if( pNC->ncFlags & NC_FromDDL ) ExprSetProperty(pExpr, EP_FromDDL);
}
if( (pDef->funcFlags & SQLITE_FUNC_INTERNAL)!=0
&& pParse->nested==0
@@ -108302,6 +109228,7 @@ static int resolveExprStep(Walker *pWalker, Expr *pExpr){
if( (pDef->funcFlags & (SQLITE_FUNC_DIRECT|SQLITE_FUNC_UNSAFE))!=0
&& !IN_RENAME_OBJECT
){
+ if( pNC->ncFlags & NC_FromDDL ) ExprSetProperty(pExpr, EP_FromDDL);
sqlite3ExprFunctionUsable(pParse, pExpr, pDef);
}
}
@@ -109355,20 +110282,22 @@ SQLITE_PRIVATE int sqlite3ResolveSelfReference(
Expr *pExpr, /* Expression to resolve. May be NULL. */
ExprList *pList /* Expression list to resolve. May be NULL. */
){
- SrcList sSrc; /* Fake SrcList for pParse->pNewTable */
+ SrcList *pSrc; /* Fake SrcList for pParse->pNewTable */
NameContext sNC; /* Name context for pParse->pNewTable */
int rc;
+ u8 srcSpace[SZ_SRCLIST_1]; /* Memory space for the fake SrcList */
assert( type==0 || pTab!=0 );
assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr
|| type==NC_GenCol || pTab==0 );
memset(&sNC, 0, sizeof(sNC));
- memset(&sSrc, 0, sizeof(sSrc));
+ pSrc = (SrcList*)srcSpace;
+ memset(pSrc, 0, SZ_SRCLIST_1);
if( pTab ){
- sSrc.nSrc = 1;
- sSrc.a[0].zName = pTab->zName;
- sSrc.a[0].pSTab = pTab;
- sSrc.a[0].iCursor = -1;
+ pSrc->nSrc = 1;
+ pSrc->a[0].zName = pTab->zName;
+ pSrc->a[0].pSTab = pTab;
+ pSrc->a[0].iCursor = -1;
if( pTab->pSchema!=pParse->db->aDb[1].pSchema ){
/* Cause EP_FromDDL to be set on TK_FUNCTION nodes of non-TEMP
** schema elements */
@@ -109376,7 +110305,7 @@ SQLITE_PRIVATE int sqlite3ResolveSelfReference(
}
}
sNC.pParse = pParse;
- sNC.pSrcList = &sSrc;
+ sNC.pSrcList = pSrc;
sNC.ncFlags = type | NC_IsDDL;
if( (rc = sqlite3ResolveExprNames(&sNC, pExpr))!=SQLITE_OK ) return rc;
if( pList ) rc = sqlite3ResolveExprListNames(&sNC, pList);
@@ -109866,7 +110795,7 @@ static int codeCompare(
p5 = binaryCompareP5(pLeft, pRight, jumpIfNull);
addr = sqlite3VdbeAddOp4(pParse->pVdbe, opcode, in2, dest, in1,
(void*)p4, P4_COLLSEQ);
- sqlite3VdbeChangeP5(pParse->pVdbe, (u8)p5);
+ sqlite3VdbeChangeP5(pParse->pVdbe, (u16)p5);
return addr;
}
@@ -111125,7 +112054,7 @@ static Expr *exprDup(
SQLITE_PRIVATE With *sqlite3WithDup(sqlite3 *db, With *p){
With *pRet = 0;
if( p ){
- sqlite3_int64 nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1);
+ sqlite3_int64 nByte = SZ_WITH(p->nCte);
pRet = sqlite3DbMallocZero(db, nByte);
if( pRet ){
int i;
@@ -111236,7 +112165,6 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3 *db, const ExprList *p, int
}
pItem->zEName = sqlite3DbStrDup(db, pOldItem->zEName);
pItem->fg = pOldItem->fg;
- pItem->fg.done = 0;
pItem->u = pOldItem->u;
}
return pNew;
@@ -111253,11 +112181,9 @@ SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3 *db, const ExprList *p, int
SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int flags){
SrcList *pNew;
int i;
- int nByte;
assert( db!=0 );
if( p==0 ) return 0;
- nByte = sizeof(*p) + (p->nSrc>0 ? sizeof(p->a[0]) * (p->nSrc-1) : 0);
- pNew = sqlite3DbMallocRawNN(db, nByte );
+ pNew = sqlite3DbMallocRawNN(db, SZ_SRCLIST(p->nSrc) );
if( pNew==0 ) return 0;
pNew->nSrc = pNew->nAlloc = p->nSrc;
for(i=0; i<p->nSrc; i++){
@@ -111319,16 +112245,13 @@ SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3 *db, const IdList *p){
int i;
assert( db!=0 );
if( p==0 ) return 0;
- assert( p->eU4!=EU4_EXPR );
- pNew = sqlite3DbMallocRawNN(db, sizeof(*pNew)+(p->nId-1)*sizeof(p->a[0]) );
+ pNew = sqlite3DbMallocRawNN(db, SZ_IDLIST(p->nId));
if( pNew==0 ) return 0;
pNew->nId = p->nId;
- pNew->eU4 = p->eU4;
for(i=0; i<p->nId; i++){
struct IdList_item *pNewItem = &pNew->a[i];
const struct IdList_item *pOldItem = &p->a[i];
pNewItem->zName = sqlite3DbStrDup(db, pOldItem->zName);
- pNewItem->u4 = pOldItem->u4;
}
return pNew;
}
@@ -111354,7 +112277,7 @@ SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3 *db, const Select *pDup, int fla
pNew->pLimit = sqlite3ExprDup(db, p->pLimit, flags);
pNew->iLimit = 0;
pNew->iOffset = 0;
- pNew->selFlags = p->selFlags & ~SF_UsesEphemeral;
+ pNew->selFlags = p->selFlags & ~(u32)SF_UsesEphemeral;
pNew->addrOpenEphm[0] = -1;
pNew->addrOpenEphm[1] = -1;
pNew->nSelectRow = p->nSelectRow;
@@ -111406,7 +112329,7 @@ SQLITE_PRIVATE SQLITE_NOINLINE ExprList *sqlite3ExprListAppendNew(
struct ExprList_item *pItem;
ExprList *pList;
- pList = sqlite3DbMallocRawNN(db, sizeof(ExprList)+sizeof(pList->a[0])*4 );
+ pList = sqlite3DbMallocRawNN(db, SZ_EXPRLIST(4));
if( pList==0 ){
sqlite3ExprDelete(db, pExpr);
return 0;
@@ -111426,8 +112349,7 @@ SQLITE_PRIVATE SQLITE_NOINLINE ExprList *sqlite3ExprListAppendGrow(
struct ExprList_item *pItem;
ExprList *pNew;
pList->nAlloc *= 2;
- pNew = sqlite3DbRealloc(db, pList,
- sizeof(*pList)+(pList->nAlloc-1)*sizeof(pList->a[0]));
+ pNew = sqlite3DbRealloc(db, pList, SZ_EXPRLIST(pList->nAlloc));
if( pNew==0 ){
sqlite3ExprListDelete(db, pList);
sqlite3ExprDelete(db, pExpr);
@@ -112033,7 +112955,7 @@ static int sqlite3ExprIsTableConstant(Expr *p, int iCur, int bAllowSubq){
** (4a) pExpr must come from an ON clause..
** (4b) and specifically the ON clause associated with the LEFT JOIN.
**
-** (5) If pSrc is not the right operand of a LEFT JOIN or the left
+** (5) If pSrc is the right operand of a LEFT JOIN or the left
** operand of a RIGHT JOIN, then pExpr must be from the WHERE
** clause, not an ON clause.
**
@@ -112356,13 +113278,7 @@ SQLITE_PRIVATE const char *sqlite3RowidAlias(Table *pTab){
int ii;
assert( VisibleRowid(pTab) );
for(ii=0; ii<ArraySize(azOpt); ii++){
- int iCol;
- for(iCol=0; iCol<pTab->nCol; iCol++){
- if( sqlite3_stricmp(azOpt[ii], pTab->aCol[iCol].zCnName)==0 ) break;
- }
- if( iCol==pTab->nCol ){
- return azOpt[ii];
- }
+ if( sqlite3ColumnIndex(pTab, azOpt[ii])<0 ) return azOpt[ii];
}
return 0;
}
@@ -112672,6 +113588,7 @@ SQLITE_PRIVATE int sqlite3FindInIndex(
if( aiMap ) aiMap[i] = j;
}
+ assert( nExpr>0 && nExpr<BMS );
assert( i==nExpr || colUsed!=(MASKBIT(nExpr)-1) );
if( colUsed==(MASKBIT(nExpr)-1) ){
/* If we reach this point, that means the index pIdx is usable */
@@ -112765,7 +113682,7 @@ static char *exprINAffinity(Parse *pParse, const Expr *pExpr){
char *zRet;
assert( pExpr->op==TK_IN );
- zRet = sqlite3DbMallocRaw(pParse->db, nVal+1);
+ zRet = sqlite3DbMallocRaw(pParse->db, 1+(i64)nVal);
if( zRet ){
int i;
for(i=0; i<nVal; i++){
@@ -112851,6 +113768,7 @@ static int findCompatibleInRhsSubrtn(
assert( pOp->opcode==OP_BeginSubrtn );
pSig = pOp->p4.pSubrtnSig;
assert( pSig!=0 );
+ if( !pSig->bComplete ) continue;
if( pNewSig->selId!=pSig->selId ) continue;
if( strcmp(pNewSig->zAff,pSig->zAff)!=0 ) continue;
pExpr->y.sub.iAddr = pSig->iAddr;
@@ -112897,6 +113815,7 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN(
KeyInfo *pKeyInfo = 0; /* Key information */
int nVal; /* Size of vector pLeft */
Vdbe *v; /* The prepared statement under construction */
+ SubrtnSig *pSig = 0; /* Signature for this subroutine */
v = pParse->pVdbe;
assert( v!=0 );
@@ -112917,7 +113836,6 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN(
** Compute a signature for the RHS of the IN operator to facility
** finding and reusing prior instances of the same IN operator.
*/
- SubrtnSig *pSig = 0;
assert( !ExprUseXSelect(pExpr) || pExpr->x.pSelect!=0 );
if( ExprUseXSelect(pExpr) && (pExpr->x.pSelect->selFlags & SF_All)==0 ){
pSig = sqlite3DbMallocRawNN(pParse->db, sizeof(pSig[0]));
@@ -112960,6 +113878,7 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN(
pExpr->y.sub.iAddr =
sqlite3VdbeAddOp2(v, OP_BeginSubrtn, 0, pExpr->y.sub.regReturn) + 1;
if( pSig ){
+ pSig->bComplete = 0;
pSig->iAddr = pExpr->y.sub.iAddr;
pSig->regReturn = pExpr->y.sub.regReturn;
pSig->iTable = iTab;
@@ -113023,11 +113942,12 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN(
sqlite3SelectDelete(pParse->db, pCopy);
sqlite3DbFree(pParse->db, dest.zAffSdst);
if( addrBloom ){
+ /* Remember that location of the Bloom filter in the P3 operand
+ ** of the OP_Once that began this subroutine. tag-202407032019 */
sqlite3VdbeGetOp(v, addrOnce)->p3 = dest.iSDParm2;
if( dest.iSDParm2==0 ){
- sqlite3VdbeChangeToNoop(v, addrBloom);
- }else{
- sqlite3VdbeGetOp(v, addrOnce)->p3 = dest.iSDParm2;
+ /* If the Bloom filter won't actually be used, keep it small */
+ sqlite3VdbeGetOp(v, addrBloom)->p1 = 10;
}
}
if( rc ){
@@ -113095,6 +114015,7 @@ SQLITE_PRIVATE void sqlite3CodeRhsOfIN(
sqlite3ReleaseTempReg(pParse, r1);
sqlite3ReleaseTempReg(pParse, r2);
}
+ if( pSig ) pSig->bComplete = 1;
if( pKeyInfo ){
sqlite3VdbeChangeP4(v, addr, (void *)pKeyInfo, P4_KEYINFO);
}
@@ -113473,7 +114394,7 @@ static void sqlite3ExprCodeIN(
if( ExprHasProperty(pExpr, EP_Subrtn) ){
const VdbeOp *pOp = sqlite3VdbeGetOp(v, pExpr->y.sub.iAddr);
assert( pOp->opcode==OP_Once || pParse->nErr );
- if( pOp->opcode==OP_Once && pOp->p3>0 ){
+ if( pOp->opcode==OP_Once && pOp->p3>0 ){ /* tag-202407032019 */
assert( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) );
sqlite3VdbeAddOp4Int(v, OP_Filter, pOp->p3, destIfFalse,
rLhs, nVector); VdbeCoverage(v);
@@ -114065,7 +114986,7 @@ static SQLITE_NOINLINE int sqlite3IndexedExprLookup(
/*
-** Expresion pExpr is guaranteed to be a TK_COLUMN or equivalent. This
+** Expression pExpr is guaranteed to be a TK_COLUMN or equivalent. This
** function checks the Parse.pIdxPartExpr list to see if this column
** can be replaced with a constant value. If so, it generates code to
** put the constant value in a register (ideally, but not necessarily,
@@ -115322,11 +116243,11 @@ SQLITE_PRIVATE void sqlite3ExprIfTrue(Parse *pParse, Expr *pExpr, int dest, int
assert( TK_ISNULL==OP_IsNull ); testcase( op==TK_ISNULL );
assert( TK_NOTNULL==OP_NotNull ); testcase( op==TK_NOTNULL );
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
- sqlite3VdbeTypeofColumn(v, r1);
+ assert( regFree1==0 || regFree1==r1 );
+ if( regFree1 ) sqlite3VdbeTypeofColumn(v, r1);
sqlite3VdbeAddOp2(v, op, r1, dest);
VdbeCoverageIf(v, op==TK_ISNULL);
VdbeCoverageIf(v, op==TK_NOTNULL);
- testcase( regFree1==0 );
break;
}
case TK_BETWEEN: {
@@ -115497,11 +116418,11 @@ SQLITE_PRIVATE void sqlite3ExprIfFalse(Parse *pParse, Expr *pExpr, int dest, int
case TK_ISNULL:
case TK_NOTNULL: {
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
- sqlite3VdbeTypeofColumn(v, r1);
+ assert( regFree1==0 || regFree1==r1 );
+ if( regFree1 ) sqlite3VdbeTypeofColumn(v, r1);
sqlite3VdbeAddOp2(v, op, r1, dest);
testcase( op==TK_ISNULL ); VdbeCoverageIf(v, op==TK_ISNULL);
testcase( op==TK_NOTNULL ); VdbeCoverageIf(v, op==TK_NOTNULL);
- testcase( regFree1==0 );
break;
}
case TK_BETWEEN: {
@@ -115567,16 +116488,23 @@ SQLITE_PRIVATE void sqlite3ExprIfFalseDup(Parse *pParse, Expr *pExpr, int dest,i
** same as that currently bound to variable pVar, non-zero is returned.
** Otherwise, if the values are not the same or if pExpr is not a simple
** SQL value, zero is returned.
+**
+** If the SQLITE_EnableQPSG flag is set on the database connection, then
+** this routine always returns false.
*/
-static int exprCompareVariable(
+static SQLITE_NOINLINE int exprCompareVariable(
const Parse *pParse,
const Expr *pVar,
const Expr *pExpr
){
- int res = 0;
+ int res = 2;
int iVar;
sqlite3_value *pL, *pR = 0;
+ if( pExpr->op==TK_VARIABLE && pVar->iColumn==pExpr->iColumn ){
+ return 0;
+ }
+ if( (pParse->db->flags & SQLITE_EnableQPSG)!=0 ) return 2;
sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, SQLITE_AFF_BLOB, &pR);
if( pR ){
iVar = pVar->iColumn;
@@ -115586,12 +116514,11 @@ static int exprCompareVariable(
if( sqlite3_value_type(pL)==SQLITE_TEXT ){
sqlite3_value_text(pL); /* Make sure the encoding is UTF-8 */
}
- res = 0==sqlite3MemCompare(pL, pR, 0);
+ res = sqlite3MemCompare(pL, pR, 0) ? 2 : 0;
}
sqlite3ValueFree(pR);
sqlite3ValueFree(pL);
}
-
return res;
}
@@ -115617,12 +116544,10 @@ static int exprCompareVariable(
** just might result in some slightly slower code. But returning
** an incorrect 0 or 1 could lead to a malfunction.
**
-** If pParse is not NULL then TK_VARIABLE terms in pA with bindings in
-** pParse->pReprepare can be matched against literals in pB. The
-** pParse->pVdbe->expmask bitmask is updated for each variable referenced.
-** If pParse is NULL (the normal case) then any TK_VARIABLE term in
-** Argument pParse should normally be NULL. If it is not NULL and pA or
-** pB causes a return value of 2.
+** If pParse is not NULL and SQLITE_EnableQPSG is off then TK_VARIABLE
+** terms in pA with bindings in pParse->pReprepare can be matched against
+** literals in pB. The pParse->pVdbe->expmask bitmask is updated for
+** each variable referenced.
*/
SQLITE_PRIVATE int sqlite3ExprCompare(
const Parse *pParse,
@@ -115634,8 +116559,8 @@ SQLITE_PRIVATE int sqlite3ExprCompare(
if( pA==0 || pB==0 ){
return pB==pA ? 0 : 2;
}
- if( pParse && pA->op==TK_VARIABLE && exprCompareVariable(pParse, pA, pB) ){
- return 0;
+ if( pParse && pA->op==TK_VARIABLE ){
+ return exprCompareVariable(pParse, pA, pB);
}
combinedFlags = pA->flags | pB->flags;
if( combinedFlags & EP_IntValue ){
@@ -115831,17 +116756,69 @@ static int exprImpliesNotNull(
}
/*
+** Return true if the boolean value of the expression is always either
+** FALSE or NULL.
+*/
+static int sqlite3ExprIsNotTrue(Expr *pExpr){
+ int v;
+ if( pExpr->op==TK_NULL ) return 1;
+ if( pExpr->op==TK_TRUEFALSE && sqlite3ExprTruthValue(pExpr)==0 ) return 1;
+ v = 1;
+ if( sqlite3ExprIsInteger(pExpr, &v, 0) && v==0 ) return 1;
+ return 0;
+}
+
+/*
+** Return true if the expression is one of the following:
+**
+** CASE WHEN x THEN y END
+** CASE WHEN x THEN y ELSE NULL END
+** CASE WHEN x THEN y ELSE false END
+** iif(x,y)
+** iif(x,y,NULL)
+** iif(x,y,false)
+*/
+static int sqlite3ExprIsIIF(sqlite3 *db, const Expr *pExpr){
+ ExprList *pList;
+ if( pExpr->op==TK_FUNCTION ){
+ const char *z = pExpr->u.zToken;
+ FuncDef *pDef;
+ if( (z[0]!='i' && z[0]!='I') ) return 0;
+ if( pExpr->x.pList==0 ) return 0;
+ pDef = sqlite3FindFunction(db, z, pExpr->x.pList->nExpr, ENC(db), 0);
+#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
+ if( pDef==0 ) return 0;
+#else
+ if( NEVER(pDef==0) ) return 0;
+#endif
+ if( (pDef->funcFlags & SQLITE_FUNC_INLINE)==0 ) return 0;
+ if( SQLITE_PTR_TO_INT(pDef->pUserData)!=INLINEFUNC_iif ) return 0;
+ }else if( pExpr->op==TK_CASE ){
+ if( pExpr->pLeft!=0 ) return 0;
+ }else{
+ return 0;
+ }
+ pList = pExpr->x.pList;
+ assert( pList!=0 );
+ if( pList->nExpr==2 ) return 1;
+ if( pList->nExpr==3 && sqlite3ExprIsNotTrue(pList->a[2].pExpr) ) return 1;
+ return 0;
+}
+
+/*
** Return true if we can prove the pE2 will always be true if pE1 is
** true. Return false if we cannot complete the proof or if pE2 might
** be false. Examples:
**
-** pE1: x==5 pE2: x==5 Result: true
-** pE1: x>0 pE2: x==5 Result: false
-** pE1: x=21 pE2: x=21 OR y=43 Result: true
-** pE1: x!=123 pE2: x IS NOT NULL Result: true
-** pE1: x!=?1 pE2: x IS NOT NULL Result: true
-** pE1: x IS NULL pE2: x IS NOT NULL Result: false
-** pE1: x IS ?2 pE2: x IS NOT NULL Result: false
+** pE1: x==5 pE2: x==5 Result: true
+** pE1: x>0 pE2: x==5 Result: false
+** pE1: x=21 pE2: x=21 OR y=43 Result: true
+** pE1: x!=123 pE2: x IS NOT NULL Result: true
+** pE1: x!=?1 pE2: x IS NOT NULL Result: true
+** pE1: x IS NULL pE2: x IS NOT NULL Result: false
+** pE1: x IS ?2 pE2: x IS NOT NULL Result: false
+** pE1: iif(x,y) pE2: x Result: true
+** PE1: iif(x,y,0) pE2: x Result: true
**
** When comparing TK_COLUMN nodes between pE1 and pE2, if pE2 has
** Expr.iTable<0 then assume a table number given by iTab.
@@ -115875,6 +116852,9 @@ SQLITE_PRIVATE int sqlite3ExprImpliesExpr(
){
return 1;
}
+ if( sqlite3ExprIsIIF(pParse->db, pE1) ){
+ return sqlite3ExprImpliesExpr(pParse,pE1->x.pList->a[0].pExpr,pE2,iTab);
+ }
return 0;
}
@@ -117247,13 +118227,13 @@ SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
assert( pNew->nCol>0 );
nAlloc = (((pNew->nCol-1)/8)*8)+8;
assert( nAlloc>=pNew->nCol && nAlloc%8==0 && nAlloc-pNew->nCol<8 );
- pNew->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*nAlloc);
+ pNew->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*(u32)nAlloc);
pNew->zName = sqlite3MPrintf(db, "sqlite_altertab_%s", pTab->zName);
if( !pNew->aCol || !pNew->zName ){
assert( db->mallocFailed );
goto exit_begin_add_column;
}
- memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol);
+ memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*(size_t)pNew->nCol);
for(i=0; i<pNew->nCol; i++){
Column *pCol = &pNew->aCol[i];
pCol->zCnName = sqlite3DbStrDup(db, pCol->zCnName);
@@ -117348,10 +118328,8 @@ SQLITE_PRIVATE void sqlite3AlterRenameColumn(
** altered. Set iCol to be the index of the column being renamed */
zOld = sqlite3NameFromToken(db, pOld);
if( !zOld ) goto exit_rename_column;
- for(iCol=0; iCol<pTab->nCol; iCol++){
- if( 0==sqlite3StrICmp(pTab->aCol[iCol].zCnName, zOld) ) break;
- }
- if( iCol==pTab->nCol ){
+ iCol = sqlite3ColumnIndex(pTab, zOld);
+ if( iCol<0 ){
sqlite3ErrorMsg(pParse, "no such column: \"%T\"", pOld);
goto exit_rename_column;
}
@@ -117854,6 +118832,7 @@ static int renameParseSql(
int bTemp /* True if SQL is from temp schema */
){
int rc;
+ u64 flags;
sqlite3ParseObjectInit(p, db);
if( zSql==0 ){
@@ -117862,11 +118841,21 @@ static int renameParseSql(
if( sqlite3StrNICmp(zSql,"CREATE ",7)!=0 ){
return SQLITE_CORRUPT_BKPT;
}
- db->init.iDb = bTemp ? 1 : sqlite3FindDbName(db, zDb);
+ if( bTemp ){
+ db->init.iDb = 1;
+ }else{
+ int iDb = sqlite3FindDbName(db, zDb);
+ assert( iDb>=0 && iDb<=0xff );
+ db->init.iDb = (u8)iDb;
+ }
p->eParseMode = PARSE_MODE_RENAME;
p->db = db;
p->nQueryLoop = 1;
+ flags = db->flags;
+ testcase( (db->flags & SQLITE_Comments)==0 && strstr(zSql," /* ")!=0 );
+ db->flags |= SQLITE_Comments;
rc = sqlite3RunParser(p, zSql);
+ db->flags = flags;
if( db->mallocFailed ) rc = SQLITE_NOMEM;
if( rc==SQLITE_OK
&& NEVER(p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0)
@@ -117929,10 +118918,11 @@ static int renameEditSql(
nQuot = sqlite3Strlen30(zQuot)-1;
}
- assert( nQuot>=nNew );
- zOut = sqlite3DbMallocZero(db, nSql + pRename->nList*nQuot + 1);
+ assert( nQuot>=nNew && nSql>=0 && nNew>=0 );
+ zOut = sqlite3DbMallocZero(db, (u64)nSql + pRename->nList*(u64)nQuot + 1);
}else{
- zOut = (char*)sqlite3DbMallocZero(db, (nSql*2+1) * 3);
+ assert( nSql>0 );
+ zOut = (char*)sqlite3DbMallocZero(db, (2*(u64)nSql + 1) * 3);
if( zOut ){
zBuf1 = &zOut[nSql*2+1];
zBuf2 = &zOut[nSql*4+2];
@@ -117944,16 +118934,17 @@ static int renameEditSql(
** with the new column name, or with single-quoted versions of themselves.
** All that remains is to construct and return the edited SQL string. */
if( zOut ){
- int nOut = nSql;
- memcpy(zOut, zSql, nSql);
+ i64 nOut = nSql;
+ assert( nSql>0 );
+ memcpy(zOut, zSql, (size_t)nSql);
while( pRename->pList ){
int iOff; /* Offset of token to replace in zOut */
- u32 nReplace;
+ i64 nReplace;
const char *zReplace;
RenameToken *pBest = renameColumnTokenNext(pRename);
if( zNew ){
- if( bQuote==0 && sqlite3IsIdChar(*pBest->t.z) ){
+ if( bQuote==0 && sqlite3IsIdChar(*(u8*)pBest->t.z) ){
nReplace = nNew;
zReplace = zNew;
}else{
@@ -117971,14 +118962,15 @@ static int renameEditSql(
memcpy(zBuf1, pBest->t.z, pBest->t.n);
zBuf1[pBest->t.n] = 0;
sqlite3Dequote(zBuf1);
- sqlite3_snprintf(nSql*2, zBuf2, "%Q%s", zBuf1,
+ assert( nSql < 0x15555554 /* otherwise malloc would have failed */ );
+ sqlite3_snprintf((int)(nSql*2), zBuf2, "%Q%s", zBuf1,
pBest->t.z[pBest->t.n]=='\'' ? " " : ""
);
zReplace = zBuf2;
nReplace = sqlite3Strlen30(zReplace);
}
- iOff = pBest->t.z - zSql;
+ iOff = (int)(pBest->t.z - zSql);
if( pBest->t.n!=nReplace ){
memmove(&zOut[iOff + nReplace], &zOut[iOff + pBest->t.n],
nOut - (iOff + pBest->t.n)
@@ -118004,11 +118996,12 @@ static int renameEditSql(
** Set all pEList->a[].fg.eEName fields in the expression-list to val.
*/
static void renameSetENames(ExprList *pEList, int val){
+ assert( val==ENAME_NAME || val==ENAME_TAB || val==ENAME_SPAN );
if( pEList ){
int i;
for(i=0; i<pEList->nExpr; i++){
assert( val==ENAME_NAME || pEList->a[i].fg.eEName==ENAME_NAME );
- pEList->a[i].fg.eEName = val;
+ pEList->a[i].fg.eEName = val&0x3;
}
}
}
@@ -118265,7 +119258,7 @@ static void renameColumnFunc(
if( sParse.pNewTable ){
if( IsView(sParse.pNewTable) ){
Select *pSelect = sParse.pNewTable->u.view.pSelect;
- pSelect->selFlags &= ~SF_View;
+ pSelect->selFlags &= ~(u32)SF_View;
sParse.rc = SQLITE_OK;
sqlite3SelectPrep(&sParse, pSelect, 0);
rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc);
@@ -118483,7 +119476,7 @@ static void renameTableFunc(
sNC.pParse = &sParse;
assert( pSelect->selFlags & SF_View );
- pSelect->selFlags &= ~SF_View;
+ pSelect->selFlags &= ~(u32)SF_View;
sqlite3SelectPrep(&sParse, pTab->u.view.pSelect, &sNC);
if( sParse.nErr ){
rc = sParse.rc;
@@ -118656,7 +119649,7 @@ static void renameQuotefixFunc(
if( sParse.pNewTable ){
if( IsView(sParse.pNewTable) ){
Select *pSelect = sParse.pNewTable->u.view.pSelect;
- pSelect->selFlags &= ~SF_View;
+ pSelect->selFlags &= ~(u32)SF_View;
sParse.rc = SQLITE_OK;
sqlite3SelectPrep(&sParse, pSelect, 0);
rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc);
@@ -118755,10 +119748,10 @@ static void renameTableTest(
if( zDb && zInput ){
int rc;
Parse sParse;
- int flags = db->flags;
+ u64 flags = db->flags;
if( bNoDQS ) db->flags &= ~(SQLITE_DqsDML|SQLITE_DqsDDL);
rc = renameParseSql(&sParse, zDb, db, zInput, bTemp);
- db->flags |= (flags & (SQLITE_DqsDML|SQLITE_DqsDDL));
+ db->flags = flags;
if( rc==SQLITE_OK ){
if( isLegacy==0 && sParse.pNewTable && IsView(sParse.pNewTable) ){
NameContext sNC;
@@ -119250,7 +120243,8 @@ static void openStatTable(
sqlite3NestedParse(pParse,
"CREATE TABLE %Q.%s(%s)", pDb->zDbSName, zTab, aTable[i].zCols
);
- aRoot[i] = (u32)pParse->regRoot;
+ assert( pParse->isCreate || pParse->nErr );
+ aRoot[i] = (u32)pParse->u1.cr.regRoot;
aCreateTbl[i] = OPFLAG_P2ISREG;
}
}else{
@@ -119441,7 +120435,7 @@ static void statInit(
int nCol; /* Number of columns in index being sampled */
int nKeyCol; /* Number of key columns */
int nColUp; /* nCol rounded up for alignment */
- int n; /* Bytes of space to allocate */
+ i64 n; /* Bytes of space to allocate */
sqlite3 *db = sqlite3_context_db_handle(context); /* Database connection */
#ifdef SQLITE_ENABLE_STAT4
/* Maximum number of samples. 0 if STAT4 data is not collected */
@@ -119477,7 +120471,7 @@ static void statInit(
p->db = db;
p->nEst = sqlite3_value_int64(argv[2]);
p->nRow = 0;
- p->nLimit = sqlite3_value_int64(argv[3]);
+ p->nLimit = sqlite3_value_int(argv[3]);
p->nCol = nCol;
p->nKeyCol = nKeyCol;
p->nSkipAhead = 0;
@@ -120610,16 +121604,6 @@ static void decodeIntArray(
while( z[0]!=0 && z[0]!=' ' ) z++;
while( z[0]==' ' ) z++;
}
-
- /* Set the bLowQual flag if the peak number of rows obtained
- ** from a full equality match is so large that a full table scan
- ** seems likely to be faster than using the index.
- */
- if( aLog[0] > 66 /* Index has more than 100 rows */
- && aLog[0] <= aLog[nOut-1] /* And only a single value seen */
- ){
- pIndex->bLowQual = 1;
- }
}
}
@@ -121215,7 +122199,7 @@ static void attachFunc(
if( aNew==0 ) return;
memcpy(aNew, db->aDb, sizeof(db->aDb[0])*2);
}else{
- aNew = sqlite3DbRealloc(db, db->aDb, sizeof(db->aDb[0])*(db->nDb+1) );
+ aNew = sqlite3DbRealloc(db, db->aDb, sizeof(db->aDb[0])*(1+(i64)db->nDb));
if( aNew==0 ) return;
}
db->aDb = aNew;
@@ -121234,6 +122218,12 @@ static void attachFunc(
sqlite3_free(zErr);
return;
}
+ if( (db->flags & SQLITE_AttachWrite)==0 ){
+ flags &= ~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE);
+ flags |= SQLITE_OPEN_READONLY;
+ }else if( (db->flags & SQLITE_AttachCreate)==0 ){
+ flags &= ~SQLITE_OPEN_CREATE;
+ }
assert( pVfs );
flags |= SQLITE_OPEN_MAIN_DB;
rc = sqlite3BtreeOpen(pVfs, zPath, db, &pNew->pBt, 0, flags);
@@ -121280,21 +122270,19 @@ static void attachFunc(
sqlite3BtreeEnterAll(db);
db->init.iDb = 0;
db->mDbFlags &= ~(DBFLAG_SchemaKnownOk);
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ if( db->setlkFlags & SQLITE_SETLK_BLOCK_ON_CONNECT ){
+ int val = 1;
+ sqlite3_file *fd = sqlite3PagerFile(sqlite3BtreePager(pNew->pBt));
+ sqlite3OsFileControlHint(fd, SQLITE_FCNTL_BLOCK_ON_CONNECT, &val);
+ }
+#endif
if( !REOPEN_AS_MEMDB(db) ){
rc = sqlite3Init(db, &zErrDyn);
}
sqlite3BtreeLeaveAll(db);
assert( zErrDyn==0 || rc!=SQLITE_OK );
}
-#ifdef SQLITE_USER_AUTHENTICATION
- if( rc==SQLITE_OK && !REOPEN_AS_MEMDB(db) ){
- u8 newAuth = 0;
- rc = sqlite3UserAuthCheckLogin(db, zName, &newAuth);
- if( newAuth<db->auth.authLevel ){
- rc = SQLITE_AUTH_USER;
- }
- }
-#endif
if( rc ){
if( ALWAYS(!REOPEN_AS_MEMDB(db)) ){
int iDb = db->nDb - 1;
@@ -121792,11 +122780,7 @@ SQLITE_PRIVATE int sqlite3AuthReadCol(
int rc; /* Auth callback return code */
if( db->init.busy ) return SQLITE_OK;
- rc = db->xAuth(db->pAuthArg, SQLITE_READ, zTab,zCol,zDb,pParse->zAuthContext
-#ifdef SQLITE_USER_AUTHENTICATION
- ,db->auth.zAuthUser
-#endif
- );
+ rc = db->xAuth(db->pAuthArg, SQLITE_READ, zTab,zCol,zDb,pParse->zAuthContext);
if( rc==SQLITE_DENY ){
char *z = sqlite3_mprintf("%s.%s", zTab, zCol);
if( db->nDb>2 || iDb!=0 ) z = sqlite3_mprintf("%s.%z", zDb, z);
@@ -121903,11 +122887,7 @@ SQLITE_PRIVATE int sqlite3AuthCheck(
testcase( zArg3==0 );
testcase( pParse->zAuthContext==0 );
- rc = db->xAuth(db->pAuthArg, code, zArg1, zArg2, zArg3, pParse->zAuthContext
-#ifdef SQLITE_USER_AUTHENTICATION
- ,db->auth.zAuthUser
-#endif
- );
+ rc = db->xAuth(db->pAuthArg,code,zArg1,zArg2,zArg3,pParse->zAuthContext);
if( rc==SQLITE_DENY ){
sqlite3ErrorMsg(pParse, "not authorized");
pParse->rc = SQLITE_AUTH;
@@ -122019,6 +122999,7 @@ static SQLITE_NOINLINE void lockTable(
}
}
+ assert( pToplevel->nTableLock < 0x7fff0000 );
nBytes = sizeof(TableLock) * (pToplevel->nTableLock+1);
pToplevel->aTableLock =
sqlite3DbReallocOrFree(pToplevel->db, pToplevel->aTableLock, nBytes);
@@ -122119,10 +123100,12 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){
|| sqlite3VdbeAssertMayAbort(v, pParse->mayAbort));
if( v ){
if( pParse->bReturning ){
- Returning *pReturning = pParse->u1.pReturning;
+ Returning *pReturning;
int addrRewind;
int reg;
+ assert( !pParse->isCreate );
+ pReturning = pParse->u1.d.pReturning;
if( pReturning->nRetCol ){
sqlite3VdbeAddOp0(v, OP_FkCheck);
addrRewind =
@@ -122140,17 +123123,6 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){
}
sqlite3VdbeAddOp0(v, OP_Halt);
-#if SQLITE_USER_AUTHENTICATION && !defined(SQLITE_OMIT_SHARED_CACHE)
- if( pParse->nTableLock>0 && db->init.busy==0 ){
- sqlite3UserAuthInit(db);
- if( db->auth.authLevel<UAUTH_User ){
- sqlite3ErrorMsg(pParse, "user not authenticated");
- pParse->rc = SQLITE_AUTH_USER;
- return;
- }
- }
-#endif
-
/* The cookie mask contains one bit for each database file open.
** (Bit 0 is for main, bit 1 is for temp, and so forth.) Bits are
** set for each database that is used. Generate code to start a
@@ -122209,7 +123181,9 @@ SQLITE_PRIVATE void sqlite3FinishCoding(Parse *pParse){
}
if( pParse->bReturning ){
- Returning *pRet = pParse->u1.pReturning;
+ Returning *pRet;
+ assert( !pParse->isCreate );
+ pRet = pParse->u1.d.pReturning;
if( pRet->nRetCol ){
sqlite3VdbeAddOp2(v, OP_OpenEphemeral, pRet->iRetCur, pRet->nRetCol);
}
@@ -122279,16 +123253,6 @@ SQLITE_PRIVATE void sqlite3NestedParse(Parse *pParse, const char *zFormat, ...){
pParse->nested--;
}
-#if SQLITE_USER_AUTHENTICATION
-/*
-** Return TRUE if zTable is the name of the system table that stores the
-** list of users and their access credentials.
-*/
-SQLITE_PRIVATE int sqlite3UserAuthTable(const char *zTable){
- return sqlite3_stricmp(zTable, "sqlite_user")==0;
-}
-#endif
-
/*
** Locate the in-memory structure that describes a particular database
** table given the name of that table and (optionally) the name of the
@@ -122307,13 +123271,6 @@ SQLITE_PRIVATE Table *sqlite3FindTable(sqlite3 *db, const char *zName, const cha
/* All mutexes are required for schema access. Make sure we hold them. */
assert( zDatabase!=0 || sqlite3BtreeHoldsAllMutexes(db) );
-#if SQLITE_USER_AUTHENTICATION
- /* Only the admin user is allowed to know that the sqlite_user table
- ** exists */
- if( db->auth.authLevel<UAUTH_Admin && sqlite3UserAuthTable(zName)!=0 ){
- return 0;
- }
-#endif
if( zDatabase ){
for(i=0; i<db->nDb; i++){
if( sqlite3StrICmp(zDatabase, db->aDb[i].zDbSName)==0 ) break;
@@ -123041,10 +123998,16 @@ SQLITE_PRIVATE Index *sqlite3PrimaryKeyIndex(Table *pTab){
** find the (first) offset of that column in index pIdx. Or return -1
** if column iCol is not used in index pIdx.
*/
-SQLITE_PRIVATE i16 sqlite3TableColumnToIndex(Index *pIdx, i16 iCol){
+SQLITE_PRIVATE int sqlite3TableColumnToIndex(Index *pIdx, int iCol){
int i;
+ i16 iCol16;
+ assert( iCol>=(-1) && iCol<=SQLITE_MAX_COLUMN );
+ assert( pIdx->nColumn<=SQLITE_MAX_COLUMN+1 );
+ iCol16 = iCol;
for(i=0; i<pIdx->nColumn; i++){
- if( iCol==pIdx->aiColumn[i] ) return i;
+ if( iCol16==pIdx->aiColumn[i] ){
+ return i;
+ }
}
return -1;
}
@@ -123298,8 +124261,9 @@ SQLITE_PRIVATE void sqlite3StartTable(
/* If the file format and encoding in the database have not been set,
** set them now.
*/
- reg1 = pParse->regRowid = ++pParse->nMem;
- reg2 = pParse->regRoot = ++pParse->nMem;
+ assert( pParse->isCreate );
+ reg1 = pParse->u1.cr.regRowid = ++pParse->nMem;
+ reg2 = pParse->u1.cr.regRoot = ++pParse->nMem;
reg3 = ++pParse->nMem;
sqlite3VdbeAddOp3(v, OP_ReadCookie, iDb, reg3, BTREE_FILE_FORMAT);
sqlite3VdbeUsesBtree(v, iDb);
@@ -123314,8 +124278,8 @@ SQLITE_PRIVATE void sqlite3StartTable(
** The record created does not contain anything yet. It will be replaced
** by the real entry in code generated at sqlite3EndTable().
**
- ** The rowid for the new entry is left in register pParse->regRowid.
- ** The root page number of the new table is left in reg pParse->regRoot.
+ ** The rowid for the new entry is left in register pParse->u1.cr.regRowid.
+ ** The root page of the new table is left in reg pParse->u1.cr.regRoot.
** The rowid and root page number values are needed by the code that
** sqlite3EndTable will generate.
*/
@@ -123326,7 +124290,7 @@ SQLITE_PRIVATE void sqlite3StartTable(
#endif
{
assert( !pParse->bReturning );
- pParse->u1.addrCrTab =
+ pParse->u1.cr.addrCrTab =
sqlite3VdbeAddOp3(v, OP_CreateBtree, iDb, reg2, BTREE_INTKEY);
}
sqlite3OpenSchemaTable(pParse, iDb);
@@ -123404,7 +124368,8 @@ SQLITE_PRIVATE void sqlite3AddReturning(Parse *pParse, ExprList *pList){
sqlite3ExprListDelete(db, pList);
return;
}
- pParse->u1.pReturning = pRet;
+ assert( !pParse->isCreate );
+ pParse->u1.d.pReturning = pRet;
pRet->pParse = pParse;
pRet->pReturnEL = pList;
sqlite3ParserAddCleanup(pParse, sqlite3DeleteReturning, pRet);
@@ -123446,7 +124411,6 @@ SQLITE_PRIVATE void sqlite3AddColumn(Parse *pParse, Token sName, Token sType){
char *zType;
Column *pCol;
sqlite3 *db = pParse->db;
- u8 hName;
Column *aNew;
u8 eType = COLTYPE_CUSTOM;
u8 szEst = 1;
@@ -123500,13 +124464,10 @@ SQLITE_PRIVATE void sqlite3AddColumn(Parse *pParse, Token sName, Token sType){
memcpy(z, sName.z, sName.n);
z[sName.n] = 0;
sqlite3Dequote(z);
- hName = sqlite3StrIHash(z);
- for(i=0; i<p->nCol; i++){
- if( p->aCol[i].hName==hName && sqlite3StrICmp(z, p->aCol[i].zCnName)==0 ){
- sqlite3ErrorMsg(pParse, "duplicate column name: %s", z);
- sqlite3DbFree(db, z);
- return;
- }
+ if( p->nCol && sqlite3ColumnIndex(p, z)>=0 ){
+ sqlite3ErrorMsg(pParse, "duplicate column name: %s", z);
+ sqlite3DbFree(db, z);
+ return;
}
aNew = sqlite3DbRealloc(db,p->aCol,((i64)p->nCol+1)*sizeof(p->aCol[0]));
if( aNew==0 ){
@@ -123517,7 +124478,7 @@ SQLITE_PRIVATE void sqlite3AddColumn(Parse *pParse, Token sName, Token sType){
pCol = &p->aCol[p->nCol];
memset(pCol, 0, sizeof(p->aCol[0]));
pCol->zCnName = z;
- pCol->hName = hName;
+ pCol->hName = sqlite3StrIHash(z);
sqlite3ColumnPropertiesFromName(p, pCol);
if( sType.n==0 ){
@@ -123541,9 +124502,14 @@ SQLITE_PRIVATE void sqlite3AddColumn(Parse *pParse, Token sName, Token sType){
pCol->affinity = sqlite3AffinityType(zType, pCol);
pCol->colFlags |= COLFLAG_HASTYPE;
}
+ if( p->nCol<=0xff ){
+ u8 h = pCol->hName % sizeof(p->aHx);
+ p->aHx[h] = p->nCol;
+ }
p->nCol++;
p->nNVCol++;
- pParse->constraintName.n = 0;
+ assert( pParse->isCreate );
+ pParse->u1.cr.constraintName.n = 0;
}
/*
@@ -123807,15 +124773,11 @@ SQLITE_PRIVATE void sqlite3AddPrimaryKey(
assert( pCExpr!=0 );
sqlite3StringToId(pCExpr);
if( pCExpr->op==TK_ID ){
- const char *zCName;
assert( !ExprHasProperty(pCExpr, EP_IntValue) );
- zCName = pCExpr->u.zToken;
- for(iCol=0; iCol<pTab->nCol; iCol++){
- if( sqlite3StrICmp(zCName, pTab->aCol[iCol].zCnName)==0 ){
- pCol = &pTab->aCol[iCol];
- makeColumnPartOfPrimaryKey(pParse, pCol);
- break;
- }
+ iCol = sqlite3ColumnIndex(pTab, pCExpr->u.zToken);
+ if( iCol>=0 ){
+ pCol = &pTab->aCol[iCol];
+ makeColumnPartOfPrimaryKey(pParse, pCol);
}
}
}
@@ -123867,8 +124829,10 @@ SQLITE_PRIVATE void sqlite3AddCheckConstraint(
&& !sqlite3BtreeIsReadonly(db->aDb[db->init.iDb].pBt)
){
pTab->pCheck = sqlite3ExprListAppend(pParse, pTab->pCheck, pCheckExpr);
- if( pParse->constraintName.n ){
- sqlite3ExprListSetName(pParse, pTab->pCheck, &pParse->constraintName, 1);
+ assert( pParse->isCreate );
+ if( pParse->u1.cr.constraintName.n ){
+ sqlite3ExprListSetName(pParse, pTab->pCheck,
+ &pParse->u1.cr.constraintName, 1);
}else{
Token t;
for(zStart++; sqlite3Isspace(zStart[0]); zStart++){}
@@ -124063,7 +125027,8 @@ static void identPut(char *z, int *pIdx, char *zSignedIdent){
** from sqliteMalloc() and must be freed by the calling function.
*/
static char *createTableStmt(sqlite3 *db, Table *p){
- int i, k, n;
+ int i, k, len;
+ i64 n;
char *zStmt;
char *zSep, *zSep2, *zEnd;
Column *pCol;
@@ -124087,8 +125052,9 @@ static char *createTableStmt(sqlite3 *db, Table *p){
sqlite3OomFault(db);
return 0;
}
- sqlite3_snprintf(n, zStmt, "CREATE TABLE ");
- k = sqlite3Strlen30(zStmt);
+ assert( n>14 && n<=0x7fffffff );
+ memcpy(zStmt, "CREATE TABLE ", 13);
+ k = 13;
identPut(zStmt, &k, p->zName);
zStmt[k++] = '(';
for(pCol=p->aCol, i=0; i<p->nCol; i++, pCol++){
@@ -124100,13 +125066,15 @@ static char *createTableStmt(sqlite3 *db, Table *p){
/* SQLITE_AFF_REAL */ " REAL",
/* SQLITE_AFF_FLEXNUM */ " NUM",
};
- int len;
const char *zType;
- sqlite3_snprintf(n-k, &zStmt[k], zSep);
- k += sqlite3Strlen30(&zStmt[k]);
+ len = sqlite3Strlen30(zSep);
+ assert( k+len<n );
+ memcpy(&zStmt[k], zSep, len);
+ k += len;
zSep = zSep2;
identPut(zStmt, &k, pCol->zCnName);
+ assert( k<n );
assert( pCol->affinity-SQLITE_AFF_BLOB >= 0 );
assert( pCol->affinity-SQLITE_AFF_BLOB < ArraySize(azType) );
testcase( pCol->affinity==SQLITE_AFF_BLOB );
@@ -124121,11 +125089,14 @@ static char *createTableStmt(sqlite3 *db, Table *p){
assert( pCol->affinity==SQLITE_AFF_BLOB
|| pCol->affinity==SQLITE_AFF_FLEXNUM
|| pCol->affinity==sqlite3AffinityType(zType, 0) );
+ assert( k+len<n );
memcpy(&zStmt[k], zType, len);
k += len;
assert( k<=n );
}
- sqlite3_snprintf(n-k, &zStmt[k], "%s", zEnd);
+ len = sqlite3Strlen30(zEnd);
+ assert( k+len<n );
+ memcpy(&zStmt[k], zEnd, len+1);
return zStmt;
}
@@ -124133,12 +125104,17 @@ static char *createTableStmt(sqlite3 *db, Table *p){
** Resize an Index object to hold N columns total. Return SQLITE_OK
** on success and SQLITE_NOMEM on an OOM error.
*/
-static int resizeIndexObject(sqlite3 *db, Index *pIdx, int N){
+static int resizeIndexObject(Parse *pParse, Index *pIdx, int N){
char *zExtra;
- int nByte;
+ u64 nByte;
+ sqlite3 *db;
if( pIdx->nColumn>=N ) return SQLITE_OK;
+ db = pParse->db;
+ assert( N>0 );
+ assert( N <= SQLITE_MAX_COLUMN*2 /* tag-20250221-1 */ );
+ testcase( N==2*pParse->db->aLimit[SQLITE_LIMIT_COLUMN] );
assert( pIdx->isResized==0 );
- nByte = (sizeof(char*) + sizeof(LogEst) + sizeof(i16) + 1)*N;
+ nByte = (sizeof(char*) + sizeof(LogEst) + sizeof(i16) + 1)*(u64)N;
zExtra = sqlite3DbMallocZero(db, nByte);
if( zExtra==0 ) return SQLITE_NOMEM_BKPT;
memcpy(zExtra, pIdx->azColl, sizeof(char*)*pIdx->nColumn);
@@ -124152,7 +125128,7 @@ static int resizeIndexObject(sqlite3 *db, Index *pIdx, int N){
zExtra += sizeof(i16)*N;
memcpy(zExtra, pIdx->aSortOrder, pIdx->nColumn);
pIdx->aSortOrder = (u8*)zExtra;
- pIdx->nColumn = N;
+ pIdx->nColumn = (u16)N; /* See tag-20250221-1 above for proof of safety */
pIdx->isResized = 1;
return SQLITE_OK;
}
@@ -124318,9 +125294,9 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
** into BTREE_BLOBKEY.
*/
assert( !pParse->bReturning );
- if( pParse->u1.addrCrTab ){
+ if( pParse->u1.cr.addrCrTab ){
assert( v );
- sqlite3VdbeChangeP3(v, pParse->u1.addrCrTab, BTREE_BLOBKEY);
+ sqlite3VdbeChangeP3(v, pParse->u1.cr.addrCrTab, BTREE_BLOBKEY);
}
/* Locate the PRIMARY KEY index. Or, if this table was originally
@@ -124406,14 +125382,14 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
pIdx->nColumn = pIdx->nKeyCol;
continue;
}
- if( resizeIndexObject(db, pIdx, pIdx->nKeyCol+n) ) return;
+ if( resizeIndexObject(pParse, pIdx, pIdx->nKeyCol+n) ) return;
for(i=0, j=pIdx->nKeyCol; i<nPk; i++){
if( !isDupColumn(pIdx, pIdx->nKeyCol, pPk, i) ){
testcase( hasColumn(pIdx->aiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) );
pIdx->aiColumn[j] = pPk->aiColumn[i];
pIdx->azColl[j] = pPk->azColl[i];
if( pPk->aSortOrder[i] ){
- /* See ticket https://www.sqlite.org/src/info/bba7b69f9849b5bf */
+ /* See ticket https://sqlite.org/src/info/bba7b69f9849b5bf */
pIdx->bAscKeyBug = 1;
}
j++;
@@ -124430,7 +125406,7 @@ static void convertToWithoutRowidTable(Parse *pParse, Table *pTab){
if( !hasColumn(pPk->aiColumn, nPk, i)
&& (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ) nExtra++;
}
- if( resizeIndexObject(db, pPk, nPk+nExtra) ) return;
+ if( resizeIndexObject(pParse, pPk, nPk+nExtra) ) return;
for(i=0, j=nPk; i<pTab->nCol; i++){
if( !hasColumn(pPk->aiColumn, j, i)
&& (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0
@@ -124760,7 +125736,7 @@ SQLITE_PRIVATE void sqlite3EndTable(
/* If this is a CREATE TABLE xx AS SELECT ..., execute the SELECT
** statement to populate the new table. The root-page number for the
- ** new table is in register pParse->regRoot.
+ ** new table is in register pParse->u1.cr.regRoot.
**
** Once the SELECT has been coded by sqlite3Select(), it is in a
** suitable state to query for the column names and types to be used
@@ -124791,7 +125767,8 @@ SQLITE_PRIVATE void sqlite3EndTable(
regRec = ++pParse->nMem;
regRowid = ++pParse->nMem;
sqlite3MayAbort(pParse);
- sqlite3VdbeAddOp3(v, OP_OpenWrite, iCsr, pParse->regRoot, iDb);
+ assert( pParse->isCreate );
+ sqlite3VdbeAddOp3(v, OP_OpenWrite, iCsr, pParse->u1.cr.regRoot, iDb);
sqlite3VdbeChangeP5(v, OPFLAG_P2ISREG);
addrTop = sqlite3VdbeCurrentAddr(v) + 1;
sqlite3VdbeAddOp3(v, OP_InitCoroutine, regYield, 0, addrTop);
@@ -124836,6 +125813,7 @@ SQLITE_PRIVATE void sqlite3EndTable(
** schema table. We just need to update that slot with all
** the information we've collected.
*/
+ assert( pParse->isCreate );
sqlite3NestedParse(pParse,
"UPDATE %Q." LEGACY_SCHEMA_TABLE
" SET type='%s', name=%Q, tbl_name=%Q, rootpage=#%d, sql=%Q"
@@ -124844,9 +125822,9 @@ SQLITE_PRIVATE void sqlite3EndTable(
zType,
p->zName,
p->zName,
- pParse->regRoot,
+ pParse->u1.cr.regRoot,
zStmt,
- pParse->regRowid
+ pParse->u1.cr.regRowid
);
sqlite3DbFree(db, zStmt);
sqlite3ChangeCookie(pParse, iDb);
@@ -125586,7 +126564,7 @@ SQLITE_PRIVATE void sqlite3CreateForeignKey(
}else{
nCol = pFromCol->nExpr;
}
- nByte = sizeof(*pFKey) + (nCol-1)*sizeof(pFKey->aCol[0]) + pTo->n + 1;
+ nByte = SZ_FKEY(nCol) + pTo->n + 1;
if( pToCol ){
for(i=0; i<pToCol->nExpr; i++){
nByte += sqlite3Strlen30(pToCol->a[i].zEName) + 1;
@@ -125788,7 +126766,7 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
** not work for UNIQUE constraint indexes on WITHOUT ROWID tables
** with DESC primary keys, since those indexes have there keys in
** a different order from the main table.
- ** See ticket: https://www.sqlite.org/src/info/bba7b69f9849b5bf
+ ** See ticket: https://sqlite.org/src/info/bba7b69f9849b5bf
*/
sqlite3VdbeAddOp1(v, OP_SeekEnd, iIdx);
}
@@ -125812,13 +126790,14 @@ static void sqlite3RefillIndex(Parse *pParse, Index *pIndex, int memRootPage){
*/
SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(
sqlite3 *db, /* Database connection */
- i16 nCol, /* Total number of columns in the index */
+ int nCol, /* Total number of columns in the index */
int nExtra, /* Number of bytes of extra space to alloc */
char **ppExtra /* Pointer to the "extra" space */
){
Index *p; /* Allocated index object */
- int nByte; /* Bytes of space for Index object + arrays */
+ i64 nByte; /* Bytes of space for Index object + arrays */
+ assert( nCol <= 2*db->aLimit[SQLITE_LIMIT_COLUMN] );
nByte = ROUND8(sizeof(Index)) + /* Index structure */
ROUND8(sizeof(char*)*nCol) + /* Index.azColl */
ROUND8(sizeof(LogEst)*(nCol+1) + /* Index.aiRowLogEst */
@@ -125831,8 +126810,9 @@ SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(
p->aiRowLogEst = (LogEst*)pExtra; pExtra += sizeof(LogEst)*(nCol+1);
p->aiColumn = (i16*)pExtra; pExtra += sizeof(i16)*nCol;
p->aSortOrder = (u8*)pExtra;
- p->nColumn = nCol;
- p->nKeyCol = nCol - 1;
+ assert( nCol>0 );
+ p->nColumn = (u16)nCol;
+ p->nKeyCol = (u16)(nCol - 1);
*ppExtra = ((char*)p) + nByte;
}
return p;
@@ -125972,9 +126952,6 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0
&& db->init.busy==0
&& pTblName!=0
-#if SQLITE_USER_AUTHENTICATION
- && sqlite3UserAuthTable(pTab->zName)==0
-#endif
){
sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName);
goto exit_create_index;
@@ -126173,6 +127150,7 @@ SQLITE_PRIVATE void sqlite3CreateIndex(
assert( j<=0x7fff );
if( j<0 ){
j = pTab->iPKey;
+ pIndex->bIdxRowid = 1;
}else{
if( pTab->aCol[j].notNull==0 ){
pIndex->uniqNotNull = 0;
@@ -126646,12 +127624,11 @@ SQLITE_PRIVATE IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token *
sqlite3 *db = pParse->db;
int i;
if( pList==0 ){
- pList = sqlite3DbMallocZero(db, sizeof(IdList) );
+ pList = sqlite3DbMallocZero(db, SZ_IDLIST(1));
if( pList==0 ) return 0;
}else{
IdList *pNew;
- pNew = sqlite3DbRealloc(db, pList,
- sizeof(IdList) + pList->nId*sizeof(pList->a));
+ pNew = sqlite3DbRealloc(db, pList, SZ_IDLIST(pList->nId+1));
if( pNew==0 ){
sqlite3IdListDelete(db, pList);
return 0;
@@ -126673,7 +127650,6 @@ SQLITE_PRIVATE void sqlite3IdListDelete(sqlite3 *db, IdList *pList){
int i;
assert( db!=0 );
if( pList==0 ) return;
- assert( pList->eU4!=EU4_EXPR ); /* EU4_EXPR mode is not currently used */
for(i=0; i<pList->nId; i++){
sqlite3DbFree(db, pList->a[i].zName);
}
@@ -126751,8 +127727,7 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListEnlarge(
return 0;
}
if( nAlloc>SQLITE_MAX_SRCLIST ) nAlloc = SQLITE_MAX_SRCLIST;
- pNew = sqlite3DbRealloc(db, pSrc,
- sizeof(*pSrc) + (nAlloc-1)*sizeof(pSrc->a[0]) );
+ pNew = sqlite3DbRealloc(db, pSrc, SZ_SRCLIST(nAlloc));
if( pNew==0 ){
assert( db->mallocFailed );
return 0;
@@ -126827,7 +127802,7 @@ SQLITE_PRIVATE SrcList *sqlite3SrcListAppend(
assert( pParse->db!=0 );
db = pParse->db;
if( pList==0 ){
- pList = sqlite3DbMallocRawNN(pParse->db, sizeof(SrcList) );
+ pList = sqlite3DbMallocRawNN(pParse->db, SZ_SRCLIST(1));
if( pList==0 ) return 0;
pList->nAlloc = 1;
pList->nSrc = 1;
@@ -127713,10 +128688,9 @@ SQLITE_PRIVATE With *sqlite3WithAdd(
}
if( pWith ){
- sqlite3_int64 nByte = sizeof(*pWith) + (sizeof(pWith->a[1]) * pWith->nCte);
- pNew = sqlite3DbRealloc(db, pWith, nByte);
+ pNew = sqlite3DbRealloc(db, pWith, SZ_WITH(pWith->nCte+1));
}else{
- pNew = sqlite3DbMallocZero(db, sizeof(*pWith));
+ pNew = sqlite3DbMallocZero(db, SZ_WITH(1));
}
assert( (pNew!=0 && zName!=0) || db->mallocFailed );
@@ -128054,12 +129028,18 @@ static int matchQuality(
u8 enc /* Desired text encoding */
){
int match;
- assert( p->nArg>=-1 );
+ assert( p->nArg>=(-4) && p->nArg!=(-2) );
+ assert( nArg>=(-2) );
/* Wrong number of arguments means "no match" */
if( p->nArg!=nArg ){
- if( nArg==(-2) ) return (p->xSFunc==0) ? 0 : FUNC_PERFECT_MATCH;
+ if( nArg==(-2) ) return p->xSFunc==0 ? 0 : FUNC_PERFECT_MATCH;
if( p->nArg>=0 ) return 0;
+ /* Special p->nArg values available to built-in functions only:
+ ** -3 1 or more arguments required
+ ** -4 2 or more arguments required
+ */
+ if( p->nArg<(-2) && nArg<(-2-p->nArg) ) return 0;
}
/* Give a better score to a function with a specific number of arguments
@@ -129682,16 +130662,10 @@ static void substrFunc(
int len;
int p0type;
i64 p1, p2;
- int negP2 = 0;
assert( argc==3 || argc==2 );
- if( sqlite3_value_type(argv[1])==SQLITE_NULL
- || (argc==3 && sqlite3_value_type(argv[2])==SQLITE_NULL)
- ){
- return;
- }
p0type = sqlite3_value_type(argv[0]);
- p1 = sqlite3_value_int(argv[1]);
+ p1 = sqlite3_value_int64(argv[1]);
if( p0type==SQLITE_BLOB ){
len = sqlite3_value_bytes(argv[0]);
z = sqlite3_value_blob(argv[0]);
@@ -129707,28 +130681,31 @@ static void substrFunc(
}
}
}
-#ifdef SQLITE_SUBSTR_COMPATIBILITY
- /* If SUBSTR_COMPATIBILITY is defined then substr(X,0,N) work the same as
- ** as substr(X,1,N) - it returns the first N characters of X. This
- ** is essentially a back-out of the bug-fix in check-in [5fc125d362df4b8]
- ** from 2009-02-02 for compatibility of applications that exploited the
- ** old buggy behavior. */
- if( p1==0 ) p1 = 1; /* <rdar://problem/6778339> */
-#endif
if( argc==3 ){
- p2 = sqlite3_value_int(argv[2]);
- if( p2<0 ){
- p2 = -p2;
- negP2 = 1;
- }
+ p2 = sqlite3_value_int64(argv[2]);
+ if( p2==0 && sqlite3_value_type(argv[2])==SQLITE_NULL ) return;
}else{
p2 = sqlite3_context_db_handle(context)->aLimit[SQLITE_LIMIT_LENGTH];
}
+ if( p1==0 ){
+#ifdef SQLITE_SUBSTR_COMPATIBILITY
+ /* If SUBSTR_COMPATIBILITY is defined then substr(X,0,N) work the same as
+ ** as substr(X,1,N) - it returns the first N characters of X. This
+ ** is essentially a back-out of the bug-fix in check-in [5fc125d362df4b8]
+ ** from 2009-02-02 for compatibility of applications that exploited the
+ ** old buggy behavior. */
+ p1 = 1; /* <rdar://problem/6778339> */
+#endif
+ if( sqlite3_value_type(argv[1])==SQLITE_NULL ) return;
+ }
if( p1<0 ){
p1 += len;
if( p1<0 ){
- p2 += p1;
- if( p2<0 ) p2 = 0;
+ if( p2<0 ){
+ p2 = 0;
+ }else{
+ p2 += p1;
+ }
p1 = 0;
}
}else if( p1>0 ){
@@ -129736,12 +130713,13 @@ static void substrFunc(
}else if( p2>0 ){
p2--;
}
- if( negP2 ){
- p1 -= p2;
- if( p1<0 ){
- p2 += p1;
- p1 = 0;
+ if( p2<0 ){
+ if( p2<-p1 ){
+ p2 = p1;
+ }else{
+ p2 = -p2;
}
+ p1 -= p2;
}
assert( p1>=0 && p2>=0 );
if( p0type!=SQLITE_BLOB ){
@@ -129755,9 +130733,11 @@ static void substrFunc(
sqlite3_result_text64(context, (char*)z, z2-z, SQLITE_TRANSIENT,
SQLITE_UTF8);
}else{
- if( p1+p2>len ){
+ if( p1>=len ){
+ p1 = p2 = 0;
+ }else if( p2>len-p1 ){
p2 = len-p1;
- if( p2<0 ) p2 = 0;
+ assert( p2>0 );
}
sqlite3_result_blob64(context, (char*)&z[p1], (u64)p2, SQLITE_TRANSIENT);
}
@@ -129768,13 +130748,13 @@ static void substrFunc(
*/
#ifndef SQLITE_OMIT_FLOATING_POINT
static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
- int n = 0;
+ i64 n = 0;
double r;
char *zBuf;
assert( argc==1 || argc==2 );
if( argc==2 ){
if( SQLITE_NULL==sqlite3_value_type(argv[1]) ) return;
- n = sqlite3_value_int(argv[1]);
+ n = sqlite3_value_int64(argv[1]);
if( n>30 ) n = 30;
if( n<0 ) n = 0;
}
@@ -129789,7 +130769,7 @@ static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
}else if( n==0 ){
r = (double)((sqlite_int64)(r+(r<0?-0.5:+0.5)));
}else{
- zBuf = sqlite3_mprintf("%!.*f",n,r);
+ zBuf = sqlite3_mprintf("%!.*f",(int)n,r);
if( zBuf==0 ){
sqlite3_result_error_nomem(context);
return;
@@ -130418,7 +131398,7 @@ static const char hexdigits[] = {
** Append to pStr text that is the SQL literal representation of the
** value contained in pValue.
*/
-SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum *pStr, sqlite3_value *pValue){
+SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum *pStr, sqlite3_value *pValue, int bEscape){
/* As currently implemented, the string must be initially empty.
** we might relax this requirement in the future, but that will
** require enhancements to the implementation. */
@@ -130466,7 +131446,7 @@ SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum *pStr, sqlite3_value *pValue){
}
case SQLITE_TEXT: {
const unsigned char *zArg = sqlite3_value_text(pValue);
- sqlite3_str_appendf(pStr, "%Q", zArg);
+ sqlite3_str_appendf(pStr, bEscape ? "%#Q" : "%Q", zArg);
break;
}
default: {
@@ -130478,6 +131458,105 @@ SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum *pStr, sqlite3_value *pValue){
}
/*
+** Return true if z[] begins with N hexadecimal digits, and write
+** a decoding of those digits into *pVal. Or return false if any
+** one of the first N characters in z[] is not a hexadecimal digit.
+*/
+static int isNHex(const char *z, int N, u32 *pVal){
+ int i;
+ int v = 0;
+ for(i=0; i<N; i++){
+ if( !sqlite3Isxdigit(z[i]) ) return 0;
+ v = (v<<4) + sqlite3HexToInt(z[i]);
+ }
+ *pVal = v;
+ return 1;
+}
+
+/*
+** Implementation of the UNISTR() function.
+**
+** This is intended to be a work-alike of the UNISTR() function in
+** PostgreSQL. Quoting from the PG documentation (PostgreSQL 17 -
+** scraped on 2025-02-22):
+**
+** Evaluate escaped Unicode characters in the argument. Unicode
+** characters can be specified as \XXXX (4 hexadecimal digits),
+** \+XXXXXX (6 hexadecimal digits), \uXXXX (4 hexadecimal digits),
+** or \UXXXXXXXX (8 hexadecimal digits). To specify a backslash,
+** write two backslashes. All other characters are taken literally.
+*/
+static void unistrFunc(
+ sqlite3_context *context,
+ int argc,
+ sqlite3_value **argv
+){
+ char *zOut;
+ const char *zIn;
+ int nIn;
+ int i, j, n;
+ u32 v;
+
+ assert( argc==1 );
+ UNUSED_PARAMETER( argc );
+ zIn = (const char*)sqlite3_value_text(argv[0]);
+ if( zIn==0 ) return;
+ nIn = sqlite3_value_bytes(argv[0]);
+ zOut = sqlite3_malloc64(nIn+1);
+ if( zOut==0 ){
+ sqlite3_result_error_nomem(context);
+ return;
+ }
+ i = j = 0;
+ while( i<nIn ){
+ char *z = strchr(&zIn[i],'\\');
+ if( z==0 ){
+ n = nIn - i;
+ memmove(&zOut[j], &zIn[i], n);
+ j += n;
+ break;
+ }
+ n = z - &zIn[i];
+ if( n>0 ){
+ memmove(&zOut[j], &zIn[i], n);
+ j += n;
+ i += n;
+ }
+ if( zIn[i+1]=='\\' ){
+ i += 2;
+ zOut[j++] = '\\';
+ }else if( sqlite3Isxdigit(zIn[i+1]) ){
+ if( !isNHex(&zIn[i+1], 4, &v) ) goto unistr_error;
+ i += 5;
+ j += sqlite3AppendOneUtf8Character(&zOut[j], v);
+ }else if( zIn[i+1]=='+' ){
+ if( !isNHex(&zIn[i+2], 6, &v) ) goto unistr_error;
+ i += 8;
+ j += sqlite3AppendOneUtf8Character(&zOut[j], v);
+ }else if( zIn[i+1]=='u' ){
+ if( !isNHex(&zIn[i+2], 4, &v) ) goto unistr_error;
+ i += 6;
+ j += sqlite3AppendOneUtf8Character(&zOut[j], v);
+ }else if( zIn[i+1]=='U' ){
+ if( !isNHex(&zIn[i+2], 8, &v) ) goto unistr_error;
+ i += 10;
+ j += sqlite3AppendOneUtf8Character(&zOut[j], v);
+ }else{
+ goto unistr_error;
+ }
+ }
+ zOut[j] = 0;
+ sqlite3_result_text64(context, zOut, j, sqlite3_free, SQLITE_UTF8);
+ return;
+
+unistr_error:
+ sqlite3_free(zOut);
+ sqlite3_result_error(context, "invalid Unicode escape", -1);
+ return;
+}
+
+
+/*
** Implementation of the QUOTE() function.
**
** The quote(X) function returns the text of an SQL literal which is the
@@ -130486,6 +131565,10 @@ SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum *pStr, sqlite3_value *pValue){
** as needed. BLOBs are encoded as hexadecimal literals. Strings with
** embedded NUL characters cannot be represented as string literals in SQL
** and hence the returned string literal is truncated prior to the first NUL.
+**
+** If sqlite3_user_data() is non-zero, then the UNISTR_QUOTE() function is
+** implemented instead. The difference is that UNISTR_QUOTE() uses the
+** UNISTR() function to escape control characters.
*/
static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
sqlite3_str str;
@@ -130493,7 +131576,7 @@ static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
assert( argc==1 );
UNUSED_PARAMETER(argc);
sqlite3StrAccumInit(&str, db, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]);
- sqlite3QuoteValue(&str,argv[0]);
+ sqlite3QuoteValue(&str,argv[0],SQLITE_PTR_TO_INT(sqlite3_user_data(context)));
sqlite3_result_text(context, sqlite3StrAccumFinish(&str), str.nChar,
SQLITE_DYNAMIC);
if( str.accError!=SQLITE_OK ){
@@ -130748,7 +131831,7 @@ static void replaceFunc(
assert( zRep==sqlite3_value_text(argv[2]) );
nOut = nStr + 1;
assert( nOut<SQLITE_MAX_LENGTH );
- zOut = contextMalloc(context, (i64)nOut);
+ zOut = contextMalloc(context, nOut);
if( zOut==0 ){
return;
}
@@ -130898,7 +131981,7 @@ static void concatFuncCore(
for(i=0; i<argc; i++){
n += sqlite3_value_bytes(argv[i]);
}
- n += (argc-1)*nSep;
+ n += (argc-1)*(i64)nSep;
z = sqlite3_malloc64(n+1);
if( z==0 ){
sqlite3_result_error_nomem(context);
@@ -131144,7 +132227,7 @@ static void kahanBabuskaNeumaierInit(
** that it returns NULL if it sums over no inputs. TOTAL returns
** 0.0 in that case. In addition, TOTAL always returns a float where
** SUM might return an integer if it never encounters a floating point
-** value. TOTAL never fails, but SUM might through an exception if
+** value. TOTAL never fails, but SUM might throw an exception if
** it overflows an integer.
*/
static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){
@@ -131196,7 +132279,10 @@ static void sumInverse(sqlite3_context *context, int argc, sqlite3_value**argv){
assert( p->cnt>0 );
p->cnt--;
if( !p->approx ){
- p->iSum -= sqlite3_value_int64(argv[0]);
+ if( sqlite3SubInt64(&p->iSum, sqlite3_value_int64(argv[0])) ){
+ p->ovrfl = 1;
+ p->approx = 1;
+ }
}else if( type==SQLITE_INTEGER ){
i64 iVal = sqlite3_value_int64(argv[0]);
if( iVal!=SMALLEST_INT64 ){
@@ -132006,9 +133092,6 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
SFUNCTION(load_extension, 1, 0, 0, loadExt ),
SFUNCTION(load_extension, 2, 0, 0, loadExt ),
#endif
-#if SQLITE_USER_AUTHENTICATION
- FUNCTION(sqlite_crypt, 2, 0, 0, sqlite3CryptFunc ),
-#endif
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
DFUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ),
DFUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ),
@@ -132025,12 +133108,10 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
FUNCTION(rtrim, 2, 2, 0, trimFunc ),
FUNCTION(trim, 1, 3, 0, trimFunc ),
FUNCTION(trim, 2, 3, 0, trimFunc ),
- FUNCTION(min, -1, 0, 1, minmaxFunc ),
- FUNCTION(min, 0, 0, 1, 0 ),
+ FUNCTION(min, -3, 0, 1, minmaxFunc ),
WAGGREGATE(min, 1, 0, 1, minmaxStep, minMaxFinalize, minMaxValue, 0,
SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER ),
- FUNCTION(max, -1, 1, 1, minmaxFunc ),
- FUNCTION(max, 0, 1, 1, 0 ),
+ FUNCTION(max, -3, 1, 1, minmaxFunc ),
WAGGREGATE(max, 1, 1, 1, minmaxStep, minMaxFinalize, minMaxValue, 0,
SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER ),
FUNCTION2(typeof, 1, 0, 0, typeofFunc, SQLITE_FUNC_TYPEOF),
@@ -132057,11 +133138,8 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
FUNCTION(hex, 1, 0, 0, hexFunc ),
FUNCTION(unhex, 1, 0, 0, unhexFunc ),
FUNCTION(unhex, 2, 0, 0, unhexFunc ),
- FUNCTION(concat, -1, 0, 0, concatFunc ),
- FUNCTION(concat, 0, 0, 0, 0 ),
- FUNCTION(concat_ws, -1, 0, 0, concatwsFunc ),
- FUNCTION(concat_ws, 0, 0, 0, 0 ),
- FUNCTION(concat_ws, 1, 0, 0, 0 ),
+ FUNCTION(concat, -3, 0, 0, concatFunc ),
+ FUNCTION(concat_ws, -4, 0, 0, concatwsFunc ),
INLINE_FUNC(ifnull, 2, INLINEFUNC_coalesce, 0 ),
VFUNCTION(random, 0, 0, 0, randomFunc ),
VFUNCTION(randomblob, 1, 0, 0, randomBlob ),
@@ -132069,7 +133147,9 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
DFUNCTION(sqlite_version, 0, 0, 0, versionFunc ),
DFUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ),
FUNCTION(sqlite_log, 2, 0, 0, errlogFunc ),
+ FUNCTION(unistr, 1, 0, 0, unistrFunc ),
FUNCTION(quote, 1, 0, 0, quoteFunc ),
+ FUNCTION(unistr_quote, 1, 1, 0, quoteFunc ),
VFUNCTION(last_insert_rowid, 0, 0, 0, last_insert_rowid),
VFUNCTION(changes, 0, 0, 0, changes ),
VFUNCTION(total_changes, 0, 0, 0, total_changes ),
@@ -132105,8 +133185,6 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
FUNCTION(unknown, -1, 0, 0, unknownFunc ),
#endif
- FUNCTION(coalesce, 1, 0, 0, 0 ),
- FUNCTION(coalesce, 0, 0, 0, 0 ),
#ifdef SQLITE_ENABLE_MATH_FUNCTIONS
MFUNCTION(ceil, 1, xCeil, ceilingFunc ),
MFUNCTION(ceiling, 1, xCeil, ceilingFunc ),
@@ -132144,8 +133222,9 @@ SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void){
MFUNCTION(pi, 0, 0, piFunc ),
#endif /* SQLITE_ENABLE_MATH_FUNCTIONS */
FUNCTION(sign, 1, 0, 0, signFunc ),
- INLINE_FUNC(coalesce, -1, INLINEFUNC_coalesce, 0 ),
- INLINE_FUNC(iif, 3, INLINEFUNC_iif, 0 ),
+ INLINE_FUNC(coalesce, -4, INLINEFUNC_coalesce, 0 ),
+ INLINE_FUNC(iif, -4, INLINEFUNC_iif, 0 ),
+ INLINE_FUNC(if, -4, INLINEFUNC_iif, 0 ),
};
#ifndef SQLITE_OMIT_ALTERTABLE
sqlite3AlterFunctions();
@@ -134357,7 +135436,7 @@ SQLITE_PRIVATE Select *sqlite3MultiValues(Parse *pParse, Select *pLeft, ExprList
f = (f & pLeft->selFlags);
}
pSelect = sqlite3SelectNew(pParse, pRow, 0, 0, 0, 0, 0, f, 0);
- pLeft->selFlags &= ~SF_MultiValue;
+ pLeft->selFlags &= ~(u32)SF_MultiValue;
if( pSelect ){
pSelect->op = TK_ALL;
pSelect->pPrior = pLeft;
@@ -134591,6 +135670,7 @@ SQLITE_PRIVATE void sqlite3Insert(
int regRowid; /* registers holding insert rowid */
int regData; /* register holding first column to insert */
int *aRegIdx = 0; /* One register allocated to each index */
+ int *aTabColMap = 0; /* Mapping from pTab columns to pCol entries */
#ifndef SQLITE_OMIT_TRIGGER
int isView; /* True if attempting to insert into a view */
@@ -134735,31 +135815,25 @@ SQLITE_PRIVATE void sqlite3Insert(
*/
bIdListInOrder = (pTab->tabFlags & (TF_OOOHidden|TF_HasStored))==0;
if( pColumn ){
- assert( pColumn->eU4!=EU4_EXPR );
- pColumn->eU4 = EU4_IDX;
- for(i=0; i<pColumn->nId; i++){
- pColumn->a[i].u4.idx = -1;
- }
+ aTabColMap = sqlite3DbMallocZero(db, pTab->nCol*sizeof(int));
+ if( aTabColMap==0 ) goto insert_cleanup;
for(i=0; i<pColumn->nId; i++){
- for(j=0; j<pTab->nCol; j++){
- if( sqlite3StrICmp(pColumn->a[i].zName, pTab->aCol[j].zCnName)==0 ){
- pColumn->a[i].u4.idx = j;
- if( i!=j ) bIdListInOrder = 0;
- if( j==pTab->iPKey ){
- ipkColumn = i; assert( !withoutRowid );
- }
+ j = sqlite3ColumnIndex(pTab, pColumn->a[i].zName);
+ if( j>=0 ){
+ if( aTabColMap[j]==0 ) aTabColMap[j] = i+1;
+ if( i!=j ) bIdListInOrder = 0;
+ if( j==pTab->iPKey ){
+ ipkColumn = i; assert( !withoutRowid );
+ }
#ifndef SQLITE_OMIT_GENERATED_COLUMNS
- if( pTab->aCol[j].colFlags & (COLFLAG_STORED|COLFLAG_VIRTUAL) ){
- sqlite3ErrorMsg(pParse,
- "cannot INSERT into generated column \"%s\"",
- pTab->aCol[j].zCnName);
- goto insert_cleanup;
- }
-#endif
- break;
+ if( pTab->aCol[j].colFlags & (COLFLAG_STORED|COLFLAG_VIRTUAL) ){
+ sqlite3ErrorMsg(pParse,
+ "cannot INSERT into generated column \"%s\"",
+ pTab->aCol[j].zCnName);
+ goto insert_cleanup;
}
- }
- if( j>=pTab->nCol ){
+#endif
+ }else{
if( sqlite3IsRowid(pColumn->a[i].zName) && !withoutRowid ){
ipkColumn = i;
bIdListInOrder = 0;
@@ -135057,7 +136131,7 @@ SQLITE_PRIVATE void sqlite3Insert(
continue;
}else if( pColumn==0 ){
/* Hidden columns that are not explicitly named in the INSERT
- ** get there default value */
+ ** get their default value */
sqlite3ExprCodeFactorable(pParse,
sqlite3ColumnExpr(pTab, &pTab->aCol[i]),
iRegStore);
@@ -135065,9 +136139,9 @@ SQLITE_PRIVATE void sqlite3Insert(
}
}
if( pColumn ){
- assert( pColumn->eU4==EU4_IDX );
- for(j=0; j<pColumn->nId && pColumn->a[j].u4.idx!=i; j++){}
- if( j>=pColumn->nId ){
+ j = aTabColMap[i];
+ assert( j>=0 && j<=pColumn->nId );
+ if( j==0 ){
/* A column not named in the insert column list gets its
** default value */
sqlite3ExprCodeFactorable(pParse,
@@ -135075,7 +136149,7 @@ SQLITE_PRIVATE void sqlite3Insert(
iRegStore);
continue;
}
- k = j;
+ k = j - 1;
}else if( nColumn==0 ){
/* This is INSERT INTO ... DEFAULT VALUES. Load the default value. */
sqlite3ExprCodeFactorable(pParse,
@@ -135320,7 +136394,10 @@ insert_cleanup:
sqlite3ExprListDelete(db, pList);
sqlite3UpsertDelete(db, pUpsert);
sqlite3SelectDelete(db, pSelect);
- sqlite3IdListDelete(db, pColumn);
+ if( pColumn ){
+ sqlite3IdListDelete(db, pColumn);
+ sqlite3DbFree(db, aTabColMap);
+ }
if( aRegIdx ) sqlite3DbNNFreeNN(db, aRegIdx);
}
@@ -135779,7 +136856,7 @@ SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(
** could happen in any order, but they are grouped up front for
** convenience.
**
- ** 2018-08-14: Ticket https://www.sqlite.org/src/info/908f001483982c43
+ ** 2018-08-14: Ticket https://sqlite.org/src/info/908f001483982c43
** The order of constraints used to have OE_Update as (2) and OE_Abort
** and so forth as (1). But apparently PostgreSQL checks the OE_Update
** constraint before any others, so it had to be moved.
@@ -137589,6 +138666,8 @@ struct sqlite3_api_routines {
/* Version 3.44.0 and later */
void *(*get_clientdata)(sqlite3*,const char*);
int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
+ /* Version 3.50.0 and later */
+ int (*setlk_timeout)(sqlite3*,int,int);
};
/*
@@ -137922,6 +139001,8 @@ typedef int (*sqlite3_loadext_entry)(
/* Version 3.44.0 and later */
#define sqlite3_get_clientdata sqlite3_api->get_clientdata
#define sqlite3_set_clientdata sqlite3_api->set_clientdata
+/* Version 3.50.0 and later */
+#define sqlite3_setlk_timeout sqlite3_api->setlk_timeout
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
@@ -138443,7 +139524,9 @@ static const sqlite3_api_routines sqlite3Apis = {
sqlite3_stmt_explain,
/* Version 3.44.0 and later */
sqlite3_get_clientdata,
- sqlite3_set_clientdata
+ sqlite3_set_clientdata,
+ /* Version 3.50.0 and later */
+ sqlite3_setlk_timeout
};
/* True if x is the directory separator character
@@ -138965,48 +140048,48 @@ static const char *const pragCName[] = {
/* 13 */ "pk",
/* 14 */ "hidden",
/* table_info reuses 8 */
- /* 15 */ "schema", /* Used by: table_list */
- /* 16 */ "name",
+ /* 15 */ "name", /* Used by: function_list */
+ /* 16 */ "builtin",
/* 17 */ "type",
- /* 18 */ "ncol",
- /* 19 */ "wr",
- /* 20 */ "strict",
- /* 21 */ "seqno", /* Used by: index_xinfo */
- /* 22 */ "cid",
- /* 23 */ "name",
- /* 24 */ "desc",
- /* 25 */ "coll",
- /* 26 */ "key",
- /* 27 */ "name", /* Used by: function_list */
- /* 28 */ "builtin",
- /* 29 */ "type",
- /* 30 */ "enc",
- /* 31 */ "narg",
- /* 32 */ "flags",
- /* 33 */ "tbl", /* Used by: stats */
- /* 34 */ "idx",
- /* 35 */ "wdth",
- /* 36 */ "hght",
- /* 37 */ "flgs",
- /* 38 */ "seq", /* Used by: index_list */
- /* 39 */ "name",
- /* 40 */ "unique",
- /* 41 */ "origin",
- /* 42 */ "partial",
+ /* 18 */ "enc",
+ /* 19 */ "narg",
+ /* 20 */ "flags",
+ /* 21 */ "schema", /* Used by: table_list */
+ /* 22 */ "name",
+ /* 23 */ "type",
+ /* 24 */ "ncol",
+ /* 25 */ "wr",
+ /* 26 */ "strict",
+ /* 27 */ "seqno", /* Used by: index_xinfo */
+ /* 28 */ "cid",
+ /* 29 */ "name",
+ /* 30 */ "desc",
+ /* 31 */ "coll",
+ /* 32 */ "key",
+ /* 33 */ "seq", /* Used by: index_list */
+ /* 34 */ "name",
+ /* 35 */ "unique",
+ /* 36 */ "origin",
+ /* 37 */ "partial",
+ /* 38 */ "tbl", /* Used by: stats */
+ /* 39 */ "idx",
+ /* 40 */ "wdth",
+ /* 41 */ "hght",
+ /* 42 */ "flgs",
/* 43 */ "table", /* Used by: foreign_key_check */
/* 44 */ "rowid",
/* 45 */ "parent",
/* 46 */ "fkid",
- /* index_info reuses 21 */
- /* 47 */ "seq", /* Used by: database_list */
- /* 48 */ "name",
- /* 49 */ "file",
- /* 50 */ "busy", /* Used by: wal_checkpoint */
- /* 51 */ "log",
- /* 52 */ "checkpointed",
- /* collation_list reuses 38 */
+ /* 47 */ "busy", /* Used by: wal_checkpoint */
+ /* 48 */ "log",
+ /* 49 */ "checkpointed",
+ /* 50 */ "seq", /* Used by: database_list */
+ /* 51 */ "name",
+ /* 52 */ "file",
+ /* index_info reuses 27 */
/* 53 */ "database", /* Used by: lock_status */
/* 54 */ "status",
+ /* collation_list reuses 33 */
/* 55 */ "cache_size", /* Used by: default_cache_size */
/* module_list pragma_list reuses 9 */
/* 56 */ "timeout", /* Used by: busy_timeout */
@@ -139099,7 +140182,7 @@ static const PragmaName aPragmaName[] = {
{/* zName: */ "collation_list",
/* ePragTyp: */ PragTyp_COLLATION_LIST,
/* ePragFlg: */ PragFlg_Result0,
- /* ColNames: */ 38, 2,
+ /* ColNames: */ 33, 2,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS)
@@ -139134,7 +140217,7 @@ static const PragmaName aPragmaName[] = {
{/* zName: */ "database_list",
/* ePragTyp: */ PragTyp_DATABASE_LIST,
/* ePragFlg: */ PragFlg_Result0,
- /* ColNames: */ 47, 3,
+ /* ColNames: */ 50, 3,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED)
@@ -139214,7 +140297,7 @@ static const PragmaName aPragmaName[] = {
{/* zName: */ "function_list",
/* ePragTyp: */ PragTyp_FUNCTION_LIST,
/* ePragFlg: */ PragFlg_Result0,
- /* ColNames: */ 27, 6,
+ /* ColNames: */ 15, 6,
/* iArg: */ 0 },
#endif
#endif
@@ -139243,17 +140326,17 @@ static const PragmaName aPragmaName[] = {
{/* zName: */ "index_info",
/* ePragTyp: */ PragTyp_INDEX_INFO,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
- /* ColNames: */ 21, 3,
+ /* ColNames: */ 27, 3,
/* iArg: */ 0 },
{/* zName: */ "index_list",
/* ePragTyp: */ PragTyp_INDEX_LIST,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
- /* ColNames: */ 38, 5,
+ /* ColNames: */ 33, 5,
/* iArg: */ 0 },
{/* zName: */ "index_xinfo",
/* ePragTyp: */ PragTyp_INDEX_INFO,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
- /* ColNames: */ 21, 6,
+ /* ColNames: */ 27, 6,
/* iArg: */ 1 },
#endif
#if !defined(SQLITE_OMIT_INTEGRITY_CHECK)
@@ -139432,7 +140515,7 @@ static const PragmaName aPragmaName[] = {
{/* zName: */ "stats",
/* ePragTyp: */ PragTyp_STATS,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq,
- /* ColNames: */ 33, 5,
+ /* ColNames: */ 38, 5,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
@@ -139451,7 +140534,7 @@ static const PragmaName aPragmaName[] = {
{/* zName: */ "table_list",
/* ePragTyp: */ PragTyp_TABLE_LIST,
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1,
- /* ColNames: */ 15, 6,
+ /* ColNames: */ 21, 6,
/* iArg: */ 0 },
{/* zName: */ "table_xinfo",
/* ePragTyp: */ PragTyp_TABLE_INFO,
@@ -139528,7 +140611,7 @@ static const PragmaName aPragmaName[] = {
{/* zName: */ "wal_checkpoint",
/* ePragTyp: */ PragTyp_WAL_CHECKPOINT,
/* ePragFlg: */ PragFlg_NeedSchema,
- /* ColNames: */ 50, 3,
+ /* ColNames: */ 47, 3,
/* iArg: */ 0 },
#endif
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
@@ -139550,7 +140633,7 @@ static const PragmaName aPragmaName[] = {
** the following macro or to the actual analysis_limit if it is non-zero,
** in order to prevent PRAGMA optimize from running for too long.
**
-** The value of 2000 is chosen emperically so that the worst-case run-time
+** The value of 2000 is chosen empirically so that the worst-case run-time
** for PRAGMA optimize does not exceed 100 milliseconds against a variety
** of test databases on a RaspberryPI-4 compiled using -Os and without
** -DSQLITE_DEBUG. Of course, your mileage may vary. For the purpose of
@@ -140658,12 +141741,6 @@ SQLITE_PRIVATE void sqlite3Pragma(
** in auto-commit mode. */
mask &= ~(SQLITE_ForeignKeys);
}
-#if SQLITE_USER_AUTHENTICATION
- if( db->auth.authLevel==UAUTH_User ){
- /* Do not allow non-admin users to modify the schema arbitrarily */
- mask &= ~(SQLITE_WriteSchema);
- }
-#endif
if( sqlite3GetBoolean(zRight, 0) ){
if( (mask & SQLITE_WriteSchema)==0
@@ -140673,7 +141750,10 @@ SQLITE_PRIVATE void sqlite3Pragma(
}
}else{
db->flags &= ~mask;
- if( mask==SQLITE_DeferFKs ) db->nDeferredImmCons = 0;
+ if( mask==SQLITE_DeferFKs ){
+ db->nDeferredImmCons = 0;
+ db->nDeferredCons = 0;
+ }
if( (mask & SQLITE_WriteSchema)!=0
&& sqlite3_stricmp(zRight, "reset")==0
){
@@ -140799,7 +141879,8 @@ SQLITE_PRIVATE void sqlite3Pragma(
char *zSql = sqlite3MPrintf(db, "SELECT*FROM\"%w\"", pTab->zName);
if( zSql ){
sqlite3_stmt *pDummy = 0;
- (void)sqlite3_prepare(db, zSql, -1, &pDummy, 0);
+ (void)sqlite3_prepare_v3(db, zSql, -1, SQLITE_PREPARE_DONT_LOG,
+ &pDummy, 0);
(void)sqlite3_finalize(pDummy);
sqlite3DbFree(db, zSql);
}
@@ -141280,7 +142361,7 @@ SQLITE_PRIVATE void sqlite3Pragma(
/* Do the b-tree integrity checks */
sqlite3VdbeAddOp4(v, OP_IntegrityCk, 1, cnt, 8, (char*)aRoot,P4_INTARRAY);
- sqlite3VdbeChangeP5(v, (u8)i);
+ sqlite3VdbeChangeP5(v, (u16)i);
addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v);
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
sqlite3MPrintf(db, "*** in database %s ***\n", db->aDb[i].zDbSName),
@@ -142900,14 +143981,7 @@ SQLITE_PRIVATE int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg, u32 mFl
#else
encoding = SQLITE_UTF8;
#endif
- if( db->nVdbeActive>0 && encoding!=ENC(db)
- && (db->mDbFlags & DBFLAG_Vacuum)==0
- ){
- rc = SQLITE_LOCKED;
- goto initone_error_out;
- }else{
- sqlite3SetTextEncoding(db, encoding);
- }
+ sqlite3SetTextEncoding(db, encoding);
}else{
/* If opening an attached database, the encoding much match ENC(db) */
if( (meta[BTREE_TEXT_ENCODING-1] & 3)!=ENC(db) ){
@@ -143848,7 +144922,7 @@ SQLITE_PRIVATE Select *sqlite3SelectNew(
pNew->addrOpenEphm[0] = -1;
pNew->addrOpenEphm[1] = -1;
pNew->nSelectRow = 0;
- if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*pSrc));
+ if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, SZ_SRCLIST_1);
pNew->pSrc = pSrc;
pNew->pWhere = pWhere;
pNew->pGroupBy = pGroupBy;
@@ -144013,10 +145087,33 @@ SQLITE_PRIVATE int sqlite3JoinType(Parse *pParse, Token *pA, Token *pB, Token *p
*/
SQLITE_PRIVATE int sqlite3ColumnIndex(Table *pTab, const char *zCol){
int i;
- u8 h = sqlite3StrIHash(zCol);
- Column *pCol;
- for(pCol=pTab->aCol, i=0; i<pTab->nCol; pCol++, i++){
- if( pCol->hName==h && sqlite3StrICmp(pCol->zCnName, zCol)==0 ) return i;
+ u8 h;
+ const Column *aCol;
+ int nCol;
+
+ h = sqlite3StrIHash(zCol);
+ aCol = pTab->aCol;
+ nCol = pTab->nCol;
+
+ /* See if the aHx gives us a lucky match */
+ i = pTab->aHx[h % sizeof(pTab->aHx)];
+ assert( i<nCol );
+ if( aCol[i].hName==h
+ && sqlite3StrICmp(aCol[i].zCnName, zCol)==0
+ ){
+ return i;
+ }
+
+ /* No lucky match from the hash table. Do a full search. */
+ i = 0;
+ while( 1 /*exit-by-break*/ ){
+ if( aCol[i].hName==h
+ && sqlite3StrICmp(aCol[i].zCnName, zCol)==0
+ ){
+ return i;
+ }
+ i++;
+ if( i>=nCol ) break;
}
return -1;
}
@@ -145208,8 +146305,8 @@ static void selectInnerLoop(
** X extra columns.
*/
SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
- int nExtra = (N+X)*(sizeof(CollSeq*)+1) - sizeof(CollSeq*);
- KeyInfo *p = sqlite3DbMallocRawNN(db, sizeof(KeyInfo) + nExtra);
+ int nExtra = (N+X)*(sizeof(CollSeq*)+1);
+ KeyInfo *p = sqlite3DbMallocRawNN(db, SZ_KEYINFO(0) + nExtra);
if( p ){
p->aSortFlags = (u8*)&p->aColl[N+X];
p->nKeyField = (u16)N;
@@ -145217,7 +146314,7 @@ SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
p->enc = ENC(db);
p->db = db;
p->nRef = 1;
- memset(&p[1], 0, nExtra);
+ memset(p->aColl, 0, nExtra);
}else{
return (KeyInfo*)sqlite3OomFault(db);
}
@@ -146918,6 +148015,7 @@ static int multiSelect(
multi_select_end:
pDest->iSdst = dest.iSdst;
pDest->nSdst = dest.nSdst;
+ pDest->iSDParm2 = dest.iSDParm2;
if( pDelete ){
sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pDelete);
}
@@ -147605,32 +148703,32 @@ static Expr *substExpr(
if( pSubst->isOuterJoin ){
ExprSetProperty(pNew, EP_CanBeNull);
}
- if( ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) ){
- sqlite3SetJoinExpr(pNew, pExpr->w.iJoin,
- pExpr->flags & (EP_OuterON|EP_InnerON));
- }
- sqlite3ExprDelete(db, pExpr);
- pExpr = pNew;
- if( pExpr->op==TK_TRUEFALSE ){
- pExpr->u.iValue = sqlite3ExprTruthValue(pExpr);
- pExpr->op = TK_INTEGER;
- ExprSetProperty(pExpr, EP_IntValue);
+ if( pNew->op==TK_TRUEFALSE ){
+ pNew->u.iValue = sqlite3ExprTruthValue(pNew);
+ pNew->op = TK_INTEGER;
+ ExprSetProperty(pNew, EP_IntValue);
}
/* Ensure that the expression now has an implicit collation sequence,
** just as it did when it was a column of a view or sub-query. */
{
- CollSeq *pNat = sqlite3ExprCollSeq(pSubst->pParse, pExpr);
+ CollSeq *pNat = sqlite3ExprCollSeq(pSubst->pParse, pNew);
CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse,
pSubst->pCList->a[iColumn].pExpr
);
- if( pNat!=pColl || (pExpr->op!=TK_COLUMN && pExpr->op!=TK_COLLATE) ){
- pExpr = sqlite3ExprAddCollateString(pSubst->pParse, pExpr,
+ if( pNat!=pColl || (pNew->op!=TK_COLUMN && pNew->op!=TK_COLLATE) ){
+ pNew = sqlite3ExprAddCollateString(pSubst->pParse, pNew,
(pColl ? pColl->zName : "BINARY")
);
}
}
- ExprClearProperty(pExpr, EP_Collate);
+ ExprClearProperty(pNew, EP_Collate);
+ if( ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) ){
+ sqlite3SetJoinExpr(pNew, pExpr->w.iJoin,
+ pExpr->flags & (EP_OuterON|EP_InnerON));
+ }
+ sqlite3ExprDelete(db, pExpr);
+ pExpr = pNew;
}
}
}else{
@@ -148367,6 +149465,7 @@ static int flattenSubquery(
/* Transfer the FROM clause terms from the subquery into the
** outer query.
*/
+ iNewParent = pSubSrc->a[0].iCursor;
for(i=0; i<nSubSrc; i++){
SrcItem *pItem = &pSrc->a[i+iFrom];
assert( pItem->fg.isTabFunc==0 );
@@ -148376,7 +149475,6 @@ static int flattenSubquery(
if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing);
*pItem = pSubSrc->a[i];
pItem->fg.jointype |= ltorj;
- iNewParent = pSubSrc->a[i].iCursor;
memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i]));
}
pSrc->a[iFrom].fg.jointype &= JT_LTORJ;
@@ -148416,6 +149514,7 @@ static int flattenSubquery(
pWhere = pSub->pWhere;
pSub->pWhere = 0;
if( isOuterJoin>0 ){
+ assert( pSubSrc->nSrc==1 );
sqlite3SetJoinExpr(pWhere, iNewParent, EP_OuterON);
}
if( pWhere ){
@@ -148527,7 +149626,8 @@ static void constInsert(
return; /* Already present. Return without doing anything. */
}
}
- if( sqlite3ExprAffinity(pColumn)==SQLITE_AFF_BLOB ){
+ assert( SQLITE_AFF_NONE<SQLITE_AFF_BLOB );
+ if( sqlite3ExprAffinity(pColumn)<=SQLITE_AFF_BLOB ){
pConst->bHasAffBlob = 1;
}
@@ -148602,7 +149702,8 @@ static int propagateConstantExprRewriteOne(
if( pColumn==pExpr ) continue;
if( pColumn->iTable!=pExpr->iTable ) continue;
if( pColumn->iColumn!=pExpr->iColumn ) continue;
- if( bIgnoreAffBlob && sqlite3ExprAffinity(pColumn)==SQLITE_AFF_BLOB ){
+ assert( SQLITE_AFF_NONE<SQLITE_AFF_BLOB );
+ if( bIgnoreAffBlob && sqlite3ExprAffinity(pColumn)<=SQLITE_AFF_BLOB ){
break;
}
/* A match is found. Add the EP_FixedCol property */
@@ -149255,7 +150356,7 @@ SQLITE_PRIVATE int sqlite3IndexedByLookup(Parse *pParse, SrcItem *pFrom){
** above that generates the code for a compound SELECT with an ORDER BY clause
** uses a merge algorithm that requires the same collating sequence on the
** result columns as on the ORDER BY clause. See ticket
-** http://www.sqlite.org/src/info/6709574d2a
+** http://sqlite.org/src/info/6709574d2a
**
** This transformation is only needed for EXCEPT, INTERSECT, and UNION.
** The UNION ALL operator works fine with multiSelectOrderBy() even when
@@ -149316,7 +150417,7 @@ static int convertCompoundSelectToSubquery(Walker *pWalker, Select *p){
#ifndef SQLITE_OMIT_WINDOWFUNC
p->pWinDefn = 0;
#endif
- p->selFlags &= ~SF_Compound;
+ p->selFlags &= ~(u32)SF_Compound;
assert( (p->selFlags & SF_Converted)==0 );
p->selFlags |= SF_Converted;
assert( pNew->pPrior!=0 );
@@ -149732,7 +150833,7 @@ static int selectExpander(Walker *pWalker, Select *p){
pEList = p->pEList;
if( pParse->pWith && (p->selFlags & SF_View) ){
if( p->pWith==0 ){
- p->pWith = (With*)sqlite3DbMallocZero(db, sizeof(With));
+ p->pWith = (With*)sqlite3DbMallocZero(db, SZ_WITH(1) );
if( p->pWith==0 ){
return WRC_Abort;
}
@@ -150519,7 +151620,7 @@ static void finalizeAggFunctions(Parse *pParse, AggInfo *pAggInfo){
}
sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, (u8)nArg);
+ sqlite3VdbeChangeP5(v, (u16)nArg);
sqlite3VdbeAddOp2(v, OP_Next, pF->iOBTab, iTop+1); VdbeCoverage(v);
sqlite3VdbeJumpHere(v, iTop);
sqlite3ReleaseTempRange(pParse, regAgg, nArg);
@@ -150682,7 +151783,7 @@ static void updateAccumulator(
}
sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, (u8)nArg);
+ sqlite3VdbeChangeP5(v, (u16)nArg);
sqlite3ReleaseTempRange(pParse, regAgg, nArg);
}
if( addrNext ){
@@ -150871,6 +151972,7 @@ static void agginfoFree(sqlite3 *db, void *pArg){
** * There is no WHERE or GROUP BY or HAVING clauses on the subqueries
** * The outer query is a simple count(*) with no WHERE clause or other
** extraneous syntax.
+** * None of the subqueries are DISTINCT (forumpost/a860f5fb2e 2025-03-10)
**
** Return TRUE if the optimization is undertaken.
*/
@@ -150903,7 +152005,11 @@ static int countOfViewOptimization(Parse *pParse, Select *p){
if( pSub->op!=TK_ALL && pSub->pPrior ) return 0; /* Must be UNION ALL */
if( pSub->pWhere ) return 0; /* No WHERE clause */
if( pSub->pLimit ) return 0; /* No LIMIT clause */
- if( pSub->selFlags & SF_Aggregate ) return 0; /* Not an aggregate */
+ if( pSub->selFlags & (SF_Aggregate|SF_Distinct) ){
+ testcase( pSub->selFlags & SF_Aggregate );
+ testcase( pSub->selFlags & SF_Distinct );
+ return 0; /* Not an aggregate nor DISTINCT */
+ }
assert( pSub->pHaving==0 ); /* Due to the previous */
pSub = pSub->pPrior; /* Repeat over compound */
}while( pSub );
@@ -150915,14 +152021,14 @@ static int countOfViewOptimization(Parse *pParse, Select *p){
pExpr = 0;
pSub = sqlite3SubqueryDetach(db, pFrom);
sqlite3SrcListDelete(db, p->pSrc);
- p->pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*p->pSrc));
+ p->pSrc = sqlite3DbMallocZero(pParse->db, SZ_SRCLIST_1);
while( pSub ){
Expr *pTerm;
pPrior = pSub->pPrior;
pSub->pPrior = 0;
pSub->pNext = 0;
pSub->selFlags |= SF_Aggregate;
- pSub->selFlags &= ~SF_Compound;
+ pSub->selFlags &= ~(u32)SF_Compound;
pSub->nSelectRow = 0;
sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pSub->pEList);
pTerm = pPrior ? sqlite3ExprDup(db, pCount, 0) : pCount;
@@ -150937,7 +152043,7 @@ static int countOfViewOptimization(Parse *pParse, Select *p){
pSub = pPrior;
}
p->pEList->a[0].pExpr = pExpr;
- p->selFlags &= ~SF_Aggregate;
+ p->selFlags &= ~(u32)SF_Aggregate;
#if TREETRACE_ENABLED
if( sqlite3TreeTrace & 0x200 ){
@@ -151144,7 +152250,7 @@ SQLITE_PRIVATE int sqlite3Select(
testcase( pParse->earlyCleanup );
p->pOrderBy = 0;
}
- p->selFlags &= ~SF_Distinct;
+ p->selFlags &= ~(u32)SF_Distinct;
p->selFlags |= SF_NoopOrderBy;
}
sqlite3SelectPrep(pParse, p, 0);
@@ -151183,7 +152289,7 @@ SQLITE_PRIVATE int sqlite3Select(
** and leaving this flag set can cause errors if a compound sub-query
** in p->pSrc is flattened into this query and this function called
** again as part of compound SELECT processing. */
- p->selFlags &= ~SF_UFSrcCheck;
+ p->selFlags &= ~(u32)SF_UFSrcCheck;
}
if( pDest->eDest==SRT_Output ){
@@ -151515,7 +152621,7 @@ SQLITE_PRIVATE int sqlite3Select(
#endif
assert( pSubq->pSelect && (pSub->selFlags & SF_PushDown)!=0 );
}else{
- TREETRACE(0x4000,pParse,p,("WHERE-lcause push-down not possible\n"));
+ TREETRACE(0x4000,pParse,p,("WHERE-clause push-down not possible\n"));
}
/* Convert unused result columns of the subquery into simple NULL
@@ -151672,7 +152778,7 @@ SQLITE_PRIVATE int sqlite3Select(
&& p->pWin==0
#endif
){
- p->selFlags &= ~SF_Distinct;
+ p->selFlags &= ~(u32)SF_Distinct;
pGroupBy = p->pGroupBy = sqlite3ExprListDup(db, pEList, 0);
if( pGroupBy ){
for(i=0; i<pGroupBy->nExpr; i++){
@@ -151781,6 +152887,12 @@ SQLITE_PRIVATE int sqlite3Select(
if( pWInfo==0 ) goto select_end;
if( sqlite3WhereOutputRowCount(pWInfo) < p->nSelectRow ){
p->nSelectRow = sqlite3WhereOutputRowCount(pWInfo);
+ if( pDest->eDest<=SRT_DistQueue && pDest->eDest>=SRT_DistFifo ){
+ /* TUNING: For a UNION CTE, because UNION is implies DISTINCT,
+ ** reduce the estimated output row count by 8 (LogEst 30).
+ ** Search for tag-20250414a to see other cases */
+ p->nSelectRow -= 30;
+ }
}
if( sDistinct.isTnct && sqlite3WhereIsDistinct(pWInfo) ){
sDistinct.eTnctType = sqlite3WhereIsDistinct(pWInfo);
@@ -152154,6 +153266,10 @@ SQLITE_PRIVATE int sqlite3Select(
if( iOrderByCol ){
Expr *pX = p->pEList->a[iOrderByCol-1].pExpr;
Expr *pBase = sqlite3ExprSkipCollateAndLikely(pX);
+ while( ALWAYS(pBase!=0) && pBase->op==TK_IF_NULL_ROW ){
+ pX = pBase->pLeft;
+ pBase = sqlite3ExprSkipCollateAndLikely(pX);
+ }
if( ALWAYS(pBase!=0)
&& pBase->op!=TK_AGG_COLUMN
&& pBase->op!=TK_REGISTER
@@ -152737,7 +153853,8 @@ SQLITE_PRIVATE Trigger *sqlite3TriggerList(Parse *pParse, Table *pTab){
assert( pParse->db->pVtabCtx==0 );
#endif
assert( pParse->bReturning );
- assert( &(pParse->u1.pReturning->retTrig) == pTrig );
+ assert( !pParse->isCreate );
+ assert( &(pParse->u1.d.pReturning->retTrig) == pTrig );
pTrig->table = pTab->zName;
pTrig->pTabSchema = pTab->pSchema;
pTrig->pNext = pList;
@@ -153705,7 +154822,8 @@ static void codeReturningTrigger(
ExprList *pNew;
Returning *pReturning;
Select sSelect;
- SrcList sFrom;
+ SrcList *pFrom;
+ u8 fromSpace[SZ_SRCLIST_1];
assert( v!=0 );
if( !pParse->bReturning ){
@@ -153714,19 +154832,21 @@ static void codeReturningTrigger(
return;
}
assert( db->pParse==pParse );
- pReturning = pParse->u1.pReturning;
+ assert( !pParse->isCreate );
+ pReturning = pParse->u1.d.pReturning;
if( pTrigger != &(pReturning->retTrig) ){
/* This RETURNING trigger is for a different statement */
return;
}
memset(&sSelect, 0, sizeof(sSelect));
- memset(&sFrom, 0, sizeof(sFrom));
+ pFrom = (SrcList*)fromSpace;
+ memset(pFrom, 0, SZ_SRCLIST_1);
sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0);
- sSelect.pSrc = &sFrom;
- sFrom.nSrc = 1;
- sFrom.a[0].pSTab = pTab;
- sFrom.a[0].zName = pTab->zName; /* tag-20240424-1 */
- sFrom.a[0].iCursor = -1;
+ sSelect.pSrc = pFrom;
+ pFrom->nSrc = 1;
+ pFrom->a[0].pSTab = pTab;
+ pFrom->a[0].zName = pTab->zName; /* tag-20240424-1 */
+ pFrom->a[0].iCursor = -1;
sqlite3SelectPrep(pParse, &sSelect, 0);
if( pParse->nErr==0 ){
assert( db->mallocFailed==0 );
@@ -153944,6 +155064,8 @@ static TriggerPrg *codeRowTrigger(
sSubParse.eTriggerOp = pTrigger->op;
sSubParse.nQueryLoop = pParse->nQueryLoop;
sSubParse.prepFlags = pParse->prepFlags;
+ sSubParse.oldmask = 0;
+ sSubParse.newmask = 0;
v = sqlite3GetVdbe(&sSubParse);
if( v ){
@@ -154076,7 +155198,7 @@ SQLITE_PRIVATE void sqlite3CodeRowTriggerDirect(
** invocation is disallowed if (a) the sub-program is really a trigger,
** not a foreign key action, and (b) the flag to enable recursive triggers
** is clear. */
- sqlite3VdbeChangeP5(v, (u8)bRecursive);
+ sqlite3VdbeChangeP5(v, (u16)bRecursive);
}
}
@@ -154698,38 +155820,32 @@ SQLITE_PRIVATE void sqlite3Update(
*/
chngRowid = chngPk = 0;
for(i=0; i<pChanges->nExpr; i++){
- u8 hCol = sqlite3StrIHash(pChanges->a[i].zEName);
/* If this is an UPDATE with a FROM clause, do not resolve expressions
** here. The call to sqlite3Select() below will do that. */
if( nChangeFrom==0 && sqlite3ResolveExprNames(&sNC, pChanges->a[i].pExpr) ){
goto update_cleanup;
}
- for(j=0; j<pTab->nCol; j++){
- if( pTab->aCol[j].hName==hCol
- && sqlite3StrICmp(pTab->aCol[j].zCnName, pChanges->a[i].zEName)==0
- ){
- if( j==pTab->iPKey ){
- chngRowid = 1;
- pRowidExpr = pChanges->a[i].pExpr;
- iRowidExpr = i;
- }else if( pPk && (pTab->aCol[j].colFlags & COLFLAG_PRIMKEY)!=0 ){
- chngPk = 1;
- }
+ j = sqlite3ColumnIndex(pTab, pChanges->a[i].zEName);
+ if( j>=0 ){
+ if( j==pTab->iPKey ){
+ chngRowid = 1;
+ pRowidExpr = pChanges->a[i].pExpr;
+ iRowidExpr = i;
+ }else if( pPk && (pTab->aCol[j].colFlags & COLFLAG_PRIMKEY)!=0 ){
+ chngPk = 1;
+ }
#ifndef SQLITE_OMIT_GENERATED_COLUMNS
- else if( pTab->aCol[j].colFlags & COLFLAG_GENERATED ){
- testcase( pTab->aCol[j].colFlags & COLFLAG_VIRTUAL );
- testcase( pTab->aCol[j].colFlags & COLFLAG_STORED );
- sqlite3ErrorMsg(pParse,
- "cannot UPDATE generated column \"%s\"",
- pTab->aCol[j].zCnName);
- goto update_cleanup;
- }
-#endif
- aXRef[j] = i;
- break;
+ else if( pTab->aCol[j].colFlags & COLFLAG_GENERATED ){
+ testcase( pTab->aCol[j].colFlags & COLFLAG_VIRTUAL );
+ testcase( pTab->aCol[j].colFlags & COLFLAG_STORED );
+ sqlite3ErrorMsg(pParse,
+ "cannot UPDATE generated column \"%s\"",
+ pTab->aCol[j].zCnName);
+ goto update_cleanup;
}
- }
- if( j>=pTab->nCol ){
+#endif
+ aXRef[j] = i;
+ }else{
if( pPk==0 && sqlite3IsRowid(pChanges->a[i].zEName) ){
j = -1;
chngRowid = 1;
@@ -156052,7 +157168,7 @@ SQLITE_PRIVATE void sqlite3Vacuum(Parse *pParse, Token *pNm, Expr *pInto){
#else
/* When SQLITE_BUG_COMPATIBLE_20160819 is defined, unrecognized arguments
** to VACUUM are silently ignored. This is a back-out of a bug fix that
- ** occurred on 2016-08-19 (https://www.sqlite.org/src/info/083f9e6270).
+ ** occurred on 2016-08-19 (https://sqlite.org/src/info/083f9e6270).
** The buggy behavior is required for binary compatibility with some
** legacy applications. */
iDb = sqlite3FindDb(pParse->db, pNm);
@@ -156131,7 +157247,7 @@ SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3RunVacuum(
saved_nChange = db->nChange;
saved_nTotalChange = db->nTotalChange;
saved_mTrace = db->mTrace;
- db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks;
+ db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_Comments;
db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum;
db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder
| SQLITE_Defensive | SQLITE_CountRows);
@@ -156836,11 +157952,12 @@ SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
** schema table. We just need to update that slot with all
** the information we've collected.
**
- ** The VM register number pParse->regRowid holds the rowid of an
+ ** The VM register number pParse->u1.cr.regRowid holds the rowid of an
** entry in the sqlite_schema table that was created for this vtab
** by sqlite3StartTable().
*/
iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
+ assert( pParse->isCreate );
sqlite3NestedParse(pParse,
"UPDATE %Q." LEGACY_SCHEMA_TABLE " "
"SET type='table', name=%Q, tbl_name=%Q, rootpage=0, sql=%Q "
@@ -156849,7 +157966,7 @@ SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){
pTab->zName,
pTab->zName,
zStmt,
- pParse->regRowid
+ pParse->u1.cr.regRowid
);
v = sqlite3GetVdbe(pParse);
sqlite3ChangeCookie(pParse, iDb);
@@ -157187,7 +158304,9 @@ SQLITE_API int sqlite3_declare_vtab(sqlite3 *db, const char *zCreateTable){
z = (const unsigned char*)zCreateTable;
for(i=0; aKeyword[i]; i++){
int tokenType = 0;
- do{ z += sqlite3GetToken(z, &tokenType); }while( tokenType==TK_SPACE );
+ do{
+ z += sqlite3GetToken(z, &tokenType);
+ }while( tokenType==TK_SPACE || tokenType==TK_COMMENT );
if( tokenType!=aKeyword[i] ){
sqlite3ErrorWithMsg(db, SQLITE_ERROR, "syntax error");
return SQLITE_ERROR;
@@ -157918,8 +159037,10 @@ struct WhereLoop {
/**** whereLoopXfer() copies fields above ***********************/
# define WHERE_LOOP_XFER_SZ offsetof(WhereLoop,nLSlot)
u16 nLSlot; /* Number of slots allocated for aLTerm[] */
+#ifdef WHERETRACE_ENABLED
LogEst rStarDelta; /* Cost delta due to star-schema heuristic. Not
- ** initialized unless pWInfo->nOutStarDelta>0 */
+ ** initialized unless pWInfo->bStarUsed */
+#endif
WhereTerm **aLTerm; /* WhereTerms used */
WhereLoop *pNextLoop; /* Next WhereLoop object in the WhereClause */
WhereTerm *aLTermSpace[3]; /* Initial aLTerm[] space */
@@ -157968,7 +159089,7 @@ struct WherePath {
Bitmask revLoop; /* aLoop[]s that should be reversed for ORDER BY */
LogEst nRow; /* Estimated number of rows generated by this path */
LogEst rCost; /* Total cost of this path */
- LogEst rUnsorted; /* Total cost of this path ignoring sorting costs */
+ LogEst rUnsort; /* Total cost of this path ignoring sorting costs */
i8 isOrdered; /* No. of ORDER BY terms satisfied. -1 for unknown */
WhereLoop **aLoop; /* Array of WhereLoop objects implementing this path */
};
@@ -158241,9 +159362,13 @@ struct WhereInfo {
unsigned bDeferredSeek :1; /* Uses OP_DeferredSeek */
unsigned untestedTerms :1; /* Not all WHERE terms resolved by outer loop */
unsigned bOrderedInnerLoop:1;/* True if only the inner-most loop is ordered */
- unsigned sorted :1; /* True if really sorted (not just grouped) */
- LogEst nOutStarDelta; /* Artifical nOut reduction for star-query */
+ unsigned sorted :1; /* True if really sorted (not just grouped) */
+ unsigned bStarDone :1; /* True if check for star-query is complete */
+ unsigned bStarUsed :1; /* True if star-query heuristic is used */
LogEst nRowOut; /* Estimated number of output rows */
+#ifdef WHERETRACE_ENABLED
+ LogEst rTotalCost; /* Total cost of the solution */
+#endif
int iTop; /* The very beginning of the WHERE loop */
int iEndWhere; /* End of the WHERE clause itself */
WhereLoop *pLoops; /* List of all WhereLoop objects */
@@ -158251,10 +159376,15 @@ struct WhereInfo {
Bitmask revMask; /* Mask of ORDER BY terms that need reversing */
WhereClause sWC; /* Decomposition of the WHERE clause */
WhereMaskSet sMaskSet; /* Map cursor numbers to bitmasks */
- WhereLevel a[1]; /* Information about each nest loop in WHERE */
+ WhereLevel a[FLEXARRAY]; /* Information about each nest loop in WHERE */
};
/*
+** The size (in bytes) of a WhereInfo object that holds N WhereLevels.
+*/
+#define SZ_WHEREINFO(N) ROUND8(offsetof(WhereInfo,a)+(N)*sizeof(WhereLevel))
+
+/*
** Private interfaces - callable only by other where.c routines.
**
** where.c:
@@ -158289,9 +159419,17 @@ SQLITE_PRIVATE int sqlite3WhereExplainBloomFilter(
const WhereInfo *pWInfo, /* WHERE clause */
const WhereLevel *pLevel /* Bloom filter on this level */
);
+SQLITE_PRIVATE void sqlite3WhereAddExplainText(
+ Parse *pParse, /* Parse context */
+ int addr,
+ SrcList *pTabList, /* Table list this loop refers to */
+ WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
+ u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
+);
#else
# define sqlite3WhereExplainOneScan(u,v,w,x) 0
# define sqlite3WhereExplainBloomFilter(u,v,w) 0
+# define sqlite3WhereAddExplainText(u,v,w,x,y)
#endif /* SQLITE_OMIT_EXPLAIN */
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
SQLITE_PRIVATE void sqlite3WhereAddScanStatus(
@@ -158493,38 +159631,38 @@ static void explainIndexRange(StrAccum *pStr, WhereLoop *pLoop){
}
/*
-** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN
-** command, or if stmt_scanstatus_v2() stats are enabled, or if SQLITE_DEBUG
-** was defined at compile-time. If it is not a no-op, a single OP_Explain
-** opcode is added to the output to describe the table scan strategy in pLevel.
-**
-** If an OP_Explain opcode is added to the VM, its address is returned.
-** Otherwise, if no OP_Explain is coded, zero is returned.
+** This function sets the P4 value of an existing OP_Explain opcode to
+** text describing the loop in pLevel. If the OP_Explain opcode already has
+** a P4 value, it is freed before it is overwritten.
*/
-SQLITE_PRIVATE int sqlite3WhereExplainOneScan(
+SQLITE_PRIVATE void sqlite3WhereAddExplainText(
Parse *pParse, /* Parse context */
+ int addr, /* Address of OP_Explain opcode */
SrcList *pTabList, /* Table list this loop refers to */
WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
){
- int ret = 0;
#if !defined(SQLITE_DEBUG)
if( sqlite3ParseToplevel(pParse)->explain==2 || IS_STMT_SCANSTATUS(pParse->db) )
#endif
{
+ VdbeOp *pOp = sqlite3VdbeGetOp(pParse->pVdbe, addr);
+
SrcItem *pItem = &pTabList->a[pLevel->iFrom];
- Vdbe *v = pParse->pVdbe; /* VM being constructed */
sqlite3 *db = pParse->db; /* Database handle */
int isSearch; /* True for a SEARCH. False for SCAN. */
WhereLoop *pLoop; /* The controlling WhereLoop object */
u32 flags; /* Flags that describe this loop */
+#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_EXPLAIN)
char *zMsg; /* Text to add to EQP output */
+#endif
StrAccum str; /* EQP output string */
char zBuf[100]; /* Initial space for EQP output string */
+ if( db->mallocFailed ) return;
+
pLoop = pLevel->pWLoop;
flags = pLoop->wsFlags;
- if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_OR_SUBCLAUSE) ) return 0;
isSearch = (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0
|| ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0))
@@ -158548,7 +159686,7 @@ SQLITE_PRIVATE int sqlite3WhereExplainOneScan(
zFmt = "AUTOMATIC PARTIAL COVERING INDEX";
}else if( flags & WHERE_AUTO_INDEX ){
zFmt = "AUTOMATIC COVERING INDEX";
- }else if( flags & WHERE_IDX_ONLY ){
+ }else if( flags & (WHERE_IDX_ONLY|WHERE_EXPRIDX) ){
zFmt = "COVERING INDEX %s";
}else{
zFmt = "INDEX %s";
@@ -158600,11 +159738,50 @@ SQLITE_PRIVATE int sqlite3WhereExplainOneScan(
sqlite3_str_append(&str, " (~1 row)", 9);
}
#endif
+#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_EXPLAIN)
zMsg = sqlite3StrAccumFinish(&str);
sqlite3ExplainBreakpoint("",zMsg);
- ret = sqlite3VdbeAddOp4(v, OP_Explain, sqlite3VdbeCurrentAddr(v),
- pParse->addrExplain, pLoop->rRun,
- zMsg, P4_DYNAMIC);
+#endif
+
+ assert( pOp->opcode==OP_Explain );
+ assert( pOp->p4type==P4_DYNAMIC || pOp->p4.z==0 );
+ sqlite3DbFree(db, pOp->p4.z);
+ pOp->p4type = P4_DYNAMIC;
+ pOp->p4.z = sqlite3StrAccumFinish(&str);
+ }
+}
+
+
+/*
+** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN
+** command, or if stmt_scanstatus_v2() stats are enabled, or if SQLITE_DEBUG
+** was defined at compile-time. If it is not a no-op, a single OP_Explain
+** opcode is added to the output to describe the table scan strategy in pLevel.
+**
+** If an OP_Explain opcode is added to the VM, its address is returned.
+** Otherwise, if no OP_Explain is coded, zero is returned.
+*/
+SQLITE_PRIVATE int sqlite3WhereExplainOneScan(
+ Parse *pParse, /* Parse context */
+ SrcList *pTabList, /* Table list this loop refers to */
+ WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
+ u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
+){
+ int ret = 0;
+#if !defined(SQLITE_DEBUG)
+ if( sqlite3ParseToplevel(pParse)->explain==2 || IS_STMT_SCANSTATUS(pParse->db) )
+#endif
+ {
+ if( (pLevel->pWLoop->wsFlags & WHERE_MULTI_OR)==0
+ && (wctrlFlags & WHERE_OR_SUBCLAUSE)==0
+ ){
+ Vdbe *v = pParse->pVdbe;
+ int addr = sqlite3VdbeCurrentAddr(v);
+ ret = sqlite3VdbeAddOp3(
+ v, OP_Explain, addr, pParse->addrExplain, pLevel->pWLoop->rRun
+ );
+ sqlite3WhereAddExplainText(pParse, addr, pTabList, pLevel, wctrlFlags);
+ }
}
return ret;
}
@@ -158703,9 +159880,10 @@ SQLITE_PRIVATE void sqlite3WhereAddScanStatus(
}
}else{
int addr;
+ VdbeOp *pOp;
assert( pSrclist->a[pLvl->iFrom].fg.isSubquery );
addr = pSrclist->a[pLvl->iFrom].u4.pSubq->addrFillSub;
- VdbeOp *pOp = sqlite3VdbeGetOp(v, addr-1);
+ pOp = sqlite3VdbeGetOp(v, addr-1);
assert( sqlite3VdbeDb(v)->mallocFailed || pOp->opcode==OP_InitCoroutine );
assert( sqlite3VdbeDb(v)->mallocFailed || pOp->p2>addr );
sqlite3VdbeScanStatusRange(v, addrExplain, addr, pOp->p2-1);
@@ -158885,7 +160063,7 @@ static void adjustOrderByCol(ExprList *pOrderBy, ExprList *pEList){
/*
** pX is an expression of the form: (vector) IN (SELECT ...)
** In other words, it is a vector IN operator with a SELECT clause on the
-** LHS. But not all terms in the vector are indexable and the terms might
+** RHS. But not all terms in the vector are indexable and the terms might
** not be in the correct order for indexing.
**
** This routine makes a copy of the input pX expression and then adjusts
@@ -159951,6 +161129,9 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
}
sqlite3VdbeAddOp2(v, OP_Integer, pLoop->u.vtab.idxNum, iReg);
sqlite3VdbeAddOp2(v, OP_Integer, nConstraint, iReg+1);
+ /* The instruction immediately prior to OP_VFilter must be an OP_Integer
+ ** that sets the "argc" value for xVFilter. This is necessary for
+ ** resolveP2() to work correctly. See tag-20250207a. */
sqlite3VdbeAddOp4(v, OP_VFilter, iCur, addrNotFound, iReg,
pLoop->u.vtab.idxStr,
pLoop->u.vtab.needFree ? P4_DYNAMIC : P4_STATIC);
@@ -160653,8 +161834,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
int nNotReady; /* The number of notReady tables */
SrcItem *origSrc; /* Original list of tables */
nNotReady = pWInfo->nLevel - iLevel - 1;
- pOrTab = sqlite3DbMallocRawNN(db,
- sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0]));
+ pOrTab = sqlite3DbMallocRawNN(db, SZ_SRCLIST(nNotReady+1));
if( pOrTab==0 ) return notReady;
pOrTab->nAlloc = (u8)(nNotReady + 1);
pOrTab->nSrc = pOrTab->nAlloc;
@@ -160705,7 +161885,7 @@ SQLITE_PRIVATE Bitmask sqlite3WhereCodeOneLoopStart(
**
** This optimization also only applies if the (x1 OR x2 OR ...) term
** is not contained in the ON clause of a LEFT JOIN.
- ** See ticket http://www.sqlite.org/src/info/f2369304e4
+ ** See ticket http://sqlite.org/src/info/f2369304e4
**
** 2022-02-04: Do not push down slices of a row-value comparison.
** In other words, "w" or "y" may not be a slice of a vector. Otherwise,
@@ -161197,7 +162377,8 @@ SQLITE_PRIVATE SQLITE_NOINLINE void sqlite3WhereRightJoinLoop(
WhereInfo *pSubWInfo;
WhereLoop *pLoop = pLevel->pWLoop;
SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom];
- SrcList sFrom;
+ SrcList *pFrom;
+ u8 fromSpace[SZ_SRCLIST_1];
Bitmask mAll = 0;
int k;
@@ -161241,13 +162422,14 @@ SQLITE_PRIVATE SQLITE_NOINLINE void sqlite3WhereRightJoinLoop(
sqlite3ExprDup(pParse->db, pTerm->pExpr, 0));
}
}
- sFrom.nSrc = 1;
- sFrom.nAlloc = 1;
- memcpy(&sFrom.a[0], pTabItem, sizeof(SrcItem));
- sFrom.a[0].fg.jointype = 0;
+ pFrom = (SrcList*)fromSpace;
+ pFrom->nSrc = 1;
+ pFrom->nAlloc = 1;
+ memcpy(&pFrom->a[0], pTabItem, sizeof(SrcItem));
+ pFrom->a[0].fg.jointype = 0;
assert( pParse->withinRJSubrtn < 100 );
pParse->withinRJSubrtn++;
- pSubWInfo = sqlite3WhereBegin(pParse, &sFrom, pSubWhere, 0, 0, 0,
+ pSubWInfo = sqlite3WhereBegin(pParse, pFrom, pSubWhere, 0, 0, 0,
WHERE_RIGHT_JOIN, 0);
if( pSubWInfo ){
int iCur = pLevel->iTabCur;
@@ -161506,12 +162688,12 @@ static int isLikeOrGlob(
z = (u8*)pRight->u.zToken;
}
if( z ){
- /* Count the number of prefix bytes prior to the first wildcard.
- ** or U+fffd character. If the underlying database has a UTF16LE
- ** encoding, then only consider ASCII characters. Note that the
- ** encoding of z[] is UTF8 - we are dealing with only UTF8 here in
- ** this code, but the database engine itself might be processing
- ** content using a different encoding. */
+ /* Count the number of prefix bytes prior to the first wildcard,
+ ** U+fffd character, or malformed utf-8. If the underlying database
+ ** has a UTF16LE encoding, then only consider ASCII characters. Note that
+ ** the encoding of z[] is UTF8 - we are dealing with only UTF8 here in this
+ ** code, but the database engine itself might be processing content using a
+ ** different encoding. */
cnt = 0;
while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){
cnt++;
@@ -161519,7 +162701,9 @@ static int isLikeOrGlob(
cnt++;
}else if( c>=0x80 ){
const u8 *z2 = z+cnt-1;
- if( sqlite3Utf8Read(&z2)==0xfffd || ENC(db)==SQLITE_UTF16LE ){
+ if( c==0xff || sqlite3Utf8Read(&z2)==0xfffd /* bad utf-8 */
+ || ENC(db)==SQLITE_UTF16LE
+ ){
cnt--;
break;
}else{
@@ -162671,9 +163855,8 @@ static void exprAnalyze(
}
if( !db->mallocFailed ){
- u8 c, *pC; /* Last character before the first wildcard */
+ u8 *pC; /* Last character before the first wildcard */
pC = (u8*)&pStr2->u.zToken[sqlite3Strlen30(pStr2->u.zToken)-1];
- c = *pC;
if( noCase ){
/* The point is to increment the last character before the first
** wildcard. But if we increment '@', that will push it into the
@@ -162681,10 +163864,17 @@ static void exprAnalyze(
** inequality. To avoid this, make sure to also run the full
** LIKE on all candidate expressions by clearing the isComplete flag
*/
- if( c=='A'-1 ) isComplete = 0;
- c = sqlite3UpperToLower[c];
+ if( *pC=='A'-1 ) isComplete = 0;
+ *pC = sqlite3UpperToLower[*pC];
+ }
+
+ /* Increment the value of the last utf8 character in the prefix. */
+ while( *pC==0xBF && pC>(u8*)pStr2->u.zToken ){
+ *pC = 0x80;
+ pC--;
}
- *pC = c + 1;
+ assert( *pC!=0xFF ); /* isLikeOrGlob() guarantees this */
+ (*pC)++;
}
zCollSeqName = noCase ? "NOCASE" : sqlite3StrBINARY;
pNewExpr1 = sqlite3ExprDup(db, pLeft, 0);
@@ -163227,11 +164417,16 @@ struct HiddenIndexInfo {
int eDistinct; /* Value to return from sqlite3_vtab_distinct() */
u32 mIn; /* Mask of terms that are <col> IN (...) */
u32 mHandleIn; /* Terms that vtab will handle as <col> IN (...) */
- sqlite3_value *aRhs[1]; /* RHS values for constraints. MUST BE LAST
- ** because extra space is allocated to hold up
- ** to nTerm such values */
+ sqlite3_value *aRhs[FLEXARRAY]; /* RHS values for constraints. MUST BE LAST
+ ** Extra space is allocated to hold up
+ ** to nTerm such values */
};
+/* Size (in bytes) of a HiddenIndeInfo object sufficient to hold as
+** many as N constraints */
+#define SZ_HIDDENINDEXINFO(N) \
+ (offsetof(HiddenIndexInfo,aRhs) + (N)*sizeof(sqlite3_value*))
+
/* Forward declaration of methods */
static int whereLoopResize(sqlite3*, WhereLoop*, int);
@@ -164031,7 +165226,7 @@ static int constraintCompatibleWithOuterJoin(
return 0;
}
if( (pSrc->fg.jointype & (JT_LEFT|JT_RIGHT))!=0
- && ExprHasProperty(pTerm->pExpr, EP_InnerON)
+ && NEVER(ExprHasProperty(pTerm->pExpr, EP_InnerON))
){
return 0;
}
@@ -164052,6 +165247,11 @@ static int constraintCompatibleWithOuterJoin(
** more than 20, then return false.
**
** 3. If no disqualifying conditions above are found, return true.
+**
+** 2025-01-03: I experimented with a new rule that returns false if the
+** the datatype of the column is "BOOLEAN". This did not improve
+** performance on any queries at hand, but it did burn CPU cycles, so the
+** idea was not committed.
*/
static SQLITE_NOINLINE int columnIsGoodIndexCandidate(
const Table *pTab,
@@ -164136,7 +165336,7 @@ static void explainAutomaticIndex(
sqlite3_str *pStr = sqlite3_str_new(pParse->db);
sqlite3_str_appendf(pStr,"CREATE AUTOMATIC INDEX ON %s(", pTab->zName);
assert( pIdx->nColumn>1 );
- assert( pIdx->aiColumn[pIdx->nColumn-1]==XN_ROWID );
+ assert( pIdx->aiColumn[pIdx->nColumn-1]==XN_ROWID || !HasRowid(pTab) );
for(ii=0; ii<(pIdx->nColumn-1); ii++){
const char *zName = 0;
int iCol = pIdx->aiColumn[ii];
@@ -164267,6 +165467,19 @@ static SQLITE_NOINLINE void constructAutomaticIndex(
}else{
extraCols = pSrc->colUsed & (~idxCols | MASKBIT(BMS-1));
}
+ if( !HasRowid(pTable) ){
+ /* For WITHOUT ROWID tables, ensure that all PRIMARY KEY columns are
+ ** either in the idxCols mask or in the extraCols mask */
+ for(i=0; i<pTable->nCol; i++){
+ if( (pTable->aCol[i].colFlags & COLFLAG_PRIMKEY)==0 ) continue;
+ if( i>=BMS-1 ){
+ extraCols |= MASKBIT(BMS-1);
+ break;
+ }
+ if( idxCols & MASKBIT(i) ) continue;
+ extraCols |= MASKBIT(i);
+ }
+ }
mxBitCol = MIN(BMS-1,pTable->nCol);
testcase( pTable->nCol==BMS-1 );
testcase( pTable->nCol==BMS-2 );
@@ -164278,7 +165491,10 @@ static SQLITE_NOINLINE void constructAutomaticIndex(
}
/* Construct the Index object to describe this index */
- pIdx = sqlite3AllocateIndexObject(pParse->db, nKeyCol+1, 0, &zNotUsed);
+ assert( nKeyCol <= pTable->nCol + MAX(0, pTable->nCol - BMS + 1) );
+ /* ^-- This guarantees that the number of index columns will fit in the u16 */
+ pIdx = sqlite3AllocateIndexObject(pParse->db, nKeyCol+HasRowid(pTable),
+ 0, &zNotUsed);
if( pIdx==0 ) goto end_auto_index_create;
pLoop->u.btree.pIndex = pIdx;
pIdx->zName = "auto-index";
@@ -164334,8 +165550,10 @@ static SQLITE_NOINLINE void constructAutomaticIndex(
}
}
assert( n==nKeyCol );
- pIdx->aiColumn[n] = XN_ROWID;
- pIdx->azColl[n] = sqlite3StrBINARY;
+ if( HasRowid(pTable) ){
+ pIdx->aiColumn[n] = XN_ROWID;
+ pIdx->azColl[n] = sqlite3StrBINARY;
+ }
/* Create the automatic index */
explainAutomaticIndex(pParse, pIdx, pPartial!=0, &addrExp);
@@ -164686,8 +165904,8 @@ static sqlite3_index_info *allocateIndexInfo(
*/
pIdxInfo = sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo)
+ (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm
- + sizeof(*pIdxOrderBy)*nOrderBy + sizeof(*pHidden)
- + sizeof(sqlite3_value*)*nTerm );
+ + sizeof(*pIdxOrderBy)*nOrderBy
+ + SZ_HIDDENINDEXINFO(nTerm) );
if( pIdxInfo==0 ){
sqlite3ErrorMsg(pParse, "out of memory");
return 0;
@@ -165524,7 +166742,7 @@ static int whereInScanEst(
#endif /* SQLITE_ENABLE_STAT4 */
-#ifdef WHERETRACE_ENABLED
+#if defined(WHERETRACE_ENABLED) || defined(SQLITE_DEBUG)
/*
** Print the content of a WhereTerm object
*/
@@ -165568,6 +166786,9 @@ SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm){
sqlite3TreeViewExpr(0, pTerm->pExpr, 0);
}
}
+SQLITE_PRIVATE void sqlite3ShowWhereTerm(WhereTerm *pTerm){
+ sqlite3WhereTermPrint(pTerm, 0);
+}
#endif
#ifdef WHERETRACE_ENABLED
@@ -165599,8 +166820,9 @@ SQLITE_PRIVATE void sqlite3WhereClausePrint(WhereClause *pWC){
** 1.002.001 t2.t2xy 2 f 010241 N 2 cost 0,56,31
*/
SQLITE_PRIVATE void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC){
+ WhereInfo *pWInfo;
if( pWC ){
- WhereInfo *pWInfo = pWC->pWInfo;
+ pWInfo = pWC->pWInfo;
int nb = 1+(pWInfo->pTabList->nSrc+3)/4;
SrcItem *pItem = pWInfo->pTabList->a + p->iTab;
Table *pTab = pItem->pSTab;
@@ -165610,6 +166832,7 @@ SQLITE_PRIVATE void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause
sqlite3DebugPrintf(" %12s",
pItem->zAlias ? pItem->zAlias : pTab->zName);
}else{
+ pWInfo = 0;
sqlite3DebugPrintf("%c%2d.%03llx.%03llx %c%d",
p->cId, p->iTab, p->maskSelf, p->prereq & 0xfff, p->cId, p->iTab);
}
@@ -165641,7 +166864,12 @@ SQLITE_PRIVATE void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause
}else{
sqlite3DebugPrintf(" f %06x N %d", p->wsFlags, p->nLTerm);
}
- sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut);
+ if( pWInfo && pWInfo->bStarUsed && p->rStarDelta!=0 ){
+ sqlite3DebugPrintf(" cost %d,%d,%d delta=%d\n",
+ p->rSetup, p->rRun, p->nOut, p->rStarDelta);
+ }else{
+ sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut);
+ }
if( p->nLTerm && (sqlite3WhereTrace & 0x4000)!=0 ){
int i;
for(i=0; i<p->nLTerm; i++){
@@ -166313,11 +167541,8 @@ static int whereLoopAddBtreeIndex(
assert( pNew->u.btree.nBtm==0 );
opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS;
}
- if( pProbe->bUnordered || pProbe->bLowQual ){
- if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
- if( pProbe->bLowQual && pSrc->fg.isIndexedBy==0 ){
- opMask &= ~(WO_EQ|WO_IN|WO_IS);
- }
+ if( pProbe->bUnordered ){
+ opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
}
assert( pNew->u.btree.nEq<pProbe->nColumn );
@@ -166630,7 +167855,7 @@ static int whereLoopAddBtreeIndex(
if( (pNew->wsFlags & WHERE_TOP_LIMIT)==0
&& pNew->u.btree.nEq<pProbe->nColumn
&& (pNew->u.btree.nEq<pProbe->nKeyCol ||
- pProbe->idxType!=SQLITE_IDXTYPE_PRIMARYKEY)
+ (pProbe->idxType!=SQLITE_IDXTYPE_PRIMARYKEY && !pProbe->bIdxRowid))
){
if( pNew->u.btree.nEq>3 ){
sqlite3ProgressCheck(pParse);
@@ -166753,13 +167978,13 @@ static int whereUsablePartialIndex(
if( !whereUsablePartialIndex(iTab,jointype,pWC,pWhere->pLeft) ) return 0;
pWhere = pWhere->pRight;
}
- if( pParse->db->flags & SQLITE_EnableQPSG ) pParse = 0;
for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
Expr *pExpr;
pExpr = pTerm->pExpr;
if( (!ExprHasProperty(pExpr, EP_OuterON) || pExpr->w.iJoin==iTab)
&& ((jointype & JT_OUTER)==0 || ExprHasProperty(pExpr, EP_OuterON))
&& sqlite3ExprImpliesExpr(pParse, pExpr, pWhere, iTab)
+ && !sqlite3ExprImpliesExpr(pParse, pExpr, pWhere, -1)
&& (pTerm->wtFlags & TERM_VNULL)==0
){
return 1;
@@ -167108,7 +168333,6 @@ static int whereLoopAddBtree(
&& (pWInfo->pParse->db->flags & SQLITE_AutoIndex)!=0
&& !pSrc->fg.isIndexedBy /* Has no INDEXED BY clause */
&& !pSrc->fg.notIndexed /* Has no NOT INDEXED clause */
- && HasRowid(pTab) /* Not WITHOUT ROWID table. (FIXME: Why not?) */
&& !pSrc->fg.isCorrelated /* Not a correlated subquery */
&& !pSrc->fg.isRecursive /* Not a recursive common table expression. */
&& (pSrc->fg.jointype & JT_RIGHT)==0 /* Not the right tab of a RIGHT JOIN */
@@ -167256,7 +168480,7 @@ static int whereLoopAddBtree(
&& (HasRowid(pTab) || pWInfo->pSelect!=0 || sqlite3FaultSim(700))
){
WHERETRACE(0x200,
- ("-> %s a covering index according to bitmasks\n",
+ ("-> %s is a covering index according to bitmasks\n",
pProbe->zName, m==0 ? "is" : "is not"));
pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED;
}
@@ -168611,68 +169835,201 @@ static LogEst whereSortingCost(
** 18 for star queries
** 12 otherwise
**
-** For the purposes of SQLite, a star-query is defined as a query
-** with a large central table that is joined against four or more
-** smaller tables. The central table is called the "fact" table.
-** The smaller tables that get joined are "dimension tables".
+** For the purposes of this heuristic, a star-query is defined as a query
+** with a large central table that is joined using an INNER JOIN,
+** not CROSS or OUTER JOINs, against four or more smaller tables.
+** The central table is called the "fact" table. The smaller tables
+** that get joined are "dimension tables". Also, any table that is
+** self-joined cannot be a dimension table; we assume that dimension
+** tables may only be joined against fact tables.
**
** SIDE EFFECT: (and really the whole point of this subroutine)
**
-** If pWInfo describes a star-query, then the cost on WhereLoops for the
-** fact table is reduced. This heuristic helps keep fact tables in
-** outer loops. Without this heuristic, paths with fact tables in outer
-** loops tend to get pruned by the mxChoice limit on the number of paths,
-** resulting in poor query plans. The total amount of heuristic cost
-** adjustment is stored in pWInfo->nOutStarDelta and the cost adjustment
-** for each WhereLoop is stored in its rStarDelta field.
+** If pWInfo describes a star-query, then the cost for SCANs of dimension
+** WhereLoops is increased to be slightly larger than the cost of a SCAN
+** in the fact table. Only SCAN costs are increased. SEARCH costs are
+** unchanged. This heuristic helps keep fact tables in outer loops. Without
+** this heuristic, paths with fact tables in outer loops tend to get pruned
+** by the mxChoice limit on the number of paths, resulting in poor query
+** plans. See the starschema1.test test module for examples of queries
+** that need this heuristic to find good query plans.
+**
+** This heuristic can be completely disabled, so that no query is
+** considered a star-query, using SQLITE_TESTCTRL_OPTIMIZATION to
+** disable the SQLITE_StarQuery optimization. In the CLI, the command
+** to do that is: ".testctrl opt -starquery".
+**
+** HISTORICAL NOTES:
+**
+** This optimization was first added on 2024-05-09 by check-in 38db9b5c83d.
+** The original optimization reduced the cost and output size estimate for
+** fact tables to help them move to outer loops. But months later (as people
+** started upgrading) performance regression reports started caming in,
+** including:
+**
+** forum post b18ef983e68d06d1 (2024-12-21)
+** forum post 0025389d0860af82 (2025-01-14)
+** forum post d87570a145599033 (2025-01-17)
+**
+** To address these, the criteria for a star-query was tightened to exclude
+** cases where the fact and dimensions are separated by an outer join, and
+** the affect of star-schema detection was changed to increase the rRun cost
+** on just full table scans of dimension tables, rather than reducing costs
+** in the all access methods of the fact table.
*/
-static int computeMxChoice(WhereInfo *pWInfo, LogEst nRowEst){
+static int computeMxChoice(WhereInfo *pWInfo){
int nLoop = pWInfo->nLevel; /* Number of terms in the join */
- if( nRowEst==0 && nLoop>=5 ){
- /* Check to see if we are dealing with a star schema and if so, reduce
- ** the cost of fact tables relative to dimension tables, as a heuristic
- ** to help keep the fact tables in outer loops.
+ WhereLoop *pWLoop; /* For looping over WhereLoops */
+
+#ifdef SQLITE_DEBUG
+ /* The star-query detection code below makes use of the following
+ ** properties of the WhereLoop list, so verify them before
+ ** continuing:
+ ** (1) .maskSelf is the bitmask corresponding to .iTab
+ ** (2) The WhereLoop list is in ascending .iTab order
+ */
+ for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){
+ assert( pWLoop->maskSelf==MASKBIT(pWLoop->iTab) );
+ assert( pWLoop->pNextLoop==0 || pWLoop->iTab<=pWLoop->pNextLoop->iTab );
+ }
+#endif /* SQLITE_DEBUG */
+
+ if( nLoop>=5
+ && !pWInfo->bStarDone
+ && OptimizationEnabled(pWInfo->pParse->db, SQLITE_StarQuery)
+ ){
+ SrcItem *aFromTabs; /* All terms of the FROM clause */
+ int iFromIdx; /* Term of FROM clause is the candidate fact-table */
+ Bitmask m; /* Bitmask for candidate fact-table */
+ Bitmask mSelfJoin = 0; /* Tables that cannot be dimension tables */
+ WhereLoop *pStart; /* Where to start searching for dimension-tables */
+
+ pWInfo->bStarDone = 1; /* Only do this computation once */
+
+ /* Look for fact tables with four or more dimensions where the
+ ** dimension tables are not separately from the fact tables by an outer
+ ** or cross join. Adjust cost weights if found.
*/
- int iLoop; /* Counter over join terms */
- Bitmask m; /* Bitmask for current loop */
- assert( pWInfo->nOutStarDelta==0 );
- for(iLoop=0, m=1; iLoop<nLoop; iLoop++, m<<=1){
- WhereLoop *pWLoop; /* For looping over WhereLoops */
+ assert( !pWInfo->bStarUsed );
+ aFromTabs = pWInfo->pTabList->a;
+ pStart = pWInfo->pLoops;
+ for(iFromIdx=0, m=1; iFromIdx<nLoop; iFromIdx++, m<<=1){
int nDep = 0; /* Number of dimension tables */
- LogEst rDelta; /* Heuristic cost adjustment */
+ LogEst mxRun; /* Maximum SCAN cost of a fact table */
Bitmask mSeen = 0; /* Mask of dimension tables */
- for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){
- if( (pWLoop->prereq & m)!=0 && (pWLoop->maskSelf & mSeen)==0 ){
- nDep++;
- mSeen |= pWLoop->maskSelf;
+ SrcItem *pFactTab; /* The candidate fact table */
+
+ pFactTab = aFromTabs + iFromIdx;
+ if( (pFactTab->fg.jointype & (JT_OUTER|JT_CROSS))!=0 ){
+ /* If the candidate fact-table is the right table of an outer join
+ ** restrict the search for dimension-tables to be tables to the right
+ ** of the fact-table. */
+ if( iFromIdx+4 > nLoop ) break; /* Impossible to reach nDep>=4 */
+ while( pStart && pStart->iTab<=iFromIdx ){
+ pStart = pStart->pNextLoop;
+ }
+ }
+ for(pWLoop=pStart; pWLoop; pWLoop=pWLoop->pNextLoop){
+ if( (aFromTabs[pWLoop->iTab].fg.jointype & (JT_OUTER|JT_CROSS))!=0 ){
+ /* Fact-tables and dimension-tables cannot be separated by an
+ ** outer join (at least for the definition of fact- and dimension-
+ ** used by this heuristic). */
+ break;
+ }
+ if( (pWLoop->prereq & m)!=0 /* pWInfo depends on iFromIdx */
+ && (pWLoop->maskSelf & mSeen)==0 /* pWInfo not already a dependency */
+ && (pWLoop->maskSelf & mSelfJoin)==0 /* Not a self-join */
+ ){
+ if( aFromTabs[pWLoop->iTab].pSTab==pFactTab->pSTab ){
+ mSelfJoin |= m;
+ }else{
+ nDep++;
+ mSeen |= pWLoop->maskSelf;
+ }
}
}
if( nDep<=3 ) continue;
- rDelta = 15*(nDep-3);
-#ifdef WHERETRACE_ENABLED /* 0x4 */
- if( sqlite3WhereTrace&0x4 ){
- SrcItem *pItem = pWInfo->pTabList->a + iLoop;
- sqlite3DebugPrintf("Fact-table %s: %d dimensions, cost reduced %d\n",
- pItem->zAlias ? pItem->zAlias : pItem->pSTab->zName,
- nDep, rDelta);
- }
-#endif
- if( pWInfo->nOutStarDelta==0 ){
+
+ /* If we reach this point, it means that pFactTab is a fact table
+ ** with four or more dimensions connected by inner joins. Proceed
+ ** to make cost adjustments. */
+
+#ifdef WHERETRACE_ENABLED
+ /* Make sure rStarDelta values are initialized */
+ if( !pWInfo->bStarUsed ){
for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){
pWLoop->rStarDelta = 0;
}
}
- pWInfo->nOutStarDelta += rDelta;
+#endif
+ pWInfo->bStarUsed = 1;
+
+ /* Compute the maximum cost of any WhereLoop for the
+ ** fact table plus one epsilon */
+ mxRun = LOGEST_MIN;
+ for(pWLoop=pStart; pWLoop; pWLoop=pWLoop->pNextLoop){
+ if( pWLoop->iTab<iFromIdx ) continue;
+ if( pWLoop->iTab>iFromIdx ) break;
+ if( pWLoop->rRun>mxRun ) mxRun = pWLoop->rRun;
+ }
+ if( ALWAYS(mxRun<LOGEST_MAX) ) mxRun++;
+
+ /* Increase the cost of table scans for dimension tables to be
+ ** slightly more than the maximum cost of the fact table */
+ for(pWLoop=pStart; pWLoop; pWLoop=pWLoop->pNextLoop){
+ if( (pWLoop->maskSelf & mSeen)==0 ) continue;
+ if( pWLoop->nLTerm ) continue;
+ if( pWLoop->rRun<mxRun ){
+#ifdef WHERETRACE_ENABLED /* 0x80000 */
+ if( sqlite3WhereTrace & 0x80000 ){
+ SrcItem *pDim = aFromTabs + pWLoop->iTab;
+ sqlite3DebugPrintf(
+ "Increase SCAN cost of dimension %s(%d) of fact %s(%d) to %d\n",
+ pDim->zAlias ? pDim->zAlias: pDim->pSTab->zName, pWLoop->iTab,
+ pFactTab->zAlias ? pFactTab->zAlias : pFactTab->pSTab->zName,
+ iFromIdx, mxRun
+ );
+ }
+ pWLoop->rStarDelta = mxRun - pWLoop->rRun;
+#endif /* WHERETRACE_ENABLED */
+ pWLoop->rRun = mxRun;
+ }
+ }
+ }
+#ifdef WHERETRACE_ENABLED /* 0x80000 */
+ if( (sqlite3WhereTrace & 0x80000)!=0 && pWInfo->bStarUsed ){
+ sqlite3DebugPrintf("WhereLoops changed by star-query heuristic:\n");
for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){
- if( pWLoop->maskSelf==m ){
- pWLoop->rRun -= rDelta;
- pWLoop->nOut -= rDelta;
- pWLoop->rStarDelta = rDelta;
+ if( pWLoop->rStarDelta ){
+ sqlite3WhereLoopPrint(pWLoop, &pWInfo->sWC);
}
}
}
+#endif
}
- return pWInfo->nOutStarDelta>0 ? 18 : 12;
+ return pWInfo->bStarUsed ? 18 : 12;
+}
+
+/*
+** Two WhereLoop objects, pCandidate and pBaseline, are known to have the
+** same cost. Look deep into each to see if pCandidate is even slightly
+** better than pBaseline. Return false if it is, if pCandidate is is preferred.
+** Return true if pBaseline is preferred or if we cannot tell the difference.
+**
+** Result Meaning
+** -------- ----------------------------------------------------------
+** true We cannot tell the difference in pCandidate and pBaseline
+** false pCandidate seems like a better choice than pBaseline
+*/
+static SQLITE_NOINLINE int whereLoopIsNoBetter(
+ const WhereLoop *pCandidate,
+ const WhereLoop *pBaseline
+){
+ if( (pCandidate->wsFlags & WHERE_INDEXED)==0 ) return 1;
+ if( (pBaseline->wsFlags & WHERE_INDEXED)==0 ) return 1;
+ if( pCandidate->u.btree.pIndex->szIdxRow <
+ pBaseline->u.btree.pIndex->szIdxRow ) return 0;
+ return 1;
}
/*
@@ -168696,7 +170053,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
int mxI = 0; /* Index of next entry to replace */
int nOrderBy; /* Number of ORDER BY clause terms */
LogEst mxCost = 0; /* Maximum cost of a set of paths */
- LogEst mxUnsorted = 0; /* Maximum unsorted cost of a set of path */
+ LogEst mxUnsort = 0; /* Maximum unsorted cost of a set of path */
int nTo, nFrom; /* Number of valid entries in aTo[] and aFrom[] */
WherePath *aFrom; /* All nFrom paths at the previous level */
WherePath *aTo; /* The nTo best paths at the current level */
@@ -168725,8 +170082,10 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
mxChoice = 1;
}else if( nLoop==2 ){
mxChoice = 5;
+ }else if( pParse->nErr ){
+ mxChoice = 1;
}else{
- mxChoice = computeMxChoice(pWInfo, nRowEst);
+ mxChoice = computeMxChoice(pWInfo);
}
assert( nLoop<=pWInfo->pTabList->nSrc );
@@ -168793,7 +170152,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
for(pWLoop=pWInfo->pLoops; pWLoop; pWLoop=pWLoop->pNextLoop){
LogEst nOut; /* Rows visited by (pFrom+pWLoop) */
LogEst rCost; /* Cost of path (pFrom+pWLoop) */
- LogEst rUnsorted; /* Unsorted cost of (pFrom+pWLoop) */
+ LogEst rUnsort; /* Unsorted cost of (pFrom+pWLoop) */
i8 isOrdered; /* isOrdered for (pFrom+pWLoop) */
Bitmask maskNew; /* Mask of src visited by (..) */
Bitmask revMask; /* Mask of rev-order loops for (..) */
@@ -168811,11 +170170,11 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
/* At this point, pWLoop is a candidate to be the next loop.
** Compute its cost */
- rUnsorted = pWLoop->rRun + pFrom->nRow;
+ rUnsort = pWLoop->rRun + pFrom->nRow;
if( pWLoop->rSetup ){
- rUnsorted = sqlite3LogEstAdd(pWLoop->rSetup, rUnsorted);
+ rUnsort = sqlite3LogEstAdd(pWLoop->rSetup, rUnsort);
}
- rUnsorted = sqlite3LogEstAdd(rUnsorted, pFrom->rUnsorted);
+ rUnsort = sqlite3LogEstAdd(rUnsort, pFrom->rUnsort);
nOut = pFrom->nRow + pWLoop->nOut;
maskNew = pFrom->maskLoop | pWLoop->maskSelf;
isOrdered = pFrom->isOrdered;
@@ -168837,15 +170196,15 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
** extra encouragement to the query planner to select a plan
** where the rows emerge in the correct order without any sorting
** required. */
- rCost = sqlite3LogEstAdd(rUnsorted, aSortCost[isOrdered]) + 3;
+ rCost = sqlite3LogEstAdd(rUnsort, aSortCost[isOrdered]) + 3;
WHERETRACE(0x002,
("---- sort cost=%-3d (%d/%d) increases cost %3d to %-3d\n",
aSortCost[isOrdered], (nOrderBy-isOrdered), nOrderBy,
- rUnsorted, rCost));
+ rUnsort, rCost));
}else{
- rCost = rUnsorted;
- rUnsorted -= 2; /* TUNING: Slight bias in favor of no-sort plans */
+ rCost = rUnsort;
+ rUnsort -= 2; /* TUNING: Slight bias in favor of no-sort plans */
}
/* Check to see if pWLoop should be added to the set of
@@ -168871,7 +170230,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
if( jj>=nTo ){
/* None of the existing best-so-far paths match the candidate. */
if( nTo>=mxChoice
- && (rCost>mxCost || (rCost==mxCost && rUnsorted>=mxUnsorted))
+ && (rCost>mxCost || (rCost==mxCost && rUnsort>=mxUnsort))
){
/* The current candidate is no better than any of the mxChoice
** paths currently in the best-so-far buffer. So discard
@@ -168879,7 +170238,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
#ifdef WHERETRACE_ENABLED /* 0x4 */
if( sqlite3WhereTrace&0x4 ){
sqlite3DebugPrintf("Skip %s cost=%-3d,%3d,%3d order=%c\n",
- wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted,
+ wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsort,
isOrdered>=0 ? isOrdered+'0' : '?');
}
#endif
@@ -168898,7 +170257,7 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
#ifdef WHERETRACE_ENABLED /* 0x4 */
if( sqlite3WhereTrace&0x4 ){
sqlite3DebugPrintf("New %s cost=%-3d,%3d,%3d order=%c\n",
- wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted,
+ wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsort,
isOrdered>=0 ? isOrdered+'0' : '?');
}
#endif
@@ -168909,24 +170268,23 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
** pTo or if the candidate should be skipped.
**
** The conditional is an expanded vector comparison equivalent to:
- ** (pTo->rCost,pTo->nRow,pTo->rUnsorted) <= (rCost,nOut,rUnsorted)
+ ** (pTo->rCost,pTo->nRow,pTo->rUnsort) <= (rCost,nOut,rUnsort)
*/
- if( pTo->rCost<rCost
- || (pTo->rCost==rCost
- && (pTo->nRow<nOut
- || (pTo->nRow==nOut && pTo->rUnsorted<=rUnsorted)
- )
- )
+ if( (pTo->rCost<rCost)
+ || (pTo->rCost==rCost && pTo->nRow<nOut)
+ || (pTo->rCost==rCost && pTo->nRow==nOut && pTo->rUnsort<rUnsort)
+ || (pTo->rCost==rCost && pTo->nRow==nOut && pTo->rUnsort==rUnsort
+ && whereLoopIsNoBetter(pWLoop, pTo->aLoop[iLoop]) )
){
#ifdef WHERETRACE_ENABLED /* 0x4 */
if( sqlite3WhereTrace&0x4 ){
sqlite3DebugPrintf(
"Skip %s cost=%-3d,%3d,%3d order=%c",
- wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted,
+ wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsort,
isOrdered>=0 ? isOrdered+'0' : '?');
sqlite3DebugPrintf(" vs %s cost=%-3d,%3d,%3d order=%c\n",
wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow,
- pTo->rUnsorted, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?');
+ pTo->rUnsort, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?');
}
#endif
/* Discard the candidate path from further consideration */
@@ -168940,11 +170298,11 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
if( sqlite3WhereTrace&0x4 ){
sqlite3DebugPrintf(
"Update %s cost=%-3d,%3d,%3d order=%c",
- wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsorted,
+ wherePathName(pFrom, iLoop, pWLoop), rCost, nOut, rUnsort,
isOrdered>=0 ? isOrdered+'0' : '?');
sqlite3DebugPrintf(" was %s cost=%-3d,%3d,%3d order=%c\n",
wherePathName(pTo, iLoop+1, 0), pTo->rCost, pTo->nRow,
- pTo->rUnsorted, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?');
+ pTo->rUnsort, pTo->isOrdered>=0 ? pTo->isOrdered+'0' : '?');
}
#endif
}
@@ -168953,20 +170311,20 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
pTo->revLoop = revMask;
pTo->nRow = nOut;
pTo->rCost = rCost;
- pTo->rUnsorted = rUnsorted;
+ pTo->rUnsort = rUnsort;
pTo->isOrdered = isOrdered;
memcpy(pTo->aLoop, pFrom->aLoop, sizeof(WhereLoop*)*iLoop);
pTo->aLoop[iLoop] = pWLoop;
if( nTo>=mxChoice ){
mxI = 0;
mxCost = aTo[0].rCost;
- mxUnsorted = aTo[0].nRow;
+ mxUnsort = aTo[0].nRow;
for(jj=1, pTo=&aTo[1]; jj<mxChoice; jj++, pTo++){
if( pTo->rCost>mxCost
- || (pTo->rCost==mxCost && pTo->rUnsorted>mxUnsorted)
+ || (pTo->rCost==mxCost && pTo->rUnsort>mxUnsort)
){
mxCost = pTo->rCost;
- mxUnsorted = pTo->rUnsorted;
+ mxUnsort = pTo->rUnsort;
mxI = jj;
}
}
@@ -168978,8 +170336,10 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
if( sqlite3WhereTrace & 0x02 ){
LogEst rMin, rFloor = 0;
int nDone = 0;
+ int nProgress;
sqlite3DebugPrintf("---- after round %d ----\n", iLoop);
- while( nDone<nTo ){
+ do{
+ nProgress = 0;
rMin = 0x7fff;
for(ii=0, pTo=aTo; ii<nTo; ii++, pTo++){
if( pTo->rCost>rFloor && pTo->rCost<rMin ) rMin = pTo->rCost;
@@ -168995,10 +170355,11 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
sqlite3DebugPrintf("\n");
}
nDone++;
+ nProgress++;
}
}
rFloor = rMin;
- }
+ }while( nDone<nTo && nProgress>0 );
}
#endif
@@ -169092,7 +170453,10 @@ static int wherePathSolver(WhereInfo *pWInfo, LogEst nRowEst){
}
}
- pWInfo->nRowOut = pFrom->nRow + pWInfo->nOutStarDelta;
+ pWInfo->nRowOut = pFrom->nRow;
+#ifdef WHERETRACE_ENABLED
+ pWInfo->rTotalCost = pFrom->rCost;
+#endif
/* Free temporary memory and return success */
sqlite3StackFreeNN(pParse->db, pSpace);
@@ -169414,7 +170778,7 @@ static SQLITE_NOINLINE Bitmask whereOmitNoopJoin(
}
if( hasRightJoin
&& ExprHasProperty(pTerm->pExpr, EP_InnerON)
- && pTerm->pExpr->w.iJoin==pItem->iCursor
+ && NEVER(pTerm->pExpr->w.iJoin==pItem->iCursor)
){
break; /* restriction (5) */
}
@@ -169490,7 +170854,6 @@ static SQLITE_NOINLINE void whereCheckIfBloomFilterIsUseful(
}
}
nSearch += pLoop->nOut;
- if( pWInfo->nOutStarDelta ) nSearch += pLoop->rStarDelta;
}
}
@@ -169734,10 +171097,7 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
** field (type Bitmask) it must be aligned on an 8-byte boundary on
** some architectures. Hence the ROUND8() below.
*/
- nByteWInfo = ROUND8P(sizeof(WhereInfo));
- if( nTabList>1 ){
- nByteWInfo = ROUND8P(nByteWInfo + (nTabList-1)*sizeof(WhereLevel));
- }
+ nByteWInfo = SZ_WHEREINFO(nTabList);
pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop));
if( db->mallocFailed ){
sqlite3DbFree(db, pWInfo);
@@ -169954,7 +171314,8 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
}
/* TUNING: Assume that a DISTINCT clause on a subquery reduces
- ** the output size by a factor of 8 (LogEst -30).
+ ** the output size by a factor of 8 (LogEst -30). Search for
+ ** tag-20250414a to see other cases.
*/
if( (pWInfo->wctrlFlags & WHERE_WANT_DISTINCT)!=0 ){
WHERETRACE(0x0080,("nRowOut reduced from %d to %d due to DISTINCT\n",
@@ -169973,7 +171334,8 @@ SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(
assert( db->mallocFailed==0 );
#ifdef WHERETRACE_ENABLED
if( sqlite3WhereTrace ){
- sqlite3DebugPrintf("---- Solution nRow=%d", pWInfo->nRowOut);
+ sqlite3DebugPrintf("---- Solution cost=%d, nRow=%d",
+ pWInfo->rTotalCost, pWInfo->nRowOut);
if( pWInfo->nOBSat>0 ){
sqlite3DebugPrintf(" ORDERBY=%d,0x%llx", pWInfo->nOBSat, pWInfo->revMask);
}
@@ -170334,6 +171696,7 @@ whereBeginError:
){
if( (db->flags & SQLITE_VdbeAddopTrace)==0 ) return;
sqlite3VdbePrintOp(0, pc, pOp);
+ sqlite3ShowWhereTerm(0); /* So compiler won't complain about unused func */
}
#endif
@@ -170633,14 +171996,28 @@ SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo *pWInfo){
pOp->p2 = x;
pOp->p1 = pLevel->iIdxCur;
OpcodeRewriteTrace(db, k, pOp);
- }else{
- /* Unable to translate the table reference into an index
- ** reference. Verify that this is harmless - that the
- ** table being referenced really is open.
- */
+ }else if( pLoop->wsFlags & (WHERE_IDX_ONLY|WHERE_EXPRIDX) ){
if( pLoop->wsFlags & WHERE_IDX_ONLY ){
+ /* An error. pLoop is supposed to be a covering index loop,
+ ** and yet the VM code refers to a column of the table that
+ ** is not part of the index. */
sqlite3ErrorMsg(pParse, "internal query planner error");
pParse->rc = SQLITE_INTERNAL;
+ }else{
+ /* The WHERE_EXPRIDX flag is set by the planner when it is likely
+ ** that pLoop is a covering index loop, but it is not possible
+ ** to be 100% sure. In this case, any OP_Explain opcode
+ ** corresponding to this loop describes the index as a "COVERING
+ ** INDEX". But, pOp proves that pLoop is not actually a covering
+ ** index loop. So clear the WHERE_EXPRIDX flag and rewrite the
+ ** text that accompanies the OP_Explain opcode, if any. */
+ pLoop->wsFlags &= ~WHERE_EXPRIDX;
+ sqlite3WhereAddExplainText(pParse,
+ pLevel->addrBody-1,
+ pTabList,
+ pLevel,
+ pWInfo->wctrlFlags
+ );
}
}
}else if( pOp->opcode==OP_Rowid ){
@@ -171673,7 +173050,7 @@ SQLITE_PRIVATE int sqlite3WindowRewrite(Parse *pParse, Select *p){
p->pWhere = 0;
p->pGroupBy = 0;
p->pHaving = 0;
- p->selFlags &= ~SF_Aggregate;
+ p->selFlags &= ~(u32)SF_Aggregate;
p->selFlags |= SF_WinRewrite;
/* Create the ORDER BY clause for the sub-select. This is the concatenation
@@ -172348,6 +173725,7 @@ static void windowAggStep(
int regArg;
int nArg = pWin->bExprArgs ? 0 : windowArgCount(pWin);
int i;
+ int addrIf = 0;
assert( bInverse==0 || pWin->eStart!=TK_UNBOUNDED );
@@ -172364,6 +173742,18 @@ static void windowAggStep(
}
regArg = reg;
+ if( pWin->pFilter ){
+ int regTmp;
+ assert( ExprUseXList(pWin->pOwner) );
+ assert( pWin->bExprArgs || !nArg ||nArg==pWin->pOwner->x.pList->nExpr );
+ assert( pWin->bExprArgs || nArg ||pWin->pOwner->x.pList==0 );
+ regTmp = sqlite3GetTempReg(pParse);
+ sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp);
+ addrIf = sqlite3VdbeAddOp3(v, OP_IfNot, regTmp, 0, 1);
+ VdbeCoverage(v);
+ sqlite3ReleaseTempReg(pParse, regTmp);
+ }
+
if( pMWin->regStartRowid==0
&& (pFunc->funcFlags & SQLITE_FUNC_MINMAX)
&& (pWin->eStart!=TK_UNBOUNDED)
@@ -172383,25 +173773,13 @@ static void windowAggStep(
}
sqlite3VdbeJumpHere(v, addrIsNull);
}else if( pWin->regApp ){
+ assert( pWin->pFilter==0 );
assert( pFunc->zName==nth_valueName
|| pFunc->zName==first_valueName
);
assert( bInverse==0 || bInverse==1 );
sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1-bInverse, 1);
}else if( pFunc->xSFunc!=noopStepFunc ){
- int addrIf = 0;
- if( pWin->pFilter ){
- int regTmp;
- assert( ExprUseXList(pWin->pOwner) );
- assert( pWin->bExprArgs || !nArg ||nArg==pWin->pOwner->x.pList->nExpr );
- assert( pWin->bExprArgs || nArg ||pWin->pOwner->x.pList==0 );
- regTmp = sqlite3GetTempReg(pParse);
- sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp);
- addrIf = sqlite3VdbeAddOp3(v, OP_IfNot, regTmp, 0, 1);
- VdbeCoverage(v);
- sqlite3ReleaseTempReg(pParse, regTmp);
- }
-
if( pWin->bExprArgs ){
int iOp = sqlite3VdbeCurrentAddr(v);
int iEnd;
@@ -172428,12 +173806,13 @@ static void windowAggStep(
sqlite3VdbeAddOp3(v, bInverse? OP_AggInverse : OP_AggStep,
bInverse, regArg, pWin->regAccum);
sqlite3VdbeAppendP4(v, pFunc, P4_FUNCDEF);
- sqlite3VdbeChangeP5(v, (u8)nArg);
+ sqlite3VdbeChangeP5(v, (u16)nArg);
if( pWin->bExprArgs ){
sqlite3ReleaseTempRange(pParse, regArg, nArg);
}
- if( addrIf ) sqlite3VdbeJumpHere(v, addrIf);
}
+
+ if( addrIf ) sqlite3VdbeJumpHere(v, addrIf);
}
}
@@ -173812,6 +175191,11 @@ SQLITE_PRIVATE void sqlite3WindowCodeStep(
/* #include "sqliteInt.h" */
/*
+** Verify that the pParse->isCreate field is set
+*/
+#define ASSERT_IS_CREATE assert(pParse->isCreate)
+
+/*
** Disable all error recovery processing in the parser push-down
** automaton.
*/
@@ -173861,12 +175245,23 @@ struct TrigEvent { int a; IdList * b; };
struct FrameBound { int eType; Expr *pExpr; };
/*
+** Generate a syntax error
+*/
+static void parserSyntaxError(Parse *pParse, Token *p){
+ sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", p);
+}
+
+/*
** Disable lookaside memory allocation for objects that might be
** shared across database connections.
*/
static void disableLookaside(Parse *pParse){
sqlite3 *db = pParse->db;
pParse->disableLookaside++;
+#ifdef SQLITE_DEBUG
+ pParse->isCreate = 1;
+#endif
+ memset(&pParse->u1.cr, 0, sizeof(pParse->u1.cr));
DisableLookaside;
}
@@ -174207,7 +175602,8 @@ static void updateDeleteLimitError(
#define TK_ERROR 182
#define TK_QNUMBER 183
#define TK_SPACE 184
-#define TK_ILLEGAL 185
+#define TK_COMMENT 185
+#define TK_ILLEGAL 186
#endif
/**************** End token definitions ***************************************/
@@ -174272,31 +175668,31 @@ static void updateDeleteLimitError(
#endif
/************* Begin control #defines *****************************************/
#define YYCODETYPE unsigned short int
-#define YYNOCODE 322
+#define YYNOCODE 323
#define YYACTIONTYPE unsigned short int
#define YYWILDCARD 102
#define sqlite3ParserTOKENTYPE Token
typedef union {
int yyinit;
sqlite3ParserTOKENTYPE yy0;
- ExprList* yy14;
- With* yy59;
- Cte* yy67;
- Upsert* yy122;
- IdList* yy132;
- int yy144;
- const char* yy168;
- SrcList* yy203;
- Window* yy211;
- OnOrUsing yy269;
- struct TrigEvent yy286;
- struct {int value; int mask;} yy383;
- u32 yy391;
- TriggerStep* yy427;
- Expr* yy454;
- u8 yy462;
- struct FrameBound yy509;
- Select* yy555;
+ u32 yy9;
+ struct TrigEvent yy28;
+ With* yy125;
+ IdList* yy204;
+ struct FrameBound yy205;
+ TriggerStep* yy319;
+ const char* yy342;
+ Cte* yy361;
+ ExprList* yy402;
+ Upsert* yy403;
+ OnOrUsing yy421;
+ u8 yy444;
+ struct {int value; int mask;} yy481;
+ Window* yy483;
+ int yy502;
+ SrcList* yy563;
+ Expr* yy590;
+ Select* yy637;
} YYMINORTYPE;
#ifndef YYSTACKDEPTH
#define YYSTACKDEPTH 100
@@ -174318,7 +175714,7 @@ typedef union {
#define YYNSTATE 583
#define YYNRULE 409
#define YYNRULE_WITH_ACTION 344
-#define YYNTOKEN 186
+#define YYNTOKEN 187
#define YY_MAX_SHIFT 582
#define YY_MIN_SHIFTREDUCE 845
#define YY_MAX_SHIFTREDUCE 1253
@@ -174327,8 +175723,8 @@ typedef union {
#define YY_NO_ACTION 1256
#define YY_MIN_REDUCE 1257
#define YY_MAX_REDUCE 1665
-#define YY_MIN_DSTRCTR 205
-#define YY_MAX_DSTRCTR 319
+#define YY_MIN_DSTRCTR 206
+#define YY_MAX_DSTRCTR 320
/************* End control #defines *******************************************/
#define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])))
@@ -174424,211 +175820,211 @@ static const YYACTIONTYPE yy_action[] = {
/* 80 */ 573, 421, 562, 137, 138, 91, 559, 1228, 1228, 1063,
/* 90 */ 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, 296,
/* 100 */ 460, 398, 1249, 134, 134, 134, 134, 133, 133, 132,
- /* 110 */ 132, 132, 131, 128, 451, 44, 1050, 1050, 1064, 1067,
+ /* 110 */ 132, 132, 131, 128, 451, 451, 1050, 1050, 1064, 1067,
/* 120 */ 1255, 1, 1, 582, 2, 1259, 581, 1174, 1259, 1174,
- /* 130 */ 321, 413, 155, 321, 1584, 155, 379, 112, 498, 1341,
+ /* 130 */ 321, 413, 155, 321, 1584, 155, 379, 112, 481, 1341,
/* 140 */ 456, 299, 1341, 134, 134, 134, 134, 133, 133, 132,
- /* 150 */ 132, 132, 131, 128, 451, 137, 138, 91, 1105, 1228,
+ /* 150 */ 132, 132, 131, 128, 451, 137, 138, 91, 498, 1228,
/* 160 */ 1228, 1063, 1066, 1053, 1053, 135, 135, 136, 136, 136,
- /* 170 */ 136, 1204, 320, 567, 288, 288, 283, 288, 288, 523,
- /* 180 */ 523, 1250, 139, 1541, 7, 214, 503, 573, 1169, 562,
- /* 190 */ 573, 1054, 562, 136, 136, 136, 136, 129, 401, 547,
- /* 200 */ 487, 1169, 245, 1568, 1169, 245, 133, 133, 132, 132,
- /* 210 */ 132, 131, 128, 451, 261, 134, 134, 134, 134, 133,
- /* 220 */ 133, 132, 132, 132, 131, 128, 451, 451, 1204, 1205,
- /* 230 */ 1204, 130, 127, 234, 455, 413, 182, 455, 130, 127,
+ /* 170 */ 136, 1204, 862, 1281, 288, 288, 283, 288, 288, 523,
+ /* 180 */ 523, 1250, 139, 578, 7, 578, 1345, 573, 1169, 562,
+ /* 190 */ 573, 1054, 562, 136, 136, 136, 136, 129, 573, 547,
+ /* 200 */ 562, 1169, 245, 1541, 1169, 245, 133, 133, 132, 132,
+ /* 210 */ 132, 131, 128, 451, 302, 134, 134, 134, 134, 133,
+ /* 220 */ 133, 132, 132, 132, 131, 128, 451, 1575, 1204, 1205,
+ /* 230 */ 1204, 7, 470, 550, 455, 413, 550, 455, 130, 127,
/* 240 */ 234, 134, 134, 134, 134, 133, 133, 132, 132, 132,
- /* 250 */ 131, 128, 451, 136, 136, 136, 136, 538, 576, 137,
- /* 260 */ 138, 91, 261, 1228, 1228, 1063, 1066, 1053, 1053, 135,
- /* 270 */ 135, 136, 136, 136, 136, 44, 472, 346, 1204, 472,
- /* 280 */ 346, 51, 51, 418, 93, 157, 134, 134, 134, 134,
- /* 290 */ 133, 133, 132, 132, 132, 131, 128, 451, 166, 363,
- /* 300 */ 298, 134, 134, 134, 134, 133, 133, 132, 132, 132,
- /* 310 */ 131, 128, 451, 1293, 461, 1570, 423, 377, 275, 134,
+ /* 250 */ 131, 128, 451, 136, 136, 136, 136, 538, 483, 137,
+ /* 260 */ 138, 91, 1019, 1228, 1228, 1063, 1066, 1053, 1053, 135,
+ /* 270 */ 135, 136, 136, 136, 136, 1085, 576, 1204, 132, 132,
+ /* 280 */ 132, 131, 128, 451, 93, 214, 134, 134, 134, 134,
+ /* 290 */ 133, 133, 132, 132, 132, 131, 128, 451, 401, 19,
+ /* 300 */ 19, 134, 134, 134, 134, 133, 133, 132, 132, 132,
+ /* 310 */ 131, 128, 451, 1498, 426, 267, 344, 467, 332, 134,
/* 320 */ 134, 134, 134, 133, 133, 132, 132, 132, 131, 128,
- /* 330 */ 451, 418, 320, 567, 1292, 1204, 1205, 1204, 257, 413,
- /* 340 */ 483, 511, 508, 507, 94, 132, 132, 132, 131, 128,
- /* 350 */ 451, 506, 1204, 548, 548, 388, 576, 384, 7, 413,
- /* 360 */ 550, 229, 522, 137, 138, 91, 530, 1228, 1228, 1063,
- /* 370 */ 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, 51,
- /* 380 */ 51, 1582, 380, 137, 138, 91, 331, 1228, 1228, 1063,
- /* 390 */ 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, 320,
- /* 400 */ 567, 288, 288, 320, 567, 1602, 582, 2, 1259, 1204,
- /* 410 */ 1205, 1204, 1628, 321, 573, 155, 562, 576, 1511, 264,
- /* 420 */ 231, 520, 1341, 134, 134, 134, 134, 133, 133, 132,
- /* 430 */ 132, 132, 131, 128, 451, 519, 1511, 1513, 1333, 1333,
- /* 440 */ 82, 82, 498, 134, 134, 134, 134, 133, 133, 132,
- /* 450 */ 132, 132, 131, 128, 451, 1435, 257, 288, 288, 511,
- /* 460 */ 508, 507, 944, 1568, 413, 1019, 1204, 943, 360, 506,
- /* 470 */ 573, 1598, 562, 44, 575, 551, 551, 557, 1107, 1582,
- /* 480 */ 544, 576, 1107, 40, 417, 245, 531, 1505, 137, 138,
+ /* 330 */ 451, 1281, 576, 6, 1204, 1205, 1204, 257, 576, 413,
+ /* 340 */ 511, 508, 507, 1279, 94, 1019, 464, 1204, 551, 551,
+ /* 350 */ 506, 1224, 1571, 44, 38, 51, 51, 411, 576, 413,
+ /* 360 */ 45, 51, 51, 137, 138, 91, 530, 1228, 1228, 1063,
+ /* 370 */ 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, 398,
+ /* 380 */ 1148, 82, 82, 137, 138, 91, 39, 1228, 1228, 1063,
+ /* 390 */ 1066, 1053, 1053, 135, 135, 136, 136, 136, 136, 344,
+ /* 400 */ 44, 288, 288, 375, 1204, 1205, 1204, 209, 1204, 1224,
+ /* 410 */ 320, 567, 471, 576, 573, 576, 562, 576, 316, 264,
+ /* 420 */ 231, 46, 160, 134, 134, 134, 134, 133, 133, 132,
+ /* 430 */ 132, 132, 131, 128, 451, 303, 82, 82, 82, 82,
+ /* 440 */ 82, 82, 442, 134, 134, 134, 134, 133, 133, 132,
+ /* 450 */ 132, 132, 131, 128, 451, 1582, 544, 320, 567, 1250,
+ /* 460 */ 874, 1582, 380, 382, 413, 1204, 1205, 1204, 360, 182,
+ /* 470 */ 288, 288, 1576, 557, 1339, 557, 7, 557, 1277, 472,
+ /* 480 */ 346, 526, 531, 573, 556, 562, 439, 1511, 137, 138,
/* 490 */ 91, 219, 1228, 1228, 1063, 1066, 1053, 1053, 135, 135,
- /* 500 */ 136, 136, 136, 136, 81, 81, 1281, 1204, 413, 553,
- /* 510 */ 1511, 48, 512, 448, 447, 493, 578, 455, 578, 344,
- /* 520 */ 45, 1204, 1233, 1204, 1205, 1204, 428, 1235, 158, 882,
- /* 530 */ 320, 567, 137, 138, 91, 1234, 1228, 1228, 1063, 1066,
+ /* 500 */ 136, 136, 136, 136, 465, 1511, 1513, 532, 413, 288,
+ /* 510 */ 288, 423, 512, 288, 288, 411, 288, 288, 874, 130,
+ /* 520 */ 127, 234, 573, 1107, 562, 1204, 573, 1107, 562, 573,
+ /* 530 */ 560, 562, 137, 138, 91, 1293, 1228, 1228, 1063, 1066,
/* 540 */ 1053, 1053, 135, 135, 136, 136, 136, 136, 134, 134,
/* 550 */ 134, 134, 133, 133, 132, 132, 132, 131, 128, 451,
- /* 560 */ 1236, 576, 1236, 329, 1204, 1205, 1204, 387, 492, 403,
- /* 570 */ 1040, 382, 489, 123, 568, 1569, 4, 377, 1204, 1205,
- /* 580 */ 1204, 570, 570, 570, 82, 82, 882, 1029, 1331, 1331,
- /* 590 */ 571, 1028, 134, 134, 134, 134, 133, 133, 132, 132,
- /* 600 */ 132, 131, 128, 451, 288, 288, 1281, 1204, 576, 423,
- /* 610 */ 576, 1568, 413, 423, 452, 378, 886, 573, 1279, 562,
- /* 620 */ 46, 557, 532, 1028, 1028, 1030, 565, 130, 127, 234,
- /* 630 */ 556, 82, 82, 82, 82, 479, 137, 138, 91, 462,
+ /* 560 */ 493, 503, 1292, 1204, 257, 288, 288, 511, 508, 507,
+ /* 570 */ 1204, 1628, 1169, 123, 568, 275, 4, 506, 573, 1511,
+ /* 580 */ 562, 331, 1204, 1205, 1204, 1169, 548, 548, 1169, 261,
+ /* 590 */ 571, 7, 134, 134, 134, 134, 133, 133, 132, 132,
+ /* 600 */ 132, 131, 128, 451, 108, 533, 130, 127, 234, 1204,
+ /* 610 */ 448, 447, 413, 1451, 452, 983, 886, 96, 1598, 1233,
+ /* 620 */ 1204, 1205, 1204, 984, 1235, 1450, 565, 1204, 1205, 1204,
+ /* 630 */ 229, 522, 1234, 534, 1333, 1333, 137, 138, 91, 1449,
/* 640 */ 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, 136, 136,
- /* 650 */ 136, 136, 1188, 487, 1506, 1040, 413, 6, 1204, 50,
- /* 660 */ 879, 121, 121, 948, 1204, 1205, 1204, 358, 557, 122,
- /* 670 */ 316, 452, 577, 452, 535, 1204, 1028, 439, 303, 212,
- /* 680 */ 137, 138, 91, 213, 1228, 1228, 1063, 1066, 1053, 1053,
+ /* 650 */ 136, 136, 373, 1595, 971, 1040, 413, 1236, 418, 1236,
+ /* 660 */ 879, 121, 121, 948, 373, 1595, 1204, 1205, 1204, 122,
+ /* 670 */ 1204, 452, 577, 452, 363, 417, 1028, 882, 373, 1595,
+ /* 680 */ 137, 138, 91, 462, 1228, 1228, 1063, 1066, 1053, 1053,
/* 690 */ 135, 135, 136, 136, 136, 136, 134, 134, 134, 134,
/* 700 */ 133, 133, 132, 132, 132, 131, 128, 451, 1028, 1028,
- /* 710 */ 1030, 1031, 35, 288, 288, 1204, 1205, 1204, 1040, 1339,
- /* 720 */ 533, 123, 568, 1569, 4, 377, 573, 1019, 562, 353,
- /* 730 */ 1277, 356, 1204, 1205, 1204, 1029, 488, 1188, 571, 1028,
+ /* 710 */ 1030, 1031, 35, 570, 570, 570, 197, 423, 1040, 198,
+ /* 720 */ 1204, 123, 568, 1204, 4, 320, 567, 1204, 1205, 1204,
+ /* 730 */ 40, 388, 576, 384, 882, 1029, 423, 1188, 571, 1028,
/* 740 */ 134, 134, 134, 134, 133, 133, 132, 132, 132, 131,
- /* 750 */ 128, 451, 576, 343, 288, 288, 449, 449, 449, 971,
- /* 760 */ 413, 1627, 452, 911, 1187, 288, 288, 573, 464, 562,
- /* 770 */ 238, 1028, 1028, 1030, 565, 82, 82, 498, 573, 411,
- /* 780 */ 562, 344, 467, 332, 137, 138, 91, 197, 1228, 1228,
+ /* 750 */ 128, 451, 529, 1568, 1204, 19, 19, 1204, 575, 492,
+ /* 760 */ 413, 157, 452, 489, 1187, 1331, 1331, 5, 1204, 949,
+ /* 770 */ 431, 1028, 1028, 1030, 565, 22, 22, 1204, 1205, 1204,
+ /* 780 */ 1204, 1205, 1204, 477, 137, 138, 91, 212, 1228, 1228,
/* 790 */ 1063, 1066, 1053, 1053, 135, 135, 136, 136, 136, 136,
- /* 800 */ 1188, 528, 1169, 1040, 413, 1110, 1110, 495, 1041, 121,
- /* 810 */ 121, 1204, 317, 540, 862, 1169, 1244, 122, 1169, 452,
- /* 820 */ 577, 452, 1340, 198, 1028, 1204, 481, 526, 137, 138,
- /* 830 */ 91, 560, 1228, 1228, 1063, 1066, 1053, 1053, 135, 135,
+ /* 800 */ 1188, 48, 111, 1040, 413, 1204, 213, 970, 1041, 121,
+ /* 810 */ 121, 1204, 1205, 1204, 1204, 1205, 1204, 122, 221, 452,
+ /* 820 */ 577, 452, 44, 487, 1028, 1204, 1205, 1204, 137, 138,
+ /* 830 */ 91, 378, 1228, 1228, 1063, 1066, 1053, 1053, 135, 135,
/* 840 */ 136, 136, 136, 136, 134, 134, 134, 134, 133, 133,
/* 850 */ 132, 132, 132, 131, 128, 451, 1028, 1028, 1030, 1031,
- /* 860 */ 35, 1204, 288, 288, 1204, 477, 288, 288, 1204, 1205,
- /* 870 */ 1204, 539, 481, 437, 470, 573, 1451, 562, 364, 573,
- /* 880 */ 1153, 562, 1204, 1205, 1204, 1188, 5, 576, 134, 134,
+ /* 860 */ 35, 461, 1204, 1205, 1204, 1569, 1040, 377, 214, 1149,
+ /* 870 */ 1657, 535, 1657, 437, 902, 320, 567, 1568, 364, 320,
+ /* 880 */ 567, 412, 329, 1029, 519, 1188, 3, 1028, 134, 134,
/* 890 */ 134, 134, 133, 133, 132, 132, 132, 131, 128, 451,
- /* 900 */ 221, 214, 302, 96, 1149, 1657, 232, 1657, 413, 392,
- /* 910 */ 19, 19, 1024, 949, 406, 373, 1595, 1085, 1204, 1205,
- /* 920 */ 1204, 1204, 1205, 1204, 1204, 426, 1149, 1658, 413, 1658,
- /* 930 */ 1659, 399, 137, 138, 91, 3, 1228, 1228, 1063, 1066,
- /* 940 */ 1053, 1053, 135, 135, 136, 136, 136, 136, 304, 1311,
- /* 950 */ 514, 1204, 137, 138, 91, 1498, 1228, 1228, 1063, 1066,
- /* 960 */ 1053, 1053, 135, 135, 136, 136, 136, 136, 434, 131,
- /* 970 */ 128, 451, 375, 1204, 274, 291, 372, 517, 367, 516,
- /* 980 */ 262, 1204, 1205, 1204, 1147, 227, 363, 448, 447, 1435,
- /* 990 */ 1568, 1310, 134, 134, 134, 134, 133, 133, 132, 132,
- /* 1000 */ 132, 131, 128, 451, 1568, 576, 1147, 487, 1204, 1205,
- /* 1010 */ 1204, 442, 134, 134, 134, 134, 133, 133, 132, 132,
- /* 1020 */ 132, 131, 128, 451, 386, 576, 485, 576, 19, 19,
- /* 1030 */ 1204, 1205, 1204, 1345, 1236, 970, 1236, 574, 47, 936,
- /* 1040 */ 936, 473, 413, 431, 1552, 573, 1125, 562, 19, 19,
- /* 1050 */ 19, 19, 49, 336, 850, 851, 852, 111, 1368, 315,
- /* 1060 */ 429, 576, 413, 433, 341, 306, 137, 138, 91, 115,
+ /* 900 */ 1659, 399, 1169, 307, 893, 307, 515, 576, 413, 214,
+ /* 910 */ 498, 944, 1024, 540, 903, 1169, 943, 392, 1169, 1028,
+ /* 920 */ 1028, 1030, 406, 298, 1204, 50, 1149, 1658, 413, 1658,
+ /* 930 */ 145, 145, 137, 138, 91, 293, 1228, 1228, 1063, 1066,
+ /* 940 */ 1053, 1053, 135, 135, 136, 136, 136, 136, 1188, 1147,
+ /* 950 */ 514, 1568, 137, 138, 91, 1505, 1228, 1228, 1063, 1066,
+ /* 960 */ 1053, 1053, 135, 135, 136, 136, 136, 136, 434, 323,
+ /* 970 */ 435, 539, 111, 1506, 274, 291, 372, 517, 367, 516,
+ /* 980 */ 262, 1204, 1205, 1204, 1574, 481, 363, 576, 7, 1569,
+ /* 990 */ 1568, 377, 134, 134, 134, 134, 133, 133, 132, 132,
+ /* 1000 */ 132, 131, 128, 451, 1568, 576, 1147, 576, 232, 576,
+ /* 1010 */ 19, 19, 134, 134, 134, 134, 133, 133, 132, 132,
+ /* 1020 */ 132, 131, 128, 451, 1169, 433, 576, 1207, 19, 19,
+ /* 1030 */ 19, 19, 19, 19, 1627, 576, 911, 1169, 47, 120,
+ /* 1040 */ 1169, 117, 413, 306, 498, 438, 1125, 206, 336, 19,
+ /* 1050 */ 19, 1435, 49, 449, 449, 449, 1368, 315, 81, 81,
+ /* 1060 */ 576, 304, 413, 1570, 207, 377, 137, 138, 91, 115,
/* 1070 */ 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, 136, 136,
- /* 1080 */ 136, 136, 576, 1309, 82, 82, 137, 138, 91, 529,
+ /* 1080 */ 136, 136, 576, 82, 82, 1207, 137, 138, 91, 1340,
/* 1090 */ 1228, 1228, 1063, 1066, 1053, 1053, 135, 135, 136, 136,
- /* 1100 */ 136, 136, 1569, 222, 377, 19, 19, 305, 1126, 1169,
- /* 1110 */ 398, 1148, 22, 22, 498, 333, 1569, 335, 377, 576,
- /* 1120 */ 438, 445, 1169, 1127, 486, 1169, 134, 134, 134, 134,
+ /* 1100 */ 136, 136, 1569, 386, 377, 82, 82, 463, 1126, 1552,
+ /* 1110 */ 333, 463, 335, 131, 128, 451, 1569, 161, 377, 16,
+ /* 1120 */ 317, 387, 428, 1127, 448, 447, 134, 134, 134, 134,
/* 1130 */ 133, 133, 132, 132, 132, 131, 128, 451, 1128, 576,
- /* 1140 */ 902, 576, 145, 145, 6, 576, 134, 134, 134, 134,
- /* 1150 */ 133, 133, 132, 132, 132, 131, 128, 451, 214, 1336,
- /* 1160 */ 922, 576, 19, 19, 19, 19, 1282, 419, 19, 19,
- /* 1170 */ 923, 412, 515, 141, 576, 1169, 413, 206, 465, 207,
- /* 1180 */ 903, 215, 1575, 552, 147, 147, 7, 227, 1169, 411,
- /* 1190 */ 1250, 1169, 120, 307, 117, 307, 413, 66, 66, 334,
+ /* 1140 */ 1105, 10, 445, 267, 576, 1554, 134, 134, 134, 134,
+ /* 1150 */ 133, 133, 132, 132, 132, 131, 128, 451, 532, 576,
+ /* 1160 */ 922, 576, 19, 19, 576, 1573, 576, 147, 147, 7,
+ /* 1170 */ 923, 1236, 498, 1236, 576, 487, 413, 552, 285, 1224,
+ /* 1180 */ 969, 215, 82, 82, 66, 66, 1435, 67, 67, 21,
+ /* 1190 */ 21, 1110, 1110, 495, 334, 297, 413, 53, 53, 297,
/* 1200 */ 137, 138, 91, 119, 1228, 1228, 1063, 1066, 1053, 1053,
- /* 1210 */ 135, 135, 136, 136, 136, 136, 413, 285, 209, 969,
- /* 1220 */ 137, 138, 91, 471, 1228, 1228, 1063, 1066, 1053, 1053,
- /* 1230 */ 135, 135, 136, 136, 136, 136, 435, 10, 1450, 267,
- /* 1240 */ 137, 126, 91, 1435, 1228, 1228, 1063, 1066, 1053, 1053,
- /* 1250 */ 135, 135, 136, 136, 136, 136, 1435, 1435, 410, 409,
+ /* 1210 */ 135, 135, 136, 136, 136, 136, 413, 1336, 1311, 446,
+ /* 1220 */ 137, 138, 91, 227, 1228, 1228, 1063, 1066, 1053, 1053,
+ /* 1230 */ 135, 135, 136, 136, 136, 136, 574, 1224, 936, 936,
+ /* 1240 */ 137, 126, 91, 141, 1228, 1228, 1063, 1066, 1053, 1053,
+ /* 1250 */ 135, 135, 136, 136, 136, 136, 533, 429, 472, 346,
/* 1260 */ 134, 134, 134, 134, 133, 133, 132, 132, 132, 131,
- /* 1270 */ 128, 451, 576, 969, 576, 1224, 498, 373, 1595, 1554,
+ /* 1270 */ 128, 451, 576, 457, 233, 343, 1435, 403, 498, 1550,
/* 1280 */ 134, 134, 134, 134, 133, 133, 132, 132, 132, 131,
- /* 1290 */ 128, 451, 532, 457, 576, 82, 82, 82, 82, 111,
+ /* 1290 */ 128, 451, 576, 324, 576, 82, 82, 487, 576, 969,
/* 1300 */ 134, 134, 134, 134, 133, 133, 132, 132, 132, 131,
- /* 1310 */ 128, 451, 109, 233, 430, 1576, 546, 67, 67, 7,
- /* 1320 */ 413, 351, 550, 1550, 260, 259, 258, 494, 443, 569,
- /* 1330 */ 419, 983, 446, 1224, 450, 545, 1207, 576, 969, 984,
- /* 1340 */ 413, 475, 1449, 1574, 1180, 138, 91, 7, 1228, 1228,
+ /* 1310 */ 128, 451, 288, 288, 546, 68, 68, 54, 54, 553,
+ /* 1320 */ 413, 69, 69, 351, 6, 573, 944, 562, 410, 409,
+ /* 1330 */ 1435, 943, 450, 545, 260, 259, 258, 576, 158, 576,
+ /* 1340 */ 413, 222, 1180, 479, 969, 138, 91, 430, 1228, 1228,
/* 1350 */ 1063, 1066, 1053, 1053, 135, 135, 136, 136, 136, 136,
- /* 1360 */ 21, 21, 267, 576, 300, 1126, 91, 233, 1228, 1228,
+ /* 1360 */ 70, 70, 71, 71, 576, 1126, 91, 576, 1228, 1228,
/* 1370 */ 1063, 1066, 1053, 1053, 135, 135, 136, 136, 136, 136,
- /* 1380 */ 1127, 373, 1595, 161, 1573, 16, 53, 53, 7, 108,
- /* 1390 */ 533, 38, 969, 125, 1207, 1128, 1180, 576, 1224, 123,
- /* 1400 */ 568, 893, 4, 324, 134, 134, 134, 134, 133, 133,
- /* 1410 */ 132, 132, 132, 131, 128, 451, 571, 564, 534, 576,
- /* 1420 */ 68, 68, 576, 39, 134, 134, 134, 134, 133, 133,
- /* 1430 */ 132, 132, 132, 131, 128, 451, 576, 160, 1571, 1223,
- /* 1440 */ 452, 576, 54, 54, 576, 69, 69, 576, 1366, 576,
- /* 1450 */ 420, 184, 565, 463, 297, 576, 1224, 463, 297, 70,
- /* 1460 */ 70, 576, 44, 474, 71, 71, 576, 72, 72, 576,
- /* 1470 */ 73, 73, 55, 55, 411, 874, 242, 576, 56, 56,
- /* 1480 */ 576, 1040, 576, 478, 57, 57, 576, 121, 121, 59,
- /* 1490 */ 59, 23, 60, 60, 411, 122, 319, 452, 577, 452,
- /* 1500 */ 74, 74, 1028, 75, 75, 76, 76, 411, 290, 20,
- /* 1510 */ 20, 108, 287, 231, 553, 123, 568, 325, 4, 320,
- /* 1520 */ 567, 97, 218, 944, 1144, 328, 400, 576, 943, 576,
- /* 1530 */ 1380, 424, 571, 874, 1028, 1028, 1030, 1031, 35, 293,
- /* 1540 */ 534, 576, 1104, 576, 1104, 9, 576, 342, 576, 111,
- /* 1550 */ 77, 77, 143, 143, 576, 205, 452, 222, 1379, 889,
- /* 1560 */ 576, 901, 900, 1188, 144, 144, 78, 78, 565, 62,
- /* 1570 */ 62, 79, 79, 323, 1021, 576, 266, 63, 63, 908,
- /* 1580 */ 909, 1589, 542, 80, 80, 576, 371, 541, 123, 568,
- /* 1590 */ 480, 4, 266, 482, 244, 266, 370, 1040, 64, 64,
- /* 1600 */ 576, 466, 576, 121, 121, 571, 1557, 576, 170, 170,
- /* 1610 */ 576, 122, 576, 452, 577, 452, 576, 889, 1028, 576,
- /* 1620 */ 165, 576, 111, 171, 171, 87, 87, 337, 1616, 452,
- /* 1630 */ 65, 65, 1530, 83, 83, 146, 146, 986, 987, 84,
- /* 1640 */ 84, 565, 168, 168, 148, 148, 1092, 347, 1032, 111,
- /* 1650 */ 1028, 1028, 1030, 1031, 35, 542, 1103, 576, 1103, 576,
- /* 1660 */ 543, 123, 568, 504, 4, 263, 576, 361, 1529, 111,
- /* 1670 */ 1040, 1088, 576, 263, 576, 490, 121, 121, 571, 1188,
- /* 1680 */ 142, 142, 169, 169, 122, 576, 452, 577, 452, 162,
- /* 1690 */ 162, 1028, 576, 563, 576, 152, 152, 151, 151, 348,
- /* 1700 */ 1376, 974, 452, 266, 1092, 942, 1032, 125, 149, 149,
- /* 1710 */ 939, 576, 125, 576, 565, 150, 150, 86, 86, 872,
- /* 1720 */ 352, 159, 576, 1028, 1028, 1030, 1031, 35, 542, 941,
- /* 1730 */ 576, 125, 355, 541, 88, 88, 85, 85, 357, 359,
- /* 1740 */ 1324, 1308, 366, 1040, 376, 52, 52, 499, 1389, 121,
- /* 1750 */ 121, 1434, 1188, 58, 58, 1362, 1374, 122, 1439, 452,
- /* 1760 */ 577, 452, 1289, 167, 1028, 1280, 280, 1268, 1267, 1269,
- /* 1770 */ 1609, 1359, 312, 313, 12, 314, 397, 1421, 224, 1416,
- /* 1780 */ 295, 237, 1409, 339, 340, 1426, 301, 345, 484, 228,
- /* 1790 */ 1371, 1307, 1372, 1370, 1425, 404, 1028, 1028, 1030, 1031,
- /* 1800 */ 35, 1601, 1192, 454, 509, 369, 292, 1502, 210, 1501,
- /* 1810 */ 1369, 396, 396, 395, 277, 393, 211, 566, 859, 1612,
- /* 1820 */ 1244, 123, 568, 391, 4, 1188, 223, 270, 1549, 1547,
- /* 1830 */ 1241, 239, 186, 327, 422, 96, 195, 220, 571, 235,
- /* 1840 */ 180, 326, 188, 468, 190, 1507, 191, 192, 92, 193,
- /* 1850 */ 469, 95, 1422, 13, 502, 247, 1430, 109, 199, 402,
- /* 1860 */ 476, 405, 452, 1496, 1428, 1427, 14, 491, 251, 102,
- /* 1870 */ 497, 1518, 241, 281, 565, 253, 203, 354, 500, 254,
- /* 1880 */ 175, 1270, 407, 43, 350, 518, 1327, 436, 255, 1326,
- /* 1890 */ 1325, 1318, 104, 893, 1626, 229, 408, 440, 1625, 441,
- /* 1900 */ 240, 310, 1296, 1040, 311, 1317, 527, 1594, 1297, 121,
- /* 1910 */ 121, 368, 1295, 1624, 268, 269, 1580, 122, 1579, 452,
- /* 1920 */ 577, 452, 374, 444, 1028, 1394, 1393, 140, 553, 90,
- /* 1930 */ 568, 11, 4, 1483, 383, 414, 385, 110, 116, 216,
- /* 1940 */ 320, 567, 1350, 555, 42, 318, 571, 537, 1349, 389,
- /* 1950 */ 390, 579, 1198, 276, 279, 278, 1028, 1028, 1030, 1031,
- /* 1960 */ 35, 580, 415, 1265, 458, 1260, 416, 185, 1534, 172,
- /* 1970 */ 452, 1535, 173, 156, 308, 846, 1533, 1532, 453, 217,
- /* 1980 */ 225, 89, 565, 174, 322, 1188, 226, 236, 1102, 154,
- /* 1990 */ 1100, 330, 176, 187, 1223, 189, 925, 338, 243, 1116,
- /* 2000 */ 246, 194, 177, 178, 425, 427, 98, 99, 196, 100,
- /* 2010 */ 101, 1040, 179, 1119, 248, 1115, 249, 121, 121, 24,
- /* 2020 */ 163, 250, 349, 1108, 266, 122, 1238, 452, 577, 452,
- /* 2030 */ 1192, 454, 1028, 200, 292, 496, 252, 201, 861, 396,
+ /* 1380 */ 1127, 166, 850, 851, 852, 1282, 419, 72, 72, 108,
+ /* 1390 */ 73, 73, 1310, 358, 1180, 1128, 576, 305, 576, 123,
+ /* 1400 */ 568, 494, 4, 488, 134, 134, 134, 134, 133, 133,
+ /* 1410 */ 132, 132, 132, 131, 128, 451, 571, 564, 534, 55,
+ /* 1420 */ 55, 56, 56, 576, 134, 134, 134, 134, 133, 133,
+ /* 1430 */ 132, 132, 132, 131, 128, 451, 576, 1104, 233, 1104,
+ /* 1440 */ 452, 1602, 582, 2, 1259, 576, 57, 57, 576, 321,
+ /* 1450 */ 576, 155, 565, 1435, 485, 353, 576, 356, 1341, 59,
+ /* 1460 */ 59, 576, 44, 969, 569, 419, 576, 238, 60, 60,
+ /* 1470 */ 261, 74, 74, 75, 75, 287, 231, 576, 1366, 76,
+ /* 1480 */ 76, 1040, 420, 184, 20, 20, 576, 121, 121, 77,
+ /* 1490 */ 77, 97, 218, 288, 288, 122, 125, 452, 577, 452,
+ /* 1500 */ 143, 143, 1028, 576, 520, 576, 573, 576, 562, 144,
+ /* 1510 */ 144, 474, 227, 1244, 478, 123, 568, 576, 4, 320,
+ /* 1520 */ 567, 245, 411, 576, 443, 411, 78, 78, 62, 62,
+ /* 1530 */ 79, 79, 571, 319, 1028, 1028, 1030, 1031, 35, 418,
+ /* 1540 */ 63, 63, 576, 290, 411, 9, 80, 80, 1144, 576,
+ /* 1550 */ 400, 576, 486, 455, 576, 1223, 452, 576, 325, 342,
+ /* 1560 */ 576, 111, 576, 1188, 242, 64, 64, 473, 565, 576,
+ /* 1570 */ 23, 576, 170, 170, 171, 171, 576, 87, 87, 328,
+ /* 1580 */ 65, 65, 542, 83, 83, 146, 146, 541, 123, 568,
+ /* 1590 */ 341, 4, 84, 84, 168, 168, 576, 1040, 576, 148,
+ /* 1600 */ 148, 576, 1380, 121, 121, 571, 1021, 576, 266, 576,
+ /* 1610 */ 424, 122, 576, 452, 577, 452, 576, 553, 1028, 142,
+ /* 1620 */ 142, 169, 169, 576, 162, 162, 528, 889, 371, 452,
+ /* 1630 */ 152, 152, 151, 151, 1379, 149, 149, 109, 370, 150,
+ /* 1640 */ 150, 565, 576, 480, 576, 266, 86, 86, 576, 1092,
+ /* 1650 */ 1028, 1028, 1030, 1031, 35, 542, 482, 576, 266, 466,
+ /* 1660 */ 543, 123, 568, 1616, 4, 88, 88, 85, 85, 475,
+ /* 1670 */ 1040, 52, 52, 222, 901, 900, 121, 121, 571, 1188,
+ /* 1680 */ 58, 58, 244, 1032, 122, 889, 452, 577, 452, 908,
+ /* 1690 */ 909, 1028, 300, 347, 504, 111, 263, 361, 165, 111,
+ /* 1700 */ 111, 1088, 452, 263, 974, 1153, 266, 1092, 986, 987,
+ /* 1710 */ 942, 939, 125, 125, 565, 1103, 872, 1103, 159, 941,
+ /* 1720 */ 1309, 125, 1557, 1028, 1028, 1030, 1031, 35, 542, 337,
+ /* 1730 */ 1530, 205, 1529, 541, 499, 1589, 490, 348, 1376, 352,
+ /* 1740 */ 355, 1032, 357, 1040, 359, 1324, 1308, 366, 563, 121,
+ /* 1750 */ 121, 376, 1188, 1389, 1434, 1362, 280, 122, 1374, 452,
+ /* 1760 */ 577, 452, 167, 1439, 1028, 1289, 1280, 1268, 1267, 1269,
+ /* 1770 */ 1609, 1359, 312, 313, 314, 397, 12, 237, 224, 1421,
+ /* 1780 */ 295, 1416, 1409, 1426, 339, 484, 340, 509, 1371, 1612,
+ /* 1790 */ 1372, 1425, 1244, 404, 301, 228, 1028, 1028, 1030, 1031,
+ /* 1800 */ 35, 1601, 1192, 454, 345, 1307, 292, 369, 1502, 1501,
+ /* 1810 */ 270, 396, 396, 395, 277, 393, 1370, 1369, 859, 1549,
+ /* 1820 */ 186, 123, 568, 235, 4, 1188, 391, 210, 211, 223,
+ /* 1830 */ 1547, 239, 1241, 327, 422, 96, 220, 195, 571, 180,
+ /* 1840 */ 188, 326, 468, 469, 190, 191, 502, 192, 193, 566,
+ /* 1850 */ 247, 109, 1430, 491, 199, 251, 102, 281, 402, 476,
+ /* 1860 */ 405, 1496, 452, 497, 253, 1422, 13, 1428, 14, 1427,
+ /* 1870 */ 203, 1507, 241, 500, 565, 354, 407, 92, 95, 1270,
+ /* 1880 */ 175, 254, 518, 43, 1327, 255, 1326, 1325, 436, 1518,
+ /* 1890 */ 350, 1318, 104, 229, 893, 1626, 440, 441, 1625, 408,
+ /* 1900 */ 240, 1296, 268, 1040, 310, 269, 1297, 527, 444, 121,
+ /* 1910 */ 121, 368, 1295, 1594, 1624, 311, 1394, 122, 1317, 452,
+ /* 1920 */ 577, 452, 374, 1580, 1028, 1393, 140, 553, 11, 90,
+ /* 1930 */ 568, 385, 4, 116, 318, 414, 1579, 110, 1483, 537,
+ /* 1940 */ 320, 567, 1350, 555, 42, 579, 571, 1349, 1198, 383,
+ /* 1950 */ 276, 390, 216, 389, 278, 279, 1028, 1028, 1030, 1031,
+ /* 1960 */ 35, 172, 580, 1265, 458, 1260, 415, 416, 185, 156,
+ /* 1970 */ 452, 1534, 1535, 173, 1533, 1532, 89, 308, 225, 226,
+ /* 1980 */ 846, 174, 565, 453, 217, 1188, 322, 236, 1102, 154,
+ /* 1990 */ 1100, 330, 187, 176, 1223, 243, 189, 925, 338, 246,
+ /* 2000 */ 1116, 194, 177, 425, 178, 427, 98, 196, 99, 100,
+ /* 2010 */ 101, 1040, 179, 1119, 1115, 248, 249, 121, 121, 163,
+ /* 2020 */ 24, 250, 349, 1238, 496, 122, 1108, 452, 577, 452,
+ /* 2030 */ 1192, 454, 1028, 266, 292, 200, 252, 201, 861, 396,
/* 2040 */ 396, 395, 277, 393, 15, 501, 859, 370, 292, 256,
/* 2050 */ 202, 554, 505, 396, 396, 395, 277, 393, 103, 239,
/* 2060 */ 859, 327, 25, 26, 1028, 1028, 1030, 1031, 35, 326,
/* 2070 */ 362, 510, 891, 239, 365, 327, 513, 904, 105, 309,
/* 2080 */ 164, 181, 27, 326, 106, 521, 107, 1185, 1069, 1155,
- /* 2090 */ 17, 1154, 284, 1188, 286, 978, 265, 204, 125, 1171,
- /* 2100 */ 241, 230, 972, 1175, 28, 1160, 29, 1179, 175, 1173,
- /* 2110 */ 30, 43, 31, 1178, 241, 32, 41, 549, 8, 33,
- /* 2120 */ 208, 111, 175, 1083, 1070, 43, 113, 1068, 240, 114,
- /* 2130 */ 1072, 34, 1073, 561, 1124, 118, 271, 36, 18, 1194,
- /* 2140 */ 1033, 873, 240, 935, 124, 37, 272, 273, 1617, 572,
- /* 2150 */ 183, 153, 394, 1193, 1256, 1256, 1256, 1256, 1256, 1256,
+ /* 2090 */ 17, 1154, 230, 1188, 284, 286, 265, 204, 125, 1171,
+ /* 2100 */ 241, 28, 978, 972, 29, 41, 1175, 1179, 175, 1173,
+ /* 2110 */ 30, 43, 31, 8, 241, 1178, 32, 1160, 208, 549,
+ /* 2120 */ 33, 111, 175, 1083, 1070, 43, 1068, 1072, 240, 113,
+ /* 2130 */ 114, 34, 561, 118, 1124, 271, 1073, 36, 18, 572,
+ /* 2140 */ 1033, 873, 240, 124, 37, 935, 272, 273, 1617, 183,
+ /* 2150 */ 153, 394, 1194, 1193, 1256, 1256, 1256, 1256, 1256, 1256,
/* 2160 */ 1256, 1256, 1256, 414, 1256, 1256, 1256, 1256, 320, 567,
/* 2170 */ 1256, 1256, 1256, 1256, 1256, 1256, 1256, 414, 1256, 1256,
/* 2180 */ 1256, 1256, 320, 567, 1256, 1256, 1256, 1256, 1256, 1256,
@@ -174636,257 +176032,257 @@ static const YYACTIONTYPE yy_action[] = {
/* 2200 */ 1256, 1256, 1256, 1256, 1256, 1256, 458,
};
static const YYCODETYPE yy_lookahead[] = {
- /* 0 */ 276, 277, 278, 240, 241, 224, 194, 226, 194, 240,
- /* 10 */ 241, 194, 216, 220, 194, 234, 253, 194, 255, 19,
- /* 20 */ 224, 297, 253, 194, 255, 205, 212, 213, 205, 217,
- /* 30 */ 218, 31, 205, 194, 217, 218, 194, 217, 218, 39,
- /* 40 */ 217, 218, 312, 43, 44, 45, 316, 47, 48, 49,
+ /* 0 */ 277, 278, 279, 241, 242, 225, 195, 227, 195, 241,
+ /* 10 */ 242, 195, 217, 221, 195, 235, 254, 195, 256, 19,
+ /* 20 */ 225, 298, 254, 195, 256, 206, 213, 214, 206, 218,
+ /* 30 */ 219, 31, 206, 195, 218, 219, 195, 218, 219, 39,
+ /* 40 */ 218, 219, 313, 43, 44, 45, 317, 47, 48, 49,
/* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 19,
- /* 60 */ 240, 241, 194, 240, 241, 194, 254, 240, 241, 276,
- /* 70 */ 277, 278, 233, 253, 254, 255, 253, 254, 255, 217,
- /* 80 */ 253, 239, 255, 43, 44, 45, 263, 47, 48, 49,
- /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 270,
- /* 100 */ 286, 22, 23, 103, 104, 105, 106, 107, 108, 109,
- /* 110 */ 110, 111, 112, 113, 114, 82, 47, 48, 49, 50,
- /* 120 */ 186, 187, 188, 189, 190, 191, 189, 87, 191, 89,
- /* 130 */ 196, 19, 198, 196, 317, 198, 319, 25, 194, 205,
- /* 140 */ 298, 270, 205, 103, 104, 105, 106, 107, 108, 109,
- /* 150 */ 110, 111, 112, 113, 114, 43, 44, 45, 11, 47,
+ /* 60 */ 241, 242, 195, 241, 242, 195, 255, 241, 242, 277,
+ /* 70 */ 278, 279, 234, 254, 255, 256, 254, 255, 256, 218,
+ /* 80 */ 254, 240, 256, 43, 44, 45, 264, 47, 48, 49,
+ /* 90 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 271,
+ /* 100 */ 287, 22, 23, 103, 104, 105, 106, 107, 108, 109,
+ /* 110 */ 110, 111, 112, 113, 114, 114, 47, 48, 49, 50,
+ /* 120 */ 187, 188, 189, 190, 191, 192, 190, 87, 192, 89,
+ /* 130 */ 197, 19, 199, 197, 318, 199, 320, 25, 195, 206,
+ /* 140 */ 299, 271, 206, 103, 104, 105, 106, 107, 108, 109,
+ /* 150 */ 110, 111, 112, 113, 114, 43, 44, 45, 195, 47,
/* 160 */ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
- /* 170 */ 58, 60, 139, 140, 240, 241, 214, 240, 241, 311,
- /* 180 */ 312, 102, 70, 239, 316, 194, 19, 253, 77, 255,
- /* 190 */ 253, 122, 255, 55, 56, 57, 58, 59, 207, 88,
- /* 200 */ 194, 90, 268, 194, 93, 268, 107, 108, 109, 110,
- /* 210 */ 111, 112, 113, 114, 47, 103, 104, 105, 106, 107,
- /* 220 */ 108, 109, 110, 111, 112, 113, 114, 114, 117, 118,
- /* 230 */ 119, 276, 277, 278, 300, 19, 194, 300, 276, 277,
- /* 240 */ 278, 103, 104, 105, 106, 107, 108, 109, 110, 111,
- /* 250 */ 112, 113, 114, 55, 56, 57, 58, 146, 194, 43,
- /* 260 */ 44, 45, 47, 47, 48, 49, 50, 51, 52, 53,
- /* 270 */ 54, 55, 56, 57, 58, 82, 129, 130, 60, 129,
- /* 280 */ 130, 217, 218, 116, 68, 25, 103, 104, 105, 106,
- /* 290 */ 107, 108, 109, 110, 111, 112, 113, 114, 23, 132,
- /* 300 */ 294, 103, 104, 105, 106, 107, 108, 109, 110, 111,
- /* 310 */ 112, 113, 114, 217, 121, 306, 194, 308, 26, 103,
+ /* 170 */ 58, 60, 21, 195, 241, 242, 215, 241, 242, 312,
+ /* 180 */ 313, 102, 70, 205, 317, 207, 242, 254, 77, 256,
+ /* 190 */ 254, 122, 256, 55, 56, 57, 58, 59, 254, 88,
+ /* 200 */ 256, 90, 269, 240, 93, 269, 107, 108, 109, 110,
+ /* 210 */ 111, 112, 113, 114, 271, 103, 104, 105, 106, 107,
+ /* 220 */ 108, 109, 110, 111, 112, 113, 114, 313, 117, 118,
+ /* 230 */ 119, 317, 81, 195, 301, 19, 195, 301, 277, 278,
+ /* 240 */ 279, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+ /* 250 */ 112, 113, 114, 55, 56, 57, 58, 146, 195, 43,
+ /* 260 */ 44, 45, 74, 47, 48, 49, 50, 51, 52, 53,
+ /* 270 */ 54, 55, 56, 57, 58, 124, 195, 60, 109, 110,
+ /* 280 */ 111, 112, 113, 114, 68, 195, 103, 104, 105, 106,
+ /* 290 */ 107, 108, 109, 110, 111, 112, 113, 114, 208, 218,
+ /* 300 */ 219, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+ /* 310 */ 112, 113, 114, 162, 233, 24, 128, 129, 130, 103,
/* 320 */ 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
- /* 330 */ 114, 116, 139, 140, 217, 117, 118, 119, 120, 19,
- /* 340 */ 194, 123, 124, 125, 24, 109, 110, 111, 112, 113,
- /* 350 */ 114, 133, 60, 311, 312, 250, 194, 252, 316, 19,
- /* 360 */ 194, 166, 167, 43, 44, 45, 205, 47, 48, 49,
- /* 370 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 217,
- /* 380 */ 218, 317, 318, 43, 44, 45, 264, 47, 48, 49,
- /* 390 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 139,
- /* 400 */ 140, 240, 241, 139, 140, 188, 189, 190, 191, 117,
- /* 410 */ 118, 119, 231, 196, 253, 198, 255, 194, 194, 258,
- /* 420 */ 259, 146, 205, 103, 104, 105, 106, 107, 108, 109,
- /* 430 */ 110, 111, 112, 113, 114, 109, 212, 213, 236, 237,
- /* 440 */ 217, 218, 194, 103, 104, 105, 106, 107, 108, 109,
- /* 450 */ 110, 111, 112, 113, 114, 194, 120, 240, 241, 123,
- /* 460 */ 124, 125, 136, 194, 19, 74, 60, 141, 23, 133,
- /* 470 */ 253, 194, 255, 82, 194, 309, 310, 254, 29, 317,
- /* 480 */ 318, 194, 33, 22, 199, 268, 263, 239, 43, 44,
+ /* 330 */ 114, 195, 195, 215, 117, 118, 119, 120, 195, 19,
+ /* 340 */ 123, 124, 125, 207, 24, 74, 246, 60, 310, 311,
+ /* 350 */ 133, 60, 311, 82, 22, 218, 219, 257, 195, 19,
+ /* 360 */ 73, 218, 219, 43, 44, 45, 206, 47, 48, 49,
+ /* 370 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 22,
+ /* 380 */ 23, 218, 219, 43, 44, 45, 54, 47, 48, 49,
+ /* 390 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 128,
+ /* 400 */ 82, 241, 242, 195, 117, 118, 119, 289, 60, 118,
+ /* 410 */ 139, 140, 294, 195, 254, 195, 256, 195, 255, 259,
+ /* 420 */ 260, 73, 22, 103, 104, 105, 106, 107, 108, 109,
+ /* 430 */ 110, 111, 112, 113, 114, 206, 218, 219, 218, 219,
+ /* 440 */ 218, 219, 234, 103, 104, 105, 106, 107, 108, 109,
+ /* 450 */ 110, 111, 112, 113, 114, 318, 319, 139, 140, 102,
+ /* 460 */ 60, 318, 319, 221, 19, 117, 118, 119, 23, 195,
+ /* 470 */ 241, 242, 313, 255, 206, 255, 317, 255, 206, 129,
+ /* 480 */ 130, 206, 264, 254, 264, 256, 264, 195, 43, 44,
/* 490 */ 45, 151, 47, 48, 49, 50, 51, 52, 53, 54,
- /* 500 */ 55, 56, 57, 58, 217, 218, 194, 60, 19, 146,
- /* 510 */ 286, 242, 23, 107, 108, 66, 204, 300, 206, 128,
- /* 520 */ 73, 60, 116, 117, 118, 119, 265, 121, 165, 60,
- /* 530 */ 139, 140, 43, 44, 45, 129, 47, 48, 49, 50,
+ /* 500 */ 55, 56, 57, 58, 246, 213, 214, 19, 19, 241,
+ /* 510 */ 242, 195, 23, 241, 242, 257, 241, 242, 118, 277,
+ /* 520 */ 278, 279, 254, 29, 256, 60, 254, 33, 256, 254,
+ /* 530 */ 206, 256, 43, 44, 45, 218, 47, 48, 49, 50,
/* 540 */ 51, 52, 53, 54, 55, 56, 57, 58, 103, 104,
/* 550 */ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114,
- /* 560 */ 154, 194, 156, 194, 117, 118, 119, 280, 283, 205,
- /* 570 */ 101, 220, 287, 19, 20, 306, 22, 308, 117, 118,
- /* 580 */ 119, 211, 212, 213, 217, 218, 117, 118, 236, 237,
- /* 590 */ 36, 122, 103, 104, 105, 106, 107, 108, 109, 110,
- /* 600 */ 111, 112, 113, 114, 240, 241, 194, 60, 194, 194,
- /* 610 */ 194, 194, 19, 194, 60, 194, 23, 253, 206, 255,
- /* 620 */ 73, 254, 19, 154, 155, 156, 72, 276, 277, 278,
- /* 630 */ 263, 217, 218, 217, 218, 271, 43, 44, 45, 271,
+ /* 560 */ 66, 19, 218, 60, 120, 241, 242, 123, 124, 125,
+ /* 570 */ 60, 232, 77, 19, 20, 26, 22, 133, 254, 287,
+ /* 580 */ 256, 265, 117, 118, 119, 90, 312, 313, 93, 47,
+ /* 590 */ 36, 317, 103, 104, 105, 106, 107, 108, 109, 110,
+ /* 600 */ 111, 112, 113, 114, 116, 117, 277, 278, 279, 60,
+ /* 610 */ 107, 108, 19, 276, 60, 31, 23, 152, 195, 116,
+ /* 620 */ 117, 118, 119, 39, 121, 276, 72, 117, 118, 119,
+ /* 630 */ 166, 167, 129, 145, 237, 238, 43, 44, 45, 276,
/* 640 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 650 */ 57, 58, 183, 194, 285, 101, 19, 214, 60, 242,
- /* 660 */ 23, 107, 108, 109, 117, 118, 119, 16, 254, 115,
- /* 670 */ 254, 117, 118, 119, 194, 60, 122, 263, 205, 264,
- /* 680 */ 43, 44, 45, 264, 47, 48, 49, 50, 51, 52,
+ /* 650 */ 57, 58, 315, 316, 144, 101, 19, 154, 116, 156,
+ /* 660 */ 23, 107, 108, 109, 315, 316, 117, 118, 119, 115,
+ /* 670 */ 60, 117, 118, 119, 132, 200, 122, 60, 315, 316,
+ /* 680 */ 43, 44, 45, 272, 47, 48, 49, 50, 51, 52,
/* 690 */ 53, 54, 55, 56, 57, 58, 103, 104, 105, 106,
/* 700 */ 107, 108, 109, 110, 111, 112, 113, 114, 154, 155,
- /* 710 */ 156, 157, 158, 240, 241, 117, 118, 119, 101, 205,
- /* 720 */ 117, 19, 20, 306, 22, 308, 253, 74, 255, 78,
- /* 730 */ 205, 80, 117, 118, 119, 118, 293, 183, 36, 122,
+ /* 710 */ 156, 157, 158, 212, 213, 214, 22, 195, 101, 22,
+ /* 720 */ 60, 19, 20, 60, 22, 139, 140, 117, 118, 119,
+ /* 730 */ 22, 251, 195, 253, 117, 118, 195, 183, 36, 122,
/* 740 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
- /* 750 */ 113, 114, 194, 294, 240, 241, 211, 212, 213, 144,
- /* 760 */ 19, 23, 60, 25, 23, 240, 241, 253, 245, 255,
- /* 770 */ 15, 154, 155, 156, 72, 217, 218, 194, 253, 256,
- /* 780 */ 255, 128, 129, 130, 43, 44, 45, 22, 47, 48,
+ /* 750 */ 113, 114, 195, 195, 60, 218, 219, 60, 195, 284,
+ /* 760 */ 19, 25, 60, 288, 23, 237, 238, 22, 60, 109,
+ /* 770 */ 233, 154, 155, 156, 72, 218, 219, 117, 118, 119,
+ /* 780 */ 117, 118, 119, 116, 43, 44, 45, 265, 47, 48,
/* 790 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
- /* 800 */ 183, 19, 77, 101, 19, 128, 129, 130, 23, 107,
- /* 810 */ 108, 60, 254, 88, 21, 90, 61, 115, 93, 117,
- /* 820 */ 118, 119, 239, 22, 122, 60, 194, 205, 43, 44,
- /* 830 */ 45, 205, 47, 48, 49, 50, 51, 52, 53, 54,
+ /* 800 */ 183, 243, 25, 101, 19, 60, 265, 144, 23, 107,
+ /* 810 */ 108, 117, 118, 119, 117, 118, 119, 115, 151, 117,
+ /* 820 */ 118, 119, 82, 195, 122, 117, 118, 119, 43, 44,
+ /* 830 */ 45, 195, 47, 48, 49, 50, 51, 52, 53, 54,
/* 840 */ 55, 56, 57, 58, 103, 104, 105, 106, 107, 108,
/* 850 */ 109, 110, 111, 112, 113, 114, 154, 155, 156, 157,
- /* 860 */ 158, 60, 240, 241, 60, 116, 240, 241, 117, 118,
- /* 870 */ 119, 146, 194, 19, 81, 253, 275, 255, 24, 253,
- /* 880 */ 98, 255, 117, 118, 119, 183, 22, 194, 103, 104,
+ /* 860 */ 158, 121, 117, 118, 119, 307, 101, 309, 195, 22,
+ /* 870 */ 23, 195, 25, 19, 35, 139, 140, 195, 24, 139,
+ /* 880 */ 140, 208, 195, 118, 109, 183, 22, 122, 103, 104,
/* 890 */ 105, 106, 107, 108, 109, 110, 111, 112, 113, 114,
- /* 900 */ 151, 194, 270, 152, 22, 23, 194, 25, 19, 202,
- /* 910 */ 217, 218, 23, 109, 207, 314, 315, 124, 117, 118,
- /* 920 */ 119, 117, 118, 119, 60, 232, 22, 23, 19, 25,
- /* 930 */ 303, 304, 43, 44, 45, 22, 47, 48, 49, 50,
- /* 940 */ 51, 52, 53, 54, 55, 56, 57, 58, 270, 227,
- /* 950 */ 96, 60, 43, 44, 45, 162, 47, 48, 49, 50,
- /* 960 */ 51, 52, 53, 54, 55, 56, 57, 58, 114, 112,
- /* 970 */ 113, 114, 194, 60, 120, 121, 122, 123, 124, 125,
- /* 980 */ 126, 117, 118, 119, 102, 25, 132, 107, 108, 194,
- /* 990 */ 194, 227, 103, 104, 105, 106, 107, 108, 109, 110,
- /* 1000 */ 111, 112, 113, 114, 194, 194, 102, 194, 117, 118,
- /* 1010 */ 119, 233, 103, 104, 105, 106, 107, 108, 109, 110,
- /* 1020 */ 111, 112, 113, 114, 194, 194, 19, 194, 217, 218,
- /* 1030 */ 117, 118, 119, 241, 154, 144, 156, 135, 242, 137,
- /* 1040 */ 138, 130, 19, 232, 194, 253, 23, 255, 217, 218,
- /* 1050 */ 217, 218, 242, 16, 7, 8, 9, 25, 261, 262,
- /* 1060 */ 265, 194, 19, 232, 153, 232, 43, 44, 45, 160,
+ /* 900 */ 304, 305, 77, 230, 127, 232, 67, 195, 19, 195,
+ /* 910 */ 195, 136, 23, 88, 75, 90, 141, 203, 93, 154,
+ /* 920 */ 155, 156, 208, 295, 60, 243, 22, 23, 19, 25,
+ /* 930 */ 218, 219, 43, 44, 45, 100, 47, 48, 49, 50,
+ /* 940 */ 51, 52, 53, 54, 55, 56, 57, 58, 183, 102,
+ /* 950 */ 96, 195, 43, 44, 45, 240, 47, 48, 49, 50,
+ /* 960 */ 51, 52, 53, 54, 55, 56, 57, 58, 114, 134,
+ /* 970 */ 131, 146, 25, 286, 120, 121, 122, 123, 124, 125,
+ /* 980 */ 126, 117, 118, 119, 313, 195, 132, 195, 317, 307,
+ /* 990 */ 195, 309, 103, 104, 105, 106, 107, 108, 109, 110,
+ /* 1000 */ 111, 112, 113, 114, 195, 195, 102, 195, 195, 195,
+ /* 1010 */ 218, 219, 103, 104, 105, 106, 107, 108, 109, 110,
+ /* 1020 */ 111, 112, 113, 114, 77, 233, 195, 60, 218, 219,
+ /* 1030 */ 218, 219, 218, 219, 23, 195, 25, 90, 243, 159,
+ /* 1040 */ 93, 161, 19, 233, 195, 233, 23, 233, 16, 218,
+ /* 1050 */ 219, 195, 243, 212, 213, 214, 262, 263, 218, 219,
+ /* 1060 */ 195, 271, 19, 307, 233, 309, 43, 44, 45, 160,
/* 1070 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 1080 */ 57, 58, 194, 227, 217, 218, 43, 44, 45, 194,
+ /* 1080 */ 57, 58, 195, 218, 219, 118, 43, 44, 45, 240,
/* 1090 */ 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
- /* 1100 */ 57, 58, 306, 143, 308, 217, 218, 294, 12, 77,
- /* 1110 */ 22, 23, 217, 218, 194, 78, 306, 80, 308, 194,
- /* 1120 */ 232, 254, 90, 27, 117, 93, 103, 104, 105, 106,
- /* 1130 */ 107, 108, 109, 110, 111, 112, 113, 114, 42, 194,
- /* 1140 */ 35, 194, 217, 218, 214, 194, 103, 104, 105, 106,
- /* 1150 */ 107, 108, 109, 110, 111, 112, 113, 114, 194, 239,
- /* 1160 */ 64, 194, 217, 218, 217, 218, 209, 210, 217, 218,
- /* 1170 */ 74, 207, 67, 22, 194, 77, 19, 232, 245, 232,
- /* 1180 */ 75, 24, 312, 232, 217, 218, 316, 25, 90, 256,
- /* 1190 */ 102, 93, 159, 229, 161, 231, 19, 217, 218, 162,
+ /* 1100 */ 57, 58, 307, 195, 309, 218, 219, 263, 12, 195,
+ /* 1110 */ 78, 267, 80, 112, 113, 114, 307, 22, 309, 24,
+ /* 1120 */ 255, 281, 266, 27, 107, 108, 103, 104, 105, 106,
+ /* 1130 */ 107, 108, 109, 110, 111, 112, 113, 114, 42, 195,
+ /* 1140 */ 11, 22, 255, 24, 195, 195, 103, 104, 105, 106,
+ /* 1150 */ 107, 108, 109, 110, 111, 112, 113, 114, 19, 195,
+ /* 1160 */ 64, 195, 218, 219, 195, 313, 195, 218, 219, 317,
+ /* 1170 */ 74, 154, 195, 156, 195, 195, 19, 233, 23, 60,
+ /* 1180 */ 25, 24, 218, 219, 218, 219, 195, 218, 219, 218,
+ /* 1190 */ 219, 128, 129, 130, 162, 263, 19, 218, 219, 267,
/* 1200 */ 43, 44, 45, 160, 47, 48, 49, 50, 51, 52,
- /* 1210 */ 53, 54, 55, 56, 57, 58, 19, 23, 288, 25,
- /* 1220 */ 43, 44, 45, 293, 47, 48, 49, 50, 51, 52,
- /* 1230 */ 53, 54, 55, 56, 57, 58, 131, 22, 275, 24,
- /* 1240 */ 43, 44, 45, 194, 47, 48, 49, 50, 51, 52,
- /* 1250 */ 53, 54, 55, 56, 57, 58, 194, 194, 107, 108,
+ /* 1210 */ 53, 54, 55, 56, 57, 58, 19, 240, 228, 255,
+ /* 1220 */ 43, 44, 45, 25, 47, 48, 49, 50, 51, 52,
+ /* 1230 */ 53, 54, 55, 56, 57, 58, 135, 118, 137, 138,
+ /* 1240 */ 43, 44, 45, 22, 47, 48, 49, 50, 51, 52,
+ /* 1250 */ 53, 54, 55, 56, 57, 58, 117, 266, 129, 130,
/* 1260 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
- /* 1270 */ 113, 114, 194, 25, 194, 60, 194, 314, 315, 194,
+ /* 1270 */ 113, 114, 195, 195, 119, 295, 195, 206, 195, 195,
/* 1280 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
- /* 1290 */ 113, 114, 19, 194, 194, 217, 218, 217, 218, 25,
+ /* 1290 */ 113, 114, 195, 195, 195, 218, 219, 195, 195, 144,
/* 1300 */ 103, 104, 105, 106, 107, 108, 109, 110, 111, 112,
- /* 1310 */ 113, 114, 150, 119, 265, 312, 67, 217, 218, 316,
- /* 1320 */ 19, 239, 194, 194, 128, 129, 130, 265, 265, 209,
- /* 1330 */ 210, 31, 254, 118, 254, 86, 60, 194, 144, 39,
- /* 1340 */ 19, 130, 275, 312, 95, 44, 45, 316, 47, 48,
+ /* 1310 */ 113, 114, 241, 242, 67, 218, 219, 218, 219, 146,
+ /* 1320 */ 19, 218, 219, 240, 215, 254, 136, 256, 107, 108,
+ /* 1330 */ 195, 141, 255, 86, 128, 129, 130, 195, 165, 195,
+ /* 1340 */ 19, 143, 95, 272, 25, 44, 45, 266, 47, 48,
/* 1350 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
- /* 1360 */ 217, 218, 24, 194, 153, 12, 45, 119, 47, 48,
+ /* 1360 */ 218, 219, 218, 219, 195, 12, 45, 195, 47, 48,
/* 1370 */ 49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
- /* 1380 */ 27, 314, 315, 22, 312, 24, 217, 218, 316, 116,
- /* 1390 */ 117, 22, 144, 25, 118, 42, 147, 194, 60, 19,
- /* 1400 */ 20, 127, 22, 194, 103, 104, 105, 106, 107, 108,
- /* 1410 */ 109, 110, 111, 112, 113, 114, 36, 64, 145, 194,
- /* 1420 */ 217, 218, 194, 54, 103, 104, 105, 106, 107, 108,
- /* 1430 */ 109, 110, 111, 112, 113, 114, 194, 22, 310, 25,
- /* 1440 */ 60, 194, 217, 218, 194, 217, 218, 194, 260, 194,
- /* 1450 */ 301, 302, 72, 262, 262, 194, 118, 266, 266, 217,
- /* 1460 */ 218, 194, 82, 245, 217, 218, 194, 217, 218, 194,
- /* 1470 */ 217, 218, 217, 218, 256, 60, 24, 194, 217, 218,
- /* 1480 */ 194, 101, 194, 245, 217, 218, 194, 107, 108, 217,
- /* 1490 */ 218, 22, 217, 218, 256, 115, 245, 117, 118, 119,
- /* 1500 */ 217, 218, 122, 217, 218, 217, 218, 256, 22, 217,
- /* 1510 */ 218, 116, 258, 259, 146, 19, 20, 194, 22, 139,
- /* 1520 */ 140, 150, 151, 136, 23, 194, 25, 194, 141, 194,
- /* 1530 */ 194, 62, 36, 118, 154, 155, 156, 157, 158, 100,
- /* 1540 */ 145, 194, 154, 194, 156, 49, 194, 23, 194, 25,
- /* 1550 */ 217, 218, 217, 218, 194, 257, 60, 143, 194, 60,
- /* 1560 */ 194, 121, 122, 183, 217, 218, 217, 218, 72, 217,
- /* 1570 */ 218, 217, 218, 134, 23, 194, 25, 217, 218, 7,
- /* 1580 */ 8, 321, 86, 217, 218, 194, 122, 91, 19, 20,
- /* 1590 */ 23, 22, 25, 23, 142, 25, 132, 101, 217, 218,
- /* 1600 */ 194, 194, 194, 107, 108, 36, 194, 194, 217, 218,
- /* 1610 */ 194, 115, 194, 117, 118, 119, 194, 118, 122, 194,
- /* 1620 */ 23, 194, 25, 217, 218, 217, 218, 194, 142, 60,
- /* 1630 */ 217, 218, 194, 217, 218, 217, 218, 84, 85, 217,
- /* 1640 */ 218, 72, 217, 218, 217, 218, 60, 23, 60, 25,
- /* 1650 */ 154, 155, 156, 157, 158, 86, 154, 194, 156, 194,
- /* 1660 */ 91, 19, 20, 23, 22, 25, 194, 23, 194, 25,
- /* 1670 */ 101, 23, 194, 25, 194, 194, 107, 108, 36, 183,
- /* 1680 */ 217, 218, 217, 218, 115, 194, 117, 118, 119, 217,
- /* 1690 */ 218, 122, 194, 237, 194, 217, 218, 217, 218, 194,
- /* 1700 */ 194, 23, 60, 25, 118, 23, 118, 25, 217, 218,
- /* 1710 */ 23, 194, 25, 194, 72, 217, 218, 217, 218, 23,
- /* 1720 */ 194, 25, 194, 154, 155, 156, 157, 158, 86, 23,
- /* 1730 */ 194, 25, 194, 91, 217, 218, 217, 218, 194, 194,
- /* 1740 */ 194, 194, 194, 101, 194, 217, 218, 290, 194, 107,
- /* 1750 */ 108, 194, 183, 217, 218, 194, 194, 115, 194, 117,
- /* 1760 */ 118, 119, 194, 243, 122, 194, 289, 194, 194, 194,
- /* 1770 */ 194, 257, 257, 257, 244, 257, 192, 273, 215, 269,
- /* 1780 */ 246, 299, 269, 295, 247, 273, 247, 246, 295, 230,
- /* 1790 */ 261, 226, 261, 261, 273, 273, 154, 155, 156, 157,
- /* 1800 */ 158, 0, 1, 2, 221, 220, 5, 220, 250, 220,
- /* 1810 */ 261, 10, 11, 12, 13, 14, 250, 282, 17, 197,
- /* 1820 */ 61, 19, 20, 246, 22, 183, 244, 142, 201, 201,
- /* 1830 */ 38, 30, 299, 32, 201, 152, 22, 151, 36, 299,
- /* 1840 */ 43, 40, 235, 18, 238, 285, 238, 238, 296, 238,
- /* 1850 */ 201, 296, 274, 272, 18, 200, 235, 150, 235, 247,
- /* 1860 */ 247, 247, 60, 247, 274, 274, 272, 201, 200, 159,
- /* 1870 */ 63, 292, 71, 201, 72, 200, 22, 201, 222, 200,
- /* 1880 */ 79, 201, 222, 82, 291, 116, 219, 65, 200, 219,
- /* 1890 */ 219, 228, 22, 127, 225, 166, 222, 24, 225, 114,
- /* 1900 */ 99, 284, 221, 101, 284, 228, 307, 315, 219, 107,
- /* 1910 */ 108, 219, 219, 219, 201, 92, 320, 115, 320, 117,
- /* 1920 */ 118, 119, 222, 83, 122, 267, 267, 149, 146, 19,
- /* 1930 */ 20, 22, 22, 279, 250, 134, 201, 148, 159, 249,
- /* 1940 */ 139, 140, 251, 141, 25, 281, 36, 147, 251, 248,
- /* 1950 */ 247, 203, 13, 195, 6, 195, 154, 155, 156, 157,
- /* 1960 */ 158, 193, 305, 193, 163, 193, 305, 302, 214, 208,
- /* 1970 */ 60, 214, 208, 223, 223, 4, 214, 214, 3, 22,
- /* 1980 */ 215, 214, 72, 208, 164, 183, 215, 15, 23, 16,
- /* 1990 */ 23, 140, 131, 152, 25, 143, 20, 16, 24, 1,
- /* 2000 */ 145, 143, 131, 131, 62, 37, 54, 54, 152, 54,
- /* 2010 */ 54, 101, 131, 117, 34, 1, 142, 107, 108, 22,
- /* 2020 */ 5, 116, 162, 69, 25, 115, 76, 117, 118, 119,
- /* 2030 */ 1, 2, 122, 69, 5, 41, 142, 116, 20, 10,
+ /* 1380 */ 27, 23, 7, 8, 9, 210, 211, 218, 219, 116,
+ /* 1390 */ 218, 219, 228, 16, 147, 42, 195, 295, 195, 19,
+ /* 1400 */ 20, 266, 22, 294, 103, 104, 105, 106, 107, 108,
+ /* 1410 */ 109, 110, 111, 112, 113, 114, 36, 64, 145, 218,
+ /* 1420 */ 219, 218, 219, 195, 103, 104, 105, 106, 107, 108,
+ /* 1430 */ 109, 110, 111, 112, 113, 114, 195, 154, 119, 156,
+ /* 1440 */ 60, 189, 190, 191, 192, 195, 218, 219, 195, 197,
+ /* 1450 */ 195, 199, 72, 195, 19, 78, 195, 80, 206, 218,
+ /* 1460 */ 219, 195, 82, 144, 210, 211, 195, 15, 218, 219,
+ /* 1470 */ 47, 218, 219, 218, 219, 259, 260, 195, 261, 218,
+ /* 1480 */ 219, 101, 302, 303, 218, 219, 195, 107, 108, 218,
+ /* 1490 */ 219, 150, 151, 241, 242, 115, 25, 117, 118, 119,
+ /* 1500 */ 218, 219, 122, 195, 146, 195, 254, 195, 256, 218,
+ /* 1510 */ 219, 246, 25, 61, 246, 19, 20, 195, 22, 139,
+ /* 1520 */ 140, 269, 257, 195, 266, 257, 218, 219, 218, 219,
+ /* 1530 */ 218, 219, 36, 246, 154, 155, 156, 157, 158, 116,
+ /* 1540 */ 218, 219, 195, 22, 257, 49, 218, 219, 23, 195,
+ /* 1550 */ 25, 195, 117, 301, 195, 25, 60, 195, 195, 23,
+ /* 1560 */ 195, 25, 195, 183, 24, 218, 219, 130, 72, 195,
+ /* 1570 */ 22, 195, 218, 219, 218, 219, 195, 218, 219, 195,
+ /* 1580 */ 218, 219, 86, 218, 219, 218, 219, 91, 19, 20,
+ /* 1590 */ 153, 22, 218, 219, 218, 219, 195, 101, 195, 218,
+ /* 1600 */ 219, 195, 195, 107, 108, 36, 23, 195, 25, 195,
+ /* 1610 */ 62, 115, 195, 117, 118, 119, 195, 146, 122, 218,
+ /* 1620 */ 219, 218, 219, 195, 218, 219, 19, 60, 122, 60,
+ /* 1630 */ 218, 219, 218, 219, 195, 218, 219, 150, 132, 218,
+ /* 1640 */ 219, 72, 195, 23, 195, 25, 218, 219, 195, 60,
+ /* 1650 */ 154, 155, 156, 157, 158, 86, 23, 195, 25, 195,
+ /* 1660 */ 91, 19, 20, 142, 22, 218, 219, 218, 219, 130,
+ /* 1670 */ 101, 218, 219, 143, 121, 122, 107, 108, 36, 183,
+ /* 1680 */ 218, 219, 142, 60, 115, 118, 117, 118, 119, 7,
+ /* 1690 */ 8, 122, 153, 23, 23, 25, 25, 23, 23, 25,
+ /* 1700 */ 25, 23, 60, 25, 23, 98, 25, 118, 84, 85,
+ /* 1710 */ 23, 23, 25, 25, 72, 154, 23, 156, 25, 23,
+ /* 1720 */ 228, 25, 195, 154, 155, 156, 157, 158, 86, 195,
+ /* 1730 */ 195, 258, 195, 91, 291, 322, 195, 195, 195, 195,
+ /* 1740 */ 195, 118, 195, 101, 195, 195, 195, 195, 238, 107,
+ /* 1750 */ 108, 195, 183, 195, 195, 195, 290, 115, 195, 117,
+ /* 1760 */ 118, 119, 244, 195, 122, 195, 195, 195, 195, 195,
+ /* 1770 */ 195, 258, 258, 258, 258, 193, 245, 300, 216, 274,
+ /* 1780 */ 247, 270, 270, 274, 296, 296, 248, 222, 262, 198,
+ /* 1790 */ 262, 274, 61, 274, 248, 231, 154, 155, 156, 157,
+ /* 1800 */ 158, 0, 1, 2, 247, 227, 5, 221, 221, 221,
+ /* 1810 */ 142, 10, 11, 12, 13, 14, 262, 262, 17, 202,
+ /* 1820 */ 300, 19, 20, 300, 22, 183, 247, 251, 251, 245,
+ /* 1830 */ 202, 30, 38, 32, 202, 152, 151, 22, 36, 43,
+ /* 1840 */ 236, 40, 18, 202, 239, 239, 18, 239, 239, 283,
+ /* 1850 */ 201, 150, 236, 202, 236, 201, 159, 202, 248, 248,
+ /* 1860 */ 248, 248, 60, 63, 201, 275, 273, 275, 273, 275,
+ /* 1870 */ 22, 286, 71, 223, 72, 202, 223, 297, 297, 202,
+ /* 1880 */ 79, 201, 116, 82, 220, 201, 220, 220, 65, 293,
+ /* 1890 */ 292, 229, 22, 166, 127, 226, 24, 114, 226, 223,
+ /* 1900 */ 99, 222, 202, 101, 285, 92, 220, 308, 83, 107,
+ /* 1910 */ 108, 220, 220, 316, 220, 285, 268, 115, 229, 117,
+ /* 1920 */ 118, 119, 223, 321, 122, 268, 149, 146, 22, 19,
+ /* 1930 */ 20, 202, 22, 159, 282, 134, 321, 148, 280, 147,
+ /* 1940 */ 139, 140, 252, 141, 25, 204, 36, 252, 13, 251,
+ /* 1950 */ 196, 248, 250, 249, 196, 6, 154, 155, 156, 157,
+ /* 1960 */ 158, 209, 194, 194, 163, 194, 306, 306, 303, 224,
+ /* 1970 */ 60, 215, 215, 209, 215, 215, 215, 224, 216, 216,
+ /* 1980 */ 4, 209, 72, 3, 22, 183, 164, 15, 23, 16,
+ /* 1990 */ 23, 140, 152, 131, 25, 24, 143, 20, 16, 145,
+ /* 2000 */ 1, 143, 131, 62, 131, 37, 54, 152, 54, 54,
+ /* 2010 */ 54, 101, 131, 117, 1, 34, 142, 107, 108, 5,
+ /* 2020 */ 22, 116, 162, 76, 41, 115, 69, 117, 118, 119,
+ /* 2030 */ 1, 2, 122, 25, 5, 69, 142, 116, 20, 10,
/* 2040 */ 11, 12, 13, 14, 24, 19, 17, 132, 5, 126,
/* 2050 */ 22, 141, 68, 10, 11, 12, 13, 14, 22, 30,
/* 2060 */ 17, 32, 22, 22, 154, 155, 156, 157, 158, 40,
/* 2070 */ 23, 68, 60, 30, 24, 32, 97, 28, 22, 68,
/* 2080 */ 23, 37, 34, 40, 150, 22, 25, 23, 23, 23,
- /* 2090 */ 22, 98, 23, 183, 23, 117, 34, 22, 25, 89,
- /* 2100 */ 71, 142, 144, 76, 34, 23, 34, 76, 79, 87,
- /* 2110 */ 34, 82, 34, 94, 71, 34, 22, 24, 44, 34,
- /* 2120 */ 25, 25, 79, 23, 23, 82, 143, 23, 99, 143,
- /* 2130 */ 23, 22, 11, 25, 23, 25, 22, 22, 22, 1,
- /* 2140 */ 23, 23, 99, 136, 22, 22, 142, 142, 142, 25,
- /* 2150 */ 25, 23, 15, 1, 322, 322, 322, 322, 322, 322,
- /* 2160 */ 322, 322, 322, 134, 322, 322, 322, 322, 139, 140,
- /* 2170 */ 322, 322, 322, 322, 322, 322, 322, 134, 322, 322,
- /* 2180 */ 322, 322, 139, 140, 322, 322, 322, 322, 322, 322,
- /* 2190 */ 322, 322, 163, 322, 322, 322, 322, 322, 322, 322,
- /* 2200 */ 322, 322, 322, 322, 322, 322, 163, 322, 322, 322,
- /* 2210 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
- /* 2220 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
- /* 2230 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
- /* 2240 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
- /* 2250 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
- /* 2260 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
- /* 2270 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
- /* 2280 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
- /* 2290 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
- /* 2300 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
- /* 2310 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
- /* 2320 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
- /* 2330 */ 322, 322, 322, 322, 322, 322, 322, 322, 322, 322,
- /* 2340 */ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186,
- /* 2350 */ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186,
- /* 2360 */ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186,
- /* 2370 */ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186,
- /* 2380 */ 186, 186, 186, 186, 186, 186, 186, 186, 186, 186,
- /* 2390 */ 186, 186, 186,
+ /* 2090 */ 22, 98, 142, 183, 23, 23, 34, 22, 25, 89,
+ /* 2100 */ 71, 34, 117, 144, 34, 22, 76, 76, 79, 87,
+ /* 2110 */ 34, 82, 34, 44, 71, 94, 34, 23, 25, 24,
+ /* 2120 */ 34, 25, 79, 23, 23, 82, 23, 23, 99, 143,
+ /* 2130 */ 143, 22, 25, 25, 23, 22, 11, 22, 22, 25,
+ /* 2140 */ 23, 23, 99, 22, 22, 136, 142, 142, 142, 25,
+ /* 2150 */ 23, 15, 1, 1, 323, 323, 323, 323, 323, 323,
+ /* 2160 */ 323, 323, 323, 134, 323, 323, 323, 323, 139, 140,
+ /* 2170 */ 323, 323, 323, 323, 323, 323, 323, 134, 323, 323,
+ /* 2180 */ 323, 323, 139, 140, 323, 323, 323, 323, 323, 323,
+ /* 2190 */ 323, 323, 163, 323, 323, 323, 323, 323, 323, 323,
+ /* 2200 */ 323, 323, 323, 323, 323, 323, 163, 323, 323, 323,
+ /* 2210 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323,
+ /* 2220 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323,
+ /* 2230 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323,
+ /* 2240 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323,
+ /* 2250 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323,
+ /* 2260 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323,
+ /* 2270 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323,
+ /* 2280 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323,
+ /* 2290 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323,
+ /* 2300 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323,
+ /* 2310 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323,
+ /* 2320 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323,
+ /* 2330 */ 323, 323, 323, 323, 323, 323, 323, 323, 323, 323,
+ /* 2340 */ 323, 187, 187, 187, 187, 187, 187, 187, 187, 187,
+ /* 2350 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187,
+ /* 2360 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187,
+ /* 2370 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187,
+ /* 2380 */ 187, 187, 187, 187, 187, 187, 187, 187, 187, 187,
+ /* 2390 */ 187, 187, 187, 187,
};
#define YY_SHIFT_COUNT (582)
#define YY_SHIFT_MIN (0)
#define YY_SHIFT_MAX (2152)
static const unsigned short int yy_shift_ofst[] = {
- /* 0 */ 2029, 1801, 2043, 1380, 1380, 33, 391, 1496, 1569, 1642,
- /* 10 */ 702, 702, 702, 193, 33, 33, 33, 33, 33, 0,
+ /* 0 */ 2029, 1801, 2043, 1380, 1380, 318, 271, 1496, 1569, 1642,
+ /* 10 */ 702, 702, 702, 740, 318, 318, 318, 318, 318, 0,
/* 20 */ 0, 216, 1177, 702, 702, 702, 702, 702, 702, 702,
- /* 30 */ 702, 702, 702, 702, 702, 702, 702, 702, 406, 406,
- /* 40 */ 111, 111, 218, 447, 547, 598, 598, 260, 260, 260,
- /* 50 */ 260, 40, 112, 320, 340, 445, 489, 593, 637, 741,
+ /* 30 */ 702, 702, 702, 702, 702, 702, 702, 702, 503, 503,
+ /* 40 */ 111, 111, 217, 287, 348, 610, 610, 736, 736, 736,
+ /* 50 */ 736, 40, 112, 320, 340, 445, 489, 593, 637, 741,
/* 60 */ 785, 889, 909, 1023, 1043, 1157, 1177, 1177, 1177, 1177,
/* 70 */ 1177, 1177, 1177, 1177, 1177, 1177, 1177, 1177, 1177, 1177,
/* 80 */ 1177, 1177, 1177, 1177, 1197, 1177, 1301, 1321, 1321, 554,
@@ -174896,97 +176292,97 @@ static const unsigned short int yy_shift_ofst[] = {
/* 120 */ 702, 702, 702, 702, 702, 702, 702, 702, 702, 702,
/* 130 */ 702, 702, 702, 702, 702, 702, 702, 702, 702, 702,
/* 140 */ 702, 702, 138, 198, 198, 198, 198, 198, 198, 198,
- /* 150 */ 183, 99, 236, 292, 598, 793, 167, 598, 598, 880,
- /* 160 */ 880, 598, 857, 150, 195, 195, 195, 264, 113, 113,
- /* 170 */ 2207, 2207, 854, 854, 854, 751, 765, 765, 765, 765,
- /* 180 */ 1096, 1096, 725, 292, 882, 904, 598, 598, 598, 598,
- /* 190 */ 598, 598, 598, 598, 598, 598, 598, 598, 598, 598,
- /* 200 */ 598, 598, 598, 598, 598, 1273, 1032, 1032, 598, 147,
- /* 210 */ 1098, 1098, 603, 603, 1276, 1276, 363, 2207, 2207, 2207,
- /* 220 */ 2207, 2207, 2207, 2207, 469, 617, 617, 801, 336, 461,
- /* 230 */ 804, 864, 615, 891, 913, 598, 598, 598, 598, 598,
- /* 240 */ 598, 598, 598, 598, 598, 653, 598, 598, 598, 598,
- /* 250 */ 598, 598, 598, 598, 598, 598, 598, 598, 1105, 1105,
- /* 260 */ 1105, 598, 598, 598, 1194, 598, 598, 598, 1215, 1249,
- /* 270 */ 598, 1353, 598, 598, 598, 598, 598, 598, 598, 598,
- /* 280 */ 677, 449, 902, 1338, 1338, 1338, 1338, 1248, 902, 902,
- /* 290 */ 326, 1151, 1047, 755, 749, 1371, 960, 1371, 1007, 1162,
- /* 300 */ 749, 749, 1162, 749, 960, 1007, 1274, 738, 215, 1300,
- /* 310 */ 1300, 1300, 1395, 1395, 1395, 1395, 1368, 1368, 1033, 1414,
- /* 320 */ 1387, 1361, 1759, 1759, 1685, 1685, 1792, 1792, 1685, 1683,
- /* 330 */ 1686, 1814, 1797, 1825, 1825, 1825, 1825, 1685, 1836, 1707,
- /* 340 */ 1686, 1686, 1707, 1814, 1797, 1707, 1797, 1707, 1685, 1836,
- /* 350 */ 1710, 1807, 1685, 1836, 1854, 1685, 1836, 1685, 1836, 1854,
- /* 360 */ 1769, 1769, 1769, 1822, 1870, 1870, 1854, 1769, 1766, 1769,
- /* 370 */ 1822, 1769, 1769, 1729, 1873, 1785, 1785, 1854, 1685, 1823,
- /* 380 */ 1823, 1840, 1840, 1778, 1782, 1909, 1685, 1779, 1778, 1789,
- /* 390 */ 1800, 1707, 1919, 1939, 1939, 1948, 1948, 1948, 2207, 2207,
+ /* 150 */ 183, 99, 169, 549, 610, 151, 542, 610, 610, 1017,
+ /* 160 */ 1017, 610, 1001, 350, 464, 464, 464, 586, 1, 1,
+ /* 170 */ 2207, 2207, 854, 854, 854, 465, 694, 694, 694, 694,
+ /* 180 */ 1096, 1096, 825, 549, 847, 904, 610, 610, 610, 610,
+ /* 190 */ 610, 610, 610, 610, 610, 610, 610, 610, 610, 610,
+ /* 200 */ 610, 610, 610, 610, 610, 488, 947, 947, 610, 1129,
+ /* 210 */ 495, 495, 1139, 1139, 967, 967, 1173, 2207, 2207, 2207,
+ /* 220 */ 2207, 2207, 2207, 2207, 617, 765, 765, 697, 444, 708,
+ /* 230 */ 660, 745, 510, 663, 864, 610, 610, 610, 610, 610,
+ /* 240 */ 610, 610, 610, 610, 610, 188, 610, 610, 610, 610,
+ /* 250 */ 610, 610, 610, 610, 610, 610, 610, 610, 839, 839,
+ /* 260 */ 839, 610, 610, 610, 1155, 610, 610, 610, 1119, 1247,
+ /* 270 */ 610, 1353, 610, 610, 610, 610, 610, 610, 610, 610,
+ /* 280 */ 1063, 494, 1101, 291, 291, 291, 291, 1319, 1101, 1101,
+ /* 290 */ 775, 1221, 1375, 1452, 667, 1341, 1198, 1341, 1435, 1487,
+ /* 300 */ 667, 667, 1487, 667, 1198, 1435, 777, 1011, 1423, 584,
+ /* 310 */ 584, 584, 1273, 1273, 1273, 1273, 1471, 1471, 880, 1530,
+ /* 320 */ 1190, 1095, 1731, 1731, 1668, 1668, 1794, 1794, 1668, 1683,
+ /* 330 */ 1685, 1815, 1796, 1824, 1824, 1824, 1824, 1668, 1828, 1701,
+ /* 340 */ 1685, 1685, 1701, 1815, 1796, 1701, 1796, 1701, 1668, 1828,
+ /* 350 */ 1697, 1800, 1668, 1828, 1848, 1668, 1828, 1668, 1828, 1848,
+ /* 360 */ 1766, 1766, 1766, 1823, 1870, 1870, 1848, 1766, 1767, 1766,
+ /* 370 */ 1823, 1766, 1766, 1727, 1872, 1783, 1783, 1848, 1668, 1813,
+ /* 380 */ 1813, 1825, 1825, 1777, 1781, 1906, 1668, 1774, 1777, 1789,
+ /* 390 */ 1792, 1701, 1919, 1935, 1935, 1949, 1949, 1949, 2207, 2207,
/* 400 */ 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207, 2207,
- /* 410 */ 2207, 2207, 2207, 69, 1037, 79, 1088, 651, 1196, 1415,
- /* 420 */ 1501, 1439, 1369, 1452, 911, 1211, 1524, 1469, 1551, 1567,
- /* 430 */ 1570, 1624, 1640, 1644, 1499, 1440, 1572, 1464, 1597, 275,
- /* 440 */ 782, 1586, 1648, 1678, 1553, 1682, 1687, 1388, 1502, 1696,
- /* 450 */ 1706, 1588, 1486, 1971, 1975, 1957, 1820, 1972, 1973, 1965,
- /* 460 */ 1967, 1851, 1841, 1861, 1969, 1969, 1974, 1852, 1976, 1855,
- /* 470 */ 1981, 1998, 1858, 1871, 1969, 1872, 1942, 1968, 1969, 1856,
- /* 480 */ 1952, 1953, 1955, 1956, 1881, 1896, 1980, 1874, 2014, 2015,
- /* 490 */ 1997, 1905, 1860, 1954, 1999, 1964, 1950, 1994, 1894, 1921,
+ /* 410 */ 2207, 2207, 2207, 69, 1032, 79, 357, 1377, 1206, 400,
+ /* 420 */ 1525, 835, 332, 1540, 1437, 1539, 1536, 1548, 1583, 1620,
+ /* 430 */ 1633, 1670, 1671, 1674, 1567, 1553, 1682, 1506, 1675, 1358,
+ /* 440 */ 1607, 1589, 1678, 1681, 1624, 1687, 1688, 1283, 1561, 1693,
+ /* 450 */ 1696, 1623, 1521, 1976, 1980, 1962, 1822, 1972, 1973, 1965,
+ /* 460 */ 1967, 1851, 1840, 1862, 1969, 1969, 1971, 1853, 1977, 1854,
+ /* 470 */ 1982, 1999, 1858, 1871, 1969, 1873, 1941, 1968, 1969, 1855,
+ /* 480 */ 1952, 1954, 1955, 1956, 1881, 1896, 1981, 1874, 2013, 2014,
+ /* 490 */ 1998, 1905, 1860, 1957, 2008, 1966, 1947, 1983, 1894, 1921,
/* 500 */ 2020, 2018, 2026, 1915, 1923, 2028, 1984, 2036, 2040, 2047,
/* 510 */ 2041, 2003, 2012, 2050, 1979, 2049, 2056, 2011, 2044, 2057,
- /* 520 */ 2048, 1934, 2063, 2064, 2065, 2061, 2066, 2068, 1993, 1959,
- /* 530 */ 2069, 2071, 1978, 2062, 2075, 1958, 2073, 2070, 2072, 2076,
- /* 540 */ 2078, 2010, 2027, 2022, 2074, 2031, 2019, 2081, 2082, 2094,
- /* 550 */ 2093, 2095, 2096, 2085, 1983, 1986, 2100, 2073, 2101, 2104,
- /* 560 */ 2107, 2109, 2108, 2110, 2111, 2114, 2121, 2115, 2116, 2117,
- /* 570 */ 2118, 2122, 2123, 2124, 2007, 2004, 2005, 2006, 2125, 2128,
- /* 580 */ 2137, 2138, 2152,
+ /* 520 */ 2048, 1934, 2063, 2064, 2065, 2061, 2066, 2068, 1993, 1950,
+ /* 530 */ 2071, 2072, 1985, 2062, 2075, 1959, 2073, 2067, 2070, 2076,
+ /* 540 */ 2078, 2010, 2030, 2022, 2069, 2031, 2021, 2082, 2094, 2083,
+ /* 550 */ 2095, 2093, 2096, 2086, 1986, 1987, 2100, 2073, 2101, 2103,
+ /* 560 */ 2104, 2109, 2107, 2108, 2111, 2113, 2125, 2115, 2116, 2117,
+ /* 570 */ 2118, 2121, 2122, 2114, 2009, 2004, 2005, 2006, 2124, 2127,
+ /* 580 */ 2136, 2151, 2152,
};
#define YY_REDUCE_COUNT (412)
-#define YY_REDUCE_MIN (-276)
-#define YY_REDUCE_MAX (1775)
+#define YY_REDUCE_MIN (-277)
+#define YY_REDUCE_MAX (1772)
static const short yy_reduce_ofst[] = {
- /* 0 */ -66, 217, -63, -177, -180, 161, 364, 64, -183, 162,
- /* 10 */ 223, 367, 414, -173, 473, 514, 525, 622, 626, -207,
- /* 20 */ 351, -276, -38, 693, 811, 831, 833, 888, -188, 945,
- /* 30 */ 947, 416, 558, 951, 867, 287, 1078, 1080, -186, 224,
- /* 40 */ -132, 42, 964, 269, 417, 796, 810, -237, -231, -237,
- /* 50 */ -231, -45, -45, -45, -45, -45, -45, -45, -45, -45,
- /* 60 */ -45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
- /* 70 */ -45, -45, -45, -45, -45, -45, -45, -45, -45, -45,
- /* 80 */ -45, -45, -45, -45, -45, -45, -45, -45, -45, 895,
- /* 90 */ 925, 967, 980, 1100, 1143, 1169, 1203, 1225, 1228, 1242,
- /* 100 */ 1247, 1250, 1253, 1255, 1261, 1267, 1272, 1275, 1283, 1286,
- /* 110 */ 1288, 1292, 1333, 1335, 1347, 1349, 1352, 1354, 1360, 1366,
- /* 120 */ 1381, 1391, 1406, 1408, 1413, 1416, 1418, 1422, 1425, 1427,
- /* 130 */ 1463, 1465, 1472, 1478, 1480, 1491, 1498, 1500, 1517, 1519,
- /* 140 */ 1528, 1536, -45, -45, -45, -45, -45, -45, -45, -45,
- /* 150 */ -45, -45, -45, 312, -158, 285, -219, 9, 166, 370,
- /* 160 */ 545, 707, -45, 930, 601, 963, 1067, 792, -45, -45,
- /* 170 */ -45, -45, -204, -204, -204, 369, -171, -129, 632, 678,
- /* 180 */ 202, 352, -270, 412, 627, 627, -9, 122, 415, 419,
- /* 190 */ -56, 248, 583, 920, 6, 261, 459, 795, 1049, 813,
- /* 200 */ 1062, 1082, -161, 778, 1063, 797, 870, 1003, 1128, 443,
- /* 210 */ 1031, 1072, 1191, 1192, 957, 1120, 105, 1149, 523, 933,
- /* 220 */ 1218, 1238, 1254, 1251, -138, 96, 117, 146, 181, 277,
- /* 230 */ 280, 421, 480, 712, 830, 850, 1085, 1099, 1129, 1209,
- /* 240 */ 1323, 1331, 1336, 1364, 1407, 368, 1412, 1433, 1438, 1474,
- /* 250 */ 1481, 1505, 1506, 1526, 1538, 1544, 1545, 1546, 722, 764,
- /* 260 */ 856, 1547, 1548, 1550, 1188, 1554, 1557, 1561, 1298, 1260,
- /* 270 */ 1562, 1456, 1564, 280, 1568, 1571, 1573, 1574, 1575, 1576,
- /* 280 */ 1457, 1477, 1520, 1514, 1515, 1516, 1518, 1188, 1520, 1520,
- /* 290 */ 1530, 1563, 1584, 1482, 1504, 1510, 1534, 1513, 1488, 1537,
- /* 300 */ 1512, 1521, 1539, 1522, 1541, 1493, 1583, 1559, 1565, 1585,
- /* 310 */ 1587, 1589, 1529, 1531, 1532, 1549, 1558, 1566, 1535, 1577,
- /* 320 */ 1582, 1622, 1533, 1540, 1627, 1628, 1552, 1555, 1633, 1560,
- /* 330 */ 1578, 1581, 1607, 1606, 1608, 1609, 1611, 1649, 1655, 1612,
- /* 340 */ 1590, 1591, 1613, 1594, 1621, 1614, 1623, 1616, 1666, 1668,
- /* 350 */ 1579, 1593, 1672, 1675, 1656, 1676, 1679, 1680, 1688, 1660,
- /* 360 */ 1667, 1670, 1671, 1663, 1669, 1673, 1674, 1689, 1681, 1692,
- /* 370 */ 1677, 1693, 1694, 1592, 1599, 1617, 1620, 1700, 1713, 1596,
- /* 380 */ 1598, 1658, 1659, 1691, 1684, 1654, 1735, 1664, 1697, 1690,
- /* 390 */ 1701, 1703, 1748, 1758, 1760, 1768, 1770, 1772, 1657, 1661,
- /* 400 */ 1665, 1761, 1754, 1757, 1762, 1763, 1764, 1750, 1751, 1765,
- /* 410 */ 1771, 1767, 1775,
+ /* 0 */ -67, 1252, -64, -178, -181, 160, 1071, 143, -184, 137,
+ /* 10 */ 218, 220, 222, -174, 229, 268, 272, 275, 324, -208,
+ /* 20 */ 242, -277, -39, 81, 537, 792, 810, 812, -189, 814,
+ /* 30 */ 831, 163, 865, 944, 887, 840, 964, 1077, -187, 292,
+ /* 40 */ -133, 274, 673, 558, 682, 795, 809, -238, -232, -238,
+ /* 50 */ -232, 329, 329, 329, 329, 329, 329, 329, 329, 329,
+ /* 60 */ 329, 329, 329, 329, 329, 329, 329, 329, 329, 329,
+ /* 70 */ 329, 329, 329, 329, 329, 329, 329, 329, 329, 329,
+ /* 80 */ 329, 329, 329, 329, 329, 329, 329, 329, 329, 557,
+ /* 90 */ 712, 949, 966, 969, 971, 979, 1097, 1099, 1103, 1142,
+ /* 100 */ 1144, 1169, 1172, 1201, 1203, 1228, 1241, 1250, 1253, 1255,
+ /* 110 */ 1261, 1266, 1271, 1282, 1291, 1308, 1310, 1312, 1322, 1328,
+ /* 120 */ 1347, 1354, 1356, 1359, 1362, 1365, 1367, 1374, 1376, 1381,
+ /* 130 */ 1401, 1403, 1406, 1412, 1414, 1417, 1421, 1428, 1447, 1449,
+ /* 140 */ 1453, 1462, 329, 329, 329, 329, 329, 329, 329, 329,
+ /* 150 */ 329, 329, 329, -22, -159, 475, -220, 756, 38, 501,
+ /* 160 */ 841, 714, 329, 118, 337, 349, 363, -56, 329, 329,
+ /* 170 */ 329, 329, -205, -205, -205, 687, -172, -130, -57, 790,
+ /* 180 */ 397, 528, -271, 136, 596, 596, 90, 316, 522, 541,
+ /* 190 */ -37, 715, 849, 977, 628, 856, 980, 991, 1081, 1102,
+ /* 200 */ 1135, 1083, -162, 208, 1258, 794, -86, 159, 41, 1109,
+ /* 210 */ 671, 852, 844, 932, 1175, 1254, 480, 1180, 100, 258,
+ /* 220 */ 1265, 1268, 1216, 1287, -139, 317, 344, 63, 339, 423,
+ /* 230 */ 563, 636, 676, 813, 908, 914, 950, 1078, 1084, 1098,
+ /* 240 */ 1363, 1384, 1407, 1439, 1464, 411, 1527, 1534, 1535, 1537,
+ /* 250 */ 1541, 1542, 1543, 1544, 1545, 1547, 1549, 1550, 990, 1164,
+ /* 260 */ 1492, 1551, 1552, 1556, 1217, 1558, 1559, 1560, 1473, 1413,
+ /* 270 */ 1563, 1510, 1568, 563, 1570, 1571, 1572, 1573, 1574, 1575,
+ /* 280 */ 1443, 1466, 1518, 1513, 1514, 1515, 1516, 1217, 1518, 1518,
+ /* 290 */ 1531, 1562, 1582, 1477, 1505, 1511, 1533, 1512, 1488, 1538,
+ /* 300 */ 1509, 1517, 1546, 1519, 1557, 1489, 1565, 1564, 1578, 1586,
+ /* 310 */ 1587, 1588, 1526, 1528, 1554, 1555, 1576, 1577, 1566, 1579,
+ /* 320 */ 1584, 1591, 1520, 1523, 1617, 1628, 1580, 1581, 1632, 1585,
+ /* 330 */ 1590, 1593, 1604, 1605, 1606, 1608, 1609, 1641, 1649, 1610,
+ /* 340 */ 1592, 1594, 1611, 1595, 1616, 1612, 1618, 1613, 1651, 1654,
+ /* 350 */ 1596, 1598, 1655, 1663, 1650, 1673, 1680, 1677, 1684, 1653,
+ /* 360 */ 1664, 1666, 1667, 1662, 1669, 1672, 1676, 1686, 1679, 1691,
+ /* 370 */ 1689, 1692, 1694, 1597, 1599, 1619, 1630, 1699, 1700, 1602,
+ /* 380 */ 1615, 1648, 1657, 1690, 1698, 1658, 1729, 1652, 1695, 1702,
+ /* 390 */ 1704, 1703, 1741, 1754, 1758, 1768, 1769, 1771, 1660, 1661,
+ /* 400 */ 1665, 1752, 1756, 1757, 1759, 1760, 1764, 1745, 1753, 1762,
+ /* 410 */ 1763, 1761, 1772,
};
static const YYACTIONTYPE yy_default[] = {
/* 0 */ 1663, 1663, 1663, 1491, 1254, 1367, 1254, 1254, 1254, 1254,
@@ -175252,6 +176648,7 @@ static const YYCODETYPE yyFallback[] = {
0, /* ERROR => nothing */
0, /* QNUMBER => nothing */
0, /* SPACE => nothing */
+ 0, /* COMMENT => nothing */
0, /* ILLEGAL => nothing */
};
#endif /* YYFALLBACK */
@@ -175521,143 +176918,144 @@ static const char *const yyTokenName[] = {
/* 182 */ "ERROR",
/* 183 */ "QNUMBER",
/* 184 */ "SPACE",
- /* 185 */ "ILLEGAL",
- /* 186 */ "input",
- /* 187 */ "cmdlist",
- /* 188 */ "ecmd",
- /* 189 */ "cmdx",
- /* 190 */ "explain",
- /* 191 */ "cmd",
- /* 192 */ "transtype",
- /* 193 */ "trans_opt",
- /* 194 */ "nm",
- /* 195 */ "savepoint_opt",
- /* 196 */ "create_table",
- /* 197 */ "create_table_args",
- /* 198 */ "createkw",
- /* 199 */ "temp",
- /* 200 */ "ifnotexists",
- /* 201 */ "dbnm",
- /* 202 */ "columnlist",
- /* 203 */ "conslist_opt",
- /* 204 */ "table_option_set",
- /* 205 */ "select",
- /* 206 */ "table_option",
- /* 207 */ "columnname",
- /* 208 */ "carglist",
- /* 209 */ "typetoken",
- /* 210 */ "typename",
- /* 211 */ "signed",
- /* 212 */ "plus_num",
- /* 213 */ "minus_num",
- /* 214 */ "scanpt",
- /* 215 */ "scantok",
- /* 216 */ "ccons",
- /* 217 */ "term",
- /* 218 */ "expr",
- /* 219 */ "onconf",
- /* 220 */ "sortorder",
- /* 221 */ "autoinc",
- /* 222 */ "eidlist_opt",
- /* 223 */ "refargs",
- /* 224 */ "defer_subclause",
- /* 225 */ "generated",
- /* 226 */ "refarg",
- /* 227 */ "refact",
- /* 228 */ "init_deferred_pred_opt",
- /* 229 */ "conslist",
- /* 230 */ "tconscomma",
- /* 231 */ "tcons",
- /* 232 */ "sortlist",
- /* 233 */ "eidlist",
- /* 234 */ "defer_subclause_opt",
- /* 235 */ "orconf",
- /* 236 */ "resolvetype",
- /* 237 */ "raisetype",
- /* 238 */ "ifexists",
- /* 239 */ "fullname",
- /* 240 */ "selectnowith",
- /* 241 */ "oneselect",
- /* 242 */ "wqlist",
- /* 243 */ "multiselect_op",
- /* 244 */ "distinct",
- /* 245 */ "selcollist",
- /* 246 */ "from",
- /* 247 */ "where_opt",
- /* 248 */ "groupby_opt",
- /* 249 */ "having_opt",
- /* 250 */ "orderby_opt",
- /* 251 */ "limit_opt",
- /* 252 */ "window_clause",
- /* 253 */ "values",
- /* 254 */ "nexprlist",
- /* 255 */ "mvalues",
- /* 256 */ "sclp",
- /* 257 */ "as",
- /* 258 */ "seltablist",
- /* 259 */ "stl_prefix",
- /* 260 */ "joinop",
- /* 261 */ "on_using",
- /* 262 */ "indexed_by",
- /* 263 */ "exprlist",
- /* 264 */ "xfullname",
- /* 265 */ "idlist",
- /* 266 */ "indexed_opt",
- /* 267 */ "nulls",
- /* 268 */ "with",
- /* 269 */ "where_opt_ret",
- /* 270 */ "setlist",
- /* 271 */ "insert_cmd",
- /* 272 */ "idlist_opt",
- /* 273 */ "upsert",
- /* 274 */ "returning",
- /* 275 */ "filter_over",
- /* 276 */ "likeop",
- /* 277 */ "between_op",
- /* 278 */ "in_op",
- /* 279 */ "paren_exprlist",
- /* 280 */ "case_operand",
- /* 281 */ "case_exprlist",
- /* 282 */ "case_else",
- /* 283 */ "uniqueflag",
- /* 284 */ "collate",
- /* 285 */ "vinto",
- /* 286 */ "nmnum",
- /* 287 */ "trigger_decl",
- /* 288 */ "trigger_cmd_list",
- /* 289 */ "trigger_time",
- /* 290 */ "trigger_event",
- /* 291 */ "foreach_clause",
- /* 292 */ "when_clause",
- /* 293 */ "trigger_cmd",
- /* 294 */ "trnm",
- /* 295 */ "tridxby",
- /* 296 */ "database_kw_opt",
- /* 297 */ "key_opt",
- /* 298 */ "add_column_fullname",
- /* 299 */ "kwcolumn_opt",
- /* 300 */ "create_vtab",
- /* 301 */ "vtabarglist",
- /* 302 */ "vtabarg",
- /* 303 */ "vtabargtoken",
- /* 304 */ "lp",
- /* 305 */ "anylist",
- /* 306 */ "wqitem",
- /* 307 */ "wqas",
- /* 308 */ "withnm",
- /* 309 */ "windowdefn_list",
- /* 310 */ "windowdefn",
- /* 311 */ "window",
- /* 312 */ "frame_opt",
- /* 313 */ "part_opt",
- /* 314 */ "filter_clause",
- /* 315 */ "over_clause",
- /* 316 */ "range_or_rows",
- /* 317 */ "frame_bound",
- /* 318 */ "frame_bound_s",
- /* 319 */ "frame_bound_e",
- /* 320 */ "frame_exclude_opt",
- /* 321 */ "frame_exclude",
+ /* 185 */ "COMMENT",
+ /* 186 */ "ILLEGAL",
+ /* 187 */ "input",
+ /* 188 */ "cmdlist",
+ /* 189 */ "ecmd",
+ /* 190 */ "cmdx",
+ /* 191 */ "explain",
+ /* 192 */ "cmd",
+ /* 193 */ "transtype",
+ /* 194 */ "trans_opt",
+ /* 195 */ "nm",
+ /* 196 */ "savepoint_opt",
+ /* 197 */ "create_table",
+ /* 198 */ "create_table_args",
+ /* 199 */ "createkw",
+ /* 200 */ "temp",
+ /* 201 */ "ifnotexists",
+ /* 202 */ "dbnm",
+ /* 203 */ "columnlist",
+ /* 204 */ "conslist_opt",
+ /* 205 */ "table_option_set",
+ /* 206 */ "select",
+ /* 207 */ "table_option",
+ /* 208 */ "columnname",
+ /* 209 */ "carglist",
+ /* 210 */ "typetoken",
+ /* 211 */ "typename",
+ /* 212 */ "signed",
+ /* 213 */ "plus_num",
+ /* 214 */ "minus_num",
+ /* 215 */ "scanpt",
+ /* 216 */ "scantok",
+ /* 217 */ "ccons",
+ /* 218 */ "term",
+ /* 219 */ "expr",
+ /* 220 */ "onconf",
+ /* 221 */ "sortorder",
+ /* 222 */ "autoinc",
+ /* 223 */ "eidlist_opt",
+ /* 224 */ "refargs",
+ /* 225 */ "defer_subclause",
+ /* 226 */ "generated",
+ /* 227 */ "refarg",
+ /* 228 */ "refact",
+ /* 229 */ "init_deferred_pred_opt",
+ /* 230 */ "conslist",
+ /* 231 */ "tconscomma",
+ /* 232 */ "tcons",
+ /* 233 */ "sortlist",
+ /* 234 */ "eidlist",
+ /* 235 */ "defer_subclause_opt",
+ /* 236 */ "orconf",
+ /* 237 */ "resolvetype",
+ /* 238 */ "raisetype",
+ /* 239 */ "ifexists",
+ /* 240 */ "fullname",
+ /* 241 */ "selectnowith",
+ /* 242 */ "oneselect",
+ /* 243 */ "wqlist",
+ /* 244 */ "multiselect_op",
+ /* 245 */ "distinct",
+ /* 246 */ "selcollist",
+ /* 247 */ "from",
+ /* 248 */ "where_opt",
+ /* 249 */ "groupby_opt",
+ /* 250 */ "having_opt",
+ /* 251 */ "orderby_opt",
+ /* 252 */ "limit_opt",
+ /* 253 */ "window_clause",
+ /* 254 */ "values",
+ /* 255 */ "nexprlist",
+ /* 256 */ "mvalues",
+ /* 257 */ "sclp",
+ /* 258 */ "as",
+ /* 259 */ "seltablist",
+ /* 260 */ "stl_prefix",
+ /* 261 */ "joinop",
+ /* 262 */ "on_using",
+ /* 263 */ "indexed_by",
+ /* 264 */ "exprlist",
+ /* 265 */ "xfullname",
+ /* 266 */ "idlist",
+ /* 267 */ "indexed_opt",
+ /* 268 */ "nulls",
+ /* 269 */ "with",
+ /* 270 */ "where_opt_ret",
+ /* 271 */ "setlist",
+ /* 272 */ "insert_cmd",
+ /* 273 */ "idlist_opt",
+ /* 274 */ "upsert",
+ /* 275 */ "returning",
+ /* 276 */ "filter_over",
+ /* 277 */ "likeop",
+ /* 278 */ "between_op",
+ /* 279 */ "in_op",
+ /* 280 */ "paren_exprlist",
+ /* 281 */ "case_operand",
+ /* 282 */ "case_exprlist",
+ /* 283 */ "case_else",
+ /* 284 */ "uniqueflag",
+ /* 285 */ "collate",
+ /* 286 */ "vinto",
+ /* 287 */ "nmnum",
+ /* 288 */ "trigger_decl",
+ /* 289 */ "trigger_cmd_list",
+ /* 290 */ "trigger_time",
+ /* 291 */ "trigger_event",
+ /* 292 */ "foreach_clause",
+ /* 293 */ "when_clause",
+ /* 294 */ "trigger_cmd",
+ /* 295 */ "trnm",
+ /* 296 */ "tridxby",
+ /* 297 */ "database_kw_opt",
+ /* 298 */ "key_opt",
+ /* 299 */ "add_column_fullname",
+ /* 300 */ "kwcolumn_opt",
+ /* 301 */ "create_vtab",
+ /* 302 */ "vtabarglist",
+ /* 303 */ "vtabarg",
+ /* 304 */ "vtabargtoken",
+ /* 305 */ "lp",
+ /* 306 */ "anylist",
+ /* 307 */ "wqitem",
+ /* 308 */ "wqas",
+ /* 309 */ "withnm",
+ /* 310 */ "windowdefn_list",
+ /* 311 */ "windowdefn",
+ /* 312 */ "window",
+ /* 313 */ "frame_opt",
+ /* 314 */ "part_opt",
+ /* 315 */ "filter_clause",
+ /* 316 */ "over_clause",
+ /* 317 */ "range_or_rows",
+ /* 318 */ "frame_bound",
+ /* 319 */ "frame_bound_s",
+ /* 320 */ "frame_bound_e",
+ /* 321 */ "frame_exclude_opt",
+ /* 322 */ "frame_exclude",
};
#endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */
@@ -176197,98 +177595,98 @@ static void yy_destructor(
** inside the C code.
*/
/********* Begin destructor definitions ***************************************/
- case 205: /* select */
- case 240: /* selectnowith */
- case 241: /* oneselect */
- case 253: /* values */
- case 255: /* mvalues */
+ case 206: /* select */
+ case 241: /* selectnowith */
+ case 242: /* oneselect */
+ case 254: /* values */
+ case 256: /* mvalues */
{
-sqlite3SelectDelete(pParse->db, (yypminor->yy555));
-}
- break;
- case 217: /* term */
- case 218: /* expr */
- case 247: /* where_opt */
- case 249: /* having_opt */
- case 269: /* where_opt_ret */
- case 280: /* case_operand */
- case 282: /* case_else */
- case 285: /* vinto */
- case 292: /* when_clause */
- case 297: /* key_opt */
- case 314: /* filter_clause */
+sqlite3SelectDelete(pParse->db, (yypminor->yy637));
+}
+ break;
+ case 218: /* term */
+ case 219: /* expr */
+ case 248: /* where_opt */
+ case 250: /* having_opt */
+ case 270: /* where_opt_ret */
+ case 281: /* case_operand */
+ case 283: /* case_else */
+ case 286: /* vinto */
+ case 293: /* when_clause */
+ case 298: /* key_opt */
+ case 315: /* filter_clause */
{
-sqlite3ExprDelete(pParse->db, (yypminor->yy454));
-}
- break;
- case 222: /* eidlist_opt */
- case 232: /* sortlist */
- case 233: /* eidlist */
- case 245: /* selcollist */
- case 248: /* groupby_opt */
- case 250: /* orderby_opt */
- case 254: /* nexprlist */
- case 256: /* sclp */
- case 263: /* exprlist */
- case 270: /* setlist */
- case 279: /* paren_exprlist */
- case 281: /* case_exprlist */
- case 313: /* part_opt */
+sqlite3ExprDelete(pParse->db, (yypminor->yy590));
+}
+ break;
+ case 223: /* eidlist_opt */
+ case 233: /* sortlist */
+ case 234: /* eidlist */
+ case 246: /* selcollist */
+ case 249: /* groupby_opt */
+ case 251: /* orderby_opt */
+ case 255: /* nexprlist */
+ case 257: /* sclp */
+ case 264: /* exprlist */
+ case 271: /* setlist */
+ case 280: /* paren_exprlist */
+ case 282: /* case_exprlist */
+ case 314: /* part_opt */
{
-sqlite3ExprListDelete(pParse->db, (yypminor->yy14));
+sqlite3ExprListDelete(pParse->db, (yypminor->yy402));
}
break;
- case 239: /* fullname */
- case 246: /* from */
- case 258: /* seltablist */
- case 259: /* stl_prefix */
- case 264: /* xfullname */
+ case 240: /* fullname */
+ case 247: /* from */
+ case 259: /* seltablist */
+ case 260: /* stl_prefix */
+ case 265: /* xfullname */
{
-sqlite3SrcListDelete(pParse->db, (yypminor->yy203));
+sqlite3SrcListDelete(pParse->db, (yypminor->yy563));
}
break;
- case 242: /* wqlist */
+ case 243: /* wqlist */
{
-sqlite3WithDelete(pParse->db, (yypminor->yy59));
+sqlite3WithDelete(pParse->db, (yypminor->yy125));
}
break;
- case 252: /* window_clause */
- case 309: /* windowdefn_list */
+ case 253: /* window_clause */
+ case 310: /* windowdefn_list */
{
-sqlite3WindowListDelete(pParse->db, (yypminor->yy211));
+sqlite3WindowListDelete(pParse->db, (yypminor->yy483));
}
break;
- case 265: /* idlist */
- case 272: /* idlist_opt */
+ case 266: /* idlist */
+ case 273: /* idlist_opt */
{
-sqlite3IdListDelete(pParse->db, (yypminor->yy132));
+sqlite3IdListDelete(pParse->db, (yypminor->yy204));
}
break;
- case 275: /* filter_over */
- case 310: /* windowdefn */
- case 311: /* window */
- case 312: /* frame_opt */
- case 315: /* over_clause */
+ case 276: /* filter_over */
+ case 311: /* windowdefn */
+ case 312: /* window */
+ case 313: /* frame_opt */
+ case 316: /* over_clause */
{
-sqlite3WindowDelete(pParse->db, (yypminor->yy211));
+sqlite3WindowDelete(pParse->db, (yypminor->yy483));
}
break;
- case 288: /* trigger_cmd_list */
- case 293: /* trigger_cmd */
+ case 289: /* trigger_cmd_list */
+ case 294: /* trigger_cmd */
{
-sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy427));
+sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy319));
}
break;
- case 290: /* trigger_event */
+ case 291: /* trigger_event */
{
-sqlite3IdListDelete(pParse->db, (yypminor->yy286).b);
+sqlite3IdListDelete(pParse->db, (yypminor->yy28).b);
}
break;
- case 317: /* frame_bound */
- case 318: /* frame_bound_s */
- case 319: /* frame_bound_e */
+ case 318: /* frame_bound */
+ case 319: /* frame_bound_s */
+ case 320: /* frame_bound_e */
{
-sqlite3ExprDelete(pParse->db, (yypminor->yy509).pExpr);
+sqlite3ExprDelete(pParse->db, (yypminor->yy205).pExpr);
}
break;
/********* End destructor definitions *****************************************/
@@ -176590,415 +177988,415 @@ static void yy_shift(
/* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side
** of that rule */
static const YYCODETYPE yyRuleInfoLhs[] = {
- 190, /* (0) explain ::= EXPLAIN */
- 190, /* (1) explain ::= EXPLAIN QUERY PLAN */
- 189, /* (2) cmdx ::= cmd */
- 191, /* (3) cmd ::= BEGIN transtype trans_opt */
- 192, /* (4) transtype ::= */
- 192, /* (5) transtype ::= DEFERRED */
- 192, /* (6) transtype ::= IMMEDIATE */
- 192, /* (7) transtype ::= EXCLUSIVE */
- 191, /* (8) cmd ::= COMMIT|END trans_opt */
- 191, /* (9) cmd ::= ROLLBACK trans_opt */
- 191, /* (10) cmd ::= SAVEPOINT nm */
- 191, /* (11) cmd ::= RELEASE savepoint_opt nm */
- 191, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */
- 196, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */
- 198, /* (14) createkw ::= CREATE */
- 200, /* (15) ifnotexists ::= */
- 200, /* (16) ifnotexists ::= IF NOT EXISTS */
- 199, /* (17) temp ::= TEMP */
- 199, /* (18) temp ::= */
- 197, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_option_set */
- 197, /* (20) create_table_args ::= AS select */
- 204, /* (21) table_option_set ::= */
- 204, /* (22) table_option_set ::= table_option_set COMMA table_option */
- 206, /* (23) table_option ::= WITHOUT nm */
- 206, /* (24) table_option ::= nm */
- 207, /* (25) columnname ::= nm typetoken */
- 209, /* (26) typetoken ::= */
- 209, /* (27) typetoken ::= typename LP signed RP */
- 209, /* (28) typetoken ::= typename LP signed COMMA signed RP */
- 210, /* (29) typename ::= typename ID|STRING */
- 214, /* (30) scanpt ::= */
- 215, /* (31) scantok ::= */
- 216, /* (32) ccons ::= CONSTRAINT nm */
- 216, /* (33) ccons ::= DEFAULT scantok term */
- 216, /* (34) ccons ::= DEFAULT LP expr RP */
- 216, /* (35) ccons ::= DEFAULT PLUS scantok term */
- 216, /* (36) ccons ::= DEFAULT MINUS scantok term */
- 216, /* (37) ccons ::= DEFAULT scantok ID|INDEXED */
- 216, /* (38) ccons ::= NOT NULL onconf */
- 216, /* (39) ccons ::= PRIMARY KEY sortorder onconf autoinc */
- 216, /* (40) ccons ::= UNIQUE onconf */
- 216, /* (41) ccons ::= CHECK LP expr RP */
- 216, /* (42) ccons ::= REFERENCES nm eidlist_opt refargs */
- 216, /* (43) ccons ::= defer_subclause */
- 216, /* (44) ccons ::= COLLATE ID|STRING */
- 225, /* (45) generated ::= LP expr RP */
- 225, /* (46) generated ::= LP expr RP ID */
- 221, /* (47) autoinc ::= */
- 221, /* (48) autoinc ::= AUTOINCR */
- 223, /* (49) refargs ::= */
- 223, /* (50) refargs ::= refargs refarg */
- 226, /* (51) refarg ::= MATCH nm */
- 226, /* (52) refarg ::= ON INSERT refact */
- 226, /* (53) refarg ::= ON DELETE refact */
- 226, /* (54) refarg ::= ON UPDATE refact */
- 227, /* (55) refact ::= SET NULL */
- 227, /* (56) refact ::= SET DEFAULT */
- 227, /* (57) refact ::= CASCADE */
- 227, /* (58) refact ::= RESTRICT */
- 227, /* (59) refact ::= NO ACTION */
- 224, /* (60) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
- 224, /* (61) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
- 228, /* (62) init_deferred_pred_opt ::= */
- 228, /* (63) init_deferred_pred_opt ::= INITIALLY DEFERRED */
- 228, /* (64) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
- 203, /* (65) conslist_opt ::= */
- 230, /* (66) tconscomma ::= COMMA */
- 231, /* (67) tcons ::= CONSTRAINT nm */
- 231, /* (68) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
- 231, /* (69) tcons ::= UNIQUE LP sortlist RP onconf */
- 231, /* (70) tcons ::= CHECK LP expr RP onconf */
- 231, /* (71) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
- 234, /* (72) defer_subclause_opt ::= */
- 219, /* (73) onconf ::= */
- 219, /* (74) onconf ::= ON CONFLICT resolvetype */
- 235, /* (75) orconf ::= */
- 235, /* (76) orconf ::= OR resolvetype */
- 236, /* (77) resolvetype ::= IGNORE */
- 236, /* (78) resolvetype ::= REPLACE */
- 191, /* (79) cmd ::= DROP TABLE ifexists fullname */
- 238, /* (80) ifexists ::= IF EXISTS */
- 238, /* (81) ifexists ::= */
- 191, /* (82) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
- 191, /* (83) cmd ::= DROP VIEW ifexists fullname */
- 191, /* (84) cmd ::= select */
- 205, /* (85) select ::= WITH wqlist selectnowith */
- 205, /* (86) select ::= WITH RECURSIVE wqlist selectnowith */
- 205, /* (87) select ::= selectnowith */
- 240, /* (88) selectnowith ::= selectnowith multiselect_op oneselect */
- 243, /* (89) multiselect_op ::= UNION */
- 243, /* (90) multiselect_op ::= UNION ALL */
- 243, /* (91) multiselect_op ::= EXCEPT|INTERSECT */
- 241, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
- 241, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
- 253, /* (94) values ::= VALUES LP nexprlist RP */
- 241, /* (95) oneselect ::= mvalues */
- 255, /* (96) mvalues ::= values COMMA LP nexprlist RP */
- 255, /* (97) mvalues ::= mvalues COMMA LP nexprlist RP */
- 244, /* (98) distinct ::= DISTINCT */
- 244, /* (99) distinct ::= ALL */
- 244, /* (100) distinct ::= */
- 256, /* (101) sclp ::= */
- 245, /* (102) selcollist ::= sclp scanpt expr scanpt as */
- 245, /* (103) selcollist ::= sclp scanpt STAR */
- 245, /* (104) selcollist ::= sclp scanpt nm DOT STAR */
- 257, /* (105) as ::= AS nm */
- 257, /* (106) as ::= */
- 246, /* (107) from ::= */
- 246, /* (108) from ::= FROM seltablist */
- 259, /* (109) stl_prefix ::= seltablist joinop */
- 259, /* (110) stl_prefix ::= */
- 258, /* (111) seltablist ::= stl_prefix nm dbnm as on_using */
- 258, /* (112) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */
- 258, /* (113) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */
- 258, /* (114) seltablist ::= stl_prefix LP select RP as on_using */
- 258, /* (115) seltablist ::= stl_prefix LP seltablist RP as on_using */
- 201, /* (116) dbnm ::= */
- 201, /* (117) dbnm ::= DOT nm */
- 239, /* (118) fullname ::= nm */
- 239, /* (119) fullname ::= nm DOT nm */
- 264, /* (120) xfullname ::= nm */
- 264, /* (121) xfullname ::= nm DOT nm */
- 264, /* (122) xfullname ::= nm DOT nm AS nm */
- 264, /* (123) xfullname ::= nm AS nm */
- 260, /* (124) joinop ::= COMMA|JOIN */
- 260, /* (125) joinop ::= JOIN_KW JOIN */
- 260, /* (126) joinop ::= JOIN_KW nm JOIN */
- 260, /* (127) joinop ::= JOIN_KW nm nm JOIN */
- 261, /* (128) on_using ::= ON expr */
- 261, /* (129) on_using ::= USING LP idlist RP */
- 261, /* (130) on_using ::= */
- 266, /* (131) indexed_opt ::= */
- 262, /* (132) indexed_by ::= INDEXED BY nm */
- 262, /* (133) indexed_by ::= NOT INDEXED */
- 250, /* (134) orderby_opt ::= */
- 250, /* (135) orderby_opt ::= ORDER BY sortlist */
- 232, /* (136) sortlist ::= sortlist COMMA expr sortorder nulls */
- 232, /* (137) sortlist ::= expr sortorder nulls */
- 220, /* (138) sortorder ::= ASC */
- 220, /* (139) sortorder ::= DESC */
- 220, /* (140) sortorder ::= */
- 267, /* (141) nulls ::= NULLS FIRST */
- 267, /* (142) nulls ::= NULLS LAST */
- 267, /* (143) nulls ::= */
- 248, /* (144) groupby_opt ::= */
- 248, /* (145) groupby_opt ::= GROUP BY nexprlist */
- 249, /* (146) having_opt ::= */
- 249, /* (147) having_opt ::= HAVING expr */
- 251, /* (148) limit_opt ::= */
- 251, /* (149) limit_opt ::= LIMIT expr */
- 251, /* (150) limit_opt ::= LIMIT expr OFFSET expr */
- 251, /* (151) limit_opt ::= LIMIT expr COMMA expr */
- 191, /* (152) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */
- 247, /* (153) where_opt ::= */
- 247, /* (154) where_opt ::= WHERE expr */
- 269, /* (155) where_opt_ret ::= */
- 269, /* (156) where_opt_ret ::= WHERE expr */
- 269, /* (157) where_opt_ret ::= RETURNING selcollist */
- 269, /* (158) where_opt_ret ::= WHERE expr RETURNING selcollist */
- 191, /* (159) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */
- 270, /* (160) setlist ::= setlist COMMA nm EQ expr */
- 270, /* (161) setlist ::= setlist COMMA LP idlist RP EQ expr */
- 270, /* (162) setlist ::= nm EQ expr */
- 270, /* (163) setlist ::= LP idlist RP EQ expr */
- 191, /* (164) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
- 191, /* (165) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */
- 273, /* (166) upsert ::= */
- 273, /* (167) upsert ::= RETURNING selcollist */
- 273, /* (168) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */
- 273, /* (169) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */
- 273, /* (170) upsert ::= ON CONFLICT DO NOTHING returning */
- 273, /* (171) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */
- 274, /* (172) returning ::= RETURNING selcollist */
- 271, /* (173) insert_cmd ::= INSERT orconf */
- 271, /* (174) insert_cmd ::= REPLACE */
- 272, /* (175) idlist_opt ::= */
- 272, /* (176) idlist_opt ::= LP idlist RP */
- 265, /* (177) idlist ::= idlist COMMA nm */
- 265, /* (178) idlist ::= nm */
- 218, /* (179) expr ::= LP expr RP */
- 218, /* (180) expr ::= ID|INDEXED|JOIN_KW */
- 218, /* (181) expr ::= nm DOT nm */
- 218, /* (182) expr ::= nm DOT nm DOT nm */
- 217, /* (183) term ::= NULL|FLOAT|BLOB */
- 217, /* (184) term ::= STRING */
- 217, /* (185) term ::= INTEGER */
- 218, /* (186) expr ::= VARIABLE */
- 218, /* (187) expr ::= expr COLLATE ID|STRING */
- 218, /* (188) expr ::= CAST LP expr AS typetoken RP */
- 218, /* (189) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */
- 218, /* (190) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */
- 218, /* (191) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
- 218, /* (192) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
- 218, /* (193) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */
- 218, /* (194) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
- 217, /* (195) term ::= CTIME_KW */
- 218, /* (196) expr ::= LP nexprlist COMMA expr RP */
- 218, /* (197) expr ::= expr AND expr */
- 218, /* (198) expr ::= expr OR expr */
- 218, /* (199) expr ::= expr LT|GT|GE|LE expr */
- 218, /* (200) expr ::= expr EQ|NE expr */
- 218, /* (201) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
- 218, /* (202) expr ::= expr PLUS|MINUS expr */
- 218, /* (203) expr ::= expr STAR|SLASH|REM expr */
- 218, /* (204) expr ::= expr CONCAT expr */
- 276, /* (205) likeop ::= NOT LIKE_KW|MATCH */
- 218, /* (206) expr ::= expr likeop expr */
- 218, /* (207) expr ::= expr likeop expr ESCAPE expr */
- 218, /* (208) expr ::= expr ISNULL|NOTNULL */
- 218, /* (209) expr ::= expr NOT NULL */
- 218, /* (210) expr ::= expr IS expr */
- 218, /* (211) expr ::= expr IS NOT expr */
- 218, /* (212) expr ::= expr IS NOT DISTINCT FROM expr */
- 218, /* (213) expr ::= expr IS DISTINCT FROM expr */
- 218, /* (214) expr ::= NOT expr */
- 218, /* (215) expr ::= BITNOT expr */
- 218, /* (216) expr ::= PLUS|MINUS expr */
- 218, /* (217) expr ::= expr PTR expr */
- 277, /* (218) between_op ::= BETWEEN */
- 277, /* (219) between_op ::= NOT BETWEEN */
- 218, /* (220) expr ::= expr between_op expr AND expr */
- 278, /* (221) in_op ::= IN */
- 278, /* (222) in_op ::= NOT IN */
- 218, /* (223) expr ::= expr in_op LP exprlist RP */
- 218, /* (224) expr ::= LP select RP */
- 218, /* (225) expr ::= expr in_op LP select RP */
- 218, /* (226) expr ::= expr in_op nm dbnm paren_exprlist */
- 218, /* (227) expr ::= EXISTS LP select RP */
- 218, /* (228) expr ::= CASE case_operand case_exprlist case_else END */
- 281, /* (229) case_exprlist ::= case_exprlist WHEN expr THEN expr */
- 281, /* (230) case_exprlist ::= WHEN expr THEN expr */
- 282, /* (231) case_else ::= ELSE expr */
- 282, /* (232) case_else ::= */
- 280, /* (233) case_operand ::= */
- 263, /* (234) exprlist ::= */
- 254, /* (235) nexprlist ::= nexprlist COMMA expr */
- 254, /* (236) nexprlist ::= expr */
- 279, /* (237) paren_exprlist ::= */
- 279, /* (238) paren_exprlist ::= LP exprlist RP */
- 191, /* (239) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
- 283, /* (240) uniqueflag ::= UNIQUE */
- 283, /* (241) uniqueflag ::= */
- 222, /* (242) eidlist_opt ::= */
- 222, /* (243) eidlist_opt ::= LP eidlist RP */
- 233, /* (244) eidlist ::= eidlist COMMA nm collate sortorder */
- 233, /* (245) eidlist ::= nm collate sortorder */
- 284, /* (246) collate ::= */
- 284, /* (247) collate ::= COLLATE ID|STRING */
- 191, /* (248) cmd ::= DROP INDEX ifexists fullname */
- 191, /* (249) cmd ::= VACUUM vinto */
- 191, /* (250) cmd ::= VACUUM nm vinto */
- 285, /* (251) vinto ::= INTO expr */
- 285, /* (252) vinto ::= */
- 191, /* (253) cmd ::= PRAGMA nm dbnm */
- 191, /* (254) cmd ::= PRAGMA nm dbnm EQ nmnum */
- 191, /* (255) cmd ::= PRAGMA nm dbnm LP nmnum RP */
- 191, /* (256) cmd ::= PRAGMA nm dbnm EQ minus_num */
- 191, /* (257) cmd ::= PRAGMA nm dbnm LP minus_num RP */
- 212, /* (258) plus_num ::= PLUS INTEGER|FLOAT */
- 213, /* (259) minus_num ::= MINUS INTEGER|FLOAT */
- 191, /* (260) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
- 287, /* (261) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
- 289, /* (262) trigger_time ::= BEFORE|AFTER */
- 289, /* (263) trigger_time ::= INSTEAD OF */
- 289, /* (264) trigger_time ::= */
- 290, /* (265) trigger_event ::= DELETE|INSERT */
- 290, /* (266) trigger_event ::= UPDATE */
- 290, /* (267) trigger_event ::= UPDATE OF idlist */
- 292, /* (268) when_clause ::= */
- 292, /* (269) when_clause ::= WHEN expr */
- 288, /* (270) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
- 288, /* (271) trigger_cmd_list ::= trigger_cmd SEMI */
- 294, /* (272) trnm ::= nm DOT nm */
- 295, /* (273) tridxby ::= INDEXED BY nm */
- 295, /* (274) tridxby ::= NOT INDEXED */
- 293, /* (275) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
- 293, /* (276) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
- 293, /* (277) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
- 293, /* (278) trigger_cmd ::= scanpt select scanpt */
- 218, /* (279) expr ::= RAISE LP IGNORE RP */
- 218, /* (280) expr ::= RAISE LP raisetype COMMA expr RP */
- 237, /* (281) raisetype ::= ROLLBACK */
- 237, /* (282) raisetype ::= ABORT */
- 237, /* (283) raisetype ::= FAIL */
- 191, /* (284) cmd ::= DROP TRIGGER ifexists fullname */
- 191, /* (285) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
- 191, /* (286) cmd ::= DETACH database_kw_opt expr */
- 297, /* (287) key_opt ::= */
- 297, /* (288) key_opt ::= KEY expr */
- 191, /* (289) cmd ::= REINDEX */
- 191, /* (290) cmd ::= REINDEX nm dbnm */
- 191, /* (291) cmd ::= ANALYZE */
- 191, /* (292) cmd ::= ANALYZE nm dbnm */
- 191, /* (293) cmd ::= ALTER TABLE fullname RENAME TO nm */
- 191, /* (294) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
- 191, /* (295) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
- 298, /* (296) add_column_fullname ::= fullname */
- 191, /* (297) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
- 191, /* (298) cmd ::= create_vtab */
- 191, /* (299) cmd ::= create_vtab LP vtabarglist RP */
- 300, /* (300) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
- 302, /* (301) vtabarg ::= */
- 303, /* (302) vtabargtoken ::= ANY */
- 303, /* (303) vtabargtoken ::= lp anylist RP */
- 304, /* (304) lp ::= LP */
- 268, /* (305) with ::= WITH wqlist */
- 268, /* (306) with ::= WITH RECURSIVE wqlist */
- 307, /* (307) wqas ::= AS */
- 307, /* (308) wqas ::= AS MATERIALIZED */
- 307, /* (309) wqas ::= AS NOT MATERIALIZED */
- 306, /* (310) wqitem ::= withnm eidlist_opt wqas LP select RP */
- 308, /* (311) withnm ::= nm */
- 242, /* (312) wqlist ::= wqitem */
- 242, /* (313) wqlist ::= wqlist COMMA wqitem */
- 309, /* (314) windowdefn_list ::= windowdefn_list COMMA windowdefn */
- 310, /* (315) windowdefn ::= nm AS LP window RP */
- 311, /* (316) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
- 311, /* (317) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
- 311, /* (318) window ::= ORDER BY sortlist frame_opt */
- 311, /* (319) window ::= nm ORDER BY sortlist frame_opt */
- 311, /* (320) window ::= nm frame_opt */
- 312, /* (321) frame_opt ::= */
- 312, /* (322) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
- 312, /* (323) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
- 316, /* (324) range_or_rows ::= RANGE|ROWS|GROUPS */
- 318, /* (325) frame_bound_s ::= frame_bound */
- 318, /* (326) frame_bound_s ::= UNBOUNDED PRECEDING */
- 319, /* (327) frame_bound_e ::= frame_bound */
- 319, /* (328) frame_bound_e ::= UNBOUNDED FOLLOWING */
- 317, /* (329) frame_bound ::= expr PRECEDING|FOLLOWING */
- 317, /* (330) frame_bound ::= CURRENT ROW */
- 320, /* (331) frame_exclude_opt ::= */
- 320, /* (332) frame_exclude_opt ::= EXCLUDE frame_exclude */
- 321, /* (333) frame_exclude ::= NO OTHERS */
- 321, /* (334) frame_exclude ::= CURRENT ROW */
- 321, /* (335) frame_exclude ::= GROUP|TIES */
- 252, /* (336) window_clause ::= WINDOW windowdefn_list */
- 275, /* (337) filter_over ::= filter_clause over_clause */
- 275, /* (338) filter_over ::= over_clause */
- 275, /* (339) filter_over ::= filter_clause */
- 315, /* (340) over_clause ::= OVER LP window RP */
- 315, /* (341) over_clause ::= OVER nm */
- 314, /* (342) filter_clause ::= FILTER LP WHERE expr RP */
- 217, /* (343) term ::= QNUMBER */
- 186, /* (344) input ::= cmdlist */
- 187, /* (345) cmdlist ::= cmdlist ecmd */
- 187, /* (346) cmdlist ::= ecmd */
- 188, /* (347) ecmd ::= SEMI */
- 188, /* (348) ecmd ::= cmdx SEMI */
- 188, /* (349) ecmd ::= explain cmdx SEMI */
- 193, /* (350) trans_opt ::= */
- 193, /* (351) trans_opt ::= TRANSACTION */
- 193, /* (352) trans_opt ::= TRANSACTION nm */
- 195, /* (353) savepoint_opt ::= SAVEPOINT */
- 195, /* (354) savepoint_opt ::= */
- 191, /* (355) cmd ::= create_table create_table_args */
- 204, /* (356) table_option_set ::= table_option */
- 202, /* (357) columnlist ::= columnlist COMMA columnname carglist */
- 202, /* (358) columnlist ::= columnname carglist */
- 194, /* (359) nm ::= ID|INDEXED|JOIN_KW */
- 194, /* (360) nm ::= STRING */
- 209, /* (361) typetoken ::= typename */
- 210, /* (362) typename ::= ID|STRING */
- 211, /* (363) signed ::= plus_num */
- 211, /* (364) signed ::= minus_num */
- 208, /* (365) carglist ::= carglist ccons */
- 208, /* (366) carglist ::= */
- 216, /* (367) ccons ::= NULL onconf */
- 216, /* (368) ccons ::= GENERATED ALWAYS AS generated */
- 216, /* (369) ccons ::= AS generated */
- 203, /* (370) conslist_opt ::= COMMA conslist */
- 229, /* (371) conslist ::= conslist tconscomma tcons */
- 229, /* (372) conslist ::= tcons */
- 230, /* (373) tconscomma ::= */
- 234, /* (374) defer_subclause_opt ::= defer_subclause */
- 236, /* (375) resolvetype ::= raisetype */
- 240, /* (376) selectnowith ::= oneselect */
- 241, /* (377) oneselect ::= values */
- 256, /* (378) sclp ::= selcollist COMMA */
- 257, /* (379) as ::= ID|STRING */
- 266, /* (380) indexed_opt ::= indexed_by */
- 274, /* (381) returning ::= */
- 218, /* (382) expr ::= term */
- 276, /* (383) likeop ::= LIKE_KW|MATCH */
- 280, /* (384) case_operand ::= expr */
- 263, /* (385) exprlist ::= nexprlist */
- 286, /* (386) nmnum ::= plus_num */
- 286, /* (387) nmnum ::= nm */
- 286, /* (388) nmnum ::= ON */
- 286, /* (389) nmnum ::= DELETE */
- 286, /* (390) nmnum ::= DEFAULT */
- 212, /* (391) plus_num ::= INTEGER|FLOAT */
- 291, /* (392) foreach_clause ::= */
- 291, /* (393) foreach_clause ::= FOR EACH ROW */
- 294, /* (394) trnm ::= nm */
- 295, /* (395) tridxby ::= */
- 296, /* (396) database_kw_opt ::= DATABASE */
- 296, /* (397) database_kw_opt ::= */
- 299, /* (398) kwcolumn_opt ::= */
- 299, /* (399) kwcolumn_opt ::= COLUMNKW */
- 301, /* (400) vtabarglist ::= vtabarg */
- 301, /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */
- 302, /* (402) vtabarg ::= vtabarg vtabargtoken */
- 305, /* (403) anylist ::= */
- 305, /* (404) anylist ::= anylist LP anylist RP */
- 305, /* (405) anylist ::= anylist ANY */
- 268, /* (406) with ::= */
- 309, /* (407) windowdefn_list ::= windowdefn */
- 311, /* (408) window ::= frame_opt */
+ 191, /* (0) explain ::= EXPLAIN */
+ 191, /* (1) explain ::= EXPLAIN QUERY PLAN */
+ 190, /* (2) cmdx ::= cmd */
+ 192, /* (3) cmd ::= BEGIN transtype trans_opt */
+ 193, /* (4) transtype ::= */
+ 193, /* (5) transtype ::= DEFERRED */
+ 193, /* (6) transtype ::= IMMEDIATE */
+ 193, /* (7) transtype ::= EXCLUSIVE */
+ 192, /* (8) cmd ::= COMMIT|END trans_opt */
+ 192, /* (9) cmd ::= ROLLBACK trans_opt */
+ 192, /* (10) cmd ::= SAVEPOINT nm */
+ 192, /* (11) cmd ::= RELEASE savepoint_opt nm */
+ 192, /* (12) cmd ::= ROLLBACK trans_opt TO savepoint_opt nm */
+ 197, /* (13) create_table ::= createkw temp TABLE ifnotexists nm dbnm */
+ 199, /* (14) createkw ::= CREATE */
+ 201, /* (15) ifnotexists ::= */
+ 201, /* (16) ifnotexists ::= IF NOT EXISTS */
+ 200, /* (17) temp ::= TEMP */
+ 200, /* (18) temp ::= */
+ 198, /* (19) create_table_args ::= LP columnlist conslist_opt RP table_option_set */
+ 198, /* (20) create_table_args ::= AS select */
+ 205, /* (21) table_option_set ::= */
+ 205, /* (22) table_option_set ::= table_option_set COMMA table_option */
+ 207, /* (23) table_option ::= WITHOUT nm */
+ 207, /* (24) table_option ::= nm */
+ 208, /* (25) columnname ::= nm typetoken */
+ 210, /* (26) typetoken ::= */
+ 210, /* (27) typetoken ::= typename LP signed RP */
+ 210, /* (28) typetoken ::= typename LP signed COMMA signed RP */
+ 211, /* (29) typename ::= typename ID|STRING */
+ 215, /* (30) scanpt ::= */
+ 216, /* (31) scantok ::= */
+ 217, /* (32) ccons ::= CONSTRAINT nm */
+ 217, /* (33) ccons ::= DEFAULT scantok term */
+ 217, /* (34) ccons ::= DEFAULT LP expr RP */
+ 217, /* (35) ccons ::= DEFAULT PLUS scantok term */
+ 217, /* (36) ccons ::= DEFAULT MINUS scantok term */
+ 217, /* (37) ccons ::= DEFAULT scantok ID|INDEXED */
+ 217, /* (38) ccons ::= NOT NULL onconf */
+ 217, /* (39) ccons ::= PRIMARY KEY sortorder onconf autoinc */
+ 217, /* (40) ccons ::= UNIQUE onconf */
+ 217, /* (41) ccons ::= CHECK LP expr RP */
+ 217, /* (42) ccons ::= REFERENCES nm eidlist_opt refargs */
+ 217, /* (43) ccons ::= defer_subclause */
+ 217, /* (44) ccons ::= COLLATE ID|STRING */
+ 226, /* (45) generated ::= LP expr RP */
+ 226, /* (46) generated ::= LP expr RP ID */
+ 222, /* (47) autoinc ::= */
+ 222, /* (48) autoinc ::= AUTOINCR */
+ 224, /* (49) refargs ::= */
+ 224, /* (50) refargs ::= refargs refarg */
+ 227, /* (51) refarg ::= MATCH nm */
+ 227, /* (52) refarg ::= ON INSERT refact */
+ 227, /* (53) refarg ::= ON DELETE refact */
+ 227, /* (54) refarg ::= ON UPDATE refact */
+ 228, /* (55) refact ::= SET NULL */
+ 228, /* (56) refact ::= SET DEFAULT */
+ 228, /* (57) refact ::= CASCADE */
+ 228, /* (58) refact ::= RESTRICT */
+ 228, /* (59) refact ::= NO ACTION */
+ 225, /* (60) defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
+ 225, /* (61) defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
+ 229, /* (62) init_deferred_pred_opt ::= */
+ 229, /* (63) init_deferred_pred_opt ::= INITIALLY DEFERRED */
+ 229, /* (64) init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
+ 204, /* (65) conslist_opt ::= */
+ 231, /* (66) tconscomma ::= COMMA */
+ 232, /* (67) tcons ::= CONSTRAINT nm */
+ 232, /* (68) tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
+ 232, /* (69) tcons ::= UNIQUE LP sortlist RP onconf */
+ 232, /* (70) tcons ::= CHECK LP expr RP onconf */
+ 232, /* (71) tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
+ 235, /* (72) defer_subclause_opt ::= */
+ 220, /* (73) onconf ::= */
+ 220, /* (74) onconf ::= ON CONFLICT resolvetype */
+ 236, /* (75) orconf ::= */
+ 236, /* (76) orconf ::= OR resolvetype */
+ 237, /* (77) resolvetype ::= IGNORE */
+ 237, /* (78) resolvetype ::= REPLACE */
+ 192, /* (79) cmd ::= DROP TABLE ifexists fullname */
+ 239, /* (80) ifexists ::= IF EXISTS */
+ 239, /* (81) ifexists ::= */
+ 192, /* (82) cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
+ 192, /* (83) cmd ::= DROP VIEW ifexists fullname */
+ 192, /* (84) cmd ::= select */
+ 206, /* (85) select ::= WITH wqlist selectnowith */
+ 206, /* (86) select ::= WITH RECURSIVE wqlist selectnowith */
+ 206, /* (87) select ::= selectnowith */
+ 241, /* (88) selectnowith ::= selectnowith multiselect_op oneselect */
+ 244, /* (89) multiselect_op ::= UNION */
+ 244, /* (90) multiselect_op ::= UNION ALL */
+ 244, /* (91) multiselect_op ::= EXCEPT|INTERSECT */
+ 242, /* (92) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
+ 242, /* (93) oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
+ 254, /* (94) values ::= VALUES LP nexprlist RP */
+ 242, /* (95) oneselect ::= mvalues */
+ 256, /* (96) mvalues ::= values COMMA LP nexprlist RP */
+ 256, /* (97) mvalues ::= mvalues COMMA LP nexprlist RP */
+ 245, /* (98) distinct ::= DISTINCT */
+ 245, /* (99) distinct ::= ALL */
+ 245, /* (100) distinct ::= */
+ 257, /* (101) sclp ::= */
+ 246, /* (102) selcollist ::= sclp scanpt expr scanpt as */
+ 246, /* (103) selcollist ::= sclp scanpt STAR */
+ 246, /* (104) selcollist ::= sclp scanpt nm DOT STAR */
+ 258, /* (105) as ::= AS nm */
+ 258, /* (106) as ::= */
+ 247, /* (107) from ::= */
+ 247, /* (108) from ::= FROM seltablist */
+ 260, /* (109) stl_prefix ::= seltablist joinop */
+ 260, /* (110) stl_prefix ::= */
+ 259, /* (111) seltablist ::= stl_prefix nm dbnm as on_using */
+ 259, /* (112) seltablist ::= stl_prefix nm dbnm as indexed_by on_using */
+ 259, /* (113) seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */
+ 259, /* (114) seltablist ::= stl_prefix LP select RP as on_using */
+ 259, /* (115) seltablist ::= stl_prefix LP seltablist RP as on_using */
+ 202, /* (116) dbnm ::= */
+ 202, /* (117) dbnm ::= DOT nm */
+ 240, /* (118) fullname ::= nm */
+ 240, /* (119) fullname ::= nm DOT nm */
+ 265, /* (120) xfullname ::= nm */
+ 265, /* (121) xfullname ::= nm DOT nm */
+ 265, /* (122) xfullname ::= nm DOT nm AS nm */
+ 265, /* (123) xfullname ::= nm AS nm */
+ 261, /* (124) joinop ::= COMMA|JOIN */
+ 261, /* (125) joinop ::= JOIN_KW JOIN */
+ 261, /* (126) joinop ::= JOIN_KW nm JOIN */
+ 261, /* (127) joinop ::= JOIN_KW nm nm JOIN */
+ 262, /* (128) on_using ::= ON expr */
+ 262, /* (129) on_using ::= USING LP idlist RP */
+ 262, /* (130) on_using ::= */
+ 267, /* (131) indexed_opt ::= */
+ 263, /* (132) indexed_by ::= INDEXED BY nm */
+ 263, /* (133) indexed_by ::= NOT INDEXED */
+ 251, /* (134) orderby_opt ::= */
+ 251, /* (135) orderby_opt ::= ORDER BY sortlist */
+ 233, /* (136) sortlist ::= sortlist COMMA expr sortorder nulls */
+ 233, /* (137) sortlist ::= expr sortorder nulls */
+ 221, /* (138) sortorder ::= ASC */
+ 221, /* (139) sortorder ::= DESC */
+ 221, /* (140) sortorder ::= */
+ 268, /* (141) nulls ::= NULLS FIRST */
+ 268, /* (142) nulls ::= NULLS LAST */
+ 268, /* (143) nulls ::= */
+ 249, /* (144) groupby_opt ::= */
+ 249, /* (145) groupby_opt ::= GROUP BY nexprlist */
+ 250, /* (146) having_opt ::= */
+ 250, /* (147) having_opt ::= HAVING expr */
+ 252, /* (148) limit_opt ::= */
+ 252, /* (149) limit_opt ::= LIMIT expr */
+ 252, /* (150) limit_opt ::= LIMIT expr OFFSET expr */
+ 252, /* (151) limit_opt ::= LIMIT expr COMMA expr */
+ 192, /* (152) cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */
+ 248, /* (153) where_opt ::= */
+ 248, /* (154) where_opt ::= WHERE expr */
+ 270, /* (155) where_opt_ret ::= */
+ 270, /* (156) where_opt_ret ::= WHERE expr */
+ 270, /* (157) where_opt_ret ::= RETURNING selcollist */
+ 270, /* (158) where_opt_ret ::= WHERE expr RETURNING selcollist */
+ 192, /* (159) cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */
+ 271, /* (160) setlist ::= setlist COMMA nm EQ expr */
+ 271, /* (161) setlist ::= setlist COMMA LP idlist RP EQ expr */
+ 271, /* (162) setlist ::= nm EQ expr */
+ 271, /* (163) setlist ::= LP idlist RP EQ expr */
+ 192, /* (164) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
+ 192, /* (165) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */
+ 274, /* (166) upsert ::= */
+ 274, /* (167) upsert ::= RETURNING selcollist */
+ 274, /* (168) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */
+ 274, /* (169) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */
+ 274, /* (170) upsert ::= ON CONFLICT DO NOTHING returning */
+ 274, /* (171) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */
+ 275, /* (172) returning ::= RETURNING selcollist */
+ 272, /* (173) insert_cmd ::= INSERT orconf */
+ 272, /* (174) insert_cmd ::= REPLACE */
+ 273, /* (175) idlist_opt ::= */
+ 273, /* (176) idlist_opt ::= LP idlist RP */
+ 266, /* (177) idlist ::= idlist COMMA nm */
+ 266, /* (178) idlist ::= nm */
+ 219, /* (179) expr ::= LP expr RP */
+ 219, /* (180) expr ::= ID|INDEXED|JOIN_KW */
+ 219, /* (181) expr ::= nm DOT nm */
+ 219, /* (182) expr ::= nm DOT nm DOT nm */
+ 218, /* (183) term ::= NULL|FLOAT|BLOB */
+ 218, /* (184) term ::= STRING */
+ 218, /* (185) term ::= INTEGER */
+ 219, /* (186) expr ::= VARIABLE */
+ 219, /* (187) expr ::= expr COLLATE ID|STRING */
+ 219, /* (188) expr ::= CAST LP expr AS typetoken RP */
+ 219, /* (189) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */
+ 219, /* (190) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */
+ 219, /* (191) expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
+ 219, /* (192) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
+ 219, /* (193) expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */
+ 219, /* (194) expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
+ 218, /* (195) term ::= CTIME_KW */
+ 219, /* (196) expr ::= LP nexprlist COMMA expr RP */
+ 219, /* (197) expr ::= expr AND expr */
+ 219, /* (198) expr ::= expr OR expr */
+ 219, /* (199) expr ::= expr LT|GT|GE|LE expr */
+ 219, /* (200) expr ::= expr EQ|NE expr */
+ 219, /* (201) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
+ 219, /* (202) expr ::= expr PLUS|MINUS expr */
+ 219, /* (203) expr ::= expr STAR|SLASH|REM expr */
+ 219, /* (204) expr ::= expr CONCAT expr */
+ 277, /* (205) likeop ::= NOT LIKE_KW|MATCH */
+ 219, /* (206) expr ::= expr likeop expr */
+ 219, /* (207) expr ::= expr likeop expr ESCAPE expr */
+ 219, /* (208) expr ::= expr ISNULL|NOTNULL */
+ 219, /* (209) expr ::= expr NOT NULL */
+ 219, /* (210) expr ::= expr IS expr */
+ 219, /* (211) expr ::= expr IS NOT expr */
+ 219, /* (212) expr ::= expr IS NOT DISTINCT FROM expr */
+ 219, /* (213) expr ::= expr IS DISTINCT FROM expr */
+ 219, /* (214) expr ::= NOT expr */
+ 219, /* (215) expr ::= BITNOT expr */
+ 219, /* (216) expr ::= PLUS|MINUS expr */
+ 219, /* (217) expr ::= expr PTR expr */
+ 278, /* (218) between_op ::= BETWEEN */
+ 278, /* (219) between_op ::= NOT BETWEEN */
+ 219, /* (220) expr ::= expr between_op expr AND expr */
+ 279, /* (221) in_op ::= IN */
+ 279, /* (222) in_op ::= NOT IN */
+ 219, /* (223) expr ::= expr in_op LP exprlist RP */
+ 219, /* (224) expr ::= LP select RP */
+ 219, /* (225) expr ::= expr in_op LP select RP */
+ 219, /* (226) expr ::= expr in_op nm dbnm paren_exprlist */
+ 219, /* (227) expr ::= EXISTS LP select RP */
+ 219, /* (228) expr ::= CASE case_operand case_exprlist case_else END */
+ 282, /* (229) case_exprlist ::= case_exprlist WHEN expr THEN expr */
+ 282, /* (230) case_exprlist ::= WHEN expr THEN expr */
+ 283, /* (231) case_else ::= ELSE expr */
+ 283, /* (232) case_else ::= */
+ 281, /* (233) case_operand ::= */
+ 264, /* (234) exprlist ::= */
+ 255, /* (235) nexprlist ::= nexprlist COMMA expr */
+ 255, /* (236) nexprlist ::= expr */
+ 280, /* (237) paren_exprlist ::= */
+ 280, /* (238) paren_exprlist ::= LP exprlist RP */
+ 192, /* (239) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
+ 284, /* (240) uniqueflag ::= UNIQUE */
+ 284, /* (241) uniqueflag ::= */
+ 223, /* (242) eidlist_opt ::= */
+ 223, /* (243) eidlist_opt ::= LP eidlist RP */
+ 234, /* (244) eidlist ::= eidlist COMMA nm collate sortorder */
+ 234, /* (245) eidlist ::= nm collate sortorder */
+ 285, /* (246) collate ::= */
+ 285, /* (247) collate ::= COLLATE ID|STRING */
+ 192, /* (248) cmd ::= DROP INDEX ifexists fullname */
+ 192, /* (249) cmd ::= VACUUM vinto */
+ 192, /* (250) cmd ::= VACUUM nm vinto */
+ 286, /* (251) vinto ::= INTO expr */
+ 286, /* (252) vinto ::= */
+ 192, /* (253) cmd ::= PRAGMA nm dbnm */
+ 192, /* (254) cmd ::= PRAGMA nm dbnm EQ nmnum */
+ 192, /* (255) cmd ::= PRAGMA nm dbnm LP nmnum RP */
+ 192, /* (256) cmd ::= PRAGMA nm dbnm EQ minus_num */
+ 192, /* (257) cmd ::= PRAGMA nm dbnm LP minus_num RP */
+ 213, /* (258) plus_num ::= PLUS INTEGER|FLOAT */
+ 214, /* (259) minus_num ::= MINUS INTEGER|FLOAT */
+ 192, /* (260) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
+ 288, /* (261) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
+ 290, /* (262) trigger_time ::= BEFORE|AFTER */
+ 290, /* (263) trigger_time ::= INSTEAD OF */
+ 290, /* (264) trigger_time ::= */
+ 291, /* (265) trigger_event ::= DELETE|INSERT */
+ 291, /* (266) trigger_event ::= UPDATE */
+ 291, /* (267) trigger_event ::= UPDATE OF idlist */
+ 293, /* (268) when_clause ::= */
+ 293, /* (269) when_clause ::= WHEN expr */
+ 289, /* (270) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
+ 289, /* (271) trigger_cmd_list ::= trigger_cmd SEMI */
+ 295, /* (272) trnm ::= nm DOT nm */
+ 296, /* (273) tridxby ::= INDEXED BY nm */
+ 296, /* (274) tridxby ::= NOT INDEXED */
+ 294, /* (275) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
+ 294, /* (276) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
+ 294, /* (277) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
+ 294, /* (278) trigger_cmd ::= scanpt select scanpt */
+ 219, /* (279) expr ::= RAISE LP IGNORE RP */
+ 219, /* (280) expr ::= RAISE LP raisetype COMMA expr RP */
+ 238, /* (281) raisetype ::= ROLLBACK */
+ 238, /* (282) raisetype ::= ABORT */
+ 238, /* (283) raisetype ::= FAIL */
+ 192, /* (284) cmd ::= DROP TRIGGER ifexists fullname */
+ 192, /* (285) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
+ 192, /* (286) cmd ::= DETACH database_kw_opt expr */
+ 298, /* (287) key_opt ::= */
+ 298, /* (288) key_opt ::= KEY expr */
+ 192, /* (289) cmd ::= REINDEX */
+ 192, /* (290) cmd ::= REINDEX nm dbnm */
+ 192, /* (291) cmd ::= ANALYZE */
+ 192, /* (292) cmd ::= ANALYZE nm dbnm */
+ 192, /* (293) cmd ::= ALTER TABLE fullname RENAME TO nm */
+ 192, /* (294) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
+ 192, /* (295) cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
+ 299, /* (296) add_column_fullname ::= fullname */
+ 192, /* (297) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
+ 192, /* (298) cmd ::= create_vtab */
+ 192, /* (299) cmd ::= create_vtab LP vtabarglist RP */
+ 301, /* (300) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
+ 303, /* (301) vtabarg ::= */
+ 304, /* (302) vtabargtoken ::= ANY */
+ 304, /* (303) vtabargtoken ::= lp anylist RP */
+ 305, /* (304) lp ::= LP */
+ 269, /* (305) with ::= WITH wqlist */
+ 269, /* (306) with ::= WITH RECURSIVE wqlist */
+ 308, /* (307) wqas ::= AS */
+ 308, /* (308) wqas ::= AS MATERIALIZED */
+ 308, /* (309) wqas ::= AS NOT MATERIALIZED */
+ 307, /* (310) wqitem ::= withnm eidlist_opt wqas LP select RP */
+ 309, /* (311) withnm ::= nm */
+ 243, /* (312) wqlist ::= wqitem */
+ 243, /* (313) wqlist ::= wqlist COMMA wqitem */
+ 310, /* (314) windowdefn_list ::= windowdefn_list COMMA windowdefn */
+ 311, /* (315) windowdefn ::= nm AS LP window RP */
+ 312, /* (316) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
+ 312, /* (317) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
+ 312, /* (318) window ::= ORDER BY sortlist frame_opt */
+ 312, /* (319) window ::= nm ORDER BY sortlist frame_opt */
+ 312, /* (320) window ::= nm frame_opt */
+ 313, /* (321) frame_opt ::= */
+ 313, /* (322) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
+ 313, /* (323) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
+ 317, /* (324) range_or_rows ::= RANGE|ROWS|GROUPS */
+ 319, /* (325) frame_bound_s ::= frame_bound */
+ 319, /* (326) frame_bound_s ::= UNBOUNDED PRECEDING */
+ 320, /* (327) frame_bound_e ::= frame_bound */
+ 320, /* (328) frame_bound_e ::= UNBOUNDED FOLLOWING */
+ 318, /* (329) frame_bound ::= expr PRECEDING|FOLLOWING */
+ 318, /* (330) frame_bound ::= CURRENT ROW */
+ 321, /* (331) frame_exclude_opt ::= */
+ 321, /* (332) frame_exclude_opt ::= EXCLUDE frame_exclude */
+ 322, /* (333) frame_exclude ::= NO OTHERS */
+ 322, /* (334) frame_exclude ::= CURRENT ROW */
+ 322, /* (335) frame_exclude ::= GROUP|TIES */
+ 253, /* (336) window_clause ::= WINDOW windowdefn_list */
+ 276, /* (337) filter_over ::= filter_clause over_clause */
+ 276, /* (338) filter_over ::= over_clause */
+ 276, /* (339) filter_over ::= filter_clause */
+ 316, /* (340) over_clause ::= OVER LP window RP */
+ 316, /* (341) over_clause ::= OVER nm */
+ 315, /* (342) filter_clause ::= FILTER LP WHERE expr RP */
+ 218, /* (343) term ::= QNUMBER */
+ 187, /* (344) input ::= cmdlist */
+ 188, /* (345) cmdlist ::= cmdlist ecmd */
+ 188, /* (346) cmdlist ::= ecmd */
+ 189, /* (347) ecmd ::= SEMI */
+ 189, /* (348) ecmd ::= cmdx SEMI */
+ 189, /* (349) ecmd ::= explain cmdx SEMI */
+ 194, /* (350) trans_opt ::= */
+ 194, /* (351) trans_opt ::= TRANSACTION */
+ 194, /* (352) trans_opt ::= TRANSACTION nm */
+ 196, /* (353) savepoint_opt ::= SAVEPOINT */
+ 196, /* (354) savepoint_opt ::= */
+ 192, /* (355) cmd ::= create_table create_table_args */
+ 205, /* (356) table_option_set ::= table_option */
+ 203, /* (357) columnlist ::= columnlist COMMA columnname carglist */
+ 203, /* (358) columnlist ::= columnname carglist */
+ 195, /* (359) nm ::= ID|INDEXED|JOIN_KW */
+ 195, /* (360) nm ::= STRING */
+ 210, /* (361) typetoken ::= typename */
+ 211, /* (362) typename ::= ID|STRING */
+ 212, /* (363) signed ::= plus_num */
+ 212, /* (364) signed ::= minus_num */
+ 209, /* (365) carglist ::= carglist ccons */
+ 209, /* (366) carglist ::= */
+ 217, /* (367) ccons ::= NULL onconf */
+ 217, /* (368) ccons ::= GENERATED ALWAYS AS generated */
+ 217, /* (369) ccons ::= AS generated */
+ 204, /* (370) conslist_opt ::= COMMA conslist */
+ 230, /* (371) conslist ::= conslist tconscomma tcons */
+ 230, /* (372) conslist ::= tcons */
+ 231, /* (373) tconscomma ::= */
+ 235, /* (374) defer_subclause_opt ::= defer_subclause */
+ 237, /* (375) resolvetype ::= raisetype */
+ 241, /* (376) selectnowith ::= oneselect */
+ 242, /* (377) oneselect ::= values */
+ 257, /* (378) sclp ::= selcollist COMMA */
+ 258, /* (379) as ::= ID|STRING */
+ 267, /* (380) indexed_opt ::= indexed_by */
+ 275, /* (381) returning ::= */
+ 219, /* (382) expr ::= term */
+ 277, /* (383) likeop ::= LIKE_KW|MATCH */
+ 281, /* (384) case_operand ::= expr */
+ 264, /* (385) exprlist ::= nexprlist */
+ 287, /* (386) nmnum ::= plus_num */
+ 287, /* (387) nmnum ::= nm */
+ 287, /* (388) nmnum ::= ON */
+ 287, /* (389) nmnum ::= DELETE */
+ 287, /* (390) nmnum ::= DEFAULT */
+ 213, /* (391) plus_num ::= INTEGER|FLOAT */
+ 292, /* (392) foreach_clause ::= */
+ 292, /* (393) foreach_clause ::= FOR EACH ROW */
+ 295, /* (394) trnm ::= nm */
+ 296, /* (395) tridxby ::= */
+ 297, /* (396) database_kw_opt ::= DATABASE */
+ 297, /* (397) database_kw_opt ::= */
+ 300, /* (398) kwcolumn_opt ::= */
+ 300, /* (399) kwcolumn_opt ::= COLUMNKW */
+ 302, /* (400) vtabarglist ::= vtabarg */
+ 302, /* (401) vtabarglist ::= vtabarglist COMMA vtabarg */
+ 303, /* (402) vtabarg ::= vtabarg vtabargtoken */
+ 306, /* (403) anylist ::= */
+ 306, /* (404) anylist ::= anylist LP anylist RP */
+ 306, /* (405) anylist ::= anylist ANY */
+ 269, /* (406) with ::= */
+ 310, /* (407) windowdefn_list ::= windowdefn */
+ 312, /* (408) window ::= frame_opt */
};
/* For rule J, yyRuleInfoNRhs[J] contains the negative of the number
@@ -177464,16 +178862,16 @@ static YYACTIONTYPE yy_reduce(
{ sqlite3FinishCoding(pParse); }
break;
case 3: /* cmd ::= BEGIN transtype trans_opt */
-{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy144);}
+{sqlite3BeginTransaction(pParse, yymsp[-1].minor.yy502);}
break;
case 4: /* transtype ::= */
-{yymsp[1].minor.yy144 = TK_DEFERRED;}
+{yymsp[1].minor.yy502 = TK_DEFERRED;}
break;
case 5: /* transtype ::= DEFERRED */
case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6);
case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7);
case 324: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==324);
-{yymsp[0].minor.yy144 = yymsp[0].major; /*A-overwrites-X*/}
+{yymsp[0].minor.yy502 = yymsp[0].major; /*A-overwrites-X*/}
break;
case 8: /* cmd ::= COMMIT|END trans_opt */
case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9);
@@ -177496,11 +178894,13 @@ static YYACTIONTYPE yy_reduce(
break;
case 13: /* create_table ::= createkw temp TABLE ifnotexists nm dbnm */
{
- sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy144,0,0,yymsp[-2].minor.yy144);
+ sqlite3StartTable(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,yymsp[-4].minor.yy502,0,0,yymsp[-2].minor.yy502);
}
break;
case 14: /* createkw ::= CREATE */
-{disableLookaside(pParse);}
+{
+ disableLookaside(pParse);
+}
break;
case 15: /* ifnotexists ::= */
case 18: /* temp ::= */ yytestcase(yyruleno==18);
@@ -177510,38 +178910,38 @@ static YYACTIONTYPE yy_reduce(
case 81: /* ifexists ::= */ yytestcase(yyruleno==81);
case 100: /* distinct ::= */ yytestcase(yyruleno==100);
case 246: /* collate ::= */ yytestcase(yyruleno==246);
-{yymsp[1].minor.yy144 = 0;}
+{yymsp[1].minor.yy502 = 0;}
break;
case 16: /* ifnotexists ::= IF NOT EXISTS */
-{yymsp[-2].minor.yy144 = 1;}
+{yymsp[-2].minor.yy502 = 1;}
break;
case 17: /* temp ::= TEMP */
-{yymsp[0].minor.yy144 = pParse->db->init.busy==0;}
+{yymsp[0].minor.yy502 = pParse->db->init.busy==0;}
break;
case 19: /* create_table_args ::= LP columnlist conslist_opt RP table_option_set */
{
- sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy391,0);
+ sqlite3EndTable(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,yymsp[0].minor.yy9,0);
}
break;
case 20: /* create_table_args ::= AS select */
{
- sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy555);
- sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy555);
+ sqlite3EndTable(pParse,0,0,0,yymsp[0].minor.yy637);
+ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy637);
}
break;
case 21: /* table_option_set ::= */
-{yymsp[1].minor.yy391 = 0;}
+{yymsp[1].minor.yy9 = 0;}
break;
case 22: /* table_option_set ::= table_option_set COMMA table_option */
-{yylhsminor.yy391 = yymsp[-2].minor.yy391|yymsp[0].minor.yy391;}
- yymsp[-2].minor.yy391 = yylhsminor.yy391;
+{yylhsminor.yy9 = yymsp[-2].minor.yy9|yymsp[0].minor.yy9;}
+ yymsp[-2].minor.yy9 = yylhsminor.yy9;
break;
case 23: /* table_option ::= WITHOUT nm */
{
if( yymsp[0].minor.yy0.n==5 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"rowid",5)==0 ){
- yymsp[-1].minor.yy391 = TF_WithoutRowid | TF_NoVisibleRowid;
+ yymsp[-1].minor.yy9 = TF_WithoutRowid | TF_NoVisibleRowid;
}else{
- yymsp[-1].minor.yy391 = 0;
+ yymsp[-1].minor.yy9 = 0;
sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z);
}
}
@@ -177549,13 +178949,13 @@ static YYACTIONTYPE yy_reduce(
case 24: /* table_option ::= nm */
{
if( yymsp[0].minor.yy0.n==6 && sqlite3_strnicmp(yymsp[0].minor.yy0.z,"strict",6)==0 ){
- yylhsminor.yy391 = TF_Strict;
+ yylhsminor.yy9 = TF_Strict;
}else{
- yylhsminor.yy391 = 0;
+ yylhsminor.yy9 = 0;
sqlite3ErrorMsg(pParse, "unknown table option: %.*s", yymsp[0].minor.yy0.n, yymsp[0].minor.yy0.z);
}
}
- yymsp[0].minor.yy391 = yylhsminor.yy391;
+ yymsp[0].minor.yy9 = yylhsminor.yy9;
break;
case 25: /* columnname ::= nm typetoken */
{sqlite3AddColumn(pParse,yymsp[-1].minor.yy0,yymsp[0].minor.yy0);}
@@ -177581,7 +178981,7 @@ static YYACTIONTYPE yy_reduce(
case 30: /* scanpt ::= */
{
assert( yyLookahead!=YYNOCODE );
- yymsp[1].minor.yy168 = yyLookaheadToken.z;
+ yymsp[1].minor.yy342 = yyLookaheadToken.z;
}
break;
case 31: /* scantok ::= */
@@ -177592,20 +178992,20 @@ static YYACTIONTYPE yy_reduce(
break;
case 32: /* ccons ::= CONSTRAINT nm */
case 67: /* tcons ::= CONSTRAINT nm */ yytestcase(yyruleno==67);
-{pParse->constraintName = yymsp[0].minor.yy0;}
+{ASSERT_IS_CREATE; pParse->u1.cr.constraintName = yymsp[0].minor.yy0;}
break;
case 33: /* ccons ::= DEFAULT scantok term */
-{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy454,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
+{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy590,yymsp[-1].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
break;
case 34: /* ccons ::= DEFAULT LP expr RP */
-{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy454,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);}
+{sqlite3AddDefaultValue(pParse,yymsp[-1].minor.yy590,yymsp[-2].minor.yy0.z+1,yymsp[0].minor.yy0.z);}
break;
case 35: /* ccons ::= DEFAULT PLUS scantok term */
-{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy454,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
+{sqlite3AddDefaultValue(pParse,yymsp[0].minor.yy590,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);}
break;
case 36: /* ccons ::= DEFAULT MINUS scantok term */
{
- Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy454, 0);
+ Expr *p = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy590, 0);
sqlite3AddDefaultValue(pParse,p,yymsp[-2].minor.yy0.z,&yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]);
}
break;
@@ -177620,151 +179020,155 @@ static YYACTIONTYPE yy_reduce(
}
break;
case 38: /* ccons ::= NOT NULL onconf */
-{sqlite3AddNotNull(pParse, yymsp[0].minor.yy144);}
+{sqlite3AddNotNull(pParse, yymsp[0].minor.yy502);}
break;
case 39: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */
-{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy144,yymsp[0].minor.yy144,yymsp[-2].minor.yy144);}
+{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy502,yymsp[0].minor.yy502,yymsp[-2].minor.yy502);}
break;
case 40: /* ccons ::= UNIQUE onconf */
-{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy144,0,0,0,0,
+{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy502,0,0,0,0,
SQLITE_IDXTYPE_UNIQUE);}
break;
case 41: /* ccons ::= CHECK LP expr RP */
-{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy454,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy0.z);}
+{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy590,yymsp[-2].minor.yy0.z,yymsp[0].minor.yy0.z);}
break;
case 42: /* ccons ::= REFERENCES nm eidlist_opt refargs */
-{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy14,yymsp[0].minor.yy144);}
+{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy402,yymsp[0].minor.yy502);}
break;
case 43: /* ccons ::= defer_subclause */
-{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy144);}
+{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy502);}
break;
case 44: /* ccons ::= COLLATE ID|STRING */
{sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);}
break;
case 45: /* generated ::= LP expr RP */
-{sqlite3AddGenerated(pParse,yymsp[-1].minor.yy454,0);}
+{sqlite3AddGenerated(pParse,yymsp[-1].minor.yy590,0);}
break;
case 46: /* generated ::= LP expr RP ID */
-{sqlite3AddGenerated(pParse,yymsp[-2].minor.yy454,&yymsp[0].minor.yy0);}
+{sqlite3AddGenerated(pParse,yymsp[-2].minor.yy590,&yymsp[0].minor.yy0);}
break;
case 48: /* autoinc ::= AUTOINCR */
-{yymsp[0].minor.yy144 = 1;}
+{yymsp[0].minor.yy502 = 1;}
break;
case 49: /* refargs ::= */
-{ yymsp[1].minor.yy144 = OE_None*0x0101; /* EV: R-19803-45884 */}
+{ yymsp[1].minor.yy502 = OE_None*0x0101; /* EV: R-19803-45884 */}
break;
case 50: /* refargs ::= refargs refarg */
-{ yymsp[-1].minor.yy144 = (yymsp[-1].minor.yy144 & ~yymsp[0].minor.yy383.mask) | yymsp[0].minor.yy383.value; }
+{ yymsp[-1].minor.yy502 = (yymsp[-1].minor.yy502 & ~yymsp[0].minor.yy481.mask) | yymsp[0].minor.yy481.value; }
break;
case 51: /* refarg ::= MATCH nm */
-{ yymsp[-1].minor.yy383.value = 0; yymsp[-1].minor.yy383.mask = 0x000000; }
+{ yymsp[-1].minor.yy481.value = 0; yymsp[-1].minor.yy481.mask = 0x000000; }
break;
case 52: /* refarg ::= ON INSERT refact */
-{ yymsp[-2].minor.yy383.value = 0; yymsp[-2].minor.yy383.mask = 0x000000; }
+{ yymsp[-2].minor.yy481.value = 0; yymsp[-2].minor.yy481.mask = 0x000000; }
break;
case 53: /* refarg ::= ON DELETE refact */
-{ yymsp[-2].minor.yy383.value = yymsp[0].minor.yy144; yymsp[-2].minor.yy383.mask = 0x0000ff; }
+{ yymsp[-2].minor.yy481.value = yymsp[0].minor.yy502; yymsp[-2].minor.yy481.mask = 0x0000ff; }
break;
case 54: /* refarg ::= ON UPDATE refact */
-{ yymsp[-2].minor.yy383.value = yymsp[0].minor.yy144<<8; yymsp[-2].minor.yy383.mask = 0x00ff00; }
+{ yymsp[-2].minor.yy481.value = yymsp[0].minor.yy502<<8; yymsp[-2].minor.yy481.mask = 0x00ff00; }
break;
case 55: /* refact ::= SET NULL */
-{ yymsp[-1].minor.yy144 = OE_SetNull; /* EV: R-33326-45252 */}
+{ yymsp[-1].minor.yy502 = OE_SetNull; /* EV: R-33326-45252 */}
break;
case 56: /* refact ::= SET DEFAULT */
-{ yymsp[-1].minor.yy144 = OE_SetDflt; /* EV: R-33326-45252 */}
+{ yymsp[-1].minor.yy502 = OE_SetDflt; /* EV: R-33326-45252 */}
break;
case 57: /* refact ::= CASCADE */
-{ yymsp[0].minor.yy144 = OE_Cascade; /* EV: R-33326-45252 */}
+{ yymsp[0].minor.yy502 = OE_Cascade; /* EV: R-33326-45252 */}
break;
case 58: /* refact ::= RESTRICT */
-{ yymsp[0].minor.yy144 = OE_Restrict; /* EV: R-33326-45252 */}
+{ yymsp[0].minor.yy502 = OE_Restrict; /* EV: R-33326-45252 */}
break;
case 59: /* refact ::= NO ACTION */
-{ yymsp[-1].minor.yy144 = OE_None; /* EV: R-33326-45252 */}
+{ yymsp[-1].minor.yy502 = OE_None; /* EV: R-33326-45252 */}
break;
case 60: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
-{yymsp[-2].minor.yy144 = 0;}
+{yymsp[-2].minor.yy502 = 0;}
break;
case 61: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
case 76: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==76);
case 173: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==173);
-{yymsp[-1].minor.yy144 = yymsp[0].minor.yy144;}
+{yymsp[-1].minor.yy502 = yymsp[0].minor.yy502;}
break;
case 63: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */
case 80: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==80);
case 219: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==219);
case 222: /* in_op ::= NOT IN */ yytestcase(yyruleno==222);
case 247: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==247);
-{yymsp[-1].minor.yy144 = 1;}
+{yymsp[-1].minor.yy502 = 1;}
break;
case 64: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
-{yymsp[-1].minor.yy144 = 0;}
+{yymsp[-1].minor.yy502 = 0;}
break;
case 66: /* tconscomma ::= COMMA */
-{pParse->constraintName.n = 0;}
+{ASSERT_IS_CREATE; pParse->u1.cr.constraintName.n = 0;}
break;
case 68: /* tcons ::= PRIMARY KEY LP sortlist autoinc RP onconf */
-{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy14,yymsp[0].minor.yy144,yymsp[-2].minor.yy144,0);}
+{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy402,yymsp[0].minor.yy502,yymsp[-2].minor.yy502,0);}
break;
case 69: /* tcons ::= UNIQUE LP sortlist RP onconf */
-{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy14,yymsp[0].minor.yy144,0,0,0,0,
+{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy402,yymsp[0].minor.yy502,0,0,0,0,
SQLITE_IDXTYPE_UNIQUE);}
break;
case 70: /* tcons ::= CHECK LP expr RP onconf */
-{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy454,yymsp[-3].minor.yy0.z,yymsp[-1].minor.yy0.z);}
+{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy590,yymsp[-3].minor.yy0.z,yymsp[-1].minor.yy0.z);}
break;
case 71: /* tcons ::= FOREIGN KEY LP eidlist RP REFERENCES nm eidlist_opt refargs defer_subclause_opt */
{
- sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy14, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy14, yymsp[-1].minor.yy144);
- sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy144);
+ sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy402, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy402, yymsp[-1].minor.yy502);
+ sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy502);
}
break;
case 73: /* onconf ::= */
case 75: /* orconf ::= */ yytestcase(yyruleno==75);
-{yymsp[1].minor.yy144 = OE_Default;}
+{yymsp[1].minor.yy502 = OE_Default;}
break;
case 74: /* onconf ::= ON CONFLICT resolvetype */
-{yymsp[-2].minor.yy144 = yymsp[0].minor.yy144;}
+{yymsp[-2].minor.yy502 = yymsp[0].minor.yy502;}
break;
case 77: /* resolvetype ::= IGNORE */
-{yymsp[0].minor.yy144 = OE_Ignore;}
+{yymsp[0].minor.yy502 = OE_Ignore;}
break;
case 78: /* resolvetype ::= REPLACE */
case 174: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==174);
-{yymsp[0].minor.yy144 = OE_Replace;}
+{yymsp[0].minor.yy502 = OE_Replace;}
break;
case 79: /* cmd ::= DROP TABLE ifexists fullname */
{
- sqlite3DropTable(pParse, yymsp[0].minor.yy203, 0, yymsp[-1].minor.yy144);
+ sqlite3DropTable(pParse, yymsp[0].minor.yy563, 0, yymsp[-1].minor.yy502);
}
break;
case 82: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm eidlist_opt AS select */
{
- sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy14, yymsp[0].minor.yy555, yymsp[-7].minor.yy144, yymsp[-5].minor.yy144);
+ sqlite3CreateView(pParse, &yymsp[-8].minor.yy0, &yymsp[-4].minor.yy0, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy402, yymsp[0].minor.yy637, yymsp[-7].minor.yy502, yymsp[-5].minor.yy502);
}
break;
case 83: /* cmd ::= DROP VIEW ifexists fullname */
{
- sqlite3DropTable(pParse, yymsp[0].minor.yy203, 1, yymsp[-1].minor.yy144);
+ sqlite3DropTable(pParse, yymsp[0].minor.yy563, 1, yymsp[-1].minor.yy502);
}
break;
case 84: /* cmd ::= select */
{
SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0, 0};
- sqlite3Select(pParse, yymsp[0].minor.yy555, &dest);
- sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy555);
+ if( (pParse->db->mDbFlags & DBFLAG_EncodingFixed)!=0
+ || sqlite3ReadSchema(pParse)==SQLITE_OK
+ ){
+ sqlite3Select(pParse, yymsp[0].minor.yy637, &dest);
+ }
+ sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy637);
}
break;
case 85: /* select ::= WITH wqlist selectnowith */
-{yymsp[-2].minor.yy555 = attachWithToSelect(pParse,yymsp[0].minor.yy555,yymsp[-1].minor.yy59);}
+{yymsp[-2].minor.yy637 = attachWithToSelect(pParse,yymsp[0].minor.yy637,yymsp[-1].minor.yy125);}
break;
case 86: /* select ::= WITH RECURSIVE wqlist selectnowith */
-{yymsp[-3].minor.yy555 = attachWithToSelect(pParse,yymsp[0].minor.yy555,yymsp[-1].minor.yy59);}
+{yymsp[-3].minor.yy637 = attachWithToSelect(pParse,yymsp[0].minor.yy637,yymsp[-1].minor.yy125);}
break;
case 87: /* select ::= selectnowith */
{
- Select *p = yymsp[0].minor.yy555;
+ Select *p = yymsp[0].minor.yy637;
if( p ){
parserDoubleLinkSelect(pParse, p);
}
@@ -177772,8 +179176,8 @@ static YYACTIONTYPE yy_reduce(
break;
case 88: /* selectnowith ::= selectnowith multiselect_op oneselect */
{
- Select *pRhs = yymsp[0].minor.yy555;
- Select *pLhs = yymsp[-2].minor.yy555;
+ Select *pRhs = yymsp[0].minor.yy637;
+ Select *pLhs = yymsp[-2].minor.yy637;
if( pRhs && pRhs->pPrior ){
SrcList *pFrom;
Token x;
@@ -177783,60 +179187,60 @@ static YYACTIONTYPE yy_reduce(
pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0);
}
if( pRhs ){
- pRhs->op = (u8)yymsp[-1].minor.yy144;
+ pRhs->op = (u8)yymsp[-1].minor.yy502;
pRhs->pPrior = pLhs;
- if( ALWAYS(pLhs) ) pLhs->selFlags &= ~SF_MultiValue;
- pRhs->selFlags &= ~SF_MultiValue;
- if( yymsp[-1].minor.yy144!=TK_ALL ) pParse->hasCompound = 1;
+ if( ALWAYS(pLhs) ) pLhs->selFlags &= ~(u32)SF_MultiValue;
+ pRhs->selFlags &= ~(u32)SF_MultiValue;
+ if( yymsp[-1].minor.yy502!=TK_ALL ) pParse->hasCompound = 1;
}else{
sqlite3SelectDelete(pParse->db, pLhs);
}
- yymsp[-2].minor.yy555 = pRhs;
+ yymsp[-2].minor.yy637 = pRhs;
}
break;
case 89: /* multiselect_op ::= UNION */
case 91: /* multiselect_op ::= EXCEPT|INTERSECT */ yytestcase(yyruleno==91);
-{yymsp[0].minor.yy144 = yymsp[0].major; /*A-overwrites-OP*/}
+{yymsp[0].minor.yy502 = yymsp[0].major; /*A-overwrites-OP*/}
break;
case 90: /* multiselect_op ::= UNION ALL */
-{yymsp[-1].minor.yy144 = TK_ALL;}
+{yymsp[-1].minor.yy502 = TK_ALL;}
break;
case 92: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */
{
- yymsp[-8].minor.yy555 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy14,yymsp[-5].minor.yy203,yymsp[-4].minor.yy454,yymsp[-3].minor.yy14,yymsp[-2].minor.yy454,yymsp[-1].minor.yy14,yymsp[-7].minor.yy144,yymsp[0].minor.yy454);
+ yymsp[-8].minor.yy637 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy402,yymsp[-5].minor.yy563,yymsp[-4].minor.yy590,yymsp[-3].minor.yy402,yymsp[-2].minor.yy590,yymsp[-1].minor.yy402,yymsp[-7].minor.yy502,yymsp[0].minor.yy590);
}
break;
case 93: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt window_clause orderby_opt limit_opt */
{
- yymsp[-9].minor.yy555 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy14,yymsp[-6].minor.yy203,yymsp[-5].minor.yy454,yymsp[-4].minor.yy14,yymsp[-3].minor.yy454,yymsp[-1].minor.yy14,yymsp[-8].minor.yy144,yymsp[0].minor.yy454);
- if( yymsp[-9].minor.yy555 ){
- yymsp[-9].minor.yy555->pWinDefn = yymsp[-2].minor.yy211;
+ yymsp[-9].minor.yy637 = sqlite3SelectNew(pParse,yymsp[-7].minor.yy402,yymsp[-6].minor.yy563,yymsp[-5].minor.yy590,yymsp[-4].minor.yy402,yymsp[-3].minor.yy590,yymsp[-1].minor.yy402,yymsp[-8].minor.yy502,yymsp[0].minor.yy590);
+ if( yymsp[-9].minor.yy637 ){
+ yymsp[-9].minor.yy637->pWinDefn = yymsp[-2].minor.yy483;
}else{
- sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy211);
+ sqlite3WindowListDelete(pParse->db, yymsp[-2].minor.yy483);
}
}
break;
case 94: /* values ::= VALUES LP nexprlist RP */
{
- yymsp[-3].minor.yy555 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy14,0,0,0,0,0,SF_Values,0);
+ yymsp[-3].minor.yy637 = sqlite3SelectNew(pParse,yymsp[-1].minor.yy402,0,0,0,0,0,SF_Values,0);
}
break;
case 95: /* oneselect ::= mvalues */
{
- sqlite3MultiValuesEnd(pParse, yymsp[0].minor.yy555);
+ sqlite3MultiValuesEnd(pParse, yymsp[0].minor.yy637);
}
break;
case 96: /* mvalues ::= values COMMA LP nexprlist RP */
case 97: /* mvalues ::= mvalues COMMA LP nexprlist RP */ yytestcase(yyruleno==97);
{
- yymsp[-4].minor.yy555 = sqlite3MultiValues(pParse, yymsp[-4].minor.yy555, yymsp[-1].minor.yy14);
+ yymsp[-4].minor.yy637 = sqlite3MultiValues(pParse, yymsp[-4].minor.yy637, yymsp[-1].minor.yy402);
}
break;
case 98: /* distinct ::= DISTINCT */
-{yymsp[0].minor.yy144 = SF_Distinct;}
+{yymsp[0].minor.yy502 = SF_Distinct;}
break;
case 99: /* distinct ::= ALL */
-{yymsp[0].minor.yy144 = SF_All;}
+{yymsp[0].minor.yy502 = SF_All;}
break;
case 101: /* sclp ::= */
case 134: /* orderby_opt ::= */ yytestcase(yyruleno==134);
@@ -177844,20 +179248,20 @@ static YYACTIONTYPE yy_reduce(
case 234: /* exprlist ::= */ yytestcase(yyruleno==234);
case 237: /* paren_exprlist ::= */ yytestcase(yyruleno==237);
case 242: /* eidlist_opt ::= */ yytestcase(yyruleno==242);
-{yymsp[1].minor.yy14 = 0;}
+{yymsp[1].minor.yy402 = 0;}
break;
case 102: /* selcollist ::= sclp scanpt expr scanpt as */
{
- yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy14, yymsp[-2].minor.yy454);
- if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy14, &yymsp[0].minor.yy0, 1);
- sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy14,yymsp[-3].minor.yy168,yymsp[-1].minor.yy168);
+ yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy402, yymsp[-2].minor.yy590);
+ if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy402, &yymsp[0].minor.yy0, 1);
+ sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy402,yymsp[-3].minor.yy342,yymsp[-1].minor.yy342);
}
break;
case 103: /* selcollist ::= sclp scanpt STAR */
{
Expr *p = sqlite3Expr(pParse->db, TK_ASTERISK, 0);
sqlite3ExprSetErrorOffset(p, (int)(yymsp[0].minor.yy0.z - pParse->zTail));
- yymsp[-2].minor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy14, p);
+ yymsp[-2].minor.yy402 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy402, p);
}
break;
case 104: /* selcollist ::= sclp scanpt nm DOT STAR */
@@ -177867,7 +179271,7 @@ static YYACTIONTYPE yy_reduce(
sqlite3ExprSetErrorOffset(pRight, (int)(yymsp[0].minor.yy0.z - pParse->zTail));
pLeft = tokenExpr(pParse, TK_ID, yymsp[-2].minor.yy0);
pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight);
- yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, pDot);
+ yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy402, pDot);
}
break;
case 105: /* as ::= AS nm */
@@ -177878,50 +179282,50 @@ static YYACTIONTYPE yy_reduce(
break;
case 107: /* from ::= */
case 110: /* stl_prefix ::= */ yytestcase(yyruleno==110);
-{yymsp[1].minor.yy203 = 0;}
+{yymsp[1].minor.yy563 = 0;}
break;
case 108: /* from ::= FROM seltablist */
{
- yymsp[-1].minor.yy203 = yymsp[0].minor.yy203;
- sqlite3SrcListShiftJoinType(pParse,yymsp[-1].minor.yy203);
+ yymsp[-1].minor.yy563 = yymsp[0].minor.yy563;
+ sqlite3SrcListShiftJoinType(pParse,yymsp[-1].minor.yy563);
}
break;
case 109: /* stl_prefix ::= seltablist joinop */
{
- if( ALWAYS(yymsp[-1].minor.yy203 && yymsp[-1].minor.yy203->nSrc>0) ) yymsp[-1].minor.yy203->a[yymsp[-1].minor.yy203->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy144;
+ if( ALWAYS(yymsp[-1].minor.yy563 && yymsp[-1].minor.yy563->nSrc>0) ) yymsp[-1].minor.yy563->a[yymsp[-1].minor.yy563->nSrc-1].fg.jointype = (u8)yymsp[0].minor.yy502;
}
break;
case 111: /* seltablist ::= stl_prefix nm dbnm as on_using */
{
- yymsp[-4].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-4].minor.yy203,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy269);
+ yymsp[-4].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-4].minor.yy563,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy421);
}
break;
case 112: /* seltablist ::= stl_prefix nm dbnm as indexed_by on_using */
{
- yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,0,&yymsp[0].minor.yy269);
- sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy203, &yymsp[-1].minor.yy0);
+ yymsp[-5].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy563,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,0,&yymsp[0].minor.yy421);
+ sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy563, &yymsp[-1].minor.yy0);
}
break;
case 113: /* seltablist ::= stl_prefix nm dbnm LP exprlist RP as on_using */
{
- yymsp[-7].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-7].minor.yy203,&yymsp[-6].minor.yy0,&yymsp[-5].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy269);
- sqlite3SrcListFuncArgs(pParse, yymsp[-7].minor.yy203, yymsp[-3].minor.yy14);
+ yymsp[-7].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-7].minor.yy563,&yymsp[-6].minor.yy0,&yymsp[-5].minor.yy0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy421);
+ sqlite3SrcListFuncArgs(pParse, yymsp[-7].minor.yy563, yymsp[-3].minor.yy402);
}
break;
case 114: /* seltablist ::= stl_prefix LP select RP as on_using */
{
- yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,0,0,&yymsp[-1].minor.yy0,yymsp[-3].minor.yy555,&yymsp[0].minor.yy269);
+ yymsp[-5].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy563,0,0,&yymsp[-1].minor.yy0,yymsp[-3].minor.yy637,&yymsp[0].minor.yy421);
}
break;
case 115: /* seltablist ::= stl_prefix LP seltablist RP as on_using */
{
- if( yymsp[-5].minor.yy203==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy269.pOn==0 && yymsp[0].minor.yy269.pUsing==0 ){
- yymsp[-5].minor.yy203 = yymsp[-3].minor.yy203;
- }else if( ALWAYS(yymsp[-3].minor.yy203!=0) && yymsp[-3].minor.yy203->nSrc==1 ){
- yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,0,0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy269);
- if( yymsp[-5].minor.yy203 ){
- SrcItem *pNew = &yymsp[-5].minor.yy203->a[yymsp[-5].minor.yy203->nSrc-1];
- SrcItem *pOld = yymsp[-3].minor.yy203->a;
+ if( yymsp[-5].minor.yy563==0 && yymsp[-1].minor.yy0.n==0 && yymsp[0].minor.yy421.pOn==0 && yymsp[0].minor.yy421.pUsing==0 ){
+ yymsp[-5].minor.yy563 = yymsp[-3].minor.yy563;
+ }else if( ALWAYS(yymsp[-3].minor.yy563!=0) && yymsp[-3].minor.yy563->nSrc==1 ){
+ yymsp[-5].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy563,0,0,&yymsp[-1].minor.yy0,0,&yymsp[0].minor.yy421);
+ if( yymsp[-5].minor.yy563 ){
+ SrcItem *pNew = &yymsp[-5].minor.yy563->a[yymsp[-5].minor.yy563->nSrc-1];
+ SrcItem *pOld = yymsp[-3].minor.yy563->a;
assert( pOld->fg.fixedSchema==0 );
pNew->zName = pOld->zName;
assert( pOld->fg.fixedSchema==0 );
@@ -177946,12 +179350,12 @@ static YYACTIONTYPE yy_reduce(
}
pOld->zName = 0;
}
- sqlite3SrcListDelete(pParse->db, yymsp[-3].minor.yy203);
+ sqlite3SrcListDelete(pParse->db, yymsp[-3].minor.yy563);
}else{
Select *pSubquery;
- sqlite3SrcListShiftJoinType(pParse,yymsp[-3].minor.yy203);
- pSubquery = sqlite3SelectNew(pParse,0,yymsp[-3].minor.yy203,0,0,0,0,SF_NestedFrom,0);
- yymsp[-5].minor.yy203 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy203,0,0,&yymsp[-1].minor.yy0,pSubquery,&yymsp[0].minor.yy269);
+ sqlite3SrcListShiftJoinType(pParse,yymsp[-3].minor.yy563);
+ pSubquery = sqlite3SelectNew(pParse,0,yymsp[-3].minor.yy563,0,0,0,0,SF_NestedFrom,0);
+ yymsp[-5].minor.yy563 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-5].minor.yy563,0,0,&yymsp[-1].minor.yy0,pSubquery,&yymsp[0].minor.yy421);
}
}
break;
@@ -177961,56 +179365,56 @@ static YYACTIONTYPE yy_reduce(
break;
case 118: /* fullname ::= nm */
{
- yylhsminor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0);
- if( IN_RENAME_OBJECT && yylhsminor.yy203 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy203->a[0].zName, &yymsp[0].minor.yy0);
+ yylhsminor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0);
+ if( IN_RENAME_OBJECT && yylhsminor.yy563 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy563->a[0].zName, &yymsp[0].minor.yy0);
}
- yymsp[0].minor.yy203 = yylhsminor.yy203;
+ yymsp[0].minor.yy563 = yylhsminor.yy563;
break;
case 119: /* fullname ::= nm DOT nm */
{
- yylhsminor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);
- if( IN_RENAME_OBJECT && yylhsminor.yy203 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy203->a[0].zName, &yymsp[0].minor.yy0);
+ yylhsminor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);
+ if( IN_RENAME_OBJECT && yylhsminor.yy563 ) sqlite3RenameTokenMap(pParse, yylhsminor.yy563->a[0].zName, &yymsp[0].minor.yy0);
}
- yymsp[-2].minor.yy203 = yylhsminor.yy203;
+ yymsp[-2].minor.yy563 = yylhsminor.yy563;
break;
case 120: /* xfullname ::= nm */
-{yymsp[0].minor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/}
+{yymsp[0].minor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[0].minor.yy0,0); /*A-overwrites-X*/}
break;
case 121: /* xfullname ::= nm DOT nm */
-{yymsp[-2].minor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/}
+{yymsp[-2].minor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
case 122: /* xfullname ::= nm DOT nm AS nm */
{
- yymsp[-4].minor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/
- if( yymsp[-4].minor.yy203 ) yymsp[-4].minor.yy203->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
+ yymsp[-4].minor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,&yymsp[-2].minor.yy0); /*A-overwrites-X*/
+ if( yymsp[-4].minor.yy563 ) yymsp[-4].minor.yy563->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
}
break;
case 123: /* xfullname ::= nm AS nm */
{
- yymsp[-2].minor.yy203 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/
- if( yymsp[-2].minor.yy203 ) yymsp[-2].minor.yy203->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
+ yymsp[-2].minor.yy563 = sqlite3SrcListAppend(pParse,0,&yymsp[-2].minor.yy0,0); /*A-overwrites-X*/
+ if( yymsp[-2].minor.yy563 ) yymsp[-2].minor.yy563->a[0].zAlias = sqlite3NameFromToken(pParse->db, &yymsp[0].minor.yy0);
}
break;
case 124: /* joinop ::= COMMA|JOIN */
-{ yymsp[0].minor.yy144 = JT_INNER; }
+{ yymsp[0].minor.yy502 = JT_INNER; }
break;
case 125: /* joinop ::= JOIN_KW JOIN */
-{yymsp[-1].minor.yy144 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/}
+{yymsp[-1].minor.yy502 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); /*X-overwrites-A*/}
break;
case 126: /* joinop ::= JOIN_KW nm JOIN */
-{yymsp[-2].minor.yy144 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/}
+{yymsp[-2].minor.yy502 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); /*X-overwrites-A*/}
break;
case 127: /* joinop ::= JOIN_KW nm nm JOIN */
-{yymsp[-3].minor.yy144 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/}
+{yymsp[-3].minor.yy502 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/}
break;
case 128: /* on_using ::= ON expr */
-{yymsp[-1].minor.yy269.pOn = yymsp[0].minor.yy454; yymsp[-1].minor.yy269.pUsing = 0;}
+{yymsp[-1].minor.yy421.pOn = yymsp[0].minor.yy590; yymsp[-1].minor.yy421.pUsing = 0;}
break;
case 129: /* on_using ::= USING LP idlist RP */
-{yymsp[-3].minor.yy269.pOn = 0; yymsp[-3].minor.yy269.pUsing = yymsp[-1].minor.yy132;}
+{yymsp[-3].minor.yy421.pOn = 0; yymsp[-3].minor.yy421.pUsing = yymsp[-1].minor.yy204;}
break;
case 130: /* on_using ::= */
-{yymsp[1].minor.yy269.pOn = 0; yymsp[1].minor.yy269.pUsing = 0;}
+{yymsp[1].minor.yy421.pOn = 0; yymsp[1].minor.yy421.pUsing = 0;}
break;
case 132: /* indexed_by ::= INDEXED BY nm */
{yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;}
@@ -178020,35 +179424,35 @@ static YYACTIONTYPE yy_reduce(
break;
case 135: /* orderby_opt ::= ORDER BY sortlist */
case 145: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==145);
-{yymsp[-2].minor.yy14 = yymsp[0].minor.yy14;}
+{yymsp[-2].minor.yy402 = yymsp[0].minor.yy402;}
break;
case 136: /* sortlist ::= sortlist COMMA expr sortorder nulls */
{
- yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14,yymsp[-2].minor.yy454);
- sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy14,yymsp[-1].minor.yy144,yymsp[0].minor.yy144);
+ yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy402,yymsp[-2].minor.yy590);
+ sqlite3ExprListSetSortOrder(yymsp[-4].minor.yy402,yymsp[-1].minor.yy502,yymsp[0].minor.yy502);
}
break;
case 137: /* sortlist ::= expr sortorder nulls */
{
- yymsp[-2].minor.yy14 = sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy454); /*A-overwrites-Y*/
- sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy14,yymsp[-1].minor.yy144,yymsp[0].minor.yy144);
+ yymsp[-2].minor.yy402 = sqlite3ExprListAppend(pParse,0,yymsp[-2].minor.yy590); /*A-overwrites-Y*/
+ sqlite3ExprListSetSortOrder(yymsp[-2].minor.yy402,yymsp[-1].minor.yy502,yymsp[0].minor.yy502);
}
break;
case 138: /* sortorder ::= ASC */
-{yymsp[0].minor.yy144 = SQLITE_SO_ASC;}
+{yymsp[0].minor.yy502 = SQLITE_SO_ASC;}
break;
case 139: /* sortorder ::= DESC */
-{yymsp[0].minor.yy144 = SQLITE_SO_DESC;}
+{yymsp[0].minor.yy502 = SQLITE_SO_DESC;}
break;
case 140: /* sortorder ::= */
case 143: /* nulls ::= */ yytestcase(yyruleno==143);
-{yymsp[1].minor.yy144 = SQLITE_SO_UNDEFINED;}
+{yymsp[1].minor.yy502 = SQLITE_SO_UNDEFINED;}
break;
case 141: /* nulls ::= NULLS FIRST */
-{yymsp[-1].minor.yy144 = SQLITE_SO_ASC;}
+{yymsp[-1].minor.yy502 = SQLITE_SO_ASC;}
break;
case 142: /* nulls ::= NULLS LAST */
-{yymsp[-1].minor.yy144 = SQLITE_SO_DESC;}
+{yymsp[-1].minor.yy502 = SQLITE_SO_DESC;}
break;
case 146: /* having_opt ::= */
case 148: /* limit_opt ::= */ yytestcase(yyruleno==148);
@@ -178057,42 +179461,42 @@ static YYACTIONTYPE yy_reduce(
case 232: /* case_else ::= */ yytestcase(yyruleno==232);
case 233: /* case_operand ::= */ yytestcase(yyruleno==233);
case 252: /* vinto ::= */ yytestcase(yyruleno==252);
-{yymsp[1].minor.yy454 = 0;}
+{yymsp[1].minor.yy590 = 0;}
break;
case 147: /* having_opt ::= HAVING expr */
case 154: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==154);
case 156: /* where_opt_ret ::= WHERE expr */ yytestcase(yyruleno==156);
case 231: /* case_else ::= ELSE expr */ yytestcase(yyruleno==231);
case 251: /* vinto ::= INTO expr */ yytestcase(yyruleno==251);
-{yymsp[-1].minor.yy454 = yymsp[0].minor.yy454;}
+{yymsp[-1].minor.yy590 = yymsp[0].minor.yy590;}
break;
case 149: /* limit_opt ::= LIMIT expr */
-{yymsp[-1].minor.yy454 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy454,0);}
+{yymsp[-1].minor.yy590 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy590,0);}
break;
case 150: /* limit_opt ::= LIMIT expr OFFSET expr */
-{yymsp[-3].minor.yy454 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy454,yymsp[0].minor.yy454);}
+{yymsp[-3].minor.yy590 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[-2].minor.yy590,yymsp[0].minor.yy590);}
break;
case 151: /* limit_opt ::= LIMIT expr COMMA expr */
-{yymsp[-3].minor.yy454 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy454,yymsp[-2].minor.yy454);}
+{yymsp[-3].minor.yy590 = sqlite3PExpr(pParse,TK_LIMIT,yymsp[0].minor.yy590,yymsp[-2].minor.yy590);}
break;
case 152: /* cmd ::= with DELETE FROM xfullname indexed_opt where_opt_ret */
{
- sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy203, &yymsp[-1].minor.yy0);
- sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy203,yymsp[0].minor.yy454,0,0);
+ sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy563, &yymsp[-1].minor.yy0);
+ sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy563,yymsp[0].minor.yy590,0,0);
}
break;
case 157: /* where_opt_ret ::= RETURNING selcollist */
-{sqlite3AddReturning(pParse,yymsp[0].minor.yy14); yymsp[-1].minor.yy454 = 0;}
+{sqlite3AddReturning(pParse,yymsp[0].minor.yy402); yymsp[-1].minor.yy590 = 0;}
break;
case 158: /* where_opt_ret ::= WHERE expr RETURNING selcollist */
-{sqlite3AddReturning(pParse,yymsp[0].minor.yy14); yymsp[-3].minor.yy454 = yymsp[-2].minor.yy454;}
+{sqlite3AddReturning(pParse,yymsp[0].minor.yy402); yymsp[-3].minor.yy590 = yymsp[-2].minor.yy590;}
break;
case 159: /* cmd ::= with UPDATE orconf xfullname indexed_opt SET setlist from where_opt_ret */
{
- sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy203, &yymsp[-4].minor.yy0);
- sqlite3ExprListCheckLength(pParse,yymsp[-2].minor.yy14,"set list");
- if( yymsp[-1].minor.yy203 ){
- SrcList *pFromClause = yymsp[-1].minor.yy203;
+ sqlite3SrcListIndexedBy(pParse, yymsp[-5].minor.yy563, &yymsp[-4].minor.yy0);
+ sqlite3ExprListCheckLength(pParse,yymsp[-2].minor.yy402,"set list");
+ if( yymsp[-1].minor.yy563 ){
+ SrcList *pFromClause = yymsp[-1].minor.yy563;
if( pFromClause->nSrc>1 ){
Select *pSubquery;
Token as;
@@ -178101,90 +179505,90 @@ static YYACTIONTYPE yy_reduce(
as.z = 0;
pFromClause = sqlite3SrcListAppendFromTerm(pParse,0,0,0,&as,pSubquery,0);
}
- yymsp[-5].minor.yy203 = sqlite3SrcListAppendList(pParse, yymsp[-5].minor.yy203, pFromClause);
+ yymsp[-5].minor.yy563 = sqlite3SrcListAppendList(pParse, yymsp[-5].minor.yy563, pFromClause);
}
- sqlite3Update(pParse,yymsp[-5].minor.yy203,yymsp[-2].minor.yy14,yymsp[0].minor.yy454,yymsp[-6].minor.yy144,0,0,0);
+ sqlite3Update(pParse,yymsp[-5].minor.yy563,yymsp[-2].minor.yy402,yymsp[0].minor.yy590,yymsp[-6].minor.yy502,0,0,0);
}
break;
case 160: /* setlist ::= setlist COMMA nm EQ expr */
{
- yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy14, yymsp[0].minor.yy454);
- sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy14, &yymsp[-2].minor.yy0, 1);
+ yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy402, yymsp[0].minor.yy590);
+ sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy402, &yymsp[-2].minor.yy0, 1);
}
break;
case 161: /* setlist ::= setlist COMMA LP idlist RP EQ expr */
{
- yymsp[-6].minor.yy14 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy14, yymsp[-3].minor.yy132, yymsp[0].minor.yy454);
+ yymsp[-6].minor.yy402 = sqlite3ExprListAppendVector(pParse, yymsp[-6].minor.yy402, yymsp[-3].minor.yy204, yymsp[0].minor.yy590);
}
break;
case 162: /* setlist ::= nm EQ expr */
{
- yylhsminor.yy14 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy454);
- sqlite3ExprListSetName(pParse, yylhsminor.yy14, &yymsp[-2].minor.yy0, 1);
+ yylhsminor.yy402 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy590);
+ sqlite3ExprListSetName(pParse, yylhsminor.yy402, &yymsp[-2].minor.yy0, 1);
}
- yymsp[-2].minor.yy14 = yylhsminor.yy14;
+ yymsp[-2].minor.yy402 = yylhsminor.yy402;
break;
case 163: /* setlist ::= LP idlist RP EQ expr */
{
- yymsp[-4].minor.yy14 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy132, yymsp[0].minor.yy454);
+ yymsp[-4].minor.yy402 = sqlite3ExprListAppendVector(pParse, 0, yymsp[-3].minor.yy204, yymsp[0].minor.yy590);
}
break;
case 164: /* cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
{
- sqlite3Insert(pParse, yymsp[-3].minor.yy203, yymsp[-1].minor.yy555, yymsp[-2].minor.yy132, yymsp[-5].minor.yy144, yymsp[0].minor.yy122);
+ sqlite3Insert(pParse, yymsp[-3].minor.yy563, yymsp[-1].minor.yy637, yymsp[-2].minor.yy204, yymsp[-5].minor.yy502, yymsp[0].minor.yy403);
}
break;
case 165: /* cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES returning */
{
- sqlite3Insert(pParse, yymsp[-4].minor.yy203, 0, yymsp[-3].minor.yy132, yymsp[-6].minor.yy144, 0);
+ sqlite3Insert(pParse, yymsp[-4].minor.yy563, 0, yymsp[-3].minor.yy204, yymsp[-6].minor.yy502, 0);
}
break;
case 166: /* upsert ::= */
-{ yymsp[1].minor.yy122 = 0; }
+{ yymsp[1].minor.yy403 = 0; }
break;
case 167: /* upsert ::= RETURNING selcollist */
-{ yymsp[-1].minor.yy122 = 0; sqlite3AddReturning(pParse,yymsp[0].minor.yy14); }
+{ yymsp[-1].minor.yy403 = 0; sqlite3AddReturning(pParse,yymsp[0].minor.yy402); }
break;
case 168: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */
-{ yymsp[-11].minor.yy122 = sqlite3UpsertNew(pParse->db,yymsp[-8].minor.yy14,yymsp[-6].minor.yy454,yymsp[-2].minor.yy14,yymsp[-1].minor.yy454,yymsp[0].minor.yy122);}
+{ yymsp[-11].minor.yy403 = sqlite3UpsertNew(pParse->db,yymsp[-8].minor.yy402,yymsp[-6].minor.yy590,yymsp[-2].minor.yy402,yymsp[-1].minor.yy590,yymsp[0].minor.yy403);}
break;
case 169: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */
-{ yymsp[-8].minor.yy122 = sqlite3UpsertNew(pParse->db,yymsp[-5].minor.yy14,yymsp[-3].minor.yy454,0,0,yymsp[0].minor.yy122); }
+{ yymsp[-8].minor.yy403 = sqlite3UpsertNew(pParse->db,yymsp[-5].minor.yy402,yymsp[-3].minor.yy590,0,0,yymsp[0].minor.yy403); }
break;
case 170: /* upsert ::= ON CONFLICT DO NOTHING returning */
-{ yymsp[-4].minor.yy122 = sqlite3UpsertNew(pParse->db,0,0,0,0,0); }
+{ yymsp[-4].minor.yy403 = sqlite3UpsertNew(pParse->db,0,0,0,0,0); }
break;
case 171: /* upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt returning */
-{ yymsp[-7].minor.yy122 = sqlite3UpsertNew(pParse->db,0,0,yymsp[-2].minor.yy14,yymsp[-1].minor.yy454,0);}
+{ yymsp[-7].minor.yy403 = sqlite3UpsertNew(pParse->db,0,0,yymsp[-2].minor.yy402,yymsp[-1].minor.yy590,0);}
break;
case 172: /* returning ::= RETURNING selcollist */
-{sqlite3AddReturning(pParse,yymsp[0].minor.yy14);}
+{sqlite3AddReturning(pParse,yymsp[0].minor.yy402);}
break;
case 175: /* idlist_opt ::= */
-{yymsp[1].minor.yy132 = 0;}
+{yymsp[1].minor.yy204 = 0;}
break;
case 176: /* idlist_opt ::= LP idlist RP */
-{yymsp[-2].minor.yy132 = yymsp[-1].minor.yy132;}
+{yymsp[-2].minor.yy204 = yymsp[-1].minor.yy204;}
break;
case 177: /* idlist ::= idlist COMMA nm */
-{yymsp[-2].minor.yy132 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy132,&yymsp[0].minor.yy0);}
+{yymsp[-2].minor.yy204 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy204,&yymsp[0].minor.yy0);}
break;
case 178: /* idlist ::= nm */
-{yymsp[0].minor.yy132 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/}
+{yymsp[0].minor.yy204 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/}
break;
case 179: /* expr ::= LP expr RP */
-{yymsp[-2].minor.yy454 = yymsp[-1].minor.yy454;}
+{yymsp[-2].minor.yy590 = yymsp[-1].minor.yy590;}
break;
case 180: /* expr ::= ID|INDEXED|JOIN_KW */
-{yymsp[0].minor.yy454=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/}
+{yymsp[0].minor.yy590=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
case 181: /* expr ::= nm DOT nm */
{
Expr *temp1 = tokenExpr(pParse,TK_ID,yymsp[-2].minor.yy0);
Expr *temp2 = tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0);
- yylhsminor.yy454 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
+ yylhsminor.yy590 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
}
- yymsp[-2].minor.yy454 = yylhsminor.yy454;
+ yymsp[-2].minor.yy590 = yylhsminor.yy590;
break;
case 182: /* expr ::= nm DOT nm DOT nm */
{
@@ -178195,27 +179599,27 @@ static YYACTIONTYPE yy_reduce(
if( IN_RENAME_OBJECT ){
sqlite3RenameTokenRemap(pParse, 0, temp1);
}
- yylhsminor.yy454 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
+ yylhsminor.yy590 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
}
- yymsp[-4].minor.yy454 = yylhsminor.yy454;
+ yymsp[-4].minor.yy590 = yylhsminor.yy590;
break;
case 183: /* term ::= NULL|FLOAT|BLOB */
case 184: /* term ::= STRING */ yytestcase(yyruleno==184);
-{yymsp[0].minor.yy454=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/}
+{yymsp[0].minor.yy590=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/}
break;
case 185: /* term ::= INTEGER */
{
- yylhsminor.yy454 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1);
- if( yylhsminor.yy454 ) yylhsminor.yy454->w.iOfst = (int)(yymsp[0].minor.yy0.z - pParse->zTail);
+ yylhsminor.yy590 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1);
+ if( yylhsminor.yy590 ) yylhsminor.yy590->w.iOfst = (int)(yymsp[0].minor.yy0.z - pParse->zTail);
}
- yymsp[0].minor.yy454 = yylhsminor.yy454;
+ yymsp[0].minor.yy590 = yylhsminor.yy590;
break;
case 186: /* expr ::= VARIABLE */
{
if( !(yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){
u32 n = yymsp[0].minor.yy0.n;
- yymsp[0].minor.yy454 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0);
- sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy454, n);
+ yymsp[0].minor.yy590 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0);
+ sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy590, n);
}else{
/* When doing a nested parse, one can include terms in an expression
** that look like this: #1 #2 ... These terms refer to registers
@@ -178223,81 +179627,81 @@ static YYACTIONTYPE yy_reduce(
Token t = yymsp[0].minor.yy0; /*A-overwrites-X*/
assert( t.n>=2 );
if( pParse->nested==0 ){
- sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &t);
- yymsp[0].minor.yy454 = 0;
+ parserSyntaxError(pParse, &t);
+ yymsp[0].minor.yy590 = 0;
}else{
- yymsp[0].minor.yy454 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
- if( yymsp[0].minor.yy454 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy454->iTable);
+ yymsp[0].minor.yy590 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
+ if( yymsp[0].minor.yy590 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy590->iTable);
}
}
}
break;
case 187: /* expr ::= expr COLLATE ID|STRING */
{
- yymsp[-2].minor.yy454 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy454, &yymsp[0].minor.yy0, 1);
+ yymsp[-2].minor.yy590 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy590, &yymsp[0].minor.yy0, 1);
}
break;
case 188: /* expr ::= CAST LP expr AS typetoken RP */
{
- yymsp[-5].minor.yy454 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1);
- sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy454, yymsp[-3].minor.yy454, 0);
+ yymsp[-5].minor.yy590 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1);
+ sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy590, yymsp[-3].minor.yy590, 0);
}
break;
case 189: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP */
{
- yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy14, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy144);
+ yylhsminor.yy590 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy402, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy502);
}
- yymsp[-4].minor.yy454 = yylhsminor.yy454;
+ yymsp[-4].minor.yy590 = yylhsminor.yy590;
break;
case 190: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP */
{
- yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-4].minor.yy14, &yymsp[-7].minor.yy0, yymsp[-5].minor.yy144);
- sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy454, yymsp[-1].minor.yy14);
+ yylhsminor.yy590 = sqlite3ExprFunction(pParse, yymsp[-4].minor.yy402, &yymsp[-7].minor.yy0, yymsp[-5].minor.yy502);
+ sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy590, yymsp[-1].minor.yy402);
}
- yymsp[-7].minor.yy454 = yylhsminor.yy454;
+ yymsp[-7].minor.yy590 = yylhsminor.yy590;
break;
case 191: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP */
{
- yylhsminor.yy454 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0);
+ yylhsminor.yy590 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0);
}
- yymsp[-3].minor.yy454 = yylhsminor.yy454;
+ yymsp[-3].minor.yy590 = yylhsminor.yy590;
break;
case 192: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist RP filter_over */
{
- yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy14, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy144);
- sqlite3WindowAttach(pParse, yylhsminor.yy454, yymsp[0].minor.yy211);
+ yylhsminor.yy590 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy402, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy502);
+ sqlite3WindowAttach(pParse, yylhsminor.yy590, yymsp[0].minor.yy483);
}
- yymsp[-5].minor.yy454 = yylhsminor.yy454;
+ yymsp[-5].minor.yy590 = yylhsminor.yy590;
break;
case 193: /* expr ::= ID|INDEXED|JOIN_KW LP distinct exprlist ORDER BY sortlist RP filter_over */
{
- yylhsminor.yy454 = sqlite3ExprFunction(pParse, yymsp[-5].minor.yy14, &yymsp[-8].minor.yy0, yymsp[-6].minor.yy144);
- sqlite3WindowAttach(pParse, yylhsminor.yy454, yymsp[0].minor.yy211);
- sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy454, yymsp[-2].minor.yy14);
+ yylhsminor.yy590 = sqlite3ExprFunction(pParse, yymsp[-5].minor.yy402, &yymsp[-8].minor.yy0, yymsp[-6].minor.yy502);
+ sqlite3WindowAttach(pParse, yylhsminor.yy590, yymsp[0].minor.yy483);
+ sqlite3ExprAddFunctionOrderBy(pParse, yylhsminor.yy590, yymsp[-2].minor.yy402);
}
- yymsp[-8].minor.yy454 = yylhsminor.yy454;
+ yymsp[-8].minor.yy590 = yylhsminor.yy590;
break;
case 194: /* expr ::= ID|INDEXED|JOIN_KW LP STAR RP filter_over */
{
- yylhsminor.yy454 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0);
- sqlite3WindowAttach(pParse, yylhsminor.yy454, yymsp[0].minor.yy211);
+ yylhsminor.yy590 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0);
+ sqlite3WindowAttach(pParse, yylhsminor.yy590, yymsp[0].minor.yy483);
}
- yymsp[-4].minor.yy454 = yylhsminor.yy454;
+ yymsp[-4].minor.yy590 = yylhsminor.yy590;
break;
case 195: /* term ::= CTIME_KW */
{
- yylhsminor.yy454 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0);
+ yylhsminor.yy590 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0);
}
- yymsp[0].minor.yy454 = yylhsminor.yy454;
+ yymsp[0].minor.yy590 = yylhsminor.yy590;
break;
case 196: /* expr ::= LP nexprlist COMMA expr RP */
{
- ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy14, yymsp[-1].minor.yy454);
- yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
- if( yymsp[-4].minor.yy454 ){
- yymsp[-4].minor.yy454->x.pList = pList;
+ ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy402, yymsp[-1].minor.yy590);
+ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
+ if( yymsp[-4].minor.yy590 ){
+ yymsp[-4].minor.yy590->x.pList = pList;
if( ALWAYS(pList->nExpr) ){
- yymsp[-4].minor.yy454->flags |= pList->a[0].pExpr->flags & EP_Propagate;
+ yymsp[-4].minor.yy590->flags |= pList->a[0].pExpr->flags & EP_Propagate;
}
}else{
sqlite3ExprListDelete(pParse->db, pList);
@@ -178305,7 +179709,7 @@ static YYACTIONTYPE yy_reduce(
}
break;
case 197: /* expr ::= expr AND expr */
-{yymsp[-2].minor.yy454=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy454,yymsp[0].minor.yy454);}
+{yymsp[-2].minor.yy590=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy590,yymsp[0].minor.yy590);}
break;
case 198: /* expr ::= expr OR expr */
case 199: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==199);
@@ -178314,7 +179718,7 @@ static YYACTIONTYPE yy_reduce(
case 202: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==202);
case 203: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==203);
case 204: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==204);
-{yymsp[-2].minor.yy454=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy454,yymsp[0].minor.yy454);}
+{yymsp[-2].minor.yy590=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy590,yymsp[0].minor.yy590);}
break;
case 205: /* likeop ::= NOT LIKE_KW|MATCH */
{yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.n|=0x80000000; /*yymsp[-1].minor.yy0-overwrite-yymsp[0].minor.yy0*/}
@@ -178324,11 +179728,11 @@ static YYACTIONTYPE yy_reduce(
ExprList *pList;
int bNot = yymsp[-1].minor.yy0.n & 0x80000000;
yymsp[-1].minor.yy0.n &= 0x7fffffff;
- pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy454);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy454);
- yymsp[-2].minor.yy454 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
- if( bNot ) yymsp[-2].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy454, 0);
- if( yymsp[-2].minor.yy454 ) yymsp[-2].minor.yy454->flags |= EP_InfixFunc;
+ pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy590);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy590);
+ yymsp[-2].minor.yy590 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
+ if( bNot ) yymsp[-2].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy590, 0);
+ if( yymsp[-2].minor.yy590 ) yymsp[-2].minor.yy590->flags |= EP_InfixFunc;
}
break;
case 207: /* expr ::= expr likeop expr ESCAPE expr */
@@ -178336,91 +179740,91 @@ static YYACTIONTYPE yy_reduce(
ExprList *pList;
int bNot = yymsp[-3].minor.yy0.n & 0x80000000;
yymsp[-3].minor.yy0.n &= 0x7fffffff;
- pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy454);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy454);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy454);
- yymsp[-4].minor.yy454 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0);
- if( bNot ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0);
- if( yymsp[-4].minor.yy454 ) yymsp[-4].minor.yy454->flags |= EP_InfixFunc;
+ pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy590);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy590);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy590);
+ yymsp[-4].minor.yy590 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0);
+ if( bNot ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0);
+ if( yymsp[-4].minor.yy590 ) yymsp[-4].minor.yy590->flags |= EP_InfixFunc;
}
break;
case 208: /* expr ::= expr ISNULL|NOTNULL */
-{yymsp[-1].minor.yy454 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy454,0);}
+{yymsp[-1].minor.yy590 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy590,0);}
break;
case 209: /* expr ::= expr NOT NULL */
-{yymsp[-2].minor.yy454 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy454,0);}
+{yymsp[-2].minor.yy590 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy590,0);}
break;
case 210: /* expr ::= expr IS expr */
{
- yymsp[-2].minor.yy454 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy454,yymsp[0].minor.yy454);
- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy454, yymsp[-2].minor.yy454, TK_ISNULL);
+ yymsp[-2].minor.yy590 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy590,yymsp[0].minor.yy590);
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy590, yymsp[-2].minor.yy590, TK_ISNULL);
}
break;
case 211: /* expr ::= expr IS NOT expr */
{
- yymsp[-3].minor.yy454 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy454,yymsp[0].minor.yy454);
- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy454, yymsp[-3].minor.yy454, TK_NOTNULL);
+ yymsp[-3].minor.yy590 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy590,yymsp[0].minor.yy590);
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy590, yymsp[-3].minor.yy590, TK_NOTNULL);
}
break;
case 212: /* expr ::= expr IS NOT DISTINCT FROM expr */
{
- yymsp[-5].minor.yy454 = sqlite3PExpr(pParse,TK_IS,yymsp[-5].minor.yy454,yymsp[0].minor.yy454);
- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy454, yymsp[-5].minor.yy454, TK_ISNULL);
+ yymsp[-5].minor.yy590 = sqlite3PExpr(pParse,TK_IS,yymsp[-5].minor.yy590,yymsp[0].minor.yy590);
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy590, yymsp[-5].minor.yy590, TK_ISNULL);
}
break;
case 213: /* expr ::= expr IS DISTINCT FROM expr */
{
- yymsp[-4].minor.yy454 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-4].minor.yy454,yymsp[0].minor.yy454);
- binaryToUnaryIfNull(pParse, yymsp[0].minor.yy454, yymsp[-4].minor.yy454, TK_NOTNULL);
+ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-4].minor.yy590,yymsp[0].minor.yy590);
+ binaryToUnaryIfNull(pParse, yymsp[0].minor.yy590, yymsp[-4].minor.yy590, TK_NOTNULL);
}
break;
case 214: /* expr ::= NOT expr */
case 215: /* expr ::= BITNOT expr */ yytestcase(yyruleno==215);
-{yymsp[-1].minor.yy454 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy454, 0);/*A-overwrites-B*/}
+{yymsp[-1].minor.yy590 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy590, 0);/*A-overwrites-B*/}
break;
case 216: /* expr ::= PLUS|MINUS expr */
{
- Expr *p = yymsp[0].minor.yy454;
+ Expr *p = yymsp[0].minor.yy590;
u8 op = yymsp[-1].major + (TK_UPLUS-TK_PLUS);
assert( TK_UPLUS>TK_PLUS );
assert( TK_UMINUS == TK_MINUS + (TK_UPLUS - TK_PLUS) );
if( p && p->op==TK_UPLUS ){
p->op = op;
- yymsp[-1].minor.yy454 = p;
+ yymsp[-1].minor.yy590 = p;
}else{
- yymsp[-1].minor.yy454 = sqlite3PExpr(pParse, op, p, 0);
+ yymsp[-1].minor.yy590 = sqlite3PExpr(pParse, op, p, 0);
/*A-overwrites-B*/
}
}
break;
case 217: /* expr ::= expr PTR expr */
{
- ExprList *pList = sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy454);
- pList = sqlite3ExprListAppend(pParse, pList, yymsp[0].minor.yy454);
- yylhsminor.yy454 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
+ ExprList *pList = sqlite3ExprListAppend(pParse, 0, yymsp[-2].minor.yy590);
+ pList = sqlite3ExprListAppend(pParse, pList, yymsp[0].minor.yy590);
+ yylhsminor.yy590 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
}
- yymsp[-2].minor.yy454 = yylhsminor.yy454;
+ yymsp[-2].minor.yy590 = yylhsminor.yy590;
break;
case 218: /* between_op ::= BETWEEN */
case 221: /* in_op ::= IN */ yytestcase(yyruleno==221);
-{yymsp[0].minor.yy144 = 0;}
+{yymsp[0].minor.yy502 = 0;}
break;
case 220: /* expr ::= expr between_op expr AND expr */
{
- ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy454);
- pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy454);
- yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy454, 0);
- if( yymsp[-4].minor.yy454 ){
- yymsp[-4].minor.yy454->x.pList = pList;
+ ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy590);
+ pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy590);
+ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy590, 0);
+ if( yymsp[-4].minor.yy590 ){
+ yymsp[-4].minor.yy590->x.pList = pList;
}else{
sqlite3ExprListDelete(pParse->db, pList);
}
- if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0);
+ if( yymsp[-3].minor.yy502 ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0);
}
break;
case 223: /* expr ::= expr in_op LP exprlist RP */
{
- if( yymsp[-1].minor.yy14==0 ){
+ if( yymsp[-1].minor.yy402==0 ){
/* Expressions of the form
**
** expr1 IN ()
@@ -178429,110 +179833,110 @@ static YYACTIONTYPE yy_reduce(
** simplify to constants 0 (false) and 1 (true), respectively,
** regardless of the value of expr1.
*/
- sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy454);
- yymsp[-4].minor.yy454 = sqlite3Expr(pParse->db, TK_STRING, yymsp[-3].minor.yy144 ? "true" : "false");
- if( yymsp[-4].minor.yy454 ) sqlite3ExprIdToTrueFalse(yymsp[-4].minor.yy454);
- }else{
- Expr *pRHS = yymsp[-1].minor.yy14->a[0].pExpr;
- if( yymsp[-1].minor.yy14->nExpr==1 && sqlite3ExprIsConstant(pParse,pRHS) && yymsp[-4].minor.yy454->op!=TK_VECTOR ){
- yymsp[-1].minor.yy14->a[0].pExpr = 0;
- sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14);
+ sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy590);
+ yymsp[-4].minor.yy590 = sqlite3Expr(pParse->db, TK_STRING, yymsp[-3].minor.yy502 ? "true" : "false");
+ if( yymsp[-4].minor.yy590 ) sqlite3ExprIdToTrueFalse(yymsp[-4].minor.yy590);
+ }else{
+ Expr *pRHS = yymsp[-1].minor.yy402->a[0].pExpr;
+ if( yymsp[-1].minor.yy402->nExpr==1 && sqlite3ExprIsConstant(pParse,pRHS) && yymsp[-4].minor.yy590->op!=TK_VECTOR ){
+ yymsp[-1].minor.yy402->a[0].pExpr = 0;
+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy402);
pRHS = sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0);
- yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy454, pRHS);
- }else if( yymsp[-1].minor.yy14->nExpr==1 && pRHS->op==TK_SELECT ){
- yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0);
- sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, pRHS->x.pSelect);
+ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy590, pRHS);
+ }else if( yymsp[-1].minor.yy402->nExpr==1 && pRHS->op==TK_SELECT ){
+ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy590, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy590, pRHS->x.pSelect);
pRHS->x.pSelect = 0;
- sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14);
- }else{
- yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0);
- if( yymsp[-4].minor.yy454==0 ){
- sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14);
- }else if( yymsp[-4].minor.yy454->pLeft->op==TK_VECTOR ){
- int nExpr = yymsp[-4].minor.yy454->pLeft->x.pList->nExpr;
- Select *pSelectRHS = sqlite3ExprListToValues(pParse, nExpr, yymsp[-1].minor.yy14);
+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy402);
+ }else{
+ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy590, 0);
+ if( yymsp[-4].minor.yy590==0 ){
+ sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy402);
+ }else if( yymsp[-4].minor.yy590->pLeft->op==TK_VECTOR ){
+ int nExpr = yymsp[-4].minor.yy590->pLeft->x.pList->nExpr;
+ Select *pSelectRHS = sqlite3ExprListToValues(pParse, nExpr, yymsp[-1].minor.yy402);
if( pSelectRHS ){
parserDoubleLinkSelect(pParse, pSelectRHS);
- sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, pSelectRHS);
+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy590, pSelectRHS);
}
}else{
- yymsp[-4].minor.yy454->x.pList = yymsp[-1].minor.yy14;
- sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy454);
+ yymsp[-4].minor.yy590->x.pList = yymsp[-1].minor.yy402;
+ sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy590);
}
}
- if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0);
+ if( yymsp[-3].minor.yy502 ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0);
}
}
break;
case 224: /* expr ::= LP select RP */
{
- yymsp[-2].minor.yy454 = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
- sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy454, yymsp[-1].minor.yy555);
+ yymsp[-2].minor.yy590 = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy590, yymsp[-1].minor.yy637);
}
break;
case 225: /* expr ::= expr in_op LP select RP */
{
- yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0);
- sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, yymsp[-1].minor.yy555);
- if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0);
+ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy590, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy590, yymsp[-1].minor.yy637);
+ if( yymsp[-3].minor.yy502 ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0);
}
break;
case 226: /* expr ::= expr in_op nm dbnm paren_exprlist */
{
SrcList *pSrc = sqlite3SrcListAppend(pParse, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);
Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0);
- if( yymsp[0].minor.yy14 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy14);
- yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy454, 0);
- sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy454, pSelect);
- if( yymsp[-3].minor.yy144 ) yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy454, 0);
+ if( yymsp[0].minor.yy402 ) sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy402);
+ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy590, 0);
+ sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy590, pSelect);
+ if( yymsp[-3].minor.yy502 ) yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy590, 0);
}
break;
case 227: /* expr ::= EXISTS LP select RP */
{
Expr *p;
- p = yymsp[-3].minor.yy454 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0);
- sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy555);
+ p = yymsp[-3].minor.yy590 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0);
+ sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy637);
}
break;
case 228: /* expr ::= CASE case_operand case_exprlist case_else END */
{
- yymsp[-4].minor.yy454 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy454, 0);
- if( yymsp[-4].minor.yy454 ){
- yymsp[-4].minor.yy454->x.pList = yymsp[-1].minor.yy454 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy14,yymsp[-1].minor.yy454) : yymsp[-2].minor.yy14;
- sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy454);
+ yymsp[-4].minor.yy590 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy590, 0);
+ if( yymsp[-4].minor.yy590 ){
+ yymsp[-4].minor.yy590->x.pList = yymsp[-1].minor.yy590 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy402,yymsp[-1].minor.yy590) : yymsp[-2].minor.yy402;
+ sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy590);
}else{
- sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy14);
- sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy454);
+ sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy402);
+ sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy590);
}
}
break;
case 229: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
{
- yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, yymsp[-2].minor.yy454);
- yymsp[-4].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, yymsp[0].minor.yy454);
+ yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy402, yymsp[-2].minor.yy590);
+ yymsp[-4].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy402, yymsp[0].minor.yy590);
}
break;
case 230: /* case_exprlist ::= WHEN expr THEN expr */
{
- yymsp[-3].minor.yy14 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy454);
- yymsp[-3].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy14, yymsp[0].minor.yy454);
+ yymsp[-3].minor.yy402 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy590);
+ yymsp[-3].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy402, yymsp[0].minor.yy590);
}
break;
case 235: /* nexprlist ::= nexprlist COMMA expr */
-{yymsp[-2].minor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy14,yymsp[0].minor.yy454);}
+{yymsp[-2].minor.yy402 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy402,yymsp[0].minor.yy590);}
break;
case 236: /* nexprlist ::= expr */
-{yymsp[0].minor.yy14 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy454); /*A-overwrites-Y*/}
+{yymsp[0].minor.yy402 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy590); /*A-overwrites-Y*/}
break;
case 238: /* paren_exprlist ::= LP exprlist RP */
case 243: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==243);
-{yymsp[-2].minor.yy14 = yymsp[-1].minor.yy14;}
+{yymsp[-2].minor.yy402 = yymsp[-1].minor.yy402;}
break;
case 239: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
{
sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0,
- sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy14, yymsp[-10].minor.yy144,
- &yymsp[-11].minor.yy0, yymsp[0].minor.yy454, SQLITE_SO_ASC, yymsp[-8].minor.yy144, SQLITE_IDXTYPE_APPDEF);
+ sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy402, yymsp[-10].minor.yy502,
+ &yymsp[-11].minor.yy0, yymsp[0].minor.yy590, SQLITE_SO_ASC, yymsp[-8].minor.yy502, SQLITE_IDXTYPE_APPDEF);
if( IN_RENAME_OBJECT && pParse->pNewIndex ){
sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0);
}
@@ -178540,29 +179944,29 @@ static YYACTIONTYPE yy_reduce(
break;
case 240: /* uniqueflag ::= UNIQUE */
case 282: /* raisetype ::= ABORT */ yytestcase(yyruleno==282);
-{yymsp[0].minor.yy144 = OE_Abort;}
+{yymsp[0].minor.yy502 = OE_Abort;}
break;
case 241: /* uniqueflag ::= */
-{yymsp[1].minor.yy144 = OE_None;}
+{yymsp[1].minor.yy502 = OE_None;}
break;
case 244: /* eidlist ::= eidlist COMMA nm collate sortorder */
{
- yymsp[-4].minor.yy14 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy14, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy144, yymsp[0].minor.yy144);
+ yymsp[-4].minor.yy402 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy402, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy502, yymsp[0].minor.yy502);
}
break;
case 245: /* eidlist ::= nm collate sortorder */
{
- yymsp[-2].minor.yy14 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy144, yymsp[0].minor.yy144); /*A-overwrites-Y*/
+ yymsp[-2].minor.yy402 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy502, yymsp[0].minor.yy502); /*A-overwrites-Y*/
}
break;
case 248: /* cmd ::= DROP INDEX ifexists fullname */
-{sqlite3DropIndex(pParse, yymsp[0].minor.yy203, yymsp[-1].minor.yy144);}
+{sqlite3DropIndex(pParse, yymsp[0].minor.yy563, yymsp[-1].minor.yy502);}
break;
case 249: /* cmd ::= VACUUM vinto */
-{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy454);}
+{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy590);}
break;
case 250: /* cmd ::= VACUUM nm vinto */
-{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy454);}
+{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy590);}
break;
case 253: /* cmd ::= PRAGMA nm dbnm */
{sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);}
@@ -178584,50 +179988,54 @@ static YYACTIONTYPE yy_reduce(
Token all;
all.z = yymsp[-3].minor.yy0.z;
all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n;
- sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy427, &all);
+ sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy319, &all);
}
break;
case 261: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
{
- sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy144, yymsp[-4].minor.yy286.a, yymsp[-4].minor.yy286.b, yymsp[-2].minor.yy203, yymsp[0].minor.yy454, yymsp[-10].minor.yy144, yymsp[-8].minor.yy144);
+ sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy502, yymsp[-4].minor.yy28.a, yymsp[-4].minor.yy28.b, yymsp[-2].minor.yy563, yymsp[0].minor.yy590, yymsp[-10].minor.yy502, yymsp[-8].minor.yy502);
yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/
+#ifdef SQLITE_DEBUG
+ assert( pParse->isCreate ); /* Set by createkw reduce action */
+ pParse->isCreate = 0; /* But, should not be set for CREATE TRIGGER */
+#endif
}
break;
case 262: /* trigger_time ::= BEFORE|AFTER */
-{ yymsp[0].minor.yy144 = yymsp[0].major; /*A-overwrites-X*/ }
+{ yymsp[0].minor.yy502 = yymsp[0].major; /*A-overwrites-X*/ }
break;
case 263: /* trigger_time ::= INSTEAD OF */
-{ yymsp[-1].minor.yy144 = TK_INSTEAD;}
+{ yymsp[-1].minor.yy502 = TK_INSTEAD;}
break;
case 264: /* trigger_time ::= */
-{ yymsp[1].minor.yy144 = TK_BEFORE; }
+{ yymsp[1].minor.yy502 = TK_BEFORE; }
break;
case 265: /* trigger_event ::= DELETE|INSERT */
case 266: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==266);
-{yymsp[0].minor.yy286.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy286.b = 0;}
+{yymsp[0].minor.yy28.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy28.b = 0;}
break;
case 267: /* trigger_event ::= UPDATE OF idlist */
-{yymsp[-2].minor.yy286.a = TK_UPDATE; yymsp[-2].minor.yy286.b = yymsp[0].minor.yy132;}
+{yymsp[-2].minor.yy28.a = TK_UPDATE; yymsp[-2].minor.yy28.b = yymsp[0].minor.yy204;}
break;
case 268: /* when_clause ::= */
case 287: /* key_opt ::= */ yytestcase(yyruleno==287);
-{ yymsp[1].minor.yy454 = 0; }
+{ yymsp[1].minor.yy590 = 0; }
break;
case 269: /* when_clause ::= WHEN expr */
case 288: /* key_opt ::= KEY expr */ yytestcase(yyruleno==288);
-{ yymsp[-1].minor.yy454 = yymsp[0].minor.yy454; }
+{ yymsp[-1].minor.yy590 = yymsp[0].minor.yy590; }
break;
case 270: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
{
- assert( yymsp[-2].minor.yy427!=0 );
- yymsp[-2].minor.yy427->pLast->pNext = yymsp[-1].minor.yy427;
- yymsp[-2].minor.yy427->pLast = yymsp[-1].minor.yy427;
+ assert( yymsp[-2].minor.yy319!=0 );
+ yymsp[-2].minor.yy319->pLast->pNext = yymsp[-1].minor.yy319;
+ yymsp[-2].minor.yy319->pLast = yymsp[-1].minor.yy319;
}
break;
case 271: /* trigger_cmd_list ::= trigger_cmd SEMI */
{
- assert( yymsp[-1].minor.yy427!=0 );
- yymsp[-1].minor.yy427->pLast = yymsp[-1].minor.yy427;
+ assert( yymsp[-1].minor.yy319!=0 );
+ yymsp[-1].minor.yy319->pLast = yymsp[-1].minor.yy319;
}
break;
case 272: /* trnm ::= nm DOT nm */
@@ -178653,58 +180061,58 @@ static YYACTIONTYPE yy_reduce(
}
break;
case 275: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
-{yylhsminor.yy427 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy203, yymsp[-3].minor.yy14, yymsp[-1].minor.yy454, yymsp[-7].minor.yy144, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy168);}
- yymsp[-8].minor.yy427 = yylhsminor.yy427;
+{yylhsminor.yy319 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy563, yymsp[-3].minor.yy402, yymsp[-1].minor.yy590, yymsp[-7].minor.yy502, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy342);}
+ yymsp[-8].minor.yy319 = yylhsminor.yy319;
break;
case 276: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
{
- yylhsminor.yy427 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy132,yymsp[-2].minor.yy555,yymsp[-6].minor.yy144,yymsp[-1].minor.yy122,yymsp[-7].minor.yy168,yymsp[0].minor.yy168);/*yylhsminor.yy427-overwrites-yymsp[-6].minor.yy144*/
+ yylhsminor.yy319 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy204,yymsp[-2].minor.yy637,yymsp[-6].minor.yy502,yymsp[-1].minor.yy403,yymsp[-7].minor.yy342,yymsp[0].minor.yy342);/*yylhsminor.yy319-overwrites-yymsp[-6].minor.yy502*/
}
- yymsp[-7].minor.yy427 = yylhsminor.yy427;
+ yymsp[-7].minor.yy319 = yylhsminor.yy319;
break;
case 277: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
-{yylhsminor.yy427 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy454, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy168);}
- yymsp[-5].minor.yy427 = yylhsminor.yy427;
+{yylhsminor.yy319 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy590, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy342);}
+ yymsp[-5].minor.yy319 = yylhsminor.yy319;
break;
case 278: /* trigger_cmd ::= scanpt select scanpt */
-{yylhsminor.yy427 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy555, yymsp[-2].minor.yy168, yymsp[0].minor.yy168); /*yylhsminor.yy427-overwrites-yymsp[-1].minor.yy555*/}
- yymsp[-2].minor.yy427 = yylhsminor.yy427;
+{yylhsminor.yy319 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy637, yymsp[-2].minor.yy342, yymsp[0].minor.yy342); /*yylhsminor.yy319-overwrites-yymsp[-1].minor.yy637*/}
+ yymsp[-2].minor.yy319 = yylhsminor.yy319;
break;
case 279: /* expr ::= RAISE LP IGNORE RP */
{
- yymsp[-3].minor.yy454 = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
- if( yymsp[-3].minor.yy454 ){
- yymsp[-3].minor.yy454->affExpr = OE_Ignore;
+ yymsp[-3].minor.yy590 = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
+ if( yymsp[-3].minor.yy590 ){
+ yymsp[-3].minor.yy590->affExpr = OE_Ignore;
}
}
break;
case 280: /* expr ::= RAISE LP raisetype COMMA expr RP */
{
- yymsp[-5].minor.yy454 = sqlite3PExpr(pParse, TK_RAISE, yymsp[-1].minor.yy454, 0);
- if( yymsp[-5].minor.yy454 ) {
- yymsp[-5].minor.yy454->affExpr = (char)yymsp[-3].minor.yy144;
+ yymsp[-5].minor.yy590 = sqlite3PExpr(pParse, TK_RAISE, yymsp[-1].minor.yy590, 0);
+ if( yymsp[-5].minor.yy590 ) {
+ yymsp[-5].minor.yy590->affExpr = (char)yymsp[-3].minor.yy502;
}
}
break;
case 281: /* raisetype ::= ROLLBACK */
-{yymsp[0].minor.yy144 = OE_Rollback;}
+{yymsp[0].minor.yy502 = OE_Rollback;}
break;
case 283: /* raisetype ::= FAIL */
-{yymsp[0].minor.yy144 = OE_Fail;}
+{yymsp[0].minor.yy502 = OE_Fail;}
break;
case 284: /* cmd ::= DROP TRIGGER ifexists fullname */
{
- sqlite3DropTrigger(pParse,yymsp[0].minor.yy203,yymsp[-1].minor.yy144);
+ sqlite3DropTrigger(pParse,yymsp[0].minor.yy563,yymsp[-1].minor.yy502);
}
break;
case 285: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
{
- sqlite3Attach(pParse, yymsp[-3].minor.yy454, yymsp[-1].minor.yy454, yymsp[0].minor.yy454);
+ sqlite3Attach(pParse, yymsp[-3].minor.yy590, yymsp[-1].minor.yy590, yymsp[0].minor.yy590);
}
break;
case 286: /* cmd ::= DETACH database_kw_opt expr */
{
- sqlite3Detach(pParse, yymsp[0].minor.yy454);
+ sqlite3Detach(pParse, yymsp[0].minor.yy590);
}
break;
case 289: /* cmd ::= REINDEX */
@@ -178721,7 +180129,7 @@ static YYACTIONTYPE yy_reduce(
break;
case 293: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
{
- sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy203,&yymsp[0].minor.yy0);
+ sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy563,&yymsp[0].minor.yy0);
}
break;
case 294: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
@@ -178732,18 +180140,18 @@ static YYACTIONTYPE yy_reduce(
break;
case 295: /* cmd ::= ALTER TABLE fullname DROP kwcolumn_opt nm */
{
- sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy203, &yymsp[0].minor.yy0);
+ sqlite3AlterDropColumn(pParse, yymsp[-3].minor.yy563, &yymsp[0].minor.yy0);
}
break;
case 296: /* add_column_fullname ::= fullname */
{
disableLookaside(pParse);
- sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy203);
+ sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy563);
}
break;
case 297: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
{
- sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy203, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0);
+ sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy563, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0);
}
break;
case 298: /* cmd ::= create_vtab */
@@ -178754,7 +180162,7 @@ static YYACTIONTYPE yy_reduce(
break;
case 300: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
{
- sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy144);
+ sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy502);
}
break;
case 301: /* vtabarg ::= */
@@ -178767,20 +180175,20 @@ static YYACTIONTYPE yy_reduce(
break;
case 305: /* with ::= WITH wqlist */
case 306: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==306);
-{ sqlite3WithPush(pParse, yymsp[0].minor.yy59, 1); }
+{ sqlite3WithPush(pParse, yymsp[0].minor.yy125, 1); }
break;
case 307: /* wqas ::= AS */
-{yymsp[0].minor.yy462 = M10d_Any;}
+{yymsp[0].minor.yy444 = M10d_Any;}
break;
case 308: /* wqas ::= AS MATERIALIZED */
-{yymsp[-1].minor.yy462 = M10d_Yes;}
+{yymsp[-1].minor.yy444 = M10d_Yes;}
break;
case 309: /* wqas ::= AS NOT MATERIALIZED */
-{yymsp[-2].minor.yy462 = M10d_No;}
+{yymsp[-2].minor.yy444 = M10d_No;}
break;
case 310: /* wqitem ::= withnm eidlist_opt wqas LP select RP */
{
- yymsp[-5].minor.yy67 = sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy14, yymsp[-1].minor.yy555, yymsp[-3].minor.yy462); /*A-overwrites-X*/
+ yymsp[-5].minor.yy361 = sqlite3CteNew(pParse, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy402, yymsp[-1].minor.yy637, yymsp[-3].minor.yy444); /*A-overwrites-X*/
}
break;
case 311: /* withnm ::= nm */
@@ -178788,160 +180196,160 @@ static YYACTIONTYPE yy_reduce(
break;
case 312: /* wqlist ::= wqitem */
{
- yymsp[0].minor.yy59 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy67); /*A-overwrites-X*/
+ yymsp[0].minor.yy125 = sqlite3WithAdd(pParse, 0, yymsp[0].minor.yy361); /*A-overwrites-X*/
}
break;
case 313: /* wqlist ::= wqlist COMMA wqitem */
{
- yymsp[-2].minor.yy59 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy59, yymsp[0].minor.yy67);
+ yymsp[-2].minor.yy125 = sqlite3WithAdd(pParse, yymsp[-2].minor.yy125, yymsp[0].minor.yy361);
}
break;
case 314: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */
{
- assert( yymsp[0].minor.yy211!=0 );
- sqlite3WindowChain(pParse, yymsp[0].minor.yy211, yymsp[-2].minor.yy211);
- yymsp[0].minor.yy211->pNextWin = yymsp[-2].minor.yy211;
- yylhsminor.yy211 = yymsp[0].minor.yy211;
+ assert( yymsp[0].minor.yy483!=0 );
+ sqlite3WindowChain(pParse, yymsp[0].minor.yy483, yymsp[-2].minor.yy483);
+ yymsp[0].minor.yy483->pNextWin = yymsp[-2].minor.yy483;
+ yylhsminor.yy483 = yymsp[0].minor.yy483;
}
- yymsp[-2].minor.yy211 = yylhsminor.yy211;
+ yymsp[-2].minor.yy483 = yylhsminor.yy483;
break;
case 315: /* windowdefn ::= nm AS LP window RP */
{
- if( ALWAYS(yymsp[-1].minor.yy211) ){
- yymsp[-1].minor.yy211->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n);
+ if( ALWAYS(yymsp[-1].minor.yy483) ){
+ yymsp[-1].minor.yy483->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n);
}
- yylhsminor.yy211 = yymsp[-1].minor.yy211;
+ yylhsminor.yy483 = yymsp[-1].minor.yy483;
}
- yymsp[-4].minor.yy211 = yylhsminor.yy211;
+ yymsp[-4].minor.yy483 = yylhsminor.yy483;
break;
case 316: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */
{
- yymsp[-4].minor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, yymsp[-2].minor.yy14, yymsp[-1].minor.yy14, 0);
+ yymsp[-4].minor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, yymsp[-2].minor.yy402, yymsp[-1].minor.yy402, 0);
}
break;
case 317: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
{
- yylhsminor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, yymsp[-2].minor.yy14, yymsp[-1].minor.yy14, &yymsp[-5].minor.yy0);
+ yylhsminor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, yymsp[-2].minor.yy402, yymsp[-1].minor.yy402, &yymsp[-5].minor.yy0);
}
- yymsp[-5].minor.yy211 = yylhsminor.yy211;
+ yymsp[-5].minor.yy483 = yylhsminor.yy483;
break;
case 318: /* window ::= ORDER BY sortlist frame_opt */
{
- yymsp[-3].minor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, 0, yymsp[-1].minor.yy14, 0);
+ yymsp[-3].minor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, 0, yymsp[-1].minor.yy402, 0);
}
break;
case 319: /* window ::= nm ORDER BY sortlist frame_opt */
{
- yylhsminor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, 0, yymsp[-1].minor.yy14, &yymsp[-4].minor.yy0);
+ yylhsminor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, 0, yymsp[-1].minor.yy402, &yymsp[-4].minor.yy0);
}
- yymsp[-4].minor.yy211 = yylhsminor.yy211;
+ yymsp[-4].minor.yy483 = yylhsminor.yy483;
break;
case 320: /* window ::= nm frame_opt */
{
- yylhsminor.yy211 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy211, 0, 0, &yymsp[-1].minor.yy0);
+ yylhsminor.yy483 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy483, 0, 0, &yymsp[-1].minor.yy0);
}
- yymsp[-1].minor.yy211 = yylhsminor.yy211;
+ yymsp[-1].minor.yy483 = yylhsminor.yy483;
break;
case 321: /* frame_opt ::= */
{
- yymsp[1].minor.yy211 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0);
+ yymsp[1].minor.yy483 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0);
}
break;
case 322: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
{
- yylhsminor.yy211 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy144, yymsp[-1].minor.yy509.eType, yymsp[-1].minor.yy509.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy462);
+ yylhsminor.yy483 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy502, yymsp[-1].minor.yy205.eType, yymsp[-1].minor.yy205.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy444);
}
- yymsp[-2].minor.yy211 = yylhsminor.yy211;
+ yymsp[-2].minor.yy483 = yylhsminor.yy483;
break;
case 323: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
{
- yylhsminor.yy211 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy144, yymsp[-3].minor.yy509.eType, yymsp[-3].minor.yy509.pExpr, yymsp[-1].minor.yy509.eType, yymsp[-1].minor.yy509.pExpr, yymsp[0].minor.yy462);
+ yylhsminor.yy483 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy502, yymsp[-3].minor.yy205.eType, yymsp[-3].minor.yy205.pExpr, yymsp[-1].minor.yy205.eType, yymsp[-1].minor.yy205.pExpr, yymsp[0].minor.yy444);
}
- yymsp[-5].minor.yy211 = yylhsminor.yy211;
+ yymsp[-5].minor.yy483 = yylhsminor.yy483;
break;
case 325: /* frame_bound_s ::= frame_bound */
case 327: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==327);
-{yylhsminor.yy509 = yymsp[0].minor.yy509;}
- yymsp[0].minor.yy509 = yylhsminor.yy509;
+{yylhsminor.yy205 = yymsp[0].minor.yy205;}
+ yymsp[0].minor.yy205 = yylhsminor.yy205;
break;
case 326: /* frame_bound_s ::= UNBOUNDED PRECEDING */
case 328: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==328);
case 330: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==330);
-{yylhsminor.yy509.eType = yymsp[-1].major; yylhsminor.yy509.pExpr = 0;}
- yymsp[-1].minor.yy509 = yylhsminor.yy509;
+{yylhsminor.yy205.eType = yymsp[-1].major; yylhsminor.yy205.pExpr = 0;}
+ yymsp[-1].minor.yy205 = yylhsminor.yy205;
break;
case 329: /* frame_bound ::= expr PRECEDING|FOLLOWING */
-{yylhsminor.yy509.eType = yymsp[0].major; yylhsminor.yy509.pExpr = yymsp[-1].minor.yy454;}
- yymsp[-1].minor.yy509 = yylhsminor.yy509;
+{yylhsminor.yy205.eType = yymsp[0].major; yylhsminor.yy205.pExpr = yymsp[-1].minor.yy590;}
+ yymsp[-1].minor.yy205 = yylhsminor.yy205;
break;
case 331: /* frame_exclude_opt ::= */
-{yymsp[1].minor.yy462 = 0;}
+{yymsp[1].minor.yy444 = 0;}
break;
case 332: /* frame_exclude_opt ::= EXCLUDE frame_exclude */
-{yymsp[-1].minor.yy462 = yymsp[0].minor.yy462;}
+{yymsp[-1].minor.yy444 = yymsp[0].minor.yy444;}
break;
case 333: /* frame_exclude ::= NO OTHERS */
case 334: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==334);
-{yymsp[-1].minor.yy462 = yymsp[-1].major; /*A-overwrites-X*/}
+{yymsp[-1].minor.yy444 = yymsp[-1].major; /*A-overwrites-X*/}
break;
case 335: /* frame_exclude ::= GROUP|TIES */
-{yymsp[0].minor.yy462 = yymsp[0].major; /*A-overwrites-X*/}
+{yymsp[0].minor.yy444 = yymsp[0].major; /*A-overwrites-X*/}
break;
case 336: /* window_clause ::= WINDOW windowdefn_list */
-{ yymsp[-1].minor.yy211 = yymsp[0].minor.yy211; }
+{ yymsp[-1].minor.yy483 = yymsp[0].minor.yy483; }
break;
case 337: /* filter_over ::= filter_clause over_clause */
{
- if( yymsp[0].minor.yy211 ){
- yymsp[0].minor.yy211->pFilter = yymsp[-1].minor.yy454;
+ if( yymsp[0].minor.yy483 ){
+ yymsp[0].minor.yy483->pFilter = yymsp[-1].minor.yy590;
}else{
- sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy454);
+ sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy590);
}
- yylhsminor.yy211 = yymsp[0].minor.yy211;
+ yylhsminor.yy483 = yymsp[0].minor.yy483;
}
- yymsp[-1].minor.yy211 = yylhsminor.yy211;
+ yymsp[-1].minor.yy483 = yylhsminor.yy483;
break;
case 338: /* filter_over ::= over_clause */
{
- yylhsminor.yy211 = yymsp[0].minor.yy211;
+ yylhsminor.yy483 = yymsp[0].minor.yy483;
}
- yymsp[0].minor.yy211 = yylhsminor.yy211;
+ yymsp[0].minor.yy483 = yylhsminor.yy483;
break;
case 339: /* filter_over ::= filter_clause */
{
- yylhsminor.yy211 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
- if( yylhsminor.yy211 ){
- yylhsminor.yy211->eFrmType = TK_FILTER;
- yylhsminor.yy211->pFilter = yymsp[0].minor.yy454;
+ yylhsminor.yy483 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
+ if( yylhsminor.yy483 ){
+ yylhsminor.yy483->eFrmType = TK_FILTER;
+ yylhsminor.yy483->pFilter = yymsp[0].minor.yy590;
}else{
- sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy454);
+ sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy590);
}
}
- yymsp[0].minor.yy211 = yylhsminor.yy211;
+ yymsp[0].minor.yy483 = yylhsminor.yy483;
break;
case 340: /* over_clause ::= OVER LP window RP */
{
- yymsp[-3].minor.yy211 = yymsp[-1].minor.yy211;
- assert( yymsp[-3].minor.yy211!=0 );
+ yymsp[-3].minor.yy483 = yymsp[-1].minor.yy483;
+ assert( yymsp[-3].minor.yy483!=0 );
}
break;
case 341: /* over_clause ::= OVER nm */
{
- yymsp[-1].minor.yy211 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
- if( yymsp[-1].minor.yy211 ){
- yymsp[-1].minor.yy211->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n);
+ yymsp[-1].minor.yy483 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
+ if( yymsp[-1].minor.yy483 ){
+ yymsp[-1].minor.yy483->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n);
}
}
break;
case 342: /* filter_clause ::= FILTER LP WHERE expr RP */
-{ yymsp[-4].minor.yy454 = yymsp[-1].minor.yy454; }
+{ yymsp[-4].minor.yy590 = yymsp[-1].minor.yy590; }
break;
case 343: /* term ::= QNUMBER */
{
- yylhsminor.yy454=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0);
- sqlite3DequoteNumber(pParse, yylhsminor.yy454);
+ yylhsminor.yy590=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0);
+ sqlite3DequoteNumber(pParse, yylhsminor.yy590);
}
- yymsp[0].minor.yy454 = yylhsminor.yy454;
+ yymsp[0].minor.yy590 = yylhsminor.yy590;
break;
default:
/* (344) input ::= cmdlist */ yytestcase(yyruleno==344);
@@ -179071,7 +180479,7 @@ static void yy_syntax_error(
UNUSED_PARAMETER(yymajor); /* Silence some compiler warnings */
if( TOKEN.z[0] ){
- sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN);
+ parserSyntaxError(pParse, &TOKEN);
}else{
sqlite3ErrorMsg(pParse, "incomplete input");
}
@@ -180122,7 +181530,7 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
case CC_MINUS: {
if( z[1]=='-' ){
for(i=2; (c=z[i])!=0 && c!='\n'; i++){}
- *tokenType = TK_SPACE; /* IMP: R-22934-25134 */
+ *tokenType = TK_COMMENT;
return i;
}else if( z[1]=='>' ){
*tokenType = TK_PTR;
@@ -180158,7 +181566,7 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *z, int *tokenType){
}
for(i=3, c=z[2]; (c!='*' || z[i]!='/') && (c=z[i])!=0; i++){}
if( c ) i++;
- *tokenType = TK_SPACE; /* IMP: R-22934-25134 */
+ *tokenType = TK_COMMENT;
return i;
}
case CC_PERCENT: {
@@ -180487,12 +181895,12 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql){
if( tokenType>=TK_WINDOW ){
assert( tokenType==TK_SPACE || tokenType==TK_OVER || tokenType==TK_FILTER
|| tokenType==TK_ILLEGAL || tokenType==TK_WINDOW
- || tokenType==TK_QNUMBER
+ || tokenType==TK_QNUMBER || tokenType==TK_COMMENT
);
#else
if( tokenType>=TK_SPACE ){
assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL
- || tokenType==TK_QNUMBER
+ || tokenType==TK_QNUMBER || tokenType==TK_COMMENT
);
#endif /* SQLITE_OMIT_WINDOWFUNC */
if( AtomicLoad(&db->u1.isInterrupted) ){
@@ -180526,6 +181934,13 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql){
assert( n==6 );
tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed);
#endif /* SQLITE_OMIT_WINDOWFUNC */
+ }else if( tokenType==TK_COMMENT
+ && (db->init.busy || (db->flags & SQLITE_Comments)!=0)
+ ){
+ /* Ignore SQL comments if either (1) we are reparsing the schema or
+ ** (2) SQLITE_DBCONFIG_ENABLE_COMMENTS is turned on (the default). */
+ zSql += n;
+ continue;
}else if( tokenType!=TK_QNUMBER ){
Token x;
x.z = zSql;
@@ -180562,7 +181977,9 @@ SQLITE_PRIVATE int sqlite3RunParser(Parse *pParse, const char *zSql){
if( pParse->zErrMsg==0 ){
pParse->zErrMsg = sqlite3MPrintf(db, "%s", sqlite3ErrStr(pParse->rc));
}
- sqlite3_log(pParse->rc, "%s in \"%s\"", pParse->zErrMsg, pParse->zTail);
+ if( (pParse->prepFlags & SQLITE_PREPARE_DONT_LOG)==0 ){
+ sqlite3_log(pParse->rc, "%s in \"%s\"", pParse->zErrMsg, pParse->zTail);
+ }
nErr++;
}
pParse->zTail = zSql;
@@ -180630,6 +182047,7 @@ SQLITE_PRIVATE char *sqlite3Normalize(
n = sqlite3GetToken((unsigned char*)zSql+i, &tokenType);
if( NEVER(n<=0) ) break;
switch( tokenType ){
+ case TK_COMMENT:
case TK_SPACE: {
break;
}
@@ -181415,6 +182833,14 @@ SQLITE_API int sqlite3_initialize(void){
if( rc==SQLITE_OK ){
sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage,
sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage);
+#ifdef SQLITE_EXTRA_INIT_MUTEXED
+ {
+ int SQLITE_EXTRA_INIT_MUTEXED(const char*);
+ rc = SQLITE_EXTRA_INIT_MUTEXED(0);
+ }
+#endif
+ }
+ if( rc==SQLITE_OK ){
sqlite3MemoryBarrier();
sqlite3GlobalConfig.isInit = 1;
#ifdef SQLITE_EXTRA_INIT
@@ -181871,17 +183297,22 @@ SQLITE_API int sqlite3_config(int op, ...){
** If lookaside is already active, return SQLITE_BUSY.
**
** The sz parameter is the number of bytes in each lookaside slot.
-** The cnt parameter is the number of slots. If pStart is NULL the
-** space for the lookaside memory is obtained from sqlite3_malloc().
-** If pStart is not NULL then it is sz*cnt bytes of memory to use for
-** the lookaside memory.
+** The cnt parameter is the number of slots. If pBuf is NULL the
+** space for the lookaside memory is obtained from sqlite3_malloc()
+** or similar. If pBuf is not NULL then it is sz*cnt bytes of memory
+** to use for the lookaside memory.
*/
-static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
+static int setupLookaside(
+ sqlite3 *db, /* Database connection being configured */
+ void *pBuf, /* Memory to use for lookaside. May be NULL */
+ int sz, /* Desired size of each lookaside memory slot */
+ int cnt /* Number of slots to allocate */
+){
#ifndef SQLITE_OMIT_LOOKASIDE
- void *pStart;
- sqlite3_int64 szAlloc = sz*(sqlite3_int64)cnt;
- int nBig; /* Number of full-size slots */
- int nSm; /* Number smaller LOOKASIDE_SMALL-byte slots */
+ void *pStart; /* Start of the lookaside buffer */
+ sqlite3_int64 szAlloc; /* Total space set aside for lookaside memory */
+ int nBig; /* Number of full-size slots */
+ int nSm; /* Number smaller LOOKASIDE_SMALL-byte slots */
if( sqlite3LookasideUsed(db,0)>0 ){
return SQLITE_BUSY;
@@ -181894,17 +183325,22 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
sqlite3_free(db->lookaside.pStart);
}
/* The size of a lookaside slot after ROUNDDOWN8 needs to be larger
- ** than a pointer to be useful.
+ ** than a pointer and small enough to fit in a u16.
*/
- sz = ROUNDDOWN8(sz); /* IMP: R-33038-09382 */
+ sz = ROUNDDOWN8(sz);
if( sz<=(int)sizeof(LookasideSlot*) ) sz = 0;
- if( cnt<0 ) cnt = 0;
- if( sz==0 || cnt==0 ){
+ if( sz>65528 ) sz = 65528;
+ /* Count must be at least 1 to be useful, but not so large as to use
+ ** more than 0x7fff0000 total bytes for lookaside. */
+ if( cnt<1 ) cnt = 0;
+ if( sz>0 && cnt>(0x7fff0000/sz) ) cnt = 0x7fff0000/sz;
+ szAlloc = (i64)sz*(i64)cnt;
+ if( szAlloc==0 ){
sz = 0;
pStart = 0;
}else if( pBuf==0 ){
sqlite3BeginBenignMalloc();
- pStart = sqlite3Malloc( szAlloc ); /* IMP: R-61949-35727 */
+ pStart = sqlite3Malloc( szAlloc );
sqlite3EndBenignMalloc();
if( pStart ) szAlloc = sqlite3MallocSize(pStart);
}else{
@@ -181913,10 +183349,10 @@ static int setupLookaside(sqlite3 *db, void *pBuf, int sz, int cnt){
#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
if( sz>=LOOKASIDE_SMALL*3 ){
nBig = szAlloc/(3*LOOKASIDE_SMALL+sz);
- nSm = (szAlloc - sz*nBig)/LOOKASIDE_SMALL;
+ nSm = (szAlloc - (i64)sz*(i64)nBig)/LOOKASIDE_SMALL;
}else if( sz>=LOOKASIDE_SMALL*2 ){
nBig = szAlloc/(LOOKASIDE_SMALL+sz);
- nSm = (szAlloc - sz*nBig)/LOOKASIDE_SMALL;
+ nSm = (szAlloc - (i64)sz*(i64)nBig)/LOOKASIDE_SMALL;
}else
#endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */
if( sz>0 ){
@@ -182071,7 +183507,7 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){
default: {
static const struct {
int op; /* The opcode */
- u32 mask; /* Mask of the bit in sqlite3.flags to set/clear */
+ u64 mask; /* Mask of the bit in sqlite3.flags to set/clear */
} aFlagOp[] = {
{ SQLITE_DBCONFIG_ENABLE_FKEY, SQLITE_ForeignKeys },
{ SQLITE_DBCONFIG_ENABLE_TRIGGER, SQLITE_EnableTrigger },
@@ -182092,6 +183528,9 @@ SQLITE_API int sqlite3_db_config(sqlite3 *db, int op, ...){
{ SQLITE_DBCONFIG_TRUSTED_SCHEMA, SQLITE_TrustedSchema },
{ SQLITE_DBCONFIG_STMT_SCANSTATUS, SQLITE_StmtScanStatus },
{ SQLITE_DBCONFIG_REVERSE_SCANORDER, SQLITE_ReverseOrder },
+ { SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE, SQLITE_AttachCreate },
+ { SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE, SQLITE_AttachWrite },
+ { SQLITE_DBCONFIG_ENABLE_COMMENTS, SQLITE_Comments },
};
unsigned int i;
rc = SQLITE_ERROR; /* IMP: R-42790-23372 */
@@ -182535,10 +183974,6 @@ SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){
sqlite3Error(db, SQLITE_OK); /* Deallocates any cached error strings. */
sqlite3ValueFree(db->pErr);
sqlite3CloseExtensions(db);
-#if SQLITE_USER_AUTHENTICATION
- sqlite3_free(db->auth.zAuthUser);
- sqlite3_free(db->auth.zAuthPW);
-#endif
db->eOpenState = SQLITE_STATE_ERROR;
@@ -182882,6 +184317,9 @@ SQLITE_API int sqlite3_busy_handler(
db->busyHandler.pBusyArg = pArg;
db->busyHandler.nBusy = 0;
db->busyTimeout = 0;
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ db->setlkTimeout = 0;
+#endif
sqlite3_mutex_leave(db->mutex);
return SQLITE_OK;
}
@@ -182931,6 +184369,9 @@ SQLITE_API int sqlite3_busy_timeout(sqlite3 *db, int ms){
sqlite3_busy_handler(db, (int(*)(void*,int))sqliteDefaultBusyCallback,
(void*)db);
db->busyTimeout = ms;
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ db->setlkTimeout = ms;
+#endif
}else{
sqlite3_busy_handler(db, 0, 0);
}
@@ -182938,6 +184379,38 @@ SQLITE_API int sqlite3_busy_timeout(sqlite3 *db, int ms){
}
/*
+** Set the setlk timeout value.
+*/
+SQLITE_API int sqlite3_setlk_timeout(sqlite3 *db, int ms, int flags){
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ int iDb;
+ int bBOC = ((flags & SQLITE_SETLK_BLOCK_ON_CONNECT) ? 1 : 0);
+#endif
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
+#endif
+ if( ms<-1 ) return SQLITE_RANGE;
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
+ db->setlkTimeout = ms;
+ db->setlkFlags = flags;
+ sqlite3BtreeEnterAll(db);
+ for(iDb=0; iDb<db->nDb; iDb++){
+ Btree *pBt = db->aDb[iDb].pBt;
+ if( pBt ){
+ sqlite3_file *fd = sqlite3PagerFile(sqlite3BtreePager(pBt));
+ sqlite3OsFileControlHint(fd, SQLITE_FCNTL_BLOCK_ON_CONNECT, (void*)&bBOC);
+ }
+ }
+ sqlite3BtreeLeaveAll(db);
+#endif
+#if !defined(SQLITE_ENABLE_API_ARMOR) && !defined(SQLITE_ENABLE_SETLK_TIMEOUT)
+ UNUSED_PARAMETER(db);
+ UNUSED_PARAMETER(flags);
+#endif
+ return SQLITE_OK;
+}
+
+/*
** Cause any pending operation to stop at its earliest opportunity.
*/
SQLITE_API void sqlite3_interrupt(sqlite3 *db){
@@ -183973,8 +185446,8 @@ static const int aHardLimit[] = {
#if SQLITE_MAX_VDBE_OP<40
# error SQLITE_MAX_VDBE_OP must be at least 40
#endif
-#if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>127
-# error SQLITE_MAX_FUNCTION_ARG must be between 0 and 127
+#if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>32767
+# error SQLITE_MAX_FUNCTION_ARG must be between 0 and 32767
#endif
#if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>125
# error SQLITE_MAX_ATTACHED must be between 0 and 125
@@ -184041,8 +185514,8 @@ SQLITE_API int sqlite3_limit(sqlite3 *db, int limitId, int newLimit){
if( newLimit>=0 ){ /* IMP: R-52476-28732 */
if( newLimit>aHardLimit[limitId] ){
newLimit = aHardLimit[limitId]; /* IMP: R-51463-25634 */
- }else if( newLimit<1 && limitId==SQLITE_LIMIT_LENGTH ){
- newLimit = 1;
+ }else if( newLimit<SQLITE_MIN_LENGTH && limitId==SQLITE_LIMIT_LENGTH ){
+ newLimit = SQLITE_MIN_LENGTH;
}
db->aLimit[limitId] = newLimit;
}
@@ -184437,6 +185910,9 @@ static int openDatabase(
| SQLITE_EnableTrigger
| SQLITE_EnableView
| SQLITE_CacheSpill
+ | SQLITE_AttachCreate
+ | SQLITE_AttachWrite
+ | SQLITE_Comments
#if !defined(SQLITE_TRUSTED_SCHEMA) || SQLITE_TRUSTED_SCHEMA+0!=0
| SQLITE_TrustedSchema
#endif
@@ -184899,7 +186375,7 @@ SQLITE_API int sqlite3_set_clientdata(
return SQLITE_OK;
}else{
size_t n = strlen(zName);
- p = sqlite3_malloc64( sizeof(DbClientData)+n+1 );
+ p = sqlite3_malloc64( SZ_DBCLIENTDATA(n+1) );
if( p==0 ){
if( xDestructor ) xDestructor(pData);
sqlite3_mutex_leave(db->mutex);
@@ -185053,13 +186529,10 @@ SQLITE_API int sqlite3_table_column_metadata(
if( zColumnName==0 ){
/* Query for existence of table only */
}else{
- for(iCol=0; iCol<pTab->nCol; iCol++){
+ iCol = sqlite3ColumnIndex(pTab, zColumnName);
+ if( iCol>=0 ){
pCol = &pTab->aCol[iCol];
- if( 0==sqlite3StrICmp(pCol->zCnName, zColumnName) ){
- break;
- }
- }
- if( iCol==pTab->nCol ){
+ }else{
if( HasRowid(pTab) && sqlite3IsRowid(zColumnName) ){
iCol = pTab->iPKey;
pCol = iCol>=0 ? &pTab->aCol[iCol] : 0;
@@ -185268,8 +186741,8 @@ SQLITE_API int sqlite3_test_control(int op, ...){
/* sqlite3_test_control(SQLITE_TESTCTRL_FK_NO_ACTION, sqlite3 *db, int b);
**
** If b is true, then activate the SQLITE_FkNoAction setting. If b is
- ** false then clearn that setting. If the SQLITE_FkNoAction setting is
- ** abled, all foreign key ON DELETE and ON UPDATE actions behave as if
+ ** false then clear that setting. If the SQLITE_FkNoAction setting is
+ ** enabled, all foreign key ON DELETE and ON UPDATE actions behave as if
** they were NO ACTION, regardless of how they are defined.
**
** NB: One must usually run "PRAGMA writable_schema=RESET" after
@@ -185387,7 +186860,6 @@ SQLITE_API int sqlite3_test_control(int op, ...){
** issue "defined but not used" warnings. */
if( x==9999 ){
sqlite3ShowExpr(0);
- sqlite3ShowExpr(0);
sqlite3ShowExprList(0);
sqlite3ShowIdList(0);
sqlite3ShowSrcList(0);
@@ -186617,7 +188089,7 @@ SQLITE_PRIVATE void sqlite3ConnectionClosed(sqlite3 *db){
** Here, array { X } means zero or more occurrences of X, adjacent in
** memory. A "position" is an index of a token in the token stream
** generated by the tokenizer. Note that POS_END and POS_COLUMN occur
-** in the same logical place as the position element, and act as sentinals
+** in the same logical place as the position element, and act as sentinels
** ending a position list array. POS_END is 0. POS_COLUMN is 1.
** The positions numbers are not stored literally but rather as two more
** than the difference from the prior position, or the just the position plus
@@ -186836,6 +188308,13 @@ SQLITE_PRIVATE void sqlite3ConnectionClosed(sqlite3 *db){
#ifndef _FTSINT_H
#define _FTSINT_H
+/* #include <assert.h> */
+/* #include <stdlib.h> */
+/* #include <stddef.h> */
+/* #include <stdio.h> */
+/* #include <string.h> */
+/* #include <stdarg.h> */
+
#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
# define NDEBUG 1
#endif
@@ -187305,6 +188784,19 @@ typedef sqlite3_int64 i64; /* 8-byte signed integer */
#define deliberate_fall_through
+/*
+** Macros needed to provide flexible arrays in a portable way
+*/
+#ifndef offsetof
+# define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
+#endif
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+# define FLEXARRAY
+#else
+# define FLEXARRAY 1
+#endif
+
+
#endif /* SQLITE_AMALGAMATION */
#ifdef SQLITE_DEBUG
@@ -187409,7 +188901,7 @@ struct Fts3Table {
#endif
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
- /* True to disable the incremental doclist optimization. This is controled
+ /* True to disable the incremental doclist optimization. This is controlled
** by special insert command 'test-no-incr-doclist'. */
int bNoIncrDoclist;
@@ -187461,7 +188953,7 @@ struct Fts3Cursor {
/*
** The Fts3Cursor.eSearch member is always set to one of the following.
-** Actualy, Fts3Cursor.eSearch can be greater than or equal to
+** Actually, Fts3Cursor.eSearch can be greater than or equal to
** FTS3_FULLTEXT_SEARCH. If so, then Fts3Cursor.eSearch - 2 is the index
** of the column to be searched. For example, in
**
@@ -187534,9 +189026,13 @@ struct Fts3Phrase {
*/
int nToken; /* Number of tokens in the phrase */
int iColumn; /* Index of column this phrase must match */
- Fts3PhraseToken aToken[1]; /* One entry for each token in the phrase */
+ Fts3PhraseToken aToken[FLEXARRAY]; /* One for each token in the phrase */
};
+/* Size (in bytes) of an Fts3Phrase object large enough to hold N tokens */
+#define SZ_FTS3PHRASE(N) \
+ (offsetof(Fts3Phrase,aToken)+(N)*sizeof(Fts3PhraseToken))
+
/*
** A tree of these objects forms the RHS of a MATCH operator.
**
@@ -187743,6 +189239,7 @@ SQLITE_PRIVATE int sqlite3Fts3MsrIncrNext(
SQLITE_PRIVATE int sqlite3Fts3EvalPhrasePoslist(Fts3Cursor *, Fts3Expr *, int iCol, char **);
SQLITE_PRIVATE int sqlite3Fts3MsrOvfl(Fts3Cursor *, Fts3MultiSegReader *, int *);
SQLITE_PRIVATE int sqlite3Fts3MsrIncrRestart(Fts3MultiSegReader *pCsr);
+SQLITE_PRIVATE int sqlite3Fts3MsrCancel(Fts3Cursor*, Fts3Expr*);
/* fts3_tokenize_vtab.c */
SQLITE_PRIVATE int sqlite3Fts3InitTok(sqlite3*, Fts3Hash *, void(*xDestroy)(void*));
@@ -187769,12 +189266,6 @@ SQLITE_PRIVATE int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk);
# define SQLITE_CORE 1
#endif
-/* #include <assert.h> */
-/* #include <stdlib.h> */
-/* #include <stddef.h> */
-/* #include <stdio.h> */
-/* #include <string.h> */
-/* #include <stdarg.h> */
/* #include "fts3.h" */
#ifndef SQLITE_CORE
@@ -190113,7 +191604,7 @@ static int fts3DoclistOrMerge(
** sizes of the two inputs, plus enough space for exactly one of the input
** docids to grow.
**
- ** A symetric argument may be made if the doclists are in descending
+ ** A symmetric argument may be made if the doclists are in descending
** order.
*/
aOut = sqlite3_malloc64((i64)n1+n2+FTS3_VARINT_MAX-1+FTS3_BUFFER_PADDING);
@@ -191912,7 +193403,7 @@ static int fts3EvalDeferredPhrase(Fts3Cursor *pCsr, Fts3Phrase *pPhrase){
nDistance = iPrev - nMaxUndeferred;
}
- aOut = (char *)sqlite3Fts3MallocZero(nPoslist+FTS3_BUFFER_PADDING);
+ aOut = (char *)sqlite3Fts3MallocZero(((i64)nPoslist)+FTS3_BUFFER_PADDING);
if( !aOut ){
sqlite3_free(aPoslist);
return SQLITE_NOMEM;
@@ -192211,7 +193702,7 @@ static int incrPhraseTokenNext(
**
** * does not contain any deferred tokens.
**
-** Advance it to the next matching documnent in the database and populate
+** Advance it to the next matching document in the database and populate
** the Fts3Doclist.pList and nList fields.
**
** If there is no "next" entry and no error occurs, then *pbEof is set to
@@ -193218,7 +194709,7 @@ static int fts3EvalNext(Fts3Cursor *pCsr){
}
/*
-** Restart interation for expression pExpr so that the next call to
+** Restart iteration for expression pExpr so that the next call to
** fts3EvalNext() visits the first row. Do not allow incremental
** loading or merging of phrase doclists for this iteration.
**
@@ -193262,6 +194753,24 @@ static void fts3EvalRestart(
}
/*
+** Expression node pExpr is an MSR phrase. This function restarts pExpr
+** so that it is a regular phrase query, not an MSR. SQLITE_OK is returned
+** if successful, or an SQLite error code otherwise.
+*/
+SQLITE_PRIVATE int sqlite3Fts3MsrCancel(Fts3Cursor *pCsr, Fts3Expr *pExpr){
+ int rc = SQLITE_OK;
+ if( pExpr->bEof==0 ){
+ i64 iDocid = pExpr->iDocid;
+ fts3EvalRestart(pCsr, pExpr, &rc);
+ while( rc==SQLITE_OK && pExpr->iDocid!=iDocid ){
+ fts3EvalNextRow(pCsr, pExpr, &rc);
+ if( pExpr->bEof ) rc = FTS_CORRUPT_VTAB;
+ }
+ }
+ return rc;
+}
+
+/*
** After allocating the Fts3Expr.aMI[] array for each phrase in the
** expression rooted at pExpr, the cursor iterates through all rows matched
** by pExpr, calling this function for each row. This function increments
@@ -193648,7 +195157,7 @@ SQLITE_PRIVATE int sqlite3Fts3Corrupt(){
}
#endif
-#if !SQLITE_CORE
+#if !defined(SQLITE_CORE)
/*
** Initialize API pointer table, if required.
*/
@@ -194393,6 +195902,23 @@ SQLITE_PRIVATE int sqlite3Fts3OpenTokenizer(
static int fts3ExprParse(ParseContext *, const char *, int, Fts3Expr **, int *);
/*
+** Search buffer z[], size n, for a '"' character. Or, if enable_parenthesis
+** is defined, search for '(' and ')' as well. Return the index of the first
+** such character in the buffer. If there is no such character, return -1.
+*/
+static int findBarredChar(const char *z, int n){
+ int ii;
+ for(ii=0; ii<n; ii++){
+ if( (z[ii]=='"')
+ || (sqlite3_fts3_enable_parentheses && (z[ii]=='(' || z[ii]==')'))
+ ){
+ return ii;
+ }
+ }
+ return -1;
+}
+
+/*
** Extract the next token from buffer z (length n) using the tokenizer
** and other information (column names etc.) in pParse. Create an Fts3Expr
** structure of type FTSQUERY_PHRASE containing a phrase consisting of this
@@ -194416,16 +195942,9 @@ static int getNextToken(
int rc;
sqlite3_tokenizer_cursor *pCursor;
Fts3Expr *pRet = 0;
- int i = 0;
-
- /* Set variable i to the maximum number of bytes of input to tokenize. */
- for(i=0; i<n; i++){
- if( sqlite3_fts3_enable_parentheses && (z[i]=='(' || z[i]==')') ) break;
- if( z[i]=='"' ) break;
- }
- *pnConsumed = i;
- rc = sqlite3Fts3OpenTokenizer(pTokenizer, pParse->iLangid, z, i, &pCursor);
+ *pnConsumed = n;
+ rc = sqlite3Fts3OpenTokenizer(pTokenizer, pParse->iLangid, z, n, &pCursor);
if( rc==SQLITE_OK ){
const char *zToken;
int nToken = 0, iStart = 0, iEnd = 0, iPosition = 0;
@@ -194433,7 +195952,18 @@ static int getNextToken(
rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition);
if( rc==SQLITE_OK ){
- nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken;
+ /* Check that this tokenization did not gobble up any " characters. Or,
+ ** if enable_parenthesis is true, that it did not gobble up any
+ ** open or close parenthesis characters either. If it did, call
+ ** getNextToken() again, but pass only that part of the input buffer
+ ** up to the first such character. */
+ int iBarred = findBarredChar(z, iEnd);
+ if( iBarred>=0 ){
+ pModule->xClose(pCursor);
+ return getNextToken(pParse, iCol, z, iBarred, ppExpr, pnConsumed);
+ }
+
+ nByte = sizeof(Fts3Expr) + SZ_FTS3PHRASE(1) + nToken;
pRet = (Fts3Expr *)sqlite3Fts3MallocZero(nByte);
if( !pRet ){
rc = SQLITE_NOMEM;
@@ -194443,7 +195973,7 @@ static int getNextToken(
pRet->pPhrase->nToken = 1;
pRet->pPhrase->iColumn = iCol;
pRet->pPhrase->aToken[0].n = nToken;
- pRet->pPhrase->aToken[0].z = (char *)&pRet->pPhrase[1];
+ pRet->pPhrase->aToken[0].z = (char*)&pRet->pPhrase->aToken[1];
memcpy(pRet->pPhrase->aToken[0].z, zToken, nToken);
if( iEnd<n && z[iEnd]=='*' ){
@@ -194467,7 +195997,11 @@ static int getNextToken(
}
*pnConsumed = iEnd;
- }else if( i && rc==SQLITE_DONE ){
+ }else if( n && rc==SQLITE_DONE ){
+ int iBarred = findBarredChar(z, n);
+ if( iBarred>=0 ){
+ *pnConsumed = iBarred;
+ }
rc = SQLITE_OK;
}
@@ -194514,9 +196048,9 @@ static int getNextString(
Fts3Expr *p = 0;
sqlite3_tokenizer_cursor *pCursor = 0;
char *zTemp = 0;
- int nTemp = 0;
+ i64 nTemp = 0;
- const int nSpace = sizeof(Fts3Expr) + sizeof(Fts3Phrase);
+ const int nSpace = sizeof(Fts3Expr) + SZ_FTS3PHRASE(1);
int nToken = 0;
/* The final Fts3Expr data structure, including the Fts3Phrase,
@@ -194888,7 +196422,7 @@ static int fts3ExprParse(
/* The isRequirePhrase variable is set to true if a phrase or
** an expression contained in parenthesis is required. If a
- ** binary operator (AND, OR, NOT or NEAR) is encounted when
+ ** binary operator (AND, OR, NOT or NEAR) is encountered when
** isRequirePhrase is set, this is a syntax error.
*/
if( !isPhrase && isRequirePhrase ){
@@ -195470,7 +197004,6 @@ static void fts3ExprTestCommon(
}
if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){
- sqlite3Fts3ExprFree(pExpr);
sqlite3_result_error(context, "Error parsing expression", -1);
}else if( rc==SQLITE_NOMEM || !(zBuf = exprToString(pExpr, 0)) ){
sqlite3_result_error_nomem(context);
@@ -195713,7 +197246,7 @@ static void fts3HashInsertElement(
}
-/* Resize the hash table so that it cantains "new_size" buckets.
+/* Resize the hash table so that it contains "new_size" buckets.
** "new_size" must be a power of 2. The hash table might fail
** to resize if sqliteMalloc() fails.
**
@@ -196168,7 +197701,7 @@ static int star_oh(const char *z){
/*
** If the word ends with zFrom and xCond() is true for the stem
-** of the word that preceeds the zFrom ending, then change the
+** of the word that precedes the zFrom ending, then change the
** ending to zTo.
**
** The input word *pz and zFrom are both in reverse order. zTo
@@ -197679,7 +199212,7 @@ static int fts3tokFilterMethod(
fts3tokResetCursor(pCsr);
if( idxNum==1 ){
const char *zByte = (const char *)sqlite3_value_text(apVal[0]);
- int nByte = sqlite3_value_bytes(apVal[0]);
+ sqlite3_int64 nByte = sqlite3_value_bytes(apVal[0]);
pCsr->zInput = sqlite3_malloc64(nByte+1);
if( pCsr->zInput==0 ){
rc = SQLITE_NOMEM;
@@ -201751,7 +203284,7 @@ static int fts3IncrmergePush(
**
** It is assumed that the buffer associated with pNode is already large
** enough to accommodate the new entry. The buffer associated with pPrev
-** is extended by this function if requrired.
+** is extended by this function if required.
**
** If an error (i.e. OOM condition) occurs, an SQLite error code is
** returned. Otherwise, SQLITE_OK.
@@ -203414,7 +204947,7 @@ SQLITE_PRIVATE int sqlite3Fts3DeferToken(
/*
** SQLite value pRowid contains the rowid of a row that may or may not be
** present in the FTS3 table. If it is, delete it and adjust the contents
-** of subsiduary data structures accordingly.
+** of subsidiary data structures accordingly.
*/
static int fts3DeleteByRowid(
Fts3Table *p,
@@ -203740,9 +205273,13 @@ struct MatchinfoBuffer {
int nElem;
int bGlobal; /* Set if global data is loaded */
char *zMatchinfo;
- u32 aMatchinfo[1];
+ u32 aMI[FLEXARRAY];
};
+/* Size (in bytes) of a MatchinfoBuffer sufficient for N elements */
+#define SZ_MATCHINFOBUFFER(N) \
+ (offsetof(MatchinfoBuffer,aMI)+(((N)+1)/2)*sizeof(u64))
+
/*
** The snippet() and offsets() functions both return text values. An instance
@@ -203767,13 +205304,13 @@ struct StrBuffer {
static MatchinfoBuffer *fts3MIBufferNew(size_t nElem, const char *zMatchinfo){
MatchinfoBuffer *pRet;
sqlite3_int64 nByte = sizeof(u32) * (2*(sqlite3_int64)nElem + 1)
- + sizeof(MatchinfoBuffer);
+ + SZ_MATCHINFOBUFFER(1);
sqlite3_int64 nStr = strlen(zMatchinfo);
pRet = sqlite3Fts3MallocZero(nByte + nStr+1);
if( pRet ){
- pRet->aMatchinfo[0] = (u8*)(&pRet->aMatchinfo[1]) - (u8*)pRet;
- pRet->aMatchinfo[1+nElem] = pRet->aMatchinfo[0]
+ pRet->aMI[0] = (u8*)(&pRet->aMI[1]) - (u8*)pRet;
+ pRet->aMI[1+nElem] = pRet->aMI[0]
+ sizeof(u32)*((int)nElem+1);
pRet->nElem = (int)nElem;
pRet->zMatchinfo = ((char*)pRet) + nByte;
@@ -203787,10 +205324,10 @@ static MatchinfoBuffer *fts3MIBufferNew(size_t nElem, const char *zMatchinfo){
static void fts3MIBufferFree(void *p){
MatchinfoBuffer *pBuf = (MatchinfoBuffer*)((u8*)p - ((u32*)p)[-1]);
- assert( (u32*)p==&pBuf->aMatchinfo[1]
- || (u32*)p==&pBuf->aMatchinfo[pBuf->nElem+2]
+ assert( (u32*)p==&pBuf->aMI[1]
+ || (u32*)p==&pBuf->aMI[pBuf->nElem+2]
);
- if( (u32*)p==&pBuf->aMatchinfo[1] ){
+ if( (u32*)p==&pBuf->aMI[1] ){
pBuf->aRef[1] = 0;
}else{
pBuf->aRef[2] = 0;
@@ -203807,18 +205344,18 @@ static void (*fts3MIBufferAlloc(MatchinfoBuffer *p, u32 **paOut))(void*){
if( p->aRef[1]==0 ){
p->aRef[1] = 1;
- aOut = &p->aMatchinfo[1];
+ aOut = &p->aMI[1];
xRet = fts3MIBufferFree;
}
else if( p->aRef[2]==0 ){
p->aRef[2] = 1;
- aOut = &p->aMatchinfo[p->nElem+2];
+ aOut = &p->aMI[p->nElem+2];
xRet = fts3MIBufferFree;
}else{
aOut = (u32*)sqlite3_malloc64(p->nElem * sizeof(u32));
if( aOut ){
xRet = sqlite3_free;
- if( p->bGlobal ) memcpy(aOut, &p->aMatchinfo[1], p->nElem*sizeof(u32));
+ if( p->bGlobal ) memcpy(aOut, &p->aMI[1], p->nElem*sizeof(u32));
}
}
@@ -203828,7 +205365,7 @@ static void (*fts3MIBufferAlloc(MatchinfoBuffer *p, u32 **paOut))(void*){
static void fts3MIBufferSetGlobal(MatchinfoBuffer *p){
p->bGlobal = 1;
- memcpy(&p->aMatchinfo[2+p->nElem], &p->aMatchinfo[1], p->nElem*sizeof(u32));
+ memcpy(&p->aMI[2+p->nElem], &p->aMI[1], p->nElem*sizeof(u32));
}
/*
@@ -204243,7 +205780,7 @@ static int fts3StringAppend(
}
/* If there is insufficient space allocated at StrBuffer.z, use realloc()
- ** to grow the buffer until so that it is big enough to accomadate the
+ ** to grow the buffer until so that it is big enough to accommodate the
** appended data.
*/
if( pStr->n+nAppend+1>=pStr->nAlloc ){
@@ -204655,16 +206192,16 @@ static size_t fts3MatchinfoSize(MatchInfo *pInfo, char cArg){
break;
case FTS3_MATCHINFO_LHITS:
- nVal = pInfo->nCol * pInfo->nPhrase;
+ nVal = (size_t)pInfo->nCol * pInfo->nPhrase;
break;
case FTS3_MATCHINFO_LHITS_BM:
- nVal = pInfo->nPhrase * ((pInfo->nCol + 31) / 32);
+ nVal = (size_t)pInfo->nPhrase * ((pInfo->nCol + 31) / 32);
break;
default:
assert( cArg==FTS3_MATCHINFO_HITS );
- nVal = pInfo->nCol * pInfo->nPhrase * 3;
+ nVal = (size_t)pInfo->nCol * pInfo->nPhrase * 3;
break;
}
@@ -205219,6 +206756,22 @@ static int fts3ExprTermOffsetInit(Fts3Expr *pExpr, int iPhrase, void *ctx){
}
/*
+** If expression pExpr is a phrase expression that uses an MSR query,
+** restart it as a regular, non-incremental query. Return SQLITE_OK
+** if successful, or an SQLite error code otherwise.
+*/
+static int fts3ExprRestartIfCb(Fts3Expr *pExpr, int iPhrase, void *ctx){
+ TermOffsetCtx *p = (TermOffsetCtx*)ctx;
+ int rc = SQLITE_OK;
+ UNUSED_PARAMETER(iPhrase);
+ if( pExpr->pPhrase && pExpr->pPhrase->bIncr ){
+ rc = sqlite3Fts3MsrCancel(p->pCsr, pExpr);
+ pExpr->pPhrase->bIncr = 0;
+ }
+ return rc;
+}
+
+/*
** Implementation of offsets() function.
*/
SQLITE_PRIVATE void sqlite3Fts3Offsets(
@@ -205254,6 +206807,12 @@ SQLITE_PRIVATE void sqlite3Fts3Offsets(
sCtx.iDocid = pCsr->iPrevId;
sCtx.pCsr = pCsr;
+ /* If a query restart will be required, do it here, rather than later of
+ ** after pointers to poslist buffers that may be invalidated by a restart
+ ** have been saved. */
+ rc = sqlite3Fts3ExprIterate(pCsr->pExpr, fts3ExprRestartIfCb, (void*)&sCtx);
+ if( rc!=SQLITE_OK ) goto offsets_out;
+
/* Loop through the table columns, appending offset information to
** string-buffer res for each column.
*/
@@ -206200,8 +207759,8 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int c, int eRemoveDiacritic){
** Beginning with version 3.45.0 (circa 2024-01-01), these routines also
** accept BLOB values that have JSON encoded using a binary representation
** called "JSONB". The name JSONB comes from PostgreSQL, however the on-disk
-** format SQLite JSONB is completely different and incompatible with
-** PostgreSQL JSONB.
+** format for SQLite-JSONB is completely different and incompatible with
+** PostgreSQL-JSONB.
**
** Decoding and interpreting JSONB is still O(N) where N is the size of
** the input, the same as text JSON. However, the constant of proportionality
@@ -206258,7 +207817,7 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int c, int eRemoveDiacritic){
**
** The payload size need not be expressed in its minimal form. For example,
** if the payload size is 10, the size can be expressed in any of 5 different
-** ways: (1) (X>>4)==10, (2) (X>>4)==12 following by on 0x0a byte,
+** ways: (1) (X>>4)==10, (2) (X>>4)==12 following by one 0x0a byte,
** (3) (X>>4)==13 followed by 0x00 and 0x0a, (4) (X>>4)==14 followed by
** 0x00 0x00 0x00 0x0a, or (5) (X>>4)==15 followed by 7 bytes of 0x00 and
** a single byte of 0x0a. The shorter forms are preferred, of course, but
@@ -206268,7 +207827,7 @@ SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int c, int eRemoveDiacritic){
** the size when it becomes known, resulting in a non-minimal encoding.
**
** The value (X>>4)==15 is not actually used in the current implementation
-** (as SQLite is currently unable handle BLOBs larger than about 2GB)
+** (as SQLite is currently unable to handle BLOBs larger than about 2GB)
** but is included in the design to allow for future enhancements.
**
** The payload follows the header. NULL, TRUE, and FALSE have no payload and
@@ -206328,23 +207887,47 @@ static const char * const jsonbType[] = {
** increase for the text-JSON parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os).
*/
static const char jsonIsSpace[] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+#ifdef SQLITE_ASCII
+/*0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1 */
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 3 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 4 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 5 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 6 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 7 */
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 8 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 9 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* a */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* b */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* c */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* e */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* f */
+#endif
+#ifdef SQLITE_EBCDIC
+/*0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, /* 0 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1 */
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 3 */
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 4 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 5 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 6 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 7 */
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 8 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 9 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* a */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* b */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* c */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* e */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* f */
+#endif
+
};
#define jsonIsspace(x) (jsonIsSpace[(unsigned char)x])
@@ -206352,7 +207935,13 @@ static const char jsonIsSpace[] = {
** The set of all space characters recognized by jsonIsspace().
** Useful as the second argument to strspn().
*/
+#ifdef SQLITE_ASCII
static const char jsonSpaces[] = "\011\012\015\040";
+#endif
+#ifdef SQLITE_EBCDIC
+static const char jsonSpaces[] = "\005\045\015\100";
+#endif
+
/*
** Characters that are special to JSON. Control characters,
@@ -206361,23 +207950,46 @@ static const char jsonSpaces[] = "\011\012\015\040";
** it in the set of special characters.
*/
static const char jsonIsOk[256] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
-
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+#ifdef SQLITE_ASCII
+/*0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1 */
+ 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, /* 2 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 3 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, /* 5 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 7 */
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 8 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 9 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* a */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* b */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* c */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* d */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* e */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* f */
+#endif
+#ifdef SQLITE_EBCDIC
+/*0 1 2 3 4 5 6 7 8 9 a b c d e f */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, /* 3 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 5 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, /* 7 */
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 8 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 9 */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* a */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* b */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* c */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* d */
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* e */
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* f */
+#endif
};
/* Objects */
@@ -206522,7 +208134,7 @@ struct JsonParse {
** Forward references
**************************************************************************/
static void jsonReturnStringAsBlob(JsonString*);
-static int jsonFuncArgMightBeBinary(sqlite3_value *pJson);
+static int jsonArgIsJsonb(sqlite3_value *pJson, JsonParse *p);
static u32 jsonTranslateBlobToText(const JsonParse*,u32,JsonString*);
static void jsonReturnParse(sqlite3_context*,JsonParse*);
static JsonParse *jsonParseFuncArg(sqlite3_context*,sqlite3_value*,u32);
@@ -206596,7 +208208,7 @@ static int jsonCacheInsert(
** most-recently used entry if it isn't so already.
**
** The JsonParse object returned still belongs to the Cache and might
-** be deleted at any moment. If the caller whants the JsonParse to
+** be deleted at any moment. If the caller wants the JsonParse to
** linger, it needs to increment the nPJRef reference counter.
*/
static JsonParse *jsonCacheSearch(
@@ -206940,11 +208552,9 @@ static void jsonAppendSqlValue(
break;
}
default: {
- if( jsonFuncArgMightBeBinary(pValue) ){
- JsonParse px;
- memset(&px, 0, sizeof(px));
- px.aBlob = (u8*)sqlite3_value_blob(pValue);
- px.nBlob = sqlite3_value_bytes(pValue);
+ JsonParse px;
+ memset(&px, 0, sizeof(px));
+ if( jsonArgIsJsonb(pValue, &px) ){
jsonTranslateBlobToText(&px, 0, p);
}else if( p->eErr==0 ){
sqlite3_result_error(p->pCtx, "JSON cannot hold BLOB values", -1);
@@ -207263,7 +208873,7 @@ static void jsonWrongNumArgs(
*/
static int jsonBlobExpand(JsonParse *pParse, u32 N){
u8 *aNew;
- u32 t;
+ u64 t;
assert( N>pParse->nBlobAlloc );
if( pParse->nBlobAlloc==0 ){
t = 100;
@@ -207273,8 +208883,9 @@ static int jsonBlobExpand(JsonParse *pParse, u32 N){
if( t<N ) t = N+100;
aNew = sqlite3DbRealloc(pParse->db, pParse->aBlob, t);
if( aNew==0 ){ pParse->oom = 1; return 1; }
+ assert( t<0x7fffffff );
pParse->aBlob = aNew;
- pParse->nBlobAlloc = t;
+ pParse->nBlobAlloc = (u32)t;
return 0;
}
@@ -207341,7 +208952,7 @@ static SQLITE_NOINLINE void jsonBlobExpandAndAppendNode(
}
-/* Append an node type byte together with the payload size and
+/* Append a node type byte together with the payload size and
** possibly also the payload.
**
** If aPayload is not NULL, then it is a pointer to the payload which
@@ -207881,7 +209492,12 @@ json_parse_restart:
|| c=='n' || c=='r' || c=='t'
|| (c=='u' && jsonIs4Hex(&z[j+1])) ){
if( opcode==JSONB_TEXT ) opcode = JSONB_TEXTJ;
- }else if( c=='\'' || c=='0' || c=='v' || c=='\n'
+ }else if( c=='\'' || c=='v' || c=='\n'
+#ifdef SQLITE_BUG_COMPATIBLE_20250510
+ || (c=='0') /* Legacy bug compatible */
+#else
+ || (c=='0' && !sqlite3Isdigit(z[j+1])) /* Correct implementation */
+#endif
|| (0xe2==(u8)c && 0x80==(u8)z[j+1]
&& (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2]))
|| (c=='x' && jsonIs2Hex(&z[j+1])) ){
@@ -208231,10 +209847,7 @@ static u32 jsonbPayloadSize(const JsonParse *pParse, u32 i, u32 *pSz){
u8 x;
u32 sz;
u32 n;
- if( NEVER(i>pParse->nBlob) ){
- *pSz = 0;
- return 0;
- }
+ assert( i<=pParse->nBlob );
x = pParse->aBlob[i]>>4;
if( x<=11 ){
sz = x;
@@ -208271,15 +209884,15 @@ static u32 jsonbPayloadSize(const JsonParse *pParse, u32 i, u32 *pSz){
*pSz = 0;
return 0;
}
- sz = (pParse->aBlob[i+5]<<24) + (pParse->aBlob[i+6]<<16) +
+ sz = ((u32)pParse->aBlob[i+5]<<24) + (pParse->aBlob[i+6]<<16) +
(pParse->aBlob[i+7]<<8) + pParse->aBlob[i+8];
n = 9;
}
if( (i64)i+sz+n > pParse->nBlob
&& (i64)i+sz+n > pParse->nBlob-pParse->delta
){
- sz = 0;
- n = 0;
+ *pSz = 0;
+ return 0;
}
*pSz = sz;
return n;
@@ -208376,9 +209989,12 @@ static u32 jsonTranslateBlobToText(
}
case JSONB_TEXT:
case JSONB_TEXTJ: {
- jsonAppendChar(pOut, '"');
- jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz);
- jsonAppendChar(pOut, '"');
+ if( pOut->nUsed+sz+2<=pOut->nAlloc || jsonStringGrow(pOut, sz+2)==0 ){
+ pOut->zBuf[pOut->nUsed] = '"';
+ memcpy(pOut->zBuf+pOut->nUsed+1,(const char*)&pParse->aBlob[i+n],sz);
+ pOut->zBuf[pOut->nUsed+sz+1] = '"';
+ pOut->nUsed += sz+2;
+ }
break;
}
case JSONB_TEXT5: {
@@ -208617,33 +210233,6 @@ static u32 jsonTranslateBlobToPrettyText(
return i;
}
-
-/* Return true if the input pJson
-**
-** For performance reasons, this routine does not do a detailed check of the
-** input BLOB to ensure that it is well-formed. Hence, false positives are
-** possible. False negatives should never occur, however.
-*/
-static int jsonFuncArgMightBeBinary(sqlite3_value *pJson){
- u32 sz, n;
- const u8 *aBlob;
- int nBlob;
- JsonParse s;
- if( sqlite3_value_type(pJson)!=SQLITE_BLOB ) return 0;
- aBlob = sqlite3_value_blob(pJson);
- nBlob = sqlite3_value_bytes(pJson);
- if( nBlob<1 ) return 0;
- if( NEVER(aBlob==0) || (aBlob[0] & 0x0f)>JSONB_OBJECT ) return 0;
- memset(&s, 0, sizeof(s));
- s.aBlob = (u8*)aBlob;
- s.nBlob = nBlob;
- n = jsonbPayloadSize(&s, 0, &sz);
- if( n==0 ) return 0;
- if( sz+n!=(u32)nBlob ) return 0;
- if( (aBlob[0] & 0x0f)<=JSONB_FALSE && sz>0 ) return 0;
- return sz+n==(u32)nBlob;
-}
-
/*
** Given that a JSONB_ARRAY object starts at offset i, return
** the number of entries in that array.
@@ -208677,6 +210266,82 @@ static void jsonAfterEditSizeAdjust(JsonParse *pParse, u32 iRoot){
}
/*
+** If the JSONB at aIns[0..nIns-1] can be expanded (by denormalizing the
+** size field) by d bytes, then write the expansion into aOut[] and
+** return true. In this way, an overwrite happens without changing the
+** size of the JSONB, which reduces memcpy() operations and also make it
+** faster and easier to update the B-Tree entry that contains the JSONB
+** in the database.
+**
+** If the expansion of aIns[] by d bytes cannot be (easily) accomplished
+** then return false.
+**
+** The d parameter is guaranteed to be between 1 and 8.
+**
+** This routine is an optimization. A correct answer is obtained if it
+** always leaves the output unchanged and returns false.
+*/
+static int jsonBlobOverwrite(
+ u8 *aOut, /* Overwrite here */
+ const u8 *aIns, /* New content */
+ u32 nIns, /* Bytes of new content */
+ u32 d /* Need to expand new content by this much */
+){
+ u32 szPayload; /* Bytes of payload */
+ u32 i; /* New header size, after expansion & a loop counter */
+ u8 szHdr; /* Size of header before expansion */
+
+ /* Lookup table for finding the upper 4 bits of the first byte of the
+ ** expanded aIns[], based on the size of the expanded aIns[] header:
+ **
+ ** 2 3 4 5 6 7 8 9 */
+ static const u8 aType[] = { 0xc0, 0xd0, 0, 0xe0, 0, 0, 0, 0xf0 };
+
+ if( (aIns[0]&0x0f)<=2 ) return 0; /* Cannot enlarge NULL, true, false */
+ switch( aIns[0]>>4 ){
+ default: { /* aIns[] header size 1 */
+ if( ((1<<d)&0x116)==0 ) return 0; /* d must be 1, 2, 4, or 8 */
+ i = d + 1; /* New hdr sz: 2, 3, 5, or 9 */
+ szHdr = 1;
+ break;
+ }
+ case 12: { /* aIns[] header size is 2 */
+ if( ((1<<d)&0x8a)==0) return 0; /* d must be 1, 3, or 7 */
+ i = d + 2; /* New hdr sz: 2, 5, or 9 */
+ szHdr = 2;
+ break;
+ }
+ case 13: { /* aIns[] header size is 3 */
+ if( d!=2 && d!=6 ) return 0; /* d must be 2 or 6 */
+ i = d + 3; /* New hdr sz: 5 or 9 */
+ szHdr = 3;
+ break;
+ }
+ case 14: { /* aIns[] header size is 5 */
+ if( d!=4 ) return 0; /* d must be 4 */
+ i = 9; /* New hdr sz: 9 */
+ szHdr = 5;
+ break;
+ }
+ case 15: { /* aIns[] header size is 9 */
+ return 0; /* No solution */
+ }
+ }
+ assert( i>=2 && i<=9 && aType[i-2]!=0 );
+ aOut[0] = (aIns[0] & 0x0f) | aType[i-2];
+ memcpy(&aOut[i], &aIns[szHdr], nIns-szHdr);
+ szPayload = nIns - szHdr;
+ while( 1/*edit-by-break*/ ){
+ i--;
+ aOut[i] = szPayload & 0xff;
+ if( i==1 ) break;
+ szPayload >>= 8;
+ }
+ assert( (szPayload>>8)==0 );
+ return 1;
+}
+
+/*
** Modify the JSONB blob at pParse->aBlob by removing nDel bytes of
** content beginning at iDel, and replacing them with nIns bytes of
** content given by aIns.
@@ -208697,6 +210362,11 @@ static void jsonBlobEdit(
u32 nIns /* Bytes of content to insert */
){
i64 d = (i64)nIns - (i64)nDel;
+ if( d<0 && d>=(-8) && aIns!=0
+ && jsonBlobOverwrite(&pParse->aBlob[iDel], aIns, nIns, (int)-d)
+ ){
+ return;
+ }
if( d!=0 ){
if( pParse->nBlob + d > pParse->nBlobAlloc ){
jsonBlobExpand(pParse, pParse->nBlob+d);
@@ -208708,7 +210378,9 @@ static void jsonBlobEdit(
pParse->nBlob += d;
pParse->delta += d;
}
- if( nIns && aIns ) memcpy(&pParse->aBlob[iDel], aIns, nIns);
+ if( nIns && aIns ){
+ memcpy(&pParse->aBlob[iDel], aIns, nIns);
+ }
}
/*
@@ -208793,7 +210465,21 @@ static u32 jsonUnescapeOneChar(const char *z, u32 n, u32 *piOut){
case 'r': { *piOut = '\r'; return 2; }
case 't': { *piOut = '\t'; return 2; }
case 'v': { *piOut = '\v'; return 2; }
- case '0': { *piOut = 0; return 2; }
+ case '0': {
+ /* JSON5 requires that the \0 escape not be followed by a digit.
+ ** But SQLite did not enforce this restriction in versions 3.42.0
+ ** through 3.49.2. That was a bug. But some applications might have
+ ** come to depend on that bug. Use the SQLITE_BUG_COMPATIBLE_20250510
+ ** option to restore the old buggy behavior. */
+#ifdef SQLITE_BUG_COMPATIBLE_20250510
+ /* Legacy bug-compatible behavior */
+ *piOut = 0;
+#else
+ /* Correct behavior */
+ *piOut = (n>2 && sqlite3Isdigit(z[2])) ? JSON_INVALID_CHAR : 0;
+#endif
+ return 2;
+ }
case '\'':
case '"':
case '/':
@@ -209293,7 +210979,7 @@ static void jsonReturnFromBlob(
char *zOut;
u32 nOut = sz;
z = (const char*)&pParse->aBlob[i+n];
- zOut = sqlite3DbMallocRaw(db, nOut+1);
+ zOut = sqlite3DbMallocRaw(db, ((u64)nOut)+1);
if( zOut==0 ) goto returnfromblob_oom;
for(iIn=iOut=0; iIn<sz; iIn++){
char c = z[iIn];
@@ -209388,10 +211074,7 @@ static int jsonFunctionArgToBlob(
return 0;
}
case SQLITE_BLOB: {
- if( jsonFuncArgMightBeBinary(pArg) ){
- pParse->aBlob = (u8*)sqlite3_value_blob(pArg);
- pParse->nBlob = sqlite3_value_bytes(pArg);
- }else{
+ if( !jsonArgIsJsonb(pArg, pParse) ){
sqlite3_result_error(ctx, "JSON cannot hold BLOB values", -1);
return 1;
}
@@ -209471,7 +211154,7 @@ static char *jsonBadPathError(
}
/* argv[0] is a BLOB that seems likely to be a JSONB. Subsequent
-** arguments come in parse where each pair contains a JSON path and
+** arguments come in pairs where each pair contains a JSON path and
** content to insert or set at that patch. Do the updates
** and return the result.
**
@@ -209542,27 +211225,46 @@ jsonInsertIntoBlob_patherror:
/*
** If pArg is a blob that seems like a JSONB blob, then initialize
** p to point to that JSONB and return TRUE. If pArg does not seem like
-** a JSONB blob, then return FALSE;
-**
-** This routine is only called if it is already known that pArg is a
-** blob. The only open question is whether or not the blob appears
-** to be a JSONB blob.
+** a JSONB blob, then return FALSE.
+**
+** For small BLOBs (having no more than 7 bytes of payload) a full
+** validity check is done. So for small BLOBs this routine only returns
+** true if the value is guaranteed to be a valid JSONB. For larger BLOBs
+** (8 byte or more of payload) only the size of the outermost element is
+** checked to verify that the BLOB is superficially valid JSONB.
+**
+** A full JSONB validation is done on smaller BLOBs because those BLOBs might
+** also be text JSON that has been incorrectly cast into a BLOB.
+** (See tag-20240123-a and https://sqlite.org/forum/forumpost/012136abd5)
+** If the BLOB is 9 bytes are larger, then it is not possible for the
+** superficial size check done here to pass if the input is really text
+** JSON so we do not need to look deeper in that case.
+**
+** Why we only need to do full JSONB validation for smaller BLOBs:
+**
+** The first byte of valid JSON text must be one of: '{', '[', '"', ' ', '\n',
+** '\r', '\t', '-', or a digit '0' through '9'. Of these, only a subset
+** can also be the first byte of JSONB: '{', '[', and digits '3'
+** through '9'. In every one of those cases, the payload size is 7 bytes
+** or less. So if we do full JSONB validation for every BLOB where the
+** payload is less than 7 bytes, we will never get a false positive for
+** JSONB on an input that is really text JSON.
*/
static int jsonArgIsJsonb(sqlite3_value *pArg, JsonParse *p){
u32 n, sz = 0;
+ u8 c;
+ if( sqlite3_value_type(pArg)!=SQLITE_BLOB ) return 0;
p->aBlob = (u8*)sqlite3_value_blob(pArg);
p->nBlob = (u32)sqlite3_value_bytes(pArg);
- if( p->nBlob==0 ){
- p->aBlob = 0;
- return 0;
- }
- if( NEVER(p->aBlob==0) ){
- return 0;
- }
- if( (p->aBlob[0] & 0x0f)<=JSONB_OBJECT
+ if( p->nBlob>0
+ && ALWAYS(p->aBlob!=0)
+ && ((c = p->aBlob[0]) & 0x0f)<=JSONB_OBJECT
&& (n = jsonbPayloadSize(p, 0, &sz))>0
&& sz+n==p->nBlob
- && ((p->aBlob[0] & 0x0f)>JSONB_FALSE || sz==0)
+ && ((c & 0x0f)>JSONB_FALSE || sz==0)
+ && (sz>7
+ || (c!=0x7b && c!=0x5b && !sqlite3Isdigit(c))
+ || jsonbValidityCheck(p, 0, p->nBlob, 1)==0)
){
return 1;
}
@@ -209640,7 +211342,7 @@ rebuild_from_cache:
** JSON functions were suppose to work. From the beginning, blob was
** reserved for expansion and a blob value should have raised an error.
** But it did not, due to a bug. And many applications came to depend
- ** upon this buggy behavior, espeically when using the CLI and reading
+ ** upon this buggy behavior, especially when using the CLI and reading
** JSON text using readfile(), which returns a blob. For this reason
** we will continue to support the bug moving forward.
** See for example https://sqlite.org/forum/forumpost/012136abd5292b8d
@@ -210655,21 +212357,17 @@ static void jsonValidFunc(
return;
}
case SQLITE_BLOB: {
- if( jsonFuncArgMightBeBinary(argv[0]) ){
+ JsonParse py;
+ memset(&py, 0, sizeof(py));
+ if( jsonArgIsJsonb(argv[0], &py) ){
if( flags & 0x04 ){
/* Superficial checking only - accomplished by the
- ** jsonFuncArgMightBeBinary() call above. */
+ ** jsonArgIsJsonb() call above. */
res = 1;
}else if( flags & 0x08 ){
/* Strict checking. Check by translating BLOB->TEXT->BLOB. If
** no errors occur, call that a "strict check". */
- JsonParse px;
- u32 iErr;
- memset(&px, 0, sizeof(px));
- px.aBlob = (u8*)sqlite3_value_blob(argv[0]);
- px.nBlob = sqlite3_value_bytes(argv[0]);
- iErr = jsonbValidityCheck(&px, 0, px.nBlob, 1);
- res = iErr==0;
+ res = 0==jsonbValidityCheck(&py, 0, py.nBlob, 1);
}
break;
}
@@ -210727,9 +212425,7 @@ static void jsonErrorFunc(
UNUSED_PARAMETER(argc);
memset(&s, 0, sizeof(s));
s.db = sqlite3_context_db_handle(ctx);
- if( jsonFuncArgMightBeBinary(argv[0]) ){
- s.aBlob = (u8*)sqlite3_value_blob(argv[0]);
- s.nBlob = sqlite3_value_bytes(argv[0]);
+ if( jsonArgIsJsonb(argv[0], &s) ){
iErrPos = (i64)jsonbValidityCheck(&s, 0, s.nBlob, 1);
}else{
s.zJson = (char*)sqlite3_value_text(argv[0]);
@@ -210890,18 +212586,20 @@ static void jsonObjectStep(
UNUSED_PARAMETER(argc);
pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
if( pStr ){
+ z = (const char*)sqlite3_value_text(argv[0]);
+ n = sqlite3Strlen30(z);
if( pStr->zBuf==0 ){
jsonStringInit(pStr, ctx);
jsonAppendChar(pStr, '{');
- }else if( pStr->nUsed>1 ){
+ }else if( pStr->nUsed>1 && z!=0 ){
jsonAppendChar(pStr, ',');
}
pStr->pCtx = ctx;
- z = (const char*)sqlite3_value_text(argv[0]);
- n = sqlite3Strlen30(z);
- jsonAppendString(pStr, z, n);
- jsonAppendChar(pStr, ':');
- jsonAppendSqlValue(pStr, argv[1]);
+ if( z!=0 ){
+ jsonAppendString(pStr, z, n);
+ jsonAppendChar(pStr, ':');
+ jsonAppendSqlValue(pStr, argv[1]);
+ }
}
}
static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){
@@ -211414,9 +213112,8 @@ static int jsonEachFilter(
memset(&p->sParse, 0, sizeof(p->sParse));
p->sParse.nJPRef = 1;
p->sParse.db = p->db;
- if( jsonFuncArgMightBeBinary(argv[0]) ){
- p->sParse.nBlob = sqlite3_value_bytes(argv[0]);
- p->sParse.aBlob = (u8*)sqlite3_value_blob(argv[0]);
+ if( jsonArgIsJsonb(argv[0], &p->sParse) ){
+ /* We have JSONB */
}else{
p->sParse.zJson = (char*)sqlite3_value_text(argv[0]);
p->sParse.nJson = sqlite3_value_bytes(argv[0]);
@@ -211740,6 +213437,14 @@ typedef unsigned int u32;
# define ALWAYS(X) (X)
# define NEVER(X) (X)
#endif
+#ifndef offsetof
+#define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
+#endif
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+# define FLEXARRAY
+#else
+# define FLEXARRAY 1
+#endif
#endif /* !defined(SQLITE_AMALGAMATION) */
/* Macro to check for 4-byte alignment. Only used inside of assert() */
@@ -212060,9 +213765,13 @@ struct RtreeMatchArg {
RtreeGeomCallback cb; /* Info about the callback functions */
int nParam; /* Number of parameters to the SQL function */
sqlite3_value **apSqlParam; /* Original SQL parameter values */
- RtreeDValue aParam[1]; /* Values for parameters to the SQL function */
+ RtreeDValue aParam[FLEXARRAY]; /* Values for parameters to the SQL function */
};
+/* Size of an RtreeMatchArg object with N parameters */
+#define SZ_RTREEMATCHARG(N) \
+ (offsetof(RtreeMatchArg,aParam)+(N)*sizeof(RtreeDValue))
+
#ifndef MAX
# define MAX(x,y) ((x) < (y) ? (y) : (x))
#endif
@@ -213751,7 +215460,7 @@ static int rtreeBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
}
/*
-** Return the N-dimensional volumn of the cell stored in *p.
+** Return the N-dimensional volume of the cell stored in *p.
*/
static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){
RtreeDValue area = (RtreeDValue)1;
@@ -215421,8 +217130,8 @@ static void rtreenode(sqlite3_context *ctx, int nArg, sqlite3_value **apArg){
sqlite3_str_append(pOut, "}", 1);
}
errCode = sqlite3_str_errcode(pOut);
- sqlite3_result_text(ctx, sqlite3_str_finish(pOut), -1, sqlite3_free);
sqlite3_result_error_code(ctx, errCode);
+ sqlite3_result_text(ctx, sqlite3_str_finish(pOut), -1, sqlite3_free);
}
/* This routine implements an SQL function that returns the "depth" parameter
@@ -215517,7 +217226,7 @@ static sqlite3_stmt *rtreeCheckPrepare(
/*
** The second and subsequent arguments to this function are a printf()
** style format string and arguments. This function formats the string and
-** appends it to the report being accumuated in pCheck.
+** appends it to the report being accumulated in pCheck.
*/
static void rtreeCheckAppendMsg(RtreeCheck *pCheck, const char *zFmt, ...){
va_list ap;
@@ -216705,7 +218414,7 @@ static void geopolyBBoxFinal(
** Determine if point (x0,y0) is beneath line segment (x1,y1)->(x2,y2).
** Returns:
**
-** +2 x0,y0 is on the line segement
+** +2 x0,y0 is on the line segment
**
** +1 x0,y0 is beneath line segment
**
@@ -216811,7 +218520,7 @@ static void geopolyWithinFunc(
sqlite3_free(p2);
}
-/* Objects used by the overlap algorihm. */
+/* Objects used by the overlap algorithm. */
typedef struct GeoEvent GeoEvent;
typedef struct GeoSegment GeoSegment;
typedef struct GeoOverlap GeoOverlap;
@@ -217858,8 +219567,7 @@ static void geomCallback(sqlite3_context *ctx, int nArg, sqlite3_value **aArg){
sqlite3_int64 nBlob;
int memErr = 0;
- nBlob = sizeof(RtreeMatchArg) + (nArg-1)*sizeof(RtreeDValue)
- + nArg*sizeof(sqlite3_value*);
+ nBlob = SZ_RTREEMATCHARG(nArg) + nArg*sizeof(sqlite3_value*);
pBlob = (RtreeMatchArg *)sqlite3_malloc64(nBlob);
if( !pBlob ){
sqlite3_result_error_nomem(ctx);
@@ -217938,7 +219646,7 @@ SQLITE_API int sqlite3_rtree_query_callback(
);
}
-#if !SQLITE_CORE
+#ifndef SQLITE_CORE
#ifdef _WIN32
__declspec(dllexport)
#endif
@@ -218529,7 +220237,7 @@ SQLITE_PRIVATE int sqlite3IcuInit(sqlite3 *db){
return rc;
}
-#if !SQLITE_CORE
+#ifndef SQLITE_CORE
#ifdef _WIN32
__declspec(dllexport)
#endif
@@ -218954,7 +220662,7 @@ SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(
**
** "RBU" stands for "Resumable Bulk Update". As in a large database update
** transmitted via a wireless network to a mobile device. A transaction
-** applied using this extension is hence refered to as an "RBU update".
+** applied using this extension is hence referred to as an "RBU update".
**
**
** LIMITATIONS
@@ -219251,7 +220959,7 @@ SQLITE_API sqlite3rbu *sqlite3rbu_open(
** the next call to sqlite3rbu_vacuum() opens a handle that starts a
** new RBU vacuum operation.
**
-** As with sqlite3rbu_open(), Zipvfs users should rever to the comment
+** As with sqlite3rbu_open(), Zipvfs users should refer to the comment
** describing the sqlite3rbu_create_vfs() API function below for
** a description of the complications associated with using RBU with
** zipvfs databases.
@@ -219347,7 +221055,7 @@ SQLITE_API int sqlite3rbu_savestate(sqlite3rbu *pRbu);
**
** If the RBU update has been completely applied, mark the RBU database
** as fully applied. Otherwise, assuming no error has occurred, save the
-** current state of the RBU update appliation to the RBU database.
+** current state of the RBU update application to the RBU database.
**
** If an error has already occurred as part of an sqlite3rbu_step()
** or sqlite3rbu_open() call, or if one occurs within this function, an
@@ -224273,7 +225981,7 @@ static int rbuVfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
/* If this is an RBU vacuum operation and this is the target database,
** pretend that it has at least one page. Otherwise, SQLite will not
- ** check for the existance of a *-wal file. rbuVfsRead() contains
+ ** check for the existence of a *-wal file. rbuVfsRead() contains
** similar logic. */
if( rc==SQLITE_OK && *pSize==0
&& p->pRbu && rbuIsVacuum(p->pRbu)
@@ -226127,6 +227835,24 @@ static int dbpageRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){
return SQLITE_OK;
}
+/*
+** Open write transactions. Since we do not know in advance which database
+** files will be written by the sqlite_dbpage virtual table, start a write
+** transaction on them all.
+**
+** Return SQLITE_OK if successful, or an SQLite error code otherwise.
+*/
+static int dbpageBeginTrans(DbpageTable *pTab){
+ sqlite3 *db = pTab->db;
+ int rc = SQLITE_OK;
+ int i;
+ for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
+ Btree *pBt = db->aDb[i].pBt;
+ if( pBt ) rc = sqlite3BtreeBeginTrans(pBt, 1, 0);
+ }
+ return rc;
+}
+
static int dbpageUpdate(
sqlite3_vtab *pVtab,
int argc,
@@ -226187,13 +227913,19 @@ static int dbpageUpdate(
/* "INSERT INTO dbpage($PGNO,NULL)" causes page number $PGNO and
** all subsequent pages to be deleted. */
pTab->iDbTrunc = iDb;
- pgno--;
- pTab->pgnoTrunc = pgno;
+ pTab->pgnoTrunc = pgno-1;
+ pgno = 1;
}else{
zErr = "bad page value";
goto update_fail;
}
}
+
+ if( dbpageBeginTrans(pTab)!=SQLITE_OK ){
+ zErr = "failed to open transaction";
+ goto update_fail;
+ }
+
pPager = sqlite3BtreePager(pBt);
rc = sqlite3PagerGet(pPager, pgno, (DbPage**)&pDbPage, 0);
if( rc==SQLITE_OK ){
@@ -226203,28 +227935,21 @@ static int dbpageUpdate(
memcpy(aPage, pData, szPage);
pTab->pgnoTrunc = 0;
}
+ }else{
+ pTab->pgnoTrunc = 0;
}
sqlite3PagerUnref(pDbPage);
return rc;
update_fail:
+ pTab->pgnoTrunc = 0;
sqlite3_free(pVtab->zErrMsg);
pVtab->zErrMsg = sqlite3_mprintf("%s", zErr);
return SQLITE_ERROR;
}
-/* Since we do not know in advance which database files will be
-** written by the sqlite_dbpage virtual table, start a write transaction
-** on them all.
-*/
static int dbpageBegin(sqlite3_vtab *pVtab){
DbpageTable *pTab = (DbpageTable *)pVtab;
- sqlite3 *db = pTab->db;
- int i;
- for(i=0; i<db->nDb; i++){
- Btree *pBt = db->aDb[i].pBt;
- if( pBt ) (void)sqlite3BtreeBeginTrans(pBt, 1, 0);
- }
pTab->pgnoTrunc = 0;
return SQLITE_OK;
}
@@ -226236,7 +227961,11 @@ static int dbpageSync(sqlite3_vtab *pVtab){
if( pTab->pgnoTrunc>0 ){
Btree *pBt = pTab->db->aDb[pTab->iDbTrunc].pBt;
Pager *pPager = sqlite3BtreePager(pBt);
- sqlite3PagerTruncateImage(pPager, pTab->pgnoTrunc);
+ sqlite3BtreeEnter(pBt);
+ if( pTab->pgnoTrunc<sqlite3BtreeLastPage(pBt) ){
+ sqlite3PagerTruncateImage(pPager, pTab->pgnoTrunc);
+ }
+ sqlite3BtreeLeave(pBt);
}
pTab->pgnoTrunc = 0;
return SQLITE_OK;
@@ -226256,7 +227985,7 @@ static int dbpageRollbackTo(sqlite3_vtab *pVtab, int notUsed1){
*/
SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3 *db){
static sqlite3_module dbpage_module = {
- 0, /* iVersion */
+ 2, /* iVersion */
dbpageConnect, /* xCreate */
dbpageConnect, /* xConnect */
dbpageBestIndex, /* xBestIndex */
@@ -226431,11 +228160,13 @@ struct sqlite3_changeset_iter {
struct SessionTable {
SessionTable *pNext;
char *zName; /* Local name of table */
- int nCol; /* Number of columns in table zName */
+ int nCol; /* Number of non-hidden columns */
+ int nTotalCol; /* Number of columns including hidden */
int bStat1; /* True if this is sqlite_stat1 */
int bRowid; /* True if this table uses rowid for PK */
const char **azCol; /* Column names */
const char **azDflt; /* Default value expressions */
+ int *aiIdx; /* Index to pass to xNew/xOld */
u8 *abPK; /* Array of primary key flags */
int nEntry; /* Total number of entries in hash table */
int nChange; /* Size of apChange[] array */
@@ -226838,22 +228569,22 @@ static int sessionPreupdateHash(
unsigned int h = 0; /* Hash value to return */
int i; /* Used to iterate through columns */
+ assert( pTab->nTotalCol==pSession->hook.xCount(pSession->hook.pCtx) );
if( pTab->bRowid ){
- assert( pTab->nCol-1==pSession->hook.xCount(pSession->hook.pCtx) );
h = sessionHashAppendI64(h, iRowid);
}else{
assert( *pbNullPK==0 );
- assert( pTab->nCol==pSession->hook.xCount(pSession->hook.pCtx) );
for(i=0; i<pTab->nCol; i++){
if( pTab->abPK[i] ){
int rc;
int eType;
sqlite3_value *pVal;
+ int iIdx = pTab->aiIdx[i];
if( bNew ){
- rc = pSession->hook.xNew(pSession->hook.pCtx, i, &pVal);
+ rc = pSession->hook.xNew(pSession->hook.pCtx, iIdx, &pVal);
}else{
- rc = pSession->hook.xOld(pSession->hook.pCtx, i, &pVal);
+ rc = pSession->hook.xOld(pSession->hook.pCtx, iIdx, &pVal);
}
if( rc!=SQLITE_OK ) return rc;
@@ -227190,6 +228921,7 @@ static int sessionPreupdateEqual(
sqlite3_value *pVal; /* Value returned by preupdate_new/old */
int rc; /* Error code from preupdate_new/old */
int eType = *a++; /* Type of value from change record */
+ int iIdx = pTab->aiIdx[iCol];
/* The following calls to preupdate_new() and preupdate_old() can not
** fail. This is because they cache their return values, and by the
@@ -227198,10 +228930,10 @@ static int sessionPreupdateEqual(
** this (that the method has already been called). */
if( op==SQLITE_INSERT ){
/* assert( db->pPreUpdate->pNewUnpacked || db->pPreUpdate->aNew ); */
- rc = pSession->hook.xNew(pSession->hook.pCtx, iCol, &pVal);
+ rc = pSession->hook.xNew(pSession->hook.pCtx, iIdx, &pVal);
}else{
/* assert( db->pPreUpdate->pUnpacked ); */
- rc = pSession->hook.xOld(pSession->hook.pCtx, iCol, &pVal);
+ rc = pSession->hook.xOld(pSession->hook.pCtx, iIdx, &pVal);
}
assert( rc==SQLITE_OK );
(void)rc; /* Suppress warning about unused variable */
@@ -227326,9 +229058,11 @@ static int sessionTableInfo(
const char *zDb, /* Name of attached database (e.g. "main") */
const char *zThis, /* Table name */
int *pnCol, /* OUT: number of columns */
+ int *pnTotalCol, /* OUT: number of hidden columns */
const char **pzTab, /* OUT: Copy of zThis */
const char ***pazCol, /* OUT: Array of column names for table */
const char ***pazDflt, /* OUT: Array of default value expressions */
+ int **paiIdx, /* OUT: Array of xNew/xOld indexes */
u8 **pabPK, /* OUT: Array of booleans - true for PK col */
int *pbRowid /* OUT: True if only PK is a rowid */
){
@@ -227343,6 +229077,7 @@ static int sessionTableInfo(
char **azCol = 0;
char **azDflt = 0;
u8 *abPK = 0;
+ int *aiIdx = 0;
int bRowid = 0; /* Set to true to use rowid as PK */
assert( pazCol && pabPK );
@@ -227350,6 +229085,8 @@ static int sessionTableInfo(
*pazCol = 0;
*pabPK = 0;
*pnCol = 0;
+ if( pnTotalCol ) *pnTotalCol = 0;
+ if( paiIdx ) *paiIdx = 0;
if( pzTab ) *pzTab = 0;
if( pazDflt ) *pazDflt = 0;
@@ -227359,9 +229096,9 @@ static int sessionTableInfo(
if( rc==SQLITE_OK ){
/* For sqlite_stat1, pretend that (tbl,idx) is the PRIMARY KEY. */
zPragma = sqlite3_mprintf(
- "SELECT 0, 'tbl', '', 0, '', 1 UNION ALL "
- "SELECT 1, 'idx', '', 0, '', 2 UNION ALL "
- "SELECT 2, 'stat', '', 0, '', 0"
+ "SELECT 0, 'tbl', '', 0, '', 1, 0 UNION ALL "
+ "SELECT 1, 'idx', '', 0, '', 2, 0 UNION ALL "
+ "SELECT 2, 'stat', '', 0, '', 0, 0"
);
}else if( rc==SQLITE_ERROR ){
zPragma = sqlite3_mprintf("");
@@ -227369,7 +229106,7 @@ static int sessionTableInfo(
return rc;
}
}else{
- zPragma = sqlite3_mprintf("PRAGMA '%q'.table_info('%q')", zDb, zThis);
+ zPragma = sqlite3_mprintf("PRAGMA '%q'.table_xinfo('%q')", zDb, zThis);
}
if( !zPragma ){
return SQLITE_NOMEM;
@@ -227386,7 +229123,9 @@ static int sessionTableInfo(
while( SQLITE_ROW==sqlite3_step(pStmt) ){
nByte += sqlite3_column_bytes(pStmt, 1); /* name */
nByte += sqlite3_column_bytes(pStmt, 4); /* dflt_value */
- nDbCol++;
+ if( sqlite3_column_int(pStmt, 6)==0 ){ /* !hidden */
+ nDbCol++;
+ }
if( sqlite3_column_int(pStmt, 5) ) bRowid = 0; /* pk */
}
if( nDbCol==0 ) bRowid = 0;
@@ -227395,7 +229134,7 @@ static int sessionTableInfo(
rc = sqlite3_reset(pStmt);
if( rc==SQLITE_OK ){
- nByte += nDbCol * (sizeof(const char *)*2 + sizeof(u8) + 1 + 1);
+ nByte += nDbCol * (sizeof(const char *)*2 +sizeof(int)+sizeof(u8) + 1 + 1);
pAlloc = sessionMalloc64(pSession, nByte);
if( pAlloc==0 ){
rc = SQLITE_NOMEM;
@@ -227406,8 +229145,8 @@ static int sessionTableInfo(
if( rc==SQLITE_OK ){
azCol = (char **)pAlloc;
azDflt = (char**)&azCol[nDbCol];
- pAlloc = (u8 *)&azDflt[nDbCol];
- abPK = (u8 *)pAlloc;
+ aiIdx = (int*)&azDflt[nDbCol];
+ abPK = (u8 *)&aiIdx[nDbCol];
pAlloc = &abPK[nDbCol];
if( pzTab ){
memcpy(pAlloc, zThis, nThis+1);
@@ -227422,27 +229161,32 @@ static int sessionTableInfo(
azCol[i] = (char*)pAlloc;
pAlloc += nName+1;
abPK[i] = 1;
+ aiIdx[i] = -1;
i++;
}
while( SQLITE_ROW==sqlite3_step(pStmt) ){
- int nName = sqlite3_column_bytes(pStmt, 1);
- int nDflt = sqlite3_column_bytes(pStmt, 4);
- const unsigned char *zName = sqlite3_column_text(pStmt, 1);
- const unsigned char *zDflt = sqlite3_column_text(pStmt, 4);
-
- if( zName==0 ) break;
- memcpy(pAlloc, zName, nName+1);
- azCol[i] = (char *)pAlloc;
- pAlloc += nName+1;
- if( zDflt ){
- memcpy(pAlloc, zDflt, nDflt+1);
- azDflt[i] = (char *)pAlloc;
- pAlloc += nDflt+1;
- }else{
- azDflt[i] = 0;
+ if( sqlite3_column_int(pStmt, 6)==0 ){ /* !hidden */
+ int nName = sqlite3_column_bytes(pStmt, 1);
+ int nDflt = sqlite3_column_bytes(pStmt, 4);
+ const unsigned char *zName = sqlite3_column_text(pStmt, 1);
+ const unsigned char *zDflt = sqlite3_column_text(pStmt, 4);
+
+ if( zName==0 ) break;
+ memcpy(pAlloc, zName, nName+1);
+ azCol[i] = (char *)pAlloc;
+ pAlloc += nName+1;
+ if( zDflt ){
+ memcpy(pAlloc, zDflt, nDflt+1);
+ azDflt[i] = (char *)pAlloc;
+ pAlloc += nDflt+1;
+ }else{
+ azDflt[i] = 0;
+ }
+ abPK[i] = sqlite3_column_int(pStmt, 5);
+ aiIdx[i] = sqlite3_column_int(pStmt, 0);
+ i++;
}
- abPK[i] = sqlite3_column_int(pStmt, 5);
- i++;
+ if( pnTotalCol ) (*pnTotalCol)++;
}
rc = sqlite3_reset(pStmt);
}
@@ -227455,6 +229199,7 @@ static int sessionTableInfo(
if( pazDflt ) *pazDflt = (const char**)azDflt;
*pabPK = abPK;
*pnCol = nDbCol;
+ if( paiIdx ) *paiIdx = aiIdx;
}else{
sessionFree(pSession, azCol);
}
@@ -227466,7 +229211,7 @@ static int sessionTableInfo(
/*
** This function is called to initialize the SessionTable.nCol, azCol[]
** abPK[] and azDflt[] members of SessionTable object pTab. If these
-** fields are already initilialized, this function is a no-op.
+** fields are already initialized, this function is a no-op.
**
** If an error occurs, an error code is stored in sqlite3_session.rc and
** non-zero returned. Or, if no error occurs but the table has no primary
@@ -227485,8 +229230,11 @@ static int sessionInitTable(
if( pTab->nCol==0 ){
u8 *abPK;
assert( pTab->azCol==0 || pTab->abPK==0 );
+ sqlite3_free(pTab->azCol);
+ pTab->abPK = 0;
rc = sessionTableInfo(pSession, db, zDb,
- pTab->zName, &pTab->nCol, 0, &pTab->azCol, &pTab->azDflt, &abPK,
+ pTab->zName, &pTab->nCol, &pTab->nTotalCol, 0, &pTab->azCol,
+ &pTab->azDflt, &pTab->aiIdx, &abPK,
((pSession==0 || pSession->bImplicitPK) ? &pTab->bRowid : 0)
);
if( rc==SQLITE_OK ){
@@ -227521,15 +229269,17 @@ static int sessionInitTable(
*/
static int sessionReinitTable(sqlite3_session *pSession, SessionTable *pTab){
int nCol = 0;
+ int nTotalCol = 0;
const char **azCol = 0;
const char **azDflt = 0;
+ int *aiIdx = 0;
u8 *abPK = 0;
int bRowid = 0;
assert( pSession->rc==SQLITE_OK );
pSession->rc = sessionTableInfo(pSession, pSession->db, pSession->zDb,
- pTab->zName, &nCol, 0, &azCol, &azDflt, &abPK,
+ pTab->zName, &nCol, &nTotalCol, 0, &azCol, &azDflt, &aiIdx, &abPK,
(pSession->bImplicitPK ? &bRowid : 0)
);
if( pSession->rc==SQLITE_OK ){
@@ -227552,8 +229302,10 @@ static int sessionReinitTable(sqlite3_session *pSession, SessionTable *pTab){
const char **a = pTab->azCol;
pTab->azCol = azCol;
pTab->nCol = nCol;
+ pTab->nTotalCol = nTotalCol;
pTab->azDflt = azDflt;
pTab->abPK = abPK;
+ pTab->aiIdx = aiIdx;
azCol = a;
}
if( pSession->bEnableSize ){
@@ -227871,7 +229623,7 @@ static int sessionUpdateMaxSize(
int ii;
for(ii=0; ii<pTab->nCol; ii++){
sqlite3_value *p = 0;
- pSession->hook.xNew(pSession->hook.pCtx, ii, &p);
+ pSession->hook.xNew(pSession->hook.pCtx, pTab->aiIdx[ii], &p);
sessionSerializeValue(0, p, &nNew);
}
}
@@ -227891,8 +229643,9 @@ static int sessionUpdateMaxSize(
int bChanged = 1;
int nOld = 0;
int eType;
+ int iIdx = pTab->aiIdx[ii];
sqlite3_value *p = 0;
- pSession->hook.xNew(pSession->hook.pCtx, ii-pTab->bRowid, &p);
+ pSession->hook.xNew(pSession->hook.pCtx, iIdx, &p);
if( p==0 ){
return SQLITE_NOMEM;
}
@@ -227989,11 +229742,11 @@ static void sessionPreupdateOneChange(
/* Check the number of columns in this xPreUpdate call matches the
** number of columns in the table. */
nExpect = pSession->hook.xCount(pSession->hook.pCtx);
- if( (pTab->nCol-pTab->bRowid)<nExpect ){
+ if( pTab->nTotalCol<nExpect ){
if( sessionReinitTable(pSession, pTab) ) return;
if( sessionUpdateChanges(pSession, pTab) ) return;
}
- if( (pTab->nCol-pTab->bRowid)!=nExpect ){
+ if( pTab->nTotalCol!=nExpect ){
pSession->rc = SQLITE_SCHEMA;
return;
}
@@ -228050,14 +229803,15 @@ static void sessionPreupdateOneChange(
/* Figure out how large an allocation is required */
nByte = sizeof(SessionChange);
- for(i=0; i<(pTab->nCol-pTab->bRowid); i++){
+ for(i=pTab->bRowid; i<pTab->nCol; i++){
+ int iIdx = pTab->aiIdx[i];
sqlite3_value *p = 0;
if( op!=SQLITE_INSERT ){
/* This may fail if the column has a non-NULL default and was added
** using ALTER TABLE ADD COLUMN after this record was created. */
- rc = pSession->hook.xOld(pSession->hook.pCtx, i, &p);
+ rc = pSession->hook.xOld(pSession->hook.pCtx, iIdx, &p);
}else if( pTab->abPK[i] ){
- TESTONLY(int trc = ) pSession->hook.xNew(pSession->hook.pCtx, i, &p);
+ TESTONLY(int trc = ) pSession->hook.xNew(pSession->hook.pCtx,iIdx,&p);
assert( trc==SQLITE_OK );
}
@@ -228092,12 +229846,13 @@ static void sessionPreupdateOneChange(
sessionPutI64(&pC->aRecord[1], iRowid);
nByte = 9;
}
- for(i=0; i<(pTab->nCol-pTab->bRowid); i++){
+ for(i=pTab->bRowid; i<pTab->nCol; i++){
sqlite3_value *p = 0;
+ int iIdx = pTab->aiIdx[i];
if( op!=SQLITE_INSERT ){
- pSession->hook.xOld(pSession->hook.pCtx, i, &p);
+ pSession->hook.xOld(pSession->hook.pCtx, iIdx, &p);
}else if( pTab->abPK[i] ){
- pSession->hook.xNew(pSession->hook.pCtx, i, &p);
+ pSession->hook.xNew(pSession->hook.pCtx, iIdx, &p);
}
sessionSerializeValue(&pC->aRecord[nByte], p, &nByte);
}
@@ -228484,7 +230239,9 @@ SQLITE_API int sqlite3session_diff(
SessionTable *pTo; /* Table zTbl */
/* Locate and if necessary initialize the target table object */
+ pSession->bAutoAttach++;
rc = sessionFindTable(pSession, zTbl, &pTo);
+ pSession->bAutoAttach--;
if( pTo==0 ) goto diff_out;
if( sessionInitTable(pSession, pTo, pSession->db, pSession->zDb) ){
rc = pSession->rc;
@@ -228495,16 +230252,43 @@ SQLITE_API int sqlite3session_diff(
if( rc==SQLITE_OK ){
int bHasPk = 0;
int bMismatch = 0;
- int nCol; /* Columns in zFrom.zTbl */
+ int nCol = 0; /* Columns in zFrom.zTbl */
int bRowid = 0;
- u8 *abPK;
+ u8 *abPK = 0;
const char **azCol = 0;
- rc = sessionTableInfo(0, db, zFrom, zTbl, &nCol, 0, &azCol, 0, &abPK,
- pSession->bImplicitPK ? &bRowid : 0
- );
+ char *zDbExists = 0;
+
+ /* Check that database zFrom is attached. */
+ zDbExists = sqlite3_mprintf("SELECT * FROM %Q.sqlite_schema", zFrom);
+ if( zDbExists==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ sqlite3_stmt *pDbExists = 0;
+ rc = sqlite3_prepare_v2(db, zDbExists, -1, &pDbExists, 0);
+ if( rc==SQLITE_ERROR ){
+ rc = SQLITE_OK;
+ nCol = -1;
+ }
+ sqlite3_finalize(pDbExists);
+ sqlite3_free(zDbExists);
+ }
+
+ if( rc==SQLITE_OK && nCol==0 ){
+ rc = sessionTableInfo(0, db, zFrom, zTbl,
+ &nCol, 0, 0, &azCol, 0, 0, &abPK,
+ pSession->bImplicitPK ? &bRowid : 0
+ );
+ }
if( rc==SQLITE_OK ){
if( pTo->nCol!=nCol ){
- bMismatch = 1;
+ if( nCol<=0 ){
+ rc = SQLITE_SCHEMA;
+ if( pzErrMsg ){
+ *pzErrMsg = sqlite3_mprintf("no such table: %s.%s", zFrom, zTbl);
+ }
+ }else{
+ bMismatch = 1;
+ }
}else{
int i;
for(i=0; i<nCol; i++){
@@ -228823,9 +230607,11 @@ static void sessionAppendIdent(
char *zOut = (char *)&p->aBuf[p->nBuf];
const char *zIn = zStr;
*zOut++ = '"';
- while( *zIn ){
- if( *zIn=='"' ) *zOut++ = '"';
- *zOut++ = *(zIn++);
+ if( zIn!=0 ){
+ while( *zIn ){
+ if( *zIn=='"' ) *zOut++ = '"';
+ *zOut++ = *(zIn++);
+ }
}
*zOut++ = '"';
p->nBuf = (int)((u8 *)zOut - p->aBuf);
@@ -229076,10 +230862,10 @@ static int sessionSelectStmt(
int rc = SQLITE_OK;
char *zSql = 0;
const char *zSep = "";
- const char *zCols = bRowid ? SESSIONS_ROWID ", *" : "*";
int nSql = -1;
int i;
+ SessionBuffer cols = {0, 0, 0};
SessionBuffer nooptest = {0, 0, 0};
SessionBuffer pkfield = {0, 0, 0};
SessionBuffer pkvar = {0, 0, 0};
@@ -229092,9 +230878,16 @@ static int sessionSelectStmt(
sessionAppendStr(&pkvar,
"?1, (CASE WHEN ?2=X'' THEN NULL ELSE ?2 END)", &rc
);
- zCols = "tbl, ?2, stat";
+ sessionAppendStr(&cols, "tbl, ?2, stat", &rc);
}else{
+ #if 0
+ if( bRowid ){
+ sessionAppendStr(&cols, SESSIONS_ROWID, &rc);
+ }
+ #endif
for(i=0; i<nCol; i++){
+ if( cols.nBuf ) sessionAppendStr(&cols, ", ", &rc);
+ sessionAppendIdent(&cols, azCol[i], &rc);
if( abPK[i] ){
sessionAppendStr(&pkfield, zSep, &rc);
sessionAppendStr(&pkvar, zSep, &rc);
@@ -229112,7 +230905,7 @@ static int sessionSelectStmt(
if( rc==SQLITE_OK ){
zSql = sqlite3_mprintf(
"SELECT %s%s FROM %Q.%Q WHERE (%s) IS (%s)",
- zCols, (bIgnoreNoop ? (char*)nooptest.aBuf : ""),
+ (char*)cols.aBuf, (bIgnoreNoop ? (char*)nooptest.aBuf : ""),
zDb, zTab, (char*)pkfield.aBuf, (char*)pkvar.aBuf
);
if( zSql==0 ) rc = SQLITE_NOMEM;
@@ -229155,6 +230948,7 @@ static int sessionSelectStmt(
sqlite3_free(nooptest.aBuf);
sqlite3_free(pkfield.aBuf);
sqlite3_free(pkvar.aBuf);
+ sqlite3_free(cols.aBuf);
return rc;
}
@@ -229270,7 +231064,7 @@ static int sessionGenerateChangeset(
){
sqlite3 *db = pSession->db; /* Source database handle */
SessionTable *pTab; /* Used to iterate through attached tables */
- SessionBuffer buf = {0,0,0}; /* Buffer in which to accumlate changeset */
+ SessionBuffer buf = {0,0,0}; /* Buffer in which to accumulate changeset */
int rc; /* Return code */
assert( xOutput==0 || (pnChangeset==0 && ppChangeset==0) );
@@ -229623,14 +231417,15 @@ SQLITE_API int sqlite3changeset_start_v2_strm(
** object and the buffer is full, discard some data to free up space.
*/
static void sessionDiscardData(SessionInput *pIn){
- if( pIn->xInput && pIn->iNext>=sessions_strm_chunk_size ){
- int nMove = pIn->buf.nBuf - pIn->iNext;
+ if( pIn->xInput && pIn->iCurrent>=sessions_strm_chunk_size ){
+ int nMove = pIn->buf.nBuf - pIn->iCurrent;
assert( nMove>=0 );
if( nMove>0 ){
- memmove(pIn->buf.aBuf, &pIn->buf.aBuf[pIn->iNext], nMove);
+ memmove(pIn->buf.aBuf, &pIn->buf.aBuf[pIn->iCurrent], nMove);
}
- pIn->buf.nBuf -= pIn->iNext;
- pIn->iNext = 0;
+ pIn->buf.nBuf -= pIn->iCurrent;
+ pIn->iNext -= pIn->iCurrent;
+ pIn->iCurrent = 0;
pIn->nData = pIn->buf.nBuf;
}
}
@@ -229984,8 +231779,8 @@ static int sessionChangesetNextOne(
p->rc = sessionInputBuffer(&p->in, 2);
if( p->rc!=SQLITE_OK ) return p->rc;
- sessionDiscardData(&p->in);
p->in.iCurrent = p->in.iNext;
+ sessionDiscardData(&p->in);
/* If the iterator is already at the end of the changeset, return DONE. */
if( p->in.iNext>=p->in.nData ){
@@ -231495,7 +233290,8 @@ static int sessionChangesetApply(
sqlite3changeset_pk(pIter, &abPK, 0);
rc = sessionTableInfo(0, db, "main", zNew,
- &sApply.nCol, &zTab, &sApply.azCol, 0, &sApply.abPK, &sApply.bRowid
+ &sApply.nCol, 0, &zTab, &sApply.azCol, 0, 0,
+ &sApply.abPK, &sApply.bRowid
);
if( rc!=SQLITE_OK ) break;
for(i=0; i<sApply.nCol; i++){
@@ -231575,12 +233371,17 @@ static int sessionChangesetApply(
}
}
}
- sqlite3_exec(db, "PRAGMA defer_foreign_keys = 0", 0, 0, 0);
+
+ {
+ int rc2 = sqlite3_exec(db, "PRAGMA defer_foreign_keys = 0", 0, 0, 0);
+ if( rc==SQLITE_OK ) rc = rc2;
+ }
if( (flags & SQLITE_CHANGESETAPPLY_NOSAVEPOINT)==0 ){
if( rc==SQLITE_OK ){
rc = sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0);
- }else{
+ }
+ if( rc!=SQLITE_OK ){
sqlite3_exec(db, "ROLLBACK TO changeset_apply", 0, 0, 0);
sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0);
}
@@ -232338,14 +234139,19 @@ SQLITE_API int sqlite3changegroup_add_change(
sqlite3_changegroup *pGrp,
sqlite3_changeset_iter *pIter
){
+ int rc = SQLITE_OK;
+
if( pIter->in.iCurrent==pIter->in.iNext
|| pIter->rc!=SQLITE_OK
|| pIter->bInvert
){
/* Iterator does not point to any valid entry or is an INVERT iterator. */
- return SQLITE_ERROR;
+ rc = SQLITE_ERROR;
+ }else{
+ pIter->in.bNoDiscard = 1;
+ rc = sessionOneChangeToHash(pGrp, pIter, 0);
}
- return sessionOneChangeToHash(pGrp, pIter, 0);
+ return rc;
}
/*
@@ -233166,14 +234972,29 @@ struct Fts5PhraseIter {
** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise,
** output variable (*ppToken) is set to point to a buffer containing the
** matching document token, and (*pnToken) to the size of that buffer in
-** bytes. This API is not available if the specified token matches a
-** prefix query term. In that case both output variables are always set
-** to 0.
+** bytes.
**
** The output text is not a copy of the document text that was tokenized.
** It is the output of the tokenizer module. For tokendata=1 tables, this
** includes any embedded 0x00 and trailing data.
**
+** This API may be slow in some cases if the token identified by parameters
+** iIdx and iToken matched a prefix token in the query. In most cases, the
+** first call to this API for each prefix token in the query is forced
+** to scan the portion of the full-text index that matches the prefix
+** token to collect the extra data required by this API. If the prefix
+** token matches a large number of token instances in the document set,
+** this may be a performance problem.
+**
+** If the user knows in advance that a query may use this API for a
+** prefix token, FTS5 may be configured to collect all required data as part
+** of the initial querying of the full-text index, avoiding the second scan
+** entirely. This also causes prefix queries that do not use this API to
+** run more slowly and use more memory. FTS5 may be configured in this way
+** either on a per-table basis using the [FTS5 insttoken | 'insttoken']
+** option, or on a per-query basis using the
+** [fts5_insttoken | fts5_insttoken()] user function.
+**
** This API can be quite slow if used with an FTS5 table created with the
** "detail=none" or "detail=column" option.
**
@@ -233683,6 +235504,18 @@ typedef sqlite3_uint64 u64;
# define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&7)==0)
#endif
+/*
+** Macros needed to provide flexible arrays in a portable way
+*/
+#ifndef offsetof
+# define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD))
+#endif
+#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+# define FLEXARRAY
+#else
+# define FLEXARRAY 1
+#endif
+
#endif
/* Truncate very long tokens to this many bytes. Hard limit is
@@ -233755,10 +235588,11 @@ typedef struct Fts5Colset Fts5Colset;
*/
struct Fts5Colset {
int nCol;
- int aiCol[1];
+ int aiCol[FLEXARRAY];
};
-
+/* Size (int bytes) of a complete Fts5Colset object with N columns. */
+#define SZ_FTS5COLSET(N) (sizeof(i64)*((N+2)/2))
/**************************************************************************
** Interface to code in fts5_config.c. fts5_config.c contains contains code
@@ -233855,7 +235689,8 @@ struct Fts5Config {
char *zRank; /* Name of rank function */
char *zRankArgs; /* Arguments to rank function */
int bSecureDelete; /* 'secure-delete' */
- int nDeleteMerge; /* 'deletemerge' */
+ int nDeleteMerge; /* 'deletemerge' */
+ int bPrefixInsttoken; /* 'prefix-insttoken' */
/* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */
char **pzErrmsg;
@@ -234112,7 +235947,14 @@ static int sqlite3Fts5StructureTest(Fts5Index*, void*);
/*
** Used by xInstToken():
*/
-static int sqlite3Fts5IterToken(Fts5IndexIter*, i64, int, int, const char**, int*);
+static int sqlite3Fts5IterToken(
+ Fts5IndexIter *pIndexIter,
+ const char *pToken, int nToken,
+ i64 iRowid,
+ int iCol,
+ int iOff,
+ const char **ppOut, int *pnOut
+);
/*
** Insert or remove data to or from the index. Each time a document is
@@ -234579,7 +236421,7 @@ static void sqlite3Fts5UnicodeAscii(u8*, u8*);
**
** The "lemon" program processes an LALR(1) input grammar file, then uses
** this template to construct a parser. The "lemon" program inserts text
-** at each "%%" line. Also, any "P-a-r-s-e" identifer prefix (without the
+** at each "%%" line. Also, any "P-a-r-s-e" identifier prefix (without the
** interstitial "-" characters) contained in this template is changed into
** the value of the %name directive from the grammar. Otherwise, the content
** of this template is copied straight through into the generate parser
@@ -236733,7 +238575,7 @@ static int fts5Bm25GetData(
** under consideration.
**
** The problem with this is that if (N < 2*nHit), the IDF is
- ** negative. Which is undesirable. So the mimimum allowable IDF is
+ ** negative. Which is undesirable. So the minimum allowable IDF is
** (1e-6) - roughly the same as a term that appears in just over
** half of set of 5,000,000 documents. */
double idf = log( (nRow - nHit + 0.5) / (nHit + 0.5) );
@@ -237196,7 +239038,7 @@ static char *sqlite3Fts5Strndup(int *pRc, const char *pIn, int nIn){
** * The 52 upper and lower case ASCII characters, and
** * The 10 integer ASCII characters.
** * The underscore character "_" (0x5F).
-** * The unicode "subsitute" character (0x1A).
+** * The unicode "substitute" character (0x1A).
*/
static int sqlite3Fts5IsBareword(char t){
u8 aBareword[128] = {
@@ -238326,6 +240168,19 @@ static int sqlite3Fts5ConfigSetValue(
}else{
pConfig->bSecureDelete = (bVal ? 1 : 0);
}
+ }
+
+ else if( 0==sqlite3_stricmp(zKey, "insttoken") ){
+ int bVal = -1;
+ if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
+ bVal = sqlite3_value_int(pVal);
+ }
+ if( bVal<0 ){
+ *pbBadkey = 1;
+ }else{
+ pConfig->bPrefixInsttoken = (bVal ? 1 : 0);
+ }
+
}else{
*pbBadkey = 1;
}
@@ -238501,9 +240356,13 @@ struct Fts5ExprNode {
/* Child nodes. For a NOT node, this array always contains 2 entries. For
** AND or OR nodes, it contains 2 or more entries. */
int nChild; /* Number of child nodes */
- Fts5ExprNode *apChild[1]; /* Array of child nodes */
+ Fts5ExprNode *apChild[FLEXARRAY]; /* Array of child nodes */
};
+/* Size (in bytes) of an Fts5ExprNode object that holds up to N children */
+#define SZ_FTS5EXPRNODE(N) \
+ (offsetof(Fts5ExprNode,apChild) + (N)*sizeof(Fts5ExprNode*))
+
#define Fts5NodeIsString(p) ((p)->eType==FTS5_TERM || (p)->eType==FTS5_STRING)
/*
@@ -238534,9 +240393,13 @@ struct Fts5ExprPhrase {
Fts5ExprNode *pNode; /* FTS5_STRING node this phrase is part of */
Fts5Buffer poslist; /* Current position list */
int nTerm; /* Number of entries in aTerm[] */
- Fts5ExprTerm aTerm[1]; /* Terms that make up this phrase */
+ Fts5ExprTerm aTerm[FLEXARRAY]; /* Terms that make up this phrase */
};
+/* Size (in bytes) of an Fts5ExprPhrase object that holds up to N terms */
+#define SZ_FTS5EXPRPHRASE(N) \
+ (offsetof(Fts5ExprPhrase,aTerm) + (N)*sizeof(Fts5ExprTerm))
+
/*
** One or more phrases that must appear within a certain token distance of
** each other within each matching document.
@@ -238545,9 +240408,12 @@ struct Fts5ExprNearset {
int nNear; /* NEAR parameter */
Fts5Colset *pColset; /* Columns to search (NULL -> all columns) */
int nPhrase; /* Number of entries in aPhrase[] array */
- Fts5ExprPhrase *apPhrase[1]; /* Array of phrase pointers */
+ Fts5ExprPhrase *apPhrase[FLEXARRAY]; /* Array of phrase pointers */
};
+/* Size (in bytes) of an Fts5ExprNearset object covering up to N phrases */
+#define SZ_FTS5EXPRNEARSET(N) \
+ (offsetof(Fts5ExprNearset,apPhrase)+(N)*sizeof(Fts5ExprPhrase*))
/*
** Parse context.
@@ -238707,7 +240573,7 @@ static int sqlite3Fts5ExprNew(
/* If the LHS of the MATCH expression was a user column, apply the
** implicit column-filter. */
if( sParse.rc==SQLITE_OK && iCol<pConfig->nCol ){
- int n = sizeof(Fts5Colset);
+ int n = SZ_FTS5COLSET(1);
Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&sParse.rc, n);
if( pColset ){
pColset->nCol = 1;
@@ -240065,7 +241931,7 @@ static Fts5ExprNearset *sqlite3Fts5ParseNearset(
if( pParse->rc==SQLITE_OK ){
if( pNear==0 ){
sqlite3_int64 nByte;
- nByte = sizeof(Fts5ExprNearset) + SZALLOC * sizeof(Fts5ExprPhrase*);
+ nByte = SZ_FTS5EXPRNEARSET(SZALLOC+1);
pRet = sqlite3_malloc64(nByte);
if( pRet==0 ){
pParse->rc = SQLITE_NOMEM;
@@ -240076,7 +241942,7 @@ static Fts5ExprNearset *sqlite3Fts5ParseNearset(
int nNew = pNear->nPhrase + SZALLOC;
sqlite3_int64 nByte;
- nByte = sizeof(Fts5ExprNearset) + nNew * sizeof(Fts5ExprPhrase*);
+ nByte = SZ_FTS5EXPRNEARSET(nNew+1);
pRet = (Fts5ExprNearset*)sqlite3_realloc64(pNear, nByte);
if( pRet==0 ){
pParse->rc = SQLITE_NOMEM;
@@ -240167,12 +242033,12 @@ static int fts5ParseTokenize(
int nNew = SZALLOC + (pPhrase ? pPhrase->nTerm : 0);
pNew = (Fts5ExprPhrase*)sqlite3_realloc64(pPhrase,
- sizeof(Fts5ExprPhrase) + sizeof(Fts5ExprTerm) * nNew
+ SZ_FTS5EXPRPHRASE(nNew+1)
);
if( pNew==0 ){
rc = SQLITE_NOMEM;
}else{
- if( pPhrase==0 ) memset(pNew, 0, sizeof(Fts5ExprPhrase));
+ if( pPhrase==0 ) memset(pNew, 0, SZ_FTS5EXPRPHRASE(1));
pCtx->pPhrase = pPhrase = pNew;
pNew->nTerm = nNew - SZALLOC;
}
@@ -240280,7 +242146,7 @@ static Fts5ExprPhrase *sqlite3Fts5ParseTerm(
if( sCtx.pPhrase==0 ){
/* This happens when parsing a token or quoted phrase that contains
** no token characters at all. (e.g ... MATCH '""'). */
- sCtx.pPhrase = sqlite3Fts5MallocZero(&pParse->rc, sizeof(Fts5ExprPhrase));
+ sCtx.pPhrase = sqlite3Fts5MallocZero(&pParse->rc, SZ_FTS5EXPRPHRASE(1));
}else if( sCtx.pPhrase->nTerm ){
sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = (u8)bPrefix;
}
@@ -240315,19 +242181,18 @@ static int sqlite3Fts5ExprClonePhrase(
sizeof(Fts5ExprPhrase*));
}
if( rc==SQLITE_OK ){
- pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc,
- sizeof(Fts5ExprNode));
+ pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc, SZ_FTS5EXPRNODE(1));
}
if( rc==SQLITE_OK ){
pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc,
- sizeof(Fts5ExprNearset) + sizeof(Fts5ExprPhrase*));
+ SZ_FTS5EXPRNEARSET(2));
}
if( rc==SQLITE_OK && ALWAYS(pOrig!=0) ){
Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset;
if( pColsetOrig ){
sqlite3_int64 nByte;
Fts5Colset *pColset;
- nByte = sizeof(Fts5Colset) + (pColsetOrig->nCol-1) * sizeof(int);
+ nByte = SZ_FTS5COLSET(pColsetOrig->nCol);
pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte);
if( pColset ){
memcpy(pColset, pColsetOrig, (size_t)nByte);
@@ -240355,7 +242220,7 @@ static int sqlite3Fts5ExprClonePhrase(
}else{
/* This happens when parsing a token or quoted phrase that contains
** no token characters at all. (e.g ... MATCH '""'). */
- sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase));
+ sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, SZ_FTS5EXPRPHRASE(1));
}
}
@@ -240420,7 +242285,8 @@ static void sqlite3Fts5ParseSetDistance(
);
return;
}
- nNear = nNear * 10 + (p->p[i] - '0');
+ if( nNear<214748363 ) nNear = nNear * 10 + (p->p[i] - '0');
+ /* ^^^^^^^^^^^^^^^--- Prevent integer overflow */
}
}else{
nNear = FTS5_DEFAULT_NEARDIST;
@@ -240449,7 +242315,7 @@ static Fts5Colset *fts5ParseColset(
assert( pParse->rc==SQLITE_OK );
assert( iCol>=0 && iCol<pParse->pConfig->nCol );
- pNew = sqlite3_realloc64(p, sizeof(Fts5Colset) + sizeof(int)*nCol);
+ pNew = sqlite3_realloc64(p, SZ_FTS5COLSET(nCol+1));
if( pNew==0 ){
pParse->rc = SQLITE_NOMEM;
}else{
@@ -240484,7 +242350,7 @@ static Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse *pParse, Fts5Colset *p
int nCol = pParse->pConfig->nCol;
pRet = (Fts5Colset*)sqlite3Fts5MallocZero(&pParse->rc,
- sizeof(Fts5Colset) + sizeof(int)*nCol
+ SZ_FTS5COLSET(nCol+1)
);
if( pRet ){
int i;
@@ -240545,7 +242411,7 @@ static Fts5Colset *sqlite3Fts5ParseColset(
static Fts5Colset *fts5CloneColset(int *pRc, Fts5Colset *pOrig){
Fts5Colset *pRet;
if( pOrig ){
- sqlite3_int64 nByte = sizeof(Fts5Colset) + (pOrig->nCol-1) * sizeof(int);
+ sqlite3_int64 nByte = SZ_FTS5COLSET(pOrig->nCol);
pRet = (Fts5Colset*)sqlite3Fts5MallocZero(pRc, nByte);
if( pRet ){
memcpy(pRet, pOrig, (size_t)nByte);
@@ -240713,7 +242579,7 @@ static Fts5ExprNode *fts5ParsePhraseToAnd(
assert( pNear->nPhrase==1 );
assert( pParse->bPhraseToAnd );
- nByte = sizeof(Fts5ExprNode) + nTerm*sizeof(Fts5ExprNode*);
+ nByte = SZ_FTS5EXPRNODE(nTerm+1);
pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
if( pRet ){
pRet->eType = FTS5_AND;
@@ -240723,7 +242589,7 @@ static Fts5ExprNode *fts5ParsePhraseToAnd(
pParse->nPhrase--;
for(ii=0; ii<nTerm; ii++){
Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero(
- &pParse->rc, sizeof(Fts5ExprPhrase)
+ &pParse->rc, SZ_FTS5EXPRPHRASE(1)
);
if( pPhrase ){
if( parseGrowPhraseArray(pParse) ){
@@ -240792,7 +242658,7 @@ static Fts5ExprNode *sqlite3Fts5ParseNode(
if( pRight->eType==eType ) nChild += pRight->nChild-1;
}
- nByte = sizeof(Fts5ExprNode) + sizeof(Fts5ExprNode*)*(nChild-1);
+ nByte = SZ_FTS5EXPRNODE(nChild);
pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
if( pRet ){
@@ -241461,7 +243327,7 @@ static int fts5ExprPopulatePoslistsCb(
int rc = sqlite3Fts5PoslistWriterAppend(
&pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff
);
- if( rc==SQLITE_OK && pExpr->pConfig->bTokendata && !pT->bPrefix ){
+ if( rc==SQLITE_OK && (pExpr->pConfig->bTokendata || pT->bPrefix) ){
int iCol = p->iOff>>32;
int iTokOff = p->iOff & 0x7FFFFFFF;
rc = sqlite3Fts5IndexIterWriteTokendata(
@@ -241654,21 +243520,20 @@ static int sqlite3Fts5ExprInstToken(
return SQLITE_RANGE;
}
pTerm = &pPhrase->aTerm[iToken];
- if( pTerm->bPrefix==0 ){
- if( pExpr->pConfig->bTokendata ){
- rc = sqlite3Fts5IterToken(
- pTerm->pIter, iRowid, iCol, iOff+iToken, ppOut, pnOut
- );
- }else{
- *ppOut = pTerm->pTerm;
- *pnOut = pTerm->nFullTerm;
- }
+ if( pExpr->pConfig->bTokendata || pTerm->bPrefix ){
+ rc = sqlite3Fts5IterToken(
+ pTerm->pIter, pTerm->pTerm, pTerm->nQueryTerm,
+ iRowid, iCol, iOff+iToken, ppOut, pnOut
+ );
+ }else{
+ *ppOut = pTerm->pTerm;
+ *pnOut = pTerm->nFullTerm;
}
return rc;
}
/*
-** Clear the token mappings for all Fts5IndexIter objects mannaged by
+** Clear the token mappings for all Fts5IndexIter objects managed by
** the expression passed as the only argument.
*/
static void sqlite3Fts5ExprClearTokens(Fts5Expr *pExpr){
@@ -241703,7 +243568,7 @@ typedef struct Fts5HashEntry Fts5HashEntry;
/*
** This file contains the implementation of an in-memory hash table used
-** to accumuluate "term -> doclist" content before it is flused to a level-0
+** to accumulate "term -> doclist" content before it is flushed to a level-0
** segment.
*/
@@ -241760,7 +243625,7 @@ struct Fts5HashEntry {
};
/*
-** Eqivalent to:
+** Equivalent to:
**
** char *fts5EntryKey(Fts5HashEntry *pEntry){ return zKey; }
*/
@@ -242696,9 +244561,13 @@ struct Fts5Structure {
u64 nOriginCntr; /* Origin value for next top-level segment */
int nSegment; /* Total segments in this structure */
int nLevel; /* Number of levels in this index */
- Fts5StructureLevel aLevel[1]; /* Array of nLevel level objects */
+ Fts5StructureLevel aLevel[FLEXARRAY]; /* Array of nLevel level objects */
};
+/* Size (in bytes) of an Fts5Structure object holding up to N levels */
+#define SZ_FTS5STRUCTURE(N) \
+ (offsetof(Fts5Structure,aLevel) + (N)*sizeof(Fts5StructureLevel))
+
/*
** An object of type Fts5SegWriter is used to write to segments.
*/
@@ -242828,11 +244697,15 @@ struct Fts5SegIter {
** Array of tombstone pages. Reference counted.
*/
struct Fts5TombstoneArray {
- int nRef; /* Number of pointers to this object */
+ int nRef; /* Number of pointers to this object */
int nTombstone;
- Fts5Data *apTombstone[1]; /* Array of tombstone pages */
+ Fts5Data *apTombstone[FLEXARRAY]; /* Array of tombstone pages */
};
+/* Size (in bytes) of an Fts5TombstoneArray holding up to N tombstones */
+#define SZ_FTS5TOMBSTONEARRAY(N) \
+ (offsetof(Fts5TombstoneArray,apTombstone)+(N)*sizeof(Fts5Data*))
+
/*
** Argument is a pointer to an Fts5Data structure that contains a
** leaf page.
@@ -242901,9 +244774,12 @@ struct Fts5Iter {
i64 iSwitchRowid; /* Firstest rowid of other than aFirst[1] */
Fts5CResult *aFirst; /* Current merge state (see above) */
- Fts5SegIter aSeg[1]; /* Array of segment iterators */
+ Fts5SegIter aSeg[FLEXARRAY]; /* Array of segment iterators */
};
+/* Size (in bytes) of an Fts5Iter object holding up to N segment iterators */
+#define SZ_FTS5ITER(N) (offsetof(Fts5Iter,aSeg)+(N)*sizeof(Fts5SegIter))
+
/*
** An instance of the following type is used to iterate through the contents
** of a doclist-index record.
@@ -242930,9 +244806,13 @@ struct Fts5DlidxLvl {
struct Fts5DlidxIter {
int nLvl;
int iSegid;
- Fts5DlidxLvl aLvl[1];
+ Fts5DlidxLvl aLvl[FLEXARRAY];
};
+/* Size (in bytes) of an Fts5DlidxIter object with up to N levels */
+#define SZ_FTS5DLIDXITER(N) \
+ (offsetof(Fts5DlidxIter,aLvl)+(N)*sizeof(Fts5DlidxLvl))
+
static void fts5PutU16(u8 *aOut, u16 iVal){
aOut[0] = (iVal>>8);
aOut[1] = (iVal&0xFF);
@@ -243052,11 +244932,13 @@ static int fts5LeafFirstTermOff(Fts5Data *pLeaf){
/*
** Close the read-only blob handle, if it is open.
*/
-static void sqlite3Fts5IndexCloseReader(Fts5Index *p){
+static void fts5IndexCloseReader(Fts5Index *p){
if( p->pReader ){
+ int rc;
sqlite3_blob *pReader = p->pReader;
p->pReader = 0;
- sqlite3_blob_close(pReader);
+ rc = sqlite3_blob_close(pReader);
+ if( p->rc==SQLITE_OK ) p->rc = rc;
}
}
@@ -243081,7 +244963,7 @@ static Fts5Data *fts5DataRead(Fts5Index *p, i64 iRowid){
assert( p->pReader==0 );
p->pReader = pBlob;
if( rc!=SQLITE_OK ){
- sqlite3Fts5IndexCloseReader(p);
+ fts5IndexCloseReader(p);
}
if( rc==SQLITE_ABORT ) rc = SQLITE_OK;
}
@@ -243165,9 +245047,13 @@ static int fts5IndexPrepareStmt(
){
if( p->rc==SQLITE_OK ){
if( zSql ){
- p->rc = sqlite3_prepare_v3(p->pConfig->db, zSql, -1,
+ int rc = sqlite3_prepare_v3(p->pConfig->db, zSql, -1,
SQLITE_PREPARE_PERSISTENT|SQLITE_PREPARE_NO_VTAB,
ppStmt, 0);
+ /* If this prepare() call fails with SQLITE_ERROR, then one of the
+ ** %_idx or %_data tables has been removed or modified. Call this
+ ** corruption. */
+ p->rc = (rc==SQLITE_ERROR ? SQLITE_CORRUPT : rc);
}else{
p->rc = SQLITE_NOMEM;
}
@@ -243294,7 +245180,7 @@ static int sqlite3Fts5StructureTest(Fts5Index *p, void *pStruct){
static void fts5StructureMakeWritable(int *pRc, Fts5Structure **pp){
Fts5Structure *p = *pp;
if( *pRc==SQLITE_OK && p->nRef>1 ){
- i64 nByte = sizeof(Fts5Structure)+(p->nLevel-1)*sizeof(Fts5StructureLevel);
+ i64 nByte = SZ_FTS5STRUCTURE(p->nLevel);
Fts5Structure *pNew;
pNew = (Fts5Structure*)sqlite3Fts5MallocZero(pRc, nByte);
if( pNew ){
@@ -243368,10 +245254,7 @@ static int fts5StructureDecode(
){
return FTS5_CORRUPT;
}
- nByte = (
- sizeof(Fts5Structure) + /* Main structure */
- sizeof(Fts5StructureLevel) * (nLevel-1) /* aLevel[] array */
- );
+ nByte = SZ_FTS5STRUCTURE(nLevel);
pRet = (Fts5Structure*)sqlite3Fts5MallocZero(&rc, nByte);
if( pRet ){
@@ -243451,10 +245334,7 @@ static void fts5StructureAddLevel(int *pRc, Fts5Structure **ppStruct){
if( *pRc==SQLITE_OK ){
Fts5Structure *pStruct = *ppStruct;
int nLevel = pStruct->nLevel;
- sqlite3_int64 nByte = (
- sizeof(Fts5Structure) + /* Main structure */
- sizeof(Fts5StructureLevel) * (nLevel+1) /* aLevel[] array */
- );
+ sqlite3_int64 nByte = SZ_FTS5STRUCTURE(nLevel+2);
pStruct = sqlite3_realloc64(pStruct, nByte);
if( pStruct ){
@@ -243993,7 +245873,7 @@ static Fts5DlidxIter *fts5DlidxIterInit(
int bDone = 0;
for(i=0; p->rc==SQLITE_OK && bDone==0; i++){
- sqlite3_int64 nByte = sizeof(Fts5DlidxIter) + i * sizeof(Fts5DlidxLvl);
+ sqlite3_int64 nByte = SZ_FTS5DLIDXITER(i+1);
Fts5DlidxIter *pNew;
pNew = (Fts5DlidxIter*)sqlite3_realloc64(pIter, nByte);
@@ -244211,7 +246091,7 @@ static void fts5SegIterSetNext(Fts5Index *p, Fts5SegIter *pIter){
static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){
const int nTomb = pIter->pSeg->nPgTombstone;
if( nTomb>0 ){
- int nByte = nTomb * sizeof(Fts5Data*) + sizeof(Fts5TombstoneArray);
+ int nByte = SZ_FTS5TOMBSTONEARRAY(nTomb+1);
Fts5TombstoneArray *pNew;
pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte);
if( pNew ){
@@ -245672,8 +247552,7 @@ static Fts5Iter *fts5MultiIterAlloc(
for(nSlot=2; nSlot<nSeg; nSlot=nSlot*2);
pNew = fts5IdxMalloc(p,
- sizeof(Fts5Iter) + /* pNew */
- sizeof(Fts5SegIter) * (nSlot-1) + /* pNew->aSeg[] */
+ SZ_FTS5ITER(nSlot) + /* pNew + pNew->aSeg[] */
sizeof(Fts5CResult) * nSlot /* pNew->aFirst[] */
);
if( pNew ){
@@ -247279,6 +249158,14 @@ static int fts5IndexReturn(Fts5Index *p){
return rc;
}
+/*
+** Close the read-only blob handle, if it is open.
+*/
+static void sqlite3Fts5IndexCloseReader(Fts5Index *p){
+ fts5IndexCloseReader(p);
+ fts5IndexReturn(p);
+}
+
typedef struct Fts5FlushCtx Fts5FlushCtx;
struct Fts5FlushCtx {
Fts5Index *pIdx;
@@ -247466,7 +249353,7 @@ static void fts5DoSecureDelete(
int iDelKeyOff = 0; /* Offset of deleted key, if any */
nIdx = nPg-iPgIdx;
- aIdx = sqlite3Fts5MallocZero(&p->rc, nIdx+16);
+ aIdx = sqlite3Fts5MallocZero(&p->rc, ((i64)nIdx)+16);
if( p->rc ) return;
memcpy(aIdx, &aPg[iPgIdx], nIdx);
@@ -247736,8 +249623,11 @@ static void fts5DoSecureDelete(
** This is called as part of flushing a delete to disk in 'secure-delete'
** mode. It edits the segments within the database described by argument
** pStruct to remove the entries for term zTerm, rowid iRowid.
+**
+** Return SQLITE_OK if successful, or an SQLite error code if an error
+** has occurred. Any error code is also stored in the Fts5Index handle.
*/
-static void fts5FlushSecureDelete(
+static int fts5FlushSecureDelete(
Fts5Index *p,
Fts5Structure *pStruct,
const char *zTerm,
@@ -247747,6 +249637,24 @@ static void fts5FlushSecureDelete(
const int f = FTS5INDEX_QUERY_SKIPHASH;
Fts5Iter *pIter = 0; /* Used to find term instance */
+ /* If the version number has not been set to SECUREDELETE, do so now. */
+ if( p->pConfig->iVersion!=FTS5_CURRENT_VERSION_SECUREDELETE ){
+ Fts5Config *pConfig = p->pConfig;
+ sqlite3_stmt *pStmt = 0;
+ fts5IndexPrepareStmt(p, &pStmt, sqlite3_mprintf(
+ "REPLACE INTO %Q.'%q_config' VALUES ('version', %d)",
+ pConfig->zDb, pConfig->zName, FTS5_CURRENT_VERSION_SECUREDELETE
+ ));
+ if( p->rc==SQLITE_OK ){
+ int rc;
+ sqlite3_step(pStmt);
+ rc = sqlite3_finalize(pStmt);
+ if( p->rc==SQLITE_OK ) p->rc = rc;
+ pConfig->iCookie++;
+ pConfig->iVersion = FTS5_CURRENT_VERSION_SECUREDELETE;
+ }
+ }
+
fts5MultiIterNew(p, pStruct, f, 0, (const u8*)zTerm, nTerm, -1, 0, &pIter);
if( fts5MultiIterEof(p, pIter)==0 ){
i64 iThis = fts5MultiIterRowid(pIter);
@@ -247764,6 +249672,7 @@ static void fts5FlushSecureDelete(
}
fts5MultiIterFree(pIter);
+ return p->rc;
}
@@ -247847,8 +249756,9 @@ static void fts5FlushOneHash(Fts5Index *p){
** using fts5FlushSecureDelete(). */
if( bSecureDelete ){
if( eDetail==FTS5_DETAIL_NONE ){
- if( iOff<nDoclist && pDoclist[iOff]==0x00 ){
- fts5FlushSecureDelete(p, pStruct, zTerm, nTerm, iRowid);
+ if( iOff<nDoclist && pDoclist[iOff]==0x00
+ && !fts5FlushSecureDelete(p, pStruct, zTerm, nTerm, iRowid)
+ ){
iOff++;
if( iOff<nDoclist && pDoclist[iOff]==0x00 ){
iOff++;
@@ -247857,8 +249767,9 @@ static void fts5FlushOneHash(Fts5Index *p){
continue;
}
}
- }else if( (pDoclist[iOff] & 0x01) ){
- fts5FlushSecureDelete(p, pStruct, zTerm, nTerm, iRowid);
+ }else if( (pDoclist[iOff] & 0x01)
+ && !fts5FlushSecureDelete(p, pStruct, zTerm, nTerm, iRowid)
+ ){
if( p->rc!=SQLITE_OK || pDoclist[iOff]==0x01 ){
iOff++;
continue;
@@ -248007,7 +249918,7 @@ static Fts5Structure *fts5IndexOptimizeStruct(
Fts5Structure *pStruct
){
Fts5Structure *pNew = 0;
- sqlite3_int64 nByte = sizeof(Fts5Structure);
+ sqlite3_int64 nByte = SZ_FTS5STRUCTURE(1);
int nSeg = pStruct->nSegment;
int i;
@@ -248036,7 +249947,8 @@ static Fts5Structure *fts5IndexOptimizeStruct(
assert( pStruct->aLevel[i].nMerge<=nThis );
}
- nByte += (pStruct->nLevel+1) * sizeof(Fts5StructureLevel);
+ nByte += (((i64)pStruct->nLevel)+1) * sizeof(Fts5StructureLevel);
+ assert( nByte==SZ_FTS5STRUCTURE(pStruct->nLevel+2) );
pNew = (Fts5Structure*)sqlite3Fts5MallocZero(&p->rc, nByte);
if( pNew ){
@@ -248477,6 +250389,387 @@ static void fts5MergePrefixLists(
*p1 = out;
}
+
+/*
+** Iterate through a range of entries in the FTS index, invoking the xVisit
+** callback for each of them.
+**
+** Parameter pToken points to an nToken buffer containing an FTS index term
+** (i.e. a document term with the preceding 1 byte index identifier -
+** FTS5_MAIN_PREFIX or similar). If bPrefix is true, then the call visits
+** all entries for terms that have pToken/nToken as a prefix. If bPrefix
+** is false, then only entries with pToken/nToken as the entire key are
+** visited.
+**
+** If the current table is a tokendata=1 table, then if bPrefix is true then
+** each index term is treated separately. However, if bPrefix is false, then
+** all index terms corresponding to pToken/nToken are collapsed into a single
+** term before the callback is invoked.
+**
+** The callback invoked for each entry visited is specified by paramter xVisit.
+** Each time it is invoked, it is passed a pointer to the Fts5Index object,
+** a copy of the 7th paramter to this function (pCtx) and a pointer to the
+** iterator that indicates the current entry. If the current entry is the
+** first with a new term (i.e. different from that of the previous entry,
+** including the very first term), then the final two parameters are passed
+** a pointer to the term and its size in bytes, respectively. If the current
+** entry is not the first associated with its term, these two parameters
+** are passed 0.
+**
+** If parameter pColset is not NULL, then it is used to filter entries before
+** the callback is invoked.
+*/
+static int fts5VisitEntries(
+ Fts5Index *p, /* Fts5 index object */
+ Fts5Colset *pColset, /* Columns filter to apply, or NULL */
+ u8 *pToken, /* Buffer containing token */
+ int nToken, /* Size of buffer pToken in bytes */
+ int bPrefix, /* True for a prefix scan */
+ void (*xVisit)(Fts5Index*, void *pCtx, Fts5Iter *pIter, const u8*, int),
+ void *pCtx /* Passed as second argument to xVisit() */
+){
+ const int flags = (bPrefix ? FTS5INDEX_QUERY_SCAN : 0)
+ | FTS5INDEX_QUERY_SKIPEMPTY
+ | FTS5INDEX_QUERY_NOOUTPUT;
+ Fts5Iter *p1 = 0; /* Iterator used to gather data from index */
+ int bNewTerm = 1;
+ Fts5Structure *pStruct = fts5StructureRead(p);
+
+ fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1);
+ fts5IterSetOutputCb(&p->rc, p1);
+ for( /* no-op */ ;
+ fts5MultiIterEof(p, p1)==0;
+ fts5MultiIterNext2(p, p1, &bNewTerm)
+ ){
+ Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ];
+ int nNew = 0;
+ const u8 *pNew = 0;
+
+ p1->xSetOutputs(p1, pSeg);
+ if( p->rc ) break;
+
+ if( bNewTerm ){
+ nNew = pSeg->term.n;
+ pNew = pSeg->term.p;
+ if( nNew<nToken || memcmp(pToken, pNew, nToken) ) break;
+ }
+
+ xVisit(p, pCtx, p1, pNew, nNew);
+ }
+ fts5MultiIterFree(p1);
+
+ fts5StructureRelease(pStruct);
+ return p->rc;
+}
+
+
+/*
+** Usually, a tokendata=1 iterator (struct Fts5TokenDataIter) accumulates an
+** array of these for each row it visits (so all iRowid fields are the same).
+** Or, for an iterator used by an "ORDER BY rank" query, it accumulates an
+** array of these for the entire query (in which case iRowid fields may take
+** a variety of values).
+**
+** Each instance in the array indicates the iterator (and therefore term)
+** associated with position iPos of rowid iRowid. This is used by the
+** xInstToken() API.
+**
+** iRowid:
+** Rowid for the current entry.
+**
+** iPos:
+** Position of current entry within row. In the usual ((iCol<<32)+iOff)
+** format (e.g. see macros FTS5_POS2COLUMN() and FTS5_POS2OFFSET()).
+**
+** iIter:
+** If the Fts5TokenDataIter iterator that the entry is part of is
+** actually an iterator (i.e. with nIter>0, not just a container for
+** Fts5TokenDataMap structures), then this variable is an index into
+** the apIter[] array. The corresponding term is that which the iterator
+** at apIter[iIter] currently points to.
+**
+** Or, if the Fts5TokenDataIter iterator is just a container object
+** (nIter==0), then iIter is an index into the term.p[] buffer where
+** the term is stored.
+**
+** nByte:
+** In the case where iIter is an index into term.p[], this variable
+** is the size of the term in bytes. If iIter is an index into apIter[],
+** this variable is unused.
+*/
+struct Fts5TokenDataMap {
+ i64 iRowid; /* Row this token is located in */
+ i64 iPos; /* Position of token */
+ int iIter; /* Iterator token was read from */
+ int nByte; /* Length of token in bytes (or 0) */
+};
+
+/*
+** An object used to supplement Fts5Iter for tokendata=1 iterators.
+**
+** This object serves two purposes. The first is as a container for an array
+** of Fts5TokenDataMap structures, which are used to find the token required
+** when the xInstToken() API is used. This is done by the nMapAlloc, nMap and
+** aMap[] variables.
+*/
+struct Fts5TokenDataIter {
+ int nMapAlloc; /* Allocated size of aMap[] in entries */
+ int nMap; /* Number of valid entries in aMap[] */
+ Fts5TokenDataMap *aMap; /* Array of (rowid+pos -> token) mappings */
+
+ /* The following are used for prefix-queries only. */
+ Fts5Buffer terms;
+
+ /* The following are used for other full-token tokendata queries only. */
+ int nIter;
+ int nIterAlloc;
+ Fts5PoslistReader *aPoslistReader;
+ int *aPoslistToIter;
+ Fts5Iter *apIter[FLEXARRAY];
+};
+
+/* Size in bytes of an Fts5TokenDataIter object holding up to N iterators */
+#define SZ_FTS5TOKENDATAITER(N) \
+ (offsetof(Fts5TokenDataIter,apIter) + (N)*sizeof(Fts5Iter))
+
+/*
+** The two input arrays - a1[] and a2[] - are in sorted order. This function
+** merges the two arrays together and writes the result to output array
+** aOut[]. aOut[] is guaranteed to be large enough to hold the result.
+**
+** Duplicate entries are copied into the output. So the size of the output
+** array is always (n1+n2) entries.
+*/
+static void fts5TokendataMerge(
+ Fts5TokenDataMap *a1, int n1, /* Input array 1 */
+ Fts5TokenDataMap *a2, int n2, /* Input array 2 */
+ Fts5TokenDataMap *aOut /* Output array */
+){
+ int i1 = 0;
+ int i2 = 0;
+
+ assert( n1>=0 && n2>=0 );
+ while( i1<n1 || i2<n2 ){
+ Fts5TokenDataMap *pOut = &aOut[i1+i2];
+ if( i2>=n2 || (i1<n1 && (
+ a1[i1].iRowid<a2[i2].iRowid
+ || (a1[i1].iRowid==a2[i2].iRowid && a1[i1].iPos<=a2[i2].iPos)
+ ))){
+ memcpy(pOut, &a1[i1], sizeof(Fts5TokenDataMap));
+ i1++;
+ }else{
+ memcpy(pOut, &a2[i2], sizeof(Fts5TokenDataMap));
+ i2++;
+ }
+ }
+}
+
+
+/*
+** Append a mapping to the token-map belonging to object pT.
+*/
+static void fts5TokendataIterAppendMap(
+ Fts5Index *p,
+ Fts5TokenDataIter *pT,
+ int iIter,
+ int nByte,
+ i64 iRowid,
+ i64 iPos
+){
+ if( p->rc==SQLITE_OK ){
+ if( pT->nMap==pT->nMapAlloc ){
+ int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64;
+ int nAlloc = nNew * sizeof(Fts5TokenDataMap);
+ Fts5TokenDataMap *aNew;
+
+ aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nAlloc);
+ if( aNew==0 ){
+ p->rc = SQLITE_NOMEM;
+ return;
+ }
+
+ pT->aMap = aNew;
+ pT->nMapAlloc = nNew;
+ }
+
+ pT->aMap[pT->nMap].iRowid = iRowid;
+ pT->aMap[pT->nMap].iPos = iPos;
+ pT->aMap[pT->nMap].iIter = iIter;
+ pT->aMap[pT->nMap].nByte = nByte;
+ pT->nMap++;
+ }
+}
+
+/*
+** Sort the contents of the pT->aMap[] array.
+**
+** The sorting algorithm requires a malloc(). If this fails, an error code
+** is left in Fts5Index.rc before returning.
+*/
+static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){
+ Fts5TokenDataMap *aTmp = 0;
+ int nByte = pT->nMap * sizeof(Fts5TokenDataMap);
+
+ aTmp = (Fts5TokenDataMap*)sqlite3Fts5MallocZero(&p->rc, nByte);
+ if( aTmp ){
+ Fts5TokenDataMap *a1 = pT->aMap;
+ Fts5TokenDataMap *a2 = aTmp;
+ i64 nHalf;
+
+ for(nHalf=1; nHalf<pT->nMap; nHalf=nHalf*2){
+ int i1;
+ for(i1=0; i1<pT->nMap; i1+=(nHalf*2)){
+ int n1 = MIN(nHalf, pT->nMap-i1);
+ int n2 = MIN(nHalf, pT->nMap-i1-n1);
+ fts5TokendataMerge(&a1[i1], n1, &a1[i1+n1], n2, &a2[i1]);
+ }
+ SWAPVAL(Fts5TokenDataMap*, a1, a2);
+ }
+
+ if( a1!=pT->aMap ){
+ memcpy(pT->aMap, a1, pT->nMap*sizeof(Fts5TokenDataMap));
+ }
+ sqlite3_free(aTmp);
+
+#ifdef SQLITE_DEBUG
+ {
+ int ii;
+ for(ii=1; ii<pT->nMap; ii++){
+ Fts5TokenDataMap *p1 = &pT->aMap[ii-1];
+ Fts5TokenDataMap *p2 = &pT->aMap[ii];
+ assert( p1->iRowid<p2->iRowid
+ || (p1->iRowid==p2->iRowid && p1->iPos<=p2->iPos)
+ );
+ }
+ }
+#endif
+ }
+}
+
+/*
+** Delete an Fts5TokenDataIter structure and its contents.
+*/
+static void fts5TokendataIterDelete(Fts5TokenDataIter *pSet){
+ if( pSet ){
+ int ii;
+ for(ii=0; ii<pSet->nIter; ii++){
+ fts5MultiIterFree(pSet->apIter[ii]);
+ }
+ fts5BufferFree(&pSet->terms);
+ sqlite3_free(pSet->aPoslistReader);
+ sqlite3_free(pSet->aMap);
+ sqlite3_free(pSet);
+ }
+}
+
+
+/*
+** fts5VisitEntries() context object used by fts5SetupPrefixIterTokendata()
+** to pass data to prefixIterSetupTokendataCb().
+*/
+typedef struct TokendataSetupCtx TokendataSetupCtx;
+struct TokendataSetupCtx {
+ Fts5TokenDataIter *pT; /* Object being populated with mappings */
+ int iTermOff; /* Offset of current term in terms.p[] */
+ int nTermByte; /* Size of current term in bytes */
+};
+
+/*
+** fts5VisitEntries() callback used by fts5SetupPrefixIterTokendata(). This
+** callback adds an entry to the Fts5TokenDataIter.aMap[] array for each
+** position in the current position-list. It doesn't matter that some of
+** these may be out of order - they will be sorted later.
+*/
+static void prefixIterSetupTokendataCb(
+ Fts5Index *p,
+ void *pCtx,
+ Fts5Iter *p1,
+ const u8 *pNew,
+ int nNew
+){
+ TokendataSetupCtx *pSetup = (TokendataSetupCtx*)pCtx;
+ int iPosOff = 0;
+ i64 iPos = 0;
+
+ if( pNew ){
+ pSetup->nTermByte = nNew-1;
+ pSetup->iTermOff = pSetup->pT->terms.n;
+ fts5BufferAppendBlob(&p->rc, &pSetup->pT->terms, nNew-1, pNew+1);
+ }
+
+ while( 0==sqlite3Fts5PoslistNext64(
+ p1->base.pData, p1->base.nData, &iPosOff, &iPos
+ ) ){
+ fts5TokendataIterAppendMap(p,
+ pSetup->pT, pSetup->iTermOff, pSetup->nTermByte, p1->base.iRowid, iPos
+ );
+ }
+}
+
+
+/*
+** Context object passed by fts5SetupPrefixIter() to fts5VisitEntries().
+*/
+typedef struct PrefixSetupCtx PrefixSetupCtx;
+struct PrefixSetupCtx {
+ void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*);
+ void (*xAppend)(Fts5Index*, u64, Fts5Iter*, Fts5Buffer*);
+ i64 iLastRowid;
+ int nMerge;
+ Fts5Buffer *aBuf;
+ int nBuf;
+ Fts5Buffer doclist;
+ TokendataSetupCtx *pTokendata;
+};
+
+/*
+** fts5VisitEntries() callback used by fts5SetupPrefixIter()
+*/
+static void prefixIterSetupCb(
+ Fts5Index *p,
+ void *pCtx,
+ Fts5Iter *p1,
+ const u8 *pNew,
+ int nNew
+){
+ PrefixSetupCtx *pSetup = (PrefixSetupCtx*)pCtx;
+ const int nMerge = pSetup->nMerge;
+
+ if( p1->base.nData>0 ){
+ if( p1->base.iRowid<=pSetup->iLastRowid && pSetup->doclist.n>0 ){
+ int i;
+ for(i=0; p->rc==SQLITE_OK && pSetup->doclist.n; i++){
+ int i1 = i*nMerge;
+ int iStore;
+ assert( i1+nMerge<=pSetup->nBuf );
+ for(iStore=i1; iStore<i1+nMerge; iStore++){
+ if( pSetup->aBuf[iStore].n==0 ){
+ fts5BufferSwap(&pSetup->doclist, &pSetup->aBuf[iStore]);
+ fts5BufferZero(&pSetup->doclist);
+ break;
+ }
+ }
+ if( iStore==i1+nMerge ){
+ pSetup->xMerge(p, &pSetup->doclist, nMerge, &pSetup->aBuf[i1]);
+ for(iStore=i1; iStore<i1+nMerge; iStore++){
+ fts5BufferZero(&pSetup->aBuf[iStore]);
+ }
+ }
+ }
+ pSetup->iLastRowid = 0;
+ }
+
+ pSetup->xAppend(
+ p, (u64)p1->base.iRowid-(u64)pSetup->iLastRowid, p1, &pSetup->doclist
+ );
+ pSetup->iLastRowid = p1->base.iRowid;
+ }
+
+ if( pSetup->pTokendata ){
+ prefixIterSetupTokendataCb(p, (void*)pSetup->pTokendata, p1, pNew, nNew);
+ }
+}
+
static void fts5SetupPrefixIter(
Fts5Index *p, /* Index to read from */
int bDesc, /* True for "ORDER BY rowid DESC" */
@@ -248487,38 +250780,41 @@ static void fts5SetupPrefixIter(
Fts5Iter **ppIter /* OUT: New iterator */
){
Fts5Structure *pStruct;
- Fts5Buffer *aBuf;
- int nBuf = 32;
- int nMerge = 1;
+ PrefixSetupCtx s;
+ TokendataSetupCtx s2;
+
+ memset(&s, 0, sizeof(s));
+ memset(&s2, 0, sizeof(s2));
+
+ s.nMerge = 1;
+ s.iLastRowid = 0;
+ s.nBuf = 32;
+ if( iIdx==0
+ && p->pConfig->eDetail==FTS5_DETAIL_FULL
+ && p->pConfig->bPrefixInsttoken
+ ){
+ s.pTokendata = &s2;
+ s2.pT = (Fts5TokenDataIter*)fts5IdxMalloc(p, SZ_FTS5TOKENDATAITER(1));
+ }
- void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*);
- void (*xAppend)(Fts5Index*, u64, Fts5Iter*, Fts5Buffer*);
if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){
- xMerge = fts5MergeRowidLists;
- xAppend = fts5AppendRowid;
+ s.xMerge = fts5MergeRowidLists;
+ s.xAppend = fts5AppendRowid;
}else{
- nMerge = FTS5_MERGE_NLIST-1;
- nBuf = nMerge*8; /* Sufficient to merge (16^8)==(2^32) lists */
- xMerge = fts5MergePrefixLists;
- xAppend = fts5AppendPoslist;
+ s.nMerge = FTS5_MERGE_NLIST-1;
+ s.nBuf = s.nMerge*8; /* Sufficient to merge (16^8)==(2^32) lists */
+ s.xMerge = fts5MergePrefixLists;
+ s.xAppend = fts5AppendPoslist;
}
- aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf);
+ s.aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*s.nBuf);
pStruct = fts5StructureRead(p);
- assert( p->rc!=SQLITE_OK || (aBuf && pStruct) );
+ assert( p->rc!=SQLITE_OK || (s.aBuf && pStruct) );
if( p->rc==SQLITE_OK ){
- const int flags = FTS5INDEX_QUERY_SCAN
- | FTS5INDEX_QUERY_SKIPEMPTY
- | FTS5INDEX_QUERY_NOOUTPUT;
+ void *pCtx = (void*)&s;
int i;
- i64 iLastRowid = 0;
- Fts5Iter *p1 = 0; /* Iterator used to gather data from index */
Fts5Data *pData;
- Fts5Buffer doclist;
- int bNewTerm = 1;
-
- memset(&doclist, 0, sizeof(doclist));
/* If iIdx is non-zero, then it is the number of a prefix-index for
** prefixes 1 character longer than the prefix being queried for. That
@@ -248526,94 +250822,46 @@ static void fts5SetupPrefixIter(
** corresponding to the prefix itself. That one is extracted from the
** main term index here. */
if( iIdx!=0 ){
- int dummy = 0;
- const int f2 = FTS5INDEX_QUERY_SKIPEMPTY|FTS5INDEX_QUERY_NOOUTPUT;
pToken[0] = FTS5_MAIN_PREFIX;
- fts5MultiIterNew(p, pStruct, f2, pColset, pToken, nToken, -1, 0, &p1);
- fts5IterSetOutputCb(&p->rc, p1);
- for(;
- fts5MultiIterEof(p, p1)==0;
- fts5MultiIterNext2(p, p1, &dummy)
- ){
- Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ];
- p1->xSetOutputs(p1, pSeg);
- if( p1->base.nData ){
- xAppend(p, (u64)p1->base.iRowid-(u64)iLastRowid, p1, &doclist);
- iLastRowid = p1->base.iRowid;
- }
- }
- fts5MultiIterFree(p1);
+ fts5VisitEntries(p, pColset, pToken, nToken, 0, prefixIterSetupCb, pCtx);
}
pToken[0] = FTS5_MAIN_PREFIX + iIdx;
- fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1);
- fts5IterSetOutputCb(&p->rc, p1);
-
- for( /* no-op */ ;
- fts5MultiIterEof(p, p1)==0;
- fts5MultiIterNext2(p, p1, &bNewTerm)
- ){
- Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ];
- int nTerm = pSeg->term.n;
- const u8 *pTerm = pSeg->term.p;
- p1->xSetOutputs(p1, pSeg);
-
- assert_nc( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 );
- if( bNewTerm ){
- if( nTerm<nToken || memcmp(pToken, pTerm, nToken) ) break;
- }
-
- if( p1->base.nData==0 ) continue;
- if( p1->base.iRowid<=iLastRowid && doclist.n>0 ){
- for(i=0; p->rc==SQLITE_OK && doclist.n; i++){
- int i1 = i*nMerge;
- int iStore;
- assert( i1+nMerge<=nBuf );
- for(iStore=i1; iStore<i1+nMerge; iStore++){
- if( aBuf[iStore].n==0 ){
- fts5BufferSwap(&doclist, &aBuf[iStore]);
- fts5BufferZero(&doclist);
- break;
- }
- }
- if( iStore==i1+nMerge ){
- xMerge(p, &doclist, nMerge, &aBuf[i1]);
- for(iStore=i1; iStore<i1+nMerge; iStore++){
- fts5BufferZero(&aBuf[iStore]);
- }
- }
- }
- iLastRowid = 0;
- }
+ fts5VisitEntries(p, pColset, pToken, nToken, 1, prefixIterSetupCb, pCtx);
- xAppend(p, (u64)p1->base.iRowid-(u64)iLastRowid, p1, &doclist);
- iLastRowid = p1->base.iRowid;
- }
-
- assert( (nBuf%nMerge)==0 );
- for(i=0; i<nBuf; i+=nMerge){
+ assert( (s.nBuf%s.nMerge)==0 );
+ for(i=0; i<s.nBuf; i+=s.nMerge){
int iFree;
if( p->rc==SQLITE_OK ){
- xMerge(p, &doclist, nMerge, &aBuf[i]);
+ s.xMerge(p, &s.doclist, s.nMerge, &s.aBuf[i]);
}
- for(iFree=i; iFree<i+nMerge; iFree++){
- fts5BufferFree(&aBuf[iFree]);
+ for(iFree=i; iFree<i+s.nMerge; iFree++){
+ fts5BufferFree(&s.aBuf[iFree]);
}
}
- fts5MultiIterFree(p1);
- pData = fts5IdxMalloc(p, sizeof(*pData)+doclist.n+FTS5_DATA_ZERO_PADDING);
+ pData = fts5IdxMalloc(p, sizeof(*pData)
+ + ((i64)s.doclist.n)+FTS5_DATA_ZERO_PADDING);
+ assert( pData!=0 || p->rc!=SQLITE_OK );
if( pData ){
pData->p = (u8*)&pData[1];
- pData->nn = pData->szLeaf = doclist.n;
- if( doclist.n ) memcpy(pData->p, doclist.p, doclist.n);
+ pData->nn = pData->szLeaf = s.doclist.n;
+ if( s.doclist.n ) memcpy(pData->p, s.doclist.p, s.doclist.n);
fts5MultiIterNew2(p, pData, bDesc, ppIter);
}
- fts5BufferFree(&doclist);
+
+ assert( (*ppIter)!=0 || p->rc!=SQLITE_OK );
+ if( p->rc==SQLITE_OK && s.pTokendata ){
+ fts5TokendataIterSortMap(p, s2.pT);
+ (*ppIter)->pTokenDataIter = s2.pT;
+ s2.pT = 0;
+ }
}
+ fts5TokendataIterDelete(s2.pT);
+ fts5BufferFree(&s.doclist);
fts5StructureRelease(pStruct);
- sqlite3_free(aBuf);
+ sqlite3_free(s.aBuf);
}
@@ -248651,7 +250899,7 @@ static int sqlite3Fts5IndexBeginWrite(Fts5Index *p, int bDelete, i64 iRowid){
static int sqlite3Fts5IndexSync(Fts5Index *p){
assert( p->rc==SQLITE_OK );
fts5IndexFlush(p);
- sqlite3Fts5IndexCloseReader(p);
+ fts5IndexCloseReader(p);
return fts5IndexReturn(p);
}
@@ -248662,11 +250910,10 @@ static int sqlite3Fts5IndexSync(Fts5Index *p){
** records must be invalidated.
*/
static int sqlite3Fts5IndexRollback(Fts5Index *p){
- sqlite3Fts5IndexCloseReader(p);
+ fts5IndexCloseReader(p);
fts5IndexDiscardData(p);
fts5StructureInvalidate(p);
- /* assert( p->rc==SQLITE_OK ); */
- return SQLITE_OK;
+ return fts5IndexReturn(p);
}
/*
@@ -248675,15 +250922,17 @@ static int sqlite3Fts5IndexRollback(Fts5Index *p){
** and the initial version of the "averages" record (a zero-byte blob).
*/
static int sqlite3Fts5IndexReinit(Fts5Index *p){
- Fts5Structure s;
+ Fts5Structure *pTmp;
+ u8 tmpSpace[SZ_FTS5STRUCTURE(1)];
fts5StructureInvalidate(p);
fts5IndexDiscardData(p);
- memset(&s, 0, sizeof(Fts5Structure));
+ pTmp = (Fts5Structure*)tmpSpace;
+ memset(pTmp, 0, SZ_FTS5STRUCTURE(1));
if( p->pConfig->bContentlessDelete ){
- s.nOriginCntr = 1;
+ pTmp->nOriginCntr = 1;
}
fts5DataWrite(p, FTS5_AVERAGES_ROWID, (const u8*)"", 0);
- fts5StructureWrite(p, &s);
+ fts5StructureWrite(p, pTmp);
return fts5IndexReturn(p);
}
@@ -248867,37 +251116,15 @@ static void fts5SegIterSetEOF(Fts5SegIter *pSeg){
pSeg->pLeaf = 0;
}
-/*
-** Usually, a tokendata=1 iterator (struct Fts5TokenDataIter) accumulates an
-** array of these for each row it visits. Or, for an iterator used by an
-** "ORDER BY rank" query, it accumulates an array of these for the entire
-** query.
-**
-** Each instance in the array indicates the iterator (and therefore term)
-** associated with position iPos of rowid iRowid. This is used by the
-** xInstToken() API.
-*/
-struct Fts5TokenDataMap {
- i64 iRowid; /* Row this token is located in */
- i64 iPos; /* Position of token */
- int iIter; /* Iterator token was read from */
-};
-
-/*
-** An object used to supplement Fts5Iter for tokendata=1 iterators.
-*/
-struct Fts5TokenDataIter {
- int nIter;
- int nIterAlloc;
-
- int nMap;
- int nMapAlloc;
- Fts5TokenDataMap *aMap;
-
- Fts5PoslistReader *aPoslistReader;
- int *aPoslistToIter;
- Fts5Iter *apIter[1];
-};
+static void fts5IterClose(Fts5IndexIter *pIndexIter){
+ if( pIndexIter ){
+ Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
+ Fts5Index *pIndex = pIter->pIndex;
+ fts5TokendataIterDelete(pIter->pTokenDataIter);
+ fts5MultiIterFree(pIter);
+ fts5IndexCloseReader(pIndex);
+ }
+}
/*
** This function appends iterator pAppend to Fts5TokenDataIter pIn and
@@ -248913,7 +251140,7 @@ static Fts5TokenDataIter *fts5AppendTokendataIter(
if( p->rc==SQLITE_OK ){
if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){
int nAlloc = pIn ? pIn->nIterAlloc*2 : 16;
- int nByte = nAlloc * sizeof(Fts5Iter*) + sizeof(Fts5TokenDataIter);
+ int nByte = SZ_FTS5TOKENDATAITER(nAlloc+1);
Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte);
if( pNew==0 ){
@@ -248926,7 +251153,7 @@ static Fts5TokenDataIter *fts5AppendTokendataIter(
}
}
if( p->rc ){
- sqlite3Fts5IterClose((Fts5IndexIter*)pAppend);
+ fts5IterClose((Fts5IndexIter*)pAppend);
}else{
pRet->apIter[pRet->nIter++] = pAppend;
}
@@ -248936,54 +251163,6 @@ static Fts5TokenDataIter *fts5AppendTokendataIter(
}
/*
-** Delete an Fts5TokenDataIter structure and its contents.
-*/
-static void fts5TokendataIterDelete(Fts5TokenDataIter *pSet){
- if( pSet ){
- int ii;
- for(ii=0; ii<pSet->nIter; ii++){
- fts5MultiIterFree(pSet->apIter[ii]);
- }
- sqlite3_free(pSet->aPoslistReader);
- sqlite3_free(pSet->aMap);
- sqlite3_free(pSet);
- }
-}
-
-/*
-** Append a mapping to the token-map belonging to object pT.
-*/
-static void fts5TokendataIterAppendMap(
- Fts5Index *p,
- Fts5TokenDataIter *pT,
- int iIter,
- i64 iRowid,
- i64 iPos
-){
- if( p->rc==SQLITE_OK ){
- if( pT->nMap==pT->nMapAlloc ){
- int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64;
- int nByte = nNew * sizeof(Fts5TokenDataMap);
- Fts5TokenDataMap *aNew;
-
- aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nByte);
- if( aNew==0 ){
- p->rc = SQLITE_NOMEM;
- return;
- }
-
- pT->aMap = aNew;
- pT->nMapAlloc = nNew;
- }
-
- pT->aMap[pT->nMap].iRowid = iRowid;
- pT->aMap[pT->nMap].iPos = iPos;
- pT->aMap[pT->nMap].iIter = iIter;
- pT->nMap++;
- }
-}
-
-/*
** The iterator passed as the only argument must be a tokendata=1 iterator
** (pIter->pTokenDataIter!=0). This function sets the iterator output
** variables (pIter->base.*) according to the contents of the current
@@ -249023,7 +251202,7 @@ static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){
pIter->base.iRowid = iRowid;
if( nHit==1 && eDetail==FTS5_DETAIL_FULL ){
- fts5TokendataIterAppendMap(pIter->pIndex, pT, iMin, iRowid, -1);
+ fts5TokendataIterAppendMap(pIter->pIndex, pT, iMin, 0, iRowid, -1);
}else
if( nHit>1 && eDetail!=FTS5_DETAIL_NONE ){
int nReader = 0;
@@ -249187,7 +251366,7 @@ static Fts5Iter *fts5SetupTokendataIter(
fts5BufferSet(&p->rc, &bSeek, nToken, pToken);
}
if( p->rc ){
- sqlite3Fts5IterClose((Fts5IndexIter*)pNew);
+ fts5IterClose((Fts5IndexIter*)pNew);
break;
}
@@ -249252,7 +251431,7 @@ static Fts5Iter *fts5SetupTokendataIter(
** not point to any terms that match the query. So delete it and break
** out of the loop - all required iterators have been collected. */
if( pSmall==0 ){
- sqlite3Fts5IterClose((Fts5IndexIter*)pNew);
+ fts5IterClose((Fts5IndexIter*)pNew);
break;
}
@@ -249276,6 +251455,7 @@ static Fts5Iter *fts5SetupTokendataIter(
pRet = fts5MultiIterAlloc(p, 0);
}
if( pRet ){
+ pRet->nSeg = 0;
pRet->pTokenDataIter = pSet;
if( pSet ){
fts5IterSetOutputsTokendata(pRet);
@@ -249291,7 +251471,6 @@ static Fts5Iter *fts5SetupTokendataIter(
return pRet;
}
-
/*
** Open a new iterator to iterate though all rowid that match the
** specified token or token prefix.
@@ -249314,8 +251493,14 @@ static int sqlite3Fts5IndexQuery(
int iIdx = 0; /* Index to search */
int iPrefixIdx = 0; /* +1 prefix index */
int bTokendata = pConfig->bTokendata;
+ assert( buf.p!=0 );
if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken);
+ /* The NOTOKENDATA flag is set when each token in a tokendata=1 table
+ ** should be treated individually, instead of merging all those with
+ ** a common prefix into a single entry. This is used, for example, by
+ ** queries performed as part of an integrity-check, or by the fts5vocab
+ ** module. */
if( flags & (FTS5INDEX_QUERY_NOTOKENDATA|FTS5INDEX_QUERY_SCAN) ){
bTokendata = 0;
}
@@ -249346,7 +251531,7 @@ static int sqlite3Fts5IndexQuery(
}
if( bTokendata && iIdx==0 ){
- buf.p[0] = '0';
+ buf.p[0] = FTS5_MAIN_PREFIX;
pRet = fts5SetupTokendataIter(p, buf.p, nToken+1, pColset);
}else if( iIdx<=pConfig->nPrefix ){
/* Straight index lookup */
@@ -249359,7 +251544,7 @@ static int sqlite3Fts5IndexQuery(
fts5StructureRelease(pStruct);
}
}else{
- /* Scan multiple terms in the main index */
+ /* Scan multiple terms in the main index for a prefix query. */
int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0;
fts5SetupPrefixIter(p, bDesc, iPrefixIdx, buf.p, nToken+1, pColset,&pRet);
if( pRet==0 ){
@@ -249375,9 +251560,9 @@ static int sqlite3Fts5IndexQuery(
}
if( p->rc ){
- sqlite3Fts5IterClose((Fts5IndexIter*)pRet);
+ fts5IterClose((Fts5IndexIter*)pRet);
pRet = 0;
- sqlite3Fts5IndexCloseReader(p);
+ fts5IndexCloseReader(p);
}
*ppIter = (Fts5IndexIter*)pRet;
@@ -249395,7 +251580,8 @@ static int sqlite3Fts5IndexQuery(
static int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
assert( pIter->pIndex->rc==SQLITE_OK );
- if( pIter->pTokenDataIter ){
+ if( pIter->nSeg==0 ){
+ assert( pIter->pTokenDataIter );
fts5TokendataIterNext(pIter, 0, 0);
}else{
fts5MultiIterNext(pIter->pIndex, pIter, 0, 0);
@@ -249432,7 +251618,8 @@ static int sqlite3Fts5IterNextScan(Fts5IndexIter *pIndexIter){
*/
static int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
- if( pIter->pTokenDataIter ){
+ if( pIter->nSeg==0 ){
+ assert( pIter->pTokenDataIter );
fts5TokendataIterNext(pIter, 1, iMatch);
}else{
fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch);
@@ -249452,13 +251639,61 @@ static const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIndexIter, int *pn){
}
/*
+** pIter is a prefix query. This function populates pIter->pTokenDataIter
+** with an Fts5TokenDataIter object containing mappings for all rows
+** matched by the query.
+*/
+static int fts5SetupPrefixIterTokendata(
+ Fts5Iter *pIter,
+ const char *pToken, /* Token prefix to search for */
+ int nToken /* Size of pToken in bytes */
+){
+ Fts5Index *p = pIter->pIndex;
+ Fts5Buffer token = {0, 0, 0};
+ TokendataSetupCtx ctx;
+
+ memset(&ctx, 0, sizeof(ctx));
+
+ fts5BufferGrow(&p->rc, &token, nToken+1);
+ assert( token.p!=0 || p->rc!=SQLITE_OK );
+ ctx.pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc,
+ SZ_FTS5TOKENDATAITER(1));
+
+ if( p->rc==SQLITE_OK ){
+
+ /* Fill in the token prefix to search for */
+ token.p[0] = FTS5_MAIN_PREFIX;
+ memcpy(&token.p[1], pToken, nToken);
+ token.n = nToken+1;
+
+ fts5VisitEntries(
+ p, 0, token.p, token.n, 1, prefixIterSetupTokendataCb, (void*)&ctx
+ );
+
+ fts5TokendataIterSortMap(p, ctx.pT);
+ }
+
+ if( p->rc==SQLITE_OK ){
+ pIter->pTokenDataIter = ctx.pT;
+ }else{
+ fts5TokendataIterDelete(ctx.pT);
+ }
+ fts5BufferFree(&token);
+
+ return fts5IndexReturn(p);
+}
+
+/*
** This is used by xInstToken() to access the token at offset iOff, column
** iCol of row iRowid. The token is returned via output variables *ppOut
** and *pnOut. The iterator passed as the first argument must be a tokendata=1
** iterator (pIter->pTokenDataIter!=0).
+**
+** pToken/nToken:
*/
static int sqlite3Fts5IterToken(
Fts5IndexIter *pIndexIter,
+ const char *pToken, int nToken,
i64 iRowid,
int iCol,
int iOff,
@@ -249466,13 +251701,22 @@ static int sqlite3Fts5IterToken(
){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
Fts5TokenDataIter *pT = pIter->pTokenDataIter;
- Fts5TokenDataMap *aMap = pT->aMap;
i64 iPos = (((i64)iCol)<<32) + iOff;
-
+ Fts5TokenDataMap *aMap = 0;
int i1 = 0;
- int i2 = pT->nMap;
+ int i2 = 0;
int iTest = 0;
+ assert( pT || (pToken && pIter->nSeg>0) );
+ if( pT==0 ){
+ int rc = fts5SetupPrefixIterTokendata(pIter, pToken, nToken);
+ if( rc!=SQLITE_OK ) return rc;
+ pT = pIter->pTokenDataIter;
+ }
+
+ i2 = pT->nMap;
+ aMap = pT->aMap;
+
while( i2>i1 ){
iTest = (i1 + i2) / 2;
@@ -249495,9 +251739,15 @@ static int sqlite3Fts5IterToken(
}
if( i2>i1 ){
- Fts5Iter *pMap = pT->apIter[aMap[iTest].iIter];
- *ppOut = (const char*)pMap->aSeg[0].term.p+1;
- *pnOut = pMap->aSeg[0].term.n-1;
+ if( pIter->nSeg==0 ){
+ Fts5Iter *pMap = pT->apIter[aMap[iTest].iIter];
+ *ppOut = (const char*)pMap->aSeg[0].term.p+1;
+ *pnOut = pMap->aSeg[0].term.n-1;
+ }else{
+ Fts5TokenDataMap *p = &aMap[iTest];
+ *ppOut = (const char*)&pT->terms.p[p->iIter];
+ *pnOut = aMap[iTest].nByte;
+ }
}
return SQLITE_OK;
@@ -249509,7 +251759,9 @@ static int sqlite3Fts5IterToken(
*/
static void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter *pIndexIter){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
- if( pIter && pIter->pTokenDataIter ){
+ if( pIter && pIter->pTokenDataIter
+ && (pIter->nSeg==0 || pIter->pIndex->pConfig->eDetail!=FTS5_DETAIL_FULL)
+ ){
pIter->pTokenDataIter->nMap = 0;
}
}
@@ -249529,17 +251781,30 @@ static int sqlite3Fts5IndexIterWriteTokendata(
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
Fts5TokenDataIter *pT = pIter->pTokenDataIter;
Fts5Index *p = pIter->pIndex;
- int ii;
+ i64 iPos = (((i64)iCol)<<32) + iOff;
assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL );
- assert( pIter->pTokenDataIter );
-
- for(ii=0; ii<pT->nIter; ii++){
- Fts5Buffer *pTerm = &pT->apIter[ii]->aSeg[0].term;
- if( nToken==pTerm->n-1 && memcmp(pToken, pTerm->p+1, nToken)==0 ) break;
- }
- if( ii<pT->nIter ){
- fts5TokendataIterAppendMap(p, pT, ii, iRowid, (((i64)iCol)<<32) + iOff);
+ assert( pIter->pTokenDataIter || pIter->nSeg>0 );
+ if( pIter->nSeg>0 ){
+ /* This is a prefix term iterator. */
+ if( pT==0 ){
+ pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc,
+ SZ_FTS5TOKENDATAITER(1));
+ pIter->pTokenDataIter = pT;
+ }
+ if( pT ){
+ fts5TokendataIterAppendMap(p, pT, pT->terms.n, nToken, iRowid, iPos);
+ fts5BufferAppendBlob(&p->rc, &pT->terms, nToken, (const u8*)pToken);
+ }
+ }else{
+ int ii;
+ for(ii=0; ii<pT->nIter; ii++){
+ Fts5Buffer *pTerm = &pT->apIter[ii]->aSeg[0].term;
+ if( nToken==pTerm->n-1 && memcmp(pToken, pTerm->p+1, nToken)==0 ) break;
+ }
+ if( ii<pT->nIter ){
+ fts5TokendataIterAppendMap(p, pT, ii, 0, iRowid, iPos);
+ }
}
return fts5IndexReturn(p);
}
@@ -249549,11 +251814,9 @@ static int sqlite3Fts5IndexIterWriteTokendata(
*/
static void sqlite3Fts5IterClose(Fts5IndexIter *pIndexIter){
if( pIndexIter ){
- Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
- Fts5Index *pIndex = pIter->pIndex;
- fts5TokendataIterDelete(pIter->pTokenDataIter);
- fts5MultiIterFree(pIter);
- sqlite3Fts5IndexCloseReader(pIndex);
+ Fts5Index *pIndex = ((Fts5Iter*)pIndexIter)->pIndex;
+ fts5IterClose(pIndexIter);
+ fts5IndexReturn(pIndex);
}
}
@@ -250083,7 +252346,7 @@ static int fts5QueryCksum(
rc = sqlite3Fts5IterNext(pIter);
}
}
- sqlite3Fts5IterClose(pIter);
+ fts5IterClose(pIter);
*pCksum = cksum;
return rc;
@@ -250560,7 +252823,7 @@ static void fts5DecodeRowid(
#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){
- int iSegid, iHeight, iPgno, bDlidx, bTomb; /* Rowid compenents */
+ int iSegid, iHeight, iPgno, bDlidx, bTomb; /* Rowid components */
fts5DecodeRowid(iKey, &bTomb, &iSegid, &bDlidx, &iHeight, &iPgno);
if( iSegid==0 ){
@@ -250806,7 +253069,7 @@ static void fts5DecodeFunction(
** buffer overreads even if the record is corrupt. */
n = sqlite3_value_bytes(apVal[1]);
aBlob = sqlite3_value_blob(apVal[1]);
- nSpace = n + FTS5_DATA_ZERO_PADDING;
+ nSpace = ((i64)n) + FTS5_DATA_ZERO_PADDING;
a = (u8*)sqlite3Fts5MallocZero(&rc, nSpace);
if( a==0 ) goto decode_out;
if( n>0 ) memcpy(a, aBlob, n);
@@ -251444,6 +253707,7 @@ struct Fts5Global {
#define FTS5_LOCALE_HDR_SIZE ((int)sizeof( ((Fts5Global*)0)->aLocaleHdr ))
#define FTS5_LOCALE_HDR(pConfig) ((const u8*)(pConfig->pGlobal->aLocaleHdr))
+#define FTS5_INSTTOKEN_SUBTYPE 73
/*
** Each auxiliary function registered with the FTS5 module is represented
@@ -251520,9 +253784,11 @@ struct Fts5Sorter {
i64 iRowid; /* Current rowid */
const u8 *aPoslist; /* Position lists for current row */
int nIdx; /* Number of entries in aIdx[] */
- int aIdx[1]; /* Offsets into aPoslist for current row */
+ int aIdx[FLEXARRAY]; /* Offsets into aPoslist for current row */
};
+/* Size (int bytes) of an Fts5Sorter object with N indexes */
+#define SZ_FTS5SORTER(N) (offsetof(Fts5Sorter,nIdx)+((N+2)/2)*sizeof(i64))
/*
** Virtual-table cursor object.
@@ -251983,6 +254249,7 @@ static int fts5BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){
if( p->usable==0 || iCol<0 ){
/* As there exists an unusable MATCH constraint this is an
** unusable plan. Return SQLITE_CONSTRAINT. */
+ idxStr[iIdxStr] = 0;
return SQLITE_CONSTRAINT;
}else{
if( iCol==nCol+1 ){
@@ -252399,7 +254666,7 @@ static int fts5CursorFirstSorted(
const char *zRankArgs = pCsr->zRankArgs;
nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr);
- nByte = sizeof(Fts5Sorter) + sizeof(int) * (nPhrase-1);
+ nByte = SZ_FTS5SORTER(nPhrase);
pSorter = (Fts5Sorter*)sqlite3_malloc64(nByte);
if( pSorter==0 ) return SQLITE_NOMEM;
memset(pSorter, 0, (size_t)nByte);
@@ -252768,6 +255035,7 @@ static int fts5FilterMethod(
sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */
int iCol; /* Column on LHS of MATCH operator */
char **pzErrmsg = pConfig->pzErrmsg;
+ int bPrefixInsttoken = pConfig->bPrefixInsttoken;
int i;
int iIdxStr = 0;
Fts5Expr *pExpr = 0;
@@ -252803,6 +255071,9 @@ static int fts5FilterMethod(
rc = fts5ExtractExprText(pConfig, apVal[i], &zText, &bFreeAndReset);
if( rc!=SQLITE_OK ) goto filter_out;
if( zText==0 ) zText = "";
+ if( sqlite3_value_subtype(apVal[i])==FTS5_INSTTOKEN_SUBTYPE ){
+ pConfig->bPrefixInsttoken = 1;
+ }
iCol = 0;
do{
@@ -252943,6 +255214,7 @@ static int fts5FilterMethod(
filter_out:
sqlite3Fts5ExprFree(pExpr);
pConfig->pzErrmsg = pzErrmsg;
+ pConfig->bPrefixInsttoken = bPrefixInsttoken;
return rc;
}
@@ -253245,7 +255517,6 @@ static int fts5UpdateMethod(
Fts5Config *pConfig = pTab->p.pConfig;
int eType0; /* value_type() of apVal[0] */
int rc = SQLITE_OK; /* Return code */
- int bUpdateOrDelete = 0;
/* A transaction must be open when this is called. */
assert( pTab->ts.eState==1 || pTab->ts.eState==2 );
@@ -253257,7 +255528,7 @@ static int fts5UpdateMethod(
);
assert( pTab->p.pConfig->pzErrmsg==0 );
if( pConfig->pgsz==0 ){
- rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
+ rc = sqlite3Fts5ConfigLoad(pTab->p.pConfig, pTab->p.pConfig->iCookie);
if( rc!=SQLITE_OK ) return rc;
}
@@ -253282,7 +255553,6 @@ static int fts5UpdateMethod(
rc = SQLITE_ERROR;
}else{
rc = fts5SpecialDelete(pTab, apVal);
- bUpdateOrDelete = 1;
}
}else{
rc = fts5SpecialInsert(pTab, z, apVal[2 + pConfig->nCol + 1]);
@@ -253319,7 +255589,6 @@ static int fts5UpdateMethod(
}else{
i64 iDel = sqlite3_value_int64(apVal[0]); /* Rowid to delete */
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iDel, 0, 0);
- bUpdateOrDelete = 1;
}
}
@@ -253347,7 +255616,6 @@ static int fts5UpdateMethod(
if( eConflict==SQLITE_REPLACE && eType1==SQLITE_INTEGER ){
i64 iNew = sqlite3_value_int64(apVal[1]); /* Rowid to delete */
rc = sqlite3Fts5StorageDelete(pTab->pStorage, iNew, 0, 0);
- bUpdateOrDelete = 1;
}
fts5StorageInsert(&rc, pTab, apVal, pRowid);
}
@@ -253401,23 +255669,8 @@ static int fts5UpdateMethod(
rc = sqlite3Fts5StorageDelete(pStorage, iOld, 0, 1);
fts5StorageInsert(&rc, pTab, apVal, pRowid);
}
- bUpdateOrDelete = 1;
sqlite3Fts5StorageReleaseDeleteRow(pStorage);
}
-
- }
- }
-
- if( rc==SQLITE_OK
- && bUpdateOrDelete
- && pConfig->bSecureDelete
- && pConfig->iVersion==FTS5_CURRENT_VERSION
- ){
- rc = sqlite3Fts5StorageConfigValue(
- pTab->pStorage, "version", 0, FTS5_CURRENT_VERSION_SECUREDELETE
- );
- if( rc==SQLITE_OK ){
- pConfig->iVersion = FTS5_CURRENT_VERSION_SECUREDELETE;
}
}
@@ -253470,6 +255723,7 @@ static int fts5RollbackMethod(sqlite3_vtab *pVtab){
Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
fts5CheckTransactionState(pTab, FTS5_ROLLBACK, 0);
rc = sqlite3Fts5StorageRollback(pTab->pStorage);
+ pTab->p.pConfig->pgsz = 0;
return rc;
}
@@ -254938,7 +257192,7 @@ static void fts5SourceIdFunc(
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
- sqlite3_result_text(pCtx, "fts5: 2024-11-25 12:07:48 b95d11e958643b969c47a8e5857f3793b9e69700b8f1469371386369a26e577e", -1, SQLITE_TRANSIENT);
+ sqlite3_result_text(pCtx, "fts5: 2025-05-29 14:26:00 dfc790f998f450d9c35e3ba1c8c89c17466cb559f87b0239e4aab9d34e28f742", -1, SQLITE_TRANSIENT);
}
/*
@@ -255003,6 +257257,20 @@ static void fts5LocaleFunc(
}
/*
+** Implementation of fts5_insttoken() function.
+*/
+static void fts5InsttokenFunc(
+ sqlite3_context *pCtx, /* Function call context */
+ int nArg, /* Number of args */
+ sqlite3_value **apArg /* Function arguments */
+){
+ assert( nArg==1 );
+ (void)nArg;
+ sqlite3_result_value(pCtx, apArg[0]);
+ sqlite3_result_subtype(pCtx, FTS5_INSTTOKEN_SUBTYPE);
+}
+
+/*
** Return true if zName is the extension on one of the shadow tables used
** by this module.
*/
@@ -255131,10 +257399,17 @@ static int fts5Init(sqlite3 *db){
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(
db, "fts5_locale", 2,
- SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE,
+ SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE|SQLITE_SUBTYPE,
p, fts5LocaleFunc, 0, 0
);
}
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_create_function(
+ db, "fts5_insttoken", 1,
+ SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE,
+ p, fts5InsttokenFunc, 0, 0
+ );
+ }
}
/* If SQLITE_FTS5_ENABLE_TEST_MI is defined, assume that the file
@@ -255142,8 +257417,8 @@ static int fts5Init(sqlite3 *db){
** its entry point to enable the matchinfo() demo. */
#ifdef SQLITE_FTS5_ENABLE_TEST_MI
if( rc==SQLITE_OK ){
- extern int sqlite3Fts5TestRegisterMatchinfo(sqlite3*);
- rc = sqlite3Fts5TestRegisterMatchinfo(db);
+ extern int sqlite3Fts5TestRegisterMatchinfoAPI(fts5_api*);
+ rc = sqlite3Fts5TestRegisterMatchinfoAPI(&pGlobal->api);
}
#endif
@@ -255398,6 +257673,11 @@ static int fts5StorageGetStmt(
if( rc!=SQLITE_OK && pzErrMsg ){
*pzErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(pC->db));
}
+ if( rc==SQLITE_ERROR && eStmt>FTS5_STMT_LOOKUP2 && eStmt<FTS5_STMT_SCAN ){
+ /* One of the internal tables - not the %_content table - is missing.
+ ** This counts as a corrupted table. */
+ rc = SQLITE_CORRUPT;
+ }
}
}
@@ -258059,8 +260339,8 @@ static int fts5TriTokenize(
char *zOut = aBuf;
int ii;
const unsigned char *zIn = (const unsigned char*)pText;
- const unsigned char *zEof = &zIn[nText];
- u32 iCode;
+ const unsigned char *zEof = (zIn ? &zIn[nText] : 0);
+ u32 iCode = 0;
int aStart[3]; /* Input offset of each character in aBuf[] */
UNUSED_PARAM(unusedFlags);
@@ -258069,8 +260349,8 @@ static int fts5TriTokenize(
for(ii=0; ii<3; ii++){
do {
aStart[ii] = zIn - (const unsigned char*)pText;
+ if( zIn>=zEof ) return SQLITE_OK;
READ_UTF8(zIn, zEof, iCode);
- if( iCode==0 ) return SQLITE_OK;
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam);
}while( iCode==0 );
WRITE_UTF8(zOut, iCode);
@@ -258091,8 +260371,11 @@ static int fts5TriTokenize(
/* Read characters from the input up until the first non-diacritic */
do {
iNext = zIn - (const unsigned char*)pText;
+ if( zIn>=zEof ){
+ iCode = 0;
+ break;
+ }
READ_UTF8(zIn, zEof, iCode);
- if( iCode==0 ) break;
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam);
}while( iCode==0 );
@@ -258973,7 +261256,6 @@ static void sqlite3Fts5UnicodeAscii(u8 *aArray, u8 *aAscii){
aAscii[0] = 0; /* 0x00 is never a token character */
}
-
/*
** 2015 May 30
**
@@ -259514,12 +261796,12 @@ static int fts5VocabInitVtab(
*pzErr = sqlite3_mprintf("wrong number of vtable arguments");
rc = SQLITE_ERROR;
}else{
- int nByte; /* Bytes of space to allocate */
+ i64 nByte; /* Bytes of space to allocate */
const char *zDb = bDb ? argv[3] : argv[1];
const char *zTab = bDb ? argv[4] : argv[3];
const char *zType = bDb ? argv[5] : argv[4];
- int nDb = (int)strlen(zDb)+1;
- int nTab = (int)strlen(zTab)+1;
+ i64 nDb = strlen(zDb)+1;
+ i64 nTab = strlen(zTab)+1;
int eType = 0;
rc = fts5VocabTableType(zType, pzErr, &eType);
@@ -260485,4 +262767,5 @@ SQLITE_API int sqlite3_stmt_init(
/************** End of stmt.c ************************************************/
/* Return the source-id for this library */
SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
+#endif /* SQLITE_AMALGAMATION */
/************************** End of sqlite3.c ******************************/
diff --git a/libs/sqlite3/src/sqlite3.h b/libs/sqlite3/src/sqlite3.h
index 02e14747d1..3b5d6fd241 100644
--- a/libs/sqlite3/src/sqlite3.h
+++ b/libs/sqlite3/src/sqlite3.h
@@ -133,7 +133,7 @@ extern "C" {
**
** Since [version 3.6.18] ([dateof:3.6.18]),
** SQLite source code has been stored in the
-** <a href="http://www.fossil-scm.org/">Fossil configuration management
+** <a href="http://fossil-scm.org/">Fossil configuration management
** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to
** a string which identifies a particular check-in of SQLite
** within its configuration management system. ^The SQLITE_SOURCE_ID
@@ -146,9 +146,9 @@ extern "C" {
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.47.1"
-#define SQLITE_VERSION_NUMBER 3047001
-#define SQLITE_SOURCE_ID "2024-11-25 12:07:48 b95d11e958643b969c47a8e5857f3793b9e69700b8f1469371386369a26e577e"
+#define SQLITE_VERSION "3.50.0"
+#define SQLITE_VERSION_NUMBER 3050000
+#define SQLITE_SOURCE_ID "2025-05-29 14:26:00 dfc790f998f450d9c35e3ba1c8c89c17466cb559f87b0239e4aab9d34e28f742"
/*
** CAPI3REF: Run-Time Library Version Numbers
@@ -1100,6 +1100,11 @@ struct sqlite3_io_methods {
** pointed to by the pArg argument. This capability is used during testing
** and only needs to be supported when SQLITE_TEST is defined.
**
+** <li>[[SQLITE_FCNTL_NULL_IO]]
+** The [SQLITE_FCNTL_NULL_IO] opcode sets the low-level file descriptor
+** or file handle for the [sqlite3_file] object such that it will no longer
+** read or write to the database file.
+**
** <li>[[SQLITE_FCNTL_WAL_BLOCK]]
** The [SQLITE_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might
** be advantageous to block on the next WAL lock if the lock is not immediately
@@ -1158,6 +1163,12 @@ struct sqlite3_io_methods {
** the value that M is to be set to. Before returning, the 32-bit signed
** integer is overwritten with the previous value of M.
**
+** <li>[[SQLITE_FCNTL_BLOCK_ON_CONNECT]]
+** The [SQLITE_FCNTL_BLOCK_ON_CONNECT] opcode is used to configure the
+** VFS to block when taking a SHARED lock to connect to a wal mode database.
+** This is used to implement the functionality associated with
+** SQLITE_SETLK_BLOCK_ON_CONNECT.
+**
** <li>[[SQLITE_FCNTL_DATA_VERSION]]
** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
** a database file. The argument is a pointer to a 32-bit unsigned integer.
@@ -1253,6 +1264,8 @@ struct sqlite3_io_methods {
#define SQLITE_FCNTL_EXTERNAL_READER 40
#define SQLITE_FCNTL_CKSM_FILE 41
#define SQLITE_FCNTL_RESET_CACHE 42
+#define SQLITE_FCNTL_NULL_IO 43
+#define SQLITE_FCNTL_BLOCK_ON_CONNECT 44
/* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
@@ -1983,13 +1996,16 @@ struct sqlite3_mem_methods {
**
** [[SQLITE_CONFIG_LOOKASIDE]] <dt>SQLITE_CONFIG_LOOKASIDE</dt>
** <dd> ^(The SQLITE_CONFIG_LOOKASIDE option takes two arguments that determine
-** the default size of lookaside memory on each [database connection].
+** the default size of [lookaside memory] on each [database connection].
** The first argument is the
-** size of each lookaside buffer slot and the second is the number of
-** slots allocated to each database connection.)^ ^(SQLITE_CONFIG_LOOKASIDE
-** sets the <i>default</i> lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE]
-** option to [sqlite3_db_config()] can be used to change the lookaside
-** configuration on individual connections.)^ </dd>
+** size of each lookaside buffer slot ("sz") and the second is the number of
+** slots allocated to each database connection ("cnt").)^
+** ^(SQLITE_CONFIG_LOOKASIDE sets the <i>default</i> lookaside size.
+** The [SQLITE_DBCONFIG_LOOKASIDE] option to [sqlite3_db_config()] can
+** be used to change the lookaside configuration on individual connections.)^
+** The [-DSQLITE_DEFAULT_LOOKASIDE] option can be used to change the
+** default lookaside configuration at compile-time.
+** </dd>
**
** [[SQLITE_CONFIG_PCACHE2]] <dt>SQLITE_CONFIG_PCACHE2</dt>
** <dd> ^(The SQLITE_CONFIG_PCACHE2 option takes a single argument which is
@@ -2205,7 +2221,15 @@ struct sqlite3_mem_methods {
** CAPI3REF: Database Connection Configuration Options
**
** These constants are the available integer configuration options that
-** can be passed as the second argument to the [sqlite3_db_config()] interface.
+** can be passed as the second parameter to the [sqlite3_db_config()] interface.
+**
+** The [sqlite3_db_config()] interface is a var-args functions. It takes a
+** variable number of parameters, though always at least two. The number of
+** parameters passed into sqlite3_db_config() depends on which of these
+** constants is given as the second parameter. This documentation page
+** refers to parameters beyond the second as "arguments". Thus, when this
+** page says "the N-th argument" it means "the N-th parameter past the
+** configuration option" or "the (N+2)-th parameter to sqlite3_db_config()".
**
** New configuration options may be added in future releases of SQLite.
** Existing configuration options might be discontinued. Applications
@@ -2217,31 +2241,57 @@ struct sqlite3_mem_methods {
** <dl>
** [[SQLITE_DBCONFIG_LOOKASIDE]]
** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>
-** <dd> ^This option takes three additional arguments that determine the
-** [lookaside memory allocator] configuration for the [database connection].
-** ^The first argument (the third parameter to [sqlite3_db_config()] is a
+** <dd> The SQLITE_DBCONFIG_LOOKASIDE option is used to adjust the
+** configuration of the [lookaside memory allocator] within a database
+** connection.
+** The arguments to the SQLITE_DBCONFIG_LOOKASIDE option are <i>not</i>
+** in the [DBCONFIG arguments|usual format].
+** The SQLITE_DBCONFIG_LOOKASIDE option takes three arguments, not two,
+** so that a call to [sqlite3_db_config()] that uses SQLITE_DBCONFIG_LOOKASIDE
+** should have a total of five parameters.
+** <ol>
+** <li><p>The first argument ("buf") is a
** pointer to a memory buffer to use for lookaside memory.
-** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb
-** may be NULL in which case SQLite will allocate the
-** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the
-** size of each lookaside buffer slot. ^The third argument is the number of
-** slots. The size of the buffer in the first argument must be greater than
-** or equal to the product of the second and third arguments. The buffer
-** must be aligned to an 8-byte boundary. ^If the second argument to
-** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally
-** rounded down to the next smaller multiple of 8. ^(The lookaside memory
+** The first argument may be NULL in which case SQLite will allocate the
+** lookaside buffer itself using [sqlite3_malloc()].
+** <li><P>The second argument ("sz") is the
+** size of each lookaside buffer slot. Lookaside is disabled if "sz"
+** is less than 8. The "sz" argument should be a multiple of 8 less than
+** 65536. If "sz" does not meet this constraint, it is reduced in size until
+** it does.
+** <li><p>The third argument ("cnt") is the number of slots. Lookaside is disabled
+** if "cnt"is less than 1. The "cnt" value will be reduced, if necessary, so
+** that the product of "sz" and "cnt" does not exceed 2,147,418,112. The "cnt"
+** parameter is usually chosen so that the product of "sz" and "cnt" is less
+** than 1,000,000.
+** </ol>
+** <p>If the "buf" argument is not NULL, then it must
+** point to a memory buffer with a size that is greater than
+** or equal to the product of "sz" and "cnt".
+** The buffer must be aligned to an 8-byte boundary.
+** The lookaside memory
** configuration for a database connection can only be changed when that
** connection is not currently using lookaside memory, or in other words
-** when the "current value" returned by
-** [sqlite3_db_status](D,[SQLITE_DBSTATUS_LOOKASIDE_USED],...) is zero.
+** when the value returned by [SQLITE_DBSTATUS_LOOKASIDE_USED] is zero.
** Any attempt to change the lookaside memory configuration when lookaside
** memory is in use leaves the configuration unchanged and returns
-** [SQLITE_BUSY].)^</dd>
+** [SQLITE_BUSY].
+** If the "buf" argument is NULL and an attempt
+** to allocate memory based on "sz" and "cnt" fails, then
+** lookaside is silently disabled.
+** <p>
+** The [SQLITE_CONFIG_LOOKASIDE] configuration option can be used to set the
+** default lookaside configuration at initialization. The
+** [-DSQLITE_DEFAULT_LOOKASIDE] option can be used to set the default lookaside
+** configuration at compile-time. Typical values for lookaside are 1200 for
+** "sz" and 40 to 100 for "cnt".
+** </dd>
**
** [[SQLITE_DBCONFIG_ENABLE_FKEY]]
** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt>
** <dd> ^This option is used to enable or disable the enforcement of
-** [foreign key constraints]. There should be two additional arguments.
+** [foreign key constraints]. This is the same setting that is
+** enabled or disabled by the [PRAGMA foreign_keys] statement.
** The first argument is an integer which is 0 to disable FK enforcement,
** positive to enable FK enforcement or negative to leave FK enforcement
** unchanged. The second parameter is a pointer to an integer into which
@@ -2263,13 +2313,13 @@ struct sqlite3_mem_methods {
** <p>Originally this option disabled all triggers. ^(However, since
** SQLite version 3.35.0, TEMP triggers are still allowed even if
** this option is off. So, in other words, this option now only disables
-** triggers in the main database schema or in the schemas of ATTACH-ed
+** triggers in the main database schema or in the schemas of [ATTACH]-ed
** databases.)^ </dd>
**
** [[SQLITE_DBCONFIG_ENABLE_VIEW]]
** <dt>SQLITE_DBCONFIG_ENABLE_VIEW</dt>
** <dd> ^This option is used to enable or disable [CREATE VIEW | views].
-** There should be two additional arguments.
+** There must be two additional arguments.
** The first argument is an integer which is 0 to disable views,
** positive to enable views or negative to leave the setting unchanged.
** The second parameter is a pointer to an integer into which
@@ -2288,7 +2338,7 @@ struct sqlite3_mem_methods {
** <dd> ^This option is used to enable or disable the
** [fts3_tokenizer()] function which is part of the
** [FTS3] full-text search engine extension.
-** There should be two additional arguments.
+** There must be two additional arguments.
** The first argument is an integer which is 0 to disable fts3_tokenizer() or
** positive to enable fts3_tokenizer() or negative to leave the setting
** unchanged.
@@ -2303,7 +2353,7 @@ struct sqlite3_mem_methods {
** interface independently of the [load_extension()] SQL function.
** The [sqlite3_enable_load_extension()] API enables or disables both the
** C-API [sqlite3_load_extension()] and the SQL function [load_extension()].
-** There should be two additional arguments.
+** There must be two additional arguments.
** When the first argument to this interface is 1, then only the C-API is
** enabled and the SQL function remains disabled. If the first argument to
** this interface is 0, then both the C-API and the SQL function are disabled.
@@ -2317,23 +2367,30 @@ struct sqlite3_mem_methods {
**
** [[SQLITE_DBCONFIG_MAINDBNAME]] <dt>SQLITE_DBCONFIG_MAINDBNAME</dt>
** <dd> ^This option is used to change the name of the "main" database
-** schema. ^The sole argument is a pointer to a constant UTF8 string
-** which will become the new schema name in place of "main". ^SQLite
-** does not make a copy of the new main schema name string, so the application
-** must ensure that the argument passed into this DBCONFIG option is unchanged
-** until after the database connection closes.
+** schema. This option does not follow the
+** [DBCONFIG arguments|usual SQLITE_DBCONFIG argument format].
+** This option takes exactly one additional argument so that the
+** [sqlite3_db_config()] call has a total of three parameters. The
+** extra argument must be a pointer to a constant UTF8 string which
+** will become the new schema name in place of "main". ^SQLite does
+** not make a copy of the new main schema name string, so the application
+** must ensure that the argument passed into SQLITE_DBCONFIG MAINDBNAME
+** is unchanged until after the database connection closes.
** </dd>
**
** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]]
** <dt>SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE</dt>
-** <dd> Usually, when a database in wal mode is closed or detached from a
-** database handle, SQLite checks if this will mean that there are now no
-** connections at all to the database. If so, it performs a checkpoint
-** operation before closing the connection. This option may be used to
-** override this behavior. The first parameter passed to this operation
-** is an integer - positive to disable checkpoints-on-close, or zero (the
-** default) to enable them, and negative to leave the setting unchanged.
-** The second parameter is a pointer to an integer
+** <dd> Usually, when a database in [WAL mode] is closed or detached from a
+** database handle, SQLite checks if if there are other connections to the
+** same database, and if there are no other database connection (if the
+** connection being closed is the last open connection to the database),
+** then SQLite performs a [checkpoint] before closing the connection and
+** deletes the WAL file. The SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE option can
+** be used to override that behavior. The first argument passed to this
+** operation (the third parameter to [sqlite3_db_config()]) is an integer
+** which is positive to disable checkpoints-on-close, or zero (the default)
+** to enable them, and negative to leave the setting unchanged.
+** The second argument (the fourth parameter) is a pointer to an integer
** into which is written 0 or 1 to indicate whether checkpoints-on-close
** have been disabled - 0 if they are not disabled, 1 if they are.
** </dd>
@@ -2494,7 +2551,7 @@ struct sqlite3_mem_methods {
** statistics. For statistics to be collected, the flag must be set on
** the database handle both when the SQL statement is prepared and when it
** is stepped. The flag is set (collection of statistics is enabled)
-** by default. This option takes two arguments: an integer and a pointer to
+** by default. <p>This option takes two arguments: an integer and a pointer to
** an integer.. The first argument is 1, 0, or -1 to enable, disable, or
** leave unchanged the statement scanstatus option. If the second argument
** is not NULL, then the value of the statement scanstatus setting after
@@ -2508,7 +2565,7 @@ struct sqlite3_mem_methods {
** in which tables and indexes are scanned so that the scans start at the end
** and work toward the beginning rather than starting at the beginning and
** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the
-** same as setting [PRAGMA reverse_unordered_selects]. This option takes
+** same as setting [PRAGMA reverse_unordered_selects]. <p>This option takes
** two arguments which are an integer and a pointer to an integer. The first
** argument is 1, 0, or -1 to enable, disable, or leave unchanged the
** reverse scan order flag, respectively. If the second argument is not NULL,
@@ -2517,7 +2574,76 @@ struct sqlite3_mem_methods {
** first argument.
** </dd>
**
+** [[SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE]]
+** <dt>SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE</dt>
+** <dd>The SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE option enables or disables
+** the ability of the [ATTACH DATABASE] SQL command to create a new database
+** file if the database filed named in the ATTACH command does not already
+** exist. This ability of ATTACH to create a new database is enabled by
+** default. Applications can disable or reenable the ability for ATTACH to
+** create new database files using this DBCONFIG option.<p>
+** This option takes two arguments which are an integer and a pointer
+** to an integer. The first argument is 1, 0, or -1 to enable, disable, or
+** leave unchanged the attach-create flag, respectively. If the second
+** argument is not NULL, then 0 or 1 is written into the integer that the
+** second argument points to depending on if the attach-create flag is set
+** after processing the first argument.
+** </dd>
+**
+** [[SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE]]
+** <dt>SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE</dt>
+** <dd>The SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE option enables or disables the
+** ability of the [ATTACH DATABASE] SQL command to open a database for writing.
+** This capability is enabled by default. Applications can disable or
+** reenable this capability using the current DBCONFIG option. If the
+** the this capability is disabled, the [ATTACH] command will still work,
+** but the database will be opened read-only. If this option is disabled,
+** then the ability to create a new database using [ATTACH] is also disabled,
+** regardless of the value of the [SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE]
+** option.<p>
+** This option takes two arguments which are an integer and a pointer
+** to an integer. The first argument is 1, 0, or -1 to enable, disable, or
+** leave unchanged the ability to ATTACH another database for writing,
+** respectively. If the second argument is not NULL, then 0 or 1 is written
+** into the integer to which the second argument points, depending on whether
+** the ability to ATTACH a read/write database is enabled or disabled
+** after processing the first argument.
+** </dd>
+**
+** [[SQLITE_DBCONFIG_ENABLE_COMMENTS]]
+** <dt>SQLITE_DBCONFIG_ENABLE_COMMENTS</dt>
+** <dd>The SQLITE_DBCONFIG_ENABLE_COMMENTS option enables or disables the
+** ability to include comments in SQL text. Comments are enabled by default.
+** An application can disable or reenable comments in SQL text using this
+** DBCONFIG option.<p>
+** This option takes two arguments which are an integer and a pointer
+** to an integer. The first argument is 1, 0, or -1 to enable, disable, or
+** leave unchanged the ability to use comments in SQL text,
+** respectively. If the second argument is not NULL, then 0 or 1 is written
+** into the integer that the second argument points to depending on if
+** comments are allowed in SQL text after processing the first argument.
+** </dd>
+**
** </dl>
+**
+** [[DBCONFIG arguments]] <h3>Arguments To SQLITE_DBCONFIG Options</h3>
+**
+** <p>Most of the SQLITE_DBCONFIG options take two arguments, so that the
+** overall call to [sqlite3_db_config()] has a total of four parameters.
+** The first argument (the third parameter to sqlite3_db_config()) is a integer.
+** The second argument is a pointer to an integer. If the first argument is 1,
+** then the option becomes enabled. If the first integer argument is 0, then the
+** option is disabled. If the first argument is -1, then the option setting
+** is unchanged. The second argument, the pointer to an integer, may be NULL.
+** If the second argument is not NULL, then a value of 0 or 1 is written into
+** the integer to which the second argument points, depending on whether the
+** setting is disabled or enabled after applying any changes specified by
+** the first argument.
+**
+** <p>While most SQLITE_DBCONFIG options use the argument format
+** described in the previous paragraph, the [SQLITE_DBCONFIG_MAINDBNAME]
+** and [SQLITE_DBCONFIG_LOOKASIDE] options are different. See the
+** documentation of those exceptional options for details.
*/
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
@@ -2539,7 +2665,10 @@ struct sqlite3_mem_methods {
#define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */
#define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */
#define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */
-#define SQLITE_DBCONFIG_MAX 1019 /* Largest DBCONFIG */
+#define SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE 1020 /* int int* */
+#define SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE 1021 /* int int* */
+#define SQLITE_DBCONFIG_ENABLE_COMMENTS 1022 /* int int* */
+#define SQLITE_DBCONFIG_MAX 1022 /* Largest DBCONFIG */
/*
** CAPI3REF: Enable Or Disable Extended Result Codes
@@ -2631,10 +2760,14 @@ SQLITE_API void sqlite3_set_last_insert_rowid(sqlite3*,sqlite3_int64);
** deleted by the most recently completed INSERT, UPDATE or DELETE
** statement on the database connection specified by the only parameter.
** The two functions are identical except for the type of the return value
-** and that if the number of rows modified by the most recent INSERT, UPDATE
+** and that if the number of rows modified by the most recent INSERT, UPDATE,
** or DELETE is greater than the maximum value supported by type "int", then
** the return value of sqlite3_changes() is undefined. ^Executing any other
** type of SQL statement does not modify the value returned by these functions.
+** For the purposes of this interface, a CREATE TABLE AS SELECT statement
+** does not count as an INSERT, UPDATE or DELETE statement and hence the rows
+** added to the new table by the CREATE TABLE AS SELECT statement are not
+** counted.
**
** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are
** considered - auxiliary changes caused by [CREATE TRIGGER | triggers],
@@ -2890,6 +3023,44 @@ SQLITE_API int sqlite3_busy_handler(sqlite3*,int(*)(void*,int),void*);
SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms);
/*
+** CAPI3REF: Set the Setlk Timeout
+** METHOD: sqlite3
+**
+** This routine is only useful in SQLITE_ENABLE_SETLK_TIMEOUT builds. If
+** the VFS supports blocking locks, it sets the timeout in ms used by
+** eligible locks taken on wal mode databases by the specified database
+** handle. In non-SQLITE_ENABLE_SETLK_TIMEOUT builds, or if the VFS does
+** not support blocking locks, this function is a no-op.
+**
+** Passing 0 to this function disables blocking locks altogether. Passing
+** -1 to this function requests that the VFS blocks for a long time -
+** indefinitely if possible. The results of passing any other negative value
+** are undefined.
+**
+** Internally, each SQLite database handle store two timeout values - the
+** busy-timeout (used for rollback mode databases, or if the VFS does not
+** support blocking locks) and the setlk-timeout (used for blocking locks
+** on wal-mode databases). The sqlite3_busy_timeout() method sets both
+** values, this function sets only the setlk-timeout value. Therefore,
+** to configure separate busy-timeout and setlk-timeout values for a single
+** database handle, call sqlite3_busy_timeout() followed by this function.
+**
+** Whenever the number of connections to a wal mode database falls from
+** 1 to 0, the last connection takes an exclusive lock on the database,
+** then checkpoints and deletes the wal file. While it is doing this, any
+** new connection that tries to read from the database fails with an
+** SQLITE_BUSY error. Or, if the SQLITE_SETLK_BLOCK_ON_CONNECT flag is
+** passed to this API, the new connection blocks until the exclusive lock
+** has been released.
+*/
+SQLITE_API int sqlite3_setlk_timeout(sqlite3*, int ms, int flags);
+
+/*
+** CAPI3REF: Flags for sqlite3_setlk_timeout()
+*/
+#define SQLITE_SETLK_BLOCK_ON_CONNECT 0x01
+
+/*
** CAPI3REF: Convenience Routines For Running Queries
** METHOD: sqlite3
**
@@ -4194,11 +4365,22 @@ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal);
** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler
** to return an error (error code SQLITE_ERROR) if the statement uses
** any virtual tables.
+**
+** [[SQLITE_PREPARE_DONT_LOG]] <dt>SQLITE_PREPARE_DONT_LOG</dt>
+** <dd>The SQLITE_PREPARE_DONT_LOG flag prevents SQL compiler
+** errors from being sent to the error log defined by
+** [SQLITE_CONFIG_LOG]. This can be used, for example, to do test
+** compiles to see if some SQL syntax is well-formed, without generating
+** messages on the global error log when it is not. If the test compile
+** fails, the sqlite3_prepare_v3() call returns the same error indications
+** with or without this flag; it just omits the call to [sqlite3_log()] that
+** logs the error.
** </dl>
*/
#define SQLITE_PREPARE_PERSISTENT 0x01
#define SQLITE_PREPARE_NORMALIZE 0x02
#define SQLITE_PREPARE_NO_VTAB 0x04
+#define SQLITE_PREPARE_DONT_LOG 0x10
/*
** CAPI3REF: Compiling An SQL Statement
@@ -4993,7 +5175,7 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
** other than [SQLITE_ROW] before any subsequent invocation of
** sqlite3_step(). Failure to reset the prepared statement using
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
-** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1],
+** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]),
** sqlite3_step() began
** calling [sqlite3_reset()] automatically in this circumstance rather
** than returning [SQLITE_MISUSE]. This is not considered a compatibility
@@ -6889,6 +7071,8 @@ SQLITE_API int sqlite3_autovacuum_pages(
**
** ^The second argument is a pointer to the function to invoke when a
** row is updated, inserted or deleted in a rowid table.
+** ^The update hook is disabled by invoking sqlite3_update_hook()
+** with a NULL pointer as the second parameter.
** ^The first argument to the callback is a copy of the third argument
** to sqlite3_update_hook().
** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
@@ -10727,8 +10911,9 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c
/*
** CAPI3REF: Serialize a database
**
-** The sqlite3_serialize(D,S,P,F) interface returns a pointer to memory
-** that is a serialization of the S database on [database connection] D.
+** The sqlite3_serialize(D,S,P,F) interface returns a pointer to
+** memory that is a serialization of the S database on
+** [database connection] D. If S is a NULL pointer, the main database is used.
** If P is not a NULL pointer, then the size of the database in bytes
** is written into *P.
**
@@ -10889,7 +11074,7 @@ SQLITE_API int sqlite3_deserialize(
#ifdef __cplusplus
} /* End of the 'extern "C"' block */
#endif
-#endif /* SQLITE3_H */
+/* #endif for SQLITE3_H will be added by mksqlite3.tcl */
/******** Begin file sqlite3rtree.h *********/
/*
@@ -11370,9 +11555,10 @@ SQLITE_API void sqlite3session_table_filter(
** is inserted while a session object is enabled, then later deleted while
** the same session object is disabled, no INSERT record will appear in the
** changeset, even though the delete took place while the session was disabled.
-** Or, if one field of a row is updated while a session is disabled, and
-** another field of the same row is updated while the session is enabled, the
-** resulting changeset will contain an UPDATE change that updates both fields.
+** Or, if one field of a row is updated while a session is enabled, and
+** then another field of the same row is updated while the session is disabled,
+** the resulting changeset will contain an UPDATE change that updates both
+** fields.
*/
SQLITE_API int sqlite3session_changeset(
sqlite3_session *pSession, /* Session object */
@@ -11444,8 +11630,9 @@ SQLITE_API sqlite3_int64 sqlite3session_changeset_size(sqlite3_session *pSession
** database zFrom the contents of the two compatible tables would be
** identical.
**
-** It an error if database zFrom does not exist or does not contain the
-** required compatible table.
+** Unless the call to this function is a no-op as described above, it is an
+** error if database zFrom does not exist or does not contain the required
+** compatible table.
**
** If the operation is successful, SQLITE_OK is returned. Otherwise, an SQLite
** error code. In this case, if argument pzErrMsg is not NULL, *pzErrMsg
@@ -11580,7 +11767,7 @@ SQLITE_API int sqlite3changeset_start_v2(
** The following flags may passed via the 4th parameter to
** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]:
**
-** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd>
+** <dt>SQLITE_CHANGESETSTART_INVERT <dd>
** Invert the changeset while iterating through it. This is equivalent to
** inverting a changeset using sqlite3changeset_invert() before applying it.
** It is an error to specify this flag with a patchset.
@@ -11895,19 +12082,6 @@ SQLITE_API int sqlite3changeset_concat(
void **ppOut /* OUT: Buffer containing output changeset */
);
-
-/*
-** CAPI3REF: Upgrade the Schema of a Changeset/Patchset
-*/
-SQLITE_API int sqlite3changeset_upgrade(
- sqlite3 *db,
- const char *zDb,
- int nIn, const void *pIn, /* Input changeset */
- int *pnOut, void **ppOut /* OUT: Inverse of input */
-);
-
-
-
/*
** CAPI3REF: Changegroup Handle
**
@@ -13140,14 +13314,29 @@ struct Fts5PhraseIter {
** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise,
** output variable (*ppToken) is set to point to a buffer containing the
** matching document token, and (*pnToken) to the size of that buffer in
-** bytes. This API is not available if the specified token matches a
-** prefix query term. In that case both output variables are always set
-** to 0.
+** bytes.
**
** The output text is not a copy of the document text that was tokenized.
** It is the output of the tokenizer module. For tokendata=1 tables, this
** includes any embedded 0x00 and trailing data.
**
+** This API may be slow in some cases if the token identified by parameters
+** iIdx and iToken matched a prefix token in the query. In most cases, the
+** first call to this API for each prefix token in the query is forced
+** to scan the portion of the full-text index that matches the prefix
+** token to collect the extra data required by this API. If the prefix
+** token matches a large number of token instances in the document set,
+** this may be a performance problem.
+**
+** If the user knows in advance that a query may use this API for a
+** prefix token, FTS5 may be configured to collect all required data as part
+** of the initial querying of the full-text index, avoiding the second scan
+** entirely. This also causes prefix queries that do not use this API to
+** run more slowly and use more memory. FTS5 may be configured in this way
+** either on a per-table basis using the [FTS5 insttoken | 'insttoken']
+** option, or on a per-query basis using the
+** [fts5_insttoken | fts5_insttoken()] user function.
+**
** This API can be quite slow if used with an FTS5 table created with the
** "detail=none" or "detail=column" option.
**
@@ -13581,3 +13770,4 @@ struct fts5_api {
#endif /* _FTS5_H */
/******** End of fts5.h *********/
+#endif /* SQLITE3_H */
diff --git a/libs/win32/mir_app.lib b/libs/win32/mir_app.lib
index aefec649ae..88bed48205 100644
--- a/libs/win32/mir_app.lib
+++ b/libs/win32/mir_app.lib
Binary files differ
diff --git a/libs/win32/mir_core.lib b/libs/win32/mir_core.lib
index 850b1a9be0..3d1a08a7d6 100644
--- a/libs/win32/mir_core.lib
+++ b/libs/win32/mir_core.lib
Binary files differ
diff --git a/libs/win64/mir_app.lib b/libs/win64/mir_app.lib
index f2db2e57a7..aab0772f56 100644
--- a/libs/win64/mir_app.lib
+++ b/libs/win64/mir_app.lib
Binary files differ
diff --git a/libs/win64/mir_core.lib b/libs/win64/mir_core.lib
index 513f95da7a..dc52baf2b0 100644
--- a/libs/win64/mir_core.lib
+++ b/libs/win64/mir_core.lib
Binary files differ