← Commit list

a362962b72

Stephen Farrell
Stephen Farrell
2024-04-16
SUCCESS Duration: 98s

Subject

TLS: add support for ECH (Encrypted Client Hello)

Binary

Download Binary (813 KB)

Architecture & Compiler Flags

Architecture: x86_64-linux-gnu Compiler: gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0

—

Build Log

$ git checkout -f a362962b7289ec02b412890c9515657cf0ed50ac
Previous HEAD position was edc2702a1f cmake: add librtmp/rtmpdump option and detection
HEAD is now at a362962b72 TLS: add support for ECH (Encrypted Client Hello)

$ git clean -fdx
Removing Makefile
Removing Makefile.in
Removing aclocal.m4
Removing autom4te.cache/
Removing compile
Removing config.guess
Removing config.log
Removing config.status
Removing config.sub
Removing configure
Removing curl-config
Removing depcomp
Removing docs/Makefile
Removing docs/Makefile.in
Removing docs/cmdline-opts/Makefile
Removing docs/cmdline-opts/Makefile.in
Removing docs/examples/.deps/
Removing docs/examples/Makefile
Removing docs/examples/Makefile.in
Removing docs/libcurl/Makefile
Removing docs/libcurl/Makefile.in
Removing docs/libcurl/opts/Makefile
Removing docs/libcurl/opts/Makefile.in
Removing include/Makefile
Removing include/Makefile.in
Removing include/curl/Makefile
Removing include/curl/Makefile.in
Removing install-sh
Removing lib/.deps/
Removing lib/.dirstamp
Removing lib/.libs/
Removing lib/Makefile
Removing lib/Makefile.in
Removing lib/base64.o
Removing lib/curl_config.h
Removing lib/curl_config.h.in
Removing lib/curl_multibyte.o
Removing lib/dynbuf.o
Removing lib/libcurl.la
Removing lib/libcurl.vers
Removing lib/libcurl_la-altsvc.lo
Removing lib/libcurl_la-altsvc.o
Removing lib/libcurl_la-amigaos.lo
Removing lib/libcurl_la-amigaos.o
Removing lib/libcurl_la-asyn-ares.lo
Removing lib/libcurl_la-asyn-ares.o
Removing lib/libcurl_la-asyn-thread.lo
Removing lib/libcurl_la-asyn-thread.o
Removing lib/libcurl_la-base64.lo
Removing lib/libcurl_la-base64.o
Removing lib/libcurl_la-bufq.lo
Removing lib/libcurl_la-bufq.o
Removing lib/libcurl_la-bufref.lo
Removing lib/libcurl_la-bufref.o
Removing lib/libcurl_la-c-hyper.lo
Removing lib/libcurl_la-c-hyper.o
Removing lib/libcurl_la-cf-h1-proxy.lo
Removing lib/libcurl_la-cf-h1-proxy.o
Removing lib/libcurl_la-cf-h2-proxy.lo
Removing lib/libcurl_la-cf-h2-proxy.o
Removing lib/libcurl_la-cf-haproxy.lo
Removing lib/libcurl_la-cf-haproxy.o
Removing lib/libcurl_la-cf-https-connect.lo
Removing lib/libcurl_la-cf-https-connect.o
Removing lib/libcurl_la-cf-socket.lo
Removing lib/libcurl_la-cf-socket.o
Removing lib/libcurl_la-cfilters.lo
Removing lib/libcurl_la-cfilters.o
Removing lib/libcurl_la-conncache.lo
Removing lib/libcurl_la-conncache.o
Removing lib/libcurl_la-connect.lo
Removing lib/libcurl_la-connect.o
Removing lib/libcurl_la-content_encoding.lo
Removing lib/libcurl_la-content_encoding.o
Removing lib/libcurl_la-cookie.lo
Removing lib/libcurl_la-cookie.o
Removing lib/libcurl_la-curl_addrinfo.lo
Removing lib/libcurl_la-curl_addrinfo.o
Removing lib/libcurl_la-curl_des.lo
Removing lib/libcurl_la-curl_des.o
Removing lib/libcurl_la-curl_endian.lo
Removing lib/libcurl_la-curl_endian.o
Removing lib/libcurl_la-curl_fnmatch.lo
Removing lib/libcurl_la-curl_fnmatch.o
Removing lib/libcurl_la-curl_get_line.lo
Removing lib/libcurl_la-curl_get_line.o
Removing lib/libcurl_la-curl_gethostname.lo
Removing lib/libcurl_la-curl_gethostname.o
Removing lib/libcurl_la-curl_gssapi.lo
Removing lib/libcurl_la-curl_gssapi.o
Removing lib/libcurl_la-curl_memrchr.lo
Removing lib/libcurl_la-curl_memrchr.o
Removing lib/libcurl_la-curl_multibyte.lo
Removing lib/libcurl_la-curl_multibyte.o
Removing lib/libcurl_la-curl_ntlm_core.lo
Removing lib/libcurl_la-curl_ntlm_core.o
Removing lib/libcurl_la-curl_path.lo
Removing lib/libcurl_la-curl_path.o
Removing lib/libcurl_la-curl_range.lo
Removing lib/libcurl_la-curl_range.o
Removing lib/libcurl_la-curl_rtmp.lo
Removing lib/libcurl_la-curl_rtmp.o
Removing lib/libcurl_la-curl_sasl.lo
Removing lib/libcurl_la-curl_sasl.o
Removing lib/libcurl_la-curl_sha512_256.lo
Removing lib/libcurl_la-curl_sha512_256.o
Removing lib/libcurl_la-curl_sspi.lo
Removing lib/libcurl_la-curl_sspi.o
Removing lib/libcurl_la-curl_threads.lo
Removing lib/libcurl_la-curl_threads.o
Removing lib/libcurl_la-curl_trc.lo
Removing lib/libcurl_la-curl_trc.o
Removing lib/libcurl_la-cw-out.lo
Removing lib/libcurl_la-cw-out.o
Removing lib/libcurl_la-dict.lo
Removing lib/libcurl_la-dict.o
Removing lib/libcurl_la-doh.lo
Removing lib/libcurl_la-doh.o
Removing lib/libcurl_la-dynbuf.lo
Removing lib/libcurl_la-dynbuf.o
Removing lib/libcurl_la-dynhds.lo
Removing lib/libcurl_la-dynhds.o
Removing lib/libcurl_la-easy.lo
Removing lib/libcurl_la-easy.o
Removing lib/libcurl_la-easygetopt.lo
Removing lib/libcurl_la-easygetopt.o
Removing lib/libcurl_la-easyoptions.lo
Removing lib/libcurl_la-easyoptions.o
Removing lib/libcurl_la-escape.lo
Removing lib/libcurl_la-escape.o
Removing lib/libcurl_la-file.lo
Removing lib/libcurl_la-file.o
Removing lib/libcurl_la-fileinfo.lo
Removing lib/libcurl_la-fileinfo.o
Removing lib/libcurl_la-fopen.lo
Removing lib/libcurl_la-fopen.o
Removing lib/libcurl_la-formdata.lo
Removing lib/libcurl_la-formdata.o
Removing lib/libcurl_la-ftp.lo
Removing lib/libcurl_la-ftp.o
Removing lib/libcurl_la-ftplistparser.lo
Removing lib/libcurl_la-ftplistparser.o
Removing lib/libcurl_la-getenv.lo
Removing lib/libcurl_la-getenv.o
Removing lib/libcurl_la-getinfo.lo
Removing lib/libcurl_la-getinfo.o
Removing lib/libcurl_la-gopher.lo
Removing lib/libcurl_la-gopher.o
Removing lib/libcurl_la-hash.lo
Removing lib/libcurl_la-hash.o
Removing lib/libcurl_la-headers.lo
Removing lib/libcurl_la-headers.o
Removing lib/libcurl_la-hmac.lo
Removing lib/libcurl_la-hmac.o
Removing lib/libcurl_la-hostasyn.lo
Removing lib/libcurl_la-hostasyn.o
Removing lib/libcurl_la-hostip.lo
Removing lib/libcurl_la-hostip.o
Removing lib/libcurl_la-hostip4.lo
Removing lib/libcurl_la-hostip4.o
Removing lib/libcurl_la-hostip6.lo
Removing lib/libcurl_la-hostip6.o
Removing lib/libcurl_la-hostsyn.lo
Removing lib/libcurl_la-hostsyn.o
Removing lib/libcurl_la-hsts.lo
Removing lib/libcurl_la-hsts.o
Removing lib/libcurl_la-http.lo
Removing lib/libcurl_la-http.o
Removing lib/libcurl_la-http1.lo
Removing lib/libcurl_la-http1.o
Removing lib/libcurl_la-http2.lo
Removing lib/libcurl_la-http2.o
Removing lib/libcurl_la-http_aws_sigv4.lo
Removing lib/libcurl_la-http_aws_sigv4.o
Removing lib/libcurl_la-http_chunks.lo
Removing lib/libcurl_la-http_chunks.o
Removing lib/libcurl_la-http_digest.lo
Removing lib/libcurl_la-http_digest.o
Removing lib/libcurl_la-http_negotiate.lo
Removing lib/libcurl_la-http_negotiate.o
Removing lib/libcurl_la-http_ntlm.lo
Removing lib/libcurl_la-http_ntlm.o
Removing lib/libcurl_la-http_proxy.lo
Removing lib/libcurl_la-http_proxy.o
Removing lib/libcurl_la-idn.lo
Removing lib/libcurl_la-idn.o
Removing lib/libcurl_la-if2ip.lo
Removing lib/libcurl_la-if2ip.o
Removing lib/libcurl_la-imap.lo
Removing lib/libcurl_la-imap.o
Removing lib/libcurl_la-inet_ntop.lo
Removing lib/libcurl_la-inet_ntop.o
Removing lib/libcurl_la-inet_pton.lo
Removing lib/libcurl_la-inet_pton.o
Removing lib/libcurl_la-krb5.lo
Removing lib/libcurl_la-krb5.o
Removing lib/libcurl_la-ldap.lo
Removing lib/libcurl_la-ldap.o
Removing lib/libcurl_la-llist.lo
Removing lib/libcurl_la-llist.o
Removing lib/libcurl_la-macos.lo
Removing lib/libcurl_la-macos.o
Removing lib/libcurl_la-md4.lo
Removing lib/libcurl_la-md4.o
Removing lib/libcurl_la-md5.lo
Removing lib/libcurl_la-md5.o
Removing lib/libcurl_la-memdebug.lo
Removing lib/libcurl_la-memdebug.o
Removing lib/libcurl_la-mime.lo
Removing lib/libcurl_la-mime.o
Removing lib/libcurl_la-mprintf.lo
Removing lib/libcurl_la-mprintf.o
Removing lib/libcurl_la-mqtt.lo
Removing lib/libcurl_la-mqtt.o
Removing lib/libcurl_la-multi.lo
Removing lib/libcurl_la-multi.o
Removing lib/libcurl_la-netrc.lo
Removing lib/libcurl_la-netrc.o
Removing lib/libcurl_la-nonblock.lo
Removing lib/libcurl_la-nonblock.o
Removing lib/libcurl_la-noproxy.lo
Removing lib/libcurl_la-noproxy.o
Removing lib/libcurl_la-openldap.lo
Removing lib/libcurl_la-openldap.o
Removing lib/libcurl_la-parsedate.lo
Removing lib/libcurl_la-parsedate.o
Removing lib/libcurl_la-pingpong.lo
Removing lib/libcurl_la-pingpong.o
Removing lib/libcurl_la-pop3.lo
Removing lib/libcurl_la-pop3.o
Removing lib/libcurl_la-progress.lo
Removing lib/libcurl_la-progress.o
Removing lib/libcurl_la-psl.lo
Removing lib/libcurl_la-psl.o
Removing lib/libcurl_la-rand.lo
Removing lib/libcurl_la-rand.o
Removing lib/libcurl_la-rename.lo
Removing lib/libcurl_la-rename.o
Removing lib/libcurl_la-request.lo
Removing lib/libcurl_la-request.o
Removing lib/libcurl_la-rtsp.lo
Removing lib/libcurl_la-rtsp.o
Removing lib/libcurl_la-select.lo
Removing lib/libcurl_la-select.o
Removing lib/libcurl_la-sendf.lo
Removing lib/libcurl_la-sendf.o
Removing lib/libcurl_la-setopt.lo
Removing lib/libcurl_la-setopt.o
Removing lib/libcurl_la-sha256.lo
Removing lib/libcurl_la-sha256.o
Removing lib/libcurl_la-share.lo
Removing lib/libcurl_la-share.o
Removing lib/libcurl_la-slist.lo
Removing lib/libcurl_la-slist.o
Removing lib/libcurl_la-smb.lo
Removing lib/libcurl_la-smb.o
Removing lib/libcurl_la-smtp.lo
Removing lib/libcurl_la-smtp.o
Removing lib/libcurl_la-socketpair.lo
Removing lib/libcurl_la-socketpair.o
Removing lib/libcurl_la-socks.lo
Removing lib/libcurl_la-socks.o
Removing lib/libcurl_la-socks_gssapi.lo
Removing lib/libcurl_la-socks_gssapi.o
Removing lib/libcurl_la-socks_sspi.lo
Removing lib/libcurl_la-socks_sspi.o
Removing lib/libcurl_la-speedcheck.lo
Removing lib/libcurl_la-speedcheck.o
Removing lib/libcurl_la-splay.lo
Removing lib/libcurl_la-splay.o
Removing lib/libcurl_la-strcase.lo
Removing lib/libcurl_la-strcase.o
Removing lib/libcurl_la-strdup.lo
Removing lib/libcurl_la-strdup.o
Removing lib/libcurl_la-strerror.lo
Removing lib/libcurl_la-strerror.o
Removing lib/libcurl_la-strtok.lo
Removing lib/libcurl_la-strtok.o
Removing lib/libcurl_la-strtoofft.lo
Removing lib/libcurl_la-strtoofft.o
Removing lib/libcurl_la-system_win32.lo
Removing lib/libcurl_la-system_win32.o
Removing lib/libcurl_la-telnet.lo
Removing lib/libcurl_la-telnet.o
Removing lib/libcurl_la-tftp.lo
Removing lib/libcurl_la-tftp.o
Removing lib/libcurl_la-timediff.lo
Removing lib/libcurl_la-timediff.o
Removing lib/libcurl_la-timeval.lo
Removing lib/libcurl_la-timeval.o
Removing lib/libcurl_la-transfer.lo
Removing lib/libcurl_la-transfer.o
Removing lib/libcurl_la-url.lo
Removing lib/libcurl_la-url.o
Removing lib/libcurl_la-urlapi.lo
Removing lib/libcurl_la-urlapi.o
Removing lib/libcurl_la-version.lo
Removing lib/libcurl_la-version.o
Removing lib/libcurl_la-version_win32.lo
Removing lib/libcurl_la-version_win32.o
Removing lib/libcurl_la-warnless.lo
Removing lib/libcurl_la-warnless.o
Removing lib/libcurl_la-ws.lo
Removing lib/libcurl_la-ws.o
Removing lib/nonblock.o
Removing lib/stamp-h1
Removing lib/strtoofft.o
Removing lib/timediff.o
Removing lib/vauth/.deps/
Removing lib/vauth/.dirstamp
Removing lib/vauth/libcurl_la-cleartext.lo
Removing lib/vauth/libcurl_la-cleartext.o
Removing lib/vauth/libcurl_la-cram.lo
Removing lib/vauth/libcurl_la-cram.o
Removing lib/vauth/libcurl_la-digest.lo
Removing lib/vauth/libcurl_la-digest.o
Removing lib/vauth/libcurl_la-digest_sspi.lo
Removing lib/vauth/libcurl_la-digest_sspi.o
Removing lib/vauth/libcurl_la-gsasl.lo
Removing lib/vauth/libcurl_la-gsasl.o
Removing lib/vauth/libcurl_la-krb5_gssapi.lo
Removing lib/vauth/libcurl_la-krb5_gssapi.o
Removing lib/vauth/libcurl_la-krb5_sspi.lo
Removing lib/vauth/libcurl_la-krb5_sspi.o
Removing lib/vauth/libcurl_la-ntlm.lo
Removing lib/vauth/libcurl_la-ntlm.o
Removing lib/vauth/libcurl_la-ntlm_sspi.lo
Removing lib/vauth/libcurl_la-ntlm_sspi.o
Removing lib/vauth/libcurl_la-oauth2.lo
Removing lib/vauth/libcurl_la-oauth2.o
Removing lib/vauth/libcurl_la-spnego_gssapi.lo
Removing lib/vauth/libcurl_la-spnego_gssapi.o
Removing lib/vauth/libcurl_la-spnego_sspi.lo
Removing lib/vauth/libcurl_la-spnego_sspi.o
Removing lib/vauth/libcurl_la-vauth.lo
Removing lib/vauth/libcurl_la-vauth.o
Removing lib/version_win32.o
Removing lib/vquic/.deps/
Removing lib/vquic/.dirstamp
Removing lib/vquic/libcurl_la-curl_msh3.lo
Removing lib/vquic/libcurl_la-curl_msh3.o
Removing lib/vquic/libcurl_la-curl_ngtcp2.lo
Removing lib/vquic/libcurl_la-curl_ngtcp2.o
Removing lib/vquic/libcurl_la-curl_osslq.lo
Removing lib/vquic/libcurl_la-curl_osslq.o
Removing lib/vquic/libcurl_la-curl_quiche.lo
Removing lib/vquic/libcurl_la-curl_quiche.o
Removing lib/vquic/libcurl_la-vquic-tls.lo
Removing lib/vquic/libcurl_la-vquic-tls.o
Removing lib/vquic/libcurl_la-vquic.lo
Removing lib/vquic/libcurl_la-vquic.o
Removing lib/vssh/.deps/
Removing lib/vssh/.dirstamp
Removing lib/vssh/libcurl_la-libssh.lo
Removing lib/vssh/libcurl_la-libssh.o
Removing lib/vssh/libcurl_la-libssh2.lo
Removing lib/vssh/libcurl_la-libssh2.o
Removing lib/vssh/libcurl_la-wolfssh.lo
Removing lib/vssh/libcurl_la-wolfssh.o
Removing lib/vtls/.deps/
Removing lib/vtls/.dirstamp
Removing lib/vtls/libcurl_la-bearssl.lo
Removing lib/vtls/libcurl_la-bearssl.o
Removing lib/vtls/libcurl_la-gtls.lo
Removing lib/vtls/libcurl_la-gtls.o
Removing lib/vtls/libcurl_la-hostcheck.lo
Removing lib/vtls/libcurl_la-hostcheck.o
Removing lib/vtls/libcurl_la-keylog.lo
Removing lib/vtls/libcurl_la-keylog.o
Removing lib/vtls/libcurl_la-mbedtls.lo
Removing lib/vtls/libcurl_la-mbedtls.o
Removing lib/vtls/libcurl_la-mbedtls_threadlock.lo
Removing lib/vtls/libcurl_la-mbedtls_threadlock.o
Removing lib/vtls/libcurl_la-openssl.lo
Removing lib/vtls/libcurl_la-openssl.o
Removing lib/vtls/libcurl_la-rustls.lo
Removing lib/vtls/libcurl_la-rustls.o
Removing lib/vtls/libcurl_la-schannel.lo
Removing lib/vtls/libcurl_la-schannel.o
Removing lib/vtls/libcurl_la-schannel_verify.lo
Removing lib/vtls/libcurl_la-schannel_verify.o
Removing lib/vtls/libcurl_la-sectransp.lo
Removing lib/vtls/libcurl_la-sectransp.o
Removing lib/vtls/libcurl_la-vtls.lo
Removing lib/vtls/libcurl_la-vtls.o
Removing lib/vtls/libcurl_la-wolfssl.lo
Removing lib/vtls/libcurl_la-wolfssl.o
Removing lib/vtls/libcurl_la-x509asn1.lo
Removing lib/vtls/libcurl_la-x509asn1.o
Removing lib/warnless.o
Removing libcurl.pc
Removing libtool
Removing ltmain.sh
Removing m4/libtool.m4
Removing m4/ltoptions.m4
Removing m4/ltsugar.m4
Removing m4/ltversion.m4
Removing m4/lt~obsolete.m4
Removing missing
Removing packages/Makefile
Removing packages/Makefile.in
Removing packages/vms/Makefile
Removing packages/vms/Makefile.in
Removing scripts/Makefile
Removing scripts/Makefile.in
Removing src/.deps/
Removing src/.libs/
Removing src/Makefile
Removing src/Makefile.in
Removing src/curl
Removing src/slist_wc.o
Removing src/tool_binmode.o
Removing src/tool_bname.o
Removing src/tool_cb_dbg.o
Removing src/tool_cb_hdr.o
Removing src/tool_cb_prg.o
Removing src/tool_cb_rea.o
Removing src/tool_cb_see.o
Removing src/tool_cb_wrt.o
Removing src/tool_cfgable.o
Removing src/tool_dirhie.o
Removing src/tool_doswin.o
Removing src/tool_easysrc.o
Removing src/tool_filetime.o
Removing src/tool_findfile.o
Removing src/tool_formparse.o
Removing src/tool_getparam.o
Removing src/tool_getpass.o
Removing src/tool_help.o
Removing src/tool_helpers.o
Removing src/tool_hugehelp.c
Removing src/tool_hugehelp.o
Removing src/tool_ipfs.o
Removing src/tool_libinfo.o
Removing src/tool_listhelp.o
Removing src/tool_main.o
Removing src/tool_msgs.o
Removing src/tool_operate.o
Removing src/tool_operhlp.o
Removing src/tool_paramhlp.o
Removing src/tool_parsecfg.o
Removing src/tool_progress.o
Removing src/tool_setopt.o
Removing src/tool_sleep.o
Removing src/tool_stderr.o
Removing src/tool_strdup.o
Removing src/tool_urlglob.o
Removing src/tool_util.o
Removing src/tool_vms.o
Removing src/tool_writeout.o
Removing src/tool_writeout_json.o
Removing src/tool_xattr.o
Removing src/var.o
Removing tests/Makefile
Removing tests/Makefile.in
Removing tests/certs/Makefile
Removing tests/certs/Makefile.in
Removing tests/certs/scripts/Makefile
Removing tests/certs/scripts/Makefile.in
Removing tests/config
Removing tests/configurehelp.pm
Removing tests/data/Makefile
Removing tests/data/Makefile.in
Removing tests/http/Makefile
Removing tests/http/Makefile.in
Removing tests/http/clients/.deps/
Removing tests/http/clients/Makefile
Removing tests/http/clients/Makefile.in
Removing tests/http/config.ini
Removing tests/libtest/.deps/
Removing tests/libtest/Makefile
Removing tests/libtest/Makefile.in
Removing tests/server/.deps/
Removing tests/server/Makefile
Removing tests/server/Makefile.in
Removing tests/unit/.deps/
Removing tests/unit/Makefile
Removing tests/unit/Makefile.in

