Merge branch '4.next'
authorJeremy Harris <jgh146exb@wizmail.org>
Sun, 8 Dec 2019 23:36:01 +0000 (23:36 +0000)
committerJeremy Harris <jgh146exb@wizmail.org>
Sun, 8 Dec 2019 23:36:01 +0000 (23:36 +0000)
25 files changed:
1  2 
doc/doc-docbook/spec.xfpt
doc/doc-txt/ChangeLog
doc/doc-txt/NewStuff
doc/doc-txt/experimental-spec.txt
src/src/EDITME
src/src/deliver.c
src/src/exim.c
src/src/functions.h
src/src/globals.c
src/src/globals.h
src/src/macros.h
src/src/readconf.c
src/src/smtp_in.c
src/src/tls-gnu.c
src/src/tls-openssl.c
src/src/tls.c
src/src/transports/smtp.c
src/src/verify.c
test/confs/0618
test/confs/5665
test/log/2102
test/runtest
test/stderr/5410
test/stderr/5420
test/stdout/0572

index abd15d452dbad0b940cd4a8f8666d01219e3ca1e,0e7d7655cafa69561d0af16371eb960b617a01ab..2946d70131621203ab938248b16f3617b65f034e
  . Update the Copyright year (only) when changing content.
  . /////////////////////////////////////////////////////////////////////////////
  
 -.set previousversion "4.92"
 +.set previousversion "4.93"
  .include ./local_params
  
  .set ACL "access control lists (ACLs)"
  .set I   "&nbsp;&nbsp;&nbsp;&nbsp;"
  
  .macro copyyear
 -2018
 +2019
  .endmacro
  
  . /////////////////////////////////////////////////////////////////////////////
@@@ -371,13 -371,11 +371,13 @@@ contributors
  .section "Exim documentation" "SECID1"
  . Keep this example change bar when updating the documentation!
  
 +.new
  .cindex "documentation"
  This edition of the Exim specification applies to version &version() of Exim.
  Substantive changes from the &previousversion; edition are marked in some
  renditions of this document; this paragraph is so marked if the rendition is
  capable of showing a change indicator.
 +.wen
  
  This document is very much a reference manual; it is not a tutorial. The reader
  is expected to have some familiarity with the SMTP mail transfer protocol and
@@@ -1888,10 -1886,11 +1888,10 @@@ to your &_Local/Makefile_& and rebuild 
  .section "Including TLS/SSL encryption support" "SECTinctlsssl"
  .cindex "TLS" "including support for TLS"
  .cindex "encryption" "including support for"
 -.cindex "SUPPORT_TLS"
  .cindex "OpenSSL" "building Exim with"
  .cindex "GnuTLS" "building Exim with"
 -Exim can be built to support encrypted SMTP connections, using the STARTTLS
 -command as per RFC 2487. It can also support legacy clients that expect to
 +Exim is usually built to support encrypted SMTP connections, using the STARTTLS
 +command as per RFC 2487. It can also support clients that expect to
  start a TLS session immediately on connection to a non-standard port (see the
  &%tls_on_connect_ports%& runtime option and the &%-tls-on-connect%& command
  line option).
@@@ -1900,39 -1899,35 +1900,39 @@@ If you want to build Exim with TLS supp
  OpenSSL or GnuTLS library. There is no cryptographic code in Exim itself for
  implementing SSL.
  
 +If you do not want TLS support you should set
 +.code
 +DISABLE_TLS=yes
 +.endd
 +in &_Local/Makefile_&.
 +
  If OpenSSL is installed, you should set
  .code
 -SUPPORT_TLS=yes
 +USE_OPENSL=yes
  TLS_LIBS=-lssl -lcrypto
  .endd
  in &_Local/Makefile_&. You may also need to specify the locations of the
  OpenSSL library and include files. For example:
  .code
 -SUPPORT_TLS=yes
 +USE_OPENSSL=yes
  TLS_LIBS=-L/usr/local/openssl/lib -lssl -lcrypto
  TLS_INCLUDE=-I/usr/local/openssl/include/
  .endd
  .cindex "pkg-config" "OpenSSL"
  If you have &'pkg-config'& available, then instead you can just use:
  .code
 -SUPPORT_TLS=yes
 +USE_OPENSSL=yes
  USE_OPENSSL_PC=openssl
  .endd
  .cindex "USE_GNUTLS"
  If GnuTLS is installed, you should set
  .code
 -SUPPORT_TLS=yes
  USE_GNUTLS=yes
  TLS_LIBS=-lgnutls -ltasn1 -lgcrypt
  .endd
  in &_Local/Makefile_&, and again you may need to specify the locations of the
  library and include files. For example:
  .code
 -SUPPORT_TLS=yes
  USE_GNUTLS=yes
  TLS_LIBS=-L/usr/gnu/lib -lgnutls -ltasn1 -lgcrypt
  TLS_INCLUDE=-I/usr/gnu/include
  .cindex "pkg-config" "GnuTLS"
  If you have &'pkg-config'& available, then instead you can just use:
  .code
 -SUPPORT_TLS=yes
  USE_GNUTLS=yes
  USE_GNUTLS_PC=gnutls
  .endd
@@@ -3963,18 -3959,6 +3963,18 @@@ is sent to the sender, containing the t
  Bounce messages are just discarded. This option can be used only by an admin
  user.
  
 +.vitem &%-MG%&&~<&'queue&~name'&>&~<&'message&~id'&>&~<&'message&~id'&>&~...
 +.oindex "&%-MG%&"
 +.cindex queue named
 +.cindex "named queues"
 +.cindex "queue" "moving messages"
 +This option requests that each listed message be moved from its current
 +queue to the given named queue.
 +The destination queue name argument is required, but can be an empty
 +string to define the default queue.
 +If the messages are not currently located in the default queue,
 +a &%-qG<name>%& option will be required to define the source queue.
 +
  .vitem &%-Mmad%&&~<&'message&~id'&>&~<&'message&~id'&>&~...
  .oindex "&%-Mmad%&"
  .cindex "delivery" "cancelling all"
@@@ -4383,6 -4367,17 +4383,17 @@@ written. When &%-oX%& is used with &%-b
  without &%-bd%&, this is the only way of causing Exim to write a pid file,
  because in those cases, the normal pid file is not used.
  
+ .new
+ .vitem &%-oPX%&
+ .oindex "&%-oPX%&"
+ .cindex "pid (process id)" "of daemon"
+ .cindex "daemon" "process id (pid)"
+ This option is not intended for general use.
+ The daemon uses it when terminating due to a SIGTEM, possibly in
+ combination with &%-oP%&&~<&'path'&>.
+ It causes the pid file to be removed.
+ .wen
  .vitem &%-or%&&~<&'time'&>
  .oindex "&%-or%&"
  .cindex "timeout" "for non-SMTP input"
@@@ -6125,6 -6120,9 +6136,6 @@@ dnslookup
    domains = ! +local_domains
    transport = remote_smtp
    ignore_target_hosts = 0.0.0.0 : 127.0.0.0/8
 -.ifdef _HAVE_DNSSEC
 -  dnssec_request_domains = *
 -.endif
    no_more
  .endd
  The &%domains%& option behaves as per smarthost, above.
@@@ -6275,6 -6273,9 +6286,6 @@@ Two remote transports and four local tr
  remote_smtp:
    driver = smtp
    message_size_limit = ${if > {$max_received_linelength}{998} {1}{0}}
 -.ifdef _HAVE_DANE
 -  hosts_try_dane = *
 -.endif
  .ifdef _HAVE_PRDR
    hosts_try_prdr = *
  .endif
  This transport is used for delivering messages over SMTP connections.
  The list of remote hosts comes from the router.
  The &%message_size_limit%& usage is a hack to avoid sending on messages
 -with over-long lines.  The built-in macro _HAVE_DANE guards configuration
 -to use DANE for delivery;
 -see section &<<SECDANE>>& for more details.
 +with over-long lines.
  
  The &%hosts_try_prdr%& option enables an efficiency SMTP option.  It is
  negotiated between client and server and not expected to cause problems
