[exim-cvs] ATRN provider

Góra strony
Delete this message
Reply to this message
Autor: Exim Git Commits Mailing List
Data:  
Dla: exim-cvs
Temat: [exim-cvs] ATRN provider
Gitweb: https://git.exim.org/exim.git/commitdiff/4bb69ec417e3c679b85641d232cb29a64f6a77ee
Commit:     4bb69ec417e3c679b85641d232cb29a64f6a77ee
Parent:     e3a7c5cfee46cb11e54183d6a53883d56f6b48db
Author:     Jeremy Harris <jgh146exb@???>
AuthorDate: Fri Nov 8 17:50:26 2024 +0000
Committer:  Jeremy Harris <jgh146exb@???>
CommitDate: Fri Nov 8 17:55:30 2024 +0000

    ATRN provider
---
 doc/doc-docbook/spec.xfpt        | 174 ++++++++++++++--
 doc/doc-txt/NewStuff             |   6 +-
 doc/doc-txt/OptionLists.txt      |   1 +
 src/src/acl.c                    |  31 ++-
 src/src/deliver.c                |  32 ++-
 src/src/expand.c                 |   1 +
 src/src/functions.h              |   2 +
 src/src/globals.c                |   6 +
 src/src/globals.h                |   7 +-
 src/src/macros.h                 |  26 +--
 src/src/queue.c                  | 104 ++++++++--
 src/src/readconf.c               |   1 +
 src/src/smtp_in.c                | 107 +++++++++-
 src/src/tls-gnu.c                |  36 ++++
 src/src/tls-openssl.c            |  31 +++
 src/src/transports/smtp.c        | 146 ++++++++------
 test/README                      |   1 +
 test/confs/0639                  |  66 +++++++
 test/confs/1148                  |  64 ++++++
 test/log/0639                    |  37 ++++
 test/log/1148                    |  18 ++
 test/rejectlog/0639              |   4 +
 test/scripts/0000-Basic/0639     | 294 +++++++++++++++++++++++++++
 test/scripts/1100-Basic-TLS/1148 | 101 ++++++++++
 test/stderr/0544                 |   1 +
 test/stderr/5410                 |   3 +
 test/stdout/0639                 | 416 +++++++++++++++++++++++++++++++++++++++
 test/stdout/1148                 | 125 ++++++++++++
 28 files changed, 1723 insertions(+), 118 deletions(-)

diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt
index 661421be1..6ad009316 100644
--- a/doc/doc-docbook/spec.xfpt
+++ b/doc/doc-docbook/spec.xfpt
@@ -3435,7 +3435,7 @@ Unqualified addresses are automatically qualified using &%qualify_domain%& and
 &%qualify_recipient%&, as appropriate, unless the &%-bnq%& option is used.
 
 Some other SMTP commands are recognized in the input. HELO and EHLO act
-as RSET; VRFY, EXPN, ETRN, and HELP act as NOOP;
+as RSET; VRFY, EXPN, ETRN, ATRN, and HELP act as NOOP;
 QUIT quits, ignoring the rest of the standard input.
 
 .cindex "return code" "for &%-bS%&"
@@ -12540,6 +12540,13 @@ to the relevant file.
 When, as a result of aliasing or forwarding, a message is directed to a pipe,
 this variable holds the pipe command when the transport is running.
 
+.new
+.vitem &$atrn_host$&
+.vindex ATRN "data for routing"
+When an ATRN command is accepted, this variable is filled in with the client
+IP and port, for use in a manualroute router.
+.wen
+
 .vitem "&$auth1$& &-- &$auth4$&"
 .vindex "&$auth1$&, &$auth2$&, etc"
 These variables are used in SMTP authenticators (see chapters
@@ -14916,6 +14923,7 @@ listed in more than one group.
 .row &%acl_not_smtp%&                "ACL for non-SMTP messages"
 .row &%acl_not_smtp_mime%&           "ACL for non-SMTP MIME parts"
 .row &%acl_not_smtp_start%&          "ACL for start of non-SMTP message"
+.row &%acl_smtp_atrn%&               "ACL for ATRN"
 .row &%acl_smtp_auth%&               "ACL for AUTH"
 .row &%acl_smtp_connect%&            "ACL for connection"
 .row &%acl_smtp_data%&               "ACL for DATA"
@@ -15068,6 +15076,7 @@ See also the &'Policy controls'& section above.
                                            connection"
 .row &%smtp_accept_reserve%&         "only reserve hosts if more connections"
 .row &%smtp_active_hostname%&        "host name to use in messages"
+.row &%smtp_atrn_command%&           "what to run for ATRN"
 .row &%smtp_banner%&                 "text for welcome banner"
 .row &%smtp_check_spool_space%&      "from SIZE on MAIL command"
 .row &%smtp_connect_backlog%&        "passed to TCP/IP stack"
@@ -15238,6 +15247,19 @@ SMTP messages.
 This option defines the ACL that is run before Exim starts reading a
 non-SMTP message. See section &<<SECnonSMTP>>& for further details.
 
+.new
+.option acl_smtp_atrn main string&!! unset
+.cindex ATRN "ACL for"
+.cindex ATRN advertisement
+.cindex "ESMTP extensions" ATRN
+This option defines the ACL that is run when an SMTP ATRN command is
+received.
+If no value is set, or the result after expansion is an empty string,
+then the ATRN facility is not advertised.
+See chapter &<<CHAPACL>>& for general information on ACLs,
+and section &<<SECTATRN>>& for description of ATRN.
+.wen
+
 .option acl_smtp_auth main string&!! unset
 .cindex "&ACL;" "setting up for SMTP commands"
 .cindex "AUTH" "ACL for"
@@ -15281,7 +15303,8 @@ See section &<<SECDKIMVFY>>& for further details.
 This option defines the ACL that is run when an SMTP ETRN command is
 received.
 If no value is set then the ETRN facility is not advertised.
-See chapter &<<CHAPACL>>& for further details.
+See chapter &<<CHAPACL>>& for general information on ACLs,
+and section &<<SECTETRN>>& for description of ETRN.
 
 .option acl_smtp_expn main string&!! unset
 .cindex "EXPN" "ACL for"
@@ -18047,7 +18070,7 @@ hosts), you can do so by an appropriate use of a &%control%& modifier in an ACL
 
 
 .option smtp_etrn_command main string&!! unset
-.cindex "ETRN" "command to be run"
+.cindex ETRN "command to be run"
 .cindex "ESMTP extensions" ETRN
 .vindex "&$domain$&"
 If this option is set, the given command is run whenever an SMTP ETRN
@@ -18074,7 +18097,7 @@ the command.
 
 
 .option smtp_etrn_serialize main boolean true
-.cindex "ETRN" "serializing"
+.cindex ETRN serializing
 When this option is set, it prevents the simultaneous execution of more than
 one identical command as a result of ETRN in an SMTP connection. See
 section &<<SECTETRN>>& for details.
@@ -30833,15 +30856,17 @@ configuration locally by running a fake SMTP session with which you interact.
 .cindex "&ACL;" "options for specifying"
 In order to cause an ACL to be used, you have to name it in one of the relevant
 options in the main part of the configuration. These options are:
+.cindex "ATRN" "ACL for"
 .cindex "AUTH" "ACL for"
 .cindex "DATA" "ACLs for"
+.cindex "DKIM" "ACL for"
 .cindex "ETRN" "ACL for"
 .cindex "EXPN" "ACL for"
 .cindex "HELO" "ACL for"
 .cindex "EHLO" "ACL for"
-.cindex "DKIM" "ACL for"
 .cindex "MAIL" "ACL for"
-.cindex "QUIT, ACL for"
+.cindex "QUIT" "ACL for"
+.cindex "PRDR" "ACL for"
 .cindex "RCPT" "ACL for"
 .cindex "STARTTLS, ACL for"
 .cindex "VRFY" "ACL for"
@@ -30849,12 +30874,12 @@ options in the main part of the configuration. These options are:
 .cindex "SMTP" "connection, ACL for"
 .cindex "non-SMTP messages" "ACLs for"
 .cindex "MIME content scanning" "ACL for"
-.cindex "PRDR" "ACL for"
 
 .table2 140pt
 .irow &%acl_not_smtp%&         "ACL for non-SMTP messages"
 .irow &%acl_not_smtp_mime%&    "ACL for non-SMTP MIME parts"
 .irow &%acl_not_smtp_start%&   "ACL at start of non-SMTP message"
+.irow &%acl_smtp_atrn%&        "ACL for ATRN"
 .irow &%acl_smtp_auth%&        "ACL for AUTH"
 .irow &%acl_smtp_connect%&     "ACL for start of SMTP connection"
 .irow &%acl_smtp_data%&        "ACL after DATA is complete"
@@ -31286,9 +31311,11 @@ For &%acl_not_smtp%&, &%acl_smtp_auth%&, &%acl_smtp_connect%&,
 &%acl_smtp_mime%&, &%acl_smtp_predata%&, and &%acl_smtp_starttls%&, the action
 when the ACL is not defined is &"accept"&.
 
-For the others (&%acl_smtp_etrn%&, &%acl_smtp_expn%&, &%acl_smtp_rcpt%&,
-&%acl_smtp_vrfy%&
-and &%acl_smtp_wellknown%&),
+.new
+For the others (&%acl_smtp_atrn%&,
+.wen
+&%acl_smtp_etrn%&, &%acl_smtp_expn%&, &%acl_smtp_rcpt%&,
+&%acl_smtp_vrfy%& and &%acl_smtp_wellknown%&),
 the action when the ACL
 is not defined is &"deny"&.  This means that &%acl_smtp_rcpt%& must be
 defined in order to receive any messages over an SMTP connection.
@@ -31339,7 +31366,7 @@ of previously accepted recipients. At DATA time (for both the DATA ACLs),
 .cindex "&ACL;" "data for non-message ACL"
 .vindex &$smtp_command_argument$&
 .vindex &$smtp_command$&
-When an ACL is being run for AUTH, EHLO, ETRN, EXPN, HELO, STARTTLS, or VRFY,
+When an ACL is being run for ATRN, AUTH, EHLO, ETRN, EXPN, HELO, STARTTLS, or VRFY,
 the remainder of the SMTP command line is placed in &$smtp_command_argument$&,
 and the entire SMTP command is available in &$smtp_command$&.
 These variables can be tested using a &%condition%& condition. For example,
@@ -32670,6 +32697,18 @@ loops. This condition allows you to use different ACLs in different
 circumstances. For example, different ACLs can be used to handle RCPT commands
 for different local users or different local domains.
 
+.new
+.vitem &*atrn_domains&~=&~*&<&'domain&~list'&>
+.cindex ATRN "checking for queued messages"
+This condition is only usable in the ATRN ACL.
+It returns true if there are any messages queued for any of the domains given
+in the list.
+The list supplied must not be tainted
+.cindex "tainted data" "de-tainting"
+and should contain only domains relevant for the authenticated user
+(to avoid leaking information about other users).
+.wen
+
 .vitem &*authenticated&~=&~*&<&'string&~list'&>
 .cindex "&%authenticated%& ACL condition"
 .cindex "authentication" "ACL checking"
@@ -37928,6 +37967,114 @@ under its own uid and gid when receiving incoming SMTP, so it is not possible
 for it to change them before running the command.
 
 
