ATRN provider
authorJeremy Harris <jgh146exb@wizmail.org>
Fri, 8 Nov 2024 17:50:26 +0000 (17:50 +0000)
committerJeremy Harris <jgh146exb@wizmail.org>
Fri, 8 Nov 2024 17:55:30 +0000 (17:55 +0000)
28 files changed:
doc/doc-docbook/spec.xfpt
doc/doc-txt/NewStuff
doc/doc-txt/OptionLists.txt
src/src/acl.c
src/src/deliver.c
src/src/expand.c
src/src/functions.h
src/src/globals.c
src/src/globals.h
src/src/macros.h
src/src/queue.c
src/src/readconf.c
src/src/smtp_in.c
src/src/tls-gnu.c
src/src/tls-openssl.c
src/src/transports/smtp.c
test/README
test/confs/0639 [new file with mode: 0644]
test/confs/1148 [new file with mode: 0644]
test/log/0639 [new file with mode: 0644]
test/log/1148 [new file with mode: 0644]
test/rejectlog/0639 [new file with mode: 0644]
test/scripts/0000-Basic/0639 [new file with mode: 0644]
test/scripts/1100-Basic-TLS/1148 [new file with mode: 0644]
test/stderr/0544
test/stderr/5410
test/stdout/0639 [new file with mode: 0644]
test/stdout/1148 [new file with mode: 0644]

index 661421be1ba1b9de7238a05cbbe78c0450f42fa5..6ad0093166b9e7e58f523e673053c18646064a82 100644 (file)
@@ -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
 &%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%&"
 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.
 
 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
 .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_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"
 .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"
                                            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"
 .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.
 
 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"
 .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.
 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"
 
 .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
 
 
 .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
 .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
 
 
 .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.
 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 "&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 "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 "ETRN" "ACL for"
 .cindex "EXPN" "ACL for"
 .cindex "HELO" "ACL for"
 .cindex "EHLO" "ACL for"
-.cindex "DKIM" "ACL for"
 .cindex "MAIL" "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"
 .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 "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"
 
 .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"
 .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"&.
 
 &%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.
 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$&
 .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,
 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.
 
 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"
 .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.
 
 
 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"
 
 .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
 .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
 .cindex "SMTP" "batched incoming"
 .cindex "batched SMTP input"
 The &%-bS%& command line option causes Exim to accept one or more messages by
index 27508be8d138dcd14abe3ea00ff0180a580eed88..cdcb11bb8cdf31f41f405e93f48dc0099cc0a523 100644 (file)
@@ -22,9 +22,11 @@ Version 4.98
     modules
 
  6. A transport "socks_proxy" may expand to an empty string, specifying no
     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
 ------------
 
 Version 4.98
 ------------
index e7b28476055a526ac642c702ab43d3a1d40c62e2..72290415c5007320a12424cdc79d53cf773c9167 100644 (file)
@@ -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
 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
 acl_smtp_auth                        string*         unset         main              4.00
 acl_smtp_connect                     string*         unset         main              4.11
 acl_smtp_data                        string*         unset         main              4.00