@@@ -6673,10 -6676,11 +6684,10 @@@ aliases or other indexed data reference
  tools for building the files can be found in several places:
  .display
  &url(https://cr.yp.to/cdb.html)
 -&url(http://www.corpit.ru/mjt/tinycdb.html)
 +&url(https://www.corpit.ru/mjt/tinycdb.html)
  &url(https://packages.debian.org/stable/utils/freecdb)
  &url(https://github.com/philpennock/cdbtools) (in Go)
  .endd
 -. --- 2018-09-07: corpit.ru http:-only
  A cdb distribution is not needed in order to build Exim with cdb support,
  because the code for reading cdb files is included directly in Exim itself.
  However, no means of building or testing cdb files is provided with Exim, so
@@@ -6761,10 -6765,13 +6772,10 @@@ lookup types support only literal keys
  the implicit key is the host's IP address rather than its name (see section
  &<<SECThoslispatsikey>>&).
  
 -.new
  &*Warning 3*&: Do not use an IPv4-mapped IPv6 address for a key; use the
  IPv4, in dotted-quad form. (Exim converts IPv4-mapped IPv6 addresses to this
  notation before executing the lookup.)
 -.wen
  .next
 -.new
  .cindex lookup json
  .cindex json "lookup type"
  .cindex JSON expansions
@@@ -6781,6 -6788,7 +6792,6 @@@ The final resulting element can be a si
  or array; for the latter two a string-representation os the JSON
  is returned.
  For elements of type string, the returned value is de-quoted.
 -.wen
  .next
  .cindex "linear search"
  .cindex "lookup" "lsearch"
@@@ -7334,7 -7342,7 +7345,7 @@@ with the lookup
  With &"strict"& a response from the DNS resolver that
  is not labelled as authenticated data
  is treated as equivalent to a temporary DNS error.
 -The default is &"never"&.
 +The default is &"lax"&.
  
  See also the &$lookup_dnssec_authenticated$& variable.
  
@@@ -8700,9 -8708,11 +8711,9 @@@ recently implemented &(iplsearch)& file
  (notated using the quoting facility) so as to distinguish them from IPv4 keys.
  For this reason, when the lookup type is &(iplsearch)&, IPv6 addresses are
  converted using colons and not dots.
 -.new
  In all cases except IPv4-mapped IPv6, full, unabbreviated IPv6
  addresses are always used.
  The latter are converted to IPv4 addresses, in dotted-quad form.
 -.wen
  
  Ideally, it would be nice to tidy up this anomalous situation by changing to
  colons in all cases, given that quoting is now available for &(lsearch)&.
@@@ -9223,10 -9233,12 +9234,10 @@@ options for which string expansion is p
  the data type.  ACL rules always expand strings.  A couple of expansion
  conditions do not expand some of the brace-delimited branches, for security
  reasons,
 -.new
  .cindex "tainted data" expansion
  .cindex expansion "tainted data"
  and expansion of data deriving from the sender (&"tainted data"&)
  is not permitted.
 -.wen
  
  
  
@@@ -9473,13 -9485,9 +9484,13 @@@ set in &_Local/Makefile_&. Once loaded
  object so that it doesn't reload the same object file in the same Exim process
  (but of course Exim does start new processes frequently).
  
 -There may be from zero to eight arguments to the function. When compiling
 -a local function that is to be called in this way, &_local_scan.h_& should be
 -included. The Exim variables and functions that are defined by that API
 +There may be from zero to eight arguments to the function.
 +
 +When compiling
 +a local function that is to be called in this way,
 +first &_DLFUNC_IMPL_& should be defined,
 +and second &_local_scan.h_& should be included.
 +The Exim variables and functions that are defined by that API
  are also available for dynamically loaded functions. The function itself
  must have the following type:
  .code
@@@ -9588,8 -9596,10 +9599,8 @@@ Matching of the key against the member 
  For the &"json"& variant,
  if a returned value is a JSON string, it retains its leading and
  trailing quotes.
 -.new
  For the &"jsons"& variant, which is intended for use with JSON strings, the
  leading and trailing quotes are removed from the returned value.
 -.wen
  . XXX should be a UTF-8 compare
  
  The results of matching are handled as above.
@@@ -9640,8 -9650,10 +9651,8 @@@ there is no choice of field separator
  For the &"json"& variant,
  if a returned value is a JSON string, it retains its leading and
  trailing quotes.
 -.new
  For the &"jsons"& variant, which is intended for use with JSON strings, the
  leading and trailing quotes are removed from the returned value.
 -.wen
  
  
  .vitem &*${filter{*&<&'string'&>&*}{*&<&'condition'&>&*}}*&
@@@ -10988,12 -11000,14 +10999,12 @@@ it as a 64-digit hexadecimal number, i
  If the string is a single variable of type certificate,
  returns the SHA-256 hash fingerprint of the certificate.
  
 -.new
  The operator can also be spelled &%sha2%& and does the same as &%sha256%&
  (except for certificates, which are not supported).
  Finally, if an underbar
  and a number is appended it specifies the output length, selecting a
  member of the SHA-2 family of hash functions.
  Values of 256, 384 and 512 are accepted, with 256 being the default.
 -.wen
  
  
  .vitem &*${sha3:*&<&'string'&>&*}*& &&&
@@@ -11380,6 -11394,7 +11391,6 @@@ being processed, to enable these expans
  
  To scan a named list, expand it with the &*listnamed*& operator.
  
 -.new
  .vitem "&*forall_json{*&<&'a JSON array'&>&*}{*&<&'a condition'&>&*}*&" &&&
         "&*forany_json{*&<&'a JSON array'&>&*}{*&<&'a condition'&>&*}*&" &&&
         "&*forall_jsons{*&<&'a JSON array'&>&*}{*&<&'a condition'&>&*}*&" &&&
@@@ -11395,6 -11410,7 +11406,6 @@@ be a JSON array
  The array separator is not changeable.
  For the &"jsons"& variants the elements are expected to be JSON strings
  and have their quotes removed before the evaluation of the condition.
 -.wen
  
  
  
@@@ -12074,12 -12090,14 +12085,12 @@@ contain the trailing slash. If &$config
  .vindex "&$config_file$&"
  The name of the main configuration file Exim is using.
  
 -.new
  .vitem &$dmarc_domain_policy$& &&&
         &$dmarc_status$& &&&
         &$dmarc_status_text$& &&&
         &$dmarc_used_domains$&
  Results of DMARC verification.
  For details see section &<<SECDMARC>>&.
 -.wen
  
  .vitem &$dkim_verify_status$&
  Results of DKIM verification.
@@@ -12774,6 -12792,7 +12785,6 @@@ or if not set, the value of &$qualify_d
  .cindex queues named
  The name of the spool queue in use; empty for the default queue.
  
 -.new
  .vitem &$r_...$&
  .vindex &$r_...$&
  .cindex router variables
@@@ -12781,6 -12800,7 +12792,6 @@@ Values can be placed in these variable
  They can be given any name that starts with &$r_$&.
  The values persist for the address being handled through subsequent routers
  and the eventual transport.
 -.wen
  
  .vitem &$rcpt_count$&
  .vindex "&$rcpt_count$&"
@@@ -13344,7 -13364,6 +13355,7 @@@ or a &%def%& condition
  &*Note*&: Under versions of OpenSSL preceding 1.1.1,
  when a list of more than one
  file is used for &%tls_certificate%&, this variable is not reliable.
 +The macro "_TLS_BAD_MULTICERT_IN_OURCERT" will be defined for those versions.
  
  .vitem &$tls_in_peercert$&
  .vindex "&$tls_in_peercert$&"
@@@ -13401,9 -13420,11 +13412,9 @@@ The deprecated &$tls_cipher$& variable 
  but in the context of an outward SMTP delivery taking place via the &(smtp)& transport
  becomes the same as &$tls_out_cipher$&.
  
 -.new
  .vitem &$tls_in_cipher_std$&
  .vindex "&$tls_in_cipher_std$&"
  As above, but returning the RFC standard name for the cipher suite.
 -.wen
  
  .vitem &$tls_out_cipher$&
  .vindex "&$tls_out_cipher$&"
@@@ -13413,9 -13434,11 +13424,9 @@@ and then set to the outgoing cipher sui
  &<<CHAPTLS>>& for details of TLS support and chapter &<<CHAPsmtptrans>>& for
  details of the &(smtp)& transport.
  
 -,new
  .vitem &$tls_out_cipher_std$&
  .vindex "&$tls_out_cipher_std$&"
  As above, but returning the RFC standard name for the cipher suite.
 -.wen
  
  .vitem &$tls_out_dane$&
  .vindex &$tls_out_dane$&
@@@ -13490,6 -13513,7 +13501,6 @@@ the transport
  .vindex &$tls_out_tlsa_usage$&
  Bitfield of TLSA record types found.  See section &<<SECDANE>>&.
  
 -.new
  .vitem &$tls_in_ver$&
  .vindex "&$tls_in_ver$&"
  When a message is received from a remote host over an encrypted SMTP connection
@@@ -13499,6 -13523,7 +13510,6 @@@ this variable is set to the protocol ve
  .vindex "&$tls_out_ver$&"
  When a message is being delivered to a remote host over an encrypted SMTP connection
  this variable is set to the protocol version.
 -.wen
  
  
  .vitem &$tod_bsdinbox$&
@@@ -14645,10 -14670,8 +14656,10 @@@ received. See chapter &<<CHAPACL>>& fo
  
  .option add_environment main "string list" empty
  .cindex "environment" "set values"
 -This option allows to set individual environment variables that the
 -currently linked libraries and programs in child processes use.
 +This option adds individual environment variables that the
 +currently linked libraries and programs in child processes may use.
 +Each list element should be of the form &"name=value"&.
 +
  See &<<SECTpipeenv>>& for the environment of &(pipe)& transports.
  
  .option admin_groups main "string list&!!" unset
@@@ -14699,9 -14722,11 +14710,9 @@@ If it is set true, Exim's domain parsin
  UTF-8 multicharacters to appear in domain name components, in addition to
  letters, digits, and hyphens.
  
 -.new
  If Exim is built with internationalization support
  and the SMTPUTF8 ESMTP option is in use (see chapter &<<CHAPi18n>>&)
  this option can be left as default.
 -.wen
  Without that,
  if you want to look up such domain names in the DNS, you must also
  adjust the value of &%dns_check_names_pattern%& to match the extended form. A
@@@ -15111,15 -15136,21 +15122,22 @@@ etc. are ignored. If IP literals are en
  to handle IPv6 literal addresses.
  
  
- .option dkim_verify_hashes main "string list" "sha256 : sha512 : sha1"
+ .new
+ .option dkim_verify_hashes main "string list" "sha256 : sha512"
  .cindex DKIM "selecting signature algorithms"
  This option gives a list of hash types which are acceptable in signatures,
++.wen
  and an order of processing.
  Signatures with algorithms not in the list will be ignored.
  
- Note that the presence of sha1 violates RFC 8301.
- Signatures using the rsa-sha1 are however (as of writing) still common.
- The default inclusion of sha1 may be dropped in a future release.
+ Acceptable values include:
+ .code
+ sha1
+ sha256
+ sha512
+ .endd
+ Note that the acceptance of sha1 violates RFC 8301.
  
  .option dkim_verify_keytypes main "string list" "ed25519 : rsa"
  This option gives a list of key types which are acceptable in signatures,
@@@ -15129,6 -15160,7 +15147,6 @@@ Signatures with algorithms not in the l
  .option dkim_verify_minimal main boolean false
  If set to true, verification of signatures will terminate after the
  first success.
 -.wen
  
  .option dkim_verify_signers main "domain list&!!" $dkim_signers
  .cindex DKIM "controlling calls to the ACL"
@@@ -15221,9 -15253,11 +15239,9 @@@ domain matches this list
  This is a fudge to help with name servers that give big delays or otherwise do
  not work for the AAAA record type. In due course, when the world's name
  servers have all been upgraded, there should be no need for this option.
 -.new
  Note that all lookups, including those done for verification, are affected;
  this will result in verify failure for IPv6 connections or ones using names
  only valid for IPv6 addresses.
 -.wen
  
  
  .option dns_retrans main time 0s
@@@ -15430,8 -15464,8 +15448,8 @@@ used. See chapter &<<CHAPsecurity>>& fo
  .cindex "Exim version"
  .cindex customizing "version number"
  .cindex "version number of Exim" override
 -This option allows to override the &$version_number$&/&$exim_version$& Exim reports in
 -various places.  Use with care, this may fool stupid security scanners.
 +This option overrides the &$version_number$&/&$exim_version$& that Exim reports in
 +various places.  Use with care; this may fool stupid security scanners.
  
  
  .option extra_local_interfaces main "string list" unset
@@@ -16054,8 -16088,10 +16072,8 @@@ when Exim is entered, so it can, for ex
  name. If no specific path is set for the log files at compile or runtime,
  or if the option is unset at runtime (i.e.  &`log_file_path = `&)
  they are written in a sub-directory called &_log_& in Exim's spool directory.
 -.new
  A path must start with a slash.
  To send to syslog, use the word &"syslog"&.
 -.wen
  Chapter &<<CHAPlog>>& contains further details about Exim's logging, and
  section &<<SECTwhelogwri>>& describes how the contents of &%log_file_path%& are
  used. If this string is fixed at your installation (contains no expansion
@@@ -16262,7 -16298,7 +16280,7 @@@ harm. This option overrides the &%pipe_
  transport driver.
  
  
 -.option openssl_options main "string list" "+no_sslv2 +no_sslv3 +single_dh_use +no_ticket"
 +.option openssl_options main "string list" "+no_sslv2 +no_sslv3 +single_dh_use +no_ticket +no_renegotiation"
  .cindex "OpenSSL "compatibility options"
  This option allows an administrator to adjust the SSL options applied
  by OpenSSL to connections.  It is given as a space-separated list of items,
@@@ -16403,9 -16439,9 +16421,9 @@@ interpreter. See chapter &<<CHAPperl>>
  This option is available only when Exim is built with an embedded Perl
  interpreter. See chapter &<<CHAPperl>>& for details of its use.
  
 -.option perl_startup main boolean false
 +.option perl_taintmode main boolean false
  .cindex "Perl"
 -This Option enables the taint mode of the embedded Perl interpreter.
 +This option enables the taint mode of the embedded Perl interpreter.
  
  
  .option pgsql_servers main "string list" unset
@@@ -16442,6 -16478,7 +16460,6 @@@ for each SMTP command and response. Whe
  that clients will use it; &"out of order"& commands that are &"expected"& do
  not count as protocol errors (see &%smtp_max_synprot_errors%&).
  
 -.new
  .option pipelining_connect_advertise_hosts main "host list&!!" *
  .cindex "pipelining" "early connection"
  .cindex "pipelining" PIPE_CONNECT
@@@ -16451,9 -16488,8 +16469,9 @@@ and from which pipeline early-connectio
  commands are acceptable.
  When used, the pipelining saves on roundtrip times.
  
 +See also the &%hosts_pipe_connect%& smtp transport option.
 +
  Currently the option name &"X_PIPE_CONNECT"& is used.
 -.wen
  
  
  .option prdr_enable main boolean false
@@@ -16716,6 -16752,7 +16734,6 @@@ used. If the expansion yields an empty 
  added to the message. Otherwise, the string should start with the text
  &"Received:"& and conform to the RFC 2822 specification for &'Received:'&
  header lines.
 -.new
  The default setting is:
  
  .code
@@@ -16734,6 -16771,7 +16752,6 @@@ received_header_text = Received: 
    id $message_exim_id\
    ${if def:received_for {\n\tfor $received_for}}
  .endd
 -.wen
  
  The reference to the TLS cipher is omitted when Exim is built without TLS
  support. The use of conditional expansions ensures that this works for both
@@@ -16912,6 -16950,12 +16930,6 @@@ it qualifies them only if the message c
  &%sender_unqualified_hosts%&, or if the message was submitted locally (not
  using TCP/IP), and the &%-bnq%& option was not set.
  
 -.option add_environment main "string list" empty
 -.cindex "environment"
 -This option allows to add individual environment variables that the
 -currently linked libraries and programs in child processes use. The
 -default list is empty.
 -
  
  .option slow_lookup_log main integer 0
  .cindex "logging" "slow lookups"
@@@ -17669,7 -17713,9 +17687,7 @@@ separator in the usual way (&<<SECTlist
  &*Note*&: Under versions of OpenSSL preceding 1.1.1,
  when a list of more than one
  file is used, the &$tls_in_ourcert$& variable is unreliable.
 -
 -&*Note*&: OCSP stapling is not usable under OpenSSL
 -when a list of more than one file is used.
 +The macro "_TLS_BAD_MULTICERT_IN_OURCERT" will be defined for those versions.
  
  If the option contains &$tls_out_sni$& and Exim is built against OpenSSL, then
  if the OpenSSL build supports TLS extensions and the TLS client sends the
@@@ -17720,8 -17766,10 +17738,8 @@@ larger prime than requested
  The value of this option is expanded and indicates the source of DH parameters
  to be used by Exim.
  
 -.new
  This option is ignored for GnuTLS version 3.6.0 and later.
  The library manages parameter negotiation internally.
 -.wen
  
  &*Note: The Exim Maintainers strongly recommend,
  for other TLS library versions,
@@@ -17820,29 -17868,23 +17838,29 @@@ status proof for the server's certifica
  Certificate Authority.
  
  Usable for GnuTLS 3.4.4 or 3.3.17 or OpenSSL 1.1.0 (or later).
 +The macro "_HAVE_TLS_OCSP" will be defined for those versions.
  
 -.new
  For OpenSSL 1.1.0 or later, and
 -.wen
  for GnuTLS 3.5.6 or later the expanded value of this option can be a list
  of files, to match a list given for the &%tls_certificate%& option.
  The ordering of the two lists must match.
 +The macro "_HAVE_TLS_OCSP_LIST" will be defined for those versions.
  
 -.new
  The file(s) should be in DER format,
 -except for GnuTLS 3.6.3 or later when an optional filetype prefix
 -can be used.  The prefix must be one of "DER" or "PEM", followed by
 +except for GnuTLS 3.6.3 or later
 +or for OpenSSL,
 +when an optional filetype prefix can be used.
 +The prefix must be one of "DER" or "PEM", followed by
  a single space.  If one is used it sets the format for subsequent
  files in the list; the initial format is DER.
 -When a PEM format file is used it may contain multiple proofs,
 -for multiple certificate chain element proofs under TLS1.3.
 -.wen
 +If multiple proofs are wanted, for multiple chain elements
 +(this only works under TLS1.3)
 +they must be coded as a combined OCSP response.
 +
 +Although GnuTLS will accept PEM files with multiple separate
 +PEM blobs (ie. separate OCSP responses), it sends them in the
 +TLS Certificate record interleaved with the certificates of the chain;
 +although a GnuTLS client is happy with that, an OpenSSL client is not.
  
  .option tls_on_connect_ports main "string list" unset
  .cindex SSMTP
@@@ -18151,7 -18193,9 +18169,7 @@@ file = ${extract{mailbox}{$address_data
  This makes the configuration file less messy, and also reduces the number of
  lookups (though Exim does cache lookups).
  
 -.new
  See also the &%set%& option below.
 -.wen
  
  .vindex "&$sender_address_data$&"
  .vindex "&$address_data$&"
@@@ -18358,7 -18402,7 +18376,7 @@@ or for any deliveries caused by this ro
  unless you really, really know what you are doing. See also the generic
  transport option of the same name.
  
 -.option dnssec_request_domains routers "domain list&!!" unset
 +.option dnssec_request_domains routers "domain list&!!" *
  .cindex "MX record" "security"
  .cindex "DNSSEC" "MX lookup"
  .cindex "security" "MX lookup"
@@@ -18934,6 -18978,7 +18952,6 @@@ latter kind
  
  This option controls whether the local part is used to form the key for retry
  hints for addresses that suffer temporary errors while being handled by this
 -.new
  router. The default value is true for any router that has any of
  &%check_local_user%&,
  &%local_parts%&,
  &%local_part_suffix%&,
  &%senders%& or
  &%require_files%&
 -.wen
  set, and false otherwise. Note that this option does not apply to hints keys
  for transport delays; they are controlled by a generic transport option of the
  same name.
@@@ -19078,6 -19124,7 +19096,6 @@@ SMTP VRFY command is enabled, it must b
  matters.
  
  
 -.new
  .option set routers "string list" unset
  .cindex router variables
  This option may be used multiple times on a router;
@@@ -19085,7 -19132,7 +19103,7 @@@ because of this the list aspect is most
  The list separator is a semicolon but can be changed in the
  usual way.
  
 -Each list-element given must be of the form $"name = value"$
 +Each list-element given must be of the form &"name = value"&
  and the names used must start with the string &"r_"&.
  Values containing a list-separator should have them doubled.
  When a router runs, the strings are evaluated in order,
@@@ -19096,10 -19143,11 +19114,10 @@@ The variables can be used by the route
  (not including any preconditions)
  and by the transport.
  Later definitions of a given named variable will override former ones.
 -Varible use is via the usual &$r_...$& syntax.
 +Variable use is via the usual &$r_...$& syntax.
  
  This is similar to the &%address_data%& option, except that
  many independent variables can be used, with choice of naming.
 -.wen
  
  
  .option translate_ip_address routers string&!! unset
@@@ -22882,12 -22930,14 +22900,12 @@@ sometimes add other information onto th
  
  Section &<<SECID136>>& contains further information.
  
 -.new
  This option should not be used when other message-handling software
  may duplicate messages by making hardlinks to the files.  When that is done Exim
  will count the message size once for each filename, in contrast with the actual
  disk usage.  When the option is not set, calculating total usage requires
  a system-call per file to get the size; the number of links is then available also
  as is used to adjust the effective size.
 -.wen
  
  
  .option quota_warn_message appendfile string&!! "see below"
@@@ -24541,7 -24591,7 +24559,7 @@@ See the &%search_parents%& option in ch
  details.
  
  
 -.option dnssec_request_domains smtp "domain list&!!" unset
 +.option dnssec_request_domains smtp "domain list&!!" *
  .cindex "MX record" "security"
  .cindex "DNSSEC" "MX lookup"
  .cindex "security" "MX lookup"
@@@ -24706,6 -24756,7 +24724,6 @@@ facilities such as AUTH, PIPELINING, SI
  Exim will not use the SMTP PIPELINING extension when delivering to any host
  that matches this list, even if the server host advertises PIPELINING support.
  
 -.new
  .option hosts_pipe_connect smtp "host list&!!" unset
  .cindex "pipelining" "early connection"
  .cindex "pipelining" PIPE_CONNECT
@@@ -24719,8 -24770,6 +24737,8 @@@ When used, the pipelining saves on roun
  It also turns SMTP into a client-first protocol
  so combines well with TCP Fast Open.
  
 +See also the &%pipelining_connect_advertise_hosts%& main option.
 +
  Note:
  When the facility is used, the transport &%helo_data%& option
  will be expanded before the &$sending_ip_address$& variable
@@@ -24729,6 -24778,7 +24747,6 @@@ A check is made for the use of that var
  presence of a &"def:"& test on it, but suitably complex coding
  can avoid the check and produce unexpected results.
  You have been warned.
 -.wen
  
  
  .option hosts_avoid_tls smtp "host list&!!" unset
@@@ -24769,6 -24819,7 +24787,6 @@@ been started will not be passed to a ne
  message on the same connection. See section &<<SECTmulmessam>>& for an
  explanation of when this might be needed.
  
 -.new
  .option hosts_noproxy_tls smtp "host list&!!" unset
  .cindex "TLS" "passing connection"
  .cindex "multiple SMTP deliveries"
  For any host that matches this list, a TLS session which has
  been started will not be passed to a new delivery process for sending another
  message on the same session.
 -.wen
  
  The traditional implementation closes down TLS and re-starts it in the new
  process, on the same open TCP connection, for each successive message
@@@ -24870,6 -24922,9 +24888,9 @@@ unauthenticated. See also &%hosts_requi
  .cindex "RFC 3030" "CHUNKING"
  This option provides a list of servers to which, provided they announce
  CHUNKING support, Exim will attempt to use BDAT commands rather than DATA.
+ .new
+ Unless DKIM signing is being done,
+ .wen
  BDAT will not be used in conjunction with a transport filter.
  
  .option hosts_try_dane smtp "host list&!!" *
@@@ -26426,8 -26481,10 +26447,8 @@@ authentication mechanism (RFC 2195), an
  the Cyrus SASL authentication library.
  The third is an interface to Dovecot's authentication system, delegating the
  work via a socket interface.
 -.new
  The fourth provides for negotiation of authentication done via non-SMTP means,
  as defined by RFC 4422 Appendix A.
 -.wen
  The fifth provides an interface to the GNU SASL authentication library, which
  provides mechanisms but typically not data sources.
  The sixth provides direct access to Heimdal GSSAPI, geared for Kerberos, but
@@@ -26852,19 -26909,7 +26873,19 @@@ security risk; you are strongly advise
  use unencrypted plain text, you should not use the same passwords for SMTP
  connections as you do for login accounts.
  
 -.section "Plaintext options" "SECID171"
 +.section "Avoiding cleartext use" "SECTplain_TLS"
 +The following generic option settings will disable &(plaintext)& authenticators when
 +TLS is not being used:
 +.code
 +  server_advertise_condition = ${if def:tls_in_cipher }
 +  client_condition =           ${if def:tls_out_cipher}
 +.endd
 +
 +&*Note*&: a plaintext SMTP AUTH done inside TLS is not vulnerable to casual snooping,
 +but is still vulnerable to a Man In The Middle attack unless certificates
 +(including their names) have been properly verified.
 +
 +.section "Plaintext server options" "SECID171"
  .cindex "options" "&(plaintext)& authenticator (server)"
  When configured as a server, &(plaintext)& uses the following options:
  
  This is actually a global authentication option, but it must be set in order to
  configure the &(plaintext)& driver as a server. Its use is described below.
  
 -.option server_prompts plaintext string&!! unset
 +.option server_prompts plaintext "string list&!!" unset
  The contents of this option, after expansion, must be a colon-separated list of
  prompt strings. If expansion fails, a temporary authentication rejection is
  given.
@@@ -27415,9 -27460,11 +27436,11 @@@ This should have meant that certificat
  non-issue, as a man-in-the-middle attack will cause the correct client and
  server to see different identifiers and authentication will fail.
  
- This is currently only supported when using the GnuTLS library.  This is
+ .new
+ This is
  only usable by mechanisms which support "channel binding"; at time of
  writing, that's the SCRAM family.
+ .wen
  
  This defaults off to ensure smooth upgrade across Exim releases, in case
  this option causes some clients to start failing.  Some future release
@@@ -27788,10 -27835,11 +27811,10 @@@ of your configured trust-anchor
  (which usually means the full set of public CAs)
  and which has a mail-SAN matching the claimed identity sent by the client.
  
 -Note that, up to TLS1.2, the client cert is on the wire in-clear, including the SAN,
 +&*Note*&: up to TLS1.2, the client cert is on the wire in-clear, including the SAN.
  The account name is therefore guessable by an opponent.
  TLS 1.3 protects both server and client certificates, and is not vulnerable
  in this way.
 -Likewise, a traditional plaintext SMTP AUTH done inside TLS is not.
  
  
  .section "Using external in a client" "SECTexternclient"
@@@ -28004,19 -28052,16 +28027,19 @@@ There is also a &%-tls-on-connect%& com
  
  .section "OpenSSL vs GnuTLS" "SECTopenvsgnu"
  .cindex "TLS" "OpenSSL &'vs'& GnuTLS"
 -The first TLS support in Exim was implemented using OpenSSL. Support for GnuTLS
 -followed later, when the first versions of GnuTLS were released. To build Exim
 -to use GnuTLS, you need to set
 +TLS is supported in Exim using either the OpenSSL or GnuTLS library.
 +To build Exim to use OpenSSL you need to set
  .code
 -USE_GNUTLS=yes
 +USE_OPENSSL=yes
  .endd
 -in Local/Makefile, in addition to
 +in Local/Makefile.
 +
 +To build Exim to use GnuTLS, you need to set
  .code
 -SUPPORT_TLS=yes
 +USE_GNUTLS=yes
  .endd
 +in Local/Makefile.
 +
  You must also set TLS_LIBS and TLS_INCLUDE appropriately, so that the
  include files and libraries for GnuTLS can be found.
  
@@@ -28574,10 -28619,12 +28597,10 @@@ transport provide the client with a cer
  if it requests it. If the server is Exim, it will request a certificate only if
  &%tls_verify_hosts%& or &%tls_try_verify_hosts%& matches the client.
  
 -.new
 -Do not use a certificate which has the OCSP-must-staple extension,
 +&*Note*&: Do not use a certificate which has the OCSP-must-staple extension,
  for client use (they are usable for server use).
 -As TLS has no means for the client to staple before TLS 1.3 it will result
 +As the TLS protocol has no means for the client to staple before TLS 1.3 it will result
  in failed connections.
 -.wen
  
  If the &%tls_verify_certificates%& option is set on the &(smtp)& transport, it
  specifies a collection of expected server certificates.
@@@ -28999,7 -29046,7 +29022,7 @@@ those who use &%hosts_require_ocsp%&, s
  
  For client-side DANE there are three new smtp transport options, &%hosts_try_dane%&, &%hosts_require_dane%&
  and &%dane_require_tls_ciphers%&.
 -The require variant will result in failure if the target host is not
 +The &"require"& variant will result in failure if the target host is not
  DNSSEC-secured. To get DNSSEC-secured hostname resolution, use
  the &%dnssec_request_domains%& router or transport option.
  
@@@ -29031,8 -29078,7 +29054,8 @@@ If DANE is requested and useable (see a
  If DANE is not usable, whether requested or not, and CA-anchored
  verification evaluation is wanted, the above variables should be set appropriately.
  
 -Currently the (router or transport options) &%dnssec_request_domains%& must be active and &%dnssec_require_domains%& is ignored.
 +The router and transport option &%dnssec_request_domains%& must not be
 +set to &"never"&, and &%dnssec_require_domains%& is ignored.
  
  If verification was successful using DANE then the "CV" item in the delivery log line will show as "CV=dane".
  
@@@ -31094,8 -31140,10 +31117,8 @@@ case-sensitively; domains are checked c
  &'Resent-Cc:'& header lines exist, they are also checked. This condition can be
  used only in a DATA or non-SMTP ACL.
  
 -.new
  There is one possible option, &`case_insensitive`&.  If this is present then
  local parts are checked case-insensitively.
 -.wen
  
  There are, of course, many legitimate messages that make use of blind (bcc)
  recipients. This check should not be used on its own for blocking messages.
@@@ -32703,12 -32751,14 +32726,12 @@@ It supports a &"generic"& interface to 
  specialized interfaces for &"daemon"& type virus scanners, which are resident
  in memory and thus are much faster.
  
 -.new
  Since message data needs to have arrived,
  the condition may be only called in ACL defined by
  &%acl_smtp_data%&,
  &%acl_smtp_data_prdr%&,
  &%acl_smtp_mime%& or
  &%acl_smtp_dkim%&
 -.wen
  
  A timeout of 2 minutes is applied to a scanner call (by default);
  if it expires then a defer action is taken.
@@@ -34248,8 -34298,8 +34271,8 @@@ with translation
  This function is used in conjunction with &'smtp_printf()'&, as described
  below.
  
 -.vitem &*void&~smtp_printf(char&~*,&~...)*&
 -The arguments of this function are like &[printf()]&; it writes to the SMTP
 +.vitem &*void&~smtp_printf(char&~*,BOOL,&~...)*&
 +The arguments of this function are almost like &[printf()]&; it writes to the SMTP
  output stream. You should use this function only when there is an SMTP output
  stream, that is, when the incoming message is being received via interactive
  SMTP. This is the case when &%smtp_input%& is TRUE and &%smtp_batched_input%&
@@@ -34261,15 -34311,6 +34284,15 @@@ is involved
  If an SMTP TLS connection is established, &'smtp_printf()'& uses the TLS
  output function, so it can be used for all forms of SMTP connection.
  
 +The second argument is used to request that the data be buffered
 +(when TRUE) or flushed (along with any previously buffered, when FALSE).
 +This is advisory only, but likely to save on system-calls and packets
 +sent when a sequence of calls to the function are made.
 +
 +The argument was added in Exim version 4.90 - changing the API/ABI.
 +Nobody noticed until 4.93 was imminent, at which point the
 +ABI version number was incremented.
 +
  Strings that are written by &'smtp_printf()'& from within &[local_scan()]&
  must start with an appropriate response code: 550 if you are going to return
  LOCAL_SCAN_REJECT, 451 if you are going to return
@@@ -34287,9 -34328,7 +34310,9 @@@ the data returned via the &%return_text
  multiple output lines.
  
  The &'smtp_printf()'& function does not return any error indication, because it
 -does not automatically flush pending output, and therefore does not test
 +does not
 +guarantee a flush of
 +pending output, and therefore does not test
  the state of the stream. (In the main code of Exim, flushing and error
  detection is done when Exim is ready for the next SMTP input command.) If
  you want to flush the output and check for an error (for example, the
@@@ -37569,6 -37608,7 +37592,6 @@@ connection is unexpectedly dropped
  &%millisec%&: Timestamps have a period and three decimal places of finer granularity
  appended to the seconds value.
  .next
 -.new
  .cindex "log" "message id"
  &%msg_id%&: The value of the Message-ID: header.
  .next
  This will be either because the message is a bounce, or was submitted locally
  (submission mode) without one.
  The field identifier will have an asterix appended: &"id*="&.
 -.wen
  .next
  .cindex "log" "outgoing interface"
  .cindex "log" "local interface"
@@@ -37612,11 -37653,13 +37635,11 @@@ The field is a single "L"
  On accept lines, where PIPELINING was offered but not used by the client,
  the field has a minus appended.
  
 -.new
  .cindex "pipelining" "early connection"
  If Exim is built with the SUPPORT_PIPE_CONNECT build option
  accept "L" fields have a period appended if the feature was
  offered but not used, or an asterisk appended if used.
  Delivery "L" fields have an asterisk appended if used.
 -.wen
  
  .next
  .cindex "log" "queue run"
@@@ -37974,8 -38017,10 +37997,8 @@@ Match only frozen messages
  .vitem &*-x*&
  Match only non-frozen messages.
  
 -.new
  .vitem &*-G*&&~<&'queuename'&>
  Match only messages in the given queue.  Without this, the default queue is searched.
 -.wen
  .endlist
  
  The following options control the format of the output:
@@@ -39663,8 -39708,10 +39686,8 @@@ was received from the client, this reco
  certificate.
  .endlist
  
 -.new
  Any of the above may have an extra hyphen prepended, to indicate the the
  corresponding data is untrusted.
 -.wen
  
  Following the options there is a list of those addresses to which the message
  is not to be delivered. This set of addresses is initialized from the command
@@@ -39854,7 -39901,9 +39877,7 @@@ These options take (expandable) string
  The domain(s) you want to sign with.
  After expansion, this can be a list.
  Each element in turn,
 -.new
  lowercased,
 -.wen
  is put into the &%$dkim_domain%& expansion variable
  while expanding the remaining signing options.
  If it is empty after expansion, DKIM signing is not done,
@@@ -39909,7 -39958,9 +39932,7 @@@ Signers MUST use RSA keys of at least 1
  Signers SHOULD use RSA keys of at least 2048 bits.
  .endd
  
 -.new
  EC keys for DKIM are defined by RFC 8463.
 -.wen
  They are considerably smaller than RSA keys for equivalent protection.
  As they are a recent development, users should consider dual-signing
  (by setting a list of selectors, and an expansion for this option)
@@@ -39929,10 -39980,12 +39952,10 @@@ openssl pkey -outform DER -pubout -in d
  certtool --load_privkey=dkim_ed25519.private --pubkey_info --outder | tail -c +13 | base64
  .endd
  
 -.new
  Exim also supports an alternate format
  of Ed25519 keys in DNS which was a candidate during development
  of the standard, but not adopted.
  A future release will probably drop that support.
 -.wen
  
  .option dkim_hash smtp string&!! sha256
  Can be set to any one of the supported hash methods, which are:
@@@ -40006,18 -40059,22 +40029,18 @@@ RFC 6376 lists these tags as RECOMMENDE
  
  Verification of DKIM signatures in SMTP incoming email is done for all
  messages for which an ACL control &%dkim_disable_verify%& has not been set.
 -.new
  .cindex DKIM "selecting signature algorithms"
  Individual classes of signature algorithm can be ignored by changing
  the main options &%dkim_verify_hashes%& or &%dkim_verify_keytypes%&.
  The &%dkim_verify_minimal%& option can be set to cease verification
  processing for a message once the first passing signature is found.
 -.wen
  
  .cindex authentication "expansion item"
  Performing verification sets up information used by the
  &%authresults%& expansion item.
  
 -.new
  For most purposes the default option settings suffice and the remainder
  of this section can be ignored.
 -.wen
  
  The results of verification are made available to the
  &%acl_smtp_dkim%& ACL, which can examine and modify them.
@@@ -40064,11 -40121,13 +40087,11 @@@ dkim_verify_signers = $sender_address_d
  If a domain or identity is listed several times in the (expanded) value of
  &%dkim_verify_signers%&, the ACL is only called once for that domain or identity.
  
 -.new
  Note that if the option is set using untrustworthy data
  (such as the From: header)
  care should be taken to force lowercase for domains
  and for the domain part if identities.
  The default setting can be regarded as trustworthy in this respect.
 -.wen
  
  If multiple signatures match a domain (or identity), the ACL is called once
  for each matching signature.
@@@ -40170,8 -40229,10 +40193,8 @@@ algorithms (currently, rsa-sha1) have p
  
  To enforce this you must either have a DKIM ACL which checks this variable
  and overwrites the &$dkim_verify_status$& variable as discussed above,
 -.new
  or have set the main option &%dkim_verify_hashes%& to exclude
  processing of such signatures.
 -.wen
  
  .vitem &%$dkim_canon_body%&
  The body canonicalization method. One of 'relaxed' or 'simple'.
@@@ -40286,12 -40347,8 +40309,12 @@@ for more information of what they mean
  
  SPF is a mechanism whereby a domain may assert which IP addresses may transmit
  messages with its domain in the envelope from, documented by RFC 7208.
 -For more information on SPF see &url(http://www.openspf.org).
 -. --- 2018-09-07: still not https
 +For more information on SPF see &url(http://www.open-spf.org), a static copy of
 +the &url(http://openspf.org).
 +. --- 2019-10-28: still not https, open-spf.org is told to be a
 +. --- web-archive copy of the now dead openspf.org site
 +. --- See https://www.mail-archive.com/mailop@mailop.org/msg08019.html for a
 +. --- discussion.
  
  Messages sent by a system not authorised will fail checking of such assertions.
  This includes retransmissions done by traditional forwarders.
@@@ -40354,7 -40411,7 +40377,7 @@@ deny spf = fai
       message = $sender_host_address is not allowed to send mail from \
                 ${if def:sender_address_domain \
                      {$sender_address_domain}{$sender_helo_name}}.  \
 -               Please see http://www.openspf.org/Why?scope=\
 +               Please see http://www.open-spf.org/Why?scope=\
                 ${if def:sender_address_domain {mfrom}{helo}};\
                 identity=${if def:sender_address_domain \
                               {$sender_address}{$sender_helo_name}};\
@@@ -40407,9 -40464,9 +40430,9 @@@ In addition to SPF, you can also perfor
  "Best-guess".  Strictly speaking, "Best-guess" is not standard
  SPF, but it is supported by the same framework that enables SPF
  capability.
 -Refer to &url(http://www.openspf.org/FAQ/Best_guess_record)
 +Refer to &url(http://www.open-spf.org/FAQ/Best_guess_record)
  for a description of what it means.
 -. --- 2018-09-07: still not https:
 +. --- 2019-10-28: still not https:
  
  To access this feature, simply use the spf_guess condition in place
  of the spf one.  For example:
@@@ -40443,7 -40500,9 +40466,7 @@@ would relax host matching rules to a br
  .cindex lookup spf
  A lookup expansion is also available. It takes an email
  address as the key and an IP address
 -.new
  (v4 or v6)
 -.wen
  as the database:
  
  .code
@@@ -40457,6 -40516,7 +40480,6 @@@ The lookup will return the same result 
  
  
  
 -.new
  .section DMARC SECDMARC
  .cindex DMARC verification
  
@@@ -40471,7 -40531,7 +40494,7 @@@ the libopendmarc library is used
  
  For building Exim yourself, obtain the library from
  &url(http://sourceforge.net/projects/opendmarc/)
 -to obtain a copy, or find it in your favorite rpm package
 +to obtain a copy, or find it in your favorite package
  repository.  You will need to attend to the local/Makefile feature
  SUPPORT_DMARC and the associated LDFLAGS addition.
  This description assumes
@@@ -40527,7 -40587,7 +40550,7 @@@ non-authenticated user.  It makes sens
  status of messages coming from remote, untrusted sources.  You can
  use standard conditions such as hosts, senders, etc, to decide that
  DMARC verification should *not* be performed for them and disable
 -DMARC with a control setting:
 +DMARC with an ACL control modifier:
  .code
    control = dmarc_disable_verify
  .endd
@@@ -40538,15 -40598,15 +40561,15 @@@ results in unintended information leaka
  be subscribed to, etc).  You must configure exim to submit forensic
  reports to the owner of the domain.  If the DMARC record contains a
  forensic address and you specify the control statement below, then
 -exim will send these forensic emails.  It's also advised that you
 -configure a dmarc_forensic_sender because the default sender address
 +exim will send these forensic emails.  It is also advised that you
 +configure a &%dmarc_forensic_sender%& because the default sender address
  construction might be inadequate.
  .code
    control = dmarc_enable_forensic
  .endd
  (AGAIN: You can choose not to send these forensic reports by simply
  not putting the dmarc_enable_forensic control line at any point in
 -your exim config.  If you don't tell it to send them, it will not
 +your exim config.  If you don't tell exim to send them, it will not
  send them.)
  
  There are no options to either control.  Both must appear before
@@@ -40555,14 -40615,14 +40578,14 @@@ the DATA acl
  . subsection
  
  DMARC checks cam be run on incoming SMTP  messages by using the
 -"dmarc_status" ACL condition in the DATA ACL.  You are required to
 -call the "spf" condition first in the ACLs, then the "dmarc_status"
 +&"dmarc_status"& ACL condition in the DATA ACL.  You are required to
 +call the &"spf"& condition first in the ACLs, then the &"dmarc_status"&
  condition.  Putting this condition in the ACLs is required in order
  for a DMARC check to actually occur.  All of the variables are set
  up before the DATA ACL, but there is no actual DMARC check that
 -occurs until a "dmarc_status" condition is encountered in the ACLs.
 +occurs until a &"dmarc_status"& condition is encountered in the ACLs.
  
 -The dmarc_status condition takes a list of strings on its
 +The &"dmarc_status"& condition takes a list of strings on its
  right-hand side.  These strings describe recommended action based
  on the DMARC check.  To understand what the policy recommendations
  mean, refer to the DMARC website above.  Valid strings are:
@@@ -40595,30 -40655,28 +40618,30 @@@ Several expansion variables are set bef
  processed, and you can use them in this ACL.  The following
  expansion variables are available:
  
 -&$dmarc_status$&
 +.vlist
 +.vitem &$dmarc_status$&
  .vindex &$dmarc_status$&
  .cindex DMARC result
 -is a one word status indicating what the DMARC library
 +A one word status indicating what the DMARC library
  thinks of the email.  It is a combination of the results of
  DMARC record lookup and the SPF/DKIM/DMARC processing results
  (if a DMARC record was found).  The actual policy declared
  in the DMARC record is in a separate expansion variable.
  
 -&$dmarc_status_text$&
 +.vitem &$dmarc_status_text$&
  .vindex &$dmarc_status_text$&
 -is a slightly longer, human readable status.
 +Slightly longer, human readable status.
  
 -&$dmarc_used_domain$&
 +.vitem &$dmarc_used_domain$&
  .vindex &$dmarc_used_domain$&
 -is the domain which DMARC used to look up the DMARC policy record.
 +The domain which DMARC used to look up the DMARC policy record.
  
 -&$dmarc_domain_policy$&
 +.vitem &$dmarc_domain_policy$&
  .vindex &$dmarc_domain_policy$&
 -is the policy declared in the DMARC record.  Valid values
 +The policy declared in the DMARC record.  Valid values
  are "none", "reject" and "quarantine".  It is blank when there
  is any error, including no DMARC record.
 +.endlist
  
  . subsection
  
@@@ -40633,7 -40691,7 +40656,7 @@@ processing or failure delivery issues)
  In order to log statistics suitable to be imported by the opendmarc
  tools, you need to:
  .ilist
 -Configure the global setting dmarc_history_file
 +Configure the global option &%dmarc_history_file%&
  .next
  Configure cron jobs to call the appropriate opendmarc history
  import scripts and truncating the dmarc_history_file
  
  In order to send forensic reports, you need to:
  .ilist
 -Configure the global setting dmarc_forensic_sender
 +Configure the global option &%dmarc_forensic_sender%&
  .next
  Configure, somewhere before the DATA ACL, the control option to
  enable sending DMARC forensic reports
@@@ -40687,6 -40745,7 +40710,6 @@@ Example usage
    warn    add_header     = :at_start:${authresults {$primary_hostname}}
  .endd
  
 -.wen
  
  
  
@@@ -41015,7 -41074,9 +41038,7 @@@ Events have names which correspond to t
  The name is placed in the variable &$event_name$& and the event action
  expansion must check this, as it will be called for every possible event type.
  
 -.new
  The current list of events is:
 -.wen
  .display
  &`dane:fail              after    transport  `& per connection
  &`msg:complete           after    main       `& per message
diff --combined doc/doc-txt/ChangeLog
index 1e8a2d216ea4a3d9d1c2c476d4fd944bfcdcc5ce,9f18a20732ab2db08a0593bb7e403b4746992219..9f8775f0f89ba5cc2f33a6a4dd7c562cca6ad0c0
@@@ -1,8 -1,32 +1,30 @@@
 -Change log file for Exim from version 4.21
 -------------------------------------------
  This document describes *changes* to previous versions, that might
  affect Exim's operation, with an unchanged configuration file.  For new
  options, and new features, see the NewStuff file next to this ChangeLog.
  
  
 -Exim version 4.next
 --------------------
++Exim version 4.94
++-----------------
+ JH/01 Avoid costly startup code when not strictly needed.  This reduces time
+       for some exim process initialisations.  It does mean that the logging
+       of TLS configuration problems is only done for the daemon startup.
+ JH/02 Early-pipelining support code is now included unless disabled in Makefile.
+ JH/03 DKIM verification defaults no long accept sha1 hashes, to conform to
+       RFC 8301.  They can still be enabled, using the dkim_verify_hashes main
+       option.
+ JH/04 Support CHUNKING from an smtp transport using a transport_filter, when
+       DKIM signing is being done.  Previously a transport_filter would always
+       disable CHUNKING, falling back to traditional DATA.
+ JH/05 Regard command-line receipients as tainted.
+ JH/06 Bug 340: Remove the daemon pid file on exit, whe due to SIGTERM.
  Exim version 4.93
  -----------------
  
@@@ -190,37 -214,14 +212,37 @@@ JH/41 With GnuTLS 3.6.0 (and later) do 
        function is unnecessary and discouraged on GnuTLS 3.6.0 or later. Since
        3.6.0, DH parameters are negotiated following RFC7919."
  
 +HS/06 Change the default of dnssec_request_domains to "*"
 +
 +JH/42 Bug 2545: Fix CHUNKING for all RCPT commands rejected.  Previously we
 +      carried on and emitted a BDAT command, even when PIPELINING was not
 +      active.
 +
  JH/43 Bug 2465: Fix taint-handling in dsearch lookup.  Previously a nontainted
        buffer was used for the filename, resulting in a trap when tainted
        arguments (eg. $domain) were used.
  
 +JH/44 With OpenSSL 1.1.1 (onwards) disable renegotiation for TLS1.2 and below;
 +      recommended to avoid a possible server-load attack.  The feature can be
 +      re-enabled via the openssl_options main cofiguration option.
 +
 +JH/45 local_scan API: documented the current smtp_printf() call. This changed
 +      for version 4.90 - adding a "more data" boolean to the arguments.
 +      Bumped the ABI version number also, this having been missed previously;
 +      release versions 4.90 to 4.92.3 inclusive were effectively broken in
 +      respect of usage of smtp_printf() by either local_scan code or libraries
 +      accessed via the ${dlfunc } expansion item.  Both will need coding
 +      adjustment for any calls to smtp_printf() to match the new function
 +      signature; a FALSE value for the new argument is always safe.
 +
  JH/46 FreeBSD: fix use of the sendfile() syscall.  The shim was not updating
        the file-offset (which the Linux syscall does, and exim expects); this
        resulted in an indefinite loop.
  
 +JH/47 ARC: fix crash in signing, triggered when a configuration error failed
 +      to do ARC verification.  The Authentication-Results: header line added
 +      by the configuration then had no ARC item.
 +
  
  Exim version 4.92
  -----------------
diff --combined doc/doc-txt/NewStuff
index fc307a3ba52a254229f281ca30daef6140db25a3,18c3d30243f2d365e39b82a371474f45b8cc58d7..763a806a579bc37f78c436954d19245772e05ad6
@@@ -6,6 -6,18 +6,16 @@@ Before a formal release, there may be q
  test from the snapshots or the Git before the documentation is updated. Once
  the documentation is updated, this file is reduced to a short list.
  
 -Version 4.next
 ---------------
++Version 4.94
++------------
+  1. EXPERIMENTAL_SRS_NATIVE optional build feature.  See the experimental.spec
+     file.
 - 2. Variables $tls_in_ver, $tls_out_ver.
 -
 - 3. Channel-binding for authenticators is now supported under OpenSSL.
++ 2. Channel-binding for authenticators is now supported under OpenSSL.
+     Previously it was GnuTLS-only.
  Version 4.93
  ------------
  
  
  11. Main options for DKIM verify to filter hash and key types.
  
 -12. Under GnuTLS, with TLS1.3, support for full-chain OCSP stapling.
 +12. With TLS1.3, support for full-chain OCSP stapling.
  
  13. Dual-certificate stacks on servers now support OCSP stapling, under OpenSSL.
  
  14: An smtp:ehlo transport event, for observability of the remote offered features.
  
 +15: Support under OpenSSL for writing NSS-style key files for packet-capture
 +    decode.  The environment variable SSLKEYLOGFILE is used; if an absolute path
 +    it must indicate a file under the spool directory; if relative the the spool
 +    directory is prepended.  Works on the server side only.  Support under 
 +    GnuTLS was already there, being done purely by the library (server side
 +    only, and exim must be run as root).
 +
 +16: Command-line option to move messages from one named queue to another.
 +
 +17. Variables $tls_in_ver, $tls_out_ver.
 +
  
  Version 4.92
  --------------
index 5193f1729118f490311654d66a7fed87dc03a473,e9a557aec8930413d98f7ae2ee585321af5453d1..2569ad3af2b76f7d0ea371a8b00703805932fed5
@@@ -292,10 -292,11 +292,11 @@@ These four steps are explained in more 
  
  
  
- SRS (Sender Rewriting Scheme) Support
+ SRS (Sender Rewriting Scheme) Support (using libsrs_alt)
  --------------------------------------------------------------
+ See also below, for an alternative native support implementation.
  
- Exiscan  currently  includes SRS  support  via Miles  Wilton's
+ Exim  currently  includes SRS  support  via Miles  Wilton's
  libsrs_alt library. The current version of the supported
  library is 0.5, there are reports of 1.0 working.
  
@@@ -343,6 -344,76 +344,76 @@@ For configuration information see https
  
  
  
+ SRS (Sender Rewriting Scheme) Support (native)
+ --------------------------------------------------------------
+ This is less full-featured than the libsrs_alt version above.
+ The Exim build needs to be done with this in Local/Makefile:
+ EXPERIMENTAL_SRS_NATIVE=yes
+ The following are provided:
+ - an expansion item "srs_encode"
+   This takes three arguments:
+   - a site SRS secret
+   - the return_path
+   - the pre-forwarding domain
+ - an expansion condition "inbound_srs"
+   This takes two arguments: the local_part to check, and a site SRS secret.
+   If the secret is zero-length, only the pattern of the local_part is checked.
+   The $srs_recipient variable is set as a side-effect.
+ - an expansion variable $srs_recipient
+   This gets the original return_path encoded in the SRS'd local_part
+ - predefined macros _HAVE_SRS and _HAVE_NATIVE_SRS
+ Sample usage:
+   #macro
+   SRS_SECRET = <pick something unique for your site for this>
+   
+   #routers
+   outbound:
+     driver =    dnslookup
+     # if outbound, and forwarding has been done, use an alternate transport
+     domains =   ! +my_domains
+     transport = ${if eq {$local_part@$domain} \
+                         {$original_local_part@$original_domain} \
+                      {remote_smtp} {remote_forwarded_smtp}}
+   
+   inbound_srs:
+     driver =    redirect
+     senders =   :
+     domains =   +my_domains
+     # detect inbound bounces which are SRS'd, and decode them
+     condition = ${if inbound_srs {$local_part} {SRS_SECRET}}
+     data =      $srs_recipient
+   
+   inbound_srs_failure:
+     driver =    redirect
+     senders =   :
+     domains =   +my_domains
+     # detect inbound bounces which look SRS'd but are invalid
+     condition = ${if inbound_srs {$local_part} {}}
+     allow_fail
+     data =      :fail: Invalid SRS recipient address
+   #... further routers here
+   
+   # transport; should look like the non-forward outbound
+   # one, plus the max_rcpt and return_path options
+   remote_forwarded_smtp:
+     driver =              smtp
+     # modify the envelope from, for mails that we forward
+     max_rcpt =            1
+     return_path =         ${srs_encode {SRS_SECRET} {$return_path} {$original_domain}}
  DCC Support
  --------------------------------------------------------------
  Distributed Checksum Clearinghouse; http://www.rhyolite.com/dcc/
@@@ -571,9 -642,6 +642,9 @@@ ARC suppor
  Specification: https://tools.ietf.org/html/draft-ietf-dmarc-arc-protocol-11
  Note that this is not an RFC yet, so may change.
  
 +[RFC 8617 was published 2019/06.  Draft 11 was 2018/01.  A review of the
 +changes has not yet been done]
 +
  ARC is intended to support the utility of SPF and DKIM in the presence of
  intermediaries in the transmission path - forwarders and mailinglists -
  by establishing a cryptographically-signed chain in headers.
@@@ -582,18 -650,10 +653,18 @@@ Normally one would only bother doing AR
  an intermediary.  One might do verify for local destinations.
  
  ARC uses the notion of a "ADministrative Management Domain" (ADMD).
 -Described in RFC 5598 (section 2.3), this is essentially the set of
 -mail-handling systems that the mail transits.  A label should be chosen to
 -identify the ADMD.  Messages should be ARC-verified on entry to the ADMD,
 -and ARC-signed on exit from it.
 +Described in RFC 5598 (section 2.3), this is essentially a set of
 +mail-handling systems that mail transits that are all under the control
 +of one organisation.  A label should be chosen to identify the ADMD.
 +Messages should be ARC-verified on entry to the ADMD, and ARC-signed on exit
 +from it.
 +
 +
 +Building with ARC Support
 +--
 +Enable using EXPERIMENTAL_ARC=yes in your Local/Makefile.
 +You must also have DKIM present (not disabled), and you very likely
 +want to have SPF enabled.
  
  
  Verification
diff --combined src/src/EDITME
index 83325abfdfcf32b5519c348c3e7e1fc17f7fd2f9,1d916a559ba3d0eb386b707bfadcf3761935e3be..9024b6f73f5e833f5bb655e41f73e41c0522cd4a
@@@ -494,12 -494,12 +494,12 @@@ SUPPORT_DANE=ye
  #------------------------------------------------------------------------------
  # Compiling the Exim monitor: If you want to compile the Exim monitor, a
  # program that requires an X11 display, then EXIM_MONITOR should be set to the
 -# value "eximon.bin". Comment out this setting to disable compilation of the
 +# value "eximon.bin". De-comment this setting to enable compilation of the
  # monitor. The locations of various X11 directories for libraries and include
  # files are defaulted in the OS/Makefile-Default file, but can be overridden in
  # local OS-specific make files.
  
 -EXIM_MONITOR=eximon.bin
 +EXIM_MONITOR=eximon.bin
  
  
  #------------------------------------------------------------------------------
@@@ -590,6 -590,10 +590,10 @@@ DISABLE_MAL_MKS=ye
  # CFLAGS  += -I/usr/local/include
  # LDFLAGS += -lsrs_alt
  
+ # Uncomment the following lines to add SRS (Sender rewriting scheme) support
+ # using only native facilities.
+ # EXPERIMENTAL_SRS_NATIVE=yes
  # Uncomment the following line to add DMARC checking capability, implemented
  # using libopendmarc libraries. You must have SPF and DKIM support enabled also.
  # SUPPORT_DMARC=yes
diff --combined src/src/deliver.c
index 94bc9a89c5853b5d656139e93177281c64364a88,58874add4ad420742e3ac6eac77da5c2bd9841dc..28a1174af6864fc50125f4a0395a3b5163763002
@@@ -1221,7 -1221,7 +1221,7 @@@ els
      {
      if (testflag(addr, af_pipelining))
        g = string_catn(g, US" L", 2);
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
      if (testflag(addr, af_early_pipe))
        g = string_catn(g, US"*", 1);
  #endif
@@@ -3537,7 -3537,7 +3537,7 @@@ while (!done
      case 'L':
        switch (*subid)
        {
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
        case 2: setflag(addr, af_early_pipe);   /*FALLTHROUGH*/
  #endif
        case 1: setflag(addr, af_pipelining); break;
@@@ -4844,7 -4844,7 +4844,7 @@@ all pipes, so I do not see a reason to 
  #endif
  
        if (testflag(addr, af_pipelining))
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
        if (testflag(addr, af_early_pipe))
          rmt_dlv_checked_write(fd, 'L', '2', NULL, 0);
        else
  
    /* Otherwise, if we are running in the test harness, wait a bit, to let the
    newly created process get going before we create another process. This should
 -  ensure repeatability in the tests. We only need to wait a tad. */
 +  ensure repeatability in the tests. Wait long enough for most cases to complete
 +  the transport. */
  
 -  else testharness_pause_ms(500);
 +  else testharness_pause_ms(600);
  
    continue;
  
@@@ -5478,26 -5477,6 +5478,26 @@@ fprintf(f, "Action: %s\n
  }
  
  
 +
 +/* When running in the test harness, there's an option that allows us to
 +fudge this time so as to get repeatability of the tests. Take the first
 +time off the list. In queue runs, the list pointer gets updated in the
 +calling process. */
 +
 +int
 +test_harness_fudged_queue_time(int actual_time)
 +{
 +int qt;
 +if (  f.running_in_test_harness && *fudged_queue_times
 +   && (qt = readconf_readtime(fudged_queue_times, '/', FALSE)) >= 0)
 +  {
 +  DEBUG(D_deliver) debug_printf("fudged queue_times = %s\n",
 +    fudged_queue_times);
 +  return qt;
 +  }
 +return actual_time;
 +}
 +
  /*************************************************
  *              Deliver one message               *
  *************************************************/
@@@ -6188,8 -6167,7 +6188,8 @@@ if (process_recipients != RECIP_IGNORE
          new->onetime_parent = recipients_list[r->pno].address;
  
        /* If DSN support is enabled, set the dsn flags and the original receipt
 -         to be passed on to other DSN enabled MTAs */
 +      to be passed on to other DSN enabled MTAs */
 +
        new->dsn_flags = r->dsn_flags & rf_dsnflags;
        new->dsn_orcpt = r->orcpt;
        DEBUG(D_deliver) debug_printf("DSN: set orcpt: %s  flags: 0x%x\n",
@@@ -7172,7 -7150,7 +7172,7 @@@ if (addr_remote
    /* Precompile some regex that are used to recognize parameters in response
    to an EHLO command, if they aren't already compiled. */
  
-   deliver_init();
+   smtp_deliver_init();
  
    /* Now sort the addresses if required, and do the deliveries. The yield of
    do_remote_deliveries is FALSE when mua_wrapper is set and all addresses
@@@ -7309,9 -7287,10 +7309,9 @@@ for (address_item * a = addr_succeed; a
        );
  
    /* send report if next hop not DSN aware or a router flagged "last DSN hop"
 -     and a report was requested */
 -  if (  (  a->dsn_aware != dsn_support_yes
 -      || a->dsn_flags & rf_dsnlasthop
 -        )
 +  and a report was requested */
 +
 +  if (  (a->dsn_aware != dsn_support_yes || a->dsn_flags & rf_dsnlasthop)
       && a->dsn_flags & rf_notify_success
       )
      {
@@@ -8155,7 -8134,21 +8155,7 @@@ else if (addr_defer != (address_item *)
      int show_time;
      int queue_time = time(NULL) - received_time.tv_sec;
  
 -    /* When running in the test harness, there's an option that allows us to
 -    fudge this time so as to get repeatability of the tests. Take the first
 -    time off the list. In queue runs, the list pointer gets updated in the
 -    calling process. */
 -
 -    if (f.running_in_test_harness && fudged_queue_times[0] != 0)
 -      {
 -      int qt = readconf_readtime(fudged_queue_times, '/', FALSE);
 -      if (qt >= 0)
 -        {
 -        DEBUG(D_deliver) debug_printf("fudged queue_times = %s\n",
 -          fudged_queue_times);
 -        queue_time = qt;
 -        }
 -      }
 +    queue_time = test_harness_fudged_queue_time(queue_time);
  
      /* See how many warnings we should have sent by now */
  
@@@ -8495,52 -8488,13 +8495,13 @@@ return final_yield
  
  
  void
deliver_init(void)
tcp_init(void)
  {
  #ifdef EXIM_TFO_PROBE
  tfo_probe();
  #else
  f.tcp_fastopen_ok = TRUE;
  #endif
- if (!regex_PIPELINING) regex_PIPELINING =
-   regex_must_compile(US"\\n250[\\s\\-]PIPELINING(\\s|\\n|$)", FALSE, TRUE);
- if (!regex_SIZE) regex_SIZE =
-   regex_must_compile(US"\\n250[\\s\\-]SIZE(\\s|\\n|$)", FALSE, TRUE);
- if (!regex_AUTH) regex_AUTH =
-   regex_must_compile(AUTHS_REGEX, FALSE, TRUE);
- #ifndef DISABLE_TLS
- if (!regex_STARTTLS) regex_STARTTLS =
-   regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE);
- #endif
- if (!regex_CHUNKING) regex_CHUNKING =
-   regex_must_compile(US"\\n250[\\s\\-]CHUNKING(\\s|\\n|$)", FALSE, TRUE);
- #ifndef DISABLE_PRDR
- if (!regex_PRDR) regex_PRDR =
-   regex_must_compile(US"\\n250[\\s\\-]PRDR(\\s|\\n|$)", FALSE, TRUE);
- #endif
- #ifdef SUPPORT_I18N
- if (!regex_UTF8) regex_UTF8 =
-   regex_must_compile(US"\\n250[\\s\\-]SMTPUTF8(\\s|\\n|$)", FALSE, TRUE);
- #endif
- if (!regex_DSN) regex_DSN  =
-   regex_must_compile(US"\\n250[\\s\\-]DSN(\\s|\\n|$)", FALSE, TRUE);
- if (!regex_IGNOREQUOTA) regex_IGNOREQUOTA =
-   regex_must_compile(US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)", FALSE, TRUE);
- #ifdef SUPPORT_PIPE_CONNECT
- if (!regex_EARLY_PIPE) regex_EARLY_PIPE =
-   regex_must_compile(US"\\n250[\\s\\-]" EARLY_PIPE_FEATURE_NAME "(\\s|\\n|$)", FALSE, TRUE);
- #endif
  }
  
  
diff --combined src/src/exim.c
index 6a2e7a644b7e9639ddf8f3acc5700c57216236fb,f2943547667f2b50864f4bc666faf57e6490545d..8c3c8e7ed3c2fff003cda2429fa283b4b88bc598
@@@ -202,7 -202,7 +202,7 @@@ va_end(ap)
  static void
  term_handler(int sig)
  {
  exit(1);
+ exit(1);
  }
  
  
@@@ -901,7 -901,7 +901,7 @@@ fprintf(fp, "Support for:")
  #ifndef DISABLE_OCSP
    fprintf(fp, " OCSP");
  #endif
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
    fprintf(fp, " PIPE_CONNECT");
  #endif
  #ifndef DISABLE_PRDR
    fprintf(fp, " DMARC");
  #endif
  #ifdef TCP_FASTOPEN
-   deliver_init();
+   tcp_init();
    if (f.tcp_fastopen_ok) fprintf(fp, " TCP_Fast_Open");
  #endif
  #ifdef EXPERIMENTAL_LMDB
  #ifdef EXPERIMENTAL_QUEUEFILE
    fprintf(fp, " Experimental_QUEUEFILE");
  #endif
- #ifdef EXPERIMENTAL_SRS
+ #if defined(EXPERIMENTAL_SRS) || defined(EXPERIMENTAL_SRS_NATIVE)
    fprintf(fp, " Experimental_SRS");
  #endif
  #ifdef EXPERIMENTAL_ARC
@@@ -2787,11 -2787,6 +2787,11 @@@ for (i = 1; i < argc; i++
        msg_action = MSG_DELIVER;
        deliver_give_up = TRUE;
        }
 +   else if (Ustrcmp(argrest, "G") == 0)
 +      {
 +      msg_action = MSG_SETQUEUE;
 +      queue_name_dest = argv[++i];
 +      }
      else if (Ustrcmp(argrest, "mad") == 0)
        {
        msg_action = MSG_MARK_ALL_DELIVERED;
  
      else if (Ustrcmp(argrest, "o") == 0) {}
  
-     /* -oP <name>: set pid file path for daemon */
+     /* -oP <name>: set pid file path for daemon
+        -oPX:       delete pid file of daemon */
  
      else if (Ustrcmp(argrest, "P") == 0)
        override_pid_file_path = argv[++i];
  
+     else if (Ustrcmp(argrest, "PX") == 0)
+       delete_pid_file();
      /* -or <n>: set timeout for non-SMTP acceptance
         -os <n>: set timeout for SMTP acceptance */
  
@@@ -4069,13 -4068,11 +4073,13 @@@ count. Only an admin user can use the t
  if (!f.admin_user)
    {
    BOOL debugset = (debug_selector & ~D_v) != 0;
 -  if (deliver_give_up || f.daemon_listen || malware_test_file ||
 -     (count_queue && queue_list_requires_admin) ||
 -     (list_queue && queue_list_requires_admin) ||
 -     (queue_interval >= 0 && prod_requires_admin) ||
 -     (debugset && !f.running_in_test_harness))
 +  if (  deliver_give_up || f.daemon_listen || malware_test_file
 +     || count_queue && queue_list_requires_admin
 +     || list_queue && queue_list_requires_admin
 +     || queue_interval >= 0 && prod_requires_admin
 +     || queue_name_dest && prod_requires_admin
 +     || debugset && !f.running_in_test_harness
 +     )
      exim_fail("exim:%s permission denied\n", debugset? " debugging" : "");
    }
  
@@@ -4172,9 -4169,13 +4176,9 @@@ now for those OS that require the firs
  root. There will be further calls later for each message received. */
  
  #ifdef LOAD_AVG_NEEDS_ROOT
 -if (receiving_message &&
 -      (queue_only_load >= 0 ||
 -        (f.is_inetd && smtp_load_reserve >= 0)
 -      ))
 -  {
 +if (  receiving_message
 +   && (queue_only_load >= 0 || (f.is_inetd && smtp_load_reserve >= 0)))
    load_average = OS_GETLOADAVG();
 -  }
  #endif
  
  /* The queue_only configuration option can be overridden by -odx on the command
@@@ -4300,11 -4301,6 +4304,11 @@@ if (msg_action_arg > 0 && msg_action !
      for (i = msg_action_arg; i < argc; i++)
        if (!queue_action(argv[i], msg_action, NULL, 0, 0))
          yield = EXIT_FAILURE;
 +    switch (msg_action)
 +      {
 +      case MSG_REMOVE: MSG_DELETE: case MSG_FREEZE: case MSG_THAW: break;
 +      default: printf("\n"); break;
 +      }
      }
  
    else if (!queue_action(argv[msg_action_arg], msg_action, argv, argc,
@@@ -4488,31 -4484,9 +4492,9 @@@ if (list_config
    }
  
  
- /* Initialise subsystems as required */
- #ifndef DISABLE_DKIM
-   {
- # ifdef MEASURE_TIMING
-   struct timeval t0;
-   gettimeofday(&t0, NULL);
- # endif
-   dkim_exim_init();
- # ifdef MEASURE_TIMING
-   report_time_since(&t0, US"dkim_exim_init (delta)");
- # endif
-   }
- #endif
-   {
- #ifdef MEASURE_TIMING
-   struct timeval t0;
-   gettimeofday(&t0, NULL);
- #endif
-   deliver_init();
- #ifdef MEASURE_TIMING
-   report_time_since(&t0, US"deliver_init (delta)");
- #endif
-   }
+ /* Initialise subsystems as required. */
  
+ tcp_init();
  
  /* Handle a request to deliver one or more messages that are already on the
  queue. Values of msg_action other than MSG_DELIVER and MSG_LOAD are dealt with
@@@ -4707,6 -4681,23 +4689,23 @@@ if (f.daemon_listen || f.inetd_wait_mod
      log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Daemon cannot be run when "
        "mua_wrapper is set");
      }
+ # ifndef DISABLE_TLS
+   /* This also checks that the library linkage is working and we can call
+   routines in it, so call even if tls_require_ciphers is unset */
+     {
+ # ifdef MEASURE_TIMING
+     struct timeval t0, diff;
+     (void)gettimeofday(&t0, NULL);
+ # endif
+     if (!tls_dropprivs_validate_require_cipher(FALSE))
+       exit(1);
+ # ifdef MEASURE_TIMING
+     report_time_since(&t0, US"validate_ciphers (delta)");
+ # endif
+     }
+ #endif
    daemon_go();
    }
  
@@@ -4822,8 -4813,9 +4821,9 @@@ if (verify_address_mode || f.address_te
      {
      while (recipients_arg < argc)
        {
-       uschar *s = argv[recipients_arg++];
-       while (*s != 0)
+       /* Supplied addresses are tainted since they come from a user */
+       uschar * s = string_copy_taint(argv[recipients_arg++], TRUE);
+       while (*s)
          {
          BOOL finished = FALSE;
          uschar *ss = parse_find_address_end(s, FALSE);
          test_address(s, flags, &exit_value);
          s = ss;
          if (!finished)
-           while (*(++s) != 0 && (*s == ',' || isspace(*s)));
+           while (*++s == ',' || isspace(*s)) ;
          }
        }
      }
  
    else for (;;)
      {
-     uschar *s = get_stdinput(NULL, NULL);
-     if (s == NULL) break;
-     test_address(s, flags, &exit_value);
+     uschar * s = get_stdinput(NULL, NULL);
+     if (!s) break;
+     test_address(string_copy_taint(s, TRUE), flags, &exit_value);
      }
  
    route_tidyup();
@@@ -5334,13 -5326,13 +5334,13 @@@ while (more
  
      raw_sender = string_copy(sender_address);
  
-     /* Loop for each argument */
+     /* Loop for each argument (supplied by user hence tainted) */
  
      for (int i = 0; i < count; i++)
        {
        int start, end, domain;
-       uschar *errmess;
-       uschar *s = list[i];
+       uschar * errmess;
+       uschar * s = string_copy_taint(list[i], TRUE);
  
        /* Loop for each comma-separated address */
  
diff --combined src/src/functions.h
index f4fcd1e19677f0e3bc68159178856777713581fd,da21b87795be02b3a5c761e74b33d78092eb1d8e..ea3cf257c5f6869043b38a2959b08683c4953520
@@@ -48,13 -48,13 +48,14 @@@ extern uschar * tls_cert_fprt_md5(void 
  extern uschar * tls_cert_fprt_sha1(void *);
  extern uschar * tls_cert_fprt_sha256(void *);
  
 +extern void    tls_clean_env(void);
  extern BOOL    tls_client_start(client_conn_ctx *, smtp_connect_args *,
                  void *, tls_support *, uschar **);
  
  extern void    tls_close(void *, int);
  extern BOOL    tls_could_read(void);
  extern void    tls_daemon_init(void);
+ extern BOOL    tls_dropprivs_validate_require_cipher(BOOL);
  extern BOOL    tls_export_cert(uschar *, size_t, void *);
  extern int     tls_feof(void);
  extern int     tls_ferror(void);
@@@ -136,6 -136,7 +137,7 @@@ extern gstring *authres_spf(gstring *)
  #endif
  
  extern uschar *b64encode(const uschar *, int);
+ extern uschar *b64encode_taint(const uschar *, int, BOOL);
  extern int     b64decode(const uschar *, uschar **);
  extern int     bdat_getc(unsigned);
  extern uschar *bdat_getbuf(unsigned *);
@@@ -175,8 -176,8 +177,8 @@@ extern void    debug_print_tree(tree_no
  extern void    debug_vprintf(int, const char *, va_list);
  extern void    decode_bits(unsigned int *, size_t, int *,
                   uschar *, bit_table *, int, uschar *, int);
+ extern void    delete_pid_file(void);
  extern address_item *deliver_make_addr(uschar *, BOOL);
- extern void    deliver_init(void);
  extern void    delivery_log(int, address_item *, int, uschar *);
  extern int     deliver_message(uschar *, BOOL, BOOL);
  extern void    deliver_msglog(const char *, ...) PRINTF_FUNCTION(1,2);
@@@ -197,8 -198,8 +199,8 @@@ extern BOOL    dkim_transport_write_mes
  #endif
  extern dns_address *dns_address_from_rr(dns_answer *, dns_record *);
  extern int     dns_basic_lookup(dns_answer *, const uschar *, int);
 -extern void    dns_build_reverse(const uschar *, uschar *);
 -extern time_t  dns_expire_from_soa(dns_answer *);
 +extern uschar *dns_build_reverse(const uschar *);
 +extern time_t  dns_expire_from_soa(dns_answer *, int);
  extern void    dns_init(BOOL, BOOL, BOOL);
  extern BOOL    dns_is_aa(const dns_answer *);
  extern BOOL    dns_is_secure(const dns_answer *);
@@@ -446,6 -447,7 +448,7 @@@ extern void    smtp_command_timeout_exi
  extern void    smtp_command_sigterm_exit(void) NORETURN;
  extern void    smtp_data_timeout_exit(void) NORETURN;
  extern void    smtp_data_sigint_exit(void) NORETURN;
+ extern void    smtp_deliver_init(void);
  extern uschar *smtp_cmd_hist(void);
  extern int     smtp_connect(smtp_connect_args *, const blob *);
  extern int     smtp_sock_connect(host_item *, int, int, uschar *,
@@@ -539,7 -541,7 +542,8 @@@ extern int     strcmpic(const uschar *
  extern int     strncmpic(const uschar *, const uschar *, int);
  extern uschar *strstric(uschar *, uschar *, BOOL);
  
 +extern int     test_harness_fudged_queue_time(int);
+ extern void    tcp_init(void);
  #ifdef EXIM_TFO_PROBE
  extern void    tfo_probe(void);
  #endif
@@@ -878,33 -880,20 +882,33 @@@ return string_sprintf("%s/%s/%s/%s"
  # endif
  
  static inline uschar *
 -spool_sname(const uschar * purpose, uschar * subdir)
 +spool_q_sname(const uschar * purpose, const uschar * q, uschar * subdir)
  {
  return string_sprintf("%s%s%s%s%s",
 -                  queue_name, *queue_name ? "/" : "",
 +                  q, *q ? "/" : "",
                    purpose,
                    *subdir ? "/" : "", subdir);
  }
  
 +static inline uschar *
 +spool_sname(const uschar * purpose, uschar * subdir)
 +{
 +return spool_q_sname(purpose, queue_name, subdir);
 +}
 +
 +static inline uschar *
 +spool_q_fname(const uschar * purpose, const uschar * q,
 +      const uschar * subdir, const uschar * fname, const uschar * suffix)
 +{
 +return string_sprintf("%s/%s/%s/%s/%s%s",
 +      spool_directory, q, purpose, subdir, fname, suffix);
 +}
 +
  static inline uschar *
  spool_fname(const uschar * purpose, const uschar * subdir, const uschar * fname,
                const uschar * suffix)
  {
 -return string_sprintf("%s/%s/%s/%s/%s%s",
 -      spool_directory, queue_name, purpose, subdir, fname, suffix);
 +return spool_q_fname(purpose, queue_name, subdir, fname, suffix);
  }
  
  static inline void
@@@ -937,7 -926,7 +941,7 @@@ static uschar buf[sizeof("0.000s")]
  if (diff->tv_sec >= 5 || !LOGGING(millisec))
    return readconf_printtime((int)diff->tv_sec);
  
 -sprintf(CS buf, "%u.%03us", (uint)diff->tv_sec, (uint)diff->tv_usec/1000);
 +snprintf(CS buf, sizeof(buf), "%u.%03us", (uint)diff->tv_sec, (uint)diff->tv_usec/1000);
  return buf;
  }
  
diff --combined src/src/globals.c
index 358c380a8c2f7d47631b976af7fddaa25f9ad235,8162de0c4d215368ecf660ddfe9fa2e0c2c1029f..de1149b6cd4e61fe41638e2d9ac8e20da80b8e0b
@@@ -238,6 -238,7 +238,7 @@@ struct global_flags f 
        .disable_logging        = FALSE,
  #ifndef DISABLE_DKIM
        .dkim_disable_verify      = FALSE,
+       .dkim_init_done           = FALSE,
  #endif
  #ifdef SUPPORT_DMARC
        .dmarc_has_been_checked  = FALSE,
        .sender_name_forced     = FALSE,
        .sender_set_untrusted   = FALSE,
        .smtp_authenticated     = FALSE,
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
        .smtp_in_early_pipe_advertised = FALSE,
        .smtp_in_early_pipe_no_auth = FALSE,
        .smtp_in_early_pipe_used = FALSE,
@@@ -830,7 -831,7 +831,7 @@@ void   *dkim_signatures             = NULL
  uschar *dkim_signers             = NULL;
  uschar *dkim_signing_domain      = NULL;
  uschar *dkim_signing_selector    = NULL;
- uschar *dkim_verify_hashes       = US"sha256:sha512:sha1";
+ uschar *dkim_verify_hashes       = US"sha256:sha512";
  uschar *dkim_verify_keytypes     = US"ed25519:rsa";
  BOOL  dkim_verify_minimal      = FALSE;
  uschar *dkim_verify_overall      = NULL;
@@@ -1167,7 -1168,7 +1168,7 @@@ uschar *override_pid_file_path = NULL
  uschar *percent_hack_domains   = NULL;
  uschar *pid_file_path          = US PID_FILE_PATH
                             "\0<--------------Space to patch pid_file_path->";
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
  uschar *pipe_connect_advertise_hosts = US"*";
  #endif
  uschar *pipelining_advertise_hosts = US"*";
@@@ -1194,7 -1195,6 +1195,7 @@@ uschar *qualify_domain_sender  = NULL
  uschar *queue_domains          = NULL;
  int     queue_interval         = -1;
  uschar *queue_name             = US"";
 +uschar *queue_name_dest        = NULL;
  uschar *queue_only_file        = NULL;
  int     queue_only_load        = -1;
  uschar *queue_run_max          = US"5";
@@@ -1259,7 -1259,7 +1260,7 @@@ const pcre *regex_From         = NULL
  const pcre *regex_IGNOREQUOTA  = NULL;
  const pcre *regex_PIPELINING   = NULL;
  const pcre *regex_SIZE         = NULL;
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
  const pcre *regex_EARLY_PIPE   = NULL;
  #endif
  const pcre *regex_ismsgid      = NULL;
@@@ -1364,7 -1364,7 +1365,7 @@@ router_instance  router_defaults = 
      .pass_router =            NULL,
      .redirect_router =                NULL,
  
 -    .dnssec =                 { NULL, NULL },            /* dnssec_domains {require,request} */
 +    .dnssec =                   { .request= US"*", .require=NULL },
  };
  
  uschar *router_name            = NULL;
@@@ -1499,6 -1499,9 +1500,9 @@@ uschar *srs_recipient          = NULL
  uschar *srs_secrets            = NULL;
  uschar *srs_status             = NULL;
  #endif
+ #ifdef EXPERIMENTAL_SRS_NATIVE
+ uschar *srs_recipient          = NULL;
+ #endif
  int     string_datestamp_offset= -1;
  int     string_datestamp_length= 0;
  int     string_datestamp_type  = -1;
diff --combined src/src/globals.h
index ca342acc2aa1c222e5b706ecc9d03ea193efbe86,8a3d4c56f077ec4013c474326695841380aecae1..03a56f0f2a36e8df766ecfbc1406a8687d7af275
@@@ -97,6 -97,7 +97,7 @@@ typedef struct 
    void         *peercert;           /* Certificate of peer, binary */
    uschar *peerdn;             /* DN from peer */
    uschar *sni;                /* Server Name Indication */
+   uschar *channelbinding;     /* b64'd data identifying channel, for authenticators */
    enum {
      OCSP_NOT_REQ=0,           /* not requested */
      OCSP_NOT_RESP,            /* no response to request */
@@@ -120,7 -121,6 +121,6 @@@ extern BOOL    gnutls_allow_auto_pkcs11
  extern uschar *openssl_options;        /* OpenSSL compatibility options */
  extern const pcre *regex_STARTTLS;     /* For recognizing STARTTLS settings */
  extern uschar *tls_certificate;        /* Certificate file */
- extern uschar *tls_channelbinding_b64; /* string of base64 channel binding */
  extern uschar *tls_crl;                /* CRL File */
  extern int     tls_dh_max_bits;        /* don't accept higher lib suggestions */
  extern uschar *tls_dhparam;            /* DH param file */
@@@ -199,6 -199,7 +199,7 @@@ extern struct global_flags 
   BOOL   disable_logging                       :1; /* Disables log writing when TRUE */
  #ifndef DISABLE_DKIM
   BOOL   dkim_disable_verify           :1; /* Set via ACL control statement. When set, DKIM verification is disabled for the current message */
+  BOOL   dkim_init_done                        :1; /* lazy-init status */
  #endif
  #ifdef SUPPORT_DMARC
   BOOL   dmarc_has_been_checked                :1; /* Global variable to check if test has been called yet */
   BOOL   sender_name_forced            :1; /* Set by -F */
   BOOL   sender_set_untrusted          :1; /* Sender set by untrusted caller */
   BOOL   smtp_authenticated            :1; /* Sending client has authenticated */
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
   BOOL   smtp_in_early_pipe_advertised :1; /* server advertised PIPE_CONNECT */
   BOOL smtp_in_early_pipe_no_auth      :1; /* too many authenticator names */
   BOOL   smtp_in_early_pipe_used               :1; /* client did send early data */
@@@ -750,7 -751,7 +751,7 @@@ extern uschar *override_pid_file_path; 
  
  extern uschar *percent_hack_domains;   /* Local domains for which '% operates */
  extern uschar *pid_file_path;          /* For writing daemon pids */
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
  extern uschar *pipe_connect_advertise_hosts; /* for banner/EHLO pipelining */
  #endif
  extern uschar *pipelining_advertise_hosts; /* As it says */
@@@ -788,7 -789,6 +789,7 @@@ extern pid_t   queue_run_pid;          
  extern int     queue_run_pipe;         /* Pipe for synchronizing */
  extern int     queue_interval;         /* Queue running interval */
  extern uschar *queue_name;             /* Name of queue, if nondefault spooling */
 +extern uschar *queue_name_dest;              /* Destination queue, for moving messages */
  extern BOOL    queue_only;             /* TRUE to disable immediate delivery */
  extern int     queue_only_load;        /* Max load before auto-queue */
  extern BOOL    queue_only_load_latch;  /* Latch queue_only_load TRUE */
@@@ -834,7 -834,7 +835,7 @@@ extern const pcre  *regex_CHUNKING;    
  extern const pcre  *regex_IGNOREQUOTA; /* For recognizing IGNOREQUOTA (LMTP) */
  extern const pcre  *regex_PIPELINING;  /* For recognizing PIPELINING */
  extern const pcre  *regex_SIZE;        /* For recognizing SIZE settings */
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
  extern const pcre  *regex_EARLY_PIPE;  /* For recognizing PIPE_CONNCT */
  #endif
  extern const pcre  *regex_ismsgid;     /* Compiled r.e. for message it */
@@@ -987,6 -987,9 +988,9 @@@ extern uschar *srs_status;             
  extern BOOL    srs_usehash;            /* SRS use hash flag */
  extern BOOL    srs_usetimestamp;       /* SRS use timestamp flag */
  #endif
+ #ifdef EXPERIMENTAL_SRS_NATIVE
+ extern uschar *srs_recipient;          /* SRS recipient */
+ #endif
  extern BOOL    strict_acl_vars;        /* ACL variables have to be set before being used */
  extern int     string_datestamp_offset;/* After insertion by string_format */
  extern int     string_datestamp_length;/* After insertion by string_format */
diff --combined src/src/macros.h
index 76913d64ed37c8f62f95e581dd224ad650172232,a9653f45ba2ed2518d04a7d255ecbe0893f137a8..cc96c85163f84e9df39e0fe3303f96fafe3066ca
@@@ -550,11 -550,8 +550,8 @@@ table exim_errstrings[] in log.c *
  #define ERRNO_DATA4XX        (-46)   /* DATA gave 4xx error */
  #define ERRNO_PROXYFAIL      (-47)   /* Negotiation failed for proxy configured host */
  #define ERRNO_AUTHPROB       (-48)   /* Authenticator "other" failure */
- #ifdef SUPPORT_I18N
- # define ERRNO_UTF8_FWD      (-49)   /* target not supporting SMTPUTF8 */
- #endif
-                               /* -50 free for re-use */
+ #define ERRNO_UTF8_FWD       (-49)   /* target not supporting SMTPUTF8 */
+ #define ERRNO_HOST_IS_LOCAL  (-50)   /* Transport refuses to talk to localhost */
  
  /* These must be last, so all retry deferments can easily be identified */
  
@@@ -845,7 -842,7 +842,7 @@@ enum 
  
  enum { MSG_DELIVER, MSG_FREEZE, MSG_REMOVE, MSG_THAW, MSG_ADD_RECIPIENT,
         MSG_MARK_ALL_DELIVERED, MSG_MARK_DELIVERED, MSG_EDIT_SENDER,
 -       MSG_SHOW_COPY, MSG_LOAD,
 +       MSG_SHOW_COPY, MSG_LOAD, MSG_SETQUEUE,
         /* These ones must be last: a test for >= MSG_SHOW_BODY is used
         to test for actions that list individual spool files. */
         MSG_SHOW_BODY, MSG_SHOW_HEADER, MSG_SHOW_LOG };
diff --combined src/src/readconf.c
index 0233019cf6eb155fcff56dde3c431ceca498a2a0,713a1a9efe9fb0b898881548d93b0f489fc9333e..08014c9af480ef00993feda19e487044a688613d
@@@ -244,7 -244,7 +244,7 @@@ static optionlist optionlist_config[] 
  #endif
    { "pid_file_path",            opt_stringptr,   &pid_file_path },
    { "pipelining_advertise_hosts", opt_stringptr, &pipelining_advertise_hosts },
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
    { "pipelining_connect_advertise_hosts", opt_stringptr,
                                                 &pipe_connect_advertise_hosts },
  #endif
@@@ -3074,80 -3074,6 +3074,6 @@@ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "m
  
  
  
- /*************************************************
- *       Drop privs for checking TLS config      *
- *************************************************/
- /* We want to validate TLS options during readconf, but do not want to be
- root when we call into the TLS library, in case of library linkage errors
- which cause segfaults; before this check, those were always done as the Exim
- runtime user and it makes sense to continue with that.
- Assumes:  tls_require_ciphers has been set, if it will be
-           exim_user has been set, if it will be
-           exim_group has been set, if it will be
- Returns:  bool for "okay"; false will cause caller to immediately exit.
- */
- #ifndef DISABLE_TLS
- static BOOL
- tls_dropprivs_validate_require_cipher(BOOL nowarn)
- {
- const uschar *errmsg;
- pid_t pid;
- int rc, status;
- void (*oldsignal)(int);
- /* If TLS will never be used, no point checking ciphers */
- if (  !tls_advertise_hosts
-    || !*tls_advertise_hosts
-    || Ustrcmp(tls_advertise_hosts, ":") == 0
-    )
-   return TRUE;
- else if (!nowarn && !tls_certificate)
-   log_write(0, LOG_MAIN,
-     "Warning: No server certificate defined; will use a selfsigned one.\n"
-     " Suggested action: either install a certificate or change tls_advertise_hosts option");
- oldsignal = signal(SIGCHLD, SIG_DFL);
- fflush(NULL);
- if ((pid = fork()) < 0)
-   log_write(0, LOG_MAIN|LOG_PANIC_DIE, "fork failed for TLS check");
- if (pid == 0)
-   {
-   /* in some modes, will have dropped privilege already */
-   if (!geteuid())
-     exim_setugid(exim_uid, exim_gid, FALSE,
-         US"calling tls_validate_require_cipher");
-   if ((errmsg = tls_validate_require_cipher()))
-     log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
-         "tls_require_ciphers invalid: %s", errmsg);
-   fflush(NULL);
-   exim_underbar_exit(0);
-   }
- do {
-   rc = waitpid(pid, &status, 0);
- } while (rc < 0 && errno == EINTR);
- DEBUG(D_tls)
-   debug_printf("tls_validate_require_cipher child %d ended: status=0x%x\n",
-       (int)pid, status);
- signal(SIGCHLD, oldsignal);
- return status == 0;
- }
- #endif /*DISABLE_TLS*/
  /*************************************************
  *         Read main configuration options        *
  *************************************************/
@@@ -3344,6 -3270,19 +3270,6 @@@ if (f.trusted_config && Ustrcmp(filenam
      }
    }
  
 -/* Do a dummy store-allocation of a size related to the (toplevel) file size.
 -This assumes we will need this much storage to handle all the allocations
 -during startup; it won't help when .include is being used.  When it does, it
 -will cut down on the number of store blocks (and malloc calls, and sbrk
 -syscalls).  It also assume we're on the relevant pool. */
 -
 -if (statbuf.st_size > 8192)
 -  {
 -  rmark r = store_mark();
 -  void * dummy = store_get((int)statbuf.st_size, FALSE);
 -  store_reset(r);
 -  }
 -
  /* Process the main configuration settings. They all begin with a lower case
  letter. If we see something starting with an upper case letter, it is taken as
  a macro definition. */
@@@ -3645,11 -3584,6 +3571,6 @@@ if ((tls_verify_hosts || tls_try_verify
      "tls_%sverify_hosts is set, but tls_verify_certificates is not set",
      tls_verify_hosts ? "" : "try_");
  
- /* This also checks that the library linkage is working and we can call
- routines in it, so call even if tls_require_ciphers is unset */
- if (!tls_dropprivs_validate_require_cipher(nowarn))
-   exit(1);
  /* Magic number: at time of writing, 1024 has been the long-standing value
  used by so many clients, and what Exim used to use always, that it makes
  sense to just min-clamp this max-clamp at that. */
@@@ -4222,7 -4156,7 +4143,7 @@@ Returns:     nothin
  static void
  auths_init(void)
  {
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
  int nauths = 0;
  #endif
  
@@@ -4248,11 -4182,11 +4169,11 @@@ for (auth_instance * au = auths; au; a
            "(%s and %s) have the same public name (%s)",
            au->client ? US"client" : US"server", au->name, bu->name,
            au->public_name);
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
    nauths++;
  #endif
    }
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
  f.smtp_in_early_pipe_no_auth = nauths > 16;
  #endif
  }
diff --combined src/src/smtp_in.c
index f37b2f6e0f1b5f7a9a8509593925040421c2de31,580fe9f91f88097a9cd9d4d79b637aa1421c4bc1..9e2b39c4e37d93694a0995a69ece91db3f68375b
@@@ -142,7 -142,7 +142,7 @@@ static struct 
    BOOL helo_verify                    :1;
    BOOL helo_seen                      :1;
    BOOL helo_accept_junk                       :1;
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
    BOOL pipe_connect_acceptable                :1;
  #endif
    BOOL rcpt_smtp_response_same                :1;
@@@ -397,7 -397,7 +397,7 @@@ return TRUE
  }
  
  
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
  static BOOL
  pipeline_connect_sends(void)
  {
@@@ -877,8 -877,6 +877,8 @@@ flush for non-TLS connections. The smtp
  checking that: for convenience, TLS output errors are remembered here so that
  they are also picked up later by smtp_fflush().
  
 +This function is exposed to the local_scan API; do not change the signature.
 +
  Arguments:
    format      format string
    more              further data expected
@@@ -899,10 -897,7 +899,10 @@@ va_end(ap)
  
  /* This is split off so that verify.c:respond_printf() can, in effect, call
  smtp_printf(), bearing in mind that in C a vararg function can't directly
 -call another vararg function, only a function which accepts a va_list. */
 +call another vararg function, only a function which accepts a va_list.
 +
 +This function is exposed to the local_scan API; do not change the signature.
 +*/
  /*XXX consider passing caller-info in, for string_vformat-onward */
  
  void
@@@ -952,16 -947,13 +952,13 @@@ if (fl.rcpt_in_progress
  
  /* Now write the string */
  
+ if (
  #ifndef DISABLE_TLS
- if (tls_in.active.sock >= 0)
-   {
-   if (tls_write(NULL, gs.s, gs.ptr, more) < 0)
-     smtp_write_error = -1;
-   }
- else
+     tls_in.active.sock >= 0 ? (tls_write(NULL, gs.s, gs.ptr, more) < 0) :
  #endif
- if (fprintf(smtp_out, "%s", gs.s) < 0) smtp_write_error = -1;
+     (fwrite(gs.s, gs.ptr, 1, smtp_out) == 0)
+    )
+     smtp_write_error = -1;
  }
  
  
  
  /* This function isn't currently used within Exim (it detects errors when it
  tries to read the next SMTP input), but is available for use in local_scan().
- For non-TLS connections, it flushes the output and checks for errors. For
- TLS-connections, it checks for a previously-detected TLS write error.
+ It flushes the output and checks for errors.
  
  Arguments:  none
  Returns:    0 for no error; -1 after an error
@@@ -983,6 -974,15 +979,15 @@@ in
  smtp_fflush(void)
  {
  if (tls_in.active.sock < 0 && fflush(smtp_out) != 0) smtp_write_error = -1;
+ if (
+ #ifndef DISABLE_TLS
+     tls_in.active.sock >= 0 ? (tls_write(NULL, NULL, 0, FALSE) < 0) :
+ #endif
+     (fflush(smtp_out) != 0)
+    )
+     smtp_write_error = -1;
  return smtp_write_error;
  }
  
@@@ -2400,24 -2400,47 +2405,47 @@@ return FALSE
  static void
  tfo_in_check(void)
  {
- # ifdef TCP_INFO
+ # ifdef __FreeBSD__
+ int is_fastopen;
+ socklen_t len = sizeof(is_fastopen);
+ /* The tinfo TCPOPT_FAST_OPEN bit seems unreliable, and we don't see state
+ TCP_SYN_RCV (as of 12.1) so no idea about data-use. */
+ if (getsockopt(fileno(smtp_out), IPPROTO_TCP, TCP_FASTOPEN, &is_fastopen, &len) == 0)
+   {
+   if (is_fastopen) 
+     {
+     DEBUG(D_receive)
+       debug_printf("TFO mode connection (TCP_FASTOPEN getsockopt)\n");
+     f.tcp_in_fastopen = TRUE;
+     }
+   }
+ else DEBUG(D_receive)
+   debug_printf("TCP_INFO getsockopt: %s\n", strerror(errno));
+ # elif defined(TCP_INFO)
  struct tcp_info tinfo;
  socklen_t len = sizeof(tinfo);
  
  if (getsockopt(fileno(smtp_out), IPPROTO_TCP, TCP_INFO, &tinfo, &len) == 0)
- #ifdef TCPI_OPT_SYN_DATA      /* FreeBSD 11 does not seem to have this yet */
+ #  ifdef TCPI_OPT_SYN_DATA    /* FreeBSD 11,12 do not seem to have this yet */
    if (tinfo.tcpi_options & TCPI_OPT_SYN_DATA)
      {
-     DEBUG(D_receive) debug_printf("TCP_FASTOPEN mode connection (ACKd data-on-SYN)\n");
+     DEBUG(D_receive)
+       debug_printf("TFO mode connection (ACKd data-on-SYN)\n");
      f.tcp_in_fastopen_data = f.tcp_in_fastopen = TRUE;
      }
    else
- #endif
-     if (tinfo.tcpi_state == TCP_SYN_RECV)
+ #  endif
+     if (tinfo.tcpi_state == TCP_SYN_RECV)     /* Not seen on FreeBSD 12.1 */
      {
-     DEBUG(D_receive) debug_printf("TCP_FASTOPEN mode connection (state TCP_SYN_RECV)\n");
+     DEBUG(D_receive)
+       debug_printf("TFO mode connection (state TCP_SYN_RECV)\n");
      f.tcp_in_fastopen = TRUE;
      }
+ else DEBUG(D_receive)
+   debug_printf("TCP_INFO getsockopt: %s\n", strerror(errno));
  # endif
  }
  #endif
@@@ -2997,7 -3020,7 +3025,7 @@@ while (*p)
  /* Before we write the banner, check that there is no input pending, unless
  this synchronisation check is disabled. */
  
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
  fl.pipe_connect_acceptable =
    sender_host_address && verify_check_host(&pipe_connect_advertise_hosts) == OK;
  
@@@ -3024,7 -3047,7 +3052,7 @@@ if (!check_sync()
  /*XXX the ehlo-resp code does its own tls/nontls bit.  Maybe subroutine that? */
  
  smtp_printf("%s",
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
    fl.pipe_connect_acceptable && pipeline_connect_sends(),
  #else
    FALSE,
  handshake arrived.  If so we must have managed a TFO. */
  
  #ifdef TCP_FASTOPEN
- tfo_in_check();
if (sender_host_address && !f.sender_host_notsocket) tfo_in_check();
  #endif
  
  return TRUE;
@@@ -3719,60 -3742,60 +3747,60 @@@ if (rc != OK
  switch(rc)
    {
    case OK:
-   if (!au->set_id || set_id)    /* Complete success */
-     {
-     if (set_id) authenticated_id = string_copy_perm(set_id, TRUE);
-     sender_host_authenticated = au->name;
-     sender_host_auth_pubname  = au->public_name;
-     authentication_failed = FALSE;
-     authenticated_fail_id = NULL;   /* Impossible to already be set? */
-     received_protocol =
-       (sender_host_address ? protocols : protocols_local)
-       [pextend + pauthed + (tls_in.active.sock >= 0 ? pcrpted:0)];
-     *s = *ss = US"235 Authentication succeeded";
-     authenticated_by = au;
-     break;
-     }
+     if (!au->set_id || set_id)    /* Complete success */
+       {
+       if (set_id) authenticated_id = string_copy_perm(set_id, TRUE);
+       sender_host_authenticated = au->name;
+       sender_host_auth_pubname  = au->public_name;
+       authentication_failed = FALSE;
+       authenticated_fail_id = NULL;   /* Impossible to already be set? */
  
-   /* Authentication succeeded, but we failed to expand the set_id string.
-   Treat this as a temporary error. */
+       received_protocol =
+       (sender_host_address ? protocols : protocols_local)
+         [pextend + pauthed + (tls_in.active.sock >= 0 ? pcrpted:0)];
+       *s = *ss = US"235 Authentication succeeded";
+       authenticated_by = au;
+       break;
+       }
+     /* Authentication succeeded, but we failed to expand the set_id string.
+     Treat this as a temporary error. */
  
-   auth_defer_msg = expand_string_message;
-   /* Fall through */
+     auth_defer_msg = expand_string_message;
+     /* Fall through */
  
    case DEFER:
-   if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE);
-   *s = string_sprintf("435 Unable to authenticate at present%s",
-     auth_defer_user_msg);
-   *ss = string_sprintf("435 Unable to authenticate at present%s: %s",
-     set_id, auth_defer_msg);
-   break;
+     if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE);
+     *s = string_sprintf("435 Unable to authenticate at present%s",
+       auth_defer_user_msg);
+     *ss = string_sprintf("435 Unable to authenticate at present%s: %s",
+       set_id, auth_defer_msg);
+     break;
  
    case BAD64:
-   *s = *ss = US"501 Invalid base64 data";
-   break;
+     *s = *ss = US"501 Invalid base64 data";
+     break;
  
    case CANCELLED:
-   *s = *ss = US"501 Authentication cancelled";
-   break;
+     *s = *ss = US"501 Authentication cancelled";
+     break;
  
    case UNEXPECTED:
-   *s = *ss = US"553 Initial data not expected";
-   break;
+     *s = *ss = US"553 Initial data not expected";
+     break;
  
    case FAIL:
-   if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE);
-   *s = US"535 Incorrect authentication data";
-   *ss = string_sprintf("535 Incorrect authentication data%s", set_id);
-   break;
+     if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE);
+     *s = US"535 Incorrect authentication data";
+     *ss = string_sprintf("535 Incorrect authentication data%s", set_id);
+     break;
  
    default:
-   if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE);
-   *s = US"435 Internal error";
-   *ss = string_sprintf("435 Internal error%s: return %d from authentication "
-     "check", set_id, rc);
-   break;
+     if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE);
+     *s = US"435 Internal error";
+     *ss = string_sprintf("435 Internal error%s: return %d from authentication "
+       "check", set_id, rc);
+     break;
    }
  
  return rc;
@@@ -3975,7 -3998,7 +4003,7 @@@ while (done <= 0
  #endif
  
    switch(smtp_read_command(
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
          !fl.pipe_connect_acceptable,
  #else
          TRUE,
          host_build_sender_fullhost();  /* Rebuild */
          break;
          }
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
        else if (!fl.pipe_connect_acceptable && !check_sync())
  #else
        else if (!check_sync())
          sync_cmd_limit = NON_SYNC_CMD_PIPELINING;
          f.smtp_in_pipelining_advertised = TRUE;
  
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
          if (fl.pipe_connect_acceptable)
            {
            f.smtp_in_early_pipe_advertised = TRUE;
  #ifndef DISABLE_TLS
        if (tls_in.active.sock >= 0)
        (void)tls_write(NULL, g->s, g->ptr,
- # ifdef SUPPORT_PIPE_CONNECT
+ # ifndef DISABLE_PIPE_CONNECT
                        fl.pipe_connect_acceptable && pipeline_connect_sends());
  # else
                        FALSE);
        f.dot_ends = TRUE;
  
      DATA_BDAT:                /* Common code for DATA and BDAT */
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
        fl.pipe_connect_acceptable = FALSE;
  #endif
        if (!discarded && recipients_count <= 0)
diff --combined src/src/tls-gnu.c
index fc426a25114b4f00ad3ac94f7cc92c6a517dea28,7b0f2f6adb01248dd0b4c273d2c6ba0a2104d459..837b991df47e906092f51ef9af9845df962ab046
@@@ -74,12 -74,6 +74,12 @@@ require current GnuTLS, then we'll dro
  # define GNUTLS_AUTO_GLOBAL_INIT
  # define GNUTLS_AUTO_PKCS11_MANUAL
  #endif
 +#if (GNUTLS_VERSION_NUMBER >= 0x030404) \
 +  || (GNUTLS_VERSION_NUMBER >= 0x030311) && (GNUTLS_VERSION_NUMBER & 0xffff00 == 0x030300)
 +# ifndef DISABLE_OCSP
 +#  define EXIM_HAVE_OCSP
 +# endif
 +#endif
  #if GNUTLS_VERSION_NUMBER >= 0x030500
  # define SUPPORT_GNUTLS_KEYLOG
  #endif
@@@ -133,12 -127,6 +133,12 @@@ builtin_macro_create_var(US"_RESUME_DEC
  # ifdef EXIM_HAVE_TLS1_3
  builtin_macro_create(US"_HAVE_TLS1_3");
  # endif
 +# ifdef EXIM_HAVE_OCSP
 +builtin_macro_create(US"_HAVE_TLS_OCSP");
 +# endif
 +# ifdef SUPPORT_SRV_OCSP_STACK
 +builtin_macro_create(US"_HAVE_TLS_OCSP_LIST");
 +# endif
  }
  #else
  
@@@ -167,7 -155,7 +167,7 @@@ Some of these correspond to variables i
  be set to point to content in one of these instances, as appropriate for
  the stage of the process lifetime.
  
- Not handled here: global tls_channelbinding_b64.
+ Not handled here: global tlsp->tls_channelbinding.
  */
  
  typedef struct exim_gnutls_state {
@@@ -479,7 -467,7 +479,7 @@@ Sets
    tls_active                fd
    tls_bits                  strength indicator
    tls_certificate_verified  bool indicator
-   tls_channelbinding_b64    for some SASL mechanisms
+   tls_channelbinding        for some SASL mechanisms
    tls_ver                   a string
    tls_cipher                a string
    tls_peercert              pointer to library internal
@@@ -511,10 -499,10 +511,10 @@@ tlsp->certificate_verified = state->pee
  tlsp->dane_verified = state->peer_dane_verified;
  #endif
  
- /* note that tls_channelbinding_b64 is not saved to the spool file, since it's
+ /* note that tls_channelbinding is not saved to the spool file, since it's
  only available for use for authenticators while this TLS session is running. */
  
- tls_channelbinding_b64 = NULL;
+ tlsp->channelbinding = NULL;
  #ifdef HAVE_GNUTLS_SESSION_CHANNEL_BINDING
  channel.data = NULL;
  channel.size = 0;
@@@ -522,11 -510,15 +522,15 @@@ if ((rc = gnutls_session_channel_bindin
    { DEBUG(D_tls) debug_printf("Channel binding error: %s\n", gnutls_strerror(rc)); }
  else
    {
+   /* Declare the taintedness of the binding info.  On server, untainted; on
+   client, tainted - being the Finish msg from the server. */
    old_pool = store_pool;
    store_pool = POOL_PERM;
-   tls_channelbinding_b64 = b64encode(CUS channel.data, (int)channel.size);
+   tlsp->channelbinding = b64encode_taint(CUS channel.data, (int)channel.size,
+                                         !!state->host);
    store_pool = old_pool;
-   DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage.\n");
+   DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage\n");
    }
  #endif
  
@@@ -2279,17 -2271,17 +2283,17 @@@ post_handshake_debug(exim_gnutls_state_
  #ifdef SUPPORT_GNUTLS_SESS_DESC
  debug_printf("%s\n", gnutls_session_get_desc(state->session));
  #endif
 -#ifdef SUPPORT_GNUTLS_KEYLOG
  
 +#ifdef SUPPORT_GNUTLS_KEYLOG
  # ifdef EXIM_HAVE_TLS1_3
  if (gnutls_protocol_get_version(state->session) < GNUTLS_TLS1_3)
 -#else
 +# else
  if (TRUE)
 -#endif
 +# endif
    {
    gnutls_datum_t c, s;
    gstring * gc, * gs;
 -  /* we only want the client random and the master secret */
 +  /* For TLS1.2 we only want the client random and the master secret */
    gnutls_session_get_random(state->session, &c, &s);
    gnutls_session_get_master_secret(state->session, &s);
    gc = ddump(&c);
@@@ -2302,8 -2294,7 +2306,8 @@@ els
      " add SSLKEYLOGFILE to keep_environment in the exim config\n"
      " run exim as root\n"
      " if using sudo, add SSLKEYLOGFILE to env_keep in /etc/sudoers\n"
 -    " (works for TLS1.2 also, and saves cut-paste into file)\n");
 +    " (works for TLS1.2 also, and saves cut-paste into file)"
 +    " Trying to use add_environment for this will not work\n");
  #endif
  }
  
@@@ -2407,9 -2398,20 +2411,20 @@@ and sent an SMTP response. *
  
  DEBUG(D_tls) debug_printf("initialising GnuTLS as a server\n");
  
- if ((rc = tls_init(NULL, tls_certificate, tls_privatekey,
-     NULL, tls_verify_certificates, tls_crl,
-     require_ciphers, &state, &tls_in, errstr)) != OK) return rc;
+   {
+ #ifdef MEASURE_TIMING
+   struct timeval t0;
+   gettimeofday(&t0, NULL);
+ #endif
+   if ((rc = tls_init(NULL, tls_certificate, tls_privatekey,
+       NULL, tls_verify_certificates, tls_crl,
+       require_ciphers, &state, &tls_in, errstr)) != OK) return rc;
+ #ifdef MEASURE_TIMING
+   report_time_since(&t0, US"server tls_init (delta)");
+ #endif
+   }
  
  #ifdef EXPERIMENTAL_TLS_RESUME
  tls_server_resume_prehandshake(state);
@@@ -2843,10 -2845,21 +2858,21 @@@ if (conn_args->dane && ob->dane_require
  if (!cipher_list)
    cipher_list = ob->tls_require_ciphers;
  
- if (tls_init(host, ob->tls_certificate, ob->tls_privatekey,
-     ob->tls_sni, ob->tls_verify_certificates, ob->tls_crl,
-     cipher_list, &state, tlsp, errstr) != OK)
-   return FALSE;
+   {
+ #ifdef MEASURE_TIMING
+   struct timeval t0;
+   gettimeofday(&t0, NULL);
+ #endif
+   if (tls_init(host, ob->tls_certificate, ob->tls_privatekey,
+       ob->tls_sni, ob->tls_verify_certificates, ob->tls_crl,
+       cipher_list, &state, tlsp, errstr) != OK)
+     return FALSE;
+ #ifdef MEASURE_TIMING
+   report_time_since(&t0, US"client tls_init (delta)");
+ #endif
+   }
  
    {
    int dh_min_bits = ob->tls_dh_min_bits;
@@@ -3084,7 -3097,7 +3110,7 @@@ gnutls_certificate_free_credentials(sta
  tlsp->active.sock = -1;
  tlsp->active.tls_ctx = NULL;
  /* Leave bits, peercert, cipher, peerdn, certificate_verified set, for logging */
- tls_channelbinding_b64 = NULL;
+ tlsp->channelbinding = NULL;
  
  
  if (state->xfer_buffer) store_free(state->xfer_buffer);
@@@ -3298,6 -3311,9 +3324,9 @@@ Arguments
    len       number of bytes
    more            more data expected soon
  
+ Calling with len zero and more unset will flush buffered writes.  The buff
+ argument can be null for that case.
  Returns:    the number of bytes after a successful write,
              -1 after a failed write
  */
diff --combined src/src/tls-openssl.c
index 063c23df75987905d51895a7daada486502063f9,7e3cc3f78e053355603716b8f2e57973b2793930..a236bc0c68c0028497f80157c75750c2d150b3b5
@@@ -76,9 -76,6 +76,9 @@@ change this guard and punt the issue fo
  #  define EXIM_HAVE_SESSION_TICKET
  #  define EXIM_HAVE_OPESSL_TRACE
  #  define EXIM_HAVE_OPESSL_GET0_SERIAL
 +#  ifndef DISABLE_OCSP
 +#   define EXIM_HAVE_OCSP
 +#  endif
  # else
  #  define EXIM_NEED_OPENSSL_INIT
  # endif
  #  define OPENSSL_HAVE_KEYLOG_CB
  #  define OPENSSL_HAVE_NUM_TICKETS
  #  define EXIM_HAVE_OPENSSL_CIPHER_STD_NAME
 +# else
 +#  define OPENSSL_BAD_SRVR_OURCERT
  # endif
  #endif
  
@@@ -151,11 -146,6 +151,11 @@@ This list is current as of
    ==>  1.0.1b  <==
  Plus SSL_OP_SAFARI_ECDHE_ECDSA_BUG from 2013-June patch/discussion on openssl-dev
  Plus SSL_OP_NO_TLSv1_3 for 1.1.2-dev
 +Plus SSL_OP_NO_RENEGOTIATION for 1.1.1
 +
 +XXX could we autobuild this list, as with predefined-macros?
 +Seems just parsing ssl.h for SSL_OP_.* would be enough.
 +Also allow a numeric literal?
  */
  static exim_openssl_option exim_openssl_options[] = {
  /* KEEP SORTED ALPHABETICALLY! */
  #ifdef SSL_OP_NO_COMPRESSION
    { US"no_compression", SSL_OP_NO_COMPRESSION },
  #endif
 +#ifdef SSL_OP_NO_RENEGOTIATION
 +  { US"no_renegotiation", SSL_OP_NO_RENEGOTIATION },
 +#endif
  #ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
    { US"no_session_resumption_on_renegotiation", SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION },
  #endif
@@@ -279,13 -266,6 +279,13 @@@ builtin_macro_create_var(US"_RESUME_DEC
  # ifdef SSL_OP_NO_TLSv1_3
  builtin_macro_create(US"_HAVE_TLS1_3");
  # endif
 +# ifdef OPENSSL_BAD_SRVR_OURCERT
 +builtin_macro_create(US"_TLS_BAD_MULTICERT_IN_OURCERT");
 +# endif
 +# ifdef EXIM_HAVE_OCSP
 +builtin_macro_create(US"_HAVE_TLS_OCSP");
 +builtin_macro_create(US"_HAVE_TLS_OCSP_LIST");
 +# endif
  }
  #else
  
@@@ -861,13 -841,7 +861,13 @@@ DEBUG(D_tls
  static void
  keylog_callback(const SSL *ssl, const char *line)
  {
 +char * filename;
 +FILE * fp;
  DEBUG(D_tls) debug_printf("%.200s\n", line);
 +if (!(filename = getenv("SSLKEYLOGFILE"))) return;
 +if (!(fp = fopen(filename, "a"))) return;
 +fprintf(fp, "%s\n", line);
 +fclose(fp);
  }
  #endif
  
@@@ -1226,13 -1200,12 +1226,13 @@@ Arguments
    sctx            the SSL_CTX* to update
    cbinfo          various parts of session state
    filename        the filename putatively holding an OCSP response
 +  is_pem        file is PEM format; otherwise is DER
  
  */
  
  static void
  ocsp_load_response(SSL_CTX * sctx, tls_ext_ctx_cb * cbinfo,
 -  const uschar * filename)
 +  const uschar * filename, BOOL is_pem)
  {
  BIO * bio;
  OCSP_RESPONSE * resp;
@@@ -1243,8 -1216,7 +1243,8 @@@ STACK_OF(X509) * sk
  unsigned long verify_flags;
  int status, reason, i;
  
 -DEBUG(D_tls) debug_printf("tls_ocsp_file        '%s'\n", filename);
 +DEBUG(D_tls)
 +  debug_printf("tls_ocsp_file (%s)  '%s'\n", is_pem ? "PEM" : "DER", filename);
  
  if (!(bio = BIO_new_file(CS filename, "rb")))
    {
    return;
    }
  
 -resp = d2i_OCSP_RESPONSE_bio(bio, NULL);
 +if (is_pem)
 +  {
 +  uschar * data, * freep;
 +  char * dummy;
 +  long len;
 +  if (!PEM_read_bio(bio, &dummy, &dummy, &data, &len))
 +    {
 +    DEBUG(D_tls) debug_printf("Failed to read PEM file \"%s\"\n",
 +      filename);
 +    return;
 +    }
 +debug_printf("read pem file\n");
 +  freep = data;
 +  resp = d2i_OCSP_RESPONSE(NULL, CUSS &data, len);
 +  OPENSSL_free(freep);
 +  }
 +else
 +  resp = d2i_OCSP_RESPONSE_bio(bio, NULL);
  BIO_free(bio);
 +
  if (!resp)
    {
    DEBUG(D_tls) debug_printf("Error reading OCSP response.\n");
@@@ -1558,7 -1512,6 +1558,7 @@@ els
        const uschar * olist = cbinfo->u_ocsp.server.file;
        int osep = 0;
        uschar * ofile;
 +      BOOL fmt_pem = FALSE;
  
        if (olist)
        if (!expand_check(olist, US"tls_ocsp_file", USS &olist, errstr))
  #ifndef DISABLE_OCSP
        if (olist)
          if ((ofile = string_nextinlist(&olist, &osep, NULL, 0)))
 -          ocsp_load_response(sctx, cbinfo, ofile);
 +          {
 +          if (Ustrncmp(ofile, US"PEM ", 4) == 0)
 +            {
 +            fmt_pem = TRUE;
 +            ofile += 4;
 +            }
 +          else if (Ustrncmp(ofile, US"DER ", 4) == 0)
 +            {
 +            fmt_pem = FALSE;
 +            ofile += 4;
 +            }
 +          ocsp_load_response(sctx, cbinfo, ofile, fmt_pem);
 +          }
          else
            DEBUG(D_tls) debug_printf("ran out of ocsp file list\n");
  #endif
@@@ -1861,13 -1802,13 +1861,13 @@@ OCSP_RESPONSE * rsp
  OCSP_BASICRESP * bs;
  int i;
  
 -DEBUG(D_tls) debug_printf("Received TLS status response (OCSP stapling):");
 +DEBUG(D_tls) debug_printf("Received TLS status callback (OCSP stapling):\n");
  len = SSL_get_tlsext_status_ocsp_resp(s, &p);
  if(!p)
   {
    /* Expect this when we requested ocsp but got none */
    if (cbinfo->u_ocsp.client.verify_required && LOGGING(tls_cipher))
 -    log_write(0, LOG_MAIN, "Received TLS status callback, null content");
 +    log_write(0, LOG_MAIN, "Required TLS certificate status not received");
    else
      DEBUG(D_tls) debug_printf(" null\n");
    return cbinfo->u_ocsp.client.verify_required ? 0 : 1;
@@@ -1903,9 -1844,8 +1903,9 @@@ if (!(bs = OCSP_response_get1_basic(rsp
  */
    {
      BIO * bp = NULL;
 -    int status, reason;
 -    ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd;
 +#ifndef EXIM_HAVE_OCSP_RESP_COUNT
 +    STACK_OF(OCSP_SINGLERESP) * sresp = bs->tbsResponseData->responses;
 +#endif
  
      DEBUG(D_tls) bp = BIO_new_fp(debug_file, BIO_NOCLOSE);
  
      /* DEBUG(D_tls) x509_store_dump_cert_s_names(cbinfo->u_ocsp.client.verify_store); */
  
      if ((i = OCSP_basic_verify(bs, cbinfo->verify_stack,
 -            cbinfo->u_ocsp.client.verify_store, 0)) <= 0)
 -      {
 -      tls_out.ocsp = OCSP_FAILED;
 -      if (LOGGING(tls_cipher)) log_write(0, LOG_MAIN,
 -            "Received TLS cert status response, itself unverifiable: %s",
 -            ERR_reason_error_string(ERR_peek_error()));
 -      BIO_printf(bp, "OCSP response verify failure\n");
 -      ERR_print_errors(bp);
 -      OCSP_RESPONSE_print(bp, rsp, 0);
 -      goto failed;
 -      }
 +            cbinfo->u_ocsp.client.verify_store, OCSP_NOEXPLICIT)) <= 0)
 +      if (ERR_peek_error())
 +      {
 +      tls_out.ocsp = OCSP_FAILED;
 +      if (LOGGING(tls_cipher)) log_write(0, LOG_MAIN,
 +              "Received TLS cert status response, itself unverifiable: %s",
 +              ERR_reason_error_string(ERR_peek_error()));
 +      BIO_printf(bp, "OCSP response verify failure\n");
 +      ERR_print_errors(bp);
 +      OCSP_RESPONSE_print(bp, rsp, 0);
 +      goto failed;
 +      }
 +      else
 +      DEBUG(D_tls) debug_printf("no explicit trust for OCSP signing"
 +        " in the root CA certificate; ignoring\n");
  
 -    BIO_printf(bp, "OCSP response well-formed and signed OK\n");
 +    DEBUG(D_tls) debug_printf("OCSP response well-formed and signed OK\n");
  
      /*XXX So we have a good stapled OCSP status.  How do we know
      it is for the cert of interest?  OpenSSL 1.1.0 has a routine
  
      For now, carry on blindly accepting the resp. */
  
 -      {
 -      OCSP_SINGLERESP * single;
 -
 +    for (int idx =
  #ifdef EXIM_HAVE_OCSP_RESP_COUNT
 -      if (OCSP_resp_count(bs) != 1)
 +          OCSP_resp_count(bs) - 1;
  #else
 -      STACK_OF(OCSP_SINGLERESP) * sresp = bs->tbsResponseData->responses;
 -      if (sk_OCSP_SINGLERESP_num(sresp) != 1)
 +          sk_OCSP_SINGLERESP_num(sresp) - 1;
  #endif
 -        {
 -      tls_out.ocsp = OCSP_FAILED;
 -        log_write(0, LOG_MAIN, "OCSP stapling "
 -          "with multiple responses not handled");
 -        goto failed;
 -        }
 -      single = OCSP_resp_get0(bs, 0);
 +       idx >= 0; idx--)
 +      {
 +      OCSP_SINGLERESP * single = OCSP_resp_get0(bs, idx);
 +      int status, reason;
 +      ASN1_GENERALIZEDTIME * rev, * thisupd, * nextupd;
 +
 +  /*XXX so I can see putting a loop in here to handle a rsp with >1 singleresp
 +  - but what happens with a GnuTLS-style input?
 +
 +  we could do with a debug label for each singleresp
 +  - it has a certID with a serialNumber, but I see no API to get that
 +  */
        status = OCSP_single_get0_status(single, &reason, &rev,
                  &thisupd, &nextupd);
 -      }
  
 -    DEBUG(D_tls) time_print(bp, "This OCSP Update", thisupd);
 -    DEBUG(D_tls) if(nextupd) time_print(bp, "Next OCSP Update", nextupd);
 -    if (!OCSP_check_validity(thisupd, nextupd,
 -        EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX_AGE))
 -      {
 -      tls_out.ocsp = OCSP_FAILED;
 -      DEBUG(D_tls) ERR_print_errors(bp);
 -      log_write(0, LOG_MAIN, "Server OSCP dates invalid");
 -      }
 -    else
 -      {
 +      DEBUG(D_tls) time_print(bp, "This OCSP Update", thisupd);
 +      DEBUG(D_tls) if(nextupd) time_print(bp, "Next OCSP Update", nextupd);
 +      if (!OCSP_check_validity(thisupd, nextupd,
 +          EXIM_OCSP_SKEW_SECONDS, EXIM_OCSP_MAX_AGE))
 +      {
 +      tls_out.ocsp = OCSP_FAILED;
 +      DEBUG(D_tls) ERR_print_errors(bp);
 +      log_write(0, LOG_MAIN, "Server OSCP dates invalid");
 +      goto failed;
 +      }
 +
        DEBUG(D_tls) BIO_printf(bp, "Certificate status: %s\n",
                    OCSP_cert_status_str(status));
        switch(status)
        {
        case V_OCSP_CERTSTATUS_GOOD:
 -        tls_out.ocsp = OCSP_VFIED;
 -        i = 1;
 -        goto good;
 +        continue;     /* the idx loop */
        case V_OCSP_CERTSTATUS_REVOKED:
 -        tls_out.ocsp = OCSP_FAILED;
          log_write(0, LOG_MAIN, "Server certificate revoked%s%s",
              reason != -1 ? "; reason: " : "",
              reason != -1 ? OCSP_crl_reason_str(reason) : "");
          DEBUG(D_tls) time_print(bp, "Revocation Time", rev);
          break;
        default:
 -        tls_out.ocsp = OCSP_FAILED;
          log_write(0, LOG_MAIN,
              "Server certificate status unknown, in OCSP stapling");
          break;
        }
 +
 +      goto failed;
        }
 +
 +    i = 1;
 +    tls_out.ocsp = OCSP_VFIED;
 +    goto good;
 +
    failed:
 +    tls_out.ocsp = OCSP_FAILED;
      i = cbinfo->u_ocsp.client.verify_required ? 0 : 1;
    good:
      BIO_free(bp);
@@@ -2369,11 -2300,7 +2369,11 @@@ if (tlsp->peercert
      for resumption next to the TLS session, and used here. */
  
      if (!tlsp->verify_override)
 -      tlsp->certificate_verified = SSL_get_verify_result(ssl) == X509_V_OK;
 +      tlsp->certificate_verified =
 +#ifdef SUPPORT_DANE
 +      tlsp->dane_verified ||
 +#endif
 +      SSL_get_verify_result(ssl) == X509_V_OK;
      }
  }
  
@@@ -2744,14 -2671,8 +2744,14 @@@ if (rc <= 0
  
      /* Handle genuine errors */
      case SSL_ERROR_SSL:
 -      (void) tls_error(US"SSL_accept", NULL, sigalrm_seen ? US"timed out" : NULL, errstr);
 +      {
 +      uschar * s = US"SSL_accept";
 +      unsigned long e = ERR_peek_error();
 +      if (ERR_GET_REASON(e) == SSL_R_WRONG_VERSION_NUMBER)
 +      s = string_sprintf("%s (%s)", s, SSL_get_version(server_ssl));
 +      (void) tls_error(s, NULL, sigalrm_seen ? US"timed out" : NULL, errstr);
        return FAIL;
 +      }
  
      default:
        DEBUG(D_tls) debug_printf("Got SSL error %d\n", error);
@@@ -2820,6 -2741,20 +2820,20 @@@ DEBUG(D_tls
    tls_in.ourcert = crt ? X509_dup(crt) : NULL;
    }
  
+ /* Channel-binding info for authenticators
+ See description in https://paquier.xyz/postgresql-2/channel-binding-openssl/ */
+   {
+   uschar c, * s;
+   size_t len = SSL_get_peer_finished(server_ssl, &c, 0);
+   int old_pool = store_pool;
+   
+   SSL_get_peer_finished(server_ssl, s = store_get((int)len, FALSE), len);
+   store_pool = POOL_PERM;
+     tls_in.channelbinding = b64encode_taint(CUS s, (int)len, FALSE);
+   store_pool = old_pool;
+   DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage\n");
+   }
  /* Only used by the server-side tls (tls_in), including tls_getc.
     Client-side (tls_out) reads (seem to?) go via
     smtp_read_response()/ip_recv().
@@@ -3382,6 -3317,20 +3396,20 @@@ tlsp->cipher_stdname = cipher_stdname_s
    tlsp->ourcert = crt ? X509_dup(crt) : NULL;
    }
  
+ /*XXX will this work with continued-TLS? */
+ /* Channel-binding info for authenticators */
+   {
+   uschar c, * s;
+   size_t len = SSL_get_finished(exim_client_ctx->ssl, &c, 0);
+   int old_pool = store_pool;
+   
+   SSL_get_finished(exim_client_ctx->ssl, s = store_get((int)len, TRUE), len);
+   store_pool = POOL_PERM;
+     tlsp->channelbinding = b64encode_taint(CUS s, (int)len, TRUE);
+   store_pool = old_pool;
+   DEBUG(D_tls) debug_printf("Have channel bindings cached for possible auth usage\n");
+   }
  tlsp->active.sock = cctx->sock;
  tlsp->active.tls_ctx = exim_client_ctx;
  cctx->tls_ctx = exim_client_ctx;
@@@ -3582,11 -3531,12 +3610,12 @@@ Arguments
  Returns:    the number of bytes after a successful write,
              -1 after a failed write
  
- Used by both server-side and client-side TLS.
+ Used by both server-side and client-side TLS.  Calling with len zero and more unset
+ will flush buffered writes; buff can be null for this case.
  */
  
  int
- tls_write(void * ct_ctx, const uschar *buff, size_t len, BOOL more)
+ tls_write(void * ct_ctx, const uschar * buff, size_t len, BOOL more)
  {
  size_t olen = len;
  int outbytes, error;
@@@ -3612,14 -3562,16 +3641,16 @@@ a store reset there, so use POOL_PERM. 
  
  if ((more || corked))
    {
- #ifdef SUPPORT_PIPE_CONNECT
+   if (!len) buff = US &error; /* dummy just so that string_catn is ok */
+ #ifndef DISABLE_PIPE_CONNECT
    int save_pool = store_pool;
    store_pool = POOL_PERM;
  #endif
  
    corked = string_catn(corked, buff, len);
  
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
    store_pool = save_pool;
  #endif
  
@@@ -3641,16 -3593,16 +3672,16 @@@ for (int left = len; left > 0;
    DEBUG(D_tls) debug_printf("outbytes=%d error=%d\n", outbytes, error);
    switch (error)
      {
+     case SSL_ERROR_NONE:      /* the usual case */
+       left -= outbytes;
+       buff += outbytes;
+       break;
      case SSL_ERROR_SSL:
        ERR_error_string_n(ERR_get_error(), ssl_errstring, sizeof(ssl_errstring));
        log_write(0, LOG_MAIN, "TLS error (SSL_write): %s", ssl_errstring);
        return -1;
  
-     case SSL_ERROR_NONE:
-       left -= outbytes;
-       buff += outbytes;
-       break;
      case SSL_ERROR_ZERO_RETURN:
        log_write(0, LOG_MAIN, "SSL channel closed on write");
        return -1;
@@@ -3993,7 -3945,7 +4024,7 @@@ BOO
  tls_openssl_options_parse(uschar *option_spec, long *results)
  {
  long result, item;
 -uschar *end;
 +uschar * exp, * end;
  uschar keep_c;
  BOOL adding, item_parsed;
  
  result = SSL_OP_NO_TICKET;
  
  /* Prior to 4.80 we or'd in SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; removed
 - * from default because it increases BEAST susceptibility. */
 +from default because it increases BEAST susceptibility. */
  #ifdef SSL_OP_NO_SSLv2
  result |= SSL_OP_NO_SSLv2;
  #endif
@@@ -4011,9 -3963,6 +4042,9 @@@ result |= SSL_OP_NO_SSLv3
  #ifdef SSL_OP_SINGLE_DH_USE
  result |= SSL_OP_SINGLE_DH_USE;
  #endif
 +#ifdef SSL_OP_NO_RENEGOTIATION
 +result |= SSL_OP_NO_RENEGOTIATION;
 +#endif
  
  if (!option_spec)
    {
    return TRUE;
    }
  
 -for (uschar * s = option_spec; *s; /**/)
 +if (!expand_check(option_spec, US"openssl_options", &exp, &end))
 +  return FALSE;
 +
 +for (uschar * s = exp; *s; /**/)
    {
    while (isspace(*s)) ++s;
    if (*s == '\0')
      DEBUG(D_tls) debug_printf("openssl option setting unrecognised: \"%s\"\n", s);
      return FALSE;
      }
 -  DEBUG(D_tls) debug_printf("openssl option, %s %8lx: %lx (%s)\n",
 +  DEBUG(D_tls) debug_printf("openssl option, %s %08lx: %08lx (%s)\n",
        adding ? "adding to    " : "removing from", result, item, s);
    if (adding)
      result |= item;
diff --combined src/src/tls.c
index a541a3c7a3210546aae49bc79435adb0e0623864,d47156cdc5ea555778ce3a5e4ea15378a775aa57..f9509121804ee997c1c3035cc349a4b60c3483ac
@@@ -61,8 -61,6 +61,6 @@@ static int ssl_xfer_eof = FALSE
  static BOOL ssl_xfer_error = FALSE;
  #endif
  
- uschar *tls_channelbinding_b64 = NULL;
  
  /*************************************************
  *       Expand string; give error on failure     *
@@@ -371,38 -369,79 +369,111 @@@ return FALSE
  }
  
  
 +/* Environment cleanup: The GnuTLS library uses SSLKEYLOGFILE in the environment
 +and writes a file by that name.  Our OpenSSL code does the same, using keying
 +info from the library API.
 +The GnuTLS support only works if exim is run by root, not taking advantage of
 +the setuid bit.
 +You can use either the external environment (modulo the keep_environment config)
 +or the add_environment config option for SSLKEYLOGFILE; the latter takes
 +precedence.
 +
 +If the path is absolute, require it starts with the spooldir; otherwise delete
 +the env variable.  If relative, prefix the spooldir.
 +*/
 +void
 +tls_clean_env(void)
 +{
 +uschar * path = US getenv("SSLKEYLOGFILE");
 +if (path)
 +  if (!*path)
 +    unsetenv("SSLKEYLOGFILE");
 +  else if (*path != '/')
 +    {
 +    DEBUG(D_tls)
 +      debug_printf("prepending spooldir to  env SSLKEYLOGFILE\n");
 +    setenv("SSLKEYLOGFILE", CCS string_sprintf("%s/%s", spool_directory, path), 1);
 +    }
 +  else if (Ustrncmp(path, spool_directory, Ustrlen(spool_directory)) != 0)
 +    {
 +    DEBUG(D_tls)
 +      debug_printf("removing env SSLKEYLOGFILE=%s: not under spooldir\n", path);
 +    unsetenv("SSLKEYLOGFILE");
 +    }
 +}
+ /*************************************************
+ *       Drop privs for checking TLS config      *
+ *************************************************/
+ /* We want to validate TLS options during readconf, but do not want to be
+ root when we call into the TLS library, in case of library linkage errors
+ which cause segfaults; before this check, those were always done as the Exim
+ runtime user and it makes sense to continue with that.
+ Assumes:  tls_require_ciphers has been set, if it will be
+           exim_user has been set, if it will be
+           exim_group has been set, if it will be
+ Returns:  bool for "okay"; false will cause caller to immediately exit.
+ */
+ BOOL
+ tls_dropprivs_validate_require_cipher(BOOL nowarn)
+ {
+ const uschar *errmsg;
+ pid_t pid;
+ int rc, status;
+ void (*oldsignal)(int);
+ /* If TLS will never be used, no point checking ciphers */
+ if (  !tls_advertise_hosts
+    || !*tls_advertise_hosts
+    || Ustrcmp(tls_advertise_hosts, ":") == 0
+    )
+   return TRUE;
+ else if (!nowarn && !tls_certificate)
+   log_write(0, LOG_MAIN,
+     "Warning: No server certificate defined; will use a selfsigned one.\n"
+     " Suggested action: either install a certificate or change tls_advertise_hosts option");
+ oldsignal = signal(SIGCHLD, SIG_DFL);
+ fflush(NULL);
+ if ((pid = fork()) < 0)
+   log_write(0, LOG_MAIN|LOG_PANIC_DIE, "fork failed for TLS check");
+ if (pid == 0)
+   {
+   /* in some modes, will have dropped privilege already */
+   if (!geteuid())
+     exim_setugid(exim_uid, exim_gid, FALSE,
+         US"calling tls_validate_require_cipher");
+   if ((errmsg = tls_validate_require_cipher()))
+     log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
+         "tls_require_ciphers invalid: %s", errmsg);
+   fflush(NULL);
+   exim_underbar_exit(0);
+   }
+ do {
+   rc = waitpid(pid, &status, 0);
+ } while (rc < 0 && errno == EINTR);
+ DEBUG(D_tls)
+   debug_printf("tls_validate_require_cipher child %d ended: status=0x%x\n",
+       (int)pid, status);
+ signal(SIGCHLD, oldsignal);
+ return status == 0;
+ }
  #endif        /*!DISABLE_TLS*/
  #endif        /*!MACRO_PREDEF*/
  
index 3fd94a19cb39b7efa592141f72730f526c3d937b,7ea079fac17f06122f01de9bfaa92e104ff93adc..a91770225738e5148b1cf7ad8f2301d930e82ad7
@@@ -110,7 -110,7 +110,7 @@@ optionlist smtp_transport_options[] = 
  #endif
    { "hosts_override",       opt_bool,
        (void *)offsetof(smtp_transport_options_block, hosts_override) },
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
    { "hosts_pipe_connect",   opt_stringptr,
        (void *)offsetof(smtp_transport_options_block, hosts_pipe_connect) },
  #endif
@@@ -260,7 -260,7 +260,7 @@@ smtp_transport_options_block smtp_trans
    .hosts_avoid_tls =          NULL,
    .hosts_verify_avoid_tls =   NULL,
    .hosts_avoid_pipelining =   NULL,
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
    .hosts_pipe_connect =               NULL,
  #endif
    .hosts_avoid_esmtp =                NULL,
    .gethostbyname =            FALSE,
    .dns_qualify_single =               TRUE,
    .dns_search_parents =               FALSE,
 -  .dnssec = { .request=NULL, .require=NULL },
 +  .dnssec = { .request= US"*", .require=NULL },
    .delay_after_cutoff =               TRUE,
    .hosts_override =           FALSE,
    .hosts_randomize =          FALSE,
@@@ -357,6 -357,51 +357,51 @@@ static BOOL    pipelining_active;        /* cu
  static unsigned ehlo_response(uschar * buf, unsigned checks);
  
  
+ /******************************************************************************/
+ void
+ smtp_deliver_init(void)
+ {
+ if (!regex_PIPELINING) regex_PIPELINING =
+   regex_must_compile(US"\\n250[\\s\\-]PIPELINING(\\s|\\n|$)", FALSE, TRUE);
+ if (!regex_SIZE) regex_SIZE =
+   regex_must_compile(US"\\n250[\\s\\-]SIZE(\\s|\\n|$)", FALSE, TRUE);
+ if (!regex_AUTH) regex_AUTH =
+   regex_must_compile(AUTHS_REGEX, FALSE, TRUE);
+ #ifndef DISABLE_TLS
+ if (!regex_STARTTLS) regex_STARTTLS =
+   regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE);
+ #endif
+ if (!regex_CHUNKING) regex_CHUNKING =
+   regex_must_compile(US"\\n250[\\s\\-]CHUNKING(\\s|\\n|$)", FALSE, TRUE);
+ #ifndef DISABLE_PRDR
+ if (!regex_PRDR) regex_PRDR =
+   regex_must_compile(US"\\n250[\\s\\-]PRDR(\\s|\\n|$)", FALSE, TRUE);
+ #endif
+ #ifdef SUPPORT_I18N
+ if (!regex_UTF8) regex_UTF8 =
+   regex_must_compile(US"\\n250[\\s\\-]SMTPUTF8(\\s|\\n|$)", FALSE, TRUE);
+ #endif
+ if (!regex_DSN) regex_DSN  =
+   regex_must_compile(US"\\n250[\\s\\-]DSN(\\s|\\n|$)", FALSE, TRUE);
+ if (!regex_IGNOREQUOTA) regex_IGNOREQUOTA =
+   regex_must_compile(US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)", FALSE, TRUE);
+ #ifndef DISABLE_PIPE_CONNECT
+ if (!regex_EARLY_PIPE) regex_EARLY_PIPE =
+   regex_must_compile(US"\\n250[\\s\\-]" EARLY_PIPE_FEATURE_NAME "(\\s|\\n|$)", FALSE, TRUE);
+ #endif
+ }
  /*************************************************
  *             Setup entry point                  *
  *************************************************/
@@@ -823,7 -868,7 +868,7 @@@ return TRUE
  
  
  
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
  static uschar *
  ehlo_cache_key(const smtp_context * sx)
  {
@@@ -1089,7 -1134,7 +1134,7 @@@ address_item * addr = sx->sync_addr
  smtp_transport_options_block * ob = sx->conn_args.ob;
  int yield = 0;
  
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
  int rc;
  if ((rc = smtp_reap_early_pipe(sx, &count)) != OK)
    return rc == FAIL ? -4 : -5;
@@@ -1411,7 -1456,7 +1456,7 @@@ smtp_auth(smtp_context * sx
  host_item * host = sx->conn_args.host;                        /* host to deliver to */
  smtp_transport_options_block * ob = sx->conn_args.ob; /* transport options */
  int require_auth = verify_check_given_host(CUSS &ob->hosts_require_auth, host);
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
  unsigned short authbits = tls_out.active.sock >= 0
        ? sx->ehlo_resp.crypted_auths : sx->ehlo_resp.cleartext_auths;
  #endif
@@@ -1427,7 -1472,7 +1472,7 @@@ if (!regex_AUTH
  
  if (  sx->esmtp
     &&
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
        sx->early_pipe_active ? authbits
        :
  #endif
    uschar * names = NULL;
    expand_nmax = -1;                          /* reset */
  
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
    if (!sx->early_pipe_active)
  #endif
      names = string_copyn(expand_nstring[1], expand_nlength[1]);
      DEBUG(D_transport) debug_printf("scanning authentication mechanisms\n");
      fail_reason = US"no common mechanisms were found";
  
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
      if (sx->early_pipe_active)
        {
        /* Scan our authenticators (which support use by a client and were offered
@@@ -1613,6 -1658,77 +1658,6 @@@ return FALSE
  
  
  
 -#ifdef SUPPORT_DANE
 -/* Lookup TLSA record for host/port.
 -Return:  OK           success with dnssec; DANE mode
 -         DEFER                Do not use this host now, may retry later
 -       FAIL_FORCED    No TLSA record; DANE not usable
 -       FAIL           Do not use this connection
 -*/
 -
 -int
 -tlsa_lookup(const host_item * host, dns_answer * dnsa, BOOL dane_required)
 -{
 -/* move this out to host.c given the similarity to dns_lookup() ? */
 -uschar buffer[300];
 -const uschar * fullname = buffer;
 -int rc;
 -BOOL sec;
 -
 -/* TLSA lookup string */
 -(void)sprintf(CS buffer, "_%d._tcp.%.256s", host->port, host->name);
 -
 -rc = dns_lookup(dnsa, buffer, T_TLSA, &fullname);
 -sec = dns_is_secure(dnsa);
 -DEBUG(D_transport)
 -  debug_printf("TLSA lookup ret %d %sDNSSEC\n", rc, sec ? "" : "not ");
 -
 -switch (rc)
 -  {
 -  case DNS_AGAIN:
 -    return DEFER; /* just defer this TLS'd conn */
 -
 -  case DNS_SUCCEED:
 -    if (sec)
 -      {
 -      DEBUG(D_transport)
 -      {
 -      dns_scan dnss;
 -      for (dns_record * rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
 -           rr = dns_next_rr(dnsa, &dnss, RESET_NEXT))
 -        if (rr->type == T_TLSA && rr->size > 3)
 -          {
 -          uint16_t payload_length = rr->size - 3;
 -          uschar s[MAX_TLSA_EXPANDED_SIZE], * sp = s, * p = US rr->data;
 -
 -          sp += sprintf(CS sp, "%d ", *p++); /* usage */
 -          sp += sprintf(CS sp, "%d ", *p++); /* selector */
 -          sp += sprintf(CS sp, "%d ", *p++); /* matchtype */
 -          while (payload_length-- > 0 && sp-s < (MAX_TLSA_EXPANDED_SIZE - 4))
 -            sp += sprintf(CS sp, "%02x", *p++);
 -
 -          debug_printf(" %s\n", s);
 -          }
 -      }
 -      return OK;
 -      }
 -    log_write(0, LOG_MAIN,
 -      "DANE error: TLSA lookup for %s not DNSSEC", host->name);
 -    /*FALLTRHOUGH*/
 -
 -  case DNS_NODATA:    /* no TLSA RR for this lookup */
 -  case DNS_NOMATCH:   /* no records at all for this lookup */
 -    return dane_required ? FAIL : FAIL_FORCED;
 -
 -  default:
 -  case DNS_FAIL:
 -    return dane_required ? FAIL : DEFER;
 -  }
 -}
 -#endif
 -
 -
 -
  typedef struct smtp_compare_s
  {
      uschar                          *current_sender_address;
@@@ -1737,7 -1853,7 +1782,7 @@@ if (  checks & OPTION_SIZ
     && pcre_exec(regex_SIZE, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
    checks &= ~OPTION_SIZE;
  
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
  if (  checks & OPTION_EARLY_PIPE
     && pcre_exec(regex_EARLY_PIPE, NULL, CS buf, bsize, 0,
                PCRE_EOPT, NULL, 0) < 0)
@@@ -1784,7 -1900,7 +1829,7 @@@ there may be more writes (like, the chu
  
  if (chunk_size > 0)
    {
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
    BOOL new_conn = !!(sx->outblock.conn_args);
  #endif
    if((cmd_count = smtp_write_command(sx,
       ) < 0) return ERROR;
    if (flags & tc_chunk_last)
      data_command = string_copy(big_buffer);  /* Save for later error message */
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
    /* That command write could have been the one that made the connection.
    Copy the fd from the client conn ctx (smtp transport specific) to the
    generic transport ctx. */
@@@ -1826,7 -1942,7 +1871,7 @@@ if (flags & tc_reap_prev  &&  prev_cmd_
  
      case -5: errno = ERRNO_TLSFAILURE;
             return DEFER;
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
      case -4:                          /* non-2xx for pipelined banner or EHLO */
  #endif
      case -1:                          /* Timeout on RCPT */
@@@ -1919,7 -2035,7 +1964,7 @@@ sx->conn_args.dane = FALSE
  sx->dane_required =
    verify_check_given_host(CUSS &ob->hosts_require_dane, sx->conn_args.host) == OK;
  #endif
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
  sx->early_pipe_active = sx->early_pipe_ok = FALSE;
  sx->ehlo_resp.cleartext_features = sx->ehlo_resp.crypted_features = 0;
  sx->pending_BANNER = sx->pending_EHLO = FALSE;
@@@ -2049,7 -2165,7 +2094,7 @@@ if (!continue_hostname
    sx->inblock.cctx = sx->outblock.cctx = &sx->cctx;
    sx->avoid_option = sx->peer_offered = smtp_peer_options = 0;
  
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
    if (  verify_check_given_host(CUSS &ob->hosts_pipe_connect,
                                            sx->conn_args.host) == OK)
  
@@@ -2124,7 -2240,7 +2169,7 @@@ will be?  Somehow I doubt it. *
  
    if (!sx->smtps)
      {
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
      if (sx->early_pipe_active)
        {
        sx->pending_BANNER = TRUE;      /* sync_responses() must eventually handle */
@@@ -2225,7 -2341,7 +2270,7 @@@ goto SEND_QUIT
    if (sx->esmtp)
      {
      if (smtp_write_command(sx,
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
          sx->early_pipe_active ? SCMD_BUFFER :
  #endif
            SCMD_FLUSH,
        goto SEND_FAILED;
      sx->esmtp_sent = TRUE;
  
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
      if (sx->early_pipe_active)
        {
        sx->pending_EHLO = TRUE;
      DEBUG(D_transport)
        debug_printf("not sending EHLO (host matches hosts_avoid_esmtp)\n");
  
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
    if (!sx->early_pipe_active)
  #endif
      if (!sx->esmtp)
  
    if (sx->esmtp || sx->lmtp)
      {
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
      if (!sx->early_pipe_active)
  #endif
        {
        sx->peer_offered = ehlo_response(sx->buffer,
        OPTION_TLS      /* others checked later */
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
        | (sx->early_pipe_ok
          ?   OPTION_IGNQ
            | OPTION_CHUNKING | OPTION_PRDR | OPTION_DSN | OPTION_PIPE | OPTION_SIZE
          )
  #endif
        );
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
        if (sx->early_pipe_ok)
        {
        sx->ehlo_resp.cleartext_features = sx->peer_offered;
@@@ -2412,7 -2528,7 +2457,7 @@@ if (  smtp_peer_options & OPTION_TL
    if (smtp_write_command(sx, SCMD_FLUSH, "STARTTLS\r\n") < 0)
      goto SEND_FAILED;
  
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
    /* If doing early-pipelining reap the banner and EHLO-response but leave
    the response for the STARTTLS we just sent alone. */
  
@@@ -2517,7 -2633,7 +2562,7 @@@ if (tls_out.active.sock >= 0
      goto SEND_QUIT;
      }
  
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
    /* For SMTPS there is no cleartext early-pipe; use the crypted permission bit.
    We're unlikely to get the group sent and delivered before the server sends its
    banner, but it's still worth sending as a group.
  
    /* For SMTPS we need to wait for the initial OK response. */
    if (sx->smtps)
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
      if (sx->early_pipe_active)
        {
        sx->pending_BANNER = TRUE;
      }
  
    if (smtp_write_command(sx,
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
        sx->early_pipe_active ? SCMD_BUFFER :
  #endif
          SCMD_FLUSH,
        "%s %s\r\n", greeting_cmd, sx->helo_data) < 0)
      goto SEND_FAILED;
  
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
    if (sx->early_pipe_active)
      sx->pending_EHLO = TRUE;
    else
@@@ -2630,13 -2746,13 +2675,13 @@@ if (continue_hostname == NUL
    {
    if (sx->esmtp || sx->lmtp)
      {
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
    if (!sx->early_pipe_active)
  #endif
      {
      sx->peer_offered = ehlo_response(sx->buffer,
        0 /* no TLS */
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
        | (sx->lmtp && ob->lmtp_ignore_quota ? OPTION_IGNQ : 0)
        | OPTION_DSN | OPTION_PIPE | OPTION_SIZE
        | OPTION_CHUNKING | OPTION_PRDR | OPTION_UTF8
        | (ob->size_addition >= 0 ? OPTION_SIZE : 0)
  #endif
        );
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
      if (tls_out.active.sock >= 0)
        sx->ehlo_resp.crypted_features = sx->peer_offered;
  #endif
      DEBUG(D_transport) debug_printf("%susing DSN\n",
                        sx->peer_offered & OPTION_DSN ? "" : "not ");
  
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
      if (  sx->early_pipe_ok
         && !sx->early_pipe_active
         && tls_out.active.sock >= 0
@@@ -3221,7 -3337,7 +3266,7 @@@ for (addr = sx->first_addr, address_cou
        case -2: return -2;                     /* non-MAIL read i/o error */
        default: return -1;                     /* any MAIL error */
  
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
        case -4: return -1;                     /* non-2xx for pipelined banner or EHLO */
        case -5: return -1;                     /* TLS first-read error */
  #endif
@@@ -3457,6 -3573,11 +3502,11 @@@ if (tblock->filter_command
       && *transport_filter_argv
       && **transport_filter_argv
       && sx.peer_offered & OPTION_CHUNKING
+ #ifndef DISABLE_DKIM
+     /* When dkim signing, chunking is handled even with a transport-filter */
+      && !(ob->dkim.dkim_private_key && ob->dkim.dkim_domain && ob->dkim.dkim_selector)
+      && !ob->dkim.force_bodyhash
+ #endif
       )
      {
      sx.peer_offered &= ~OPTION_CHUNKING;
@@@ -3555,7 -3676,7 +3605,7 @@@ if (  !(sx.peer_offered & OPTION_CHUNKI
  
      case -1: goto END_OFF;            /* Timeout on RCPT */
  
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
      case -5:                          /* TLS first-read error */
      case -4:  HDEBUG(D_transport)
                debug_printf("failed reaping pipelined cmd responses\n");
@@@ -3573,8 -3694,7 +3623,8 @@@ for handling the SMTP dot-handling prot
  well as body. Set the appropriate timeout value to be used for each chunk.
  (Haven't been able to make it work using select() for writing yet.) */
  
 -if (!(sx.peer_offered & OPTION_CHUNKING) && !sx.ok)
 +if (  !sx.ok
 +   && (!(sx.peer_offered & OPTION_CHUNKING) || !pipelining_active))
    {
    /* Save the first address of the next batch. */
    sx.first_addr = sx.next_addr;
@@@ -3711,7 -3831,7 +3761,7 @@@ els
  
        case -1: goto END_OFF;          /* Timeout on RCPT */
  
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
        case -5:                                /* TLS first-read error */
        case -4:  HDEBUG(D_transport)
                  debug_printf("failed reaping pipelined cmd responses\n");
        if (tcp_out_fastopen >= TFO_USED_DATA) setflag(addr, af_tcp_fastopen_data);
        }
        if (sx.pipelining_used) setflag(addr, af_pipelining);
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
        if (sx.early_pipe_active) setflag(addr, af_early_pipe);
  #endif
  #ifndef DISABLE_PRDR
@@@ -4065,7 -4185,7 +4115,7 @@@ if (!sx.ok
  
      else
        {
- #ifdef SUPPORT_PIPE_CONNECT
+ #ifndef DISABLE_PIPE_CONNECT
        /* If we were early-pipelinng and the actual EHLO response did not match
        the cached value we assumed, we could have detected it and passed a
        custom errno through to here.  It would be nice to RSET and retry right
@@@ -4805,7 -4925,7 +4855,7 @@@ retry_non_continued
          {
          for (address_item * addr = addrlist; addr; addr = addr->next)
            {
-           addr->basic_errno = 0;
+           addr->basic_errno = ERRNO_HOST_IS_LOCAL;
            addr->message = string_sprintf("%s transport found host %s to be "
              "local", tblock->name, host->name);
            }
diff --combined src/src/verify.c
index fc8cd84ea91f50119f2616c50d452a3ab4bd950a,1a44de1eaf6a240dd25662f8f3d20068243ea7ee..b68d604428b73b8f3c9b4f39dc05255430713c6b
@@@ -586,6 -586,10 +586,10 @@@ els
        log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
          "callout_random_local_part: %s", expand_string_message);
  
+   /* Compile regex' used by client-side smtp */
+   smtp_deliver_init();
    /* Default the connect and overall callout timeouts if not set, and record the
    time we are starting so that we can enforce it. */
  
@@@ -3478,7 -3482,7 +3482,7 @@@ els
        dns_basic_lookup(), we have a dnslist cache entry allocated and
        tree-inserted. So we may as well use it. */
  
 -      time_t soa_negttl = dns_expire_from_soa(dnsa);
 +      time_t soa_negttl = dns_expire_from_soa(dnsa, T_A);
        cb->expiry = soa_negttl ? soa_negttl : time(NULL) + ttl;
        break;
        }
diff --combined test/confs/0618
index a461065e5fdfde166f7aa91003da29e7d837333b,7359e16336d239e7053d5b74e6b8eed1f9e05fff..b9235339773b0913d18ac2995cbe285d9f3e18f4
@@@ -11,7 -11,7 +11,7 @@@ primary_hostname = myhost.test.e
  .ifdef OPT
  dsn_advertise_hosts = *
  .endif
 -delay_warning = 3s : 24h
 +delay_warning = 6s : 24h
  
  accept_8bitmime = false
  pipelining_advertise_hosts = :
@@@ -75,6 -75,7 +75,7 @@@ begin transport
  smtp:
    driver =    smtp
    event_action = ${acl {ev_log}}
+   hosts_try_fastopen = :
  
  bad_tpt:
    driver =    smtp
@@@ -89,5 -90,5 +90,5 @@@ tofile
  
  begin retry
  
 -*     *       F,1m,3s
 +*     *       F,2m,10s
  # End
diff --combined test/confs/5665
index da6e5197a9f6206cbfcb7e37b6f8bbab85cea8f7,0000000000000000000000000000000000000000..673ec66567fef9150c7b2ddf91ea25bd9764432c
mode 100644,000000..100644
--- /dev/null
@@@ -1,101 -1,0 +1,102 @@@
 +# Exim test configuration 5652
 +# OCSP stapling, server, multiple leaf-certs
 +
 +.include DIR/aux-var/tls_conf_prefix
 +
 +primary_hostname = server1.example.com
 +
 +# ----- Main settings -----
 +
 +acl_smtp_mail = check_mail
 +acl_smtp_rcpt = check_recipient
 +
 +log_selector = +tls_peerdn
 +
 +queue_only
 +queue_run_in_order
 +
 +tls_advertise_hosts = *
 +
 +CADIR = DIR/aux-fixed/exim-ca
 +DRSA = CADIR/example.com
 +DECDSA = CADIR/example_ec.com
 +
 +tls_certificate = DRSA/server1.example.com/server1.example.com.pem \
 +            : DECDSA/server1.example_ec.com/server1.example_ec.com.pem
 +tls_privatekey =  DRSA/server1.example.com/server1.example.com.unlocked.key \
 +            : DECDSA/server1.example_ec.com/server1.example_ec.com.unlocked.key
 +tls_ocsp_file =   DRSA/server1.example.com/server1.example.com.ocsp.good.resp \
 +            : DECDSA/server1.example_ec.com/server1.example_ec.com.ocsp.good.resp
 +
 +
 +.ifdef _HAVE_GNUTLS
 +tls_require_ciphers = NORMAL:!VERS-ALL:+VERS-TLS1.2:+VERS-TLS1.0
 +.endif
 +.ifdef _OPT_OPENSSL_NO_TLSV1_3_X
 +openssl_options = +no_tlsv1_3
 +.endif
 +
 +# ------ ACL ------
 +
 +begin acl
 +
 +check_mail:
 +  accept   logwrite = acl_mail: ocsp in status: $tls_in_ocsp \
 +    (${listextract {${eval:$tls_in_ocsp+1}} \
 +              {notreq:notresp:vfynotdone:failed:verified}})
 +
 +check_recipient:
 +  accept
 +
 +
 +# ----- Routers -----
 +
 +begin routers
 +
 +client:
 +  driver = manualroute
 +  condition = ${if !eq {SERVER}{server}}
 +  route_list = * 127.0.0.1
 +  self = send
 +  transport = remote_delivery
 +  errors_to = ""
 +
 +srvr:
 +  driver = accept
 +  retry_use_local_part
 +  transport = local_delivery
 +
 +
 +# ----- Transports -----
 +
 +begin transports
 +
 +remote_delivery:
 +  driver =                    smtp
 +  port =                      PORT_D
++  hosts_try_fastopen =                :
 +  hosts_require_tls =         *
 +.ifdef _HAVE_GNUTLS
 +  tls_require_ciphers =               NONE:\
 +                              ${if eq {SELECTOR}{auth_ecdsa} \
 +                                      {+SIGN-ECDSA-SHA512:+VERS-TLS-ALL:+KX-ALL:} \
 +                                      {+SIGN-RSA-SHA256:+VERS-TLS-ALL:+ECDHE-RSA:+DHE-RSA:+RSA:}}\
 +                              +CIPHER-ALL:+MAC-ALL:+COMP-NULL:+CURVE-ALL:+CTYPE-X509
 +.endif
 +.ifdef _HAVE_OPENSSL
 +  tls_require_ciphers =               ${if eq {SELECTOR}{auth_ecdsa} {ECDSA:RSA:!COMPLEMENTOFDEFAULT} {RSA}}
 +.endif
 +  hosts_require_ocsp =                *
 +  tls_verify_certificates =   CADIR/\
 +                              ${if eq {SELECTOR}{auth_ecdsa} \
 +                                      {example_ec.com/server1.example_ec.com/ca_chain.pem}\
 +                                      {example.com/server1.example.com/ca_chain.pem}}
 +  tls_verify_cert_hostnames = :
 +
 +local_delivery:
 +  driver = appendfile
 +  file = DIR/test-mail/$local_part
 +  headers_add = TLS: cipher=$tls_cipher peerdn=$tls_peerdn
 +  user = CALLER
 +
 +# End
diff --combined test/log/2102
index dfcfc1b67ab004706cbc6c6c5889fc9cb245ba1b,bddb8e97397e839c6fae8d46e614146c78a8993e..91761cd68e3539ba775949ed048a7715ef777880
  1999-03-02 09:44:33 der_b64 MIIDuDCCAqCgAwIBAgICAMkwDQYJKoZIhvcNAQELBQAwNzEUMBIGA1UEChMLZXhhbXBsZS5jb20xHzAdBgNVBAMTFmNsaWNhIFNpZ25pbmcgQ2VydCByc2EwHhcNMTIxMTAxMTI0MDA0WhcNMzcxMjAxMTI0MDA0WjAeMRwwGgYDVQQDExNzZXJ2ZXIyLmV4YW1wbGUuY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA52Rfiv2Igy0NiaKN5gc0VPLbEoHngkdJWv3wEORp+iFl6skQRbsCylT8djJ2pvHstFpnzSodF3Wwjj2/EDuj3iKBzN9HeXJOvJz8j9Si1xkgCxJeUjPGgYcvKdxybaZAOpi9l3xwPCCEXN4JBq/WaQQ9+eP1PczeMNfvFtXma+VcHXG743ttPOv7eSMr0JxQl3zjQvYGOhFP/KAw6jh/N6YPqii9kV0cC/ubeVzpqJ5/+hndx5YrmAu39N5qzwWujhDPkFNSgCJUhfkEiMaQiPxFxDTbUzWnQ5jpAQ5El4WJVkGWkqxose1bOjSSNzFPJt59YtxxJC3IWN3UtGODTwIDAQABo4HmMIHjMA4GA1UdDwEB/wQEAwIE8DAgBgNVHSUBAf8EFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwTgYDVR0jBEcwRYANQUFidHdDeGNYZ2IwUaExpC8wLTEUMBIGA1UEChMLZXhhbXBsZS5jb20xFTATBgNVBAMTDGNsaWNhIENBIHJzYYIBQjA0BggrBgEFBQcBAQQoMCYwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vc2NwLmV4YW1wbGUuY29tLzApBgNVHREEIjAgghNzZXJ2ZXIyLmV4YW1wbGUuY29tggkqLnRlc3QuZXgwDQYJKoZIhvcNAQELBQADggEBALHOkZkvHLpNm0QSof09vmmdNFE6/+0TCIoPExeqqSOsy4NsF+Ha46WttjJRSVtbhRxF8jxEU7btPiFgQUaOcJZTwQPDhmQSOPNO8GS46oJ57aQ7U7O+X3M1sVS5Pa2IzE6vrJSh349/CNbTA8WPQdWLlxVJhJXAcZNtaEu6lCsZuDSMTpAsW5I4+snyrm3yvP5t0eD28K5LgCKePX962drkAOP6XGQ51VnbMQ7b1TSdQedtYKIpR3VKUvG5Ky/+0c+Rmwfi2aQ8oXXwekzJyS5jvovdVVsdhO68It+Rz/zursN5Pn+Gj1YuQNUs2nDrGHN+VIIFpgWUjLZO4bcJctY=
  1999-03-02 09:44:33 cipher: TLS1.x:ke-RSA-AES256-SHAnnn:xxx
  1999-03-02 09:44:33 cipher_ TLS1.x:ke_RSA_WITH_ci_mac
++<<<<<<< HEAD
 +1999-03-02 09:44:33 ver:    TLS1.x
++=======
+ 1999-03-02 09:44:33 ver:    TLSv1.x
++>>>>>>> 4.next
  1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@test.ex H=[ip4.ip4.ip4.ip4] P=smtps X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=yes DN="/CN=server2.example.com" S=sss
  1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D
  1999-03-02 09:44:33 Our cert SN: <CN=server1.example_ec.com>
diff --combined test/runtest
index 87c78070e45dd9a2f1aac88acf912deafbc4619c,30315044fa6f62e3dd74ea4246450bab8209fce6..f53b9d72bf7c2e4beb00b3cbbc98a6582080b8f0
@@@ -64,7 -64,7 +64,7 @@@ my $force_continue = 0
  my $force_update = 0;
  my $log_failed_filename = 'failed-summary.log';
  my $log_summary_filename = 'run-summary.log';
 -my $more = 'less -XF';
 +my @more = qw'less -XF';
  my $optargs = '';
  my $save_output = 0;
  my $server_opts = '';
@@@ -800,12 -800,6 +800,12 @@@ RESET_AFTER_EXTRA_LINE_READ
    # Also, the length of space at the end of the host line is dependent
    # on the length of the longest line, so strip it also on otherwise
    # un-rewritten lines like localhost
 +  #
 +  # host 127.0.0.1     [127.0.0.1]
 +  # host 10.0.0.1      [10.0.0.1]-
 +  #
 +  # host 127.0.0.1     [127.0.0.1]--
 +  # host 169.16.16.16  [169.16.16.10]
  
    s/^\s+host\s(\S+)\s+(\S+)/  host $1 $2/;
    s/^\s+(host\s\S+\s\S+)\s+(port=.*)/  host $1 $2/;
    s/(^|\W)\K\Q$parm_ipv6_stripped\E/ip6:ip6:ip6:ip6:ip6:ip6:ip6:ip6/g;
    s/\b\Q$parm_ipv4r\E\b/ip4-reverse/g;
    s/(^|\W)\K\Q$parm_ipv6r\E/ip6-reverse/g;
 -  s/^(\s+host\s\S+\s+\[\S+\]) +$/$1 /;
 +  s/^\s+host\s\S+\s+\[\S+\]\K +$//;     # strip, not collapse the trailing whitespace
  
  
    # ======== Test network IP addresses ========
    last if !defined $_;
  
  
+   # SRS timestamps and signatures vary by hostname and from run to run
+   s/SRS0=....=..=[^=]+=[^@]+\@test.ex/SRS0=ZZZZ=YY=the.local.host.name=CALLER\@test.ex/;
    # ======== Output from the "fd" program about open descriptors ========
    # The statuses seem to be different on different operating systems, but
    # at least we'll still be checking the number of open fd's.
  
      s/(?<=^>>>>>>>>>>>>>>>> Exim pid=)\d+(?= terminating)/pppp/;
      s/^(proxy-proc \w{5}-pid) \d+$/$1 pppp/;
+     s/^(?:\s*\d+ )(exec .* -oPX)$/pppp $1/;
  
      # IP address lookups use gethostbyname() when IPv6 is not supported,
      # and gethostbyname2() or getipnodebyname() when it is.
      next if /OpenSSL compile-time version: OpenSSL \d+[\.\da-z]+/;
      next if /OpenSSL runtime version: OpenSSL \d+[\.\da-z]+/;
  
+     # this is timing-dependent
+     next if /^OpenSSL: creating STEK$/;
      # drop lookups
      next if /^Lookups \(built-in\):/;
      next if /^Loading lookup modules from/;
      next if /DNS lookup of \S+ \(AAAA\) using fakens/;
      next if / in dns_ipv4_lookup?/;
      next if / writing neg-cache entry for .*AAAA/;
 +    next if /^faking res_search\(AAAA\) response length as 65535/;
  
      if (/DNS lookup of \S+ \(AAAA\) gave NO_DATA/)
        {
        next;
        }
  
 +    # Non-TLS bulds have a different Recieved: header expansion
 +    s/^((.*)\t}}}}by \$primary_hostname \$\{if def:received_protocol \{with \$received_protocol }})\(Exim \$version_number\)$/$1\${if def:tls_in_cipher_std { tls \$tls_in_cipher_std\n$2\t}}(Exim \$version_number)/;
 +    s/^((\s*).*considering: with \$received_protocol }})\(Exim \$version_number\)$/$1\${if def:tls_in_cipher_std { tls \$tls_in_cipher_std\n$2\t}}(Exim \$version_number)/;
 +    if (/condition: def:tls_in_cipher_std$/)
 +      {
 +      $_= <IN>; $_= <IN>; $_= <IN>; $_= <IN>;
 +      $_= <IN>; $_= <IN>; $_= <IN>; $_= <IN>;
 +      $_= <IN>; $_= <IN>; $_= <IN>; next;
 +      }
 +
 +
      # Skip tls_advertise_hosts and hosts_require_tls checks when the options
      # are unset, because tls ain't always there.
  
      next if /^DKIM \[[^[]+\] (Header hash|b) computed:/;
  
      # Not all platforms support TCP Fast Open, and the compile omits the check
-     if (s/\S+ in hosts_try_fastopen\? (no \(option unset\)|yes \(matched "\*"\))\n$//)
+     if (s/\S+ in hosts_try_fastopen\? (no \(option unset\)|no \(end of list\)|yes \(matched "\*"\))\n$//)
        {
        chomp;
        $_ .= <IN>;
      # Platform differences in errno strings
      s/  SMTP\(Operation timed out\)<</  SMTP(Connection timed out)<</;
  
+     # Platform differences for errno values (eg. Hurd)
+     s/^errno = \d+$/errno = EEE/;
+     s/^writing error \d+: /writing error EEE: /;
      # When Exim is checking the size of directories for maildir, it uses
      # the check_dir_size() function to scan directories. Of course, the order
      # of the files that are obtained using readdir() varies from system to
  
      # openssl version variances
      s/(TLS error on connection [^:]*: error:)[0-9A-F]{8}(:system library):(?:fopen|func\(4095\)):(No such file or directory)$/$1xxxxxxxx$2:fopen:$3/;
 -    s/(DANE attempt failed.*error:)[0-9A-F]{8}(:SSL routines:)(ssl3_get_server_certificate|tls_process_server_certificate|CONNECT_CR_CERT)(?=:certificate verify failed$)/$1xxxxxxxx$2ssl3_get_server_certificate/;
 +    s/(DANE attempt failed.*error:)[0-9A-F]{8}(:SSL routines:)(?:(?i)ssl3_get_server_certificate|tls_process_server_certificate|CONNECT_CR_CERT)(?=:certificate verify failed$)/$1xxxxxxxx$2ssl3_get_server_certificate/;
      s/(DKIM: validation error: )error:[0-9A-F]{8}:rsa routines:(?:(?i)int_rsa_verify|CRYPTO_internal):(?:bad signature|algorithm mismatch)$/$1Public key signature verification has failed./;
      s/ARC: AMS signing: privkey PEM-block import: error:\K[0-9A-F]{8}:(PEM routines):get_name:(no start line)/0906D06C:$1:PEM_read_bio:$2/;
  
      s/(?:\[[^\]]*\]:|port )\K$parm_port_n/PORT_N/;
      s/I=\[[^\]]*\]:\K\d+/ppppp/;
  
+     # Platform differences for errno values (eg. Hurd).  Leave 0 and negative numbers alone.
+     s/R=\w+ T=\w+ defer\K \([1-9]\d*\): / (EEE): /;
      }
  
    # ======== mail ========
@@@ -1546,7 -1543,7 +1561,7 @@@ if (! -e $sf_current
        log_failure($log_failed_filename, $testno, $rf);
        log_test($log_summary_filename, $testno, 'F') if ($force_continue);
      }
-     return 1 if /^c$/i && $rf !~ /paniclog/ && $rsf !~ /paniclog/;
+     return 1 if /^c$/i && $rf !~ /paniclog/ && (!defined $rsf || $rsf !~ /paniclog/);
      last if (/^[sc]$/);
      }
  
        print "\n";
        print "------------ $f -----------\n"
          if (defined $rf && -s $rf && defined $rsf && -s $rsf);
 -      system("$more '$f'");
 +      system @more => $f;
        }
      }
  
@@@ -1639,25 -1636,26 +1654,25 @@@ if (-e $sf_current
          }
        }
  
 -    open(MUNGED, '>', $mf) || tests_exit(-1, "Failed to open $mf: $!");
 -    for ($i = 0; $i < @munged; $i++)
 -      { print MUNGED $munged[$i]; }
 -    close(MUNGED);
 +    open(my $fh, '>', $mf) or tests_exit(-1, "Failed to open $mf: $!");
 +    print $fh @munged;
      }
  
    # Deal with log sorting
  
    if ($sortfile)
      {
 -    my(@munged, $i, $j);
  
 -    open(MUNGED, $mf) || tests_exit(-1, "Failed to open $mf: $!");
 -    @munged = <MUNGED>;
 -    close(MUNGED);
 +    my @munged = do {
 +      open(my $fh, '<', $mf) or tests_exit(-1, "Failed to open $mf: $!");
 +      <$fh>;
 +    };
  
 -    for ($i = 0; $i < @munged; $i++)
 +    for (my $i = 0; $i < @munged; $i++)
        {
        if ($munged[$i] =~ /^[-\d]{10}\s[:\d]{8}\s[-A-Za-z\d]{16}\s[-=*]>/)
          {
 +        my $j;
          for ($j = $i + 1; $j < @munged; $j++)
            {
            last if $munged[$j] !~
          }
        }
  
 -    open(MUNGED, ">$mf") || tests_exit(-1, "Failed to open $mf: $!");
 -    print MUNGED "**NOTE: The delivery lines in this file have been sorted.\n";
 -    for ($i = 0; $i < @munged; $i++)
 -      { print MUNGED $munged[$i]; }
 -    close(MUNGED);
 +    open(my $fh, '>', $mf) or tests_exit(-1, "Failed to open $mf: $!");
 +    print $fh "**NOTE: The delivery lines in this file have been sorted.\n";
 +    print $fh @munged;
      }
  
    # Do the comparison
    # Handle comparison failure
  
    print "** Comparison of $mf with $sf_current failed";
 -  system("$more test-cf");
 +  system @more => 'test-cf';
  
    print "\n";
    for (;;)
@@@ -1712,7 -1712,8 +1727,7 @@@ els
      # if we deal with a flavour file, we can't delete it, because next time the generic
      # file would be used again
      if ($sf_current eq $sf_flavour) {
 -      open(FOO, ">$sf_current");
 -      close(FOO);
 +      open(my $fh, '>', $sf_current);
      }
      else {
        tests_exit(-1, "Failed to unlink $sf_current") if !unlink($sf_current);
@@@ -1778,7 -1779,7 +1793,7 @@@ $munges 
      },
  
      'debug_pid' =>
 -    { 'stderr' => 's/(^\s{0,4}|(?<=Process )|(?<=child ))\d{1,5}/ppppp/g' },
 +    { 'stderr' => 's/(^\s{0,4}|(?<=Process )|(?<=child ))\d+/ppppp/g' },
  
      'optional_dsn_info' =>
      { 'mail' => '/^(X-(Remote-MTA-(smtp-greeting|helo-response)|Exim-Diagnostic|(body|message)-linecount):|Remote-MTA: X-ip;)/'
@@@ -2174,37 -2175,34 +2189,37 @@@ if (/^dbmbuild\s+(\S+)\s+(\S+)/
  
  if (/^dump\s+(\S+)/)
    {
 -  my($which) = $1;
 -  my(@temp);
 +  my $which  = $1;
    print ">> ./eximdir/exim_dumpdb $parm_cwd/spool $which\n" if $debug;
 -  open(IN, "./eximdir/exim_dumpdb $parm_cwd/spool $which |");
 -  open(OUT, ">>test-stdout");
 -  print OUT "+++++++++++++++++++++++++++\n";
 +  open(my $in, "-|", './eximdir/exim_dumpdb', "$parm_cwd/spool", $which) or die "Can't run exim_dumpdb: $!";
 +  open(my $out, ">>test-stdout");
 +  print $out "+++++++++++++++++++++++++++\n";
  
    if ($which eq "retry")
      {
 -    $/ = "\n  ";
 -    @temp = <IN>;
 -    $/ = "\n";
 -
 -    @temp = sort {
 -                   my($aa) = split(' ', $a);
 -                   my($bb) = split(' ', $b);
 -                   return $aa cmp $bb;
 -                 } @temp;
 -
 +    # the sort key is the first part of the retry db dump line, but for
 +    # sorting we (temporarly) replace the own hosts ipv4 with a munged
 +    # version, which matches the munging that is done later
 +    # Why? We must ensure sure, that 127.0.0.1 always sorts first
 +    # map-sort-map: Schwartz's transformation
 +    # test 0099
 +    my @temp = map  { $_->[1] }
 +               sort { $a->[0] cmp $b->[0] }
 +               #map  { [ (split)[0] =~ s/\Q$parm_ipv4/ip4.ip4.ip4.ip4/gr, $_ ] }  # this is too modern for 5.10.1
 +               map  {
 +                (my $k = (split)[0]) =~ s/\Q$parm_ipv4/ip4.ip4.ip4.ip4/g;
 +                [ $k, $_ ]
 +               }
 +               do { local $/ = "\n  "; <$in> };
      foreach $item (@temp)
        {
        $item =~ s/^\s*(.*)\n(.*)\n?\s*$/$1\n$2/m;
 -      print OUT "  $item\n";
 +      print $out "  $item\n";
        }
      }
    else
      {
 -    @temp = <IN>;
 +    my @temp = <$in>;
      if ($which eq "callout")
        {
        @temp = sort {
                     return $aa cmp $bb;
                     } @temp;
        }
 -    print OUT @temp;
 +    print $out @temp;
      }
 -
 -  close(IN);
 -  close(OUT);
 +  close($in); # close it explicitly, otherwise $? does not get set
    return 1;
    }
  
@@@ -2558,24 -2558,9 +2573,24 @@@ elsif (/^((?i:[A-Z\d_]+=\S+\s+)+)?(\d+)
  
    if ($args =~ /\$msg/)
      {
 -    my @listcmd  = ("$parm_cwd/eximdir/exim", '-bp',
 +    my($queuespec);
 +    if ($args =~ /-qG\w+/) { $queuespec = $&; }
 +
 +    my @listcmd;
 +
 +    if (defined $queuespec)
 +      {
 +      @listcmd  = ("$parm_cwd/eximdir/exim", '-bp',
 +                 $queuespec,
 +                   "-DEXIM_PATH=$parm_cwd/eximdir/exim",
 +                   -C => "$parm_cwd/test-config");
 +      }
 +    else
 +      {
 +      @listcmd  = ("$parm_cwd/eximdir/exim", '-bp',
                     "-DEXIM_PATH=$parm_cwd/eximdir/exim",
                     -C => "$parm_cwd/test-config");
 +      }
      print ">> Getting queue list from:\n>>    @listcmd\n" if $debug;
      # We need the message ids sorted in ascending order.
      # Message id is: <timestamp>-<pid>-<fractional-time>. On some systems (*BSD) the
@@@ -2800,7 -2785,7 +2815,7 @@@ umask 022
  #       Check for the "less" command             #
  ##################################################
  
 -$more = 'more' if system('which less >/dev/null 2>&1') != 0;
 +@more = 'more' if system('which less >/dev/null 2>&1') != 0;
  
  
  
@@@ -2825,7 -2810,7 +2840,7 @@@ Getopt::Long::Configure qw(no_getopt_co
  GetOptions(
      'debug'    => sub { $debug          = 1; $cr   = "\n" },
      'diff'     => sub { $cf             = 'diff -u' },
 -    'continue' => sub { $force_continue = 1; $more = 'cat' },
 +    'continue' => sub { $force_continue = 1; @more = 'cat' },
      'update'   => \$force_update,
      'ipv4!'    => \$have_ipv4,
      'ipv6!'    => \$have_ipv6,
@@@ -3450,17 -3435,16 +3465,17 @@@ while (not ($parm_ipv4 and $parm_ipv6) 
    {
    if (/^(?:[0-9]+: )?([a-z0-9]+): /) { $ifname = $1; }
  
-   if (not $parm_ipv4 and /^\s*inet(?:\saddr)?:?\s?(\d+\.\d+\.\d+\.\d+)(?:\/\d+)?\s/i)
+   if (not $parm_ipv4 and /^\s*inet(?:\saddr(?:ess))?:?\s*(\d+\.\d+\.\d+\.\d+)(?:\/\d+)?\s/i)
      {
 -    # It would ne nice to be able to vary the /16 used for manyhome; we could take
 +    # It would be nice to be able to vary the /16 used for manyhome; we could take
      # an option to runtest used here - but we'd also have to pass it on to fakens.
      # Possibly an environment variable?
      next if $1 eq '0.0.0.0' or $1 =~ /^(?:127|10\.250)\./;
      $parm_ipv4 = $1;
      }
  
 -  if (not $parm_ipv6 and /^\s*inet6(?:\saddr(?:ess))?:?\s*([abcdef\d:]+)(?:%[^ \/]+)?(?:\/\d+)?/i)
 +  if (   (not $parm_ipv6 or $parm_ipv6 =~ /%/)
-      and /^\s*inet6(?:\saddr)?:?\s?([abcdef\d:]+)(?:%[^ \/]+)?(?:\/\d+)?/i)
++     and /^\s*inet6(?:\saddr(?:ess))?:?\s*([abcdef\d:]+)(?:%[^ \/]+)?(?:\/\d+)?/i)
      {
      next if $1 eq '::' or $1 eq '::1' or $1 =~ /^ff00/i or $1 =~ /^fe80::1/i;
      $parm_ipv6 = $1;
@@@ -3829,10 -3813,6 +3844,10 @@@ DIR: for (my $i = 0; $i < @test_dirs; $
        }
        unlink("$parm_cwd/test-config");
          }
 +      elsif (/^ipv6-non-linklocal/)
 +      {
 +      if ($parm_ipv6 =~ /%/) { $wantthis = 0; last; }
 +      }
        else
          {
          tests_exit(-1, "Unknown line in \"scripts/$testdir/REQUIRES\": \"$_\"");
@@@ -3973,7 -3953,7 +3988,7 @@@ if ($have_ipv4 && $parm_ipv4 ne "127.0.
        tests_exit(-1, "Failed  to open $parm_cwd/dnszones/db.ip4.$components[0]: $!");
      print OUT "$components[3].$components[2].$components[1]  PTR  $parm_hostname.\n\n";
      close(OUT);
 -    } 
 +    }
    else
      {
      open(OUT, ">$parm_cwd/dnszones/db.ip4.$components[0]") ||
@@@ -4325,11 -4305,11 +4340,11 @@@ foreach $test (@test_list
          last if /^[rc]$/i;
          if (/^e$/i)
            {
 -          system("$more test-stderr");
 +          system @more => 'test-stderr';
            }
          elsif (/^o$/i)
            {
 -          system("$more test-stdout");
 +          system @more => 'test-stdout';
            }
          }
  
diff --combined test/stderr/5410
index 0461d84ed5830ef80853d707317911ab7097e655,9953eee7cd2f31e396d237708388f899e4a8f940..b29fee2e723846d96ccb3a5bd50b547566f434b5
@@@ -1,5 -1,4 +1,5 @@@
  Exim version x.yz ....
 +adding SSLKEYLOGFILE=TESTSUITE/spool/sslkeys
  configuration file is TESTSUITE/test-config
  admin user
   in hosts_connection_nolog? no (option unset)
@@@ -131,7 -130,7 +131,7 @@@ sync_responses expect rcp
    SMTP<< 250 Accepted
  holding verify callout open for cutthrough delivery
  ----------- end cutthrough setup ------------
- processing "accept" (TESTSUITE/test-config 55)
+ processing "accept" (TESTSUITE/test-config 56)
  accept: condition test succeeded in inline ACL
  end of inline ACL: ACCEPT
    SMTP>> DATA
        for $received_for}}
    ├──expanding:  ($tls_in_ver)
    ├─────result:  ()
 -  ╰───skipping: result is not used
 - ├──condition: def:tls_in_cipher_std
 - ├─────result: false
 -  ╭───scanning:  tls $tls_in_cipher_std
 -      }}(Exim $version_number)
 -      ${if def:sender_address {(envelope-from <$sender_address>)
 -      }}id $message_exim_id${if def:received_for {
 -      for $received_for}}
 -  ├──expanding:  tls $tls_in_cipher_std
 -      
 -  ├─────result:  tls 
 -      
    ╰───skipping: result is not used
   ├──condition: def:sender_address
   ├─────result: true
@@@ -273,7 -284,6 +273,7 @@@ LOG: smtp_connection MAI
    SMTP connection from CALLER closed by QUIT
  >>>>>>>>>>>>>>>> Exim pid=pppp (msg setup toplevel) terminating with rc=0 >>>>>>>>>>>>>>>>
  Exim version x.yz ....
 +adding SSLKEYLOGFILE=TESTSUITE/spool/sslkeys
  configuration file is TESTSUITE/test-config
  admin user
   in hosts_connection_nolog? no (option unset)
@@@ -373,7 -383,7 +373,7 @@@ sync_responses expect rcp
    SMTP<< 250 Accepted
  holding verify callout open for cutthrough delivery
  ----------- end cutthrough setup ------------
- processing "accept" (TESTSUITE/test-config 55)
+ processing "accept" (TESTSUITE/test-config 56)
  accept: condition test succeeded in inline ACL
  end of inline ACL: ACCEPT
    SMTP>> DATA
        for $received_for}}
    ├──expanding:  ($tls_in_ver)
    ├─────result:  ()
 -  ╰───skipping: result is not used
 - ├──condition: def:tls_in_cipher_std
 - ├─────result: false
 -  ╭───scanning:  tls $tls_in_cipher_std
 -      }}(Exim $version_number)
 -      ${if def:sender_address {(envelope-from <$sender_address>)
 -      }}id $message_exim_id${if def:received_for {
 -      for $received_for}}
 -  ├──expanding:  tls $tls_in_cipher_std
 -      
 -  ├─────result:  tls 
 -      
    ╰───skipping: result is not used
   ├──condition: def:sender_address
   ├─────result: true
@@@ -515,7 -537,6 +515,7 @@@ LOG: smtp_connection MAI
    SMTP connection from CALLER closed by QUIT
  >>>>>>>>>>>>>>>> Exim pid=pppp (msg setup toplevel) terminating with rc=0 >>>>>>>>>>>>>>>>
  Exim version x.yz ....
 +adding SSLKEYLOGFILE=TESTSUITE/spool/sslkeys
  configuration file is TESTSUITE/test-config
  admin user
   in hosts_connection_nolog? no (option unset)
@@@ -615,7 -636,7 +615,7 @@@ sync_responses expect rcp
    SMTP<< 250 Accepted
  holding verify callout open for cutthrough delivery
  ----------- end cutthrough setup ------------
- processing "accept" (TESTSUITE/test-config 55)
+ processing "accept" (TESTSUITE/test-config 56)
  accept: condition test succeeded in inline ACL
  end of inline ACL: ACCEPT
    SMTP>> DATA
        for $received_for}}
    ├──expanding:  ($tls_in_ver)
    ├─────result:  ()
 -  ╰───skipping: result is not used
 - ├──condition: def:tls_in_cipher_std
 - ├─────result: false
 -  ╭───scanning:  tls $tls_in_cipher_std
 -      }}(Exim $version_number)
 -      ${if def:sender_address {(envelope-from <$sender_address>)
 -      }}id $message_exim_id${if def:received_for {
 -      for $received_for}}
 -  ├──expanding:  tls $tls_in_cipher_std
 -      
 -  ├─────result:  tls 
 -      
    ╰───skipping: result is not used
   ├──condition: def:sender_address
   ├─────result: true
diff --combined test/stderr/5420
index b0f1f0a094e79730098c0417a2263e6322a0d621,d8325430732e5e101ced85b36dbe013a14fdf213..b06eb8d33fc6565b1474d05bb9ba19f06809a643
@@@ -1,5 -1,4 +1,5 @@@
  Exim version x.yz ....
 +adding SSLKEYLOGFILE=TESTSUITE/spool/sslkeys
  configuration file is TESTSUITE/test-config
  admin user
   in hosts_connection_nolog? no (option unset)
@@@ -132,7 -131,7 +132,7 @@@ sync_responses expect rcp
    SMTP<< 250 Accepted
  holding verify callout open for cutthrough delivery
  ----------- end cutthrough setup ------------
- processing "accept" (TESTSUITE/test-config 54)
+ processing "accept" (TESTSUITE/test-config 55)
  accept: condition test succeeded in inline ACL
  end of inline ACL: ACCEPT
    SMTP>> DATA
        for $received_for}}
    ├──expanding:  ($tls_in_ver)
    ├─────result:  ()
 -  ╰───skipping: result is not used
 - ├──condition: def:tls_in_cipher_std
 - ├─────result: false
 -  ╭───scanning:  tls $tls_in_cipher_std
 -      }}(Exim $version_number)
 -      ${if def:sender_address {(envelope-from <$sender_address>)
 -      }}id $message_exim_id${if def:received_for {
 -      for $received_for}}
 -  ├──expanding:  tls $tls_in_cipher_std
 -      
 -  ├─────result:  tls 
 -      
    ╰───skipping: result is not used
   ├──condition: def:sender_address
   ├─────result: true
@@@ -274,7 -285,6 +274,7 @@@ LOG: smtp_connection MAI
    SMTP connection from CALLER closed by QUIT
  >>>>>>>>>>>>>>>> Exim pid=pppp (msg setup toplevel) terminating with rc=0 >>>>>>>>>>>>>>>>
  Exim version x.yz ....
 +adding SSLKEYLOGFILE=TESTSUITE/spool/sslkeys
  configuration file is TESTSUITE/test-config
  admin user
   in hosts_connection_nolog? no (option unset)
@@@ -374,7 -384,7 +374,7 @@@ sync_responses expect rcp
    SMTP<< 250 Accepted
  holding verify callout open for cutthrough delivery
  ----------- end cutthrough setup ------------
- processing "accept" (TESTSUITE/test-config 54)
+ processing "accept" (TESTSUITE/test-config 55)
  accept: condition test succeeded in inline ACL
  end of inline ACL: ACCEPT
    SMTP>> DATA
        for $received_for}}
    ├──expanding:  ($tls_in_ver)
    ├─────result:  ()
 -  ╰───skipping: result is not used
 - ├──condition: def:tls_in_cipher_std
 - ├─────result: false
 -  ╭───scanning:  tls $tls_in_cipher_std
 -      }}(Exim $version_number)
 -      ${if def:sender_address {(envelope-from <$sender_address>)
 -      }}id $message_exim_id${if def:received_for {
 -      for $received_for}}
 -  ├──expanding:  tls $tls_in_cipher_std
 -      
 -  ├─────result:  tls 
 -      
    ╰───skipping: result is not used
   ├──condition: def:sender_address
   ├─────result: true
@@@ -516,7 -538,6 +516,7 @@@ LOG: smtp_connection MAI
    SMTP connection from CALLER closed by QUIT
  >>>>>>>>>>>>>>>> Exim pid=pppp (msg setup toplevel) terminating with rc=0 >>>>>>>>>>>>>>>>
  Exim version x.yz ....
 +adding SSLKEYLOGFILE=TESTSUITE/spool/sslkeys
  configuration file is TESTSUITE/test-config
  admin user
   in hosts_connection_nolog? no (option unset)
@@@ -616,7 -637,7 +616,7 @@@ sync_responses expect rcp
    SMTP<< 250 Accepted
  holding verify callout open for cutthrough delivery
  ----------- end cutthrough setup ------------
- processing "accept" (TESTSUITE/test-config 54)
+ processing "accept" (TESTSUITE/test-config 55)
  accept: condition test succeeded in inline ACL
  end of inline ACL: ACCEPT
    SMTP>> DATA
        for $received_for}}
    ├──expanding:  ($tls_in_ver)
    ├─────result:  ()
 -  ╰───skipping: result is not used
 - ├──condition: def:tls_in_cipher_std
 - ├─────result: false
 -  ╭───scanning:  tls $tls_in_cipher_std
 -      }}(Exim $version_number)
 -      ${if def:sender_address {(envelope-from <$sender_address>)
 -      }}id $message_exim_id${if def:received_for {
 -      for $received_for}}
 -  ├──expanding:  tls $tls_in_cipher_std
 -      
 -  ├─────result:  tls 
 -      
    ╰───skipping: result is not used
   ├──condition: def:sender_address
   ├─────result: true
diff --combined test/stdout/0572
index 40c8b35296f3db1fda7540d5e6ca84d720d42fc3,06cc972a146817f81d301d5f2a26e415a1f07e51..4d428078f3cc70ede0514e057cfdac98c1079ca8
@@@ -41,7 -41,7 +41,7 @@@ data_timeout = 5
  delay_after_cutoff
  dns_qualify_single
  no_dns_search_parents
 -dnssec_request_domains = 
 +dnssec_request_domains = *
  dnssec_require_domains = 
  dscp = 
  fallback_hosts = 
@@@ -58,7 -58,7 +58,7 @@@ no_hosts_randomiz
  hosts_require_auth = 
  hosts_try_auth = 
  hosts_try_chunking = *
- hosts_try_fastopen = *
+ hosts_try_fastopen = :
  hosts_try_prdr = *
  interface = ip4.ip4.ip4.ip4
  keepalive
@@@ -78,8 -78,7 +78,8 @@@ OPT 
  # 1 "TESTSUITE/aux-var/std_conf_prefix"
  # 1 "TESTSUITE/aux-var/std_conf_prefix"
  # 1 "TESTSUITE/aux-var/tls_conf_prefix"
 -keep_environment = PATH:SSLKEYLOGFILE:EXIM_TESTHARNESS_DISABLE_OCSPVALIDITYCHECK
 +keep_environment = PATH:EXIM_TESTHARNESS_DISABLE_OCSPVALIDITYCHECK
 +add_environment = SSLKEYLOGFILE=TESTSUITE/spool/sslkeys
  exim_path = TESTSUITE/eximdir/exim
  host_lookup_order = bydns
  spool_directory = TESTSUITE/spool
@@@ -111,6 -110,7 +111,7 @@@ begin transport
      driver = smtp
      interface = ip4.ip4.ip4.ip4
      port = 1224
+     hosts_try_fastopen = :
      debug_print = transport_name <$transport_name>
  # Exim Configuration (X)
  # 1 "TESTSUITE/test-config"
@@@ -119,8 -119,7 +120,8 @@@ OPT 
  # 1 "TESTSUITE/aux-var/std_conf_prefix"
  # 1 "TESTSUITE/aux-var/std_conf_prefix"
  # 1 "TESTSUITE/aux-var/tls_conf_prefix"
 -keep_environment = PATH:SSLKEYLOGFILE:EXIM_TESTHARNESS_DISABLE_OCSPVALIDITYCHECK
 +keep_environment = PATH:EXIM_TESTHARNESS_DISABLE_OCSPVALIDITYCHECK
 +add_environment = SSLKEYLOGFILE=TESTSUITE/spool/sslkeys
  exim_path = TESTSUITE/eximdir/exim
  host_lookup_order = bydns
  spool_directory = TESTSUITE/spool
@@@ -148,4 -147,5 +149,5 @@@ my_smtp
  driver = smtp
  interface = ip4.ip4.ip4.ip4
  port = 1224
+ hosts_try_fastopen = :
  debug_print = transport_name <$transport_name>