+.new
+.subsection "The ATRN command" SECTATRN
+.cindex ATRN processing
+.cindex "ESMTP extensions" ATRN
+A second method for intermittently-connecting destinations
+is specified by
+&url(https://www.rfc-editor.org/rfc/rfc2645.html,RFC 2645).
+
+This describes an ESMTP command called ATRN which requests
+a swap in server/client roles of the communicating endpoints, and delivery
+of queued messages.
+Note that this supports customers having IP addresses that
+change frequently.
+
+Exim supports the &"provider"& side of ATRN, using the terms
+of that specification:
+initially as an SMTP server, then transferring to an SMTP client
+role if an ATRN command is accepted.
+
+.oindex "&%acl_smtp_atrn%&"
+The command is only available if permitted by an ACL
+specfied by the main-section &%acl_smtp_atrn%& option.
+Per the standard, this should only be for a specific
+provider port number (386, named "odmr");
+Exim should be configured to listen on that port
+(in addition to other duties) via &%daemon_smtp_ports%&
+or equivalent commandline options, and restrict the
+advertising of the facility to the port:
+.code
+acl_smtp_atrn = ${if = {$received_port}{386} {check_atrn}{}}
+.endd
+
+A recieved ATRN command will be rejected unless
+authentication has previously been done on the connection.
+
+Any arguments supplied with an ATRN command are (per standard)
+a comma-separated list of requested domains,
+and will be available in the &$smtp_command_argument$&
+variable.
+
+The ACL configured may return &"deny"& for any policy reaons
+(for example, the authenticated user is not permitted the facility).
+Otherwise it should use the ACL &"atrn_domains"& condition,
+which returns true if there are queued messages for any of
+the given list of domains.
+If that condition fails the ACL should return &"defer"&
+with a "453 You have no mail" response;
+else it should return &"accept"&.
+
+For example (with default domain handling, and one possible de-taint method) :
+.code
+check_atrn:
+  warn  set acl_m0 = clientdom.net
+  deny  condition = ${if def:smtp_command_argument}
+        set acl_m0 = ${map \
+          {<, $smtp_command_argument} \
+          {${if inlist{$item}{clientdom.net:cl2dom.net} {$value}}} \
+          }
+        condition = ${if !def:acl_m0}
+  defer !atrn_domains = <, $acl_m0
+        message = 453 You have no mail
+  accept
+.endd
+
+Acceptance by the ACL will result in a queue-run for messages
+having addresses with the given domains.
+A suitable router and transport must be configured for the deliveries.
+
+To access a named queue
+.cindex queue named
+the ACL should use a "queue =" modifier before the "atrn_domains"
+condition.
+If the ACL does not accept, re-set the queue to an empty value
+so as to not disrupt any later SMTP operations on the connection.
+
+Use of the &"atrn_domains"& condition additionally sets up
+the &$atrn_host$& variable, which can be used by a manualroute
+router.  Being otherwise empty, this router will decline in
+other situations so can be safely placed in a general router chain.
+
+For example:
+.code
+begin routers
+odmr_client:
+  driver =      manualroute
+  route_data =  <;$atrn_host
+  transport =   client_smtp
+
+begin transports
+client_smtp:
+  driver =      smtp
+.endd
+
+Although not discssed in the specification document,
+Exim supports use of ATRN within a STARTTLS-
+or TLS-on-connect- encrypted connection
+(which is wise if a plaintext authentication mechanism is used).
+In such cases the TLS connection will remain open across the
+role-swap, and be used for the sending of queued messages.
+
+Note that the RFC requires that the CRAM-MD5 authentication
+method be supported.
+Exim does not enforce this, but leaves it up to the configuration;
+see chapter &<<CHID9>>&.
+
+.wen
+
+
 
 .section "Incoming local SMTP" "SECID238"
 .cindex "SMTP" "local incoming"
@@ -37947,7 +38094,8 @@ This accepts SMTP messages from local processes without doing any other tests.
 
 
 
-.section "Outgoing batched SMTP" "SECTbatchSMTP"
+.section "Batched SMTP" "SECTgenbatchSMTP"
+.subsection "Outgoing batched SMTP" "SECTbatchSMTP"
 .cindex "SMTP" "batched outgoing"
 .cindex "batched SMTP output"
 Both the &(appendfile)& and &(pipe)& transports can be used for handling
@@ -37994,7 +38142,7 @@ message (unless there are more than 1000 recipients).
 
 
 
-.section "Incoming batched SMTP" "SECTincomingbatchedSMTP"
+.subsection "Incoming batched SMTP" "SECTincomingbatchedSMTP"
 .cindex "SMTP" "batched incoming"
 .cindex "batched SMTP input"
 The &%-bS%& command line option causes Exim to accept one or more messages by
diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff
index 27508be8d..cdcb11bb8 100644
--- a/doc/doc-txt/NewStuff
+++ b/doc/doc-txt/NewStuff
@@ -22,9 +22,11 @@ Version 4.98
     modules
 
  6. A transport "socks_proxy" may expand to an empty string, specifying no
-    proxying.
+    proxying
 
- 7. Variables $dmarc_alignment_spf and $dmarc_alignment_dkim.
+ 7. Variables $dmarc_alignment_spf and $dmarc_alignment_dkim
+
+ 8. ATRN provider support
 
 Version 4.98
 ------------
diff --git a/doc/doc-txt/OptionLists.txt b/doc/doc-txt/OptionLists.txt
index e7b284760..72290415c 100644
--- a/doc/doc-txt/OptionLists.txt
+++ b/doc/doc-txt/OptionLists.txt
@@ -51,6 +51,7 @@ in fact some of them were inherited from earlier versions.
 accept_8bitmime                      boolean         true          main              1.60 changed to true in 4.80
 acl_not_smtp                         string*         unset         main              4.11
 acl_not_smtp_mime                    string*         unset         main              4.51 with content scan
+acl_smtp_etrn                        string*         unset         main              4.99+
 acl_smtp_auth                        string*         unset         main              4.00
 acl_smtp_connect                     string*         unset         main              4.11
 acl_smtp_data                        string*         unset         main              4.00
diff --git a/src/src/acl.c b/src/src/acl.c
index ab05b13a9..88d09479c 100644
--- a/src/src/acl.c
+++ b/src/src/acl.c
@@ -61,6 +61,7 @@ static int msgcond[] = {
 
 enum { ACLC_ACL,
        ACLC_ADD_HEADER,
+       ACLC_ATRN_DOMAINS,
        ACLC_AUTHENTICATED,
 #ifdef EXPERIMENTAL_BRIGHTMAIL
        ACLC_BMI_OPTIN,
@@ -160,6 +161,10 @@ static condition_def conditions[] = {
                     ACL_BIT_NOTSMTP_START),
   },
 
+  [ACLC_ATRN_DOMAINS] =        { US"atrn_domains",    ACD_EXP,
+                  PERMITTED(ACL_BIT_ATRN)
+                },
+
   [ACLC_AUTHENTICATED] =    { US"authenticated",    0,
                   FORBIDDEN(ACL_BIT_NOTSMTP |
                     ACL_BIT_NOTSMTP_START |
@@ -3448,10 +3453,6 @@ for (; cb; cb = cb->next)
 
   switch(cb->type)
     {
-    case ACLC_ADD_HEADER:
-      setup_header(arg);
-      break;
-
     /* A nested ACL that returns "discard" makes sense only for an "accept" or
     "discard" verb. */
 
@@ -3466,6 +3467,28 @@ for (; cb; cb = cb->next)
         }
       break;
 
+    case ACLC_ADD_HEADER:
+      setup_header(arg);
+      break;
+
+    case ACLC_ATRN_DOMAINS:
+      if (is_tainted(arg))
+    {
+    log_write(0, LOG_MAIN|LOG_PANIC,
+          "attempt to used tainted value '%s' for atrn_domains%#s",
+            arg,
+            config_lineno
+            ? string_sprintf(" (%s %d)", config_filename, config_lineno)
+            : NULL);
+    *log_msgptr = US"internal configuration error";
+        return ERROR;
+    }
+      atrn_domains = string_copy(arg);
+      expand_level++;
+      rc = spool_has_one_undelivered_dom(arg);
+      expand_level--;
+      break;
+
     case ACLC_AUTHENTICATED:
       rc = sender_host_authenticated ? match_isinlist(sender_host_authenticated,
           &arg, 0, NULL, NULL, MCL_STRING, TRUE, NULL) : FAIL;
diff --git a/src/src/deliver.c b/src/src/deliver.c
index 0ba5f81b0..0f7e83bd2 100644
--- a/src/src/deliver.c
+++ b/src/src/deliver.c
@@ -4624,7 +4624,13 @@ nonmatch domains
   f.continue_more = FALSE;           /* In case got set for the last lot */
   if (continue_transport)
     {
-    BOOL ok = Ustrcmp(continue_transport, tp->drinst.name) == 0;
+    BOOL ok;
+
+    if (atrn_domains)
+      { continue_transport = tp->drinst.name; ok = TRUE; }
+    else
+      ok = Ustrcmp(continue_transport, tp->drinst.name) == 0;
+
 /*XXX do we need to check for a DANEd conn vs. a change of domain? */
 
     /* If the transport is about to override the host list do not check
@@ -4809,7 +4815,7 @@ do_remote_deliveries par_reduce par_wait par_read_pipe
 
   /*XXX what about firsttime? */
   /*XXX also, ph1? Note tp->name would possibly change per message,
-  so a check/close/open would be needed. Might was to change that var name
+  so a check/close/open would be needed. Might want to change that var name
   "continue_wait_db" as we'd be using it for a non-continued-transport
   context. */
   if (continue_transport && !exim_lockfile_needed())
@@ -5300,6 +5306,11 @@ do_remote_deliveries par_reduce par_wait par_read_pipe
     par_reduce(0, fallback);
     if (!*continue_next_id && continue_wait_db)
     { dbfn_close_multi(continue_wait_db); continue_wait_db = NULL; }
+
+    /* After the first ATRN message on the channel the EHLO has been dealt with;
+    ensure subsequence ones do not do that. */
+
+    atrn_domains = NULL;
     }
 
   /* Otherwise, if we are running in the test harness, wait a bit, to let the
@@ -6634,6 +6645,7 @@ whoever).
 A delivery operation has a process all to itself; we never deliver more than
 one message in the same process. Therefore we needn't worry too much about
 store leakage.
+XXX No longer true with new continued-transport ops; cf. goto CONTINUED_ID
 
 Liable to be called as root.
 
@@ -7267,6 +7279,9 @@ recipients tree, add an addr item to the chain of new addresses. If the pno
 value is non-negative, we must set the onetime parent from it. This which
 points to the relevant entry in the recipients list.
 
+When running for an ATRN delivery only include addresses for the domains
+to be delivered.
+
 This processing can be altered by the setting of the process_recipients
 variable, which is changed if recipients are to be ignored, failed, or
 deferred. This can happen as a result of system filter activity, or if the -Mg
@@ -7279,9 +7294,17 @@ complications for local addresses. */
 
 if (process_recipients != RECIP_IGNORE)
   for (i = 0; i < recipients_count; i++)
-    if (!tree_search(tree_nonrecipients, recipients_list[i].address))
+    {
+    recipient_item * r = recipients_list + i;
+    uschar * s;
+
+    if (  !tree_search(tree_nonrecipients, r->address)
+       && (  !atrn_domains
+      ||    (s = Ustrrchr(r->address, '@'))
+         && match_isinlist(s+1, &atrn_domains, 0, &domainlist_anchor, NULL,
+                MCL_DOMAIN + MCL_NOEXPAND, TRUE, NULL) == OK
+       )  )
       {
-      recipient_item * r = recipients_list + i;
       address_item * new = deliver_make_addr(r->address, FALSE);
 
       new->prop.errors_address = r->errors_to;
@@ -7405,6 +7428,7 @@ if (process_recipients != RECIP_IGNORE)
     }
 #endif
       }
+    }
 
 DEBUG(D_deliver)
   {
diff --git a/src/src/expand.c b/src/src/expand.c
index fd5884306..e4224dbb1 100644
--- a/src/src/expand.c
+++ b/src/src/expand.c
@@ -459,6 +459,7 @@ static var_entry var_table[] = {
   { "arc_state",           vtype_module,    US"arc" },
   { "arc_state_reason",    vtype_module,    US"arc" },
 #endif
+  { "atrn_host",       vtype_stringptr,   &atrn_host },
   { "authenticated_fail_id",vtype_stringptr,  &authenticated_fail_id },
   { "authenticated_id",    vtype_stringptr,   &authenticated_id },
   { "authenticated_sender",vtype_stringptr,   &authenticated_sender },
diff --git a/src/src/functions.h b/src/src/functions.h
index a8c546efd..11a4b6657 100644
--- a/src/src/functions.h
+++ b/src/src/functions.h
@@ -73,6 +73,7 @@ extern int     tls_read(void *, uschar *, size_t);
 extern int     tls_server_start(uschar **);
 extern void    tls_shutdown_wr(void *);
 extern BOOL    tls_smtp_buffered(void);
+extern void    tls_turnaround(int, const uschar *, int);
 extern int     tls_ungetc(int);
 #if defined(EXIM_HAVE_INOTIFY) || defined(EXIM_HAVE_KEVENT)
 extern void    tls_watch_discard_event(int);
@@ -521,6 +522,7 @@ extern int     spam(const uschar **);
 extern FILE   *spool_mbox(unsigned long *, const uschar *, uschar **);
 #endif
 extern void    spool_clear_header_globals(void);
+extern int     spool_has_one_undelivered_dom(const uschar *);
 extern BOOL    spool_move_message(const uschar *, const uschar *, const uschar *, const uschar *);
 extern int     spool_open_datafile(const uschar *);
 extern int     spool_open_temp(uschar *);
diff --git a/src/src/globals.c b/src/src/globals.c
index e887c2741..dcd5dbe3f 100644
--- a/src/src/globals.c
+++ b/src/src/globals.c
@@ -441,6 +441,7 @@ uschar *acl_not_smtp_mime      = NULL;
 #endif
 uschar *acl_not_smtp_start     = NULL;
 uschar *acl_removed_headers    = NULL;
+uschar *acl_smtp_atrn          = NULL;
 uschar *acl_smtp_auth          = NULL;
 uschar *acl_smtp_connect       = NULL;
 uschar *acl_smtp_data          = NULL;
@@ -487,6 +488,7 @@ uschar *acl_wherenames[]       = { [ACL_WHERE_RCPT] =        US"RCPT",
                                    [ACL_WHERE_PRDR] =        US"PRDR",
 #endif
                                    [ACL_WHERE_NOTSMTP] =    US"non-SMTP",
+                                   [ACL_WHERE_ATRN] =        US"ATRN",
                                    [ACL_WHERE_AUTH] =        US"AUTH",
                                    [ACL_WHERE_CONNECT] =    US"connection",
                                    [ACL_WHERE_ETRN] =        US"ETRN",
@@ -514,8 +516,10 @@ uschar *acl_wherecodes[]       = { [ACL_WHERE_RCPT] =    US"550",
 #ifndef DISABLE_PRDR
                                    [ACL_WHERE_PRDR] =    US"550",
 #endif
+                                   [ACL_WHERE_ATRN] =    US"450",
                                    [ACL_WHERE_AUTH] =    US"503",
                                    [ACL_WHERE_CONNECT] = US"550",
+                                   [ACL_WHERE_ATRN] =    US"450",
                                    [ACL_WHERE_ETRN] =    US"458",
                                    [ACL_WHERE_EXPN] =    US"550",
                                    [ACL_WHERE_HELO] =    US"550",
@@ -615,6 +619,8 @@ const uschar *address_pipe           = NULL;
 tree_node *addresslist_anchor  = NULL;
 int     addresslist_count      = 0;
 gid_t  *admin_groups           = NULL;
+const uschar *atrn_domains     = NULL;
+const uschar *atrn_host        = NULL;
 
 uschar *authenticated_fail_id  = NULL;
 uschar *authenticated_id       = NULL;
diff --git a/src/src/globals.h b/src/src/globals.h
index 5d912254d..8fd24fa76 100644
--- a/src/src/globals.h
+++ b/src/src/globals.h
@@ -326,10 +326,11 @@ extern uschar *acl_smtp_connect;       /* ACL run on SMTP connection */
 extern uschar *acl_smtp_data;          /* ACL run after DATA received */
 #ifndef DISABLE_PRDR
 extern uschar *acl_smtp_data_prdr;     /* ACL run after DATA received if in PRDR mode*/
-extern const pcre2_code *regex_PRDR;         /* For recognizing PRDR settings */
+extern const pcre2_code *regex_PRDR;   /* For recognizing PRDR settings */
 #endif
+extern uschar *acl_smtp_atrn;          /* ACL run for ATRN */
 #ifndef DISABLE_DKIM
-extern uschar *acl_smtp_dkim;          /* ACL run for DKIM signatures / domains */
+extern uschar *acl_smtp_dkim;          /* ACL run for DKIM signatures/domains */
 #endif
 extern uschar *acl_smtp_etrn;          /* ACL run for ETRN */
 extern uschar *acl_smtp_expn;          /* ACL run for EXPN */
@@ -365,6 +366,8 @@ extern gid_t  *admin_groups;           /* List of admin groups */
 extern BOOL    allow_domain_literals;  /* As it says */
 extern BOOL    allow_mx_to_ip;         /* Allow MX records to -> ip address */
 extern BOOL    allow_utf8_domains;     /* For experimenting */
+extern const uschar *atrn_domains;     /* Domains requested for transfer */
+extern const uschar *atrn_host;        /* host spec for client */
 extern uschar *authenticated_fail_id;  /* ID that failed authentication */
 extern uschar *authenticated_id;       /* ID that was authenticated */
 extern uschar *authenticated_sender;   /* From AUTH on MAIL */
diff --git a/src/src/macros.h b/src/src/macros.h
index 6acf242c1..377a87fa2 100644
--- a/src/src/macros.h
+++ b/src/src/macros.h
@@ -827,7 +827,7 @@ local_scan.h */
 most recent SMTP commands. SCH_NONE is "empty". */
 
 enum { SCH_NONE, SCH_AUTH, SCH_DATA, SCH_BDAT,
-       SCH_EHLO, SCH_ETRN, SCH_EXPN, SCH_HELO,
+       SCH_EHLO, SCH_ATRN, SCH_ETRN, SCH_EXPN, SCH_HELO,
        SCH_HELP, SCH_MAIL, SCH_NOOP, SCH_QUIT, SCH_RCPT, SCH_RSET, SCH_STARTTLS,
        SCH_VRFY,
 #ifndef DISABLE_WELLKNOWN
@@ -956,20 +956,21 @@ order without checking carefully!
 **** IMPORTANT ****
 */
 
-enum { ACL_WHERE_RCPT,       /* Some controls are for RCPT only */
-       ACL_WHERE_MAIL,       /* )                                           */
-       ACL_WHERE_PREDATA,    /* ) There are several tests for "in message", */
-       ACL_WHERE_MIME,       /* ) implemented by <= WHERE_NOTSMTP           */
-       ACL_WHERE_DKIM,       /* )                                           */
-       ACL_WHERE_DATA,       /* )                                           */
+enum { ACL_WHERE_RCPT,        /* Some controls are for RCPT only */
+       ACL_WHERE_MAIL,        /* )                        */
+       ACL_WHERE_PREDATA,    /* ) There are several tests for "in message",    */
+       ACL_WHERE_MIME,        /* ) implemented by <= WHERE_NOTSMTP        */
+       ACL_WHERE_DKIM,        /* )                        */
+       ACL_WHERE_DATA,        /* )                        */
 #ifndef DISABLE_PRDR
-       ACL_WHERE_PRDR,       /* )                                           */
+       ACL_WHERE_PRDR,        /* )                        */
 #endif
-       ACL_WHERE_NOTSMTP,    /* )                                           */
+       ACL_WHERE_NOTSMTP,    /* )                        */
 
-       ACL_WHERE_AUTH,       /* These remaining ones are not currently    */
-       ACL_WHERE_CONNECT,    /* required to be in a special order so they */
-       ACL_WHERE_ETRN,       /* are just alphabetical.                    */
+       ACL_WHERE_AUTH,        /* These remaining ones are not currently    */
+       ACL_WHERE_ATRN,        /* */
+       ACL_WHERE_CONNECT,    /* required to be in a special order so they    */
+       ACL_WHERE_ETRN,        /* are just alphabetical.            */
        ACL_WHERE_EXPN,
        ACL_WHERE_HELO,
        ACL_WHERE_MAILAUTH,
@@ -1000,6 +1001,7 @@ enum { ACL_WHERE_RCPT,       /* Some controls are for RCPT only */
 #define ACL_BIT_NOTSMTP        BIT(ACL_WHERE_NOTSMTP)
 #define ACL_BIT_AUTH        BIT(ACL_WHERE_AUTH)
 #define ACL_BIT_CONNECT        BIT(ACL_WHERE_CONNECT)
+#define ACL_BIT_ATRN        BIT(ACL_WHERE_ATRN)
 #define ACL_BIT_ETRN        BIT(ACL_WHERE_ETRN)
 #define ACL_BIT_EXPN        BIT(ACL_WHERE_EXPN)
 #define ACL_BIT_HELO        BIT(ACL_WHERE_HELO)
diff --git a/src/src/queue.c b/src/src/queue.c
index 8568de786..0b460edf3 100644
--- a/src/src/queue.c
+++ b/src/src/queue.c
@@ -46,10 +46,9 @@ Returns:       a pointer to a merged ordered list
 */
 
 static queue_filename *
-merge_queue_lists(queue_filename *a, queue_filename *b)
+merge_queue_lists(queue_filename * a, queue_filename * b)
 {
-queue_filename *first = NULL;
-queue_filename **append = &first;
+queue_filename * first = NULL, ** append = &first;
 
 while (a && b)
   {
@@ -126,15 +125,11 @@ Returns:         pointer to a chain of queue name items
 */
 
 static queue_filename *
-queue_get_spool_list(int subdiroffset, uschar *subdirs, int *subcount,
+queue_get_spool_list(int subdiroffset, uschar * subdirs, int * subcount,
   BOOL randomize, unsigned * pcount)
 {
-int i;
-int flags = 0;
-int resetflags = -1;
-int subptr;
-queue_filename *yield = NULL;
-queue_filename *last = NULL;
+int i, flags = 0, resetflags = -1, subptr;
+queue_filename * yield = NULL, * last = NULL;
 uschar buffer[256];
 queue_filename *root[LOG2_MAXNODES];
 
@@ -366,7 +361,7 @@ uschar * log_detail = NULL;
 int subcount = 0;
 uschar subdirs[64];
 pid_t qpid[4] = {0};    /* Parallelism factor for q2stage 1st phase */
-BOOL single_id = FALSE;
+BOOL single_id = FALSE, msg_handled = FALSE;
 
 #ifdef MEASURE_TIMING
 report_time_since(&timestamp_startup, US"queue_run start");
@@ -637,6 +632,8 @@ for (int i = queue_run_in_order ? -1 : 0;
               fq->text, deliver_selectstring);
           wanted = FALSE;
           }
+    else DEBUG(D_acl) if (atrn_domains)
+      debug_printf_indent("%s matches ATRN\n", fq->text);
         }
 
       /* Recover store used when reading the header */
@@ -688,6 +685,7 @@ for (int i = queue_run_in_order ? -1 : 0;
 #ifdef MEASURE_TIMING
     report_time_since(&timestamp_startup, US"queue msg selected");
 #endif
+    msg_handled = TRUE;
 
 single_item_retry:
     if ((pid = exim_fork(
@@ -708,7 +706,14 @@ single_item_retry:
 
     (void)close(pfd[pipe_write]);
     set_process_info("running queue: waiting for %s (%d)", fq->text, pid);
-    while (wait(&status) != pid);
+    for (int ret; (ret = wait (&status)) != pid; )
+      if (ret == -1)
+    {
+    DEBUG(D_any) debug_printf("%s %d: wait: %s\n", __FUNCTION__, __LINE__,
+                  strerror(errno));
+    status = 0;
+    break;
+    }
 
     /* A zero return means a delivery was attempted; turn off the force flag
     for any subsequent calls unless queue_force is set. */
@@ -816,11 +821,23 @@ if (q->queue_2stage)
 /* At top level, log the end of the run. */
 
 if (!recurse)
+  {
   if (q->name)
     log_write(L_queue_run, LOG_MAIN, "End '%s' queue run: %s",
       q->name, log_detail);
   else
     log_write(L_queue_run, LOG_MAIN, "End queue run: %s", log_detail);
+
+  /* If no ATRN messages were sent, try to close the channel semi-cleanly.
+  XXX is this the best place to be doing this? We really ought to be
+  using smtp_write_command() but that needs a transport context. */
+
+  if (atrn_domains && !msg_handled)
+    {
+    DEBUG(D_any) debug_printf("ATRN: no messages; sending QUIT\n");
+    (void) send(0, "QUIT\r\n", 6, 0);
+    }
+  }
 }
 
 
@@ -838,12 +855,75 @@ set_process_info("running the %s%s%squeue (single queue run%s)",
   *queue_name ? "'" : "", queue_name, *queue_name ? "' " : "",
   q->queue_2stage ? ", 2-phase" : ""
   );
+if (*queue_name) q->name = queue_name;
 queue_run(q, start_id, stop_id, FALSE);
 }
 
 
 
 
+/* Search spool for messages having at least one undelivered recipient domain
+matching the given list, early-out.  We ignore J-files, assuming any such
+will get accounted for elsewhere.
+
+Arguments:    domains        list of domains to match
+Return        OK/FAIL
+*/
+
+int
+spool_has_one_undelivered_dom(const uschar * domains)
+{
+int yield = FAIL, subcount;
+uschar subdirs[64];
+BOOL orig_dont_deliver = f.dont_deliver;
+uschar * orig_sa = sender_host_address;
+int orig_sp = sender_host_port;
+tls_support orig_tls_in = tls_in;
+
+for (queue_filename * fq = queue_get_spool_list(-1,    /* entire queue */
+                      subdirs,        /* for holding sublist*/
+                      &subcount,    /* for subcount */
+                      FALSE,        /* not random */
+                      NULL);
+     fq && yield != OK; fq = fq->next)
+  {
+  struct stat statbuf;
+  rmark reset_point = store_mark();
+
+  message_subdir[0] = fq->dir_uschar;
+  if (  Ustat(spool_fname(US"input", message_subdir, fq->text, US""), &statbuf)
+    >= 0
+     && spool_read_header(fq->text, FALSE, TRUE) == spool_read_OK
+     )
+    {
+    uschar * s;
+    for (const recipient_item * r = recipients_list;
+    r < recipients_list + recipients_count && yield != OK; r++)
+
+      if (  !tree_search(tree_nonrecipients, r->address)
+     && (s = Ustrchr(r->address, '@'))
+     && match_isinlist(s+1, &domains, 0, &domainlist_anchor, NULL,
+              MCL_DOMAIN + MCL_NOEXPAND, TRUE, NULL) == OK)
+    {
+    DEBUG(D_all)
+      debug_printf_indent("found a matching message: '%s'\n", r->address);
+    yield = OK;
+    }
+
+    spool_clear_header_globals();
+    }
+  store_reset(reset_point);
+  }
+
+f.dont_deliver = orig_dont_deliver;
+sender_host_address = orig_sa;
+sender_host_port = orig_sp;
+tls_in = orig_tls_in;
+
+return yield;
+}
+
+
 /************************************************
 *         Count messages on the queue           *
 ************************************************/
diff --git a/src/src/readconf.c b/src/src/readconf.c
index 960008953..7912bca4a 100644
--- a/src/src/readconf.c
+++ b/src/src/readconf.c
@@ -41,6 +41,7 @@ static optionlist optionlist_config[] = {
   { "acl_not_smtp_mime",        opt_stringptr,   {&acl_not_smtp_mime} },
 #endif
   { "acl_not_smtp_start",       opt_stringptr,   {&acl_not_smtp_start} },
+  { "acl_smtp_atrn",            opt_stringptr,   {&acl_smtp_atrn} },
   { "acl_smtp_auth",            opt_stringptr,   {&acl_smtp_auth} },
   { "acl_smtp_connect",         opt_stringptr,   {&acl_smtp_connect} },
   { "acl_smtp_data",            opt_stringptr,   {&acl_smtp_data} },
diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c
index e76790fea..8718df190 100644
--- a/src/src/smtp_in.c
+++ b/src/src/smtp_in.c
@@ -72,7 +72,7 @@ enum {
 
   HELO_CMD, EHLO_CMD, DATA_CMD, /* These are listed in the pipelining */
   VRFY_CMD, EXPN_CMD, NOOP_CMD, /* RFC as requiring synchronization */
-  ETRN_CMD,                     /* This by analogy with TURN from the RFC */
+  ATRN_CMD, ETRN_CMD,        /* This by analogy with TURN from the RFC */
   STARTTLS_CMD,                 /* Required by the STARTTLS RFC */
   TLS_AUTH_CMD,            /* auto-command at start of SSL */
 #ifdef EXPERIMENTAL_XCLIENT
@@ -231,6 +231,7 @@ static smtp_cmd_list cmd_list[] = {
   { "bdat",       sizeof("bdat")-1,       BDAT_CMD, TRUE,  TRUE  },
   { "quit",       sizeof("quit")-1,       QUIT_CMD, FALSE, TRUE  },
   { "noop",       sizeof("noop")-1,       NOOP_CMD, TRUE,  FALSE },
+  { "atrn",       sizeof("atrn")-1,       ATRN_CMD, TRUE,  FALSE },
   { "etrn",       sizeof("etrn")-1,       ETRN_CMD, TRUE,  FALSE },
   { "vrfy",       sizeof("vrfy")-1,       VRFY_CMD, TRUE,  FALSE },
   { "expn",       sizeof("expn")-1,       EXPN_CMD, TRUE,  FALSE },
@@ -1949,13 +1950,10 @@ while (done <= 0)
       break;
 
 
-    /* The VRFY, EXPN, HELP, ETRN, and NOOP commands are ignored. */
+    /* The VRFY, EXPN, HELP, ETRN, ATRN and NOOP commands are ignored. */
 
-    case VRFY_CMD:
-    case EXPN_CMD:
-    case HELP_CMD:
-    case NOOP_CMD:
-    case ETRN_CMD:
+    case VRFY_CMD: case EXPN_CMD: case HELP_CMD: case NOOP_CMD:
+    case ETRN_CMD: case ATRN_CMD:
 #ifndef DISABLE_WELLKNOWN
     case WELLKNOWN_CMD:
 #endif
@@ -4161,9 +4159,20 @@ while (done <= 0)
       fl.dsn_advertised = TRUE;
       }
 
-    /* Advertise ETRN/VRFY/EXPN if there's are ACL checking whether a host is
-    permitted to issue them; a check is made when any host actually tries. */
+    /* Advertise ATRN/ETRN/VRFY/EXPN if there's are ACL checking whether a
+    host is permitted to issue them; a check is made when any host actually
+    tries. */
 
+    GET_OPTION("acl_smtp_atrn");
+    if (acl_smtp_atrn)
+      {
+      const uschar * s = expand_cstring(acl_smtp_atrn);
+      if (s && *s)
+        {
+        g = string_catn(g, smtp_code, 3);
+        g = string_catn(g, US"-ATRN\r\n", 7);
+        }
+      }
     GET_OPTION("acl_smtp_etrn");
     if (acl_smtp_etrn)
       {
@@ -5520,6 +5529,7 @@ while (done <= 0)
 #endif
       smtp_printf(" HELO EHLO MAIL RCPT DATA BDAT", SP_MORE);
       smtp_printf(" NOOP QUIT RSET HELP", SP_MORE);
+      if (acl_smtp_atrn) smtp_printf(" ATRN", SP_MORE);
       if (acl_smtp_etrn) smtp_printf(" ETRN", SP_MORE);
       if (acl_smtp_expn) smtp_printf(" EXPN", SP_MORE);
       if (acl_smtp_vrfy) smtp_printf(" VRFY", SP_MORE);
@@ -5569,6 +5579,85 @@ while (done <= 0)
       break;
 
 
+    case ATRN_CMD:
+      {
+      uschar * exp_acl = NULL;
+      const uschar * list;
+      int sep = 0;
+      gstring * g = NULL;
+      qrunner q = {0};
+
+      HAD(SCH_ATRN);
+      /*XXX could we used a cached value for "advertised"? */
+      GET_OPTION("acl_smtp_atrn");
+      if (acl_smtp_atrn
+     && (exp_acl = expand_string(acl_smtp_atrn)) && !*exp_acl)
+    exp_acl = NULL;
+      if (!exp_acl || !authenticated_id || sender_address)
+    {
+    done = synprot_error(L_smtp_protocol_error,
+      !exp_acl ? 502 : !authenticated_id ? 530 : 503,
+      NULL,
+      !exp_acl ?        US"ATRN command used when not advertised"
+      : !authenticated_id ?    US"ATRN is not permitted without authentication"
+      :            US"ATRN is not permitted inside a transaction"
+      );
+    break;
+    }
+
+      log_write(L_etrn, LOG_MAIN, "ATRN '%s' received from %s",
+    smtp_cmd_argument, host_and_ident(FALSE));
+
+      if (  (rc = acl_check(ACL_WHERE_ATRN, NULL, exp_acl, &user_msg, &log_msg))
+     != OK)
+    {
+    done = smtp_handle_acl_fail(ACL_WHERE_ATRN, rc, user_msg, log_msg);
+    break;
+    }
+
+      /* want to do a qrun for the given domain(s), using the already open channel.
+      TODO: alternate named queue
+      TODO: docs
+
+      /* ACK the command, record the connection details
+      and turn the line around */
+
+      smtp_printf("250 ODMR server turning line around\r\n", SP_NO_MORE);
+      atrn_host = string_sprintf("[%s]:%d",
+                sender_host_address, sender_host_port);
+
+#ifndef DISABLE_TLS
+      if (tls_in.active.sock >= 0)
+    tls_turnaround(0, sender_host_address, sender_host_port);
+#endif
+      fflush(smtp_out);
+      force_fd(fileno(smtp_in), 0);
+      smtp_in = smtp_out = NULL;
+
+      /* Set up a onetime queue run, filtering for messages with the
+      given domains. Later filtering will leave out addresses for other domains
+      on these messages. */
+
+      continue_transport = US"ATRN-client";
+      continue_hostname = continue_host_address = sender_host_address;
+
+      q.next_tick = time(NULL);
+      q.run_max = 1;
+      q.queue_2stage = TRUE;
+
+      /* Convert the domainlist to a regex, as the existing queue-selection
+      facilities support that but not a list */
+
+      list = atrn_domains;
+      for (const uschar * ele; ele = string_nextinlist(&list, &sep, NULL, 0); )
+    g = string_append_listele(g, '|', ele);
+      deliver_selectstring = string_sprintf("@(%Y)", g);
+      f.deliver_selectstring_regex = TRUE;
+
+      single_queue_run(&q , NULL, NULL);
+      exim_exit(EXIT_SUCCESS);
+      }
+
     case ETRN_CMD:
       HAD(SCH_ETRN);
       if (sender_address)
diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c
index 7963e2c97..cf41a74c5 100644
--- a/src/src/tls-gnu.c
+++ b/src/src/tls-gnu.c
@@ -4305,6 +4305,42 @@ return NULL;
 }
 
 
+/* For ATRN: transfer the tls_in context to tls_out */
+
+void
+tls_turnaround(int newfd, const uschar * ipaddr, int port)
+{
+exim_gnutls_state_st * state;
+host_item * h;
+int old_pool = store_pool;
+
+store_pool = POOL_PERM;
+state = store_get(sizeof(exim_gnutls_state_st), GET_UNTAINTED);
+h = store_get(sizeof(host_item), GET_UNTAINTED);
+
+memset(h, 0, sizeof(host_item));
+h->name = h->address = string_copy(ipaddr);
+h->port = port;
+
+*state = state_server;
+
+state->fd_in = newfd;
+state->fd_out = newfd;
+state->tlsp = &tls_out;
+state->host = h;
+
+tls_out = tls_in;
+tls_out.active.sock = newfd;
+tls_out.active.tls_ctx = state;
+
+memset(&tls_in, 0, sizeof(tls_in));
+
+gnutls_transport_set_ptr2(state->session,
+    (gnutls_transport_ptr_t)(long) newfd,
+    (gnutls_transport_ptr_t)(long) newfd);
+store_pool = old_pool;
+}
+
 
 
 /*************************************************
diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c
index 302404b6c..2b6a05afd 100644
--- a/src/src/tls-openssl.c
+++ b/src/src/tls-openssl.c
@@ -5191,6 +5191,37 @@ for (uschar * s = exp; *s; /**/)
 return TRUE;
 }
 
+
+
+/* For ATRN: transfer the tls_in context to tls_out */
+
+void
+tls_turnaround(int newfd, const uschar * ipaddr, int port)
+{
+exim_openssl_client_tls_ctx * exim_client_ctx;
+int old_pool = store_pool;
+
+state_server.is_server = FALSE;
+state_server.tlsp = &tls_out;
+client_static_state = &state_server;
+
+store_pool = POOL_PERM;
+exim_client_ctx = store_get(sizeof(exim_openssl_client_tls_ctx), GET_UNTAINTED);
+exim_client_ctx->ctx = client_static_state->lib_state.lib_ctx;
+exim_client_ctx->ssl = client_static_state->lib_state.lib_ssl;
+exim_client_ctx->corked = NULL;
+store_pool = old_pool;
+
+SSL_set_fd(exim_client_ctx->ssl, newfd);
+
+tls_out = tls_in;
+tls_out.active.sock = newfd;
+tls_out.active.tls_ctx = exim_client_ctx;
+
+memset(&tls_in, 0, sizeof(tls_in));
+}
+
+
 #endif    /*!MACRO_PREDEF*/
 /* vi: aw ai sw=2
 */
diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c
index 79bacfc31..4f9851737 100644
--- a/src/src/transports/smtp.c
+++ b/src/src/transports/smtp.c
@@ -2181,7 +2181,7 @@ sx->max_rcpt = expand_max_rcpt(sx->conn_args.tblock->max_addresses);
 sx->igquotstr = US"";
 if (!sx->helo_data) sx->helo_data = ob->helo_data;
 
-smtp_command = US"initial connection";
+smtp_command = atrn_domains ? US"ATRN line turnaround" : US"initial connection";
 
 /* Set up the buffer for reading SMTP response packets. */
 
@@ -2196,21 +2196,27 @@ sx->outblock.buffer = sx->outbuffer;
 sx->outblock.buffersize = sizeof(sx->outbuffer);
 sx->outblock.ptr = sx->outbuffer;
 
-/* Reset the parameters of a TLS session. */
+/* Reset the parameters of a TLS session, unless ATRN. */
 
-tls_out.bits = 0;
-tls_out.cipher = NULL;    /* the one we may use for this transport */
-tls_out.ourcert = NULL;
-tls_out.peercert = NULL;
-tls_out.peerdn = NULL;
+if (!atrn_domains || tls_out.active.sock < 0)
+  {
+  tls_out.bits = 0;
+  tls_out.cipher = NULL;    /* the one we may use for this transport */
+  tls_out.peerdn = NULL;
 #ifdef USE_OPENSSL
-tls_out.sni = NULL;
+  tls_out.sni = NULL;
 #endif
-tls_out.ocsp = OCSP_NOT_REQ;
+  tls_out.ocsp = OCSP_NOT_REQ;
 #ifndef DISABLE_TLS_RESUME
-tls_out.resumption = 0;
+  tls_out.resumption = 0;
 #endif
-tls_out.ver = NULL;
+  tls_out.ver = NULL;
+  }
+
+/* If we do not null out ourcert under ATRN we segv trying to write the
+cert down the transport-result pipe. Unclear why. */
+tls_out.ourcert = NULL;
+tls_out.peercert = NULL;
 
 /* Flip the legacy TLS-related variables over to the outbound set in case
 they're used in the context of the transport.  Don't bother resetting
@@ -2293,7 +2299,7 @@ if (continue_hostname && continue_proxy_cipher)
 the initial interaction and HELO/EHLO/LHLO. Connect timeout errors are handled
 specially so they can be identified for retries. */
 
-if (!continue_hostname)
+if (!continue_hostname || atrn_domains)
   {
   if (sx->verify) HDEBUG(D_verify)
     debug_printf("interface=%s port=%d\n", sx->conn_args.interface, sx->port);
@@ -2340,59 +2346,77 @@ if (!continue_hostname)
   sx->avoid_option = sx->peer_offered = smtp_peer_options = 0;
   smtp_debug_cmd_log_init();
 
+  if (atrn_domains)
+    {
+    DEBUG(D_transport)
+      debug_printf("ATRN mode: TCP%s connection already present\n",
+            tls_out.active.sock >= 0 ? "/TLS" : "");
+    sx->cctx.sock = 0;
+    sx->cctx.tls_ctx = tls_out.active.tls_ctx;
+
+    /* If already TLS, do not do a STARTTLS,EHLO sequence */
+    if (tls_out.active.sock >= 0)
+      sx->smtps = TRUE;
+    
+    /* Record the port that was used */
+    smtp_port_for_connect(sx->conn_args.host, sx->port);
+    }
+  else
+    {
 #ifndef DISABLE_PIPE_CONNECT
-  if (  verify_check_given_host(CUSS &ob->hosts_pipe_connect,
-                        sx->conn_args.host) == OK)
+    if (  verify_check_given_host(CUSS &ob->hosts_pipe_connect,
+                          sx->conn_args.host) == OK)
 
-    /* We don't find out the local ip address until the connect, so if
-    the helo string might use it avoid doing early-pipelining. */
+      /* We don't find out the local ip address until the connect, so if
+      the helo string might use it avoid doing early-pipelining. */
 
-    if (  !sx->helo_data
-       || sx->conn_args.interface
-       || !Ustrstr(sx->helo_data, "$sending_ip_address")
-       || Ustrstr(sx->helo_data, "def:sending_ip_address")
-       )
-      {
-      sx->early_pipe_ok = TRUE;
-      if (  read_ehlo_cache_entry(sx)
-     && sx->ehlo_resp.cleartext_features & OPTION_EARLY_PIPE)
+      if (  !sx->helo_data
+     || sx->conn_args.interface
+     || !Ustrstr(sx->helo_data, "$sending_ip_address")
+     || Ustrstr(sx->helo_data, "def:sending_ip_address")
+     )
     {
-    DEBUG(D_transport)
-      debug_printf("Using cached cleartext PIPECONNECT\n");
-    sx->early_pipe_active = TRUE;
-    sx->peer_offered = sx->ehlo_resp.cleartext_features;
+    sx->early_pipe_ok = TRUE;
+    if (  read_ehlo_cache_entry(sx)
+       && sx->ehlo_resp.cleartext_features & OPTION_EARLY_PIPE)
+      {
+      DEBUG(D_transport)
+        debug_printf("Using cached cleartext PIPECONNECT\n");
+      sx->early_pipe_active = TRUE;
+      sx->peer_offered = sx->ehlo_resp.cleartext_features;
+      }
     }
-      }
-    else DEBUG(D_transport)
-      debug_printf("helo needs $sending_ip_address; avoid early-pipelining\n");
-
-PIPE_CONNECT_RETRY:
-  if (sx->early_pipe_active)
-    {
-    sx->outblock.conn_args = &sx->conn_args;
-    (void) smtp_boundsock(&sx->conn_args);
-    }
-  else
-#endif
-    {
-    blob lazy_conn = {.data = NULL};
-    /* For TLS-connect, a TFO lazy-connect is useful since the Client Hello
-    can go on the TCP SYN. */
+      else DEBUG(D_transport)
+    debug_printf("helo needs $sending_ip_address; avoid early-pipelining\n");
 
-    if ((sx->cctx.sock = smtp_connect(&sx->conn_args,
-                sx->smtps ? &lazy_conn : NULL)) < 0)
+  PIPE_CONNECT_RETRY:
+    if (sx->early_pipe_active)
       {
-      set_errno_nohost(sx->addrlist,
-    errno == ETIMEDOUT ? ERRNO_CONNECTTIMEOUT : errno,
-    sx->verify ? US strerror(errno) : NULL,
-    DEFER, FALSE, &sx->delivery_start);
-      sx->send_quit = FALSE;
-      return DEFER;
+      sx->outblock.conn_args = &sx->conn_args;
+      (void) smtp_boundsock(&sx->conn_args);
+      }
+    else
+#endif    /*DISABLE_PIPE_CONNECT*/
+      {
+      blob lazy_conn = {.data = NULL};
+      /* For TLS-connect, a TFO lazy-connect is useful since the Client Hello
+      can go on the TCP SYN. */
+
+      if ((sx->cctx.sock = smtp_connect(&sx->conn_args,
+                  sx->smtps ? &lazy_conn : NULL)) < 0)
+    {
+    set_errno_nohost(sx->addrlist,
+      errno == ETIMEDOUT ? ERRNO_CONNECTTIMEOUT : errno,
+      sx->verify ? US strerror(errno) : NULL,
+      DEFER, FALSE, &sx->delivery_start);
+    sx->send_quit = FALSE;
+    return DEFER;
+    }
+  #ifdef TCP_QUICKACK
+      (void) setsockopt(sx->cctx.sock, IPPROTO_TCP, TCP_QUICKACK, US &off,
+              sizeof(off));
+  #endif
       }
-#ifdef TCP_QUICKACK
-    (void) setsockopt(sx->cctx.sock, IPPROTO_TCP, TCP_QUICKACK, US &off,
-            sizeof(off));
-#endif
     }
   /* Expand the greeting message while waiting for the initial response. (Makes
   sense if helo_data contains ${lookup dnsdb ...} stuff). The expansion is
@@ -2801,7 +2825,9 @@ if (  smtp_peer_options & OPTION_TLS
   TLS_NEGOTIATE:
     {
     sx->conn_args.sending_ip_address = sending_ip_address;
-    if (!tls_client_start(&sx->cctx, &sx->conn_args, sx->addrlist, &tls_out, &tls_errstr))
+    if (  !atrn_domains
+       && !tls_client_start(&sx->cctx, &sx->conn_args, sx->addrlist, &tls_out,
+                &tls_errstr))
       {
       /* TLS negotiation failed; give an error. From outside, this function may
       be called again to try in clear on a new connection, if the options permit
@@ -3002,7 +3028,7 @@ so its response needs to be analyzed. If TLS is not active and this is a
 continued session down a previously-used socket, we haven't just done EHLO, so
 we skip this. */
 
-if (   !continue_hostname
+if (   !continue_hostname && !atrn_domains
 #ifndef DISABLE_TLS
     || tls_out.active.sock >= 0
 #endif
@@ -3951,7 +3977,7 @@ commands were already sent; do not re-send but do mark the addrs as
 having been accepted up to RCPT stage.  A traditional cont-conn
 always has a sequence number greater than one. */
 
-if (continue_hostname && continue_sequence == 1)
+if (continue_hostname && continue_sequence == 1 && !atrn_domains)
   {
   /* sx->pending_MAIL = FALSE; */
   sx->ok = TRUE;
diff --git a/test/README b/test/README
index 3f8675545..1798c4dc2 100644
--- a/test/README
+++ b/test/README
@@ -1085,6 +1085,7 @@ Lines in client scripts are of several kinds:
 (1) "??? ": If a line begins with three question marks and a space, the rest of the
     line defines the start of expected output from the server. If what is
     received does not match, the client bombs out with an error message.
+    Escape sequences described below in (7) are usable in the expected text.
 
 (2) "???*": If a line begins with three question marks and an asterisk, the server
     is expected to close the connection.
diff --git a/test/confs/0639 b/test/confs/0639
new file mode 100644
index 000000000..fb3219734
--- /dev/null
+++ b/test/confs/0639
@@ -0,0 +1,66 @@
+# Exim test configuration 0639
+
+.include DIR/aux-var/std_conf_prefix
+
+QDG=
+
+
+# ----- Main settings -----
+
+acl_smtp_atrn = ${if = {$received_port}{PORT_D2} {check_atrn}{}}
+acl_smtp_rcpt = accept
+
+queue_only
+queue_run_in_order
+
+# ----- ACL -----
+
+begin acl
+
+check_atrn:
+  deny        hosts = 127.0.0.1
+  warn        set acl_m0 = clientdom.net
+  deny        condition = ${if def:smtp_command_argument}
+        set acl_m0 = ${map \
+                {<, $smtp_command_argument} \
+                {${if inlist{$item}{clientdom.net:cl2dom.net} {$value}}} \
+                  }
+        condition = ${if !def:acl_m0}
+  defer
+.ifdef QDG
+        queue = QDG
+.endif
+        !atrn_domains = <, $acl_m0
+.ifdef QDG
+        queue =
+.endif
+        message = 453 You have no mail
+  accept
+
+# ----- auths ----
+
+begin authenticators
+
+plain:
+  driver = plaintext
+  public_name = PLAIN
+  server_condition = "\
+    ${if and {{eq{$auth2}{userx}}{eq{$auth3}{secret}}}{yes}{no}}"
+  server_set_id = $auth2
+
+# -------- routers ---
+
+begin routers
+
+client:
+  driver =    manualroute
+  route_data =    <;$atrn_host
+  self =    send
+  transport =    client_smtp
+
+begin transports
+
+client_smtp:
+  driver =    smtp
+
+# End
diff --git a/test/confs/1148 b/test/confs/1148
new file mode 100644
index 000000000..f521785fc
--- /dev/null
+++ b/test/confs/1148
@@ -0,0 +1,64 @@
+# Exim test configuration 1148
+
+.include DIR/aux-var/tls_conf_prefix
+
+primary_hostname = myhost.test.ex
+
+# ----- Main settings -----
+
+.ifdef SUB
+tls_on_connect_ports = SUB
+.endif
+
+tls_advertise_hosts = *
+tls_certificate = DIR/aux-fixed/cert1
+tls_privatekey = DIR/aux-fixed/cert1
+
+queue_only
+queue_run_in_order
+
+acl_smtp_atrn = check_atrn 
+
+# ----- ACL -----
+
+begin acl
+
+check_atrn:
+  warn        set acl_m0 = clientdom.net
+        logwrite = tls_in_cipher $tls_in_cipher
+  defer        !atrn_domains = <, $acl_m0
+        message = 453 You have no mail
+  accept
+
+# ----- auths ----
+
+begin authenticators
+
+plain:
+  driver = plaintext
+  public_name = PLAIN
+  server_advertise_condition = ${if def:tls_in_cipher}
+  server_condition = "\
+    ${if and {{eq{$auth2}{userx}}{eq{$auth3}{secret}}}{yes}{no}}"
+  server_set_id = $auth2
+
+# -------- routers ---
+
+begin routers
+
+client:
+  driver =    manualroute
+  route_data =    <;$atrn_host
+  self =    send
+  transport =    client_smtp
+
+begin transports
+
+client_smtp:
+    driver =        smtp
+    allow_localhost
+    hosts_require_tls = *
+    tls_verify_certificates =    DIR/aux-fixed/cert1
+    tls_verify_cert_hostnames =    :
+
+# End
diff --git a/test/log/0639 b/test/log/0639
new file mode 100644
index 000000000..f9bab3787
--- /dev/null
+++ b/test/log/0639
@@ -0,0 +1,37 @@
+1999-03-02 09:44:33 10HmaX-000000005vi-0000 <= CALLER@??? U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaY-000000005vi-0000 <= CALLER@??? U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaZ-000000005vi-0000 <= CALLER@??? U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmbA-000000005vi-0000 <= CALLER@??? U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmbB-000000005vi-0000 <= CALLER@??? U=CALLER P=local S=sss Q=altqueue
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=p1234, no queue runs, listening for SMTP on port PORT_D port PORT_D2
+1999-03-02 09:44:33 ATRN '#failacl' received from (tester) [127.0.0.1]
+1999-03-02 09:44:33 H=(tester) [127.0.0.1] rejected ATRN #failacl
+1999-03-02 09:44:33 10HmbC-000000005vi-0000 <= fred@??? H=(tester) [ip4.ip4.ip4.ip4] P=esmtp S=sss
+1999-03-02 09:44:33 ATRN '' received from (tester) [ip4.ip4.ip4.ip4]
+1999-03-02 09:44:33 Start queue run: pid=p1235 -qq -Rr @(clientdom.net)
+1999-03-02 09:44:33 10HmbC-000000005vi-0000 => bill@??? R=client T=client_smtp H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] C="250 ODMR client: message accepted"
+1999-03-02 09:44:33 10HmbC-000000005vi-0000 Completed
+1999-03-02 09:44:33 End queue run: pid=p1235 -qq -Rr @(clientdom.net)
+1999-03-02 09:44:33 ATRN '' received from (tester) [ip4.ip4.ip4.ip4]
+1999-03-02 09:44:33 Start queue run: pid=p1236 -qq -Rr @(clientdom.net)
+1999-03-02 09:44:33 10HmaY-000000005vi-0000 => b@??? R=client T=client_smtp H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] C="250 ODMR client: message accepted"
+1999-03-02 09:44:33 10HmaY-000000005vi-0000 -> d@??? R=client T=client_smtp H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] C="250 ODMR client: message accepted"
+1999-03-02 09:44:33 10HmaY-000000005vi-0000 Completed
+1999-03-02 09:44:33 10HmaZ-000000005vi-0000 => e@??? R=client T=client_smtp H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4]* C="250 ODMR client: message accepted"
+1999-03-02 09:44:33 10HmaZ-000000005vi-0000 Completed
+1999-03-02 09:44:33 End queue run: pid=p1236 -qq -Rr @(clientdom.net)
+1999-03-02 09:44:33 ATRN 'cl2dom.net' received from (tester) [ip4.ip4.ip4.ip4]
+1999-03-02 09:44:33 Start queue run: pid=p1237 -qq -Rr @(cl2dom.net)
+1999-03-02 09:44:33 10HmbA-000000005vi-0000 => f@??? R=client T=client_smtp H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] C="250 ODMR client: message accepted"
+1999-03-02 09:44:33 10HmbA-000000005vi-0000 Completed
+1999-03-02 09:44:33 End queue run: pid=p1237 -qq -Rr @(cl2dom.net)
+1999-03-02 09:44:33 ATRN 'cl2dom.net' received from (tester) [ip4.ip4.ip4.ip4]
+1999-03-02 09:44:33 U=unknown temporarily rejected ATRN cl2dom.net: 453 You have no mail
+1999-03-02 09:44:33 exim x.yz daemon started: pid=p1238, no queue runs, listening for SMTP on port PORT_D port PORT_D2
+1999-03-02 09:44:33 ATRN '' received from (tester) [ip4.ip4.ip4.ip4]
+1999-03-02 09:44:33 Start 'altqueue' queue run: pid=p1239 -qq -Rr @(clientdom.net)
+1999-03-02 09:44:33 10HmbB-000000005vi-0000 => g@??? Q=altqueue R=client T=client_smtp H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] C="250 ODMR client: message accepted"
+1999-03-02 09:44:33 10HmbB-000000005vi-0000 Completed
+1999-03-02 09:44:33 End 'altqueue' queue run: pid=p1239 -qq -Rr @(clientdom.net)
diff --git a/test/log/1148 b/test/log/1148
new file mode 100644
index 000000000..38965de77
--- /dev/null
+++ b/test/log/1148
@@ -0,0 +1,18 @@
+1999-03-02 09:44:33 10HmaX-000000005vi-0000 <= CALLER@??? U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaY-000000005vi-0000 <= CALLER@??? U=CALLER P=local S=sss
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=p1234, no queue runs, listening for SMTP on port PORT_D
+1999-03-02 09:44:33 ATRN '' received from (testclient) [127.0.0.1]
+1999-03-02 09:44:33 tls_in_cipher TLS1.x:ke-RSA-AES256-SHAnnn:xxx
+1999-03-02 09:44:33 Start queue run: pid=p1235 -qq -Rr @(clientdom.net)
+1999-03-02 09:44:33 10HmaX-000000005vi-0000 => a@??? R=client T=client_smtp H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no C="250 ODMR client: message accepted"
+1999-03-02 09:44:33 10HmaX-000000005vi-0000 Completed
+1999-03-02 09:44:33 End queue run: pid=p1235 -qq -Rr @(clientdom.net)
+1999-03-02 09:44:33 exim x.yz daemon started: pid=p1236, no queue runs, listening for SMTPS on port PORT_D
+1999-03-02 09:44:33 ATRN '' received from (testclient) [127.0.0.1]
+1999-03-02 09:44:33 tls_in_cipher TLS1.x:ke-RSA-AES256-SHAnnn:xxx
+1999-03-02 09:44:33 Start queue run: pid=p1237 -qq -Rr @(clientdom.net)
+1999-03-02 09:44:33 10HmaY-000000005vi-0000 => b@??? R=client T=client_smtp H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke-RSA-AES256-SHAnnn:xxx CV=no C="250 ODMR client: message accepted"
+1999-03-02 09:44:33 10HmaY-000000005vi-0000 Completed
+1999-03-02 09:44:33 End queue run: pid=p1237 -qq -Rr @(clientdom.net)
diff --git a/test/rejectlog/0639 b/test/rejectlog/0639
new file mode 100644
index 000000000..de8ac791a
--- /dev/null
+++ b/test/rejectlog/0639
@@ -0,0 +1,4 @@
+
+******** SERVER ********
+1999-03-02 09:44:33 H=(tester) [127.0.0.1] rejected ATRN #failacl
+1999-03-02 09:44:33 U=unknown temporarily rejected ATRN cl2dom.net: 453 You have no mail
diff --git a/test/scripts/0000-Basic/0639 b/test/scripts/0000-Basic/0639
new file mode 100644
index 000000000..9461a98be
--- /dev/null
+++ b/test/scripts/0000-Basic/0639
@@ -0,0 +1,294 @@
+# ATRN
+exim -bd -DSERVER=server -oX PORT_D:PORT_D2
+****
+#
+# Should not advertise on "normal" SMTP port,
+# end should reject an attempt
+client 127.0.0.1 PORT_D
+??? 220
+EHLO tester
+??? 250-
+??? 250-SIZE
+??? 250-LIMITS
+??? 250-8BITMIME
+??? 250-PIPELINING
+??? 250-AUTH
+??? 250 HELP
+ATRN #notadvertised
+??? 502
+QUIT
+??? 221
+???*
+****
+# should reject when not authenticated
+# also when authed, but tried within a mail transaction
+# also when the ACL says deny (based on src ip)
+client 127.0.0.1 PORT_D2
+??? 220
+EHLO tester
+??? 250-
+??? 250-SIZE
+??? 250-LIMITS
+??? 250-8BITMIME
+??? 250-ATRN
+??? 250-PIPELINING
+??? 250-AUTH
+??? 250 HELP
+ATRN #notauth
+??? 530
+AUTH PLAIN AHVzZXJ4AHNlY3JldA==
+??? 235
+MAIL FROM:<fred@???>
+??? 250
+ATRN #inmail
+??? 503
+RSET
+??? 250
+ATRN #failacl
+??? 450
+****
+#
+#
+# setup a queued message, then probe for it
+client HOSTIPV4 PORT_D2
+??? 220
+EHLO tester
+??? 250-
+??? 250-
+??? 250-
+??? 250-
+??? 250-ATRN
+??? 250-
+??? 250-AUTH
+??? 250 HELP
+MAIL FROM:<fred@???>
+??? 250
+RCPT TO:<bill@???>
+??? 250
+DATA
+??? 354
+Subject: queued msg
+
+Body data is a single line.
+.
+??? 250
+AUTH PLAIN AHVzZXJ4AHNlY3JldA==
+??? 235
+ATRN
+??? 250
+220 ODMR client now in reversed state
+??? EHLO
+250-hello, mate
+250 HELP
+??? MAIL
+250 saw your MAIL cmd
+??? RCPT
+250 saw your RCPT cmd
+??? DATA
+354 go ahead
+??? Received:
+??? \x09by
+??? \x09(
+??? \x09id
+??? \x09for
+??? \x09
+??? Subject: queued msg
+??? 
+??? Body
+??? .
+250 ODMR client: message accepted
+??? QUIT
+221 bye
+???*
+****
+#
+#
+# A mixture of spooled messages
+exim a@???
+Subject: should not look at this one
+****
+exim b@??? c@??? d@???
+Subject: only b & d should be delivered
+****
+exim e@???
+Subject: should be delivered
+****
+client HOSTIPV4 PORT_D2
+??? 220
+EHLO tester
+??? 250-
+??? 250-
+??? 250-
+??? 250-
+??? 250-ATRN
+??? 250-
+??? 250-AUTH
+??? 250 HELP
+AUTH PLAIN AHVzZXJ4AHNlY3JldA==
+??? 235
+ATRN
+??? 250
+220 ODMR client now in reversed state
+??? EHLO
+250-hello, mate
+250-PIPELINING
+250 HELP
+??? MAIL
+250 saw your MAIL cmd
+??? RCPT TO:<b
+250 saw your RCPT cmd for b
+??? RCPT TO:<d
+250 saw your RCPT cmd for d
+??? DATA
+354 go ahead
+??? Received:
+??? \x09(
+??? \x09id
+??? \x09
+??? Subject: only b & d should be delivered
+??? Message-Id:
+??? From:
+??? Date:
+??? 
+??? .
+250 ODMR client: message accepted
+??? MAIL
+250 saw your MAIL cmd
+??? RCPT TO:<e
+250 saw your RCPT cmd for e
+??? DATA
+354 go ahead
+??? Received:
+??? \x09(
+??? \x09id
+??? \x09for
+??? \x09
+??? Subject: should be delivered
+??? Message-Id:
+??? From:
+??? Date:
+??? 
+??? .
+250 ODMR client: message accepted
+??? QUIT
+221 bye
+****
+millisleep 500
+#
+#
+# client requests specific domain; ok
+# then tries again; tmp-reject since nothing waiting
+exim f@???
+Subject: should be delivered
+****
+client HOSTIPV4 PORT_D2
+??? 220
+EHLO tester
+??? 250-
+??? 250-
+??? 250-
+??? 250-
+??? 250-ATRN
+??? 250-
+??? 250-AUTH
+??? 250 HELP
+AUTH PLAIN AHVzZXJ4AHNlY3JldA==
+??? 235
+ATRN cl2dom.net
+??? 250
+220 ODMR client now in reversed state
+??? EHLO
+250-hello, mate
+250 HELP
+??? MAIL
+250 saw your MAIL cmd
+??? RCPT TO:<f
+250 saw your RCPT cmd for f
+??? DATA
+354 go ahead
+??? Received:
+??? \x09(
+??? \x09id
+??? \x09for
+??? \x09
+??? Subject: should be delivered
+??? Message-Id:
+??? From:
+??? Date:
+??? 
+??? .
+250 ODMR client: message accepted
+??? QUIT
+221 bye
+****
+sleep 1
+exim -bp
+****
+client HOSTIPV4 PORT_D2
+??? 220
+EHLO tester
+??? 250-
+??? 250-
+??? 250-
+??? 250-
+??? 250-ATRN
+??? 250-
+??? 250-AUTH
+??? 250 HELP
+AUTH PLAIN AHVzZXJ4AHNlY3JldA==
+??? 235
+ATRN cl2dom.net
+??? 453
+QUIT
+??? 221
+****
+killdaemon
+#
+exim -bd -DSERVER=server -DQDG=altqueue -oX PORT_D:PORT_D2
+****
+exim -MCG altqueue g@???
+Subject: should be delivered
+****
+client HOSTIPV4 PORT_D2
+??? 220
+EHLO tester
+??? 250-
+??? 250-
+??? 250-
+??? 250-
+??? 250-ATRN
+??? 250-
+??? 250-AUTH
+??? 250 HELP
+AUTH PLAIN AHVzZXJ4AHNlY3JldA==
+??? 235
+ATRN
+??? 250
+220 ODMR client now in reversed state
+??? EHLO
+250-hello, mate
+250 HELP
+??? MAIL
+250 saw your MAIL cmd
+??? RCPT TO:<g
+250 saw your RCPT cmd for g
+??? DATA
+354 go ahead
+??? Received:
+??? \x09(
+??? \x09id
+??? \x09for
+??? \x09
+??? Subject: should be delivered
+??? Message-Id:
+??? From:
+??? Date:
+??? 
+??? .
+250 ODMR client: message accepted
+??? QUIT
+221 bye
+****
+#
+killdaemon
+no_msglog_check
diff --git a/test/scripts/1100-Basic-TLS/1148 b/test/scripts/1100-Basic-TLS/1148
new file mode 100644
index 000000000..97c328a2f
--- /dev/null
+++ b/test/scripts/1100-Basic-TLS/1148
@@ -0,0 +1,101 @@
+# ATRN under TLS
+exim -DSERVER=server -bd -oX PORT_D
+****
+exim a@???
+Subject: foo
+
+sole body line
+****
+client-anytls 127.0.0.1 PORT_D
+??? 220
+EHLO testclient
+????250
+STARTTLS
+??? 220
+EHLO testclient
+????250
+AUTH PLAIN AHVzZXJ4AHNlY3JldA==
+??? 235
+ATRN
+??? 250
+220 ODMR client now in reversed state
+??? EHLO
+250-hello, mate
+250 HELP
+??? MAIL
+250 saw your MAIL cmd
+??? RCPT
+250 saw your RCPT cmd
+??? DATA
+354 go ahead
+??? Received:
+??? \x09(
+??? \x09id
+??? \x09for
+??? \x09
+??? Subject:
+??? Message-Id:
+??? From:
+??? Date:
+??? 
+??? sole body line
+??? .
+250 ODMR client: message accepted
+??? QUIT
+221 bye
+stoptls
+???*
+****
+millisleep 500
+killdaemon
+#
+#
+#
+# Again, for TLS-on-connect
+exim -DSERVER=server -DSUB=PORT_D -bd -oX PORT_D
+****
+exim b@???
+Subject: foo
+
+sole body line
+****
+client-anytls -tls-on-connect 127.0.0.1 PORT_D
+??? 220
+EHLO testclient
+????250
+EHLO testclient
+????250
+AUTH PLAIN AHVzZXJ4AHNlY3JldA==
+??? 235
+ATRN
+??? 250
+220 ODMR client now in reversed state
+??? EHLO
+250-hello, mate
+250 HELP
+??? MAIL
+250 saw your MAIL cmd
+??? RCPT
+250 saw your RCPT cmd
+??? DATA
+354 go ahead
+??? Received:
+??? \x09(
+??? \x09id
+??? \x09for
+??? \x09
+??? Subject:
+??? Message-Id:
+??? From:
+??? Date:
+??? 
+??? sole body line
+??? .
+250 ODMR client: message accepted
+??? QUIT
+221 bye
+stoptls
+???*
+****
+millisleep 500
+killdaemon
diff --git a/test/stderr/0544 b/test/stderr/0544
index 84ecb04f3..34fd87a97 100644
--- a/test/stderr/0544
+++ b/test/stderr/0544
@@ -506,6 +506,7 @@ try option acl_smtp_helo
  ├─────result: true
  ├───expanded: ${if░match_domain░{$sender_helo_name}{+dlist}}
  ╰─────result: true
+try option acl_smtp_atrn
 try option acl_smtp_etrn
 try option acl_smtp_vrfy
 try option acl_smtp_expn
diff --git a/test/stderr/5410 b/test/stderr/5410
index c263d3415..dce73fa67 100644
--- a/test/stderr/5410
+++ b/test/stderr/5410
@@ -31,6 +31,7 @@ try option acl_smtp_helo
  list element: *
   in limits_advertise_hosts? yes (matched "*")
  in dsn_advertise_hosts? no (option unset)
+try option acl_smtp_atrn
 try option acl_smtp_etrn
 try option acl_smtp_vrfy
 try option acl_smtp_expn
@@ -708,6 +709,7 @@ try option acl_smtp_helo
  list element: *
   in limits_advertise_hosts? yes (matched "*")
  in dsn_advertise_hosts? no (option unset)
+try option acl_smtp_atrn
 try option acl_smtp_etrn
 try option acl_smtp_vrfy
 try option acl_smtp_expn
@@ -1334,6 +1336,7 @@ try option acl_smtp_helo
  list element: *
   in limits_advertise_hosts? yes (matched "*")
  in dsn_advertise_hosts? no (option unset)
+try option acl_smtp_atrn
 try option acl_smtp_etrn
 try option acl_smtp_vrfy
 try option acl_smtp_expn
diff --git a/test/stdout/0639 b/test/stdout/0639
new file mode 100644
index 000000000..6efe93adc
--- /dev/null
+++ b/test/stdout/0639
@@ -0,0 +1,416 @@
+Connecting to 127.0.0.1 port 1225 ... connected
+??? 220
+<<< 220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+>>> EHLO tester
+??? 250-
+<<< 250-the.local.host.name Hello tester [127.0.0.1]
+??? 250-SIZE
+<<< 250-SIZE 52428800
+??? 250-LIMITS
+<<< 250-LIMITS MAILMAX=1000 RCPTMAX=50000
+??? 250-8BITMIME
+<<< 250-8BITMIME
+??? 250-PIPELINING
+<<< 250-PIPELINING
+??? 250-AUTH
+<<< 250-AUTH PLAIN
+??? 250 HELP
+<<< 250 HELP
+>>> ATRN #notadvertised
+??? 502
+<<< 502 ATRN command used when not advertised
+>>> QUIT
+??? 221
+<<< 221 the.local.host.name closing connection
+???*
+Expected EOF read
+End of script
+Connecting to 127.0.0.1 port 1226 ... connected
+??? 220
+<<< 220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+>>> EHLO tester
+??? 250-
+<<< 250-the.local.host.name Hello tester [127.0.0.1]
+??? 250-SIZE
+<<< 250-SIZE 52428800
+??? 250-LIMITS
+<<< 250-LIMITS MAILMAX=1000 RCPTMAX=50000
+??? 250-8BITMIME
+<<< 250-8BITMIME
+??? 250-ATRN
+<<< 250-ATRN
+??? 250-PIPELINING
+<<< 250-PIPELINING
+??? 250-AUTH
+<<< 250-AUTH PLAIN
+??? 250 HELP
+<<< 250 HELP
+>>> ATRN #notauth
+??? 530
+<<< 530 ATRN is not permitted without authentication
+>>> AUTH PLAIN AHVzZXJ4AHNlY3JldA==
+??? 235
+<<< 235 Authentication succeeded
+>>> MAIL FROM:<fred@???>
+??? 250
+<<< 250 OK
+>>> ATRN #inmail
+??? 503
+<<< 503 ATRN is not permitted inside a transaction
+>>> RSET
+??? 250
+<<< 250 Reset OK
+>>> ATRN #failacl
+??? 450
+<<< 450 Administrative prohibition
+End of script
+Connecting to ip4.ip4.ip4.ip4 port 1226 ... connected
+??? 220
+<<< 220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+>>> EHLO tester
+??? 250-
+<<< 250-the.local.host.name Hello tester [ip4.ip4.ip4.ip4]
+??? 250-
+<<< 250-SIZE 52428800
+??? 250-
+<<< 250-LIMITS MAILMAX=1000 RCPTMAX=50000
+??? 250-
+<<< 250-8BITMIME
+??? 250-ATRN
+<<< 250-ATRN
+??? 250-
+<<< 250-PIPELINING
+??? 250-AUTH
+<<< 250-AUTH PLAIN
+??? 250 HELP
+<<< 250 HELP
+>>> MAIL FROM:<fred@???>
+??? 250
+<<< 250 OK
+>>> RCPT TO:<bill@???>
+??? 250
+<<< 250 Accepted
+>>> DATA
+??? 354
+<<< 354 Enter message, ending with "." on a line by itself
+>>> Subject: queued msg
+>>> 
+>>> Body data is a single line.
+>>> .
+??? 250
+<<< 250 OK id=10HmbC-000000005vi-0000
+>>> AUTH PLAIN AHVzZXJ4AHNlY3JldA==
+??? 235
+<<< 235 Authentication succeeded
+>>> ATRN
+??? 250
+<<< 250 ODMR server turning line around
+>>> 220 ODMR client now in reversed state
+??? EHLO
+<<< EHLO the.local.host.name
+>>> 250-hello, mate
+>>> 250 HELP
+??? MAIL
+<<< MAIL FROM:<fred@???>
+>>> 250 saw your MAIL cmd
+??? RCPT
+<<< RCPT TO:<bill@???>
+>>> 250 saw your RCPT cmd
+??? DATA
+<<< DATA
+>>> 354 go ahead
+??? Received:
+<<< Received: from [ip4.ip4.ip4.ip4] (helo=tester)
+??? \x09by
+<<<     by the.local.host.name with esmtp (Exim x.yz)
+??? \x09(
+<<<     (envelope-from <fred@???>)
+??? \x09id
+<<<     id 10HmbC-000000005vi-0000
+??? \x09for
+<<<     for bill@???;
+??? \x09
+<<<     Tue, 2 Mar 1999 09:44:33 +0000
+??? Subject: queued msg
+<<< Subject: queued msg
+??? 
+<<< 
+??? Body
+<<< Body data is a single line.
+??? .
+<<< .
+>>> 250 ODMR client: message accepted
+??? QUIT
+<<< QUIT
+>>> 221 bye
+???*
+Expected EOF read
+End of script
+Connecting to ip4.ip4.ip4.ip4 port 1226 ... connected
+??? 220
+<<< 220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+>>> EHLO tester
+??? 250-
+<<< 250-the.local.host.name Hello tester [ip4.ip4.ip4.ip4]
+??? 250-
+<<< 250-SIZE 52428800
+??? 250-
+<<< 250-LIMITS MAILMAX=1000 RCPTMAX=50000
+??? 250-
+<<< 250-8BITMIME
+??? 250-ATRN
+<<< 250-ATRN
+??? 250-
+<<< 250-PIPELINING
+??? 250-AUTH
+<<< 250-AUTH PLAIN
+??? 250 HELP
+<<< 250 HELP
+>>> AUTH PLAIN AHVzZXJ4AHNlY3JldA==
+??? 235
+<<< 235 Authentication succeeded
+>>> ATRN
+??? 250
+<<< 250 ODMR server turning line around
+>>> 220 ODMR client now in reversed state
+??? EHLO
+<<< EHLO the.local.host.name
+>>> 250-hello, mate
+>>> 250-PIPELINING
+>>> 250 HELP
+??? MAIL
+<<< MAIL FROM:<CALLER@???>
+>>> 250 saw your MAIL cmd
+??? RCPT TO:<b
+<<< RCPT TO:<b@???>
+>>> 250 saw your RCPT cmd for b
+??? RCPT TO:<d
+<<< RCPT TO:<d@???>
+>>> 250 saw your RCPT cmd for d
+??? DATA
+<<< DATA
+>>> 354 go ahead
+??? Received:
+<<< Received: from CALLER by the.local.host.name with local (Exim x.yz)
+??? \x09(
+<<<     (envelope-from <CALLER@???>)
+??? \x09id
+<<<     id 10HmaY-000000005vi-0000;
+??? \x09
+<<<     Tue, 2 Mar 1999 09:44:33 +0000
+??? Subject: only b & d should be delivered
+<<< Subject: only b & d should be delivered
+??? Message-Id:
+<<< Message-Id: <E10HmaY-000000005vi-0000@???>
+??? From:
+<<< From: CALLER_NAME <CALLER@???>
+??? Date:
+<<< Date: Tue, 2 Mar 1999 09:44:33 +0000
+??? 
+<<< 
+??? .
+<<< .
+>>> 250 ODMR client: message accepted
+??? MAIL
+<<< MAIL FROM:<CALLER@???>
+>>> 250 saw your MAIL cmd
+??? RCPT TO:<e
+<<< RCPT TO:<e@???>
+>>> 250 saw your RCPT cmd for e
+??? DATA
+<<< DATA
+>>> 354 go ahead
+??? Received:
+<<< Received: from CALLER by the.local.host.name with local (Exim x.yz)
+??? \x09(
+<<<     (envelope-from <CALLER@???>)
+??? \x09id
+<<<     id 10HmaZ-000000005vi-0000
+??? \x09for
+<<<     for e@???;
+??? \x09
+<<<     Tue, 2 Mar 1999 09:44:33 +0000
+??? Subject: should be delivered
+<<< Subject: should be delivered
+??? Message-Id:
+<<< Message-Id: <E10HmaZ-000000005vi-0000@???>
+??? From:
+<<< From: CALLER_NAME <CALLER@???>
+??? Date:
+<<< Date: Tue, 2 Mar 1999 09:44:33 +0000
+??? 
+<<< 
+??? .
+<<< .
+>>> 250 ODMR client: message accepted
+??? QUIT
+<<< QUIT
+>>> 221 bye
+End of script
+Connecting to ip4.ip4.ip4.ip4 port 1226 ... connected
+??? 220
+<<< 220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+>>> EHLO tester
+??? 250-
+<<< 250-the.local.host.name Hello tester [ip4.ip4.ip4.ip4]
+??? 250-
+<<< 250-SIZE 52428800
+??? 250-
+<<< 250-LIMITS MAILMAX=1000 RCPTMAX=50000
+??? 250-
+<<< 250-8BITMIME
+??? 250-ATRN
+<<< 250-ATRN
+??? 250-
+<<< 250-PIPELINING
+??? 250-AUTH
+<<< 250-AUTH PLAIN
+??? 250 HELP
+<<< 250 HELP
+>>> AUTH PLAIN AHVzZXJ4AHNlY3JldA==
+??? 235
+<<< 235 Authentication succeeded
+>>> ATRN cl2dom.net
+??? 250
+<<< 250 ODMR server turning line around
+>>> 220 ODMR client now in reversed state
+??? EHLO
+<<< EHLO the.local.host.name
+>>> 250-hello, mate
+>>> 250 HELP
+??? MAIL
+<<< MAIL FROM:<CALLER@???>
+>>> 250 saw your MAIL cmd
+??? RCPT TO:<f
+<<< RCPT TO:<f@???>
+>>> 250 saw your RCPT cmd for f
+??? DATA
+<<< DATA
+>>> 354 go ahead
+??? Received:
+<<< Received: from CALLER by the.local.host.name with local (Exim x.yz)
+??? \x09(
+<<<     (envelope-from <CALLER@???>)
+??? \x09id
+<<<     id 10HmbA-000000005vi-0000
+??? \x09for
+<<<     for f@???;
+??? \x09
+<<<     Tue, 2 Mar 1999 09:44:33 +0000
+??? Subject: should be delivered
+<<< Subject: should be delivered
+??? Message-Id:
+<<< Message-Id: <E10HmbA-000000005vi-0000@???>
+??? From:
+<<< From: CALLER_NAME <CALLER@???>
+??? Date:
+<<< Date: Tue, 2 Mar 1999 09:44:33 +0000
+??? 
+<<< 
+??? .
+<<< .
+>>> 250 ODMR client: message accepted
+??? QUIT
+<<< QUIT
+>>> 221 bye
+End of script
+TTT   sss 10HmaX-000000005vi-0000 <CALLER@???>
+          a@???
+
+Connecting to ip4.ip4.ip4.ip4 port 1226 ... connected
+??? 220
+<<< 220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+>>> EHLO tester
+??? 250-
+<<< 250-the.local.host.name Hello tester [ip4.ip4.ip4.ip4]
+??? 250-
+<<< 250-SIZE 52428800
+??? 250-
+<<< 250-LIMITS MAILMAX=1000 RCPTMAX=50000
+??? 250-
+<<< 250-8BITMIME
+??? 250-ATRN
+<<< 250-ATRN
+??? 250-
+<<< 250-PIPELINING
+??? 250-AUTH
+<<< 250-AUTH PLAIN
+??? 250 HELP
+<<< 250 HELP
+>>> AUTH PLAIN AHVzZXJ4AHNlY3JldA==
+??? 235
+<<< 235 Authentication succeeded
+>>> ATRN cl2dom.net
+??? 453
+<<< 453 You have no mail
+>>> QUIT
+??? 221
+<<< 221 the.local.host.name closing connection
+End of script
+Connecting to ip4.ip4.ip4.ip4 port 1226 ... connected
+??? 220
+<<< 220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+>>> EHLO tester
+??? 250-
+<<< 250-the.local.host.name Hello tester [ip4.ip4.ip4.ip4]
+??? 250-
+<<< 250-SIZE 52428800
+??? 250-
+<<< 250-LIMITS MAILMAX=1000 RCPTMAX=50000
+??? 250-
+<<< 250-8BITMIME
+??? 250-ATRN
+<<< 250-ATRN
+??? 250-
+<<< 250-PIPELINING
+??? 250-AUTH
+<<< 250-AUTH PLAIN
+??? 250 HELP
+<<< 250 HELP
+>>> AUTH PLAIN AHVzZXJ4AHNlY3JldA==
+??? 235
+<<< 235 Authentication succeeded
+>>> ATRN
+??? 250
+<<< 250 ODMR server turning line around
+>>> 220 ODMR client now in reversed state
+??? EHLO
+<<< EHLO the.local.host.name
+>>> 250-hello, mate
+>>> 250 HELP
+??? MAIL
+<<< MAIL FROM:<CALLER@???>
+>>> 250 saw your MAIL cmd
+??? RCPT TO:<g
+<<< RCPT TO:<g@???>
+>>> 250 saw your RCPT cmd for g
+??? DATA
+<<< DATA
+>>> 354 go ahead
+??? Received:
+<<< Received: from CALLER by the.local.host.name with local (Exim x.yz)
+??? \x09(
+<<<     (envelope-from <CALLER@???>)
+??? \x09id
+<<<     id 10HmbB-000000005vi-0000
+??? \x09for
+<<<     for g@???;
+??? \x09
+<<<     Tue, 2 Mar 1999 09:44:33 +0000
+??? Subject: should be delivered
+<<< Subject: should be delivered
+??? Message-Id:
+<<< Message-Id: <E10HmbB-000000005vi-0000@???>
+??? From:
+<<< From: CALLER_NAME <CALLER@???>
+??? Date:
+<<< Date: Tue, 2 Mar 1999 09:44:33 +0000
+??? 
+<<< 
+??? .
+<<< .
+>>> 250 ODMR client: message accepted
+??? QUIT
+<<< QUIT
+>>> 221 bye
+End of script
diff --git a/test/stdout/1148 b/test/stdout/1148
new file mode 100644
index 000000000..1ea653694
--- /dev/null
+++ b/test/stdout/1148
@@ -0,0 +1,125 @@
+Connecting to 127.0.0.1 port 1225 ... connected
+??? 220
+<<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+>>> EHLO testclient
+????250
+>>> STARTTLS
+??? 220
+<<< 220 TLS go ahead
+Attempting to start TLS
+Succeeded in starting TLS
+>>> EHLO testclient
+????250
+>>> AUTH PLAIN AHVzZXJ4AHNlY3JldA==
+??? 235
+<<< 235 Authentication succeeded
+>>> ATRN
+??? 250
+<<< 250 ODMR server turning line around
+>>> 220 ODMR client now in reversed state
+??? EHLO
+<<< EHLO myhost.test.ex
+>>> 250-hello, mate
+>>> 250 HELP
+??? MAIL
+<<< MAIL FROM:<CALLER@???>
+>>> 250 saw your MAIL cmd
+??? RCPT
+<<< RCPT TO:<a@???>
+>>> 250 saw your RCPT cmd
+??? DATA
+<<< DATA
+>>> 354 go ahead
+??? Received:
+<<< Received: from CALLER by myhost.test.ex with local (Exim x.yz)
+??? \x09(
+<<<     (envelope-from <CALLER@???>)
+??? \x09id
+<<<     id 10HmaX-000000005vi-0000
+??? \x09for
+<<<     for a@???;
+??? \x09
+<<<     Tue, 2 Mar 1999 09:44:33 +0000
+??? Subject:
+<<< Subject: foo
+??? Message-Id:
+<<< Message-Id: <E10HmaX-000000005vi-0000@???>
+??? From:
+<<< From: CALLER_NAME <CALLER@???>
+??? Date:
+<<< Date: Tue, 2 Mar 1999 09:44:33 +0000
+??? 
+<<< 
+??? sole body line
+<<< sole body line
+??? .
+<<< .
+>>> 250 ODMR client: message accepted
+??? QUIT
+<<< QUIT
+>>> 221 bye
+Shutting down TLS encryption
+???*
+Expected EOF read
+End of script
+Connecting to 127.0.0.1 port 1225 ... connected
+Attempting to start TLS
+Succeeded in starting TLS
+??? 220
+<<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+>>> EHLO testclient
+????250
+>>> EHLO testclient
+????250
+>>> AUTH PLAIN AHVzZXJ4AHNlY3JldA==
+??? 235
+<<< 235 Authentication succeeded
+>>> ATRN
+??? 250
+<<< 250 ODMR server turning line around
+>>> 220 ODMR client now in reversed state
+??? EHLO
+<<< EHLO myhost.test.ex
+>>> 250-hello, mate
+>>> 250 HELP
+??? MAIL
+<<< MAIL FROM:<CALLER@???>
+>>> 250 saw your MAIL cmd
+??? RCPT
+<<< RCPT TO:<b@???>
+>>> 250 saw your RCPT cmd
+??? DATA
+<<< DATA
+>>> 354 go ahead
+??? Received:
+<<< Received: from CALLER by myhost.test.ex with local (Exim x.yz)
+??? \x09(
+<<<     (envelope-from <CALLER@???>)
+??? \x09id
+<<<     id 10HmaY-000000005vi-0000
+??? \x09for
+<<<     for b@???;
+??? \x09
+<<<     Tue, 2 Mar 1999 09:44:33 +0000
+??? Subject:
+<<< Subject: foo
+??? Message-Id:
+<<< Message-Id: <E10HmaY-000000005vi-0000@???>
+??? From:
+<<< From: CALLER_NAME <CALLER@???>
+??? Date:
+<<< Date: Tue, 2 Mar 1999 09:44:33 +0000
+??? 
+<<< 
+??? sole body line
+<<< sole body line
+??? .
+<<< .
+>>> 250 ODMR client: message accepted
+??? QUIT
+<<< QUIT
+>>> 221 bye
+Shutting down TLS encryption
+???*
+Expected EOF read
+End of script


--
## subscription configuration (requires account):
## https://lists.exim.org/mailman3/postorius/lists/exim-cvs.lists.exim.org/
## unsubscribe (doesn't require an account):
## exim-cvs-unsubscribe@???
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/