index ab05b13a966e92c134631db97698d656a60e5e10..88d09479ce08ed8a1635a9dbee1336f0ebe38c26 100644 (file)
@@ -61,6 +61,7 @@ static int msgcond[] = {
 
 enum { ACLC_ACL,
        ACLC_ADD_HEADER,
 
 enum { ACLC_ACL,
        ACLC_ADD_HEADER,
+       ACLC_ATRN_DOMAINS,
        ACLC_AUTHENTICATED,
 #ifdef EXPERIMENTAL_BRIGHTMAIL
        ACLC_BMI_OPTIN,
        ACLC_AUTHENTICATED,
 #ifdef EXPERIMENTAL_BRIGHTMAIL
        ACLC_BMI_OPTIN,
@@ -160,6 +161,10 @@ static condition_def conditions[] = {
                                    ACL_BIT_NOTSMTP_START),
   },
 
                                    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 |
   [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)
     {
 
   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. */
 
     /* 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;
 
         }
       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;
     case ACLC_AUTHENTICATED:
       rc = sender_host_authenticated ? match_isinlist(sender_host_authenticated,
              &arg, 0, NULL, NULL, MCL_STRING, TRUE, NULL) : FAIL;
index 0ba5f81b07c0ae838b7491882de25f88cd3d0a93..0f7e83bd2bf8664729159c6b5093d62df415459a 100644 (file)
@@ -4624,7 +4624,13 @@ nonmatch domains
   f.continue_more = FALSE;           /* In case got set for the last lot */
   if (continue_transport)
     {
   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
 /*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,
 
   /*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())
   "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; }
     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
     }
 
   /* 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.
 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.
 
 
 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.
 
 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
 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 (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;
       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
       }
        }
 #endif
       }
+    }
 
 DEBUG(D_deliver)
   {
 
 DEBUG(D_deliver)
   {
index fd5884306400c1851d2ae9cd769749cef393d192..e4224dbb1b9ff8ba92e9c3165f2f774fdb9377e8 100644 (file)
@@ -459,6 +459,7 @@ static var_entry var_table[] = {
   { "arc_state",           vtype_module,       US"arc" },
   { "arc_state_reason",    vtype_module,       US"arc" },
 #endif
   { "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 },
   { "authenticated_fail_id",vtype_stringptr,  &authenticated_fail_id },
   { "authenticated_id",    vtype_stringptr,   &authenticated_id },
   { "authenticated_sender",vtype_stringptr,   &authenticated_sender },
index a8c546efda004d883083cb6b584c2690a0b9bec7..11a4b66578c849588026bbcbbcfc9d6489dcd96b 100644 (file)
@@ -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 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);
 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 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 *);
 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 *);
index e887c27410aefc9b2ec40275ee157c16b01ad3b5..dcd5dbe3f0da2024ec01ae0f6b975ecc3e856e91 100644 (file)
@@ -441,6 +441,7 @@ uschar *acl_not_smtp_mime      = NULL;
 #endif
 uschar *acl_not_smtp_start     = NULL;
 uschar *acl_removed_headers    = 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;
 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_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",
                                    [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
 #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_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",
                                    [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;
 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;
 
 uschar *authenticated_fail_id  = NULL;
 uschar *authenticated_id       = NULL;
index 5d912254d7cd6437a482449e26c5eadd8d59e823..8fd24fa762befab74069daf25d47181ba826b78b 100644 (file)
@@ -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 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
 #endif
+extern uschar *acl_smtp_atrn;          /* ACL run for ATRN */
 #ifndef DISABLE_DKIM
 #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 */
 #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 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 */
 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 */
index 6acf242c155f4746ff7188901ef24e7790bdddba..377a87fa2ef5ae147ec4784ad388211b1d446783 100644 (file)
@@ -827,7 +827,7 @@ local_scan.h */
 most recent SMTP commands. SCH_NONE is "empty". */
 
 enum { SCH_NONE, SCH_AUTH, SCH_DATA, SCH_BDAT,
 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
        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 ****
 */
 
 **** 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
 #ifndef DISABLE_PRDR
-       ACL_WHERE_PRDR,       /* )                                           */
+       ACL_WHERE_PRDR,         /* )                                            */
 #endif
 #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,
        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_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)
 #define ACL_BIT_ETRN           BIT(ACL_WHERE_ETRN)
 #define ACL_BIT_EXPN           BIT(ACL_WHERE_EXPN)
 #define ACL_BIT_HELO           BIT(ACL_WHERE_HELO)
index 8568de78627c4adcafdec7748966b9dfa5f81658..0b460edf38586ebc9e1a0fa3f5020463ff245943 100644 (file)
@@ -46,10 +46,9 @@ Returns:       a pointer to a merged ordered list
 */
 
 static queue_filename *
 */
 
 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)
   {
 
 while (a && b)
   {
@@ -126,15 +125,11 @@ Returns:         pointer to a chain of queue name items
 */
 
 static queue_filename *
 */
 
 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)
 {
   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];
 
 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 */
 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");
 
 #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;
           }
               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 */
         }
 
       /* 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
 #ifdef MEASURE_TIMING
     report_time_since(&timestamp_startup, US"queue msg selected");
 #endif
+    msg_handled = TRUE;
 
 single_item_retry:
     if ((pid = exim_fork(
 
 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);
 
     (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. */
 
     /* 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)
 /* 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 (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" : ""
   );
   *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);
 }
 
 
 
 
 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           *
 ************************************************/
 /************************************************
 *         Count messages on the queue           *
 ************************************************/
index 9600089533d75f9fb78101e8ae86fe6619fd8dcd..7912bca4a94a13c356c9649da588883d6897f220 100644 (file)
@@ -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_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} },
   { "acl_smtp_auth",            opt_stringptr,   {&acl_smtp_auth} },
   { "acl_smtp_connect",         opt_stringptr,   {&acl_smtp_connect} },
   { "acl_smtp_data",            opt_stringptr,   {&acl_smtp_data} },
index e76790fea241c42ac92a9aef33bfadb842715093..8718df1908a641ff6246e5bc9ab89cf92366fb6f 100644 (file)
@@ -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 */
 
   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
   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 },
   { "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 },
   { "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;
 
 
       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
 #ifndef DISABLE_WELLKNOWN
     case WELLKNOWN_CMD:
 #endif
@@ -4161,9 +4159,20 @@ while (done <= 0)
          fl.dsn_advertised = TRUE;
          }
 
          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)
          {
        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);
 #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);
       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;
 
 
       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)
     case ETRN_CMD:
       HAD(SCH_ETRN);
       if (sender_address)
