summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/doc-docbook/spec.xfpt18
-rw-r--r--doc/doc-txt/NewStuff4
-rw-r--r--src/src/acl.c80
-rw-r--r--src/src/globals.c1
-rw-r--r--src/src/globals.h3
-rw-r--r--src/src/receive.c41
-rw-r--r--src/src/smtp_in.c19
-rw-r--r--src/src/verify.c51
-rw-r--r--test/confs/540011
l---------test/confs/54051
-rw-r--r--test/log/540523
-rw-r--r--test/rejectlog/54052
-rw-r--r--test/scripts/5400-cutthrough/5405228
-rw-r--r--test/stdout/5405280
14 files changed, 686 insertions, 76 deletions
diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt
index 622e5272d..4ebeaa0c6 100644
--- a/doc/doc-docbook/spec.xfpt
+++ b/doc/doc-docbook/spec.xfpt
@@ -13003,8 +13003,7 @@ or external command, as described above. It is also used during a
.vitem &$verify_mode$&
.vindex "&$verify_mode$&"
-While a router or transport is being run in verify mode
-or for cutthrough delivery,
+While a router or transport is being run in verify mode or for cutthrough delivery,
contains "S" for sender-verification or "R" for recipient-verification.
Otherwise, empty.
@@ -28802,7 +28801,7 @@ Notice that we put back the lower cased version afterwards, assuming that
is what is wanted for subsequent tests.
-.vitem &*control&~=&~cutthrough_delivery*&
+.vitem &*control&~=&~cutthrough_delivery/*&<&'options'&>
.cindex "&ACL;" "cutthrough routing"
.cindex "cutthrough" "requesting"
This option requests delivery be attempted while the item is being received.
@@ -28842,11 +28841,20 @@ It is not supported for messages received with the SMTP PRDR option in use.
Should the ultimate destination system positively accept or reject the mail,
a corresponding indication is given to the source system and nothing is queued.
-If there is a temporary error the item is queued for later delivery in the
-usual fashion. If the item is successfully delivered in cutthrough mode
+If the item is successfully delivered in cutthrough mode
the delivery log lines are tagged with ">>" rather than "=>" and appear
before the acceptance "<=" line.
+.new
+If there is a temporary error the item is queued for later delivery in the
+usual fashion.
+This behaviour can be adjusted by appending the option &*defer=*&<&'value'&>
+to the control; the default value is &"spool"& and the alternate value
+&"pass"& copies an SMTP defer response from the target back to the initiator
+and does not queue the message.
+Note that this is independent of any receipient verify conditions in the ACL.
+.wen
+
Delivery in this mode avoids the generation of a bounce mail to a
(possibly faked)
sender when the destination system is doing content-scan based rejection.
diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff
index 20af1a28f..33b726536 100644
--- a/doc/doc-txt/NewStuff
+++ b/doc/doc-txt/NewStuff
@@ -43,6 +43,10 @@ Version 4.88
11. Integer values for options can take a "G" multiplier.
+12. defer=pass option for the ACL control cutthrough_delivery, to reflect 4xx
+ returns from the target back to the initiator, rather than spooling the
+ message.
+
Version 4.87
------------
diff --git a/src/src/acl.c b/src/src/acl.c
index 492e9bf17..eff698b34 100644
--- a/src/src/acl.c
+++ b/src/src/acl.c
@@ -744,7 +744,7 @@ static control_def controls_list[] = {
{ US"fakereject", CONTROL_FAKEREJECT, TRUE },
{ US"submission", CONTROL_SUBMISSION, TRUE },
{ US"suppress_local_fixups", CONTROL_SUPPRESS_LOCAL_FIXUPS, FALSE },
- { US"cutthrough_delivery", CONTROL_CUTTHROUGH_DELIVERY, FALSE },
+ { US"cutthrough_delivery", CONTROL_CUTTHROUGH_DELIVERY, TRUE },
#ifdef SUPPORT_I18N
{ US"utf8_downconvert", CONTROL_UTF8_DOWNCONVERT, TRUE }
#endif
@@ -3398,7 +3398,23 @@ for (; cb != NULL; cb = cb->next)
*log_msgptr = US"fakereject";
else
{
- if (rcpt_count == 1) cutthrough.delivery = TRUE;
+ if (rcpt_count == 1)
+ {
+ cutthrough.delivery = TRUE;
+ while (*p == '/')
+ {
+ const uschar * pp = p+1;
+ if (Ustrncmp(pp, "defer=", 6) == 0)
+ {
+ pp += 6;
+ if (Ustrncmp(pp, "pass", 4) == 0) cutthrough.defer_pass = TRUE;
+ /* else if (Ustrncmp(pp, "spool") == 0) ; default */
+ }
+ else
+ while (*pp && *pp != '/') pp++;
+ p = pp;
+ }
+ }
break;
}
*log_msgptr = string_sprintf("\"control=%s\" on %s item",
@@ -4502,32 +4518,50 @@ If temp-reject, close the conn (and keep the spooled copy).
If conn-failure, no action (and keep the spooled copy).
*/
switch (where)
-{
-case ACL_WHERE_RCPT:
+ {
+ case ACL_WHERE_RCPT:
#ifndef DISABLE_PRDR
-case ACL_WHERE_PRDR:
+ case ACL_WHERE_PRDR:
#endif
- if (host_checking_callout) /* -bhc mode */
- cancel_cutthrough_connection("host-checking mode");
- else if (rc == OK && cutthrough.delivery && rcpt_count > cutthrough.nrcpt)
- rc = open_cutthrough_connection(addr);
- break;
+ if (host_checking_callout) /* -bhc mode */
+ cancel_cutthrough_connection("host-checking mode");
+
+ else if ( rc == OK
+ && cutthrough.delivery
+ && rcpt_count > cutthrough.nrcpt
+ && (rc = open_cutthrough_connection(addr)) == DEFER
+ )
+ if (cutthrough.defer_pass)
+ {
+ uschar * s = addr->message;
+ /* Horrid kludge to recover target's SMTP message */
+ while (*s) s++;
+ do --s; while (!isdigit(*s));
+ if (*--s && isdigit(*s) && *--s && isdigit(*s)) *user_msgptr = s;
+ acl_temp_details = TRUE;
+ }
+ else
+ {
+ HDEBUG(D_acl) debug_printf("cutthrough defer; will spool\n");
+ rc = OK;
+ }
+ break;
-case ACL_WHERE_PREDATA:
- if (rc == OK)
- cutthrough_predata();
- else
- cancel_cutthrough_connection("predata acl not ok");
- break;
+ case ACL_WHERE_PREDATA:
+ if (rc == OK)
+ cutthrough_predata();
+ else
+ cancel_cutthrough_connection("predata acl not ok");
+ break;
-case ACL_WHERE_QUIT:
-case ACL_WHERE_NOTQUIT:
- cancel_cutthrough_connection("quit or notquit");
- break;
+ case ACL_WHERE_QUIT:
+ case ACL_WHERE_NOTQUIT:
+ cancel_cutthrough_connection("quit or notquit");
+ break;
-default:
- break;
-}
+ default:
+ break;
+ }
deliver_domain = deliver_localpart = deliver_address_data =
sender_address_data = NULL;
diff --git a/src/src/globals.c b/src/src/globals.c
index 9e6f9d347..8d2010273 100644
--- a/src/src/globals.c
+++ b/src/src/globals.c
@@ -532,6 +532,7 @@ uschar *continue_transport = NULL;
uschar *csa_status = NULL;
cut_t cutthrough = {
FALSE, /* delivery: when to attempt */
+ FALSE, /* on defer: spool locally */
-1, /* fd: open connection */
0, /* nrcpt: number of addresses */
};
diff --git a/src/src/globals.h b/src/src/globals.h
index b2bfca64c..0faf713bf 100644
--- a/src/src/globals.h
+++ b/src/src/globals.h
@@ -301,7 +301,8 @@ extern uschar *continue_transport; /* Transport for continued delivery */
extern uschar *csa_status; /* Client SMTP Authorization result */
typedef struct {
- BOOL delivery; /* When to attempt */
+ unsigned delivery:1; /* When to attempt */
+ unsigned defer_pass:1; /* Pass 4xx to caller rather than spoolling */
int fd; /* Open connection */
int nrcpt; /* Count of addresses */
uschar * interface; /* (address of) */
diff --git a/src/src/receive.c b/src/src/receive.c
index 4d30419f0..51ce2844e 100644
--- a/src/src/receive.c
+++ b/src/src/receive.c
@@ -4052,7 +4052,8 @@ for this message. */
Send dot onward. If accepted, wipe the spooled files, log as delivered and accept
the sender's dot (below).
If rejected: copy response to sender, wipe the spooled files, log approriately.
- If temp-reject: accept to sender, keep the spooled files.
+ If temp-reject: normally accept to sender, keep the spooled file - unless defer=pass
+ in which case pass temp-reject back to initiator and dump the files.
Having the normal spool files lets us do data-filtering, and store/forward on temp-reject.
@@ -4068,13 +4069,17 @@ if(cutthrough.fd >= 0)
cutthrough_done = ACCEPTED;
break; /* message_id needed for SMTP accept below */
+ case '4': /* Temp-reject. Keep spoolfiles and accept, unless defer-pass mode.
+ ... for which, pass back the exact error */
+ if (cutthrough.defer_pass) smtp_reply = string_copy_malloc(msg);
+ /*FALLTRHOUGH*/
+
default: /* Unknown response, or error. Treat as temp-reject. */
- case '4': /* Temp-reject. Keep spoolfiles and accept. */
cutthrough_done = TMP_REJ; /* Avoid the usual immediate delivery attempt */
break; /* message_id needed for SMTP accept below */
case '5': /* Perm-reject. Do the same to the source. Dump any spoolfiles */
- smtp_reply= msg; /* Pass on the exact error */
+ smtp_reply = string_copy_malloc(msg); /* Pass on the exact error */
cutthrough_done = PERM_REJ;
break;
}
@@ -4188,27 +4193,37 @@ if (smtp_input)
/* smtp_reply is set non-empty */
else if (smtp_reply[0] != 0)
- {
if (fake_response != OK && (smtp_reply[0] == '2'))
smtp_respond((fake_response == DEFER)? US"450" : US"550", 3, TRUE,
fake_response_text);
else
smtp_printf("%.1024s\r\n", smtp_reply);
- }
switch (cutthrough_done)
{
- case ACCEPTED: log_write(0, LOG_MAIN, "Completed");/* Delivery was done */
+ case ACCEPTED:
+ log_write(0, LOG_MAIN, "Completed");/* Delivery was done */
case PERM_REJ:
- { /* Delete spool files */
- Uunlink(spool_fname(US"input", message_subdir, message_id, US"-D"));
- Uunlink(spool_fname(US"input", message_subdir, message_id, US"-H"));
- Uunlink(spool_fname(US"msglog", message_subdir, message_id, US""));
- }
- case TMP_REJ: message_id[0] = 0; /* Prevent a delivery from starting */
- default:break;
+ /* Delete spool files */
+ Uunlink(spool_fname(US"input", message_subdir, message_id, US"-D"));
+ Uunlink(spool_fname(US"input", message_subdir, message_id, US"-H"));
+ Uunlink(spool_fname(US"msglog", message_subdir, message_id, US""));
+ message_id[0] = 0; /* Prevent a delivery from starting */
+ break;
+
+ case TMP_REJ:
+ if (cutthrough.defer_pass)
+ {
+ Uunlink(spool_fname(US"input", message_subdir, message_id, US"-D"));
+ Uunlink(spool_fname(US"input", message_subdir, message_id, US"-H"));
+ Uunlink(spool_fname(US"msglog", message_subdir, message_id, US""));
+ }
+ message_id[0] = 0; /* Prevent a delivery from starting */
+ default:
+ break;
}
cutthrough.delivery = FALSE;
+ cutthrough.defer_pass = FALSE;
}
/* For batched SMTP, generate an error message on failure, and do
diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c
index 597b88e38..387affaf3 100644
--- a/src/src/smtp_in.c
+++ b/src/src/smtp_in.c
@@ -2961,8 +2961,9 @@ if (lognl != NULL) *lognl = 0;
always a 5xx one - see comments at the start of this function. If the original
rc was FAIL_DROP we drop the connection and yield 2. */
-if (rc == FAIL) smtp_respond(smtp_code, codelen, TRUE, (user_msg == NULL)?
- US"Administrative prohibition" : user_msg);
+if (rc == FAIL)
+ smtp_respond(smtp_code, codelen, TRUE,
+ user_msg ? user_msg : US"Administrative prohibition");
/* Send temporary failure response to the command. Don't give any details,
unless acl_temp_details is set. This is TRUE for a callout defer, a "defer"
@@ -2973,21 +2974,19 @@ interactions between temp_details and return_error_details. One day it should
be re-implemented in a tidier fashion. */
else
- {
- if (acl_temp_details && user_msg != NULL)
+ if (acl_temp_details && user_msg)
{
- if (smtp_return_error_details &&
- sender_verified_failed != NULL &&
- sender_verified_failed->message != NULL)
- {
+ if ( smtp_return_error_details
+ && sender_verified_failed
+ && sender_verified_failed->message
+ )
smtp_respond(smtp_code, codelen, FALSE, sender_verified_failed->message);
- }
+
smtp_respond(smtp_code, codelen, TRUE, user_msg);
}
else
smtp_respond(smtp_code, codelen, TRUE,
US"Temporary local problem - please try later");
- }
/* Log the incident to the logs that are specified by log_reject_target
(default main, reject). This can be empty to suppress logging of rejections. If
diff --git a/src/src/verify.c b/src/src/verify.c
index d890f5fc6..865a01d07 100644
--- a/src/src/verify.c
+++ b/src/src/verify.c
@@ -226,7 +226,7 @@ else if ((dbm_file = dbfn_open(US"callout", O_RDWR, &dbblock, FALSE)) == NULL)
/* If a cache database is available see if we can avoid the need to do an
actual callout by making use of previously-obtained data. */
-if (dbm_file != NULL)
+if (dbm_file)
{
dbdata_callout_cache_address *cache_address_record;
dbdata_callout_cache *cache_record = get_callout_cache_record(dbm_file,
@@ -237,7 +237,7 @@ if (dbm_file != NULL)
/* If an unexpired cache record was found for this domain, see if the callout
process can be short-circuited. */
- if (cache_record != NULL)
+ if (cache_record)
{
/* In most cases, if an early command (up to and including MAIL FROM:<>)
was rejected, there is no point carrying on. The callout fails. However, if
@@ -297,7 +297,7 @@ if (dbm_file != NULL)
but has not been done before, we are going to have to do a callout, so skip
remaining cache processing. */
- if (pm_mailfrom != NULL)
+ if (pm_mailfrom)
{
if (cache_record->postmaster_result == ccache_reject)
{
@@ -343,7 +343,7 @@ if (dbm_file != NULL)
callout_cache_positive_expire,
callout_cache_negative_expire);
- if (cache_address_record != NULL)
+ if (cache_address_record)
{
if (cache_address_record->result == ccache_accept)
{
@@ -404,7 +404,7 @@ else
and cause the client to time out. So in this case we forgo the PIPELINING
optimization. */
- if (smtp_out != NULL && !disable_callout_flush) mac_smtp_fflush();
+ if (smtp_out && !disable_callout_flush) mac_smtp_fflush();
/* cutthrough-multi: if a nonfirst rcpt has the same routing as the first,
and we are holding a cutthrough conn open, we can just append the rcpt to
@@ -516,7 +516,7 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount.
/* Now make connections to the hosts and do real callouts. The list of hosts
is passed in as an argument. */
- for (host = host_list; host != NULL && !done; host = host->next)
+ for (host = host_list; host && !done; host = host->next)
{
smtp_inblock inblock;
smtp_outblock outblock;
@@ -544,7 +544,7 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount.
/* Skip this host if we don't have an IP address for it. */
- if (host->address == NULL)
+ if (!host->address)
{
DEBUG(D_verify) debug_printf("no IP address for host name %s: skipping\n",
host->name);
@@ -561,7 +561,7 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount.
/* Set IPv4 or IPv6 */
- host_af = (Ustrchr(host->address, ':') == NULL)? AF_INET:AF_INET6;
+ host_af = Ustrchr(host->address, ':') == NULL ? AF_INET : AF_INET6;
/* Expand and interpret the interface and port strings. The latter will not
be used if there is a host-specific port (e.g. from a manualroute router).
@@ -659,10 +659,10 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount.
/* Expand the helo_data string to find the host name to use. */
- if (tf->helo_data != NULL)
+ if (tf->helo_data)
{
- uschar *s = expand_string(tf->helo_data);
- if (s == NULL)
+ uschar * s = expand_string(tf->helo_data);
+ if (!s)
log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: failed to expand transport's "
"helo_data value for callout: %s", addr->address,
expand_string_message);
@@ -1299,8 +1299,9 @@ can do it there for the non-rcpt-verify case. For this we keep an addresscount.
{
/* Ensure no cutthrough on multiple address verifies */
if (options & vopt_callout_recipsender)
- cancel_cutthrough_connection("multiple verify calls");
- if (send_quit) (void)smtp_write_command(&outblock, FALSE, "QUIT\r\n");
+ cancel_cutthrough_connection("not usable for cutthrough");
+ if (send_quit)
+ (void) smtp_write_command(&outblock, FALSE, "QUIT\r\n");
#ifdef SUPPORT_TLS
tls_close(FALSE, TRUE);
@@ -1353,9 +1354,9 @@ if (done)
if ( !(options & vopt_callout_no_cache)
&& new_address_record.result != ccache_unknown)
{
- if (dbm_file == NULL)
+ if (!dbm_file)
dbm_file = dbfn_open(US"callout", O_RDWR|O_CREAT, &dbblock, FALSE);
- if (dbm_file == NULL)
+ if (!dbm_file)
{
HDEBUG(D_verify) debug_printf("no callout cache available\n");
}
@@ -1376,22 +1377,24 @@ it alone if supplying details. Otherwise, give a generic response. */
else /* !done */
{
- uschar *dullmsg = string_sprintf("Could not complete %s verify callout",
+ uschar * dullmsg = string_sprintf("Could not complete %s verify callout",
options & vopt_is_recipient ? "recipient" : "sender");
yield = DEFER;
- if (host_list->next != NULL || addr->message == NULL) addr->message = dullmsg;
+ if (host_list->next || !addr->message)
+ addr->message = dullmsg;
- addr->user_message = (!smtp_return_error_details)? dullmsg :
- string_sprintf("%s for <%s>.\n"
+ addr->user_message = smtp_return_error_details
+ ? string_sprintf("%s for <%s>.\n"
"The mail server(s) for the domain may be temporarily unreachable, or\n"
"they may be permanently unreachable from this server. In the latter case,\n%s",
dullmsg, addr->address,
options & vopt_is_recipient
- ? "the address will never be accepted."
+ ? "the address will never be accepted."
: "you need to change the address or create an MX record for its domain\n"
"if it is supposed to be generally accessible from the Internet.\n"
- "Talk to your mail administrator for details.");
+ "Talk to your mail administrator for details.")
+ : dullmsg;
/* Force a specific error code */
@@ -1401,7 +1404,7 @@ else /* !done */
/* Come here from within the cache-reading code on fast-track exit. */
END_CALLOUT:
-if (dbm_file != NULL) dbfn_close(dbm_file);
+if (dbm_file) dbfn_close(dbm_file);
return yield;
}
@@ -1423,10 +1426,12 @@ get rewritten. */
addr2 = *addr;
HDEBUG(D_acl) debug_printf("----------- %s cutthrough setup ------------\n",
rcpt_count > 1 ? "more" : "start");
-rc= verify_address(&addr2, NULL,
+rc = verify_address(&addr2, NULL,
vopt_is_recipient | vopt_callout_recipsender | vopt_callout_no_cache,
CUTTHROUGH_CMD_TIMEOUT, -1, -1,
NULL, NULL, NULL);
+addr->message = addr2.message;
+addr->user_message = addr2.user_message;
HDEBUG(D_acl) debug_printf("----------- end cutthrough setup ------------\n");
return rc;
}
diff --git a/test/confs/5400 b/test/confs/5400
index f87f985fd..9be13fea6 100644
--- a/test/confs/5400
+++ b/test/confs/5400
@@ -1,10 +1,17 @@
# Exim test configuration 5400
+# any options on the cutthrough_delivery contol
+CONTROL=
+
+# optional verify-callout
+OPT=
+
.include DIR/aux-var/std_conf_prefix
primary_hostname = myhost.test.ex
log_selector = +received_recipients
+queue_only
# ----- Main settings -----
@@ -18,7 +25,9 @@ acl_smtp_rcpt = ar
begin acl
ar:
- accept control = cutthrough_delivery
+ accept
+ control = cutthrough_deliveryCONTROL
+ OPT
logwrite = rcpt for $local_part@$domain
# ----- Routers -----
diff --git a/test/confs/5405 b/test/confs/5405
new file mode 120000
index 000000000..8f6811b7e
--- /dev/null
+++ b/test/confs/5405
@@ -0,0 +1 @@
+5400 \ No newline at end of file
diff --git a/test/log/5405 b/test/log/5405
new file mode 100644
index 000000000..ae3ad767d
--- /dev/null
+++ b/test/log/5405
@@ -0,0 +1,23 @@
+1999-03-02 09:44:33 rcpt for usera@domain.com
+1999-03-02 09:44:33 10HmaX-0005vi-00 >> usera@domain.com R=all T=smtp H=127.0.0.1 [127.0.0.1] C="250 OK"
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss for usera@domain.com
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 rcpt for userb@domain.com
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss for userb@domain.com
+1999-03-02 09:44:33 rcpt for userc@domain.com
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss for userc@domain.com
+1999-03-02 09:44:33 rcpt for userd@domain.com
+1999-03-02 09:44:33 U=CALLER F=<CALLER@myhost.test.ex> temporarily rejected RCPT <userd@domain.com>
+1999-03-02 09:44:33 rcpt for usere@domain.com
+1999-03-02 09:44:33 10HmbA-0005vi-00 U=CALLER usere@domain.com F=<CALLER@myhost.test.ex> tmp-reject from cutthrough after DATA: R=all T=smtp H=127.0.0.1 [127.0.0.1] C="459 content reject"
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss for usere@domain.com
+1999-03-02 09:44:33 rcpt for userf@domain.com
+1999-03-02 09:44:33 10HmbB-0005vi-00 U=CALLER userf@domain.com F=<CALLER@myhost.test.ex> tmp-reject from cutthrough after DATA: R=all T=smtp H=127.0.0.1 [127.0.0.1] C="459 content reject"
+1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss for userf@domain.com
+1999-03-02 09:44:33 rcpt for userg@domain.com
+1999-03-02 09:44:33 10HmbC-0005vi-00 U=CALLER userg@domain.com F=<CALLER@myhost.test.ex> tmp-reject from cutthrough after DATA: R=all T=smtp H=127.0.0.1 [127.0.0.1] C="459 content reject"
+1999-03-02 09:44:33 rcpt for userh@domain.com
+1999-03-02 09:44:33 10HmbD-0005vi-00 >> userh@domain.com R=all T=smtp H=127.0.0.1 [127.0.0.1] C="250 OK"
+1999-03-02 09:44:33 10HmbD-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss for userh@domain.com
+1999-03-02 09:44:33 10HmbD-0005vi-00 Completed
+1999-03-02 09:44:33 U=CALLER F=<CALLER@myhost.test.ex> temporarily rejected RCPT <useri@domain.com>: response to "RCPT TO:<useri@domain.com>" from 127.0.0.1 [127.0.0.1] was: 450 not right now
diff --git a/test/rejectlog/5405 b/test/rejectlog/5405
new file mode 100644
index 000000000..7df2e1018
--- /dev/null
+++ b/test/rejectlog/5405
@@ -0,0 +1,2 @@
+1999-03-02 09:44:33 U=CALLER F=<CALLER@myhost.test.ex> temporarily rejected RCPT <userd@domain.com>
+1999-03-02 09:44:33 U=CALLER F=<CALLER@myhost.test.ex> temporarily rejected RCPT <useri@domain.com>: response to "RCPT TO:<useri@domain.com>" from 127.0.0.1 [127.0.0.1] was: 450 not right now
diff --git a/test/scripts/5400-cutthrough/5405 b/test/scripts/5400-cutthrough/5405
new file mode 100644
index 000000000..2e7a33388
--- /dev/null
+++ b/test/scripts/5400-cutthrough/5405
@@ -0,0 +1,228 @@
+# cutthrough_delivery tmp-rejects
+need_ipv4
+munge loopback
+#
+# basic operation. Should deliver in cutthrough mode.
+server PORT_S
+220 ESMTP
+EHLO
+250 OK
+MAIL FROM:
+250 Sender OK
+RCPT TO:
+250 Recipient OK
+DATA
+354 Send data
+.
+250 OK
+QUIT
+250 OK
+****
+exim -bs
+EHLO myhost.test.ex
+MAIL FROM:<CALLER@myhost.test.ex>
+RCPT TO:<usera@domain.com>
+DATA
+
+.
+QUIT
+****
+######################################################
+# RCPT-time
+#
+# temp-reject (at RCPT), default. Should spool msg.
+server PORT_S
+220 ESMTP
+EHLO
+250 OK
+MAIL FROM:
+250 Sender OK
+RCPT TO:
+450 not right now
+QUIT
+250 OK
+****
+exim -bs
+EHLO myhost.test.ex
+MAIL FROM:<CALLER@myhost.test.ex>
+RCPT TO:<userb@domain.com>
+DATA
+
+.
+QUIT
+****
+#
+# temp-reject (at RCPT), option "spool". Should spool msg (again).
+server PORT_S
+220 ESMTP
+EHLO
+250 OK
+MAIL FROM:
+250 Sender OK
+RCPT TO:
+450 not right now
+QUIT
+250 OK
+****
+exim -DCONTROL=/defer=spool -bs
+EHLO myhost.test.ex
+MAIL FROM:<CALLER@myhost.test.ex>
+RCPT TO:<userc@domain.com>
+DATA
+
+.
+QUIT
+****
+#
+# temp-reject (at RCPT), option "pass". Should tmp-rej source, at RCPT.
+server PORT_S
+220 ESMTP
+EHLO
+250 OK
+MAIL FROM:
+250 Sender OK
+RCPT TO:
+450 not right now
+QUIT
+250 OK
+****
+exim -DCONTROL=/defer=pass -bs
+EHLO myhost.test.ex
+MAIL FROM:<CALLER@myhost.test.ex>
+RCPT TO:<userd@domain.com>
+QUIT
+****
+#########################################################
+# DATA-time
+#
+#
+# temp-reject (at DATA-dot), default. Should spool msg.
+server PORT_S
+220 ESMTP
+EHLO
+250 OK
+MAIL FROM:
+250 Sender OK
+RCPT TO:
+250 good rcpt
+DATA
+354 Send data
+.
+459 content reject
+QUIT
+250 OK
+****
+exim -bs
+EHLO myhost.test.ex
+MAIL FROM:<CALLER@myhost.test.ex>
+RCPT TO:<usere@domain.com>
+DATA
+
+.
+QUIT
+****
+#
+# temp-reject (at DATA-dot), option "spool". Should spool msg (again).
+server PORT_S
+220 ESMTP
+EHLO
+250 OK
+MAIL FROM:
+250 Sender OK
+RCPT TO:
+250 good rcpt
+DATA
+354 Send data
+.
+459 content reject
+QUIT
+250 OK
+****
+exim -DCONTROL=/defer=spool -bs
+EHLO myhost.test.ex
+MAIL FROM:<CALLER@myhost.test.ex>
+RCPT TO:<userf@domain.com>
+DATA
+
+.
+QUIT
+****
+#
+# temp-reject (at DATA-dot), option "pass". Should tmp-rej source, at DATA.
+server PORT_S
+220 ESMTP
+EHLO
+250 OK
+MAIL FROM:
+250 Sender OK
+RCPT TO:
+250 good rcpt
+DATA
+354 Send data
+.
+459 content reject
+QUIT
+250 OK
+****
+exim -DCONTROL=/defer=pass -bs
+EHLO myhost.test.ex
+MAIL FROM:<CALLER@myhost.test.ex>
+RCPT TO:<userg@domain.com>
+DATA
+
+.
+QUIT
+****
+#
+#########################################################
+#, Now using a conn opened by a recipient verify
+#
+# basic operation. Should deliver in cutthrough mode.
+server PORT_S
+220 ESMTP
+EHLO
+250 OK
+MAIL FROM:
+250 Sender OK
+RCPT TO:
+250 Recipient OK
+DATA
+354 Send data
+.
+250 OK
+QUIT
+250 OK
+****
+exim -DOPT=verify=recipient/callout=use_sender -bs
+EHLO myhost.test.ex
+MAIL FROM:<CALLER@myhost.test.ex>
+RCPT TO:<userh@domain.com>
+DATA
+
+.
+QUIT
+****
+#
+# temp-reject (at RCPT), default. Should tmp-rej, due to the r-verify
+server PORT_S
+220 ESMTP
+EHLO
+250 OK
+MAIL FROM:
+250 Sender OK
+RCPT TO:
+450 not right now
+QUIT
+250 OK
+****
+exim -DOPT=verify=recipient/callout=use_sender -bs
+EHLO myhost.test.ex
+MAIL FROM:<CALLER@myhost.test.ex>
+RCPT TO:<useri@domain.com>
+DATA
+
+.
+QUIT
+****
+#
+no_msglog_check
diff --git a/test/stdout/5405 b/test/stdout/5405
new file mode 100644
index 000000000..14bfdabdd
--- /dev/null
+++ b/test/stdout/5405
@@ -0,0 +1,280 @@
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+250-myhost.test.ex Hello CALLER at myhost.test.ex
+250-SIZE 52428800
+250-8BITMIME
+250-PIPELINING
+250 HELP
+250 OK
+250 Accepted
+354 Enter message, ending with "." on a line by itself
+250 OK id=10HmaX-0005vi-00
+221 myhost.test.ex closing connection
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+250-myhost.test.ex Hello CALLER at myhost.test.ex
+250-SIZE 52428800
+250-8BITMIME
+250-PIPELINING
+250 HELP
+250 OK
+250 Accepted
+354 Enter message, ending with "." on a line by itself
+250 OK id=10HmaY-0005vi-00
+221 myhost.test.ex closing connection
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+250-myhost.test.ex Hello CALLER at myhost.test.ex
+250-SIZE 52428800
+250-8BITMIME
+250-PIPELINING
+250 HELP
+250 OK
+250 Accepted
+354 Enter message, ending with "." on a line by itself
+250 OK id=10HmaZ-0005vi-00
+221 myhost.test.ex closing connection
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+250-myhost.test.ex Hello CALLER at myhost.test.ex
+250-SIZE 52428800
+250-8BITMIME
+250-PIPELINING
+250 HELP
+250 OK
+450 not right now
+221 myhost.test.ex closing connection
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+250-myhost.test.ex Hello CALLER at myhost.test.ex
+250-SIZE 52428800
+250-8BITMIME
+250-PIPELINING
+250 HELP
+250 OK
+250 Accepted
+354 Enter message, ending with "." on a line by itself
+250 OK id=10HmbA-0005vi-00
+221 myhost.test.ex closing connection
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+250-myhost.test.ex Hello CALLER at myhost.test.ex
+250-SIZE 52428800
+250-8BITMIME
+250-PIPELINING
+250 HELP
+250 OK
+250 Accepted
+354 Enter message, ending with "." on a line by itself
+250 OK id=10HmbB-0005vi-00
+221 myhost.test.ex closing connection
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+250-myhost.test.ex Hello CALLER at myhost.test.ex
+250-SIZE 52428800
+250-8BITMIME
+250-PIPELINING
+250 HELP
+250 OK
+250 Accepted
+354 Enter message, ending with "." on a line by itself
+459 content reject
+221 myhost.test.ex closing connection
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+250-myhost.test.ex Hello CALLER at myhost.test.ex
+250-SIZE 52428800
+250-8BITMIME
+250-PIPELINING
+250 HELP
+250 OK
+250 Accepted
+354 Enter message, ending with "." on a line by itself
+250 OK id=10HmbD-0005vi-00
+221 myhost.test.ex closing connection
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+250-myhost.test.ex Hello CALLER at myhost.test.ex
+250-SIZE 52428800
+250-8BITMIME
+250-PIPELINING
+250 HELP
+250 OK
+451 Could not complete recipient verify callout
+503-All RCPT commands were rejected with this error:
+503-Could not complete recipient verify callout
+503 Valid RCPT command must precede DATA
+500 unrecognized command
+500 unrecognized command
+221 myhost.test.ex closing connection
+
+******** SERVER ********
+Listening on port 1224 ...
+Connection request from [ip4.ip4.ip4.ip4]
+220 ESMTP
+EHLO myhost.test.ex
+250 OK
+MAIL FROM:<CALLER@myhost.test.ex>
+250 Sender OK
+RCPT TO:<usera@domain.com>
+250 Recipient OK
+DATA
+354 Send data
+Received: from CALLER (helo=myhost.test.ex)
+ by myhost.test.ex with local-esmtp (Exim x.yz)
+ (envelope-from <CALLER@myhost.test.ex>)
+ id 10HmaX-0005vi-00
+ for usera@domain.com; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaX-0005vi-00@myhost.test.ex>
+From: CALLER_NAME <CALLER@myhost.test.ex>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+X-hdr-rtr-new: +++
+
+.
+250 OK
+QUIT
+250 OK
+End of script
+Listening on port 1224 ...
+Connection request from [ip4.ip4.ip4.ip4]
+220 ESMTP
+EHLO myhost.test.ex
+250 OK
+MAIL FROM:<CALLER@myhost.test.ex>
+250 Sender OK
+RCPT TO:<userb@domain.com>
+450 not right now
+QUIT
+250 OK
+End of script
+Listening on port 1224 ...
+Connection request from [ip4.ip4.ip4.ip4]
+220 ESMTP
+EHLO myhost.test.ex
+250 OK
+MAIL FROM:<CALLER@myhost.test.ex>
+250 Sender OK
+RCPT TO:<userc@domain.com>
+450 not right now
+QUIT
+250 OK
+End of script
+Listening on port 1224 ...
+Connection request from [ip4.ip4.ip4.ip4]
+220 ESMTP
+EHLO myhost.test.ex
+250 OK
+MAIL FROM:<CALLER@myhost.test.ex>
+250 Sender OK
+RCPT TO:<userd@domain.com>
+450 not right now
+QUIT
+250 OK
+End of script
+Listening on port 1224 ...
+Connection request from [ip4.ip4.ip4.ip4]
+220 ESMTP
+EHLO myhost.test.ex
+250 OK
+MAIL FROM:<CALLER@myhost.test.ex>
+250 Sender OK
+RCPT TO:<usere@domain.com>
+250 good rcpt
+DATA
+354 Send data
+Received: from CALLER (helo=myhost.test.ex)
+ by myhost.test.ex with local-esmtp (Exim x.yz)
+ (envelope-from <CALLER@myhost.test.ex>)
+ id 10HmbA-0005vi-00
+ for usere@domain.com; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmbA-0005vi-00@myhost.test.ex>
+From: CALLER_NAME <CALLER@myhost.test.ex>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+X-hdr-rtr-new: +++
+
+.
+459 content reject
+QUIT
+250 OK
+End of script
+Listening on port 1224 ...
+Connection request from [ip4.ip4.ip4.ip4]
+220 ESMTP
+EHLO myhost.test.ex
+250 OK
+MAIL FROM:<CALLER@myhost.test.ex>
+250 Sender OK
+RCPT TO:<userf@domain.com>
+250 good rcpt
+DATA
+354 Send data
+Received: from CALLER (helo=myhost.test.ex)
+ by myhost.test.ex with local-esmtp (Exim x.yz)
+ (envelope-from <CALLER@myhost.test.ex>)
+ id 10HmbB-0005vi-00
+ for userf@domain.com; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmbB-0005vi-00@myhost.test.ex>
+From: CALLER_NAME <CALLER@myhost.test.ex>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+X-hdr-rtr-new: +++
+
+.
+459 content reject
+QUIT
+250 OK
+End of script
+Listening on port 1224 ...
+Connection request from [ip4.ip4.ip4.ip4]
+220 ESMTP
+EHLO myhost.test.ex
+250 OK
+MAIL FROM:<CALLER@myhost.test.ex>
+250 Sender OK
+RCPT TO:<userg@domain.com>
+250 good rcpt
+DATA
+354 Send data
+Received: from CALLER (helo=myhost.test.ex)
+ by myhost.test.ex with local-esmtp (Exim x.yz)
+ (envelope-from <CALLER@myhost.test.ex>)
+ id 10HmbC-0005vi-00
+ for userg@domain.com; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmbC-0005vi-00@myhost.test.ex>
+From: CALLER_NAME <CALLER@myhost.test.ex>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+X-hdr-rtr-new: +++
+
+.
+459 content reject
+QUIT
+250 OK
+End of script
+Listening on port 1224 ...
+Connection request from [ip4.ip4.ip4.ip4]
+220 ESMTP
+EHLO myhost.test.ex
+250 OK
+MAIL FROM:<CALLER@myhost.test.ex>
+250 Sender OK
+RCPT TO:<userh@domain.com>
+250 Recipient OK
+DATA
+354 Send data
+Received: from CALLER (helo=myhost.test.ex)
+ by myhost.test.ex with local-esmtp (Exim x.yz)
+ (envelope-from <CALLER@myhost.test.ex>)
+ id 10HmbD-0005vi-00
+ for userh@domain.com; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmbD-0005vi-00@myhost.test.ex>
+From: CALLER_NAME <CALLER@myhost.test.ex>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+X-hdr-rtr-new: +++
+
+.
+250 OK
+QUIT
+250 OK
+End of script
+Listening on port 1224 ...
+Connection request from [ip4.ip4.ip4.ip4]
+220 ESMTP
+EHLO myhost.test.ex
+250 OK
+MAIL FROM:<CALLER@myhost.test.ex>
+250 Sender OK
+RCPT TO:<useri@domain.com>
+450 not right now
+QUIT
+250 OK
+End of script