$ autoreconf -fi
libtoolize: putting auxiliary files in '.'.
libtoolize: copying file './ltmain.sh'
libtoolize: putting macros in AC_CONFIG_MACRO_DIRS, 'm4'.
libtoolize: copying file 'm4/libtool.m4'
libtoolize: copying file 'm4/ltoptions.m4'
libtoolize: copying file 'm4/ltsugar.m4'
libtoolize: copying file 'm4/ltversion.m4'
libtoolize: copying file 'm4/lt~obsolete.m4'
libtoolize: Remember to add 'LT_INIT' to configure.ac.
configure.ac:124: installing './compile'
configure.ac:426: installing './config.guess'
configure.ac:426: installing './config.sub'
configure.ac:124: installing './install-sh'
configure.ac:130: installing './missing'
docs/examples/Makefile.am: installing './depcomp'

$ bash -c find . -name config.guess -exec cp /usr/share/misc/config.guess {} \;

$ bash -c find . -name config.sub -exec cp /usr/share/misc/config.sub {} \;

$ chmod +x configure

$ ./configure --disable-shared --without-ssl --disable-docs
checking whether to enable maintainer-specific portions of Makefiles... no
checking whether make supports nested variables... yes
checking whether to enable debug build options... no
checking whether to enable compiler optimizer... (assumed) yes
checking whether to enable strict compiler warnings... no
checking whether to enable compiler warnings as errors... no
checking whether to enable curl debug memory tracking... no
checking whether to enable hiding of library internal symbols... yes
checking whether to enable c-ares for DNS lookups... no
checking whether to disable dependency on -lrt... (assumed no)
checking whether to enable HTTPSRR support... no
checking whether to enable ECH support... no
checking for path separator... :
checking for sed... /usr/bin/sed
checking for grep... /usr/bin/grep
checking that grep -E works... yes
checking for ar... /usr/bin/ar
checking for a BSD-compatible install... /usr/bin/install -c
checking for gcc... ccache gcc
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables... 
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether the compiler supports GNU C... yes
checking whether ccache gcc accepts -g... yes
checking for ccache gcc option to enable C11 features... none needed
checking whether ccache gcc understands -c and -o together... yes
checking how to run the C preprocessor... ccache gcc -E
checking for stdio.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for strings.h... yes
checking for sys/stat.h... yes
checking for sys/types.h... yes
checking for unistd.h... yes
checking for stdatomic.h... yes
checking if _Atomic is available... yes
checking for a sed that does not truncate output... (cached) /usr/bin/sed
checking for code coverage support... no
checking whether build environment is sane... yes
checking for a race-free mkdir -p... /usr/bin/mkdir -p
checking for gawk... gawk
checking whether make sets $(MAKE)... yes
checking whether make supports the include directive... yes (GNU style)
checking dependency style of ccache gcc... gcc3
checking curl version... 8.8.0-DEV
checking for httpd... no
checking for apache2... no
checking for apachectl... no
checking for apxs... no
configure: httpd/apache2 not in PATH, http tests disabled
configure: apxs not in PATH, http tests disabled
checking for nghttpx... no
checking for caddy... no
checking build system type... x86_64-pc-linux-gnu
checking host system type... x86_64-pc-linux-gnu
checking for grep that handles long lines and -e... (cached) /usr/bin/grep
checking for egrep... /usr/bin/grep -E
checking if OS is AIX (to define _ALL_SOURCE)... no
checking if _THREAD_SAFE is already defined... no
checking if _THREAD_SAFE is actually needed... no
checking if _THREAD_SAFE is onwards defined... no
checking if _REENTRANT is already defined... no
checking if _REENTRANT is actually needed... no
checking if _REENTRANT is onwards defined... no
checking for special C compiler options needed for large files... no
checking for _FILE_OFFSET_BITS value needed for large files... no
checking how to print strings... printf
checking for a sed that does not truncate output... (cached) /usr/bin/sed
checking for fgrep... /usr/bin/grep -F
checking for ld used by ccache gcc... /usr/bin/ld
checking if the linker (/usr/bin/ld) is GNU ld... yes
checking for BSD- or MS-compatible name lister (nm)... /usr/bin/nm -B
checking the name lister (/usr/bin/nm -B) interface... BSD nm
checking whether ln -s works... yes
checking the maximum length of command line arguments... 1572864
checking how to convert x86_64-pc-linux-gnu file names to x86_64-pc-linux-gnu format... func_convert_file_noop
checking how to convert x86_64-pc-linux-gnu file names to toolchain format... func_convert_file_noop
checking for /usr/bin/ld option to reload object files... -r
checking for objdump... objdump
checking how to recognize dependent libraries... pass_all
checking for dlltool... no
checking how to associate runtime and link libraries... printf %s\n
checking for archiver @FILE support... @
checking for strip... strip
checking for ranlib... ranlib
checking command to parse /usr/bin/nm -B output from ccache gcc object... ok
checking for sysroot... no
checking for a working dd... /usr/bin/dd
checking how to truncate binary pipes... /usr/bin/dd bs=4096 count=1
checking for mt... mt
checking if mt is a manifest tool... no
checking for dlfcn.h... yes
checking for objdir... .libs
checking if ccache gcc supports -fno-rtti -fno-exceptions... no
checking for ccache gcc option to produce PIC... -fPIC -DPIC
checking if ccache gcc PIC flag -fPIC -DPIC works... yes
checking if ccache gcc static flag -static works... yes
checking if ccache gcc supports -c -o file.o... yes
checking if ccache gcc supports -c -o file.o... (cached) yes
checking whether the ccache gcc linker (/usr/bin/ld -m elf_x86_64) supports shared libraries... yes
checking dynamic linker characteristics... GNU/Linux ld.so
checking how to hardcode library paths into programs... immediate
checking whether stripping libraries is possible... yes
checking if libtool supports shared libraries... yes
checking whether to build shared libraries... no
checking whether to build static libraries... yes
checking whether to build shared libraries with -version-info... yes
checking whether to build shared libraries with -no-undefined... no
checking whether to build shared libraries with -mimpure-text... no
checking whether to build shared libraries with PIC... yes
checking whether to build static libraries with PIC... yes
checking whether to build shared libraries only... no
checking whether to build static libraries only... yes
checking for windres... no
checking for inline... inline
checking if cpp -P is needed... yes
checking if cpp -P works... yes
checking if compiler is DEC/Compaq/HP C... no
checking if compiler is HP-UX C... no
checking if compiler is IBM C... no
checking if compiler is Intel C... no
checking if compiler is clang... no
checking if compiler is GNU C... yes
checking compiler version... gcc '1100' (raw: '11')
checking if compiler is SGI MIPSpro C... no
checking if compiler is SGI MIPS C... no
checking if compiler is SunPro C... no
checking if compiler is Tiny C... no
checking whether build target is a native Windows one... no
checking if compiler accepts some basic options... yes
configure: compiler options added: -Werror-implicit-function-declaration 
checking if compiler optimizer assumed setting might be used... yes
checking if compiler accepts optimizer enabling options... yes
configure: compiler options added: -O2
checking if compiler accepts strict warning options... yes
configure: compiler options added: -Wno-system-headers 
checking if compiler halts on compilation errors... yes
checking if compiler halts on negative sized arrays... yes
checking if compiler halts on function prototype mismatch... yes
checking if compiler supports hiding library internal symbols... yes
checking whether build target supports WIN32 file API... no
checking whether build target supports WIN32 crypto API... no
checking for good-to-use Darwin CFLAGS... no
checking whether to link macOS CoreFoundation, CoreServices, and SystemConfiguration frameworks... no
checking to see if the compiler supports __builtin_available()... no
checking whether to support http... yes
checking whether to support ftp... yes
checking whether to support file... yes
checking whether to support ldap... yes
checking whether to support ldaps... yes
checking whether to support rtsp... yes
checking whether to support proxies... yes
checking whether to support dict... yes
checking whether to support telnet... yes
checking whether to support tftp... yes
checking whether to support pop3... yes
checking whether to support imap... yes
checking whether to support smb... yes
checking whether to support smtp... yes
checking whether to support gopher... yes
checking whether to support mqtt... no
checking whether to provide built-in manual... yes
checking whether to build documentation... no
checking whether to enable generation of C code... yes
checking whether to use libgcc... no
checking if X/Open network library is required... no
checking for gethostbyname... yes
checking whether build target is a native Windows one... (cached) no
checking for proto/bsdsocket.h... no
checking for connect in libraries... yes
checking for sys/types.h... (cached) yes
checking for sys/time.h... yes
checking for monotonic clock_gettime... yes
checking for clock_gettime in libraries... no additional lib required
checking if monotonic clock_gettime works... yes
checking for sys/types.h... (cached) yes
checking for sys/time.h... (cached) yes
checking for raw monotonic clock_gettime... yes
checking for pkg-config... /usr/bin/pkg-config
checking for zlib options with pkg-config... found
checking for zlib.h... yes
configure: found both libz and libz.h header
checking for BrotliDecoderDecompress in -lbrotlidec... no
checking for brotli/decode.h... no
checking for ZSTD_createDStream in -lzstd... no
checking for zstd.h... no
checking for lber.h... no
checking for ldap.h... no
checking for ldap_ssl.h... no
checking for LDAP libraries... cannot find LDAP libraries
checking whether to enable IPv6... yes
checking if struct sockaddr_in6 has sin6_scope_id member... yes
checking if argv can be written to... yes
checking if GSS-API support is requested... no
checking whether to enable Windows native SSL/TLS... no
checking whether to enable Secure Transport... no
checking whether to enable Amiga native SSL/TLS (AmiSSL v5)... no
checking for psl_builtin in -lpsl... yes
checking for libpsl.h... yes
checking for library containing gsasl_init... no
checking for RTMP_Init in -lrtmp... no
checking whether versioned symbols are wanted... no
checking whether to enable Windows native IDN (Windows native builds only)... no
checking whether to build with libidn2... (assumed) yes
checking for pkg-config... (cached) /usr/bin/pkg-config
checking for libidn2 options with pkg-config... no
configure: IDN_LIBS: "-lidn2"
configure: IDN_LDFLAGS: ""
configure: IDN_CPPFLAGS: ""
configure: IDN_DIR: ""
checking if idn2_lookup_ul can be linked... no
checking for idn2.h... no
checking for pkg-config... (cached) /usr/bin/pkg-config
checking for libnghttp2 options with pkg-config... no
checking for nghttp2_session_get_stream_local_window_size in -lnghttp2... no
checking for sys/types.h... (cached) yes
checking for sys/time.h... (cached) yes
checking for sys/select.h... yes
checking for sys/socket.h... yes
checking for sys/ioctl.h... yes
checking for unistd.h... (cached) yes
checking for stdlib.h... (cached) yes
checking for arpa/inet.h... yes
checking for net/if.h... yes
checking for netinet/in.h... yes
checking for netinet/in6.h... no
checking for sys/un.h... yes
checking for linux/tcp.h... yes
checking for netinet/tcp.h... yes
checking for netinet/udp.h... yes
checking for netdb.h... yes
checking for sys/sockio.h... no
checking for sys/stat.h... (cached) yes
checking for sys/param.h... yes
checking for termios.h... yes
checking for termio.h... yes
checking for fcntl.h... yes
checking for io.h... no
checking for pwd.h... yes
checking for utime.h... yes
checking for sys/utime.h... no
checking for sys/poll.h... yes
checking for poll.h... yes
checking for socket.h... no
checking for sys/resource.h... yes
checking for libgen.h... yes
checking for locale.h... yes
checking for stdbool.h... yes
checking for sys/filio.h... no
checking for sys/wait.h... yes
checking for setjmp.h... yes
checking for an ANSI C-conforming const... yes
checking for size_t... yes
checking for sys/types.h... (cached) yes
checking for sys/time.h... (cached) yes
checking for sys/socket.h... (cached) yes
checking for struct timeval... yes
checking run-time libs availability... fine
checking size of size_t... 8
checking size of long... 8
checking size of int... 4
checking size of time_t... 8
checking size of off_t... 8
checking size of curl_off_t... 8
checking size of curl_socket_t... 4
checking for long long... yes
checking for ssize_t... yes
checking for bool... yes
checking for sa_family_t... yes
checking for suseconds_t... yes
checking if time_t is unsigned... no
checking for in_addr_t... yes
checking for struct sockaddr_storage... yes
checking for sys/select.h... (cached) yes
checking for sys/socket.h... (cached) yes
checking for select... yes
checking for sys/types.h... (cached) yes
checking for sys/socket.h... (cached) yes
checking for recv... yes
checking for sys/types.h... (cached) yes
checking for sys/socket.h... (cached) yes
checking for send... yes
checking for sys/types.h... (cached) yes
checking for sys/socket.h... (cached) yes
checking for MSG_NOSIGNAL... yes
checking for sys/types.h... (cached) yes
checking for unistd.h... (cached) yes
checking if alarm can be linked... yes
checking if alarm is prototyped... yes
checking if alarm is compilable... yes
checking if alarm usage allowed... yes
checking if alarm might be used... yes
checking for sys/types.h... (cached) yes
checking for strings.h... (cached) yes
checking for sys/types.h... (cached) yes
checking for libgen.h... (cached) yes
checking if basename can be linked... yes
checking if basename is prototyped... yes
checking if basename is compilable... yes
checking if basename usage allowed... yes
checking if basename might be used... yes
checking for sys/types.h... (cached) yes
checking for socket.h... (cached) no
checking if closesocket can be linked... no
checking if closesocket might be used... no
checking for sys/types.h... (cached) yes
checking for sys/socket.h... (cached) yes
checking if CloseSocket can be linked... no
checking if CloseSocket might be used... no
checking for sys/types.h... (cached) yes
checking for unistd.h... (cached) yes
checking for fcntl.h... (cached) yes
checking if fcntl can be linked... yes
checking if fcntl is prototyped... yes
checking if fcntl is compilable... yes
checking if fcntl usage allowed... yes
checking if fcntl might be used... yes
checking if fcntl O_NONBLOCK is compilable... yes
checking if fcntl O_NONBLOCK usage allowed... yes
checking if fcntl O_NONBLOCK might be used... yes
checking whether build target is a native Windows one... (cached) no
checking for sys/types.h... (cached) yes
checking for netdb.h... (cached) yes
checking if freeaddrinfo can be linked... yes
checking if freeaddrinfo is prototyped... yes
checking if freeaddrinfo is compilable... yes
checking if freeaddrinfo usage allowed... yes
checking if freeaddrinfo might be used... yes
checking for sys/types.h... (cached) yes
checking for sys/xattr.h... yes
checking if fsetxattr can be linked... yes
checking if fsetxattr is prototyped... yes
checking if fsetxattr takes 5 args.... yes
checking if fsetxattr is compilable... yes
checking if fsetxattr usage allowed... yes
checking if fsetxattr might be used... yes
checking if ftruncate can be linked... yes
checking if ftruncate is prototyped... yes
checking if ftruncate is compilable... yes
checking if ftruncate usage allowed... yes
checking if ftruncate might be used... yes
checking for sys/types.h... (cached) yes
checking if getaddrinfo can be linked... yes
checking if getaddrinfo is prototyped... yes
checking if getaddrinfo is compilable... yes
checking if getaddrinfo seems to work... yes
checking if getaddrinfo usage allowed... yes
checking if getaddrinfo might be used... yes
checking if getaddrinfo is threadsafe... yes
checking if gethostbyname can be linked... yes
checking if gethostbyname is prototyped... yes
checking if gethostbyname is compilable... yes
checking if gethostbyname usage allowed... yes
checking if gethostbyname might be used... yes
checking if gethostbyname_r can be linked... yes
checking if gethostbyname_r is prototyped... yes
checking if gethostbyname_r takes 3 args.... no
checking if gethostbyname_r takes 5 args.... no
checking if gethostbyname_r takes 6 args.... yes
checking if gethostbyname_r is compilable... yes
checking if gethostbyname_r usage allowed... yes
checking if gethostbyname_r might be used... yes
checking if gethostname can be linked... yes
checking if gethostname is prototyped... yes
checking if gethostname is compilable... yes
checking for gethostname arg 2 data type... size_t
checking if gethostname usage allowed... yes
checking if gethostname might be used... yes
checking if getpeername can be linked... yes
checking if getpeername is prototyped... yes
checking if getpeername is compilable... yes
checking if getpeername usage allowed... yes
checking if getpeername might be used... yes
checking if getsockname can be linked... yes
checking if getsockname is prototyped... yes
checking if getsockname is compilable... yes
checking if getsockname usage allowed... yes
checking if getsockname might be used... yes
checking for net/if.h... (cached) yes
checking if if_nametoindex can be linked... yes
checking if if_nametoindex is prototyped... yes
checking if if_nametoindex is compilable... yes
checking if if_nametoindex usage allowed... yes
checking if if_nametoindex might be used... yes
checking for sys/types.h... (cached) yes
checking for sys/socket.h... (cached) yes
checking for netinet/in.h... (cached) yes
checking for ifaddrs.h... yes
checking if getifaddrs can be linked... yes
checking if getifaddrs is prototyped... yes
checking if getifaddrs is compilable... yes
checking if getifaddrs seems to work... yes
checking if getifaddrs usage allowed... yes
checking if getifaddrs might be used... yes
checking for sys/types.h... (cached) yes
checking for sys/time.h... (cached) yes
checking if gmtime_r can be linked... yes
checking if gmtime_r is prototyped... yes
checking if gmtime_r is compilable... yes
checking if gmtime_r seems to work... yes
checking if gmtime_r usage allowed... yes
checking if gmtime_r might be used... yes
checking for sys/types.h... (cached) yes
checking for sys/socket.h... (cached) yes
checking for netinet/in.h... (cached) yes
checking for arpa/inet.h... (cached) yes
checking if inet_ntop can be linked... yes
checking if inet_ntop is prototyped... yes
checking if inet_ntop is compilable... yes
checking if inet_ntop seems to work... yes
checking if inet_ntop usage allowed... yes
checking if inet_ntop might be used... yes
checking if inet_pton can be linked... yes
checking if inet_pton is prototyped... yes
checking if inet_pton is compilable... yes
checking if inet_pton seems to work... yes
checking if inet_pton usage allowed... yes
checking if inet_pton might be used... yes
checking for sys/types.h... (cached) yes
checking for unistd.h... (cached) yes
checking for sys/socket.h... (cached) yes
checking for sys/ioctl.h... (cached) yes
checking for stropts.h... no
checking if ioctl can be linked... yes
checking if ioctl is prototyped... yes
checking if ioctl is compilable... yes
checking if ioctl usage allowed... yes
checking if ioctl might be used... yes
checking if ioctl FIONBIO is compilable... yes
checking if ioctl FIONBIO usage allowed... yes
checking if ioctl FIONBIO might be used... yes
checking if ioctl SIOCGIFADDR is compilable... yes
checking if ioctl SIOCGIFADDR usage allowed... yes
checking if ioctl SIOCGIFADDR might be used... yes
checking if ioctlsocket can be linked... no
checking if ioctlsocket might be used... no
checking if IoctlSocket can be linked... no
checking if IoctlSocket might be used... no
checking if memrchr can be linked... yes
checking if memrchr is prototyped... no
checking if memrchr might be used... no
checking for sys/types.h... (cached) yes
checking for poll.h... (cached) yes
checking for sys/poll.h... (cached) yes
checking if poll can be linked... yes
checking if poll is prototyped... yes
checking if poll is compilable... yes
checking if poll seems to work... yes
checking if poll usage allowed... yes
checking if poll might be used... yes
checking for sys/types.h... (cached) yes
checking if sigaction can be linked... yes
checking if sigaction is prototyped... yes
checking if sigaction is compilable... yes
checking if sigaction usage allowed... yes
checking if sigaction might be used... yes
checking if siginterrupt can be linked... yes
checking if siginterrupt is prototyped... yes
checking if siginterrupt is compilable... yes
checking if siginterrupt usage allowed... yes
checking if siginterrupt might be used... yes
checking if signal can be linked... yes
checking if signal is prototyped... yes
checking if signal is compilable... yes
checking if signal usage allowed... yes
checking if signal might be used... yes
checking for sys/types.h... (cached) yes
checking if sigsetjmp can be linked... no
checking if sigsetjmp seems a macro... yes
checking if sigsetjmp is compilable... yes
checking if sigsetjmp usage allowed... yes
checking if sigsetjmp might be used... yes
checking if socket can be linked... yes
checking if socket is prototyped... yes
checking if socket is compilable... yes
checking if socket usage allowed... yes
checking if socket might be used... yes
checking if socketpair can be linked... yes
checking if socketpair is prototyped... yes
checking if socketpair is compilable... yes
checking if socketpair usage allowed... yes
checking if socketpair might be used... yes
checking if strcasecmp can be linked... yes
checking if strcasecmp is prototyped... yes
checking if strcasecmp is compilable... yes
checking if strcasecmp usage allowed... yes
checking if strcasecmp might be used... yes
checking if strcmpi can be linked... no
checking if strcmpi might be used... no
checking if strdup can be linked... yes
checking if strdup is prototyped... yes
checking if strdup is compilable... yes
checking if strdup usage allowed... yes
checking if strdup might be used... yes
checking if strerror_r can be linked... yes
checking if strerror_r is prototyped... yes
checking if strerror_r is compilable... yes
checking if strerror_r is glibc like... no
checking if strerror_r is POSIX like... yes
checking if strerror_r seems to work... yes
checking if strerror_r usage allowed... yes
checking if strerror_r might be used... yes
checking if stricmp can be linked... no
checking if stricmp might be used... no
checking if strtok_r can be linked... yes
checking if strtok_r is prototyped... yes
checking if strtok_r is compilable... yes
checking if strtok_r usage allowed... yes
checking if strtok_r might be used... yes
checking if strtoll can be linked... yes
checking if strtoll is prototyped... yes
checking if strtoll is compilable... yes
checking if strtoll usage allowed... yes
checking if strtoll might be used... yes
checking for ccache gcc options needed to detect all undeclared functions... none needed
checking whether getpwuid_r is declared... yes
checking for _fseeki64... no
checking deeper for _fseeki64... but still no
checking for arc4random... no
checking deeper for arc4random... but still no
checking for fnmatch... yes
checking for fseeko... yes
checking for geteuid... yes
checking for getpass_r... no
checking deeper for getpass_r... but still no
checking for getppid... yes
checking for getpwuid... yes
checking for getpwuid_r... yes
checking for getrlimit... yes
checking for gettimeofday... yes
checking for if_nametoindex... yes
checking for mach_absolute_time... no
checking deeper for mach_absolute_time... but still no
checking for pipe... yes
checking for sched_yield... yes
checking for sendmsg... yes
checking for setlocale... yes
checking for setmode... no
checking deeper for setmode... but still no
checking for setrlimit... yes
checking for snprintf... yes
checking for utime... yes
checking for utimes... yes
checking whether fseeko is declared... yes
checking how to set a socket into non-blocking mode... fcntl O_NONBLOCK
checking for perl... /usr/bin/perl
checking whether to enable the threaded resolver... yes
checking whether to use POSIX threads for threaded resolver... auto
checking for pthread.h... yes
checking for pthread_create... yes
checking for dirent.h... yes
checking for opendir... yes
checking convert -I options to -isystem... yes
checking whether to enable verbose strings... yes
checking whether to enable SSPI support (Windows native builds only)... no
checking whether to enable basic authentication method... yes
checking whether to enable bearer authentication method... yes
checking whether to enable digest authentication method... yes
checking whether to enable kerberos authentication method... yes
checking whether to enable negotiate authentication method... yes
checking whether to enable aws sig methods... yes
checking whether to support NTLM... yes
checking whether to enable TLS-SRP authentication... yes
checking whether to enable Unix domain sockets... auto
checking for struct sockaddr_un.sun_path... yes
checking whether to support cookies... yes
checking whether to support socketpair... yes
checking whether to support HTTP authentication... yes
checking whether to support DoH... yes
checking whether to support the MIME API... yes
checking whether to support binding connections locally... yes
checking whether to support the form API... yes
checking whether to support date parsing... yes
checking whether to support netrc parsing... yes
checking whether to support progress-meter... yes
checking whether to support DNS shuffling... yes
checking whether to support curl_easy_option*... yes
checking whether to support alt-svc... yes
checking whether to support headers-api... yes
configure: disables HSTS due to lack of SSL
checking whether to support WebSockets... no
checking whether hiding of library internal symbols will actually happen... yes
checking if this build supports HTTPS-proxy... no
checking that generated files are newer than configure... done
configure: creating ./config.status
config.status: creating Makefile
config.status: creating docs/Makefile
config.status: creating docs/examples/Makefile
config.status: creating docs/libcurl/Makefile
config.status: creating docs/libcurl/opts/Makefile
config.status: creating docs/cmdline-opts/Makefile
config.status: creating include/Makefile
config.status: creating include/curl/Makefile
config.status: creating src/Makefile
config.status: creating lib/Makefile
config.status: creating scripts/Makefile
config.status: creating lib/libcurl.vers
config.status: creating tests/Makefile
config.status: creating tests/config
config.status: creating tests/certs/Makefile
config.status: creating tests/certs/scripts/Makefile
config.status: creating tests/data/Makefile
config.status: creating tests/server/Makefile
config.status: creating tests/libtest/Makefile
config.status: creating tests/unit/Makefile
config.status: creating tests/http/config.ini
config.status: creating tests/http/Makefile
config.status: creating tests/http/clients/Makefile
config.status: creating packages/Makefile
config.status: creating packages/vms/Makefile
config.status: creating curl-config
config.status: creating libcurl.pc
config.status: creating lib/curl_config.h
config.status: executing depfiles commands
config.status: executing libtool commands
configure: Configured to build curl/libcurl:

  Host setup:       x86_64-pc-linux-gnu
  Install prefix:   /usr/local
  Compiler:         ccache gcc
   CFLAGS:          -Werror-implicit-function-declaration -O2 -Wno-system-headers
   CPPFLAGS:        
   LDFLAGS:         
   LIBS:            -lpsl -lz

  curl version:     8.8.0-DEV
  SSL:              no      (--with-{openssl,gnutls,mbedtls,wolfssl,schannel,secure-transport,amissl,bearssl,rustls} )
  SSH:              no      (--with-{libssh,libssh2})
  zlib:             enabled
  brotli:           no      (--with-brotli)
  zstd:             no      (--with-zstd)
  GSS-API:          no      (--with-gssapi)
  GSASL:            no      (libgsasl not found)
  TLS-SRP:          no      (--enable-tls-srp)
  resolver:         POSIX threaded
  IPv6:             enabled
  Unix sockets:     enabled
  IDN:              no      (--with-{libidn2,winidn})
  Build docs:       no
  Build libcurl:    Shared=no, Static=yes
  Built-in manual:  no      (--enable-manual)
  --libcurl option: enabled (--disable-libcurl-option)
  Verbose errors:   enabled (--disable-verbose)
  Code coverage:    disabled
  SSPI:             no      (--enable-sspi)
  ca cert bundle:   no
  ca cert path:     
  ca fallback:      
  LDAP:             no      (--enable-ldap / --with-ldap-lib / --with-lber-lib)
  LDAPS:            no      (--enable-ldaps)
  RTSP:             enabled
  RTMP:             no      (--with-librtmp)
  PSL:              enabled
  Alt-svc:          enabled (--disable-alt-svc)
  Headers API:      enabled (--disable-headers-api)
  HSTS:             no      (--enable-hsts)
  HTTP1:            enabled (internal)
  HTTP2:            no      (--with-nghttp2)
  HTTP3:            no      (--with-ngtcp2 --with-nghttp3, --with-quiche, --with-openssl-quic, --with-msh3)
  ECH:              no      (--enable-ech)
  WebSockets:       no      (--enable-websockets)
  Protocols:        DICT FILE FTP GOPHER HTTP IMAP IPFS IPNS MQTT POP3 RTSP SMTP TELNET TFTP
  Features:         AsynchDNS IPv6 Largefile PSL UnixSockets alt-svc libz threadsafe