index 7963e2c97bb5664824c990bd5e1ce3df1508dba3..cf41a74c5ffc0bcf64325130ce976d7773115ccc 100644 (file)
@@ -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;
+}
+
 
 
 /*************************************************
 
 
 /*************************************************
index 302404b6c9029f1d8321f58cf44916fa1045c4f3..2b6a05afd8060c9d15921170cd199828a747150b 100644 (file)
@@ -5191,6 +5191,37 @@ for (uschar * s = exp; *s; /**/)
 return TRUE;
 }
 
 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
 */
 #endif /*!MACRO_PREDEF*/
 /* vi: aw ai sw=2
 */
index 79bacfc31d56614138d309314d20fab3359f80eb..4f9851737b4b27f95cecc201edeed296a127ae60 100644 (file)
@@ -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;
 
 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. */
 
 
 /* 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;
 
 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
 #ifdef USE_OPENSSL
-tls_out.sni = NULL;
+  tls_out.sni = NULL;
 #endif
 #endif
-tls_out.ocsp = OCSP_NOT_REQ;
+  tls_out.ocsp = OCSP_NOT_REQ;
 #ifndef DISABLE_TLS_RESUME
 #ifndef DISABLE_TLS_RESUME
-tls_out.resumption = 0;
+  tls_out.resumption = 0;
 #endif
 #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
 
 /* 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. */
 
 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);
   {
   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();
 
   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
 #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
     }
   /* 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;
   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
       {
       /* 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. */
 
 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
 #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. */
 
 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;
   {
   /* sx->pending_MAIL = FALSE; */
   sx->ok = TRUE;
index 3f86755458981fe870e7b11ba05932fbaaf4ee6c..1798c4dc20831be1d49a32256c83a70d4a91349b 100644 (file)
@@ -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.
 (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.
 
 (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 (file)
index 0000000..fb32197
--- /dev/null
@@ -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 (file)
index 0000000..f521785
--- /dev/null
@@ -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 (file)
index 0000000..f9bab37
--- /dev/null
@@ -0,0 +1,37 @@
+1999-03-02 09:44:33 10HmaX-000000005vi-0000 <= CALLER@the.local.host.name U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaY-000000005vi-0000 <= CALLER@the.local.host.name U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaZ-000000005vi-0000 <= CALLER@the.local.host.name U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmbA-000000005vi-0000 <= CALLER@the.local.host.name U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmbB-000000005vi-0000 <= CALLER@the.local.host.name 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@example.com 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@clientdom.net 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@clientdom.net 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@clientdom.net 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@clientdom.net 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@cl2dom.net 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@clientdom.net 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 (file)
index 0000000..38965de
--- /dev/null
@@ -0,0 +1,18 @@
+1999-03-02 09:44:33 10HmaX-000000005vi-0000 <= CALLER@myhost.test.ex U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaY-000000005vi-0000 <= CALLER@myhost.test.ex 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@clientdom.net 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@clientdom.net 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 (file)
index 0000000..de8ac79
--- /dev/null
@@ -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 (file)
index 0000000..9461a98
--- /dev/null
@@ -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@example.com>
+??? 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@example.com>
+??? 250
+RCPT TO:<bill@clientdom.net>
+??? 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@otherdomain.net
+Subject: should not look at this one
+****
+exim b@clientdom.net c@elsewhere.net d@clientdom.net
+Subject: only b & d should be delivered
+****
+exim e@clientdom.net
+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@cl2dom.net
+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@clientdom.net
+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 (file)
index 0000000..97c328a
--- /dev/null
@@ -0,0 +1,101 @@
+# ATRN under TLS
+exim -DSERVER=server -bd -oX PORT_D
+****
+exim a@clientdom.net
+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@clientdom.net
+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
index 84ecb04f3efe72cf93e4ef6c24b9a3832f2fb512..34fd87a97d542e81ffb6344167b57ba52ee8e0ae 100644 (file)
@@ -506,6 +506,7 @@ try option acl_smtp_helo
  ├─────result: true
  ├───expanded: ${if░match_domain░{$sender_helo_name}{+dlist}}
  ╰─────result: true
  ├─────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
 try option acl_smtp_etrn
 try option acl_smtp_vrfy
 try option acl_smtp_expn
index c263d34151a774ba860e0e0a766f1aa4a7ef8075..dce73fa674947bbdf503bb86af5ac90fe94b74c3 100644 (file)
@@ -31,6 +31,7 @@ try option acl_smtp_helo
  list element: *
   in limits_advertise_hosts? yes (matched "*")
  in dsn_advertise_hosts? no (option unset)
  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
 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)
  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
 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)
  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
 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 (file)
index 0000000..6efe93a
--- /dev/null
@@ -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@example.com>
+??? 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@example.com>
+??? 250
+<<< 250 OK
+>>> RCPT TO:<bill@clientdom.net>
+??? 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@example.com>
+>>> 250 saw your MAIL cmd
+??? RCPT
+<<< RCPT TO:<bill@clientdom.net>
+>>> 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@example.com>)
+??? \x09id
+<<<    id 10HmbC-000000005vi-0000
+??? \x09for
+<<<    for bill@clientdom.net;
+??? \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@the.local.host.name>
+>>> 250 saw your MAIL cmd
+??? RCPT TO:<b
+<<< RCPT TO:<b@clientdom.net>
+>>> 250 saw your RCPT cmd for b
+??? RCPT TO:<d
+<<< RCPT TO:<d@clientdom.net>
+>>> 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@the.local.host.name>)
+??? \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@the.local.host.name>
+??? From:
+<<< From: CALLER_NAME <CALLER@the.local.host.name>
+??? Date:
+<<< Date: Tue, 2 Mar 1999 09:44:33 +0000
+??? 
+<<< 
+??? .
+<<< .
+>>> 250 ODMR client: message accepted
+??? MAIL
+<<< MAIL FROM:<CALLER@the.local.host.name>
+>>> 250 saw your MAIL cmd
+??? RCPT TO:<e
+<<< RCPT TO:<e@clientdom.net>
+>>> 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@the.local.host.name>)
+??? \x09id
+<<<    id 10HmaZ-000000005vi-0000
+??? \x09for
+<<<    for e@clientdom.net;
+??? \x09
+<<<    Tue, 2 Mar 1999 09:44:33 +0000
+??? Subject: should be delivered
+<<< Subject: should be delivered
+??? Message-Id:
+<<< Message-Id: <E10HmaZ-000000005vi-0000@the.local.host.name>
+??? From:
+<<< From: CALLER_NAME <CALLER@the.local.host.name>
+??? 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@the.local.host.name>
+>>> 250 saw your MAIL cmd
+??? RCPT TO:<f
+<<< RCPT TO:<f@cl2dom.net>
+>>> 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@the.local.host.name>)
+??? \x09id
+<<<    id 10HmbA-000000005vi-0000
+??? \x09for
+<<<    for f@cl2dom.net;
+??? \x09
+<<<    Tue, 2 Mar 1999 09:44:33 +0000
+??? Subject: should be delivered
+<<< Subject: should be delivered
+??? Message-Id:
+<<< Message-Id: <E10HmbA-000000005vi-0000@the.local.host.name>
+??? From:
+<<< From: CALLER_NAME <CALLER@the.local.host.name>
+??? 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@the.local.host.name>
+          a@otherdomain.net
+
+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@the.local.host.name>
+>>> 250 saw your MAIL cmd
+??? RCPT TO:<g
+<<< RCPT TO:<g@clientdom.net>
+>>> 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@the.local.host.name>)
+??? \x09id
+<<<    id 10HmbB-000000005vi-0000
+??? \x09for
+<<<    for g@clientdom.net;
+??? \x09
+<<<    Tue, 2 Mar 1999 09:44:33 +0000
+??? Subject: should be delivered
+<<< Subject: should be delivered
+??? Message-Id:
+<<< Message-Id: <E10HmbB-000000005vi-0000@the.local.host.name>
+??? From:
+<<< From: CALLER_NAME <CALLER@the.local.host.name>
+??? 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 (file)
index 0000000..1ea6536
--- /dev/null
@@ -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@myhost.test.ex>
+>>> 250 saw your MAIL cmd
+??? RCPT
+<<< RCPT TO:<a@clientdom.net>
+>>> 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@myhost.test.ex>)
+??? \x09id
+<<<    id 10HmaX-000000005vi-0000
+??? \x09for
+<<<    for a@clientdom.net;
+??? \x09
+<<<    Tue, 2 Mar 1999 09:44:33 +0000
+??? Subject:
+<<< Subject: foo
+??? Message-Id:
+<<< Message-Id: <E10HmaX-000000005vi-0000@myhost.test.ex>
+??? From:
+<<< From: CALLER_NAME <CALLER@myhost.test.ex>
+??? 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@myhost.test.ex>
+>>> 250 saw your MAIL cmd
+??? RCPT
+<<< RCPT TO:<b@clientdom.net>
+>>> 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@myhost.test.ex>)
+??? \x09id
+<<<    id 10HmaY-000000005vi-0000
+??? \x09for
+<<<    for b@clientdom.net;
+??? \x09
+<<<    Tue, 2 Mar 1999 09:44:33 +0000
+??? Subject:
+<<< Subject: foo
+??? Message-Id:
+<<< Message-Id: <E10HmaY-000000005vi-0000@myhost.test.ex>
+??? From:
+<<< From: CALLER_NAME <CALLER@myhost.test.ex>
+??? 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