configure: WARNING: Cannot find libraries for LDAP support: LDAP disabled
configure: WARNING: libgsasl was not found
configure: WARNING: Cannot find libraries for IDN support: IDN disabled

$ make -j3
Making all in lib
make[1]: Entering directory '/root/asi_db/builds/curl/_worktrees/w0/lib'
make  all-am
make[2]: Entering directory '/root/asi_db/builds/curl/_worktrees/w0/lib'
  CC       libcurl_la-altsvc.lo
  CC       libcurl_la-amigaos.lo
  CC       libcurl_la-asyn-ares.lo
  CC       libcurl_la-asyn-thread.lo
  CC       libcurl_la-base64.lo
  CC       libcurl_la-bufq.lo
  CC       libcurl_la-bufref.lo
  CC       libcurl_la-c-hyper.lo
  CC       libcurl_la-cf-h1-proxy.lo
  CC       libcurl_la-cf-h2-proxy.lo
  CC       libcurl_la-cf-haproxy.lo
  CC       libcurl_la-cf-https-connect.lo
  CC       libcurl_la-cf-socket.lo
  CC       libcurl_la-cfilters.lo
  CC       libcurl_la-conncache.lo
  CC       libcurl_la-connect.lo
  CC       libcurl_la-content_encoding.lo
  CC       libcurl_la-cookie.lo
  CC       libcurl_la-curl_addrinfo.lo
  CC       libcurl_la-curl_des.lo
  CC       libcurl_la-curl_endian.lo
  CC       libcurl_la-curl_fnmatch.lo
  CC       libcurl_la-curl_get_line.lo
  CC       libcurl_la-curl_gethostname.lo
  CC       libcurl_la-curl_gssapi.lo
  CC       libcurl_la-curl_memrchr.lo
  CC       libcurl_la-curl_multibyte.lo
  CC       libcurl_la-curl_ntlm_core.lo
  CC       libcurl_la-curl_path.lo
  CC       libcurl_la-curl_range.lo
  CC       libcurl_la-curl_rtmp.lo
  CC       libcurl_la-curl_sasl.lo
  CC       libcurl_la-curl_sha512_256.lo
  CC       libcurl_la-curl_sspi.lo
  CC       libcurl_la-curl_threads.lo
  CC       libcurl_la-curl_trc.lo
  CC       libcurl_la-cw-out.lo
  CC       libcurl_la-dict.lo
  CC       libcurl_la-doh.lo
  CC       libcurl_la-dynbuf.lo
  CC       libcurl_la-dynhds.lo
  CC       libcurl_la-easy.lo
  CC       libcurl_la-easygetopt.lo
  CC       libcurl_la-easyoptions.lo
  CC       libcurl_la-escape.lo
  CC       libcurl_la-file.lo
  CC       libcurl_la-fileinfo.lo
  CC       libcurl_la-fopen.lo
  CC       libcurl_la-ftp.lo
  CC       libcurl_la-formdata.lo
  CC       libcurl_la-ftplistparser.lo
  CC       libcurl_la-getenv.lo
  CC       libcurl_la-getinfo.lo
  CC       libcurl_la-gopher.lo
  CC       libcurl_la-hash.lo
  CC       libcurl_la-headers.lo
  CC       libcurl_la-hmac.lo
  CC       libcurl_la-hostasyn.lo
  CC       libcurl_la-hostip.lo
  CC       libcurl_la-hostip4.lo
  CC       libcurl_la-hostip6.lo
  CC       libcurl_la-hostsyn.lo
  CC       libcurl_la-hsts.lo
  CC       libcurl_la-http.lo
  CC       libcurl_la-http1.lo
  CC       libcurl_la-http2.lo
  CC       libcurl_la-http_aws_sigv4.lo
  CC       libcurl_la-http_chunks.lo
  CC       libcurl_la-http_digest.lo
  CC       libcurl_la-http_negotiate.lo
  CC       libcurl_la-http_ntlm.lo
  CC       libcurl_la-http_proxy.lo
  CC       libcurl_la-idn.lo
  CC       libcurl_la-if2ip.lo
  CC       libcurl_la-imap.lo
  CC       libcurl_la-inet_ntop.lo
  CC       libcurl_la-inet_pton.lo
  CC       libcurl_la-krb5.lo
  CC       libcurl_la-ldap.lo
  CC       libcurl_la-llist.lo
  CC       libcurl_la-macos.lo
  CC       libcurl_la-md4.lo
  CC       libcurl_la-md5.lo
  CC       libcurl_la-memdebug.lo
  CC       libcurl_la-mime.lo
  CC       libcurl_la-mprintf.lo
  CC       libcurl_la-mqtt.lo
  CC       libcurl_la-multi.lo
  CC       libcurl_la-netrc.lo
  CC       libcurl_la-nonblock.lo
  CC       libcurl_la-noproxy.lo
  CC       libcurl_la-openldap.lo
  CC       libcurl_la-parsedate.lo
  CC       libcurl_la-pingpong.lo
  CC       libcurl_la-pop3.lo
  CC       libcurl_la-progress.lo
  CC       libcurl_la-psl.lo
  CC       libcurl_la-rand.lo
  CC       libcurl_la-rename.lo
  CC       libcurl_la-request.lo
  CC       libcurl_la-rtsp.lo
  CC       libcurl_la-select.lo
  CC       libcurl_la-sendf.lo
  CC       libcurl_la-setopt.lo
  CC       libcurl_la-sha256.lo
  CC       libcurl_la-share.lo
  CC       libcurl_la-slist.lo
  CC       libcurl_la-smb.lo
  CC       libcurl_la-smtp.lo
  CC       libcurl_la-socketpair.lo
  CC       libcurl_la-socks.lo
  CC       libcurl_la-socks_gssapi.lo
  CC       libcurl_la-socks_sspi.lo
  CC       libcurl_la-speedcheck.lo
  CC       libcurl_la-splay.lo
  CC       libcurl_la-strcase.lo
  CC       libcurl_la-strdup.lo
  CC       libcurl_la-strerror.lo
  CC       libcurl_la-strtok.lo
  CC       libcurl_la-strtoofft.lo
  CC       libcurl_la-system_win32.lo
  CC       libcurl_la-tftp.lo
  CC       libcurl_la-telnet.lo
  CC       libcurl_la-timediff.lo
  CC       libcurl_la-timeval.lo
  CC       libcurl_la-transfer.lo
  CC       libcurl_la-url.lo
  CC       libcurl_la-urlapi.lo
  CC       libcurl_la-version.lo
  CC       libcurl_la-version_win32.lo
  CC       libcurl_la-warnless.lo
  CC       libcurl_la-ws.lo
  CC       vauth/libcurl_la-cleartext.lo
  CC       vauth/libcurl_la-cram.lo
  CC       vauth/libcurl_la-digest.lo
  CC       vauth/libcurl_la-digest_sspi.lo
  CC       vauth/libcurl_la-gsasl.lo
  CC       vauth/libcurl_la-krb5_gssapi.lo
  CC       vauth/libcurl_la-krb5_sspi.lo
  CC       vauth/libcurl_la-ntlm.lo
  CC       vauth/libcurl_la-ntlm_sspi.lo
  CC       vauth/libcurl_la-oauth2.lo
  CC       vauth/libcurl_la-spnego_gssapi.lo
  CC       vauth/libcurl_la-spnego_sspi.lo
  CC       vauth/libcurl_la-vauth.lo
  CC       vtls/libcurl_la-bearssl.lo
  CC       vtls/libcurl_la-gtls.lo
  CC       vtls/libcurl_la-hostcheck.lo
  CC       vtls/libcurl_la-keylog.lo
  CC       vtls/libcurl_la-mbedtls.lo
  CC       vtls/libcurl_la-mbedtls_threadlock.lo
  CC       vtls/libcurl_la-openssl.lo
  CC       vtls/libcurl_la-rustls.lo
  CC       vtls/libcurl_la-schannel.lo
  CC       vtls/libcurl_la-schannel_verify.lo
  CC       vtls/libcurl_la-sectransp.lo
  CC       vtls/libcurl_la-vtls.lo
  CC       vtls/libcurl_la-wolfssl.lo
  CC       vtls/libcurl_la-x509asn1.lo
  CC       vquic/libcurl_la-curl_msh3.lo
  CC       vquic/libcurl_la-curl_ngtcp2.lo
  CC       vquic/libcurl_la-curl_osslq.lo
  CC       vquic/libcurl_la-curl_quiche.lo
  CC       vquic/libcurl_la-vquic.lo
  CC       vquic/libcurl_la-vquic-tls.lo
  CC       vssh/libcurl_la-libssh.lo
  CC       vssh/libcurl_la-libssh2.lo
  CC       vssh/libcurl_la-wolfssh.lo
  CCLD     libcurl.la
make[2]: Leaving directory '/root/asi_db/builds/curl/_worktrees/w0/lib'
make[1]: Leaving directory '/root/asi_db/builds/curl/_worktrees/w0/lib'
Making all in docs
make[1]: Entering directory '/root/asi_db/builds/curl/_worktrees/w0/docs'
Making all in .
make[2]: Entering directory '/root/asi_db/builds/curl/_worktrees/w0/docs'
make[2]: Nothing to be done for 'all-am'.
make[2]: Leaving directory '/root/asi_db/builds/curl/_worktrees/w0/docs'
Making all in cmdline-opts
make[2]: Entering directory '/root/asi_db/builds/curl/_worktrees/w0/docs/cmdline-opts'
make[2]: Nothing to be done for 'all'.
make[2]: Leaving directory '/root/asi_db/builds/curl/_worktrees/w0/docs/cmdline-opts'
Making all in libcurl
make[2]: Entering directory '/root/asi_db/builds/curl/_worktrees/w0/docs/libcurl'
Making all in opts
make[3]: Entering directory '/root/asi_db/builds/curl/_worktrees/w0/docs/libcurl/opts'
make[3]: Nothing to be done for 'all'.
make[3]: Leaving directory '/root/asi_db/builds/curl/_worktrees/w0/docs/libcurl/opts'
make[3]: Entering directory '/root/asi_db/builds/curl/_worktrees/w0/docs/libcurl'
make[3]: Nothing to be done for 'all-am'.
make[3]: Leaving directory '/root/asi_db/builds/curl/_worktrees/w0/docs/libcurl'
make[2]: Leaving directory '/root/asi_db/builds/curl/_worktrees/w0/docs/libcurl'
make[1]: Leaving directory '/root/asi_db/builds/curl/_worktrees/w0/docs'
Making all in src
make[1]: Entering directory '/root/asi_db/builds/curl/_worktrees/w0/src'
make[2]: Entering directory '/root/asi_db/builds/curl/_worktrees/w0/src'
  CC       slist_wc.o
  CC       tool_binmode.o
  CC       tool_bname.o
  CC       tool_cb_dbg.o
  CC       tool_cb_hdr.o
  CC       tool_cb_prg.o
  CC       tool_cb_rea.o
  CC       tool_cb_see.o
  CC       tool_cb_wrt.o
  CC       tool_cfgable.o
  CC       tool_dirhie.o
  CC       tool_doswin.o
  CC       tool_easysrc.o
  CC       tool_filetime.o
  CC       tool_findfile.o
  CC       tool_formparse.o
  CC       tool_getparam.o
  CC       tool_getpass.o
  CC       tool_help.o
echo '#include "tool_hugehelp.h"' >> tool_hugehelp.c
  CC       tool_helpers.o
  CC       tool_ipfs.o
  CC       tool_libinfo.o
  CC       tool_listhelp.o
  CC       tool_main.o
  CC       tool_msgs.o
  CC       tool_operate.o
  CC       tool_operhlp.o
  CC       tool_paramhlp.o
  CC       tool_parsecfg.o
  CC       tool_progress.o
  CC       tool_setopt.o
  CC       tool_sleep.o
  CC       tool_stderr.o
  CC       tool_strdup.o
  CC       tool_urlglob.o
  CC       tool_util.o
  CC       tool_vms.o
  CC       tool_writeout_json.o
  CC       tool_writeout.o
  CC       tool_xattr.o
  CC       var.o
  CC       tool_hugehelp.o
  CC       ../lib/base64.o
  CC       ../lib/curl_multibyte.o
  CC       ../lib/dynbuf.o
  CC       ../lib/nonblock.o
  CC       ../lib/strtoofft.o
  CC       ../lib/timediff.o
  CC       ../lib/version_win32.o
  CC       ../lib/warnless.o
  CCLD     curl
make[2]: Leaving directory '/root/asi_db/builds/curl/_worktrees/w0/src'
make[1]: Leaving directory '/root/asi_db/builds/curl/_worktrees/w0/src'
Making all in scripts
make[1]: Entering directory '/root/asi_db/builds/curl/_worktrees/w0/scripts'
make[1]: Nothing to be done for 'all'.
make[1]: Leaving directory '/root/asi_db/builds/curl/_worktrees/w0/scripts'
make[1]: Entering directory '/root/asi_db/builds/curl/_worktrees/w0'
make[1]: Nothing to be done for 'all-am'.
make[1]: Leaving directory '/root/asi_db/builds/curl/_worktrees/w0'

Commit Diff GitHub →

.github/scripts/spellcheck.words+23 -0
@@ -2,6 +2,7 @@
#
# SPDX-License-Identifier: curl
#
+AAAA
ABI
accessor
ACK
@@ -10,6 +11,7 @@ AIA
AIX
al
Alessandro
+aliasMode
allocator
alnum
ALPN
@@ -109,6 +111,7 @@ CLA
CLAs
cleartext
CLI
+ClientHello
clientp
cliget
closesocket
@@ -116,6 +119,8 @@ CMake
cmake
CMake's
cmake's
+CNAME
+CNAMEs
CMakeLists
CNA
CodeQL
@@ -146,6 +151,7 @@ cURL
CURLcode
curldown
CURLE
+CURLECH
CURLH
curlimages
CURLINFO
@@ -164,6 +170,7 @@ dbg
Debian
DEBUGBUILD
decrypt
+decrypting
deepcode
DELE
DER
@@ -190,6 +197,7 @@ DNS
dns
dnsop
DoH
+DoT
doxygen
drftpd
dsa
@@ -201,6 +209,9 @@ EBCDIC
ECC
ECDHE
ECH
+ecl
+ECHConfig
+ECHConfigList
ECONNREFUSED
eCOS
EFnet
@@ -284,6 +295,8 @@ GOST
GPG
GPL
GPLed
+GREASE
+GREASEing
Greear
groff
gsasl
@@ -307,6 +320,7 @@ Hards
Haxx
haxx
Heimdal
+HelloRetryRequest
HELO
HH
HMAC
@@ -316,6 +330,7 @@ homebrew
hostname
hostnames
Housley
+HRR
Hruska
HSTS
hsts
@@ -460,6 +475,7 @@ Marek
Mavrogiannopoulos
Mbed
mbedTLS
+md
Meglio
memdebug
MesaLink
@@ -470,6 +486,7 @@ Michal
Micrium
MicroBlaze
MicroOS
+middlebox
mingw
MinGW
MINIX
@@ -590,6 +607,7 @@ pkcs
PKGBUILD
PKI
pluggable
+pn
PolarSSL
Polhem
pollset
@@ -625,6 +643,7 @@ py
pycurl
pytest
Pytest
+qname
QNX
QoS
Qubes
@@ -668,6 +687,9 @@ Roadmap
Rockbox
roffit
RPG
+RR
+RRs
+RRtype
RSA
RTMP
rtmp
@@ -784,6 +806,7 @@ SunSSH
superset
svc
svcb
+SVCB
Svyatoslav
Swisscom
sws
.gitignore+1 -0
@@ -65,3 +65,4 @@ curl_fuzzer_seed_corpus.zip
libstandaloneengine.a
tests/string
tests/config
+tests/ech-log/
CMakeLists.txt+28 -0
@@ -37,6 +37,7 @@
# HAVE_GNUTLS_SRP: `gnutls_srp_verifier` present in GnuTLS
# HAVE_SSL_CTX_SET_QUIC_METHOD: `SSL_CTX_set_quic_method` present in OpenSSL/wolfSSL
# HAVE_QUICHE_CONN_SET_QLOG_FD: `quiche_conn_set_qlog_fd` present in QUICHE
+# HAVE_ECH: ECH API checks for OpenSSL, boringssl or wolfSSL
#
# For each of the above variables, if the variable is DEFINED (either
# to ON or OFF), the symbol detection will be skipped. If the
@@ -654,6 +655,31 @@ if(USE_OPENSSL OR USE_WOLFSSL)
endif()
endif()
+option(USE_HTTPSRR "Enable HTTPS RR support for ECH (experimental)" OFF)
+option(USE_ECH "Enable ECH support" OFF)
+if(USE_ECH)
+ if(USE_OPENSSL OR USE_WOLFSSL)
+ # Be sure that the OpenSSL/wolfSSL library actually supports ECH.
+ if(NOT DEFINED HAVE_ECH)
+ if(USE_OPENSSL AND HAVE_BORINGSSL)
+ openssl_check_symbol_exists(SSL_set1_ech_config_list "openssl/ssl.h" HAVE_ECH)
+ elseif(USE_OPENSSL)
+ openssl_check_symbol_exists(SSL_ech_set1_echconfig "openssl/ech.h" HAVE_ECH)
+ elseif(USE_WOLFSSL)
+ openssl_check_symbol_exists(wolfSSL_CTX_GenerateEchConfig "wolfssl/options.h;wolfssl/ssl.h" HAVE_ECH)
+ endif()
+ endif()
+ if(NOT HAVE_ECH)
+ message(FATAL_ERROR "ECH support missing in OpenSSL/BoringSSL/wolfSSL")
+ else()
+ message("ECH enabled.")
+ endif()
+ else()
+ message(FATAL_ERROR "ECH requires ECH-enablded OpenSSL, BoringSSL or wolfSSL")
+ endif()
+endif()
+
+
option(USE_NGHTTP2 "Use nghttp2 library" OFF)
if(USE_NGHTTP2)
find_package(NGHTTP2 REQUIRED)
@@ -1590,6 +1616,8 @@ if(NOT CURL_DISABLE_INSTALL)
_add_if("IPFS" NOT CURL_DISABLE_HTTP)
_add_if("IPNS" NOT CURL_DISABLE_HTTP)
_add_if("HTTPS" NOT CURL_DISABLE_HTTP AND SSL_ENABLED)
+ _add_if("ECH" HAVE_ECH)
+ _add_if("HTTPSRR" HAVE_ECH)
_add_if("FTP" NOT CURL_DISABLE_FTP)
_add_if("FTPS" NOT CURL_DISABLE_FTP AND SSL_ENABLED)
_add_if("FILE" NOT CURL_DISABLE_FILE)
configure.ac+27 -10
@@ -51,6 +51,7 @@ CURL_CHECK_OPTION_CURLDEBUG
CURL_CHECK_OPTION_SYMBOL_HIDING
CURL_CHECK_OPTION_ARES
CURL_CHECK_OPTION_RT
+CURL_CHECK_OPTION_HTTPSRR
CURL_CHECK_OPTION_ECH
XC_CHECK_PATH_SEPARATOR
@@ -4538,6 +4539,16 @@ if test "x$hsts" != "xyes"; then
AC_DEFINE(CURL_DISABLE_HSTS, 1, [disable alt-svc])
fi
+
+dnl *************************************************************
+dnl check whether HTTPSRR support if desired
+dnl
+if test "x$want_httpsrr" != "xno"; then
+ AC_MSG_RESULT([HTTPSRR support is available])
+ AC_DEFINE(USE_HTTPSRR, 1, [enable HTTPS RR support])
+ experimental="$experimental HTTPSRR"
+fi
+
dnl *************************************************************
dnl check whether ECH support, if desired, is actually available
dnl
@@ -4548,18 +4559,28 @@ if test "x$want_ech" != "xno"; then
ECH_ENABLED=0
ECH_SUPPORT=''
- dnl OpenSSL with a chosen ECH function should be enough
- dnl so more exhaustive checking seems unnecessary for now
+ dnl check for OpenSSL
if test "x$OPENSSL_ENABLED" = "x1"; then
- AC_CHECK_FUNCS(SSL_get_ech_status,
- ECH_SUPPORT="ECH support available (OpenSSL with SSL_get_ech_status)"
+ AC_CHECK_FUNCS(SSL_ech_set1_echconfig,
+ ECH_SUPPORT="ECH support available via OpenSSL with SSL_ech_set1_echconfig"
+ ECH_ENABLED=1)
+ fi
+ dnl check for boringssl equivalent
+ if test "x$OPENSSL_ENABLED" = "x1"; then
+ AC_CHECK_FUNCS(SSL_set1_ech_config_list,
+ ECH_SUPPORT="ECH support available via boringssl with SSL_set1_ech_config_list"
+ ECH_ENABLED=1)
+ fi
+ if test "x$WOLFSSL_ENABLED" = "x1"; then
+ AC_CHECK_FUNCS(wolfSSL_CTX_GenerateEchConfig,
+ ECH_SUPPORT="ECH support available via WolfSSL with wolfSSL_CTX_GenerateEchConfig"
ECH_ENABLED=1)
-
- dnl add 'elif' chain here for additional implementations
fi
dnl now deal with whatever we found
if test "x$ECH_ENABLED" = "x1"; then
+ dnl force pre-requisites for ECH
+ AC_DEFINE(USE_HTTPSRR, 1, [force HTTPS RR support for ECH])
AC_DEFINE(USE_ECH, 1, [if ECH support is available])
AC_MSG_RESULT($ECH_SUPPORT)
experimental="$experimental ECH"
@@ -4777,10 +4798,6 @@ else
AC_MSG_RESULT([no])
fi
-if test "x$ECH_ENABLED" = "x1"; then
- SUPPORT_FEATURES="$SUPPORT_FEATURES ECH"
-fi
-
if test ${ac_cv_sizeof_curl_off_t} -gt 4; then
if test ${ac_cv_sizeof_off_t} -gt 4 -o \
"$curl_win32_file_api" = "win32_large_files"; then
docs/ECH.md+479 -0
@@ -0,0 +1,479 @@
+<!--
+Copyright (C) Daniel Stenberg, <[email protected]>, et al.
+
+SPDX-License-Identifier: curl
+-->
+
+# Building curl with HTTPS-RR and ECH support
+
+We've added support for ECH to in this curl build. That can use HTTPS RRs
+published in the DNS, if curl is using DoH, or else can accept the relevant
+ECHConfigList values from the command line. That works with OpenSSL,
+WolfSSL or boringssl as the TLS provider, depending on how you build curl.
+
+This feature is EXPERIMENTAL. DO NOT USE IN PRODUCTION.
+
+This should however provide enough of a proof-of-concept to prompt an informed
+discussion about a good path forward for ECH support in curl, when using
+OpenSSL, or other TLS libraries, as those add ECH support.
+
+## OpenSSL Build
+
+To build our ECH-enabled OpenSSL fork:
+
+```bash
+ cd $HOME/code
+ git clone https://github.com/defo-project/openssl
+ cd openssl
+ ./config --libdir=lib --prefix=$HOME/code/openssl-local-inst
+ ...stuff...
+ make -j8
+ ...stuff (maybe go for coffee)...
+ make install_sw
+ ...a little bit of stuff...
+```
+
+To build curl ECH-enabled, making use of the above:
+
+```bash
+ cd $HOME/code
+ git clone https://github.com/curl/curl
+ cd curl
+ autoreconf -fi
+ LDFLAGS="-Wl,-rpath,$HOME/code/openss-local-inst/lib/" ./configure --with-ssl=$HOME/code/openssl-local-inst --enable-ech --enable-httpsrr
+ ...lots of output...
+ WARNING: ech ECH HTTPSRR enabled but marked EXPERIMENTAL...
+ make
+ ...lots more output...
+```
+
+If you do not get that WARNING at the end of the ``configure`` command, then ECH
+is not enabled, so go back some steps and re-do whatever needs re-doing:-) If you
+want to debug curl then you should add ``--enable-debug`` to the ``configure``
+command.
+
+With the above build, I still need to set ``LD_LIBRARY_PATH`` to run the
+version of curl built against OpenSSL in my development environment (Ubuntu
+23.10).
+
+## Using ECH and DoH
+
+Curl supports using DoH for A/AAAA lookups so it was relatively easy to add
+retrieval of HTTPS RRs in that situation. To use ECH and DoH together:
+
+```bash
+ cd $HOME/code/curl
+ LD_LIBRARY_PATH=$HOME/code/openssl ./src/curl --ech true --doh-url https://one.one.one.one/dns-query https://defo.ie/ech-check.php
+ ...
+ SSL_ECH_STATUS: success <img src="greentick-small.png" alt="good" /> <br/>
+ ...
+```
+
+The output snippet above is within the HTML for the webpage, when things work.
+
+The above works for these test sites:
+
+```bash
+ https://defo.ie/ech-check.php
+ https://draft-13.esni.defo.ie:8413/stats
+ https://draft-13.esni.defo.ie:8414/stats
+ https://crypto.cloudflare.com/cdn-cgi/trace
+ https://tls-ech.dev
+```
+
+The list above has 4 different server technologies, implemented by 3 different
+parties, and includes a case (the port 8414 server) where HelloRetryRequest
+(HRR) is forced.
+
+We currently support the following new curl command line arguments/options:
+
+- ``--ech <config>`` - the ``config`` value can be one of:
+ - ``false`` says to not attempt ECH
+ - ``true`` says to attempt ECH, if possible
+ - ``grease`` if attempting ECH is not possible, then send a GREASE ECH extension
+ - ``hard`` hard-fail the connection if ECH cannot be attempted
+ - ``ecl:<b64value>`` a base64 encoded ECHConfigList, rather than one accessed from the DNS
+ - ``pn:<name>`` over-ride the ``public_name`` from an ECHConfigList
+
+Note that in the above "attempt ECH" means the client emitting a TLS
+ClientHello with a "real" ECH extension, but that does not mean that the
+relevant server can succeed in decrypting, as things can fail for other
+reasons.
+
+## Supplying an ECHConfigList on the command line
+
+To supply the ECHConfigList on the command line, you might need a bit of
+cut-and-paste, e.g.:
+
+```bash
+ dig +short https defo.ie
+ 1 . ipv4hint=213.108.108.101 ech=AED+DQA8PAAgACD8WhlS7VwEt5bf3lekhHvXrQBGDrZh03n/LsNtAodbUAAEAAEAAQANY292ZXIuZGVmby5pZQAA ipv6hint=2a00:c6c0:0:116:5::10
+```
+
+Then paste the base64 encoded ECHConfigList onto the curl command line:
+
+```bash
+ LD_LIBRARY_PATH=$HOME/code/openssl ./src/curl --ech ecl:AED+DQA8PAAgACD8WhlS7VwEt5bf3lekhHvXrQBGDrZh03n/LsNtAodbUAAEAAEAAQANY292ZXIuZGVmby5pZQAA https://defo.ie/ech-check.php
+ ...
+ SSL_ECH_STATUS: success <img src="greentick-small.png" alt="good" /> <br/>
+ ...
+```
+
+The output snippet above is within the HTML for the webpage.
+
+If you paste in the wrong ECHConfigList (it changes hourly for ``defo.ie``) you
+should get an error like this:
+
+```bash
+ LD_LIBRARY_PATH=$HOME/code/openssl ./src/curl -vvv --ech ecl:AED+DQA8yAAgACDRMQo+qYNsNRNj+vfuQfFIkrrUFmM4vogucxKj/4nzYgAEAAEAAQANY292ZXIuZGVmby5pZQAA https://defo.ie/ech-check.php
+ ...
+ * OpenSSL/3.3.0: error:0A00054B:SSL routines::ech required
+ ...
+```
+
+There is a reason to want this command line option - for use before publishing
+an ECHConfigList in the DNS as per the Internet-draft [A well-known URI for
+publishing ECHConfigList values](https://datatracker.ietf.org/doc/draft-ietf-tls-wkech/).
+
+If you do use a wrong ECHConfigList value, then the server might return a
+good value, via the ``retry_configs`` mechanism. You can see that value in
+the verbose output, e.g.:
+
+```bash
+ LD_LIBRARY_PATH=$HOME/code/openssl ./src/curl -vvv --ech ecl:AED+DQA8yAAgACDRMQo+qYNsNRNj+vfuQfFIkrrUFmM4vogucxKj/4nzYgAEAAEAAQANY292ZXIuZGVmby5pZQAA https://defo.ie/ech-check.php
+ ...
+* ECH: retry_configs AQD+DQA8DAAgACBvYqJy+Hgk33wh/ZLBzKSPgwxeop7gvojQzfASq7zeZQAEAAEAAQANY292ZXIuZGVmby5pZQAA/g0APEMAIAAgXkT5r4cYs8z19q5rdittyIX8gfQ3ENW4wj1fVoiJZBoABAABAAEADWNvdmVyLmRlZm8uaWUAAP4NADw2ACAAINXSE9EdXzEQIJZA7vpwCIQsWqsFohZARXChgPsnfI1kAAQAAQABAA1jb3Zlci5kZWZvLmllAAD+DQA8cQAgACASeiD5F+UoSnVoHvA2l1EifUVMFtbVZ76xwDqmMPraHQAEAAEAAQANY292ZXIuZGVmby5pZQAA
+* ECH: retry_configs for defo.ie from cover.defo.ie, 319
+ ...
+```
+
+At that point, you could copy the base64 encoded value above and try again.
+For now, this only works for the OpenSSL and boringssl builds.
+
+## Default settings
+
+Curl has various ways to configure default settings, e.g. in ``$HOME/.curlrc``,
+so one can set the DoH URL and enable ECH that way:
+
+```bash
+ cat ~/.curlrc
+ doh-url=https://one.one.one.one/dns-query
+ silent
+ ech=true
+```
+
+Note that when you use the system's curl command (rather than our ECH-enabled
+build), it is liable to warn that ``ech`` is an unknown option. If that is an
+issue (e.g. if some script re-directs stdout and stderr somewhere) then adding
+the ``silent`` line above seems to be a good enough fix. (Though of
+course, yet another script could depend on non-silent behavior, so you may have
+to figure out what you prefer yourself.) That seems to have changed with the
+latest build, previously ``silent=TRUE`` was what I used in ``~/.curlrc`` but
+now that seems to cause a problem, so that the following line(s) are ignored.
+
+If you want to always use our OpenSSL build you can set ``LD_LIBRARY_PATH``
+in the environment:
+
+```bash
+ export LD_LIBRARY_PATH=$HOME/code/openssl
+```
+
+When you do the above, there can be a mismatch between OpenSSL versions
+for applications that check that. A ``git push`` for example fails so you
+should unset ``LD_LIBRARY_PATH`` before doing that or use a different shell.
+
+```bash
+ git push
+ OpenSSL version mismatch. Built against 30000080, you have 30200000
+ ...
+```
+
+With all that setup as above the command line gets simpler:
+
+```bash
+ ./src/curl https://defo.ie/ech-check.php
+ ...
+ SSL_ECH_STATUS: success <img src="greentick-small.png" alt="good" /> <br/>
+ ...
+```
+
+The ``--ech true`` option is opportunistic, so tries to do ECH but does not fail if
+the client for example cannot find any ECHConfig values. The ``--ech hard``
+option hard-fails if there is no ECHConfig found in DNS, so for now, that is not
+a good option to set as a default. Once ECH has really been attempted by
+the client, if decryption on the server side fails, then curl fails.
+
+## Code changes for ECH support when using DoH
+
+Code changes are ``#ifdef`` protected via ``USE_ECH`` or ``USE_HTTPSRR``:
+
+- ``USE_HTTPSRR`` is used for HTTPS RR retrieval code that could be generically
+ used should non-ECH uses for HTTPS RRs be identified, e.g. use of ALPN values
+or IP address hints.
+
+- ``USE_ECH`` protects ECH specific code.
+
+There are various obvious code blocks for handling the new command line
+arguments which aren't described here, but should be fairly clear.
+
+As shown in the ``configure`` usage above, there are ``configure.ac`` changes
+that allow separately dis/enabling ``USE_HTTPSRR`` and ``USE_ECH``. If ``USE_ECH``
+is enabled, then ``USE_HTTPSRR`` is forced. In both cases ``USE_DOH``
+is required. (There may be some configuration conflicts available for the
+determined:-)
+
+The main functional change, as you would expect, is in ``lib/vtls/openssl.c``
+where an ECHConfig, if available from command line or DNS cache, is fed into
+the OpenSSL library via the new APIs implemented in our OpenSSL fork for that
+purpose. This code also implements the opportunistic (``--ech true``) or hard-fail
+(``--ech hard``) logic.
+
+Other than that, the main additions are in ``lib/doh.c``
+where we re-use ``dohprobe()`` to retrieve an HTTPS RR value for the target
+domain. If such a value is found, that is stored using a new ``store_https()``
+function in a new field in the ``dohentry`` structure.
+
+The qname for the DoH query is modified if the port number is not 443, as
+defined in the SVCB specification.
+
+When the DoH process has worked, ``Curl_doh_is_resolved()`` now also returns
+the relevant HTTPS RR value data in the ``Curl_dns_entry`` structure.
+That is later accessed when the TLS session is being established, if ECH is
+enabled (from ``lib/vtls/openssl.c`` as described above).
+
+## Limitations
+
+Things that need fixing, but that can probably be ignored for the
+moment:
+
+- We could easily add code to make use of an ``alpn=`` value found in an HTTPS
+ RR, passing that on to OpenSSL for use as the "inner" ALPN value, but have
+yet to do that.
+
+Current limitations (more interesting than the above):
+
+- Only the first HTTPS RR value retrieved is actually processed as described
+ above, that could be extended in future, though picking the "right" HTTPS RR
+could be non-trivial if multiple RRs are published - matching IP address hints
+versus A/AAAA values might be a good basis for that. Last I checked though,
+browsers supporting ECH did not handle multiple HTTPS RRs well, though that
+needs re-checking as it has been a while.
+
+- It is unclear how one should handle any IP address hints found in an HTTPS RR.
+ It may be that a bit of consideration of how "multi-CDN" deployments might
+emerge would provide good answers there, but for now, it is not clear how best
+curl might handle those values when present in the DNS.
+
+- The SVCB/HTTPS RR specification supports a new "CNAME at apex" indirection
+ ("aliasMode") - the current code takes no account of that at all. One could
+envisage implementing the equivalent of following CNAMEs in such cases, but
+it is not clear if that'd be a good plan. (As of now, chrome browsers do not seem
+to have any support for that "aliasMode" and we've not checked Firefox for that
+recently.)
+
+- We have not investigated what related changes or additions might be needed
+ for applications using libcurl, as opposed to use of curl as a command line
+tool.
+
+- We have not yet implemented tests as part of the usual curl test harness as
+doing so would seem to require re-implementing an ECH-enabled server as part
+of the curl test harness. For now, we have a ``./tests/ech_test.sh`` script
+that attempts ECH with various test servers and with many combinations of the
+allowed command line options. While that is a useful test and has find issues,
+it is not comprehensive and we're not (as yet) sure what would be the right
+level of coverage. When running that script you should not have a
+``$HOME/.curlrc`` file that affects ECH or some of the negative tests could
+produce spurious failures.
+
+## Building with cmake
+
+To build with cmake, assuming our ECH-enabled OpenSSL is as before:
+
+```bash
+ cd $HOME/code
+ git clone https://github.com/curl/curl
+ cd curl
+ mkdir build
+ cd build
+ cmake -DOPENSSL_ROOT_DIR=$HOME/code/openssl -DUSE_ECH=1 -DUSE_HTTPSRR=1 ..
+ ...
+ make
+ ...
+ [100%] Built target curl
+```
+
+The binary produced by the cmake build does not need any ECH-specific
+``LD_LIBRARY_PATH`` setting.
+
+## boringssl build
+
+BoringSSL is also supported by curl and also supports ECH, so to build
+with that, instead of our ECH-enabled OpenSSL:
+
+```bash
+ cd $HOME/code
+ git clone https://boringssl.googlesource.com/boringssl
+ cd boringssl
+ cmake -DCMAKE_INSTALL_PREFIX:PATH=$HOME/code/boringssl/inst -DBUILD_SHARED_LIBS=1
+ make
+ ...
+ make install
+```
+
+Then:
+
+```bash
+ cd $HOME/code
+ git clone https://github.com/curl/curl
+ cd curl
+ autoreconf -fi
+ LDFLAGS="-Wl,-rpath,$HOME/code/boringssl/inst/lib" ./configure --with-ssl=$HOME/code/boringssl/inst --enable-ech --enable-httpsrr
+ ...lots of output...
+ WARNING: ech ECH HTTPSRR enabled but marked EXPERIMENTAL. Use with caution!
+ make
+```
+
+The boringssl APIs are fairly similar to those in our ECH-enabled OpenSSL
+fork, so code changes are also in ``lib/vtls/openssl.c``, protected
+via ``#ifdef OPENSSL_IS_BORINGSSL`` and are mostly obvious API variations.
+
+The boringssl APIs however do not support the ``--ech pn:`` command line
+variant as of now.
+
+## WolfSSL build
+
+WolfSSL also supports ECH and can be used by curl, so here's how:
+
+```bash
+ cd $HOME/code
+ git clone https://github.com/wolfSSL/wolfssl
+ cd wolfssl
+ ./autogen.sh
+ ./configure --prefix=$HOME/code/wolfssl/inst --enable-ech --enable-debug --enable-opensslextra
+ make
+ make install
+```
+
+The install prefix (``inst``) in the above causes WolfSSL to be installed there
+and we seem to need that for the curl configure command to work out. The
+``--enable-opensslextra`` turns out (after much faffing about;-) to be
+important or else we get build problems with curl below.
+
+```bash
+ cd $HOME/code
+ git clone https://github.com/curl/curl
+ cd curl
+ autoreconf -fi
+ ./configure --with-wolfssl=$HOME/code/wolfssl/inst --enable-ech --enable-httpsrr
+ make
+```
+
+There are some known issues with the ECH implementation in WolfSSL:
+
+- The main issue is that the client currently handles HelloRetryRequest
+ incorrectly. [HRR issue](https://github.com/wolfSSL/wolfssl/issues/6802).)
+ The HRR issue means that the client does not work for
+ [this ECH test web site](https://tls-ech.dev) and any other similarly configured
+ sites.
+- There is also an issue related to so-called middlebox compatibility mode.
+ [middlebox compatibility issue](https://github.com/wolfSSL/wolfssl/issues/6774)
+
+### Code changes to support WolfSSL
+
+There are what seem like oddball differences:
+
+- The DoH URL in``$HOME/.curlrc`` can use "1.1.1.1" for OpenSSL but has to be
+ "one.one.one.one" for WolfSSL. The latter works for both, so OK, we'll change
+ to that.
+- There seems to be some difference in CA databases too - the WolfSSL version
+ does not like ``defo.ie``, whereas the system and OpenSSL ones do. We can ignore
+ that for our purposes via ``--insecure``/``-k`` but would need to fix for a
+ real setup. (Browsers do like those certificates though.)
+
+Then there are some functional code changes:
+
+- tweak to ``configure.ac`` to check if WolfSSL has ECH or not
+- added code to ``lib/vtls/wolfssl.c`` mirroring what's done in the
+ OpenSSL equivalent above.
+- WolfSSL does not support ``--ech false`` or the ``--ech pn:`` command line
+ argument.
+
+The lack of support for ``--ech false`` is because wolfSSL has decided to
+always at least GREASE if built to support ECH. In other words, GREASE is
+a compile time choice for wolfSSL, but a runtime choice for OpenSSL or
+boringssl. (Both are reasonable.)
+
+## Additional notes
+
+### Supporting ECH without DoH
+
+All of the above only applies if DoH is being used. There should be a use-case
+for ECH when DoH is not used by curl - if a system stub resolver supports DoT
+or DoH, then, considering only ECH and the network threat model, it would make
+sense for curl to support ECH without curl itself using DoH. The author for
+example uses a combination of stubby+unbound as the system resolver listening
+on localhost:53, so would fit this use-case. That said, it is unclear if
+this is a niche that is worth trying to address. (The author is just as happy to
+let curl use DoH to talk to the same public recursive that stubby might use:-)
+
+Assuming for the moment this is a use-case we'd like to support, then
+if DoH is not being used by curl, it is not clear at this time how to provide
+support for ECH. One option would seem to be to extend the ``c-ares`` library
+to support HTTPS RRs, but in that case it is not now clear whether such changes
+would be attractive to the ``c-ares`` maintainers, nor whether the "tag=value"
+extensibility inherent in the HTTPS/SVCB specification is a good match for the
+``c-ares`` approach of defining structures specific to decoded answers for each
+supported RRtype. We're also not sure how many downstream curl deployments
+actually make use of the ``c-ares`` library, which would affect the utility of
+such changes. Another option might be to consider using some other generic DNS
+library that does support HTTPS RRs, but it is unclear if such a library could
+or would be used by all or almost all curl builds and downstream releases of
+curl.
+
+Our current conclusion is that doing the above is likely best left until we
+have some experience with the "using DoH" approach, so we're going to punt on
+this for now.
+
+### Debugging
+
+Just a note to self as remembering this is a nuisance:
+
+```bash
+LD_LIBRARY_PATH=$HOME/code/openssl:./lib/.libs gdb ./src/.libs/curl
+```
+
+### Localhost testing
+
+It can be useful to be able to run against a localhost OpenSSL ``s_server``
+for testing. We have published instructions for such
+[localhost tests](https://github.com/defo-project/ech-dev-utils/blob/main/howtos/localhost-tests.md)
+in another repository. Once you have that set up, you can start a server
+and then run curl against that:
+
+```bash
+ cd $HOME/code/ech-dev-utils
+ ./scripts/echsvr.sh -d
+ ...
+```
+
+The ``echsvr.sh`` script supports many ECH-related options. Use ``echsvr.sh -h``
+for details.
+
+In another window:
+
+```bash
+ cd $HOME/code/curl/
+ ./src/curl -vvv --insecure --connect-to foo.example.com:8443:localhost:8443 --ech ecl:AD7+DQA6uwAgACBix2B78sX+EQhEbxMspDOc8Z3xVS5aQpYP0Cxpc2AWPAAEAAEAAQALZXhhbXBsZS5jb20AAA==
+```
+
+### Automated use of ``retry_configs`` not supported so far...
+
+As of now we have not added support for using ``retry_config`` handling in the
+application - for a command line tool, one can just use ``dig`` (or ``kdig``)
+to get the HTTPS RR and pass the ECHConfigList from that on the command line,
+if needed, or one can access the value from command line output in verbose more
+and then re-use that in another invocation.
+
+Both our OpenSSL fork and boringssl have APIs for both controlling GREASE and
+accessing and logging ``retry_configs``, it seems WolfSSL has neither.
+
docs/EXPERIMENTAL.md+1 -0
@@ -28,3 +28,4 @@ Experimental support in curl means:
- HTTP/3 support (using the quiche or msh3 backends)
- The rustls backend
- WebSocket
+ - Use of the HTTPS resource record and Encrypted Client Hello (ECH) when using DoH
docs/cmdline-opts/Makefile.inc+1 -0
@@ -90,6 +90,7 @@ DPAGES = \
doh-insecure.md \
doh-url.md \
dump-header.md \
+ ech.md \
egd-file.md \
engine.md \
etag-compare.md \
docs/cmdline-opts/ech.md+54 -0
@@ -0,0 +1,54 @@
+---
+c: Copyright (C) Daniel Stenberg, <[email protected]>, et al.
+SPDX-License-Identifier: curl
+Long: ech
+Arg: <config>
+Help: Configure Encrypted Client Hello (ECH) for use with the TLS session
+Added: 8.8.0
+Category: tls ECH
+Protocols: HTTPS
+Multi: single
+See-also:
+ - doh-url
+Example:
+ - --ech true $URL
+---
+
+# `--ech`
+
+Specifies how to do ECH (Encrypted Client Hello).
+
+The values allowed for \<config\> can be:
+
+## "false"
+Do not attempt ECH
+
+## "grease"
+
+Send a GREASE ECH extension
+
+## "true"
+
+Attempt ECH if possible, but do not fail if ECH is not attempted.
+(The connection fails if ECH is attempted but fails.)
+
+## "hard"
+
+Attempt ECH and fail if that is not possible.
+ECH only works with TLS 1.3 and also requires using
+DoH or providing an ECHConfigList on the command line.
+
+## "ecl:<b64val>"
+
+A base64 encoded ECHConfigList that is used for ECH.
+
+## "pn:<name>"
+
+A name to use to over-ride the `public_name` field of an ECHConfigList
+(only available with OpenSSL TLS support)
+
+## Errors
+
+Most errors cause error
+*CURLE_ECH_REQUIRED* (101).
+
docs/libcurl/curl_easy_setopt.md+6 -0
@@ -1361,6 +1361,12 @@ int main(void)
}
~~~
+# ENCRYPTED CLIENT HELLO OPTIONS
+
+## CURLOPT_ECH
+
+Set the configuration for ECH. See CURLOPT_ECH(3)
+
# AVAILABILITY
Always
docs/libcurl/libcurl-errors.md+4 -0
@@ -487,6 +487,10 @@ An internal call to poll() or select() returned error that is not recoverable.
A value or data field grew larger than allowed.
+## CURLE_ECH_REQUIRED (101)"
+
+ECH was attempted but failed.
+
# CURLMcode
This is the generic return code used by functions in the libcurl multi
docs/libcurl/opts/CURLOPT_ECH.md+83 -0
@@ -0,0 +1,83 @@
+---
+c: Copyright (C) Daniel Stenberg, <daniel.se>, et al.
+SPDX-License-Identifier: curl
+Title: CURLOPT_ECH
+Section: 3
+Source: libcurl
+See-also:
+ - (3)
+Protocol:
+ - TLS
+TLS-backend:
+ - OpenSSL
+ - wolfSSL
+---
+
+# NAME
+
+CURLOPT_ECH - configuration for Encrypted Client Hello
+
+# SYNOPSIS
+
+~~~c
+#include <curl/curl.h>
+
+CURLcode curl_easy_setopt(CURL *handle, CURLOPT_ECH, char *config);
+~~~
+
+# DESCRIPTION
+
+ECH is only compatible with TLSv1.3.
+
+This experimental feature requires a special build of OpenSSL, as ECH is not
+yet supported in OpenSSL releases. In contrast ECH is supported by the latest
+BoringSSL and wolfSSL releases. See [ECH.md](../../ECH.md) for details of how
+to build such an OpenSSL library.
+
+There is also a known issue with using wolfSSL which does not support ECH
+when the HelloRetryRequest mechanism is used.
+
+Pass a string that specifies configuration details for ECH.
+In all cases, if ECH is attempted, it may fail for various reasons.
+The keywords supported are:
+
+## false
+Turns off ECH.
+## grease
+Instructs client to emit a GREASE ECH extension.
+(The connection fails if ECH is attempted but fails.)
+## true
+Instructs client to attempt ECH, if possible, but to not fail if attempting ECH is not possible.
+## hard
+Instructs client to attempt ECH and fail if if attempting ECH is not possible.
+## ecl:\<base64-value\>
+If the string starts with "ecl:" then the remainder of the string should be a base64-encoded
+ECHConfigList that is used for ECH rather than attempting to download such a value from
+the DNS.
+## pn:\<name\>
+If the string starts with "pn:" then the remainder of the string should be a DNS/hostname
+that is used to over-ride the public_name field of the ECHConfigList that is used
+for ECH.
+
+# DEFAULT
+
+NULL, meaning ECH is disabled.
+
+# EXAMPLE
+
+~~~c
+CURL *curl = curl_easy_init();
+
+const char *config ="ecl:AED+DQA87wAgACB/RuzUCsW3uBbSFI7mzD63TUXpI8sGDTnFTbFCDpa+CAAEAAEAAQANY292ZXIuZGVmby5pZQAA";
+if(curl) {
+ curl_easy_setopt(curl, CURLOPT_ECH, config);
+ curl_easy_perform(curl);
+}
+~~~
+# AVAILABILITY
+
+Added in 8.8.0
+
+# RETURN VALUE
+
+Returns CURLE_OK on success or CURLE_OUT_OF_MEMORY if there was insufficient heap space.
docs/libcurl/opts/Makefile.inc+1 -0
@@ -166,6 +166,7 @@ man_MANS = \
CURLOPT_DOH_SSL_VERIFYPEER.3 \
CURLOPT_DOH_SSL_VERIFYSTATUS.3 \
CURLOPT_DOH_URL.3 \
+ CURLOPT_ECH.3 \
CURLOPT_EGDSOCKET.3 \
CURLOPT_ERRORBUFFER.3 \
CURLOPT_EXPECT_100_TIMEOUT_MS.3 \
docs/libcurl/symbols-in-versions+2 -0
@@ -340,6 +340,7 @@ CURLE_URL_MALFORMAT_USER 7.1 7.17.0
CURLE_USE_SSL_FAILED 7.17.0
CURLE_WEIRD_SERVER_REPLY 7.51.0
CURLE_WRITE_ERROR 7.1
+CURLE_ECH_REQUIRED 8.8.0
CURLFILETYPE_DEVICE_BLOCK 7.21.0
CURLFILETYPE_DEVICE_CHAR 7.21.0
CURLFILETYPE_DIRECTORY 7.21.0
@@ -617,6 +618,7 @@ CURLOPT_DOH_SSL_VERIFYHOST 7.76.0
CURLOPT_DOH_SSL_VERIFYPEER 7.76.0
CURLOPT_DOH_SSL_VERIFYSTATUS 7.76.0
CURLOPT_DOH_URL 7.62.0
+CURLOPT_ECH 8.8.0
CURLOPT_EGDSOCKET 7.7 7.84.0
CURLOPT_ENCODING 7.10 7.21.6
CURLOPT_ERRORBUFFER 7.1
docs/options-in-versions+1 -0
@@ -55,6 +55,7 @@
--doh-insecure 7.76.0
--doh-url 7.62.0
--dump-header (-D) 5.7
+--ech 8.8.0
--egd-file 7.7
--engine 7.9.3
--etag-compare 7.68.0
include/curl/curl.h+5 -1
@@ -632,6 +632,7 @@ typedef enum {
CURLE_SSL_CLIENTCERT, /* 98 - client-side certificate required */
CURLE_UNRECOVERABLE_POLL, /* 99 - poll/select returned fatal error */
CURLE_TOO_LARGE, /* 100 - a value/data met its maximum */
+ CURLE_ECH_REQUIRED, /* 101 - ECH tried but failed */
CURL_LAST /* never use! */
} CURLcode;
@@ -2209,6 +2210,9 @@ typedef enum {
/* millisecond version */
CURLOPT(CURLOPT_SERVER_RESPONSE_TIMEOUT_MS, CURLOPTTYPE_LONG, 324),
+ /* set ECH configuration */
+ CURLOPT(CURLOPT_ECH, CURLOPTTYPE_STRINGPOINT, 325),
+
CURLOPT_LASTENTRY /* the last unused */
} CURLoption;
@@ -3161,7 +3165,7 @@ typedef struct curl_version_info_data curl_version_info_data;
#define CURL_VERSION_GSASL (1<<29) /* libgsasl is supported */
#define CURL_VERSION_THREADSAFE (1<<30) /* libcurl API is thread-safe */
- /*
+/*
* NAME curl_version_info()
*
* DESCRIPTION
include/curl/typecheck-gcc.h+1 -0
@@ -275,6 +275,7 @@ CURLWARNING(_curl_easy_getinfo_err_curl_off_t,
(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 || \
lib/curl_config.h.cmake+6 -0
@@ -805,3 +805,9 @@ ${SIZEOF_TIME_T_CODE}
/* Define to 1 to enable TLS-SRP support. */
#cmakedefine USE_TLS_SRP 1
+
+/* Define to 1 to query for HTTPSRR when using DoH */
+#cmakedefine USE_HTTPSRR 1
+
+/* if ECH support is available */
+#cmakedefine USE_ECH 1
lib/doh.c+411 -2
@@ -42,9 +42,13 @@
#include "curl_printf.h"
#include "curl_memory.h"
#include "memdebug.h"
+#include "escape.h"
#define DNS_CLASS_IN 0x01
+/* local_print_buf truncates if the hex string will be more than this */
+#define LOCAL_PB_HEXMAX 400
+
#ifndef CURL_DISABLE_VERBOSE_STRINGS
static const char * const errors[]={
"",
@@ -187,6 +191,26 @@ doh_write_cb(const void *contents, size_t size, size_t nmemb, void *userp)
return realsize;
}
+#if defined(USE_HTTPSRR) && defined(CURLDEBUG)
+static void local_print_buf(struct Curl_easy *data,
+ const char *prefix,
+ unsigned char *buf, size_t len)
+{
+ unsigned char hexstr[LOCAL_PB_HEXMAX];
+ size_t hlen = LOCAL_PB_HEXMAX;
+ bool truncated = false;
+
+ if(len > (LOCAL_PB_HEXMAX / 2))
+ truncated = true;
+ Curl_hexencode(buf, len, hexstr, hlen);
+ if(!truncated)
+ infof(data, "%s: len=%d, val=%s", prefix, (int)len, hexstr);
+ else
+ infof(data, "%s: len=%d (truncated)val=%s", prefix, (int)len, hexstr);
+ return;
+}
+#endif
+
/* called from multi.c when this DoH transfer is complete */
static int doh_done(struct Curl_easy *doh, CURLcode result)
{
@@ -379,6 +403,12 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
int slot;
struct dohdata *dohp;
struct connectdata *conn = data->conn;
+#ifdef USE_HTTPSRR
+ /* for now, this is only used when ECH is enabled */
+# ifdef USE_ECH
+ char *qname = NULL;
+# endif
+#endif
*waitp = FALSE;
(void)hostname;
(void)port;
@@ -418,6 +448,37 @@ struct Curl_addrinfo *Curl_doh(struct Curl_easy *data,
goto error;
dohp->pending++;
}
+#endif
+
+#ifdef USE_HTTPSRR
+ /*
+ * TODO: Figure out the conditions under which we want to make
+ * a request for an HTTPS RR when we are not doing ECH. For now,
+ * making this request breaks a bunch of DoH tests, e.g. test2100,
+ * where the addiitonal request doesn't match the pre-cooked data
+ * files, so there's a bit of work attached to making the request
+ * in a non-ECH use-case. For the present, we'll only make the
+ * request when ECH is enabled in the build and is being used for
+ * the curl operation.
+ */
+# ifdef USE_ECH
+ if(data->set.tls_ech & CURLECH_ENABLE
+ || data->set.tls_ech & CURLECH_HARD) {
+ if(port == 443)
+ qname = strdup(hostname);
+ else
+ qname = aprintf("_%d._https.%s", port, hostname);
+ if(!qname)
+ goto error;
+ result = dohprobe(data, &dohp->probe[DOH_PROBE_SLOT_HTTPS],
+ DNS_TYPE_HTTPS, qname, data->set.str[STRING_DOH],
+ data->multi, dohp->headers);
+ free(qname);
+ if(result)
+ goto error;
+ dohp->pending++;
+ }
+# endif
#endif
*waitp = TRUE; /* this never returns synchronously */
return NULL;
@@ -501,6 +562,25 @@ static DOHcode store_aaaa(const unsigned char *doh,
return DOH_OK;
}
+#ifdef USE_HTTPSRR
+static DOHcode store_https(const unsigned char *doh,
+ int index,
+ struct dohentry *d,
+ uint16_t len)
+{
+ /* silently ignore RRs over the limit */
+ if(d->numhttps_rrs < DOH_MAX_HTTPS) {
+ struct dohhttps_rr *h = &d->https_rrs[d->numhttps_rrs];
+ h->val = Curl_memdup(&doh[index], len);
+ if(!h->val)
+ return DOH_OUT_OF_MEM;
+ h->len = len;
+ d->numhttps_rrs++;
+ }
+ return DOH_OK;
+}
+#endif
+
static DOHcode store_cname(const unsigned char *doh,
size_t dohlen,
unsigned int index,
@@ -563,7 +643,8 @@ static DOHcode rdata(const unsigned char *doh,
/* RDATA
- A (TYPE 1): 4 bytes
- AAAA (TYPE 28): 16 bytes
- - NS (TYPE 2): N bytes */
+ - NS (TYPE 2): N bytes
+ - HTTPS (TYPE 65): N bytes */
DOHcode rc;
switch(type) {
@@ -581,6 +662,13 @@ static DOHcode rdata(const unsigned char *doh,
if(rc)
return rc;
break;
+#ifdef USE_HTTPSRR
+ case DNS_TYPE_HTTPS:
+ rc = store_https(doh, index, d, rdlength);
+ if(rc)
+ return rc;
+ break;
+#endif
case DNS_TYPE_CNAME:
rc = store_cname(doh, dohlen, index, d);
if(rc)
@@ -737,7 +825,11 @@ UNITTEST DOHcode doh_decode(const unsigned char *doh,
if(index != dohlen)
return DOH_DNS_MALFORMAT; /* something is wrong */
+#ifdef USE_HTTTPS
+ if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr && !d->numhttps_rrs)
+#else
if((type != DNS_TYPE_NS) && !d->numcname && !d->numaddr)
+#endif
/* nothing stored! */
return DOH_NO_CONTENT;
@@ -776,6 +868,16 @@ static void showdoh(struct Curl_easy *data,
infof(data, "%s", buffer);
}
}
+#ifdef USE_HTTPSRR
+ for(i = 0; i < d->numhttps_rrs; i++) {
+# ifdef CURLDEBUG
+ local_print_buf(data, "DoH HTTPS",
+ d->https_rrs[i].val, d->https_rrs[i].len);
+# else
+ infof(data, "DoH HTTPS RR: length %d", d->https_rrs[i].len);
+# endif
+ }
+#endif
for(i = 0; i < d->numcname; i++) {
infof(data, "CNAME: %s", Curl_dyn_ptr(&d->cname[i]));
}
@@ -895,7 +997,18 @@ static CURLcode doh2ai(const struct dohentry *de, const char *hostname,
#ifndef CURL_DISABLE_VERBOSE_STRINGS
static const char *type2name(DNStype dnstype)
{
- return (dnstype == DNS_TYPE_A)?"A":"AAAA";
+ switch(dnstype) {
+ case DNS_TYPE_A:
+ return "A";
+ case DNS_TYPE_AAAA:
+ return "AAAA";
+#ifdef USE_HTTPSRR
+ case DNS_TYPE_HTTPS:
+ return "HTTPS";
+#endif
+ default:
+ return "unknown";
+ }
}
#endif
@@ -905,8 +1018,282 @@ UNITTEST void de_cleanup(struct dohentry *d)
for(i = 0; i < d->numcname; i++) {
Curl_dyn_free(&d->cname[i]);
}
+#ifdef USE_HTTPSRR
+ for(i = 0; i < d->numhttps_rrs; i++)
+ free(d->https_rrs[i].val);
+#endif
}
+#ifdef USE_HTTPSRR
+
+/*
+ * @brief decode the DNS name in a binary RRData
+ * @param buf points to the buffer (in/out)
+ * @param remaining points to the remaining buffer length (in/out)
+ * @param dnsname returns the string form name on success
+ * @return is 1 for success, error otherwise
+ *
+ * The encoding here is defined in
+ * https://tools.ietf.org/html/rfc1035#section-3.1
+ *
+ * The input buffer pointer will be modified so it points to
+ * just after the end of the DNS name encoding on output. (And
+ * that's why it's an "unsigned char **" :-)
+ */
+static CURLcode local_decode_rdata_name(unsigned char **buf, size_t *remaining,
+ char **dnsname)
+{
+ unsigned char *cp = NULL;
+ int rem = 0;
+ char *thename = NULL, *tp = NULL;
+ unsigned char clen = 0; /* chunk len */
+
+ if(!buf || !remaining || !dnsname)
+ return CURLE_OUT_OF_MEMORY;
+ rem = (int)*remaining;
+ thename = calloc(1, CURL_MAXLEN_host_name);
+ if(!thename)
+ return CURLE_OUT_OF_MEMORY;
+ cp = *buf;
+ tp = thename;
+ clen = *cp++;
+ if(clen == 0) {
+ /* special case - return "." as name */
+ thename[0] = '.';
+ thename[1] = 0x00;
+ }
+ while(clen) {
+ if(clen >= rem) {
+ free(thename);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ if(((tp - thename) + clen) > CURL_MAXLEN_host_name) {
+ free(thename);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ memcpy(tp, cp, clen);
+ tp += clen;
+ *tp++ = '.';
+ cp += clen;
+ rem -= (clen + 1);
+ if(rem <= 0) {
+ free(thename);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ clen = *cp++;
+ }
+ *buf = cp;
+ if(rem <= 0) {
+ free(thename);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ *remaining = rem - 1;
+ *dnsname = thename;
+ return CURLE_OK;
+}
+
+static CURLcode local_decode_rdata_alpn(unsigned char *rrval, size_t len,
+ char **alpns)
+{
+ /*
+ * spec here is as per draft-ietf-dnsop-svcb-https, section-7.1.1
+ * encoding is catenated list of strings each preceded by a one
+ * octet length
+ * output is comma-sep list of the strings
+ * implementations may or may not handle quoting of comma within
+ * string values, so we might see a comma within the wire format
+ * version of a string, in which case we'll 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 olen = 0, 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;
+ }
+ olen = Curl_dyn_len(&dval);
+ /* I think the + 1 here is ok but it could trigger a read error */
+ oval = (char *)Curl_memdup(Curl_dyn_ptr(&dval), olen + 1);
+ if(!oval)
+ goto err;
+ Curl_dyn_free(&dval);
+ oval[olen]='\0';
+ *alpns = oval;
+ return CURLE_OK;
+err:
+ Curl_dyn_free(&dval);
+ return CURLE_BAD_CONTENT_ENCODING;
+}
+
+#ifdef CURLDEBUG
+static CURLcode test_alpn_escapes(void)
+{
+ /* we'll use an example from draft-ietf-dnsop-svcb, figure 10 */
+ static unsigned char example[] = {
+ 0x08, /* length 8 */
+ 0x66, 0x5c, 0x6f, 0x6f, 0x2c, 0x62, 0x61, 0x72, /* value "f\\oo,bar" */
+ 0x02, /* length 2 */
+ 0x68, 0x32 /* value "h2" */
+ };
+ size_t example_len = sizeof(example);
+ char *aval = NULL;
+ static const char *expected = "f\\\\oo\\,bar,h2";
+
+ if(local_decode_rdata_alpn(example, example_len, &aval) != CURLE_OK)
+ return CURLE_BAD_CONTENT_ENCODING;
+ if(strlen(aval) != strlen(expected))
+ return CURLE_BAD_CONTENT_ENCODING;
+ if(memcmp(aval, expected, strlen(aval)))
+ return CURLE_BAD_CONTENT_ENCODING;
+ return CURLE_OK;
+}
+#endif
+
+static CURLcode Curl_doh_decode_httpsrr(unsigned char *rrval, size_t len,
+ struct Curl_https_rrinfo **hrr)
+{
+ size_t remaining = len;
+ unsigned char *cp = rrval;
+ uint16_t pcode = 0, plen = 0;
+ struct Curl_https_rrinfo *lhrr = NULL;
+ char *dnsname = NULL;
+
+#ifdef CURLDEBUG
+ /* a few tests of escaping, shouldn't be here but ok for now */
+ if(test_alpn_escapes() != CURLE_OK)
+ return CURLE_OUT_OF_MEMORY;
+#endif
+ lhrr = calloc(1, sizeof(struct Curl_https_rrinfo));
+ if(!lhrr)
+ return CURLE_OUT_OF_MEMORY;
+ lhrr->val = calloc(1, len);
+ if(!lhrr->val)
+ goto err;
+ lhrr->len = len;
+ memcpy(lhrr->val, rrval, len);
+ if(remaining <= 2)
+ goto err;
+ lhrr->priority = (uint16_t)((cp[0] << 8) + cp[1]);
+ cp += 2;
+ remaining -= (uint16_t)2;
+ if(local_decode_rdata_name(&cp, &remaining, &dnsname) != CURLE_OK)
+ goto err;
+ lhrr->target = dnsname;
+ while(remaining >= 4) {
+ pcode = (uint16_t)((*cp << 8) + (*(cp + 1)));
+ cp += 2;
+ plen = (uint16_t)((*cp << 8) + (*(cp + 1)));
+ cp += 2;
+ remaining -= 4;
+ if(pcode == HTTPS_RR_CODE_ALPN) {
+ if(local_decode_rdata_alpn(cp, plen, &lhrr->alpns) != CURLE_OK)
+ goto err;
+ }
+ if(pcode == HTTPS_RR_CODE_NO_DEF_ALPN)
+ lhrr->no_def_alpn = TRUE;
+ else if(pcode == HTTPS_RR_CODE_IPV4) {
+ lhrr->ipv4hints = Curl_memdup(cp, plen);
+ if(!lhrr->ipv4hints)
+ goto err;
+ lhrr->ipv4hints_len = (size_t)plen;
+ }
+ else if(pcode == HTTPS_RR_CODE_ECH) {
+ lhrr->echconfiglist = Curl_memdup(cp, plen);
+ if(!lhrr->echconfiglist)
+ goto err;
+ lhrr->echconfiglist_len = (size_t)plen;
+ }
+ else if(pcode == HTTPS_RR_CODE_IPV6) {
+ lhrr->ipv6hints = Curl_memdup(cp, plen);
+ if(!lhrr->ipv6hints)
+ goto err;
+ lhrr->ipv6hints_len = (size_t)plen;
+ }
+ if(plen > 0 && plen <= remaining) {
+ cp += plen;
+ remaining -= plen;
+ }
+ }
+ DEBUGASSERT(!remaining);
+ *hrr = lhrr;
+ return CURLE_OK;
+err:
+ if(lhrr) {
+ if(lhrr->target)
+ free(lhrr->target);
+ if(lhrr->echconfiglist)
+ free(lhrr->echconfiglist);
+ if(lhrr->val)
+ free(lhrr->val);
+ free(lhrr);
+ }
+ return CURLE_OUT_OF_MEMORY;
+}
+
+# ifdef CURLDEBUG
+static void local_print_httpsrr(struct Curl_easy *data,
+ struct Curl_https_rrinfo *hrr)
+{
+ DEBUGASSERT(hrr);
+ infof(data, "HTTPS RR: priority %d, target: %s",
+ hrr->priority, hrr->target);
+ if(hrr->alpns)
+ infof(data, "HTTPS RR: alpns %s", hrr->alpns);
+ else
+ infof(data, "HTTPS RR: no alpns");
+ if(hrr->no_def_alpn)
+ infof(data, "HTTPS RR: no_def_alpn set");
+ else
+ infof(data, "HTTPS RR: no_def_alpn not set");
+ if(hrr->ipv4hints) {
+ local_print_buf(data, "HTTPS RR: ipv4hints",
+ hrr->ipv4hints, hrr->ipv4hints_len);
+ }
+ else
+ infof(data, "HTTPS RR: no ipv4hints");
+ if(hrr->echconfiglist) {
+ local_print_buf(data, "HTTPS RR: ECHConfigList",
+ hrr->echconfiglist, hrr->echconfiglist_len);
+ }
+ else
+ infof(data, "HTTPS RR: no ECHConfigList");
+ if(hrr->ipv6hints) {
+ local_print_buf(data, "HTTPS RR: ipv6hint",
+ hrr->ipv6hints, hrr->ipv6hints_len);
+ }
+ else
+ infof(data, "HTTPS RR: no ipv6hints");
+ return;
+}
+# endif
+#endif
+
CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
struct Curl_dns_entry **dnsp)
{
@@ -923,9 +1310,15 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
CURLE_COULDNT_RESOLVE_HOST;
}
else if(!dohp->pending) {
+#ifndef USE_HTTPSRR
DOHcode rc[DOH_PROBE_SLOTS] = {
DOH_OK, DOH_OK
};
+#else
+ DOHcode rc[DOH_PROBE_SLOTS] = {
+ DOH_OK, DOH_OK, DOH_OK
+ };
+#endif
struct dohentry de;
int slot;
/* remove DoH handles from multi handle and close them */
@@ -991,6 +1384,22 @@ CURLcode Curl_doh_is_resolved(struct Curl_easy *data,
} /* address processing done */
/* Now process any build-specific attributes retrieved from DNS */
+#ifdef USE_HTTPSRR
+ if(de.numhttps_rrs > 0 && result == CURLE_OK && *dnsp) {
+ struct Curl_https_rrinfo *hrr = NULL;
+ result = Curl_doh_decode_httpsrr(de.https_rrs->val, de.https_rrs->len,
+ &hrr);
+ if(result) {
+ infof(data, "Failed to decode HTTPS RR");
+ return result;
+ }
+ infof(data, "Some HTTPS RR to process");
+# ifdef CURLDEBUG
+ local_print_httpsrr(data, hrr);
+# endif
+ (*dnsp)->hinfo = hrr;
+ }
+#endif
/* All done */
de_cleanup(&de);
lib/doh.h+38 -1
@@ -26,6 +26,9 @@
#include "urldata.h"
#include "curl_addrinfo.h"
+#ifdef USE_HTTPSRR
+# include <stdint.h>
+#endif
#ifndef CURL_DISABLE_DOH
@@ -51,7 +54,8 @@ typedef enum {
DNS_TYPE_NS = 2,
DNS_TYPE_CNAME = 5,
DNS_TYPE_AAAA = 28,
- DNS_TYPE_DNAME = 39 /* RFC6672 */
+ DNS_TYPE_DNAME = 39, /* RFC6672 */
+ DNS_TYPE_HTTPS = 65
} DNStype;
/* one of these for each DoH request */
@@ -88,6 +92,7 @@ int Curl_doh_getsock(struct connectdata *conn, curl_socket_t *socks);
#define DOH_MAX_ADDR 24
#define DOH_MAX_CNAME 4
+#define DOH_MAX_HTTPS 4
struct dohaddr {
int type;
@@ -97,12 +102,44 @@ struct dohaddr {
} ip;
};
+#ifdef USE_HTTPSRR
+
+/*
+ * These are the code points for DNS wire format SvcParams as
+ * per draft-ietf-dnsop-svcb-https
+ * Not all are supported now, and even those that are may need
+ * more work in future to fully support the spec.
+ */
+#define HTTPS_RR_CODE_ALPN 0x01
+#define HTTPS_RR_CODE_NO_DEF_ALPN 0x02
+#define HTTPS_RR_CODE_PORT 0x03
+#define HTTPS_RR_CODE_IPV4 0x04
+#define HTTPS_RR_CODE_ECH 0x05
+#define HTTPS_RR_CODE_IPV6 0x06
+
+/*
+ * These may need escaping when found within an alpn string
+ * value.
+ */
+#define COMMA_CHAR ','
+#define BACKSLASH_CHAR '\\'
+
+struct dohhttps_rr {
+ uint16_t len; /* raw encoded length */
+ unsigned char *val; /* raw encoded octets */
+};
+#endif
+
struct dohentry {
struct dynbuf cname[DOH_MAX_CNAME];
struct dohaddr addr[DOH_MAX_ADDR];
int numaddr;
unsigned int ttl;
int numcname;
+#ifdef USE_HTTPSRR
+ struct dohhttps_rr https_rrs[DOH_MAX_HTTPS];
+ int numhttps_rrs;
+#endif
};
lib/easyoptions.c+2 -1
@@ -86,6 +86,7 @@ struct curl_easyoption Curl_easyopts[] = {
{"DOH_SSL_VERIFYPEER", CURLOPT_DOH_SSL_VERIFYPEER, CURLOT_LONG, 0},
{"DOH_SSL_VERIFYSTATUS", CURLOPT_DOH_SSL_VERIFYSTATUS, CURLOT_LONG, 0},
{"DOH_URL", CURLOPT_DOH_URL, CURLOT_STRING, 0},
+ {"ECH", CURLOPT_ECH, CURLOT_STRING, 0},
{"EGDSOCKET", CURLOPT_EGDSOCKET, CURLOT_STRING, 0},
{"ENCODING", CURLOPT_ACCEPT_ENCODING, CURLOT_STRING, CURLOT_FLAG_ALIAS},
{"ERRORBUFFER", CURLOPT_ERRORBUFFER, CURLOT_OBJECT, 0},
@@ -375,6 +376,6 @@ struct curl_easyoption Curl_easyopts[] = {
*/
int Curl_easyopts_check(void)
{
- return ((CURLOPT_LASTENTRY%10000) != (324 + 1));
+ return ((CURLOPT_LASTENTRY%10000) != (325 + 1));
}
#endif
lib/hostip.c+17 -0
@@ -1070,6 +1070,23 @@ static void freednsentry(void *freethis)
dns->inuse--;
if(dns->inuse == 0) {
Curl_freeaddrinfo(dns->addr);
+#ifdef USE_HTTPSRR
+ if(dns->hinfo) {
+ if(dns->hinfo->target)
+ free(dns->hinfo->target);
+ if(dns->hinfo->alpns)
+ free(dns->hinfo->alpns);
+ if(dns->hinfo->ipv4hints)
+ free(dns->hinfo->ipv4hints);
+ if(dns->hinfo->echconfiglist)
+ free(dns->hinfo->echconfiglist);
+ if(dns->hinfo->ipv6hints)
+ free(dns->hinfo->ipv6hints);
+ if(dns->hinfo->val)
+ free(dns->hinfo->val);
+ free(dns->hinfo);
+ }
+#endif
free(dns);
}
}
lib/hostip.h+37 -0
@@ -32,6 +32,10 @@
#include <setjmp.h>
+#ifdef USE_HTTPSRR
+# include <stdint.h>
+#endif
+
/* Allocate enough memory to hold the full name information structs and
* everything. OSF1 is known to require at least 8872 bytes. The buffer
* required for storing all possible aliases and IP numbers is according to
@@ -58,8 +62,41 @@ struct connectdata;
*/
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 don't support ports (keytag = 3) as we don't support
+ * port-switching yet
+ */
+ unsigned char *ipv4hints; /* keytag = 4 */
+ size_t ipv4hints_len;
+ unsigned char *echconfiglist; /* keytag = 5 */
+ size_t echconfiglist_len;
+ unsigned char *ipv6hints; /* keytag = 6 */
+ size_t ipv6hints_len;
+};
+#endif
+
struct Curl_dns_entry {
struct Curl_addrinfo *addr;
+#ifdef USE_HTTPSRR
+ struct Curl_https_rrinfo *hinfo;
+#endif
/* timestamp == 0 -- permanent CURLOPT_RESOLVE entry (doesn't time out) */
time_t timestamp;
/* use-counter, use Curl_resolv_unlock to release reference */
lib/setopt.c+43 -0
@@ -3141,6 +3141,49 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
data->set.ws_raw_mode = raw;
break;
}
+#endif
+#ifdef USE_ECH
+ case CURLOPT_ECH: {
+ size_t plen = 0;
+
+ argptr = va_arg(param, char *);
+ if(!argptr) {
+ data->set.tls_ech = CURLECH_DISABLE;
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ return result;
+ }
+ plen = strlen(argptr);
+ if(plen > CURL_MAX_INPUT_LENGTH) {
+ data->set.tls_ech = CURLECH_DISABLE;
+ result = CURLE_BAD_FUNCTION_ARGUMENT;
+ return result;
+ }
+ /* set tls_ech flag value, preserving CLA_CFG bit */
+ if(plen == 5 && !strcmp(argptr, "false"))
+ data->set.tls_ech = CURLECH_DISABLE
+ | (data->set.tls_ech & CURLECH_CLA_CFG);
+ else if(plen == 6 && !strcmp(argptr, "grease"))
+ data->set.tls_ech = CURLECH_GREASE
+ | (data->set.tls_ech & CURLECH_CLA_CFG);
+ else if(plen == 4 && !strcmp(argptr, "true"))
+ data->set.tls_ech = CURLECH_ENABLE
+ | (data->set.tls_ech & CURLECH_CLA_CFG);
+ else if(plen == 4 && !strcmp(argptr, "hard"))
+ data->set.tls_ech = CURLECH_HARD
+ | (data->set.tls_ech & CURLECH_CLA_CFG);
+ else if(plen > 5 && !strncmp(argptr, "ecl:", 4)) {
+ result = Curl_setstropt(&data->set.str[STRING_ECH_CONFIG], argptr + 4);
+ if(result)
+ return result;
+ data->set.tls_ech |= CURLECH_CLA_CFG;
+ }
+ else if(plen > 4 && !strncmp(argptr, "pn:", 3)) {
+ result = Curl_setstropt(&data->set.str[STRING_ECH_PUBLIC], argptr + 3);
+ if(result)
+ return result;
+ }
+ break;
+ }
#endif
case CURLOPT_QUICK_EXIT:
data->set.quick_exit = (0 != va_arg(param, long)) ? 1L:0L;
lib/strerror.c+3 -0
@@ -322,6 +322,9 @@ curl_easy_strerror(CURLcode error)
case CURLE_TOO_LARGE:
return "A value or data field grew larger than allowed";
+ case CURLE_ECH_REQUIRED:
+ return "ECH attempted but failed";
+
/* error codes not used by current libcurl */
case CURLE_OBSOLETE20:
case CURLE_OBSOLETE24:
lib/urldata.h+17 -0
@@ -55,6 +55,15 @@
struct curl_trc_featt;
+#ifdef USE_ECH
+/* CURLECH_ bits for the tls_ech option */
+# define CURLECH_DISABLE (1<<0)
+# define CURLECH_GREASE (1<<1)
+# define CURLECH_ENABLE (1<<2)
+# define CURLECH_HARD (1<<3)
+# define CURLECH_CLA_CFG (1<<4)
+#endif
+
#ifdef USE_WEBSOCKETS
/* CURLPROTO_GOPHERS (29) is the highest publicly used protocol bit number,
* the rest are internal information. If we use higher bits we only do this on
@@ -627,6 +636,9 @@ enum doh_slots {
DOH_PROBE_SLOT_IPADDR_V6 = 1, /* 'V6' likewise */
/* Space here for (possibly build-specific) additional slot definitions */
+#ifdef USE_HTTPSRR
+ DOH_PROBE_SLOT_HTTPS = 2, /* for HTTPS RR */
+#endif
/* for example */
/* #ifdef WANT_DOH_FOOBAR_TXT */
@@ -1532,6 +1544,8 @@ enum dupstring {
#ifndef CURL_DISABLE_PROXY
STRING_HAPROXY_CLIENT_IP, /* CURLOPT_HAPROXY_CLIENT_IP */
#endif
+ STRING_ECH_CONFIG, /* CURLOPT_ECH_CONFIG */
+ STRING_ECH_PUBLIC, /* CURLOPT_ECH_PUBLIC */
/* -- end of null-terminated strings -- */
@@ -1859,6 +1873,9 @@ struct UserDefined {
#ifdef USE_WEBSOCKETS
BIT(ws_raw_mode);
#endif
+#ifdef USE_ECH
+ int tls_ech; /* TLS ECH configuration */
+#endif
};
#ifndef CURL_DISABLE_MIME
lib/vtls/openssl.c+284 -0
@@ -82,6 +82,17 @@
#include <openssl/tls1.h>
#include <openssl/evp.h>
+#ifdef USE_ECH
+# ifndef OPENSSL_IS_BORINGSSL
+# 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)
#include <openssl/ocsp.h>
#endif
@@ -3508,6 +3519,9 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
const char * const ssl_cert_type = ssl_config->cert_type;
const bool verifypeer = conn_config->verifypeer;
char error_buffer[256];
+#ifdef USE_ECH
+ struct ssl_connect_data *connssl = cf->ctx;
+#endif
/* Make funny stuff to get random input */
result = ossl_seed(data);
@@ -3843,6 +3857,135 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx,
return CURLE_SSL_CONNECT_ERROR;
}
}
+
+#ifdef USE_ECH
+ if(ECH_ENABLED(data)) {
+ unsigned char *ech_config = NULL;
+ size_t ech_config_len = 0;
+ char *outername = data->set.str[STRING_ECH_PUBLIC];
+ int trying_ech_now = 0;
+
+ if(data->set.tls_ech & CURLECH_GREASE) {
+ infof(data, "ECH: will GREASE ClientHello");
+# ifdef OPENSSL_IS_BORINGSSL
+ SSL_set_enable_ech_grease(octx->ssl, 1);
+# else
+ SSL_set_options(octx->ssl, SSL_OP_ECH_GREASE);
+# endif
+ }
+ else if(data->set.tls_ech & CURLECH_CLA_CFG) {
+# ifdef OPENSSL_IS_BORINGSSL
+ /* have to do base64 decode here for boring */
+ const char *b64 = data->set.str[STRING_ECH_CONFIG];
+
+ if(!b64) {
+ infof(data, "ECH: ECHConfig from command line empty");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ ech_config_len = 2 * strlen(b64);
+ result = Curl_base64_decode(b64, &ech_config, &ech_config_len);
+ if(result || !ech_config) {
+ infof(data, "ECH: can't 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;
+
+ dns = Curl_fetch_addr(data, connssl->peer.hostname, connssl->peer.port);
+ if(!dns) {
+ infof(data, "ECH: requested but no DNS info available");
+ if(data->set.tls_ech & CURLECH_HARD)
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ else {
+ struct Curl_https_rrinfo *rinfo = NULL;
+
+ rinfo = dns->hinfo;
+ if(rinfo && rinfo->echconfiglist) {
+ unsigned char *ecl = rinfo->echconfiglist;
+ size_t elen = rinfo->echconfiglist_len;
+
+ infof(data, "ECH: ECHConfig from DoH HTTPS RR");
+# 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 %ld", elen);
+ }
+ }
+ else {
+ infof(data, "ECH: requested but no ECHConfig available");
+ if(data->set.tls_ech & CURLECH_HARD)
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ Curl_resolv_unlock(data, dns);
+ }
+ }
+# 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'",
+ connssl->peer.hostname, outername);
+ result = SSL_ech_set_server_names(octx->ssl,
+ connssl->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: Can't force TLSv1.3 [ERROR]");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+#endif /* USE_ECH */
+
#endif
octx->reused_session = FALSE;
@@ -3926,6 +4069,70 @@ static CURLcode ossl_connect_step1(struct Curl_cfilter *cf,
return CURLE_OK;
}
+#ifdef USE_ECH
+/* If we have retry configs, then trace those out */
+static void ossl_trace_ech_retry_configs(struct Curl_easy *data, SSL* ssl,
+ int reason)
+{
+ CURLcode result = CURLE_OK;
+ size_t rcl = 0;
+ int rv = 1;
+# ifndef OPENSSL_IS_BORINGSSL
+ char *inner = NULL;
+ unsigned char *rcs = NULL;
+ char *outer = NULL;
+# else
+ const char *inner = NULL;
+ const uint8_t *rcs = NULL;
+ const char *outer = NULL;
+ size_t out_name_len = 0;
+ int servername_type = 0;
+# endif
+
+ /* nothing to trace if not doing ECH */
+ if(!ECH_ENABLED(data))
+ return;
+# ifndef OPENSSL_IS_BORINGSSL
+ rv = SSL_ech_get_retry_config(ssl, &rcs, &rcl);
+# else
+ SSL_get0_ech_retry_configs(ssl, &rcs, &rcl);
+ rv = (int)rcl;
+# endif
+
+ if(rv && rcs) {
+# define HEXSTR_MAX 800
+ char *b64str = NULL;
+ size_t blen = 0;
+
+ result = Curl_base64_encode((const char *)rcs, rcl,
+ &b64str, &blen);
+ if(!result && b64str)
+ infof(data, "ECH: retry_configs %s", b64str);
+ free(b64str);
+# ifndef OPENSSL_IS_BORINGSSL
+ rv = SSL_ech_get_status(ssl, &inner, &outer);
+ infof(data, "ECH: retry_configs for %s from %s, %d %d",
+ inner ? inner : "NULL", outer ? outer : "NULL", reason, rv);
+#else
+ rv = SSL_ech_accepted(ssl);
+ servername_type = SSL_get_servername_type(ssl);
+ inner = SSL_get_servername(ssl, servername_type);
+ SSL_get0_ech_name_override(ssl, &outer, &out_name_len);
+ /* TODO: get the inner from boring */
+ infof(data, "ECH: retry_configs for %s from %s, %d %d",
+ inner ? inner : "NULL", outer ? outer : "NULL", reason, rv);
+#endif
+ }
+ else
+ infof(data, "ECH: no retry_configs (rv = %d)", rv);
+# ifndef OPENSSL_IS_BORINGSSL
+ OPENSSL_free((void *)rcs);
+# endif
+ return;
+}
+
+#endif
+
static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
struct Curl_easy *data)
{
@@ -4038,6 +4245,21 @@ static CURLcode ossl_connect_step2(struct Curl_cfilter *cf,
result = CURLE_SSL_CLIENTCERT;
ossl_strerror(errdetail, error_buffer, sizeof(error_buffer));
}
+#endif
+#ifdef USE_ECH
+ else if((lib == ERR_LIB_SSL) &&
+# ifndef OPENSSL_IS_BORINGSSL
+ (reason == SSL_R_ECH_REQUIRED)) {
+# else
+ (reason == SSL_R_ECH_REJECTED)) {
+# endif
+
+ /* trace retry_configs if we got some */
+ ossl_trace_ech_retry_configs(data, octx->ssl, reason);
+
+ result = CURLE_ECH_REQUIRED;
+ ossl_strerror(errdetail, error_buffer, sizeof(error_buffer));
+ }
#endif
else {
result = CURLE_SSL_CONNECT_ERROR;
@@ -4092,6 +4314,68 @@ 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
+ if(ECH_ENABLED(data)) {
+ char *inner = NULL, *outer = NULL;
+ const char *status = NULL;
+ int rv;
+
+ rv = SSL_ech_get_status(octx->ssl, &inner, &outer);
+ switch(rv) {
+ case SSL_ECH_STATUS_SUCCESS:
+ status = "succeeded";
+ break;
+ case SSL_ECH_STATUS_GREASE_ECH:
+ status = "sent GREASE, got retry-configs";
+ break;
+ case SSL_ECH_STATUS_GREASE:
+ status = "sent GREASE";
+ break;
+ case SSL_ECH_STATUS_NOT_TRIED:
+ status = "not attempted";
+ break;
+ case SSL_ECH_STATUS_NOT_CONFIGURED:
+ status = "not configured";
+ break;
+ case SSL_ECH_STATUS_BACKEND:
+ status = "backend (unexpected)";
+ break;
+ case SSL_ECH_STATUS_FAILED:
+ status = "failed";
+ break;
+ case SSL_ECH_STATUS_BAD_CALL:
+ status = "bad call (unexpected)";
+ break;
+ case SSL_ECH_STATUS_BAD_NAME:
+ status = "bad name (unexpected)";
+ break;
+ default:
+ status = "unexpected status";
+ infof(data, "ECH: unexpected status %d",rv);
+ }
+ infof(data, "ECH: result: status is %s, inner is %s, outer is %s",
+ (status?status:"NULL"),
+ (inner?inner:"NULL"),
+ (outer?outer:"NULL"));
+ OPENSSL_free(inner);
+ OPENSSL_free(outer);
+ if(rv == SSL_ECH_STATUS_GREASE_ECH) {
+ /* trace retry_configs if we got some */
+ ossl_trace_ech_retry_configs(data, octx->ssl, 0);
+ }
+ if(rv != SSL_ECH_STATUS_SUCCESS
+ && data->set.tls_ech & CURLECH_HARD) {
+ infof(data, "ECH: ech-hard failed");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ }
+ else {
+ infof(data, "ECH: result: status is not attempted");
+ }
+# endif /* BORING */
+#endif /* USE_ECH */
+
#ifdef HAS_ALPN
/* Sets data and len to negotiated protocol, len is 0 if no protocol was
* negotiated
lib/vtls/wolfssl.c+109 -0
@@ -74,6 +74,14 @@
#include "curl_memory.h"
#include "memdebug.h"
+#ifdef USE_ECH
+# include "curl_base64.h"
+# define ECH_ENABLED(__data__) \
+ (__data__->set.tls_ech && \
+ !(__data__->set.tls_ech & CURLECH_DISABLE)\
+ )
+#endif /* USE_ECH */
+
/* KEEP_PEER_CERT is a product of the presence of build time symbol
OPENSSL_EXTRA without NO_CERTS, depending on the version. KEEP_PEER_CERT is
in wolfSSL's settings.h, and the latter two are build time symbols in
@@ -725,6 +733,82 @@ wolfssl_connect_step1(struct Curl_cfilter *cf, struct Curl_easy *data)
Curl_ssl_sessionid_unlock(data);
}
+#ifdef USE_ECH
+ if(ECH_ENABLED(data)) {
+ int trying_ech_now = 0;
+
+ if(data->set.str[STRING_ECH_PUBLIC]) {
+ infof(data, "ECH: outername not (yet) supported with WolfSSL");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ if(data->set.tls_ech == CURLECH_GREASE) {
+ infof(data, "ECH: GREASE'd ECH not yet supported for wolfSSL");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ if(data->set.tls_ech & CURLECH_CLA_CFG
+ && data->set.str[STRING_ECH_CONFIG]) {
+ char *b64val = data->set.str[STRING_ECH_CONFIG];
+ word32 b64len = 0;
+
+ b64len = (word32) strlen(b64val);
+ if(b64len
+ && wolfSSL_SetEchConfigsBase64(backend->handle, b64val, b64len)
+ != WOLFSSL_SUCCESS) {
+ if(data->set.tls_ech & CURLECH_HARD)
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ else {
+ trying_ech_now = 1;
+ infof(data, "ECH: ECHConfig from command line");
+ }
+ }
+ else {
+ struct Curl_dns_entry *dns = NULL;
+
+ dns = Curl_fetch_addr(data, connssl->peer.hostname, connssl->peer.port);
+ if(!dns) {
+ infof(data, "ECH: requested but no DNS info available");
+ if(data->set.tls_ech & CURLECH_HARD)
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ else {
+ struct Curl_https_rrinfo *rinfo = NULL;
+
+ rinfo = dns->hinfo;
+ if(rinfo && rinfo->echconfiglist) {
+ unsigned char *ecl = rinfo->echconfiglist;
+ size_t elen = rinfo->echconfiglist_len;
+
+ infof(data, "ECH: ECHConfig from DoH HTTPS RR");
+ if(wolfSSL_SetEchConfigs(backend->handle, ecl, (word32) elen) !=
+ WOLFSSL_SUCCESS) {
+ infof(data, "ECH: wolfSSL_SetEchConfigs failed");
+ if(data->set.tls_ech & CURLECH_HARD)
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ else {
+ trying_ech_now = 1;
+ infof(data, "ECH: imported ECHConfigList of length %ld", elen);
+ }
+ }
+ else {
+ infof(data, "ECH: requested but no ECHConfig available");
+ if(data->set.tls_ech & CURLECH_HARD)
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+ Curl_resolv_unlock(data, dns);
+ }
+ }
+
+ if(trying_ech_now
+ && SSL_set_min_proto_version(backend->handle, TLS1_3_VERSION) != 1) {
+ infof(data, "ECH: Can't force TLSv1.3 [ERROR]");
+ return CURLE_SSL_CONNECT_ERROR;
+ }
+
+ }
+#endif /* USE_ECH */
+
#ifdef USE_BIO_CHAIN
{
WOLFSSL_BIO *bio;
@@ -858,6 +942,31 @@ wolfssl_connect_step2(struct Curl_cfilter *cf, struct Curl_easy *data)
"continuing anyway");
}
}
+#endif
+#ifdef USE_ECH
+ else if(-1 == detail) {
+ /* try access a retry_config ECHConfigList for tracing */
+ byte echConfigs[1000];
+ word32 echConfigsLen = 1000;
+ int rv = 0;
+
+ /* this currently doesn't produce the retry_configs */
+ rv = wolfSSL_GetEchConfigs(backend->handle, echConfigs,
+ &echConfigsLen);
+ if(rv != WOLFSSL_SUCCESS) {
+ infof(data, "Failed to get ECHConfigs");
+ }
+ else {
+ char *b64str = NULL;
+ size_t blen = 0;
+
+ rv = Curl_base64_encode((const char *)echConfigs, echConfigsLen,
+ &b64str, &blen);
+ if(!rv && b64str)
+ infof(data, "ECH: (not yet) retry_configs %s", b64str);
+ free(b64str);
+ }
+ }
#endif
else if(backend->io_result == CURLE_AGAIN) {
return CURLE_OK;
m4/curl-confopts.m4+100 -0
@@ -568,6 +568,105 @@ AC_DEFUN([CURL_CHECK_LIB_ARES], [
fi
])
+dnl CURL_CHECK_OPTION_NTLM_WB
+dnl -------------------------------------------------
+dnl Verify if configure has been invoked with option
+dnl --enable-ntlm-wb or --disable-ntlm-wb, and set
+dnl shell variable want_ntlm_wb and want_ntlm_wb_file
+dnl as appropriate.
+
+AC_DEFUN([CURL_CHECK_OPTION_NTLM_WB], [
+ AC_BEFORE([$0],[CURL_CHECK_NTLM_WB])dnl
+ OPT_NTLM_WB="default"
+ AC_ARG_ENABLE(ntlm-wb,
+AS_HELP_STRING([--enable-ntlm-wb@<:@=FILE@:>@],[Enable NTLM delegation to winbind's ntlm_auth helper, where FILE is ntlm_auth's absolute filename (default: /usr/bin/ntlm_auth)])
+AS_HELP_STRING([--disable-ntlm-wb],[Disable NTLM delegation to winbind's ntlm_auth helper]),
+ OPT_NTLM_WB=$enableval)
+ want_ntlm_wb_file="/usr/bin/ntlm_auth"
+ case "$OPT_NTLM_WB" in
+ no)
+ dnl --disable-ntlm-wb option used
+ want_ntlm_wb="no"
+ ;;
+ default)
+ dnl configure option not specified
+ want_ntlm_wb="yes"
+ ;;
+ *)
+ dnl --enable-ntlm-wb option used
+ want_ntlm_wb="yes"
+ if test -n "$enableval" && test "$enableval" != "yes"; then
+ want_ntlm_wb_file="$enableval"
+ fi
+ ;;
+ esac
+])
+
+
+dnl CURL_CHECK_NTLM_WB
+dnl -------------------------------------------------
+dnl Check if support for NTLM delegation to winbind's
+dnl ntlm_auth helper will finally be enabled depending
+dnl on given configure options and target platform.
+
+AC_DEFUN([CURL_CHECK_NTLM_WB], [
+ AC_REQUIRE([CURL_CHECK_OPTION_NTLM_WB])dnl
+ AC_REQUIRE([CURL_CHECK_NATIVE_WINDOWS])dnl
+ AC_MSG_CHECKING([whether to enable NTLM delegation to winbind's helper])
+ if test "$curl_cv_native_windows" = "yes" ||
+ test "x$SSL_ENABLED" = "x"; then
+ want_ntlm_wb_file=""
+ want_ntlm_wb="no"
+ elif test "x$ac_cv_func_fork" != "xyes"; then
+ dnl ntlm_wb requires fork
+ want_ntlm_wb="no"
+ fi
+ AC_MSG_RESULT([$want_ntlm_wb])
+ if test "$want_ntlm_wb" = "yes"; then
+ AC_DEFINE(NTLM_WB_ENABLED, 1,
+ [Define to enable NTLM delegation to winbind's ntlm_auth helper.])
+ AC_DEFINE_UNQUOTED(NTLM_WB_FILE, "$want_ntlm_wb_file",
+ [Define absolute filename for winbind's ntlm_auth helper.])
+ NTLM_WB_ENABLED=1
+ fi
+])
+
+dnl CURL_CHECK_OPTION_HTTPSRR
+dnl -----------------------------------------------------
+dnl Verify whether configure has been invoked with option
+dnl --enable-httpsrr or --disable-httpsrr, and set
+dnl shell variable want_httpsrr as appropriate.
+
+AC_DEFUN([CURL_CHECK_OPTION_HTTPSRR], [
+ AC_MSG_CHECKING([whether to enable HTTPSRR support])
+ OPT_HTTPSRR="default"
+ AC_ARG_ENABLE(httpsrr,
+AS_HELP_STRING([--enable-httpsrr],[Enable HTTPSRR support])
+AS_HELP_STRING([--disable-httpsrr],[Disable HTTPSRR support]),
+ OPT_HTTPSRR=$enableval)
+ case "$OPT_HTTPSRR" in
+ no)
+ dnl --disable-httpsrr option used
+ want_httpsrr="no"
+ curl_httpsrr_msg="no (--enable-httpsrr)"
+ AC_MSG_RESULT([no])
+ ;;
+ default)
+ dnl configure option not specified
+ want_httpsrr="no"
+ curl_httpsrr_msg="no (--enable-httpsrr)"
+ AC_MSG_RESULT([no])
+ ;;
+ *)
+ dnl --enable-httpsrr option used
+ want_httpsrr="yes"
+ curl_httpsrr_msg="enabled (--disable-httpsrr)"
+ experimental="httpsrr"
+ AC_MSG_RESULT([yes])
+ ;;
+ esac
+])
+
dnl CURL_CHECK_OPTION_ECH
dnl -----------------------------------------------------
dnl Verify whether configure has been invoked with option
@@ -603,3 +702,4 @@ AS_HELP_STRING([--disable-ech],[Disable ECH support]),
;;
esac
])
+])
packages/OS400/ccsidcurl.c+3 -0
@@ -1097,6 +1097,9 @@ curl_easy_setopt_ccsid(CURL *easy, CURLoption tag, ...)
case CURLOPT_DNS_LOCAL_IP6:
case CURLOPT_DNS_SERVERS:
case CURLOPT_DOH_URL:
+#ifdef USE_ECH
+ case CURLOPT_ECH:
+#endif
case CURLOPT_EGDSOCKET:
case CURLOPT_FTPPORT:
case CURLOPT_FTP_ACCOUNT:
src/tool_cfgable.c+8 -0
@@ -176,6 +176,14 @@ static void free_config_fields(struct OperationConfig *config)
Curl_safefree(config->aws_sigv4);
Curl_safefree(config->proto_str);
Curl_safefree(config->proto_redir_str);
+#ifdef USE_ECH
+ Curl_safefree(config->ech);
+ config->ech = NULL;
+ Curl_safefree(config->ech_config);
+ config->ech_config = NULL;
+ Curl_safefree(config->ech_public);
+ config->ech_public = NULL;
+#endif
}
void config_free(struct OperationConfig *config)
src/tool_cfgable.h+6 -0
@@ -298,6 +298,12 @@ struct OperationConfig {
struct State state; /* for create_transfer() */
bool rm_partial; /* on error, remove partially written output
files */
+#ifdef USE_ECH
+ char *ech; /* Config set by --ech keywords */
+ char *ech_config; /* Config set by "--ech esl:" option */
+ char *ech_public; /* Config set by "--ech pn:" option */
+#endif
+
};
struct GlobalConfig {
src/tool_getparam.c+53 -0
@@ -123,6 +123,7 @@ typedef enum {
C_DOH_INSECURE,
C_DOH_URL,
C_DUMP_HEADER,
+ C_ECH,
C_EGD_FILE,
C_ENGINE,
C_EPRT,
@@ -404,6 +405,7 @@ static const struct LongShort aliases[]= {
{"doh-insecure", ARG_BOOL, ' ', C_DOH_INSECURE},
{"doh-url" , ARG_STRG, ' ', C_DOH_URL},
{"dump-header", ARG_FILE, 'D', C_DUMP_HEADER},
+ {"ech", ARG_STRG, ' ', C_ECH},
{"egd-file", ARG_STRG, ' ', C_EGD_FILE},
{"engine", ARG_STRG, ' ', C_ENGINE},
{"eprt", ARG_BOOL, ' ', C_EPRT},
@@ -2079,6 +2081,57 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
err = PARAM_ENGINES_REQUESTED;
}
break;
+#ifndef USE_ECH
+ case C_ECH: /* --ech, not implemented by default */
+ err = PARAM_LIBCURL_DOESNT_SUPPORT;
+ break;
+#else
+ case C_ECH: /* --ech */
+ if(strlen(nextarg) > 4 && strncasecompare("pn:", nextarg, 3)) {
+ /* a public_name */
+ err = getstr(&config->ech_public, nextarg, DENY_BLANK);
+ }
+ else if(strlen(nextarg) > 5 && strncasecompare("ecl:", nextarg, 4)) {
+ /* an ECHConfigList */
+ if('@' != *(nextarg + 4)) {
+ err = getstr(&config->ech_config, nextarg, DENY_BLANK);
+ }
+ else {
+ /* Indirect case: @filename or @- for stdin */
+ char *tmpcfg = NULL;
+ FILE *file;
+
+ nextarg++; /* skip over '@' */
+ if(!strcmp("-", nextarg)) {
+ file = stdin;
+ }
+ else {
+ file = fopen(nextarg, FOPEN_READTEXT);
+ }
+ if(!file) {
+ warnf(global,
+ "Couldn't read file \"%s\" "
+ "specified for \"--ech ecl:\" option",
+ nextarg);
+ return PARAM_BAD_USE; /* */
+ }
+ err = file2string(&tmpcfg, file);
+ if(file != stdin)
+ fclose(file);
+ if(err)
+ return err;
+ config->ech_config = aprintf("ecl:%s",tmpcfg);
+ if(!config->ech_config)
+ return PARAM_NO_MEM;
+ free(tmpcfg);
+ } /* file done */
+ }
+ else {
+ /* Simple case: just a string, with a keyword */
+ err = getstr(&config->ech, nextarg, DENY_BLANK);
+ }
+ break;
+#endif
case C_CAPATH: /* --capath */
err = getstr(&config->capath, nextarg, DENY_BLANK);
break;
src/tool_help.c+1 -0
@@ -67,6 +67,7 @@ static const struct category_descriptors categories[] = {
{"telnet", "TELNET protocol options", CURLHELP_TELNET},
{"tftp", "TFTP protocol options", CURLHELP_TFTP},
{"tls", "All TLS/SSL related options", CURLHELP_TLS},
+ {"ech", "All Encrypted Client Hello (ECH) options", CURLHELP_ECH},
{"upload", "All options for uploads",
CURLHELP_UPLOAD},
{"verbose", "Options related to any kind of command line output of curl",
src/tool_help.h+1 -0
@@ -68,6 +68,7 @@ struct helptxt {
#define CURLHELP_TLS 1u << 22u
#define CURLHELP_UPLOAD 1u << 23u
#define CURLHELP_VERBOSE 1u << 24u
+#define CURLHELP_ECH 1u << 25u
extern const struct helptxt helptext[];
src/tool_listhelp.c+3 -0
@@ -168,6 +168,9 @@ const struct helptxt helptext[] = {
{"-D, --dump-header <filename>",
"Write the received headers to <filename>",
CURLHELP_HTTP | CURLHELP_FTP},
+ {" --ech <config>",
+ "Configure Encrypted Client Hello (ECH) for use with the TLS session",
+ CURLHELP_TLS | CURLHELP_ECH},
{" --egd-file <file>",
"EGD socket path for random data",
CURLHELP_TLS},
src/tool_operate.c+10 -0
@@ -2187,6 +2187,16 @@ static CURLcode single_transfer(struct GlobalConfig *global,
if(config->hsts)
my_setopt_str(curl, CURLOPT_HSTS, config->hsts);
+#ifdef USE_ECH
+ /* only if enabled in configure */
+ if(config->ech) /* only if set (optional) */
+ my_setopt_str(curl, CURLOPT_ECH, config->ech);
+ if(config->ech_public) /* only if set (optional) */
+ my_setopt_str(curl, CURLOPT_ECH, config->ech_public);
+ if(config->ech_config) /* only if set (optional) */
+ my_setopt_str(curl, CURLOPT_ECH, config->ech_config);
+#endif
+
/* initialize retry vars for loop below */
per->retry_sleep_default = (config->retry_delay) ?
config->retry_delay*1000L : RETRY_SLEEP_DEFAULT; /* ms */
tests/data/test1462+1 -0
@@ -54,6 +54,7 @@ Invalid category provided, here is a list of all categories:
telnet TELNET protocol options
tftp TFTP protocol options
tls All TLS/SSL related options
+ ech All Encrypted Client Hello (ECH) options
upload All options for uploads
verbose Options related to any kind of command line output of curl
</stdout>
tests/data/test1538+2 -1
@@ -133,7 +133,8 @@ e97: proxy handshake error
e98: SSL Client Certificate required
e99: Unrecoverable error in select/poll
e100: A value or data field grew larger than allowed
-e101: Unknown error
+e101: ECH attempted but failed
+e102: Unknown error
m-1: Please call curl_multi_perform() soon
m0: No error
m1: Invalid multi handle
tests/ech_combos.py+99 -0
@@ -0,0 +1,99 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#***************************************************************************
+# _ _ ____ _
+# Project ___| | | | _ \| |
+# / __| | | | |_) | |
+# | (__| |_| | _ <| |___
+# \___|\___/|_| \_\_____|
+#
+# Copyright (C) Daniel Stenberg, <[email protected]>, et al.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at https://curl.se/docs/copyright.html.
+#
+# You may opt to use, copy, modify, merge, publish, distribute and/or sell
+# copies of the Software, and permit persons to whom the Software is
+# furnished to do so, under the terms of the COPYING file.
+#
+# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+# KIND, either express or implied.
+#
+# SPDX-License-Identifier: curl
+#
+###########################################################################
+#
+# Python3 program to print all combination of size r in an array of size n.
+# This is used to generate test lines in tests/ech_test.sh.
+# This will be discarded in the process of moving from experimental,
+# but is worth preserving for the moment in case of changes to the
+# ECH command line args
+
+def CombinationRepetitionUtil(chosen, arr, badarr, index,
+ r, start, end):
+
+ # Current combination is ready,
+ # print it
+ if index == r:
+ # figure out if result should be good or bad and
+ # print prefix, assuming $turl does support ECH so
+ # should work if given "positive" parameters
+ res = 1
+ j = len(chosen) - 1
+ while res and j >= 0:
+ if chosen[j] in badarr:
+ res = 0
+ j = j - 1
+ print("cli_test $turl 1", res, end = " ")
+ # print combination but eliminating any runs of
+ # two identical params
+ for j in range(r):
+ if j != 0 and chosen[j] != chosen[j-1]:
+ print(chosen[j], end = " ")
+
+ print()
+ return
+
+ # When no more elements are
+ # there to put in chosen[]
+ if start > n:
+ return
+
+ # Current is included, put
+ # next at next location
+ chosen[index] = arr[start]
+
+ # Current is excluded, replace it
+ # with next (Note that i+1 is passed,
+ # but index is not changed)
+ CombinationRepetitionUtil(chosen, arr, badarr, index + 1,
+ r, start, end)
+ CombinationRepetitionUtil(chosen, arr, badarr, index,
+ r, start + 1, end)
+
+# The main function that prints all
+# combinations of size r in arr[] of
+# size n. This function mainly uses
+# CombinationRepetitionUtil()
+def CombinationRepetition(arr, badarr, n, r):
+
+ # A temporary array to store
+ # all combination one by one
+ chosen = [0] * r
+
+ # Print all combination using
+ # temporary array 'chosen[]'
+ CombinationRepetitionUtil(chosen, arr, badarr, 0, r, 0, n)
+
+# Driver code
+badarr = [ '--ech grease', '--ech false', '--ech ecl:$badecl', '--ech pn:$badpn' ]
+goodarr = [ '--ech hard', '--ech true', '--ech ecl:$goodecl', '--ech pn:$goodpn' ]
+arr = badarr + goodarr
+r = 8
+n = len(arr) - 1
+
+CombinationRepetition(arr, badarr, n, r)
+
+# This code is contributed by Vaibhav Kumar 12.
+
tests/ech_tests.sh+1151 -0
@@ -0,0 +1,1151 @@
+#!/bin/bash
+#***************************************************************************
+# _ _ ____ _
+# Project ___| | | | _ \| |
+# / __| | | | |_) | |
+# | (__| |_| | _ <| |___
+# \___|\___/|_| \_\_____|
+#
+# Copyright (C) Daniel Stenberg, <[email protected]>, et al.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at https://curl.se/docs/copyright.html.
+#
+# You may opt to use, copy, modify, merge, publish, distribute and/or sell
+# copies of the Software, and permit persons to whom the Software is
+# furnished to do so, under the terms of the COPYING file.
+#
+# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+# KIND, either express or implied.
+#
+# SPDX-License-Identifier: curl
+#
+###########################################################################
+#
+
+# Run some tests against servers we know to support ECH (CF, defo.ie, etc.).
+# as well as some we know don't do ECH but have an HTTPS RR, and finally some
+# for which neither is the case.
+
+# TODO: Translate this into something that approximates a valid curl test:-)
+# Should be useful though even before such translation and a pile less work
+# to do this than that. The pile of work required would include making an
+# ECH-enabled server and a DoH server. For now, this is just run manually.
+#
+
+# set -x
+
+# Exit with an error if there's an active ech stanza in ~/.curlrc
+# as that'd likely skew some results (e.g. turning a fail into a
+# success or vice versa)
+: "${CURL_CFG_FILE=$HOME/.curlrc}"
+active_ech=$(grep ech "$CURL_CFG_FILE" | grep -v "#.*ech")
+if [[ "$active_ech" != "" ]]
+then
+ echo "You seem to have an active ECH setting in $CURL_CFG_FILE"
+ echo "That might affect results so please remove that or comment"
+ echo "it out - exiting."
+ exit 1
+fi
+
+
+# Targets we expect to be ECH-enabled servers
+# for which an HTTPS RR is published.
+# structure is host:port mapped to pathname
+# TODO: add negative tests for these
+declare -A ech_targets=(
+ [my-own.net]="ech-check.php"
+ [my-own.net:8443]="ech-check.php"
+ [defo.ie]="ech-check.php"
+ [cover.defo.ie]=""
+ [draft-13.esni.defo.ie:8413]="stats"
+ [draft-13.esni.defo.ie:8414]="stats"
+ [draft-13.esni.defo.ie:9413]=""
+ [draft-13.esni.defo.ie:10413]=""
+ [draft-13.esni.defo.ie:11413]=""
+ [draft-13.esni.defo.ie:12413]=""
+ [draft-13.esni.defo.ie:12414]=""
+ [crypto.cloudflare.com]="cdn-cgi/trace"
+ [tls-ech.dev]=""
+ [epochbelt.com]=""
+)
+
+# Targets we expect not to be ECH-enabled servers
+# but for which an HTTPS RR is published.
+declare -A httpsrr_targets=(
+ [ietf.org]=""
+ [rte.ie]=""
+)
+
+# Targets we expect not to be ECH-enabled servers
+# and for which no HTTPS RR is published.
+declare -A neither_targets=(
+ [www.tcd.ie]=""
+ [jell.ie]=""
+)
+
+#
+# Variables that can be over-ridden from environment
+#
+
+# Top of curl test tree, assume we're there
+: "${CTOP:=.}"
+
+# Plase to put test log output
+: "${LTOP:=$CTOP/tests/ech-log/}"
+
+# place to stash outputs when things go wrong
+: "${BTOP:=$LTOP}"
+
+# time to wait for a remote access to work, 10 seconds
+: "${tout:=10s}"
+
+# Where we find OpenSSL .so's
+: "${OSSL:=$HOME/code/openssl}"
+
+# Where we find WolfSSL .so's
+: "${WSSL:=$HOME/code/wolfssl/inst/lib}"
+
+# Where we find boringssl .so's
+: "${BSSL:=$HOME/code/boringssl/inst/lib}"
+
+# Where we send DoH queries when using kdig or curl
+: "${DOHSERVER:=one.one.one.one}"
+: "${DOHPATH:=dns-query}"
+
+# Whether to send mail when bad things happen (mostly for cronjob)
+: "${DOMAIL:=no}"
+
+# Misc vars and functions
+
+DEFPORT=443
+
+function whenisitagain()
+{
+ /bin/date -u +%Y%m%d-%H%M%S
+}
+
+function fileage()
+{
+ echo $(($(date +%s) - $(date +%s -r "$1")))
+}
+
+function hostport2host()
+{
+ case $1 in
+ *:*) host=${1%:*} port=${1##*:};;
+ *) host=$1 port=$DEFPORT;;
+ esac
+ echo "$host"
+}
+
+function hostport2port()
+{
+ case $1 in
+ *:*) host=${1%:*} port=${1##*:};;
+ *) host=$1 port=$DEFPORT;;
+ esac
+ echo "$port"
+}
+
+function cli_test()
+{
+ # 1st param is target URL
+ turl=$1
+ # 2nd param is 0 if we expect curl to not work or 1 if we expect it
+ # to have worked
+ curl_winorlose=$2
+ # 3rd param is 0 if we expect ECH to not work or 1 if we expect it
+ # to have worked
+ ech_winorlose=$3
+ # remaining params are passed to command line
+ # echparms=(${@:4})
+ IFS=" " read -r -a echparms <<< "${@:4}"
+
+ TMPF=$(mktemp)
+ cmd="timeout $tout $CURL ${CURL_PARAMS[*]} ${echparms[*]} $turl >$TMPF 2>&1"
+ echo "cli_test: $cmd " >> "$logfile"
+ timeout "$tout" "$CURL" "${CURL_PARAMS[@]}" "${echparms[@]}" "$turl" >"$TMPF" 2>&1
+ eres=$?
+ if [[ "$eres" == "124" ]]
+ then
+ allgood="no"
+ echo "cli_test: Timeout running $cmd"
+ cat "$TMPF" >> "$logfile"
+ echo "cli_test: Timeout running $cmd" >> "$logfile"
+ fi
+ if [[ "$eres" != "0" && "$curl_winorlose" == "1" ]]
+ then
+ allgood="no"
+ echo "cli_test: curl failure running $cmd"
+ cat "$TMPF" >> "$logfile"
+ echo "cli_test: curl failure running $cmd" >> "$logfile"
+ fi
+ ech_success=$(grep -c "ECH: result: status is succeeded" "$TMPF")
+ if [[ "$ech_success" == "$ech_winorlose" ]]
+ then
+ echo "cli_test ok for ${echparms[*]}"
+ else
+ allgood="no"
+ echo "cli_test: ECH failure running $cmd"
+ cat "$TMPF" >> "$logfile"
+ echo "cli_test: ECH failure running $cmd" >> "$logfile"
+ fi
+ rm -f "$TMPF"
+}
+
+function get_ech_configlist()
+{
+ domain=$1
+ ecl=$(dig +short https "$domain" | grep "ech=" | sed -e 's/^.*ech=//' | sed -e 's/ .*//')
+ echo "$ecl"
+}
+
+# start of main script
+
+# start by assuming we have nothing we need...
+have_ossl="no"
+have_wolf="no"
+have_bssl="no"
+using_ossl="no"
+using_wolf="no"
+using_bssl="no"
+have_curl="no"
+have_dig="no"
+have_kdig="no"
+have_presout="no"
+have_portsblocked="no"
+
+# setup logging
+NOW=$(whenisitagain)
+BINNAME=$(basename "$0" .sh)
+if [ ! -d "$LTOP" ]
+then
+ mkdir -p "$LTOP"
+fi
+if [ ! -d "$LTOP" ]
+then
+ echo "Can't see $LTOP for logs - exiting"
+ exit 1
+fi
+logfile=$LTOP/${BINNAME}_$NOW.log
+
+echo "-----" > "$logfile"
+echo "Running $0 at $NOW" >> "$logfile"
+echo "Running $0 at $NOW"
+
+# check we have the binaries needed and which TLS library we'll be using
+if [ -f "$OSSL"/libssl.so ]
+then
+ have_ossl="yes"
+fi
+if [ -f "$WSSL"/libwolfssl.so ]
+then
+ have_wolf="yes"
+fi
+if [ -f "$BSSL"/libssl.so ]
+then
+ have_bssl="yes"
+fi
+CURL="$CTOP/src/curl"
+CURL_PARAMS=(-vvv --doh-url https://one.one.one.one/dns-query)
+if [ -f "$CTOP"/src/curl ]
+then
+ have_curl="yes"
+fi
+ossl_cnt=$(LD_LIBRARY_PATH=$OSSL $CURL "${CURL_PARAMS[@]}" -V 2> /dev/null | grep -c OpenSSL)
+if ((ossl_cnt == 1))
+then
+ using_ossl="yes"
+ # setup access to our .so
+ export LD_LIBRARY_PATH=$OSSL
+fi
+bssl_cnt=$(LD_LIBRARY_PATH=$BSSL $CURL "${CURL_PARAMS[@]}" -V 2> /dev/null | grep -c BoringSSL)
+if ((bssl_cnt == 1))
+then
+ using_bssl="yes"
+ # setup access to our .so
+ export LD_LIBRARY_PATH=$BSSL
+fi
+wolf_cnt=$($CURL "${CURL_PARAMS[@]}" -V 2> /dev/null | grep -c wolfSSL)
+if ((wolf_cnt == 1))
+then
+ using_wolf="yes"
+ # for some reason curl+wolfSSL dislikes certs that are ok
+ # for browsers, so we'll test using "insecure" mode (-k)
+ # but that's ok here as we're only interested in ECH testing
+ CURL_PARAMS+=(-k)
+fi
+# check if we have dig and it knows https or not
+digcmd="dig +short"
+wdig=$(type -p dig)
+if [[ "$wdig" != "" ]]
+then
+ have_dig="yes"
+fi
+wkdig=$(type -p kdig)
+if [[ "$wkdig" != "" ]]
+then
+ have_kdig="yes"
+ digcmd="kdig @$DOHSERVER +https +short"
+fi
+# see if our dig version knows HTTPS
+dout=$($digcmd https defo.ie)
+if [[ $dout != "1 . "* ]]
+then
+ dout=$($digcmd -t TYPE65 defo.ie)
+ if [[ $dout == "1 . "* ]]
+ then
+ # we're good
+ have_presout="yes"
+ fi
+else
+ have_presout="yes"
+fi
+
+# Check if ports other than 443 are blocked from this
+# vantage point (I run tests in a n/w where that's
+# sadly true sometimes;-)
+# echo "Checking if ports other than 443 are maybe blocked"
+not443testurl="https://draft-13.esni.defo.ie:9413/"
+timeout "$tout" "$CURL" "${CURL_PARAMS[@]}" "$not443testurl" >/dev/null 2>&1
+eres=$?
+if [[ "$eres" == "124" ]]
+then
+ echo "Timeout running curl for $not443testurl" >> "$logfile"
+ echo "Timeout running curl for $not443testurl"
+ have_portsblocked="yes"
+fi
+
+{
+ echo "have_ossl: $have_ossl"
+ echo "have_wolf: $have_wolf"
+ echo "have_bssl: $have_bssl"
+ echo "using_ossl: $using_ossl"
+ echo "using_wolf: $using_wolf"
+ echo "using_bssl: $using_bssl"
+ echo "have_curl: $have_curl"
+ echo "have_dig: $have_dig"
+ echo "have_kdig: $have_kdig"
+ echo "have_presout: $have_presout"
+ echo "have_portsblocked: $have_portsblocked"
+} >> "$logfile"
+
+echo "curl: have $have_curl, cURL command: |$CURL ${CURL_PARAMS[*]}|"
+echo "ossl: have: $have_ossl, using: $using_ossl"
+echo "wolf: have: $have_wolf, using: $using_wolf"
+echo "bssl: have: $have_bssl, using: $using_bssl"
+echo "dig: $have_dig, kdig: $have_kdig, HTTPS pres format: $have_presout"
+echo "dig command: |$digcmd|"
+echo "ports != 443 blocked: $have_portsblocked"
+
+if [[ "$have_curl" == "no" ]]
+then
+ echo "Can't proceed without curl - exiting"
+ exit 32
+fi
+
+allgood="yes"
+
+skip="false"
+
+if [[ "$skip" != "true" ]]
+then
+
+# basic ECH good/bad
+for targ in "${!ech_targets[@]}"
+do
+ if [[ "$using_wolf" == "yes" ]]
+ then
+ case $targ in
+ "draft-13.esni.defo.ie:8414" | "tls-ech.dev" | \
+ "crypto.cloudflare.com" | "epochbelt.com")
+ echo "Skipping $targ 'cause wolf"; continue;;
+ *)
+ ;;
+ esac
+ fi
+ host=$(hostport2host "$targ")
+ port=$(hostport2port "$targ")
+ if [[ "$port" != "443" && "$have_portsblocked" == "yes" ]]
+ then
+ echo "Skipping $targ as ports != 443 seem blocked"
+ continue
+ fi
+ path=${ech_targets[$targ]}
+ turl="https://$host:$port/$path"
+ echo "ECH check for $turl"
+ {
+ echo ""
+ echo "ECH check for $turl"
+ } >> "$logfile"
+ timeout "$tout" "$CURL" "${CURL_PARAMS[@]}" --ech hard "$turl" >> "$logfile" 2>&1
+ eres=$?
+ if [[ "$eres" == "124" ]]
+ then
+ allgood="no"
+ {
+ echo "Timeout for $turl"
+ echo -e "\tTimeout for $turl"
+ echo "Timeout running curl for $host:$port/$path"
+ } >> "$logfile"
+ fi
+ if [[ "$eres" != "0" ]]
+ then
+ allgood="no"
+ echo "Error ($eres) for $turl" >> "$logfile"
+ echo -e "\tError ($eres) for $turl"
+ fi
+ echo "" >> "$logfile"
+done
+
+# check if public_name override works (OpenSSL only)
+if [[ "$using_ossl" == "yes" ]]
+then
+ for targ in "${!ech_targets[@]}"
+ do
+ host=$(hostport2host "$targ")
+ port=$(hostport2port "$targ")
+ if [[ "$port" != "443" && "$have_portsblocked" == "yes" ]]
+ then
+ echo "Skipping $targ as ports != 443 seem blocked"
+ continue
+ fi
+ path=${ech_targets[$targ]}
+ turl="https://$host:$port/$path"
+ echo "PN override check for $turl"
+ {
+ echo ""
+ echo "PN override check for $turl"
+ } >> "$logfile"
+ timeout "$tout" "$CURL" "${CURL_PARAMS[@]}" --ech pn:override --ech hard "$turl" >> "$logfile" 2>&1
+ eres=$?
+ if [[ "$eres" == "124" ]]
+ then
+ allgood="no"
+ {
+ echo "Timeout for $turl"
+ echo -e "\tTimeout for $turl"
+ echo "Timeout running curl for $host:$port/$path"
+ } >> "$logfile"
+ fi
+ if [[ "$eres" != "0" ]]
+ then
+ allgood="no"
+ echo "PN override Error ($eres) for $turl" >> "$logfile"
+ echo -e "\tPN override Error ($eres) for $turl"
+ fi
+ echo "" >> "$logfile"
+ done
+fi
+
+for targ in "${!httpsrr_targets[@]}"
+do
+ host=$(hostport2host "$targ")
+ port=$(hostport2port "$targ")
+ if [[ "$port" != "443" && "$have_portsblocked" == "yes" ]]
+ then
+ echo "Skipping $targ as ports != 443 seem blocked"
+ continue
+ fi
+ path=${httpsrr_targets[$targ]}
+ turl="https://$host:$port/$path"
+ echo "HTTPS RR but no ECHConfig check for $turl"
+ {
+ echo ""
+ echo "HTTPS RR but no ECHConfig check for $turl"
+ } >> "$logfile"
+ timeout "$tout" "$CURL" "${CURL_PARAMS[@]}" --ech true "$turl" >> "$logfile" 2>&1
+ eres=$?
+ if [[ "$eres" == "124" ]]
+ then
+ allgood="no"
+ {
+ echo "Timeout for $turl"
+ echo -e "\tTimeout for $turl"
+ echo "Timeout running curl for $host:$port/$path"
+ } >> "$logfile"
+ fi
+ if [[ "$eres" != "0" ]]
+ then
+ allgood="no"
+ echo "Error ($eres) for $turl" >> "$logfile"
+ echo -e "\tError ($eres) for $turl"
+ fi
+ echo "" >> "$logfile"
+done
+
+for targ in "${!neither_targets[@]}"
+do
+ host=$(hostport2host "$targ")
+ port=$(hostport2port "$targ")
+ if [[ "$port" != "443" && "$have_portsblocked" == "yes" ]]
+ then
+ echo "Skipping $targ as ports != 443 seem blocked"
+ continue
+ fi
+ path=${neither_targets[$targ]}
+ turl="https://$host:$port/$path"
+ echo "Neither HTTPS nor ECHConfig check for $turl"
+ {
+ echo ""
+ echo "Neither HTTPS nor ECHConfig check for $turl"
+ } >> "$logfile"
+ timeout "$tout" "$CURL" "${CURL_PARAMS[@]}" --ech true "$turl" >> "$logfile" 2>&1
+ eres=$?
+ if [[ "$eres" == "124" ]]
+ then
+ allgood="no"
+ {
+ echo "Timeout for $turl"
+ echo -e "\tTimeout for $turl"
+ echo "Timeout running curl for $host:$port/$path"
+ } >> "$logfile"
+ fi
+ if [[ "$eres" != "0" ]]
+ then
+ allgood="no"
+ echo "Error ($eres) for $turl" >> "$logfile"
+ echo -e "\tError ($eres) for $turl"
+ fi
+ echo "" >> "$logfile"
+done
+
+
+# Check various command line options, if we're good so far
+if [[ "$using_ossl" == "yes" && "$allgood" == "yes" ]]
+then
+ # use this test URL as it'll tell us if things worked
+ turl="https://defo.ie/ech-check.php"
+ echo "cli_test with $turl"
+ echo "cli_test with $turl" >> "$logfile"
+ cli_test "$turl" 1 1 --ech true
+ cli_test "$turl" 1 0 --ech false
+ cli_test "$turl" 1 1 --ech false --ech true
+ cli_test "$turl" 1 1 --ech false --ech true --ech pn:foobar
+ cli_test "$turl" 1 1 --ech false --ech pn:foobar --ech true
+ echconfiglist=$(get_ech_configlist defo.ie)
+ cli_test "$turl" 1 1 --ech ecl:"$echconfiglist"
+ cli_test "$turl" 1 0 --ech ecl:
+fi
+
+fi # skip
+
+# Check combinations of command line options, if we're good so far
+# Most of this only works for openssl, which is ok, as we're checking
+# the argument handling here, not the ECH protocol
+if [[ "$using_ossl" == "yes" && "$allgood" == "yes" ]]
+then
+ # ech can be hard, true, grease or false
+ # ecl:ecl can be correct, incorrect or missing
+ # ech:pn can be correct, incorrect or missing
+ # in all cases the "last" argument provided should "win"
+ # but only one of hard, true, grease or false will apply
+ turl="https://defo.ie/ech-check.php"
+ echconfiglist=$(get_ech_configlist defo.ie)
+ goodecl=$echconfiglist
+ echconfiglist=$(get_ech_configlist hidden.hoba.ie)
+ badecl=$echconfiglist
+ goodpn="cover.defo.ie"
+ badpn="hoba.ie"
+ echo "more cli_test with $turl"
+ echo "more cli_test with $turl" >> "$logfile"
+
+ # The combinatorics here are handled via the tests/ech_combos.py script
+ # which produces all the relevant combinations or inputs and orders
+ # thereof. We have to manually assess whether or not ECH is expected to
+ # work for each case.
+ cli_test "$turl" 0 0
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech ecl:"$badecl" --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech ecl:"$badecl" --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech hard
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech ecl:"$badecl" --ech hard --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech ecl:"$badecl" --ech hard --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech hard --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech hard --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech ecl:"$badecl" --ech hard --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech ecl:"$badecl" --ech hard --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech hard --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech pn:$badpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech ecl:"$badecl" --ech pn:$badpn --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech ecl:"$badecl" --ech pn:$badpn --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech pn:$badpn --ech hard
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech pn:$badpn --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech pn:$badpn --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech ecl:"$badecl" --ech pn:$badpn --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech ecl:"$badecl" --ech pn:$badpn --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" - 0 --ech ecl:"$badecl" --ech pn:$badpn --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech ecl:"$badecl" --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech ecl:"$badecl" --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 0 --ech false
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 0 --ech false --ech ecl:"$badecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 0 --ech false --ech ecl:"$badecl" --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 0 --ech false --ech ecl:"$badecl" --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech hard
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech ecl:"$badecl" --ech hard --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech ecl:"$badecl" --ech hard --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech hard --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech hard --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech ecl:"$badecl" --ech hard --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech ecl:"$badecl" --ech hard --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech hard --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 0 --ech false --ech ecl:"$badecl" --ech pn:$badpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 0 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 0 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech hard
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 0 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 0 --ech false --ech ecl:"$badecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech ecl:"$badecl" --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech ecl:"$badecl" --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 0 --ech false --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 0 --ech false --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech hard
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech hard --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech hard --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech hard --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech hard --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech hard --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech hard --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech hard --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 0 --ech false --ech pn:$badpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 0 --ech false --ech pn:$badpn --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 0 --ech false --ech pn:$badpn --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech pn:$badpn --ech hard
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech pn:$badpn --ech hard --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech pn:$badpn --ech hard --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech pn:$badpn --ech hard --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech pn:$badpn --ech hard --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech pn:$badpn --ech hard --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech pn:$badpn --ech hard --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech pn:$badpn --ech hard --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 0 --ech false --ech pn:$badpn --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech pn:$badpn --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech pn:$badpn --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech pn:$badpn --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech pn:$badpn --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 0 --ech false --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech false --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech hard
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech hard --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech hard --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech hard --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech hard --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech hard --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech hard --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech hard --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 0 --ech pn:$badpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech pn:$badpn --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech pn:$badpn --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech pn:$badpn --ech hard
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech pn:$badpn --ech hard --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech pn:$badpn --ech hard --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech pn:$badpn --ech hard --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech pn:$badpn --ech hard --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech pn:$badpn --ech hard --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech pn:$badpn --ech hard --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech pn:$badpn --ech hard --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 0 --ech pn:$badpn --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech pn:$badpn --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech pn:$badpn --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech pn:$badpn --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech pn:$badpn --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 0 --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 0
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 0 --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 1 1 --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+
+ # a target URL that doesn't support ECH
+ turl="https://tcd.ie"
+ echo "cli_test with $turl"
+ echo "cli_test with $turl" >> "$logfile"
+ # the params below don't matter much here as we'll fail anyway
+ echconfiglist=$(get_ech_configlist defo.ie)
+ goodecl=$echconfiglist
+ badecl="$goodecl"
+ goodpn="tcd.ie"
+ badpn="tcd.ie"
+ cli_test "$turl" 1 0
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech hard
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech hard --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech hard --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech hard --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech hard --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech hard --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech hard --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech hard --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech pn:$badpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech pn:$badpn --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech pn:$badpn --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech pn:$badpn --ech hard
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech pn:$badpn --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech pn:$badpn --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech pn:$badpn --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech pn:$badpn --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech pn:$badpn --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$badecl" --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech hard
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech hard --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech hard --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech hard --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech hard --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech hard --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech hard --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech hard --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech pn:$badpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech hard
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech hard --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech pn:$badpn --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$badecl" --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech hard
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech hard --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech hard --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech hard --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech hard --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech hard --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech hard --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech hard --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech pn:$badpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech pn:$badpn --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech pn:$badpn --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech pn:$badpn --ech hard
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech pn:$badpn --ech hard --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech pn:$badpn --ech hard --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech pn:$badpn --ech hard --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech pn:$badpn --ech hard --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech pn:$badpn --ech hard --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech pn:$badpn --ech hard --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech pn:$badpn --ech hard --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech pn:$badpn --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech pn:$badpn --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech pn:$badpn --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech pn:$badpn --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech pn:$badpn --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech false --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech hard
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech hard --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech hard --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech hard --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech hard --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech hard --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech hard --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech hard --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech pn:$badpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech pn:$badpn --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech pn:$badpn --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech pn:$badpn --ech hard
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech pn:$badpn --ech hard --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech pn:$badpn --ech hard --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech pn:$badpn --ech hard --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech pn:$badpn --ech hard --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech pn:$badpn --ech hard --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech pn:$badpn --ech hard --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech pn:$badpn --ech hard --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech pn:$badpn --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech pn:$badpn --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech pn:$badpn --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech pn:$badpn --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech pn:$badpn --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech true
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech true --ech ecl:"$goodecl"
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech true --ech ecl:"$goodecl" --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+ cli_test "$turl" 0 0 --ech true --ech pn:$goodpn
+ if [[ "$allgood" != "yes" ]]; then echo $LINENO; fi
+fi
+
+
+END=$(whenisitagain)
+echo "Finished $0 at $END" >> "$logfile"
+echo "-----" >> "$logfile"
+
+if [[ "$allgood" == "yes" ]]
+then
+ echo "Finished $0 at $END"
+ echo "All good, log in $logfile"
+ exit 0
+else
+ echo "Finished $0 at $END"
+ echo "NOT all good, log in $logfile"
+fi
+
+# send a mail to root (will be fwd'd) but just once every 24 hours
+# 'cause we only really need "new" news
+itsnews="yes"
+age_of_news=0
+if [ -f "$LTOP"/bad_runs ]
+then
+ age_of_news=$(fileage "$LTOP"/bad_runs)
+ # only consider news "new" if we haven't mailed today
+ if ((age_of_news < 24*3600))
+ then
+ itsnews="no"
+ fi
+fi
+if [[ "$DOMAIL" == "yes" && "$itsnews" == "yes" ]]
+then
+ echo "ECH badness at $NOW" | mail -s "ECH badness at $NOW" root
+fi
+# add to list of bad runs (updating file age)
+echo "ECH badness at $NOW" >>"$LTOP"/bad_runs
+exit 2
+