Gitweb:
https://git.exim.org/exim.git/commitdiff/a1108b5118d32e969c5fe91b2110944f7483a7cb
Commit: a1108b5118d32e969c5fe91b2110944f7483a7cb
Parent: 4022c6568fa89eb47e0d78f86f7b7cfbcfefcdc4
Author: Jeremy Harris <jgh146exb@???>
AuthorDate: Sun Apr 18 01:51:28 2021 +0100
Committer: Jeremy Harris <jgh146exb@???>
CommitDate: Sun Apr 18 01:51:28 2021 +0100
Experimental: ESMTP LIMITS extension
---
doc/doc-docbook/spec.xfpt | 12 +-
doc/doc-txt/experimental-spec.txt | 36 +++
src/src/config.h.defaults | 1 +
src/src/dbfn.c | 2 +-
src/src/dbstuff.h | 10 +-
src/src/deliver.c | 3 +
src/src/exim.c | 16 +-
src/src/functions.h | 7 +-
src/src/globals.c | 12 +
src/src/globals.h | 12 +
src/src/readconf.c | 3 +
src/src/smtp_in.c | 13 +
src/src/transport.c | 36 ++-
src/src/transports/smtp.c | 544 +++++++++++++++++++++++---------
src/src/transports/smtp.h | 17 +-
test/aux-var-src/tls_conf_prefix | 3 +
test/confs/0453 | 4 +-
test/confs/0564 | 3 +
test/confs/0900 | 4 +-
test/confs/0901 | 3 +
test/confs/0906 | 3 +
test/confs/3414 | 3 +
test/confs/4050 | 3 +
test/confs/4710 | 36 +++
test/confs/4711 | 31 ++
test/confs/4712 | 28 ++
test/confs/4713 | 31 ++
test/confs/4714 | 36 +++
test/confs/5670 | 8 +-
test/log/4710 | 6 +
test/log/4711 | 24 ++
test/log/4712 | 8 +
test/log/4713 | 11 +
test/log/4714 | 14 +
test/runtest | 6 +
test/scripts/0000-Basic/0453 | 2 +-
test/scripts/4710-esmtp-limits/4710 | 86 +++++
test/scripts/4710-esmtp-limits/4711 | 138 ++++++++
test/scripts/4710-esmtp-limits/4712 | 65 ++++
test/scripts/4710-esmtp-limits/4713 | 64 ++++
test/scripts/4710-esmtp-limits/4714 | 84 +++++
test/scripts/4710-esmtp-limits/REQUIRES | 1 +
test/scripts/5670-OCSP-GnuTLS-1.3/5670 | 8 +-
test/stdout/4710 | 87 +++++
test/stdout/4711 | 183 +++++++++++
test/stdout/4712 | 89 ++++++
test/stdout/4713 | 86 +++++
test/stdout/4714 | 117 +++++++
48 files changed, 1829 insertions(+), 170 deletions(-)
diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt
index 36be62f..56da191 100644
--- a/doc/doc-docbook/spec.xfpt
+++ b/doc/doc-docbook/spec.xfpt
@@ -3940,6 +3940,16 @@ This option is not intended for use by external callers. It is used internally
by Exim in conjunction with the &%-MC%& option. It signifies that a
remote host supports the ESMTP &_CHUNKING_& extension.
+.new
+.vitem &%-MCL%&
+.oindex "&%-MCL%&"
+This option is not intended for use by external callers. It is used internally
+by Exim in conjunction with the &%-MC%& option. It signifies that the server to
+which Exim is connected advertised limits on numbers of mails, recipients or
+recipient domains.
+The limits are given by the following three arguments.
+.wen
+
.vitem &%-MCP%&
.oindex "&%-MCP%&"
This option is not intended for use by external callers. It is used internally
@@ -25725,7 +25735,7 @@ has advertised support for IGNOREQUOTA in its response to the LHLO command.
This option limits the number of RCPT commands that are sent in a single
SMTP message transaction. Each set of addresses is treated independently, and
so can cause parallel connections to the same host if &%remote_max_parallel%&
-permits this.
+permits this. A value setting of zero disables the limit.
.new
diff --git a/doc/doc-txt/experimental-spec.txt b/doc/doc-txt/experimental-spec.txt
index 47cd93f..5e0b044 100644
--- a/doc/doc-txt/experimental-spec.txt
+++ b/doc/doc-txt/experimental-spec.txt
@@ -680,6 +680,42 @@ to its QUIT, not properly closing the TLS session and not properly closing
the TCP connection. Previously this resulted is an error from SSL_write
being logged.
+
+
+Limits ESMTP extension
+---------------------------------------------------------------
+Per
https://datatracker.ietf.org/doc/html/draft-freed-smtp-limits-01
+
+If compiled with EXPERIMENTAL_ESMTP_LIMITS=yes :-
+
+As a server, Exim will advertise, in the EHLO response, the limit for RCPT
+commands set by the recipients_max main-section config option (if it is set),
+and the limit for MAIL commands set by the smtp_accept_max_per_connection
+option.
+
+Note that as of writing, smtp_accept_max_per_connection is expanded but
+recipients_max is not.
+
+A new main-section option "limits_advertise_hosts" controls whether
+the limits are advertised; the default for the option is "*".
+
+As a client, Exim will:
+
+ - note an advertised MAILMAX; the lower of the value given and the
+ value from the transport connection_max_messages option is used.
+
+ - note an advertised RCPTMAX; the lower of the
+ value given and the value from the transport max_rcpt option is used.
+ Parallisation of transactions is not done if due to a RCPTMAX, unlike
+ max_rcpt.
+
+ - note an advertised RCPTDOMAINMAX, and behave as if the transport
+ multi_domains option was set to false. The value advertised is ignored.
+
+Values advertised are only noted for TLS connections and ones for which
+the server does not advertise TLS support.
+
+
--------------------------------------------------------------
End of file
--------------------------------------------------------------
diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults
index 02031b7..e233fb3 100644
--- a/src/src/config.h.defaults
+++ b/src/src/config.h.defaults
@@ -205,6 +205,7 @@ Do not put spaces between # and the 'define'.
#define EXPERIMENTAL_BRIGHTMAIL
#define EXPERIMENTAL_DCC
#define EXPERIMENTAL_DSN_INFO
+#define EXPERIMENTAL_ESMTP_LIMITS
#define EXPERIMENTAL_QUEUEFILE
#define EXPERIMENTAL_SRS_ALT
diff --git a/src/src/dbfn.c b/src/src/dbfn.c
index be6a47a..0f56ad5 100644
--- a/src/src/dbfn.c
+++ b/src/src/dbfn.c
@@ -342,7 +342,7 @@ return yield;
/* Read a record. If the length is not as expected then delete it, write
-an error log line and return NULL.
+an error log line, delete the record and return NULL.
Use this for fixed-size records (so not retry or wait records).
Arguments:
diff --git a/src/src/dbstuff.h b/src/src/dbstuff.h
index 5a8441d..8993415 100644
--- a/src/src/dbstuff.h
+++ b/src/src/dbstuff.h
@@ -792,13 +792,21 @@ typedef struct {
#ifndef DISABLE_PIPE_CONNECT
/* This structure records the EHLO responses, cleartext and crypted,
-for an IP, as bitmasks (cf. OPTION_TLS) */
+for an IP, as bitmasks (cf. OPTION_TLS). For LIMITS, also values
+advertised for MAILMAX, RCPTMAX and RCPTDOMAINMAX; zero meaning no
+value advertised. */
typedef struct {
unsigned short cleartext_features;
unsigned short crypted_features;
unsigned short cleartext_auths;
unsigned short crypted_auths;
+
+# ifdef EXPERIMENTAL_ESMTP_LIMITS
+ unsigned int limit_mail;
+ unsigned int limit_rcpt;
+ unsigned int limit_rcptdom;
+# endif
} ehlo_resp_precis;
typedef struct {
diff --git a/src/src/deliver.c b/src/src/deliver.c
index 0cddec7..9ebd281 100644
--- a/src/src/deliver.c
+++ b/src/src/deliver.c
@@ -8570,6 +8570,9 @@ f.tcp_fastopen_ok = TRUE;
+/* Called from a commandline, or from the daemon, to do a delivery.
+We need to regain privs; do this by exec of the exim binary. */
+
void
delivery_re_exec(int exec_type)
{
diff --git a/src/src/exim.c b/src/src/exim.c
index 1244aee..8526cbb 100644
--- a/src/src/exim.c
+++ b/src/src/exim.c
@@ -1010,6 +1010,9 @@ g = string_cat(NULL, US"Support for:");
#ifdef EXPERIMENTAL_DSN_INFO
g = string_cat(g, US" Experimental_DSN_info");
#endif
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+ g = string_cat(g, US" Experimental_ESMTP_Limits");
+#endif
#ifdef EXPERIMENTAL_QUEUEFILE
g = string_cat(g, US" Experimental_QUEUEFILE");
#endif
@@ -2257,7 +2260,7 @@ on the second character (the one after '-'), to save some effort. */
case 'P':
/* -bP config: we need to setup here, because later,
- * when list_options is checked, the config is read already */
+ when list_options is checked, the config is read already */
if (*argrest)
badarg = TRUE;
else if (argv[i+1] && Ustrcmp(argv[i+1], "config") == 0)
@@ -2788,6 +2791,17 @@ on the second character (the one after '-'), to save some effort. */
case 'K': smtp_peer_options |= OPTION_CHUNKING; break;
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+ /* -MCL: peer used LIMITS RCPTMAX and/or RCPTDOMAINMAX */
+ case 'L': if (++i < argc) continue_limit_mail = Uatoi(argv[i]);
+ else badarg = TRUE;
+ if (++i < argc) continue_limit_rcpt = Uatoi(argv[i]);
+ else badarg = TRUE;
+ if (++i < argc) continue_limit_rcptdom = Uatoi(argv[i]);
+ else badarg = TRUE;
+ break;
+#endif
+
/* -MCP: set the smtp_use_pipelining flag; this is useful only when
it preceded -MC (see above) */
diff --git a/src/src/functions.h b/src/src/functions.h
index 0f962b3..7d6e338 100644
--- a/src/src/functions.h
+++ b/src/src/functions.h
@@ -593,8 +593,11 @@ extern BOOL transport_check_waiting(const uschar *, const uschar *, int, usch
extern void transport_init(void);
extern void transport_do_pass_socket(const uschar *, const uschar *,
const uschar *, uschar *, int);
-extern BOOL transport_pass_socket(const uschar *, const uschar *, const uschar *, uschar *,
- int);
+extern BOOL transport_pass_socket(const uschar *, const uschar *, const uschar *, uschar *, int
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+ , unsigned, unsigned, unsigned
+#endif
+ );
extern uschar *transport_rcpt_address(address_item *, BOOL);
extern BOOL transport_set_up_command(const uschar ***, uschar *,
BOOL, int, address_item *, uschar *, uschar **);
diff --git a/src/src/globals.c b/src/src/globals.c
index 7ee7c38..c45e8a9 100644
--- a/src/src/globals.c
+++ b/src/src/globals.c
@@ -710,6 +710,10 @@ unsigned chunking_data_left = 0;
chunking_state_t chunking_state= CHUNKING_NOT_OFFERED;
const pcre *regex_CHUNKING = NULL;
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+const pcre *regex_LIMITS = NULL;
+#endif
+
uschar *client_authenticator = NULL;
uschar *client_authenticated_id = NULL;
uschar *client_authenticated_sender = NULL;
@@ -742,6 +746,11 @@ uschar *continue_hostname = NULL;
uschar *continue_host_address = NULL;
int continue_sequence = 1;
uschar *continue_transport = NULL;
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+unsigned continue_limit_mail = 0;
+unsigned continue_limit_rcpt = 0;
+unsigned continue_limit_rcptdom= 0;
+#endif
uschar *csa_status = NULL;
cut_t cutthrough = {
@@ -1001,6 +1010,9 @@ uschar *keep_environment = NULL;
int keep_malformed = 4*24*60*60; /* 4 days */
uschar *eldap_dn = NULL;
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+uschar *limits_advertise_hosts = US"*";
+#endif
int load_average = -2;
uschar *local_from_prefix = NULL;
uschar *local_from_suffix = NULL;
diff --git a/src/src/globals.h b/src/src/globals.h
index 58bf3c4..ed7cffb 100644
--- a/src/src/globals.h
+++ b/src/src/globals.h
@@ -434,6 +434,12 @@ extern uschar *continue_hostname; /* Host for continued delivery */
extern uschar *continue_host_address; /* IP address for ditto */
extern int continue_sequence; /* Sequence num for continued delivery */
extern uschar *continue_transport; /* Transport for continued delivery */
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+extern unsigned continue_limit_mail; /* Peer advertised limit */
+extern unsigned continue_limit_rcpt;
+extern unsigned continue_limit_rcptdom;
+#endif
+
extern uschar *csa_status; /* Client SMTP Authorization result */
@@ -652,6 +658,9 @@ extern uschar *keep_environment; /* Whitelist for environment variables */
extern int keep_malformed; /* Time to keep malformed messages */
extern uschar *eldap_dn; /* Where LDAP DNs are left */
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+extern uschar *limits_advertise_hosts; /* for banner/EHLO pipelining */
+#endif
extern int load_average; /* Most recently read load average */
extern BOOL local_from_check; /* For adding Sender: (global value) */
extern uschar *local_from_prefix; /* Permitted prefixes */
@@ -854,6 +863,9 @@ extern const pcre *regex_check_dns_names; /* For DNS name checking */
extern const pcre *regex_From; /* For recognizing "From_" lines */
extern const pcre *regex_CHUNKING; /* For recognizing CHUNKING (RFC 3030) */
extern const pcre *regex_IGNOREQUOTA; /* For recognizing IGNOREQUOTA (LMTP) */
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+extern const pcre *regex_LIMITS; /* For recognizing LIMITS */
+#endif
extern const pcre *regex_PIPELINING; /* For recognizing PIPELINING */
extern const pcre *regex_SIZE; /* For recognizing SIZE settings */
#ifndef DISABLE_PIPE_CONNECT
diff --git a/src/src/readconf.c b/src/src/readconf.c
index 0ae3166..e8e310b 100644
--- a/src/src/readconf.c
+++ b/src/src/readconf.c
@@ -201,6 +201,9 @@ static optionlist optionlist_config[] = {
{ "ldap_start_tls", opt_bool, {&eldap_start_tls} },
{ "ldap_version", opt_int, {&eldap_version} },
#endif
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+ { "limits_advertise_hosts", opt_stringptr, {&limits_advertise_hosts} },
+#endif
{ "local_from_check", opt_bool, {&local_from_check} },
{ "local_from_prefix", opt_stringptr, {&local_from_prefix} },
{ "local_from_suffix", opt_stringptr, {&local_from_suffix} },
diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c
index 2bb15b6..d60e7d5 100644
--- a/src/src/smtp_in.c
+++ b/src/src/smtp_in.c
@@ -4346,6 +4346,19 @@ while (done <= 0)
g = string_catn(g, US"-SIZE\r\n", 7);
}
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+ if ( (mailmax > 0 || recipients_max)
+ && verify_check_host(&limits_advertise_hosts) == OK)
+ {
+ g = string_fmt_append(g, "%.3s-LIMITS", smtp_code);
+ if (mailmax > 0)
+ g = string_fmt_append(g, " MAILMAX=%d", mailmax);
+ if (recipients_max)
+ g = string_fmt_append(g, " RCPTMAX=%d", recipients_max);
+ g = string_catn(g, US"\r\n", 2);
+ }
+#endif
+
/* Exim does not do protocol conversion or data conversion. It is 8-bit
clean; if it has an 8-bit character in its hand, it just sends it. It
cannot therefore specify 8BITMIME and remain consistent with the RFCs.
diff --git a/src/src/transport.c b/src/src/transport.c
index 49a84cc..d8fd858 100644
--- a/src/src/transport.c
+++ b/src/src/transport.c
@@ -1880,9 +1880,21 @@ void
transport_do_pass_socket(const uschar *transport_name, const uschar *hostname,
const uschar *hostaddress, uschar *id, int socket_fd)
{
-int i = 27;
+int i = 13;
const uschar **argv;
+#ifndef DISABLE_TLS
+if (smtp_peer_options & OPTION_TLS) i += 6;
+#endif
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+if (continue_limit_mail || continue_limit_rcpt || continue_limit_rcptdom)
+ i += 4;
+#endif
+if (queue_run_pid != (pid_t)0) i += 3;
+#ifdef SUPPORT_SOCKS
+if (proxy_session) i += 5;
+#endif
+
/* Set up the calling arguments; use the standard function for the basics,
but we have a number of extras that may be added. */
@@ -1916,6 +1928,16 @@ if (smtp_peer_options & OPTION_TLS)
argv[i++] = US"-MCT";
#endif
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+if (continue_limit_rcpt || continue_limit_rcptdom)
+ {
+ argv[i++] = US"-MCL";
+ argv[i++] = string_sprintf("%u", continue_limit_mail);
+ argv[i++] = string_sprintf("%u", continue_limit_rcpt);
+ argv[i++] = string_sprintf("%u", continue_limit_rcptdom);
+ }
+#endif
+
if (queue_run_pid != (pid_t)0)
{
argv[i++] = US"-MCQ";
@@ -1976,13 +1998,23 @@ Returns: FALSE if fork fails; TRUE otherwise
BOOL
transport_pass_socket(const uschar *transport_name, const uschar *hostname,
- const uschar *hostaddress, uschar *id, int socket_fd)
+ const uschar *hostaddress, uschar *id, int socket_fd
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+ , unsigned peer_limit_mail, unsigned peer_limit_rcpt, unsigned peer_limit_rcptdom
+#endif
+ )
{
pid_t pid;
int status;
DEBUG(D_transport) debug_printf("transport_pass_socket entered\n");
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+continue_limit_mail = peer_limit_mail;
+continue_limit_rcpt = peer_limit_rcpt;
+continue_limit_rcptdom = peer_limit_rcptdom;
+#endif
+
if ((pid = exim_fork(US"continued-transport-interproc")) == 0)
{
/* Disconnect entirely from the parent process. If we are running in the
diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c
index 2a2928c..5294057 100644
--- a/src/src/transports/smtp.c
+++ b/src/src/transports/smtp.c
@@ -274,6 +274,11 @@ if (!regex_IGNOREQUOTA) regex_IGNOREQUOTA =
if (!regex_EARLY_PIPE) regex_EARLY_PIPE =
regex_must_compile(US"\\n250[\\s\\-]" EARLY_PIPE_FEATURE_NAME "(\\s|\\n|$)", FALSE, TRUE);
#endif
+
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+if (!regex_LIMITS) regex_LIMITS =
+ regex_must_compile(US"\\n250[\\s\\-]LIMITS\\s", FALSE, TRUE);
+#endif
}
@@ -756,6 +761,82 @@ return TRUE;
}
+/******************************************************************************/
+
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+/* If TLS, or TLS not offered, called with the EHLO response in the buffer.
+Check it for a LIMITS keyword and parse values into the smtp context structure.
+
+We don't bother with peers that we won't talk TLS to, even though they can,
+just ignore their LIMITS advice (if any) and treat them as if they do not.
+This saves us dealing with a duplicate set of values. */
+
+static void
+ehlo_response_limits_read(smtp_context * sx)
+{
+int ovec[3]; /* results vector for a main-match only */
+
+/* matches up to just after the first space after the keyword */
+
+if (pcre_exec(regex_LIMITS, NULL, CS sx->buffer, Ustrlen(sx->buffer),
+ 0, PCRE_EOPT, ovec, nelem(ovec)) >= 0)
+ for (const uschar * s = sx->buffer + ovec[1]; *s; )
+ {
+ while (isspace(*s)) s++;
+ if (*s == '\n') break;
+
+ if (strncmpic(s, US"MAILMAX=", 8) == 0)
+ {
+ sx->peer_limit_mail = atoi(CS (s += 8));
+ while (isdigit(*s)) s++;
+ }
+ else if (strncmpic(s, US"RCPTMAX=", 8) == 0)
+ {
+ sx->peer_limit_rcpt = atoi(CS (s += 8));
+ while (isdigit(*s)) s++;
+ }
+ else if (strncmpic(s, US"RCPTDOMAINMAX=", 14) == 0)
+ {
+ sx->peer_limit_rcptdom = atoi(CS (s += 14));
+ while (isdigit(*s)) s++;
+ }
+ else
+ while (*s && !isspace(*s)) s++;
+ }
+}
+
+/* Apply given values to the current connection */
+static void
+ehlo_limits_apply(smtp_context * sx,
+ unsigned limit_mail, unsigned limit_rcpt, unsigned limit_rcptdom)
+{
+if (limit_mail && limit_mail < sx->max_mail) sx->max_mail = limit_mail;
+if (limit_rcpt && limit_rcpt < sx->max_rcpt) sx->max_rcpt = limit_rcpt;
+if (limit_rcptdom)
+ {
+ DEBUG(D_transport) debug_printf("will treat as !multi_domain\n");
+ sx->single_rcpt_domain = TRUE;
+ }
+}
+
+/* Apply values from EHLO-resp to the current connection */
+static void
+ehlo_response_limits_apply(smtp_context * sx)
+{
+ehlo_limits_apply(sx, sx->peer_limit_mail, sx->peer_limit_rcpt,
+ sx->peer_limit_rcptdom);
+}
+
+/* Apply values read from cache to the current connection */
+static void
+ehlo_cache_limits_apply(smtp_context * sx)
+{
+ehlo_limits_apply(sx, sx->ehlo_resp.limit_mail, sx->ehlo_resp.limit_rcpt,
+ sx->ehlo_resp.limit_rcptdom);
+}
+#endif
+
+/******************************************************************************/
#ifndef DISABLE_PIPE_CONNECT
static uschar *
@@ -769,19 +850,45 @@ return Ustrchr(host->address, ':')
host->port == PORT_NONE ? sx->port : host->port);
}
+/* Cache EHLO-response info for use by early-pipe.
+Called
+- During a normal flow on EHLO response (either cleartext or under TLS),
+ when we are willing to do PIPE_CONNECT and it is offered
+- During an early-pipe flow on receiving the actual EHLO response and noting
+ disparity versus the cached info used, when PIPE_CONNECT is still being offered
+
+We assume that suitable values have been set in the sx.ehlo_resp structure for
+features and auths; we handle the copy of limits. */
+
static void
-write_ehlo_cache_entry(const smtp_context * sx)
+write_ehlo_cache_entry(smtp_context * sx)
{
open_db dbblock, * dbm_file;
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+sx->ehlo_resp.limit_mail = sx->peer_limit_mail;
+sx->ehlo_resp.limit_rcpt = sx->peer_limit_rcpt;
+sx->ehlo_resp.limit_rcptdom = sx->peer_limit_rcptdom;
+#endif
+
if ((dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE)))
{
uschar * ehlo_resp_key = ehlo_cache_key(sx);
dbdata_ehlo_resp er = { .data = sx->ehlo_resp };
- HDEBUG(D_transport) debug_printf("writing clr %04x/%04x cry %04x/%04x\n",
- sx->ehlo_resp.cleartext_features, sx->ehlo_resp.cleartext_auths,
- sx->ehlo_resp.crypted_features, sx->ehlo_resp.crypted_auths);
+ HDEBUG(D_transport)
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+ if (sx->ehlo_resp.limit_mail || sx->ehlo_resp.limit_rcpt || sx->ehlo_resp.limit_rcptdom)
+ debug_printf("writing clr %04x/%04x cry %04x/%04x lim %05d/%05d/%05d\n",
+ sx->ehlo_resp.cleartext_features, sx->ehlo_resp.cleartext_auths,
+ sx->ehlo_resp.crypted_features, sx->ehlo_resp.crypted_auths,
+ sx->ehlo_resp.limit_mail, sx->ehlo_resp.limit_rcpt,
+ sx->ehlo_resp.limit_rcptdom);
+ else
+#endif
+ debug_printf("writing clr %04x/%04x cry %04x/%04x\n",
+ sx->ehlo_resp.cleartext_features, sx->ehlo_resp.cleartext_auths,
+ sx->ehlo_resp.crypted_features, sx->ehlo_resp.crypted_auths);
dbfn_write(dbm_file, ehlo_resp_key, &er, (int)sizeof(er));
dbfn_close(dbm_file);
@@ -826,12 +933,26 @@ else
}
else
{
+ DEBUG(D_transport)
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+ if (er->data.limit_mail || er->data.limit_rcpt || er->data.limit_rcptdom)
+ debug_printf("EHLO response bits from cache:"
+ " cleartext 0x%04x/0x%04x crypted 0x%04x/0x%04x lim %05d/%05d/%05d\n",
+ er->data.cleartext_features, er->data.cleartext_auths,
+ er->data.crypted_features, er->data.crypted_auths,
+ er->data.limit_mail, er->data.limit_rcpt, er->data.limit_rcptdom);
+ else
+#endif
+ debug_printf("EHLO response bits from cache:"
+ " cleartext 0x%04x/0x%04x crypted 0x%04x/0x%04x\n",
+ er->data.cleartext_features, er->data.cleartext_auths,
+ er->data.crypted_features, er->data.crypted_auths);
+
sx->ehlo_resp = er->data;
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+ ehlo_cache_limits_apply(sx);
+#endif
dbfn_close(dbm_file);
- DEBUG(D_transport) debug_printf(
- "EHLO response bits from cache: cleartext 0x%04x/0x%04x crypted 0x%04x/0x%04x\n",
- er->data.cleartext_features, er->data.cleartext_auths,
- er->data.crypted_features, er->data.crypted_auths);
return TRUE;
}
dbfn_close(dbm_file);
@@ -933,8 +1054,9 @@ if (pending_EHLO)
goto fail;
}
- /* Compare the actual EHLO response to the cached value we assumed;
- on difference, dump or rewrite the cache and arrange for a retry. */
+ /* Compare the actual EHLO response extensions and AUTH methods to the cached
+ value we assumed; on difference, dump or rewrite the cache and arrange for a
+ retry. */
ap = tls_out.active.sock < 0
? &sx->ehlo_resp.cleartext_auths : &sx->ehlo_resp.crypted_auths;
@@ -944,6 +1066,10 @@ if (pending_EHLO)
| OPTION_CHUNKING | OPTION_PRDR | OPTION_DSN | OPTION_PIPE | OPTION_SIZE
| OPTION_UTF8 | OPTION_EARLY_PIPE
);
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+ if (tls_out.active.sock >= 0 || !(peer_offered & OPTION_TLS))
+ ehlo_response_limits_read(sx);
+#endif
if ( peer_offered != sx->peer_offered
|| (authbits = study_ehlo_auths(sx)) != *ap)
{
@@ -951,16 +1077,44 @@ if (pending_EHLO)
debug_printf("EHLO %s extensions changed, 0x%04x/0x%04x -> 0x%04x/0x%04x\n",
tls_out.active.sock < 0 ? "cleartext" : "crypted",
sx->peer_offered, *ap, peer_offered, authbits);
- *(tls_out.active.sock < 0
- ? &sx->ehlo_resp.cleartext_features : &sx->ehlo_resp.crypted_features) = peer_offered;
- *ap = authbits;
if (peer_offered & OPTION_EARLY_PIPE)
+ {
+ *(tls_out.active.sock < 0
+ ? &sx->ehlo_resp.cleartext_features : &sx->ehlo_resp.crypted_features) =
+ peer_offered;
+ *ap = authbits;
write_ehlo_cache_entry(sx);
+ }
else
invalidate_ehlo_cache_entry(sx);
return OK; /* just carry on */
}
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+ /* If we are handling LIMITS, compare the actual EHLO LIMITS values with the
+ cached values and invalidate cache if different. OK to carry on with
+ connect since values are advisory. */
+ {
+ if ( (tls_out.active.sock >= 0 || !(peer_offered & OPTION_TLS))
+ && ( sx->peer_limit_mail != sx->ehlo_resp.limit_mail
+ || sx->peer_limit_rcpt != sx->ehlo_resp.limit_rcpt
+ || sx->peer_limit_rcptdom != sx->ehlo_resp.limit_rcptdom
+ ) )
+ {
+ HDEBUG(D_transport)
+ {
+ debug_printf("EHLO LIMITS changed:");
+ if (sx->peer_limit_mail != sx->ehlo_resp.limit_mail)
+ debug_printf(" MAILMAX %u -> %u\n", sx->ehlo_resp.limit_mail, sx->peer_limit_mail);
+ else if (sx->peer_limit_rcpt != sx->ehlo_resp.limit_rcpt)
+ debug_printf(" RCPTMAX %u -> %u\n", sx->ehlo_resp.limit_rcpt, sx->peer_limit_rcpt);
+ else
+ debug_printf(" RCPTDOMAINMAX %u -> %u\n", sx->ehlo_resp.limit_rcptdom, sx->peer_limit_rcptdom);
+ }
+ invalidate_ehlo_cache_entry(sx);
+ }
+ }
+#endif
}
return OK;
@@ -1882,7 +2036,8 @@ sx->dane_required =
/* sx->pending_BANNER = sx->pending_EHLO = sx->pending_MAIL = FALSE; */
#endif
-if ((sx->max_rcpt = sx->conn_args.tblock->max_addresses) == 0) sx->max_rcpt = 999999;
+if ((sx->max_mail = sx->conn_args.tblock->connection_max_messages) == 0) sx->max_mail = 999999;
+if ((sx->max_rcpt = sx->conn_args.tblock->max_addresses) == 0) sx->max_rcpt = 999999;
/* sx->peer_offered = 0; */
/* sx->avoid_option = 0; */
sx->igquotstr = US"";
@@ -2083,6 +2238,9 @@ if (!continue_hostname)
sx->cctx.tls_ctx = NULL;
sx->inblock.cctx = sx->outblock.cctx = &sx->cctx;
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+ sx->peer_limit_mail = sx->peer_limit_rcpt = sx->peer_limit_rcptdom =
+#endif
sx->avoid_option = sx->peer_offered = smtp_peer_options = 0;
#ifndef DISABLE_PIPE_CONNECT
@@ -2361,6 +2519,13 @@ goto SEND_QUIT;
)
#endif
);
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+ if (tls_out.active.sock >= 0 || !(sx->peer_offered & OPTION_TLS))
+ {
+ ehlo_response_limits_read(sx);
+ ehlo_response_limits_apply(sx);
+ }
+#endif
#ifndef DISABLE_PIPE_CONNECT
if (sx->early_pipe_ok)
{
@@ -2415,6 +2580,13 @@ else
sx->inblock.cctx = sx->outblock.cctx = &sx->cctx;
smtp_command = big_buffer;
sx->peer_offered = smtp_peer_options;
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+ /* Limits passed by cmdline over exec. */
+ ehlo_limits_apply(sx,
+ sx->peer_limit_mail = continue_limit_mail,
+ sx->peer_limit_rcpt = continue_limit_rcpt,
+ sx->peer_limit_rcptdom = continue_limit_rcptdom);
+#endif
sx->helo_data = NULL; /* ensure we re-expand ob->helo_data */
/* For a continued connection with TLS being proxied for us, or a
@@ -2456,7 +2628,8 @@ if ( smtp_peer_options & OPTION_TLS
#ifndef DISABLE_PIPE_CONNECT
/* If doing early-pipelining reap the banner and EHLO-response but leave
- the response for the STARTTLS we just sent alone. */
+ the response for the STARTTLS we just sent alone. On fail, assume wrong
+ cached capability and retry with the pipelining disabled. */
if (sx->early_pipe_active && sync_responses(sx, 2, 0) != 0)
{
@@ -2592,6 +2765,13 @@ if (tls_out.active.sock >= 0)
DEBUG(D_transport) debug_printf("Using cached crypted PIPE_CONNECT\n");
}
#endif
+#ifdef EXPERIMMENTAL_ESMTP_LIMITS
+ /* As we are about to send another EHLO, forget any LIMITS received so far. */
+ sx->peer_limit_mail = sx->peer_limit_rcpt = sx->peer_limit_rcptdom = 0;
+ if ((sx->max_mail = sx->conn_args.tblock->connection_max_message) == 0) sx->max_mail = 999999;
+ if ((sx->max_rcpt = sx->conn_args.tblock->max_addresses) == 0) sx->max_rcpt = 999999;
+ sx->single_rcpt_domain = FALSE;
+#endif
/* For SMTPS we need to wait for the initial OK response. */
if (sx->smtps)
@@ -2721,6 +2901,14 @@ if ( !continue_hostname
if (tls_out.active.sock >= 0)
sx->ehlo_resp.crypted_features = sx->peer_offered;
#endif
+
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+ if (tls_out.active.sock >= 0 || !(sx->peer_offered & OPTION_TLS))
+ {
+ ehlo_response_limits_read(sx);
+ ehlo_response_limits_apply(sx);
+ }
+#endif
}
/* Set for IGNOREQUOTA if the response to LHLO specifies support and the
@@ -3054,7 +3242,7 @@ if ( sx->peer_offered & OPTION_UTF8
/* check if all addresses have DSN-lasthop flag; do not send RET and ENVID if so */
for (sx->dsn_all_lasthop = TRUE, addr = addrlist, address_count = 0;
- addr && address_count < sx->max_rcpt;
+ addr && address_count < sx->max_rcpt; /*XXX maybe also || sx->single_rcpt_domain ? */
addr = addr->next) if (addr->transport_return == PENDING_DEFER)
{
address_count++;
@@ -3142,6 +3330,9 @@ int
smtp_write_mail_and_rcpt_cmds(smtp_context * sx, int * yield)
{
address_item * addr;
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+address_item * restart_addr = NULL;
+#endif
int address_count, pipe_limit;
int rc;
@@ -3233,6 +3424,24 @@ for (addr = sx->first_addr, address_count = 0, pipe_limit = 100;
BOOL no_flush;
uschar * rcpt_addr;
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+ if ( sx->single_rcpt_domain /* restriction on domains */
+ && address_count > 0 /* not first being sent */
+ && Ustrcmp(addr->domain, sx->first_addr->domain) != 0 /* dom diff from first */
+ )
+ {
+ DEBUG(D_transport) debug_printf("skipping different domain %s\n", addr->domain);
+
+ /* Ensure the smtp-response reaper does not think the address had a RCPT
+ command sent for it. Reset to PENDING_DEFER in smtp_deliver(), where we
+ goto SEND_MESSAGE. */
+
+ addr->transport_return = SKIP;
+ if (!restart_addr) restart_addr = addr; /* note restart point */
+ continue; /* skip this one */
+ }
+#endif
+
addr->dsn_aware = sx->peer_offered & OPTION_DSN
? dsn_support_yes : dsn_support_no;
@@ -3304,7 +3513,11 @@ for (addr = sx->first_addr, address_count = 0, pipe_limit = 100;
}
} /* Loop for next address */
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+sx->next_addr = restart_addr ? restart_addr : addr;
+#else
sx->next_addr = addr;
+#endif
return 0;
}
@@ -3493,10 +3706,13 @@ int yield = OK;
int save_errno;
int rc;
-BOOL pass_message = FALSE;
uschar *message = NULL;
uschar new_message_id[MESSAGE_ID_LENGTH + 1];
smtp_context * sx = store_get(sizeof(*sx), TRUE); /* tainted, for the data buffers */
+BOOL pass_message = FALSE;
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+BOOL mail_limit = FALSE;
+#endif
#ifdef SUPPORT_DANE
BOOL dane_held;
#endif
@@ -3516,8 +3732,8 @@ sx->conn_args.tblock = tblock;
gettimeofday(&sx->delivery_start, NULL);
sx->sync_addr = sx->first_addr = addrlist;
+REPEAT_CONN:
#ifdef SUPPORT_DANE
-DANE_DOMAINS:
dane_held = FALSE;
#endif
@@ -4090,7 +4306,7 @@ else
else
sprintf(CS sx->buffer, "%.500s\n", addr->unique);
- DEBUG(D_deliver) debug_printf("S:journalling %s\n", sx->buffer);
+ DEBUG(D_deliver) debug_printf("S:journalling %s", sx->buffer);
len = Ustrlen(CS sx->buffer);
if (write(journal_fd, sx->buffer, len) != len)
log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for "
@@ -4335,81 +4551,95 @@ DEBUG(D_transport)
sx->send_rset, f.continue_more, yield, sx->first_addr ? "not " : "");
if (sx->completed_addr && sx->ok && sx->send_quit)
- {
- smtp_compare_t t_compare =
- {.tblock = tblock, .current_sender_address = sender_address};
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+ if (mail_limit = continue_sequence >= sx->max_mail)
+ {
+ DEBUG(D_transport)
+ debug_printf("reached limit %u for MAILs per conn\n", sx->max_mail);
+ }
+ else
+#endif
+ {
+ smtp_compare_t t_compare =
+ {.tblock = tblock, .current_sender_address = sender_address};
- if ( sx->first_addr /* more addrs for this message */
- || f.continue_more /* more addrs for continued-host */
- || tcw_done && tcw /* more messages for host */
- || (
+ if ( sx->first_addr /* more addrs for this message */
+ || f.continue_more /* more addrs for continued-host */
+ || tcw_done && tcw /* more messages for host */
+ || (
#ifndef DISABLE_TLS
- ( tls_out.active.sock < 0 && !continue_proxy_cipher
- || verify_check_given_host(CUSS &ob->hosts_nopass_tls, host) != OK
- )
- &&
+ ( tls_out.active.sock < 0 && !continue_proxy_cipher
+ || verify_check_given_host(CUSS &ob->hosts_nopass_tls, host) != OK
+ )
+ &&
#endif
- transport_check_waiting(tblock->name, host->name,
- tblock->connection_max_messages, new_message_id,
- (oicf)smtp_are_same_identities, (void*)&t_compare)
- ) )
- {
- uschar *msg;
- BOOL pass_message;
+ transport_check_waiting(tblock->name, host->name,
+ sx->max_mail, new_message_id,
+ (oicf)smtp_are_same_identities, (void*)&t_compare)
+ ) )
+ {
+ uschar *msg;
+ BOOL pass_message;
- if (sx->send_rset)
- if (! (sx->ok = smtp_write_command(sx, SCMD_FLUSH, "RSET\r\n") >= 0))
- {
- msg = US string_sprintf("send() to %s [%s] failed: %s", host->name,
- host->address, strerror(errno));
- sx->send_quit = FALSE;
- }
- else if (! (sx->ok = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
- '2', ob->command_timeout)))
- {
- int code;
- sx->send_quit = check_response(host, &errno, 0, sx->buffer, &code, &msg,
- &pass_message);
- if (!sx->send_quit)
- {
- DEBUG(D_transport) debug_printf("H=%s [%s] %s\n",
- host->name, host->address, msg);
- }
- }
+ if (sx->send_rset)
+ if (! (sx->ok = smtp_write_command(sx, SCMD_FLUSH, "RSET\r\n") >= 0))
+ {
+ msg = US string_sprintf("send() to %s [%s] failed: %s", host->name,
+ host->address, strerror(errno));
+ sx->send_quit = FALSE;
+ }
+ else if (! (sx->ok = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
+ '2', ob->command_timeout)))
+ {
+ int code;
+ sx->send_quit = check_response(host, &errno, 0, sx->buffer, &code, &msg,
+ &pass_message);
+ if (!sx->send_quit)
+ {
+ DEBUG(D_transport) debug_printf("H=%s [%s] %s\n",
+ host->name, host->address, msg);
+ }
+ }
- /* Either RSET was not needed, or it succeeded */
+ /* Either RSET was not needed, or it succeeded */
- if (sx->ok)
- {
+ if (sx->ok)
+ {
#ifndef DISABLE_TLS
- int pfd[2];
-#endif
- int socket_fd = sx->cctx.sock;
-
- if (sx->first_addr) /* More addresses still to be sent */
- { /* for this message */
- continue_sequence++; /* for consistency */
- clearflag(sx->first_addr, af_new_conn);
- setflag(sx->first_addr, af_cont_conn); /* Causes * in logging */
- pipelining_active = sx->pipelining_used; /* was cleared at DATA */
- goto SEND_MESSAGE;
- }
+ int pfd[2];
+#endif
+ int socket_fd = sx->cctx.sock;
+
+ if (sx->first_addr) /* More addresses still to be sent */
+ { /* for this message */
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+ /* Any that we marked as skipped, reset to do now */
+ for (address_item * a = sx->first_addr; a; a = a->next)
+ if (a->transport_return == SKIP)
+ a->transport_return = PENDING_DEFER;
+#endif
+ continue_sequence++; /* for consistency */
+ clearflag(sx->first_addr, af_new_conn);
+ setflag(sx->first_addr, af_cont_conn); /* Causes * in logging */
+ pipelining_active = sx->pipelining_used; /* was cleared at DATA */
+ goto SEND_MESSAGE;
+ }
- /* Unless caller said it already has more messages listed for this host,
- pass the connection on to a new Exim process (below, the call to
- transport_pass_socket). If the caller has more ready, just return with
- the connection still open. */
+ /* Unless caller said it already has more messages listed for this host,
+ pass the connection on to a new Exim process (below, the call to
+ transport_pass_socket). If the caller has more ready, just return with
+ the connection still open. */
#ifndef DISABLE_TLS
- if (tls_out.active.sock >= 0)
- if ( f.continue_more
- || verify_check_given_host(CUSS &ob->hosts_noproxy_tls, host) == OK)
- {
- /* Before passing the socket on, or returning to caller with it still
- open, we must shut down TLS. Not all MTAs allow for the continuation
- of the SMTP session when TLS is shut down. We test for this by sending
- a new EHLO. If we don't get a good response, we don't attempt to pass
- the socket on. */
+ if (tls_out.active.sock >= 0)
+ if ( f.continue_more
+ || verify_check_given_host(CUSS &ob->hosts_noproxy_tls, host) == OK)
+ {
+ /* Before passing the socket on, or returning to caller with it still
+ open, we must shut down TLS. Not all MTAs allow for the continuation
+ of the SMTP session when TLS is shut down. We test for this by sending
+ a new EHLO. If we don't get a good response, we don't attempt to pass
+ the socket on. */
tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_WAIT);
sx->send_tlsclose = FALSE;
@@ -4422,84 +4652,88 @@ if (sx->completed_addr && sx->ok && sx->send_quit)
&& smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
'2', ob->command_timeout);
- if (sx->ok && f.continue_more)
- goto TIDYUP; /* More addresses for another run */
- }
- else
- {
- /* Set up a pipe for proxying TLS for the new transport process */
-
- smtp_peer_options |= OPTION_TLS;
- if ((sx->ok = socketpair(AF_UNIX, SOCK_STREAM, 0, pfd) == 0))
- socket_fd = pfd[1];
+ if (sx->ok && f.continue_more)
+ goto TIDYUP; /* More addresses for another run */
+ }
else
- set_errno(sx->first_addr, errno, US"internal allocation problem",
- DEFER, FALSE, host,
+ {
+ /* Set up a pipe for proxying TLS for the new transport process */
+
+ smtp_peer_options |= OPTION_TLS;
+ if ((sx->ok = socketpair(AF_UNIX, SOCK_STREAM, 0, pfd) == 0))
+ socket_fd = pfd[1];
+ else
+ set_errno(sx->first_addr, errno, US"internal allocation problem",
+ DEFER, FALSE, host,
# ifdef EXPERIMENTAL_DSN_INFO
- sx->smtp_greeting, sx->helo_response,
+ sx->smtp_greeting, sx->helo_response,
# endif
- &sx->delivery_start);
- }
- else
+ &sx->delivery_start);
+ }
+ else
#endif
- if (f.continue_more)
- goto TIDYUP; /* More addresses for another run */
-
- /* If the socket is successfully passed, we mustn't send QUIT (or
- indeed anything!) from here. */
+ if (f.continue_more)
+ goto TIDYUP; /* More addresses for another run */
-/*XXX DSN_INFO: assume likely to do new HELO; but for greet we'll want to
-propagate it from the initial
-*/
- if (sx->ok && transport_pass_socket(tblock->name, host->name,
- host->address, new_message_id, socket_fd))
- {
- sx->send_quit = FALSE;
+ /* If the socket is successfully passed, we mustn't send QUIT (or
+ indeed anything!) from here. */
- /* We have passed the client socket to a fresh transport process.
- If TLS is still active, we need to proxy it for the transport we
- just passed the baton to. Fork a child to to do it, and return to
- get logging done asap. Which way to place the work makes assumptions
- about post-fork prioritisation which may not hold on all platforms. */
-#ifndef DISABLE_TLS
- if (tls_out.active.sock >= 0)
+ /*XXX DSN_INFO: assume likely to do new HELO; but for greet we'll want to
+ propagate it from the initial
+ */
+ if (sx->ok && transport_pass_socket(tblock->name, host->name,
+ host->address, new_message_id, socket_fd
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+ , sx->peer_limit_mail, sx->peer_limit_rcpt, sx->peer_limit_rcptdom
+#endif
+ ))
{
- int pid = exim_fork(US"tls-proxy-interproc");
- if (pid == 0) /* child; fork again to disconnect totally */
- {
- /* does not return */
- smtp_proxy_tls(sx->cctx.tls_ctx, sx->buffer, sizeof(sx->buffer), pfd,
- ob->command_timeout);
- }
+ sx->send_quit = FALSE;
- if (pid > 0) /* parent */
+ /* We have passed the client socket to a fresh transport process.
+ If TLS is still active, we need to proxy it for the transport we
+ just passed the baton to. Fork a child to to do it, and return to
+ get logging done asap. Which way to place the work makes assumptions
+ about post-fork prioritisation which may not hold on all platforms. */
+#ifndef DISABLE_TLS
+ if (tls_out.active.sock >= 0)
{
- close(pfd[0]);
- /* tidy the inter-proc to disconn the proxy proc */
- waitpid(pid, NULL, 0);
- tls_close(sx->cctx.tls_ctx, TLS_NO_SHUTDOWN);
- sx->cctx.tls_ctx = NULL;
- (void)close(sx->cctx.sock);
- sx->cctx.sock = -1;
- continue_transport = NULL;
- continue_hostname = NULL;
- goto TIDYUP;
+ int pid = exim_fork(US"tls-proxy-interproc");
+ if (pid == 0) /* child; fork again to disconnect totally */
+ {
+ /* does not return */
+ smtp_proxy_tls(sx->cctx.tls_ctx, sx->buffer, sizeof(sx->buffer), pfd,
+ ob->command_timeout);
+ }
+
+ if (pid > 0) /* parent */
+ {
+ close(pfd[0]);
+ /* tidy the inter-proc to disconn the proxy proc */
+ waitpid(pid, NULL, 0);
+ tls_close(sx->cctx.tls_ctx, TLS_NO_SHUTDOWN);
+ sx->cctx.tls_ctx = NULL;
+ (void)close(sx->cctx.sock);
+ sx->cctx.sock = -1;
+ continue_transport = NULL;
+ continue_hostname = NULL;
+ goto TIDYUP;
+ }
+ log_write(0, LOG_PANIC_DIE, "fork failed");
}
- log_write(0, LOG_PANIC_DIE, "fork failed");
- }
#endif
+ }
}
- }
- /* If RSET failed and there are addresses left, they get deferred. */
- else
- set_errno(sx->first_addr, errno, msg, DEFER, FALSE, host,
+ /* If RSET failed and there are addresses left, they get deferred. */
+ else
+ set_errno(sx->first_addr, errno, msg, DEFER, FALSE, host,
#ifdef EXPERIMENTAL_DSN_INFO
- sx->smtp_greeting, sx->helo_response,
+ sx->smtp_greeting, sx->helo_response,
#endif
- &sx->delivery_start);
+ &sx->delivery_start);
+ }
}
- }
/* End off tidily with QUIT unless the connection has died or the socket has
been passed to another process. */
@@ -4613,12 +4847,24 @@ if (dane_held)
}
}
continue_sequence = 1; /* for consistency */
- goto DANE_DOMAINS;
+ goto REPEAT_CONN;
+ }
+#endif
+
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+if (mail_limit && sx->first_addr)
+ {
+ /* Reset the sequence count since we closed the connection. This is flagged
+ on the pipe back to the delivery process so that a non-continued-conn delivery
+ is logged. */
+
+ continue_sequence = 1; /* for consistency */
+ clearflag(sx->first_addr, af_cont_conn);
+ setflag(sx->first_addr, af_new_conn); /* clear * from logging */
+ goto REPEAT_CONN;
}
#endif
-continue_transport = NULL;
-continue_hostname = NULL;
return yield;
TIDYUP:
diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h
index 1ea2a4d..aff3f54 100644
--- a/src/src/transports/smtp.h
+++ b/src/src/transports/smtp.h
@@ -171,15 +171,25 @@ typedef struct {
BOOL pending_BDAT:1;
BOOL RCPT_452:1;
BOOL good_RCPT:1;
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+ BOOL single_rcpt_domain:1;
+#endif
BOOL completed_addr:1;
BOOL send_rset:1;
BOOL send_quit:1;
BOOL send_tlsclose:1;
+ unsigned peer_offered;
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+ unsigned peer_limit_mail;
+ unsigned peer_limit_rcpt;
+ unsigned peer_limit_rcptdom;
+#endif
+
+ unsigned max_mail;
int max_rcpt;
int cmd_count;
- unsigned peer_offered;
unsigned avoid_option;
uschar * igquotstr;
uschar * helo_data;
@@ -188,6 +198,11 @@ typedef struct {
uschar * helo_response;
#endif
#ifndef DISABLE_PIPE_CONNECT
+ /* Info about the EHLO response stored to / retrieved from cache. When
+ operating early-pipe, we use the cached values. For each of plaintext and
+ crypted we store bitmaps for ESMTP features and AUTH methods. If the LIMITS
+ extension is built and usable them at least one of the limits values cached
+ is nonzero, and we use the values to constrain the connection. */
ehlo_resp_precis ehlo_resp;
#endif
diff --git a/test/aux-var-src/tls_conf_prefix b/test/aux-var-src/tls_conf_prefix
index 5418176..e3f09b9 100644
--- a/test/aux-var-src/tls_conf_prefix
+++ b/test/aux-var-src/tls_conf_prefix
@@ -24,3 +24,6 @@ pipelining_connect_advertise_hosts = :
.ifdef _HAVE_DMARC
dmarc_tld_file =
.endif
+.ifdef _EXP_LIMITS
+limits_advertise_hosts = !*
+.endif
diff --git a/test/confs/0453 b/test/confs/0453
index 3ecc865..c611d4a 100644
--- a/test/confs/0453
+++ b/test/confs/0453
@@ -1,6 +1,6 @@
# Exim test configuration 0453
-LIMIT=
+ERROR_DETAILS=
.include DIR/aux-var/std_conf_prefix
@@ -11,7 +11,7 @@ primary_hostname = myhost.test.ex
qualify_domain = test.ex
-LIMIT
+ERROR_DETAILS
# End
diff --git a/test/confs/0564 b/test/confs/0564
index de71325..fd38c62 100644
--- a/test/confs/0564
+++ b/test/confs/0564
@@ -15,6 +15,9 @@ pipelining_connect_advertise_hosts =
.ifdef _HAVE_DMARC
dmarc_tld_file =
.endif
+.ifdef _OPT_MAIN_LIMITS_ADVERTISE_HOSTS
+limits_advertise_hosts = !*
+.endif
# ----- Main settings -----
diff --git a/test/confs/0900 b/test/confs/0900
index 7775fc4..a56ec0e 100644
--- a/test/confs/0900
+++ b/test/confs/0900
@@ -23,7 +23,9 @@ pipelining_connect_advertise_hosts = :
.ifdef _HAVE_DMARC
dmarc_tld_file =
.endif
-
+.ifdef _OPT_MAIN_LIMITS_ADVERTISE_HOSTS
+limits_advertise_hosts = !*
+.endif
# ----- Main settings -----
diff --git a/test/confs/0901 b/test/confs/0901
index a1f3916..361a9bf 100644
--- a/test/confs/0901
+++ b/test/confs/0901
@@ -17,6 +17,9 @@ tls_advertise_hosts = ${if eq {SRV}{tls} {*}}
.ifdef _HAVE_DMARC
dmarc_tld_file =
.endif
+.ifdef _OPT_MAIN_LIMITS_ADVERTISE_HOSTS
+limits_advertise_hosts = !*
+.endif
pipelining_advertise_hosts = :
diff --git a/test/confs/0906 b/test/confs/0906
index 5bd5f2a..57f359f 100644
--- a/test/confs/0906
+++ b/test/confs/0906
@@ -16,6 +16,9 @@ pipelining_connect_advertise_hosts =
.ifdef _HAVE_DMARC
dmarc_tld_file =
.endif
+.ifdef _EXP_LIMITS
+limits_advertise_hosts = !*
+.endif
# ----- Main settings -----
diff --git a/test/confs/3414 b/test/confs/3414
index 5d0f93c..25205a4 100644
--- a/test/confs/3414
+++ b/test/confs/3414
@@ -13,6 +13,9 @@ gecos_pattern = ""
gecos_name = CALLER_NAME
chunking_advertise_hosts =
tls_advertise_hosts =
+.ifdef _EXP_LIMITS
+limits_advertise_hosts = !*
+.endif
# ----- Main settings -----
diff --git a/test/confs/4050 b/test/confs/4050
index c26b7a9..fd3d7db 100644
--- a/test/confs/4050
+++ b/test/confs/4050
@@ -25,6 +25,9 @@ tls_advertise_hosts =
.ifdef _HAVE_DMARC
dmarc_tld_file =
.endif
+.ifdef _OPT_MAIN_LIMITS_ADVERTISE_HOSTS
+limits_advertise_hosts = !*
+.endif
pipelining_connect_advertise_hosts = CONNECTCOND
retry_data_expire = RETRY
diff --git a/test/confs/4710 b/test/confs/4710
new file mode 100644
index 0000000..017fb56
--- /dev/null
+++ b/test/confs/4710
@@ -0,0 +1,36 @@
+# Exim test configuration 4710
+#
+
+exim_path = EXIM_PATH
+keep_environment = USER
+host_lookup_order = bydns
+primary_hostname = myhost.test.ex
+spool_directory = DIR/spool
+log_file_path = DIR/spool/log/%slog
+gecos_pattern = ""
+gecos_name = CALLER_NAME
+tls_advertise_hosts =
+.ifdef _HAVE_PIPE_CONNECT
+pipelining_connect_advertise_hosts = :
+.endif
+.ifdef _HAVE_DMARC
+dmarc_tld_file =
+.endif
+
+# ----- Main settings -----
+
+.ifdef CONTROL
+limits_advertise_hosts = :
+.endif
+.ifdef MAXNM
+smtp_accept_max_per_connection = ${if eq {127.0.0.1}{$sender_host_address} {MAXNM}{44}}
+.endif
+.ifdef RCPT_MSG
+recipients_max = RCPT_MSG
+.endif
+
+# ----- ACL -----
+
+begin acl
+
+# End
diff --git a/test/confs/4711 b/test/confs/4711
new file mode 100644
index 0000000..23f141d
--- /dev/null
+++ b/test/confs/4711
@@ -0,0 +1,31 @@
+# Exim test configuration 4711
+
+.include DIR/aux-var/std_conf_prefix
+
+
+# ----- Main settings -----
+
+acl_smtp_rcpt = accept
+
+# ----- Routers -----
+
+begin routers
+
+send_to_server:
+ driver = accept
+ transport = to_server
+
+# ----- Transports -----
+
+begin transports
+
+to_server:
+ driver = smtp
+ allow_localhost
+ hosts = 127.0.0.1
+ port = PORT_D
+.ifdef RCPT_MSG
+ max_rcpt = RCPT_MSG
+.endif
+
+# End
diff --git a/test/confs/4712 b/test/confs/4712
new file mode 100644
index 0000000..c48be50
--- /dev/null
+++ b/test/confs/4712
@@ -0,0 +1,28 @@
+# Exim test configuration 4712
+
+.include DIR/aux-var/std_conf_prefix
+
+
+# ----- Main settings -----
+
+acl_smtp_rcpt = accept
+
+# ----- Routers -----
+
+begin routers
+
+send_to_server:
+ driver = accept
+ transport = to_server
+
+# ----- Transports -----
+
+begin transports
+
+to_server:
+ driver = smtp
+ allow_localhost
+ hosts = 127.0.0.1
+ port = PORT_D
+
+# End
diff --git a/test/confs/4713 b/test/confs/4713
new file mode 100644
index 0000000..c5394fb
--- /dev/null
+++ b/test/confs/4713
@@ -0,0 +1,31 @@
+# Exim test configuration 4711
+
+.include DIR/aux-var/std_conf_prefix
+
+
+# ----- Main settings -----
+
+acl_smtp_rcpt = accept
+
+# ----- Routers -----
+
+begin routers
+
+send_to_server:
+ driver = accept
+ transport = to_server
+
+# ----- Transports -----
+
+begin transports
+
+to_server:
+ driver = smtp
+ allow_localhost
+ hosts = 127.0.0.1
+ port = PORT_D
+.ifdef CONTROL
+ multi_domain = false
+.endif
+
+# End
diff --git a/test/confs/4714 b/test/confs/4714
new file mode 100644
index 0000000..dd06bd8
--- /dev/null
+++ b/test/confs/4714
@@ -0,0 +1,36 @@
+# Exim test configuration 4714
+
+.include DIR/aux-var/std_conf_prefix
+
+
+# ----- Main settings -----
+
+log_selector = +received_recipients
+queue_run_in_order
+
+acl_smtp_rcpt = accept
+
+# ----- Routers -----
+
+begin routers
+
+send_to_server:
+ driver = accept
+ transport = to_server
+
+# ----- Transports -----
+
+begin transports
+
+to_server:
+ driver = smtp
+ allow_localhost
+ hosts = 127.0.0.1
+ port = PORT_D
+
+# ----- Retry -----
+
+begin retry
+* * F,5d,10s
+
+# End
diff --git a/test/confs/5670 b/test/confs/5670
index d4bedac..f425266 100644
--- a/test/confs/5670
+++ b/test/confs/5670
@@ -32,11 +32,11 @@ tls_ocsp_file = PEM DIR/tmp/ocsp/double_r.ocsp.pem
.ifdef _HAVE_GNUTLS
-tls_require_ciphers = ${if eq {LIMIT}{TLS1.2} {NORMAL:!VERS-ALL:+VERS-TLS1.2} {}}
+tls_require_ciphers = ${if eq {TRUSTED}{TLS1.2} {NORMAL:!VERS-ALL:+VERS-TLS1.2} {}}
.endif
.ifdef _HAVE_OPENSSL
-.ifdef LIMIT
-openssl_options = ${if eq {LIMIT}{TLS1.2} {+no_tlsv1_3} {}}
+.ifdef TRUSTED
+openssl_options = ${if eq {TRUSTED}{TLS1.2} {+no_tlsv1_3} {}}
.endif
.endif
@@ -81,7 +81,7 @@ remote_delivery:
hosts_require_tls = *
.ifdef _HAVE_GNUTLS
- tls_require_ciphers = ${if eq {LIMIT}{TLS1.2} \
+ tls_require_ciphers = ${if eq {TRUSTED}{TLS1.2} \
{NONE:\
+SIGN-RSA-SHA256:+VERS-TLS-ALL:+ECDHE-RSA:+DHE-RSA:+RSA\
:+CIPHER-ALL:+MAC-ALL:+COMP-NULL:+CURVE-ALL:+CTYPE-X509} \
diff --git a/test/log/4710 b/test/log/4710
new file mode 100644
index 0000000..6e70d69
--- /dev/null
+++ b/test/log/4710
@@ -0,0 +1,6 @@
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port PORT_D
diff --git a/test/log/4711 b/test/log/4711
new file mode 100644
index 0000000..520ad1f
--- /dev/null
+++ b/test/log/4711
@@ -0,0 +1,24 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@??? U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaX-0005vi-00 => a@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1] C="250 message received"
+1999-03-02 09:44:33 10HmaX-0005vi-00 -> b@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1] C="250 message received"
+1999-03-02 09:44:33 10HmaX-0005vi-00 -> c@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1] C="250 message received"
+1999-03-02 09:44:33 10HmaX-0005vi-00 -> d@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1] C="250 message received"
+1999-03-02 09:44:33 10HmaX-0005vi-00 -> e@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1] C="250 message received"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@??? U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaY-0005vi-00 => a@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1] C="250 message received"
+1999-03-02 09:44:33 10HmaY-0005vi-00 -> b@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1] C="250 message received"
+1999-03-02 09:44:33 10HmaY-0005vi-00 => c@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1]* C="250 message received"
+1999-03-02 09:44:33 10HmaY-0005vi-00 -> d@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1]* C="250 message received"
+1999-03-02 09:44:33 10HmaY-0005vi-00 => e@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1]* C="250 message received"
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@??? U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => a@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1] C="250 message received"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 -> b@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1] C="250 message received"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => c@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1]* C="250 message received"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@??? U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmbA-0005vi-00 => a@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1] C="250 message received"
+1999-03-02 09:44:33 10HmbA-0005vi-00 -> b@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1] C="250 message received"
+1999-03-02 09:44:33 10HmbA-0005vi-00 => c@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1] C="250 message received"
+1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
diff --git a/test/log/4712 b/test/log/4712
new file mode 100644
index 0000000..8479516
--- /dev/null
+++ b/test/log/4712
@@ -0,0 +1,8 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@??? U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaX-0005vi-00 => a@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1] C="250 message received"
+1999-03-02 09:44:33 10HmaX-0005vi-00 => b@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1]* C="250 message received"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@??? U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaY-0005vi-00 => a@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1] C="250 message received"
+1999-03-02 09:44:33 10HmaY-0005vi-00 => b@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1] C="250 message received"
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
diff --git a/test/log/4713 b/test/log/4713
new file mode 100644
index 0000000..09a56f3
--- /dev/null
+++ b/test/log/4713
@@ -0,0 +1,11 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@??? U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaX-0005vi-00 => a@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1] C="250 message received"
+1999-03-02 09:44:33 10HmaX-0005vi-00 -> b@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1] C="250 message received"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@??? U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaY-0005vi-00 => a@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1] C="250 message received"
+1999-03-02 09:44:33 10HmaY-0005vi-00 => b@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1]* C="250 second message received"
+1999-03-02 09:44:33 10HmaY-0005vi-00 => c@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1]* C="250 third message received"
+1999-03-02 09:44:33 10HmaY-0005vi-00 -> a2@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1]* C="250 message received"
+1999-03-02 09:44:33 10HmaY-0005vi-00 -> b2@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1]* C="250 second message received"
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
diff --git a/test/log/4714 b/test/log/4714
new file mode 100644
index 0000000..f97d89f
--- /dev/null
+++ b/test/log/4714
@@ -0,0 +1,14 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@??? U=CALLER P=local S=sss for r1_1.test.ex r1_2.test.ex
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@??? U=CALLER P=local S=sss for r2_1.test.ex r2_2.test.ex
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@??? U=CALLER P=local S=sss for r3_1.test.ex r3_2.test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp -qq
+1999-03-02 09:44:33 10HmaX-0005vi-00 => r1_1.test.ex@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1] C="250 message 1 received"
+1999-03-02 09:44:33 10HmaX-0005vi-00 => r1_2.test.ex@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1]* C="250 message 2 received"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => r3_1.test.ex@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1]* C="250 message 3 received"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => r3_2.test.ex@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1]* C="250 message 4 received"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
+1999-03-02 09:44:33 10HmaY-0005vi-00 => r2_1.test.ex@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1]* C="250 message 5 received"
+1999-03-02 09:44:33 10HmaY-0005vi-00 => r2_2.test.ex@??? R=send_to_server T=to_server H=127.0.0.1 [127.0.0.1] C="250 message 6 received"
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp -qq
diff --git a/test/runtest b/test/runtest
index 8ebba50..8f49b26 100755
--- a/test/runtest
+++ b/test/runtest
@@ -1020,6 +1020,9 @@ RESET_AFTER_EXTRA_LINE_READ:
# ARC is not always supported by the build
next if /^arc_sign =/;
+ # LIMITS is not always supported by the build
+ next if /^limits_advertise_hosts =/;
+
# TLS resumption is not always supported by the build
next if /^tls_resumption_hosts =/;
next if /^-tls_resumption/;
@@ -1285,6 +1288,9 @@ RESET_AFTER_EXTRA_LINE_READ:
# Experimental_REQUIRETLS
next if / in tls_advertise_requiretls?\? no \(end of list\)/;
+ # Experimental_LIMITS
+ next if / in limits_advertise_hosts?\? no \(matched "!\*"\)/;
+
# TCP Fast Open
next if /^(ppppp )?setsockopt FASTOPEN: Network Error/;
diff --git a/test/scripts/0000-Basic/0453 b/test/scripts/0000-Basic/0453
index 8c199fe..b07a712 100644
--- a/test/scripts/0000-Basic/0453
+++ b/test/scripts/0000-Basic/0453
@@ -9,7 +9,7 @@ helo
helo
****
1
-exim -DLIMIT=smtp_max_synprot_errors=1 -bs
+exim -DERROR_DETAILS=smtp_max_synprot_errors=1 -bs
mail from:<>
mail from:<>
mail from:<>
diff --git a/test/scripts/4710-esmtp-limits/4710 b/test/scripts/4710-esmtp-limits/4710
new file mode 100644
index 0000000..875613c
--- /dev/null
+++ b/test/scripts/4710-esmtp-limits/4710
@@ -0,0 +1,86 @@
+# ESMTP LIMITS extension, server
+#
+# Baseline: advertised by default
+exim -DSERVER=server -bd -oX PORT_D
+****
+client 127.0.0.1 PORT_D
+??? 220
+EHLO tester
+??? 250-
+??? 250-SIZE
+??? 250-LIMITS MAILMAX=1000
+??? 250
+****
+killdaemon
+#
+# not advertised when disabled
+exim -DSERVER=server -DCONTROL=disable -bd -oX PORT_D
+****
+client 127.0.0.1 PORT_D
+??? 220
+EHLO tester
+??? 250-
+??? 250-SIZE
+??? 250-8BITMIME
+****
+killdaemon
+#
+# smtp_accept_max_per_connection controls the MAILMAX value advertised, and is expanded
+exim -DSERVER=server -DMAXNM=42 -bd -oX PORT_D
+****
+client 127.0.0.1 PORT_D
+??? 220
+EHLO tester
+??? 250-
+??? 250-SIZE
+??? 250-LIMITS MAILMAX=42
+??? 250
+****
+client HOSTIPV4 PORT_D
+??? 220
+EHLO tester
+??? 250-
+??? 250-SIZE
+??? 250-LIMITS MAILMAX=44
+??? 250
+****
+killdaemon
+#
+#
+# not advertised when zero and no RCPTMAX
+exim -DSERVER=server -DMAXNM=0 -bd -oX PORT_D
+****
+client 127.0.0.1 PORT_D
+??? 220
+EHLO tester
+??? 250-
+??? 250-SIZE
+??? 250
+****
+killdaemon
+#
+# reeipients_max controls an advertised RCPTMAX
+exim -DSERVER=server -DRCPT_MSG=5 -bd -oX PORT_D
+****
+client 127.0.0.1 PORT_D
+??? 220
+EHLO tester
+??? 250-
+??? 250-SIZE
+??? 250-LIMITS MAILMAX=1000 RCPTMAX=5
+??? 250
+****
+killdaemon
+#
+# RCPTMAX can appear on its own
+exim -DSERVER=server -DMAXNM=0 -DRCPT_MSG=5 -bd -oX PORT_D
+****
+client 127.0.0.1 PORT_D
+??? 220
+EHLO tester
+??? 250-
+??? 250-SIZE
+??? 250-LIMITS RCPTMAX=5
+??? 250
+****
+killdaemon
diff --git a/test/scripts/4710-esmtp-limits/4711 b/test/scripts/4710-esmtp-limits/4711
new file mode 100644
index 0000000..ffbfa07
--- /dev/null
+++ b/test/scripts/4710-esmtp-limits/4711
@@ -0,0 +1,138 @@
+# ESMTP LIMITS extension, client RCPTMAX
+#
+# Baseline: no RCPTMAX advertised, can send 5 RCPT commands
+server PORT_D
+220 Hi there
+EHLO
+250-yeah mate
+250 LIMITS MAILMAX=10
+MAIL FROM
+250 mail cmd good
+RCPT TO
+250 rcpt cmd 1 good
+RCPT TO
+250 rcpt cmd 2 good
+RCPT TO
+250 rcpt cmd 3 good
+RCPT TO
+250 rcpt cmd 4 good
+RCPT TO
+250 rcpt cmd 5 good
+DATA
+352 go ahead
+.
+250 message received
+QUIT
+220 bye
+****
+exim -odi a@??? b@??? c@??? d@??? e@???
+****
+#
+# RCPTMAX advertised, limits RCPT commands
+# Client should immediate-retry fusther MAIL transaction for remaning rcpts
+server PORT_D
+220 Hi there
+EHLO
+250-yeah mate
+250 LIMITS RCPTMAX=2
+MAIL FROM
+250 mail cmd good
+RCPT TO
+250 rcpt cmd 1 good
+RCPT TO
+250 rcpt cmd 2 good
+DATA
+352 go ahead
+.
+250 message received
+MAIL FROM
+250 mail cmd good
+RCPT TO
+250 rcpt cmd 3 good
+RCPT TO
+250 rcpt cmd 4 good
+DATA
+352 go ahead
+.
+250 message received
+MAIL FROM
+250 mail cmd good
+RCPT TO
+250 rcpt cmd 5 good
+DATA
+352 go ahead
+.
+250 message received
+QUIT
+220 bye
+****
+exim -odi a@??? b@??? c@??? d@??? e@???
+****
+#
+# RCPTMAX advertised, overrides larger tpt max_rcpt and limits RCPT commands
+server PORT_D
+220 Hi there
+EHLO
+250-yeah mate
+250 LIMITS RCPTMAX=2
+MAIL FROM
+250 mail cmd good
+RCPT TO
+250 rcpt cmd 1 good
+RCPT TO
+250 rcpt cmd 2 good
+DATA
+352 go ahead
+.
+250 message received
+MAIL FROM
+250 mail cmd good
+RCPT TO
+250 rcpt cmd 3 good
+DATA
+352 go ahead
+.
+250 message received
+QUIT
+220 bye
+****
+exim -odi -DRCPT_MSG=3 a@??? b@??? c@???
+****
+#
+# RCPTMAX advertised, does not override smaller tpt max_rcpt which limits RCPT commands
+# Client make a separate conn for the second transaction
+server PORT_D 2
+220 Hi there
+EHLO
+250-yeah mate
+250 LIMITS RCPTMAX=3
+MAIL FROM
+250 mail cmd good
+RCPT TO
+250 rcpt cmd 1 good
+RCPT TO
+250 rcpt cmd 2 good
+DATA
+352 go ahead
+.
+250 message received
+QUIT
+220 bye
+*eof
+220 Hi there
+EHLO
+250-yeah mate
+250 LIMITS RCPTMAX=3
+MAIL FROM
+250 mail cmd good
+RCPT TO
+250 rcpt cmd 3 good
+DATA
+352 go ahead
+.
+250 message received
+QUIT
+220 bye
+****
+exim -odi -DRCPT_MSG=2 a@??? b@??? c@???
+****
diff --git a/test/scripts/4710-esmtp-limits/4712 b/test/scripts/4710-esmtp-limits/4712
new file mode 100644
index 0000000..c5d1cab
--- /dev/null
+++ b/test/scripts/4710-esmtp-limits/4712
@@ -0,0 +1,65 @@
+# ESMTP LIMITS extension, client MAILMAX
+#
+# Baseline: no MAILMAX advertised, can send 2 messages
+# - limiting the RCPT to 1 is convenient to get the multiple messages
+server PORT_D
+220 Hi there
+EHLO
+250-yeah mate
+250 LIMITS RCPTMAX=1
+MAIL FROM
+250 mail cmd 1 good
+RCPT TO
+250 rcpt cmd good
+DATA
+352 go ahead
+.
+250 message received
+MAIL FROM
+250 mail cmd 2 good
+RCPT TO
+250 rcpt cmd good
+DATA
+352 go ahead
+.
+250 message received
+QUIT
+220 bye
+****
+exim -odi a@??? b@???
+****
+#
+# limited to one MAIL per conn. Client should immediate-retry a second one.
+server PORT_D 2
+220 Hi there
+EHLO
+250-yeah mate
+250 LIMITS RCPTMAX=1 MAILMAX=1
+MAIL FROM
+250 mail cmd 1 good
+RCPT TO
+250 rcpt cmd good
+DATA
+352 go ahead
+.
+250 message received
+QUIT
+220 bye
+*eof
+220 Hi there
+EHLO
+250-yeah mate
+250 LIMITS RCPTMAX=1 MAILMAX=1
+MAIL FROM
+250 mail cmd 2 good
+RCPT TO
+250 rcpt cmd good
+DATA
+352 go ahead
+.
+250 message received
+QUIT
+220 bye
+****
+exim -odi a@??? b@???
+****
diff --git a/test/scripts/4710-esmtp-limits/4713 b/test/scripts/4710-esmtp-limits/4713
new file mode 100644
index 0000000..6003f27
--- /dev/null
+++ b/test/scripts/4710-esmtp-limits/4713
@@ -0,0 +1,64 @@
+# ESMTP LIMITS extension, client RCPTDOMAINMAX Limit
+#
+# Baseline: no RCPTDOMAINMAX Limit advertised, can send RCPT commands with distinct domains
+server PORT_D
+220 Hi there
+EHLO
+250-yeah mate
+250 LIMITS MAILMAX=10
+MAIL FROM
+250 mail cmd good
+RCPT TO
+250 rcpt cmd 1 good
+RCPT TO
+250 rcpt cmd 2 good
+DATA
+352 go ahead
+.
+250 message received
+QUIT
+220 bye
+****
+exim -odi a@??? b@???
+****
+#
+# RCPTDOMAINMAX Limit advertised, second domain temp-rejected
+# Client should immediate-retry further MAIL transactions for remaining rcpts
+server PORT_D
+220 Hi there
+EHLO
+250-yeah mate
+250 LIMITS MAILMAX=10 RCPTDOMAINMAX=100
+MAIL FROM
+250 mail cmd good
+RCPT TO:<a@???>
+250 rcpt cmd 1 good
+RCPT TO:<a2@???>
+250 rcpt cmd 2 good
+DATA
+352 go ahead
+.
+250 message received
+MAIL FROM
+250 second mail cmd good
+RCPT TO:<b@???>
+250 rcpt cmd 1 good
+RCPT TO
+250 rcpt cmd 2 good
+DATA
+352 go ahead
+.
+250 second message received
+MAIL FROM
+250 third mail cmd good
+RCPT TO:<c@???>
+250 rcpt cmd 1 good
+DATA
+352 go ahead
+.
+250 third message received
+QUIT
+220 bye
+****
+exim -odi a@??? b@??? c@??? a2@??? b2@???
+****
diff --git a/test/scripts/4710-esmtp-limits/4714 b/test/scripts/4710-esmtp-limits/4714
new file mode 100644
index 0000000..f97d989
--- /dev/null
+++ b/test/scripts/4710-esmtp-limits/4714
@@ -0,0 +1,84 @@
+# ESMTP LIMITS extension, client continued-connection
+#
+# queue up 3 messages each with 2 recipients
+exim -odq r1_1.test.ex r1_2.test.ex
+Subject: message 1
+****
+exim -odq r2_1.test.ex r2_2.test.ex
+Subject: message 2
+****
+exim -odq r3_1.test.ex r3_2.test.ex
+Subject: message 3
+****
+#
+# Handed limits of 5 MAIL, 1 RCPT, expect to use 5 transactions in a one connection
+# when the client does a 2-phase queue run, followed by one transaction in one connection
+# from the same queue run.
+# The second pair and third initial should be from continued-connection trasports, flagged by the log lines.
+server PORT_D 2
+220 Hi there
+EHLO
+250-yeah mate
+250 LIMITS MAILMAX=5 RCPTMAX=1
+MAIL FROM
+250 mail cmd 1 good
+RCPT TO
+250 rcpt cmd good
+DATA
+352 go ahead
+.
+250 message 1 received
+MAIL FROM
+250 mail cmd 2 good
+RCPT TO
+250 rcpt cmd good
+DATA
+352 go ahead
+.
+250 message 2 received
+MAIL FROM
+250 mail cmd 3 good
+RCPT TO
+250 rcpt cmd good
+DATA
+352 go ahead
+.
+250 message 3 received
+MAIL FROM
+250 mail cmd 4 good
+RCPT TO
+250 rcpt cmd good
+DATA
+352 go ahead
+.
+250 message 4 received
+MAIL FROM
+250 mail cmd 5 good
+RCPT TO
+250 rcpt cmd good
+DATA
+352 go ahead
+.
+250 message 5 received
+QUIT
+221 bye
+*eof
+220 Hi there
+EHLO
+250-yeah mate
+250
+MAIL FROM
+250 mail cmd 1 good
+RCPT TO
+250 rcpt cmd good
+DATA
+352 go ahead
+.
+250 message 6 received
+QUIT
+221 bye
+*eof
+****
+#
+exim -qq
+****
diff --git a/test/scripts/4710-esmtp-limits/REQUIRES b/test/scripts/4710-esmtp-limits/REQUIRES
new file mode 100644
index 0000000..4817265
--- /dev/null
+++ b/test/scripts/4710-esmtp-limits/REQUIRES
@@ -0,0 +1 @@
+support Experimental_ESMTP_Limits
diff --git a/test/scripts/5670-OCSP-GnuTLS-1.3/5670 b/test/scripts/5670-OCSP-GnuTLS-1.3/5670
index 1df75fb..1ff6d81 100644
--- a/test/scripts/5670-OCSP-GnuTLS-1.3/5670
+++ b/test/scripts/5670-OCSP-GnuTLS-1.3/5670
@@ -14,10 +14,10 @@ system 'cat server1.example.com/server1.example.com.ocsp.signernocert.good.resp.
exim -z '1: TLS1.2 Server sends good leaf-staple on request, to client requiring RSA auth'
****
#
-sudo exim -bd -oX PORT_D -DSERVER=server -DLIMIT=TLS1.2
+sudo exim -bd -oX PORT_D -DSERVER=server -DTRUSTED=TLS1.2
****
#
-exim -odf -DOPT=rsa -DLIMIT=TLS1.2 rsa.auth@???
+exim -odf -DOPT=rsa -DTRUSTED=TLS1.2 rsa.auth@???
Subject: test
.
@@ -29,7 +29,7 @@ exim -z '2: TLS1.3 Server sends good 3-element staple on request, to client requ
****
#
# Prefix with sudo to get SSLKEYLOGFILE to work. Only works on the server.
-exim -bd -oX PORT_D -DSERVER=server -DLIMIT=TLS1.3
+exim -bd -oX PORT_D -DSERVER=server -DTRUSTED=TLS1.3
****
exim -odf -DOPT=rsa rsa.auth@???
Subject: test
@@ -43,7 +43,7 @@ killdaemon
exim -z '3: TLS1.3 Server sends bad nonleaf staple, client detects it'
****
#
-EXIM_TESTHARNESS_DISABLE_OCSPVALIDITYCHECK=y exim -bd -oX PORT_D -DSERVER=server -DLIMIT=TLS1.3 -DCONTROL=bad
+EXIM_TESTHARNESS_DISABLE_OCSPVALIDITYCHECK=y exim -bd -oX PORT_D -DSERVER=server -DTRUSTED=TLS1.3 -DCONTROL=bad
****
exim -odf -DOPT=rsa rsa.auth@???
Subject: test
diff --git a/test/stdout/4710 b/test/stdout/4710
new file mode 100644
index 0000000..35a51c4
--- /dev/null
+++ b/test/stdout/4710
@@ -0,0 +1,87 @@
+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 tester
+??? 250-
+<<< 250-myhost.test.ex Hello tester [127.0.0.1]
+??? 250-SIZE
+<<< 250-SIZE 52428800
+??? 250-LIMITS MAILMAX=1000
+<<< 250-LIMITS MAILMAX=1000
+??? 250
+<<< 250-8BITMIME
+End of script
+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 tester
+??? 250-
+<<< 250-myhost.test.ex Hello tester [127.0.0.1]
+??? 250-SIZE
+<<< 250-SIZE 52428800
+??? 250-8BITMIME
+<<< 250-8BITMIME
+End of script
+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 tester
+??? 250-
+<<< 250-myhost.test.ex Hello tester [127.0.0.1]
+??? 250-SIZE
+<<< 250-SIZE 52428800
+??? 250-LIMITS MAILMAX=42
+<<< 250-LIMITS MAILMAX=42
+??? 250
+<<< 250-8BITMIME
+End of script
+Connecting to ip4.ip4.ip4.ip4 port 1225 ... connected
+??? 220
+<<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+>>> EHLO tester
+??? 250-
+<<< 250-myhost.test.ex Hello tester [ip4.ip4.ip4.ip4]
+??? 250-SIZE
+<<< 250-SIZE 52428800
+??? 250-LIMITS MAILMAX=44
+<<< 250-LIMITS MAILMAX=44
+??? 250
+<<< 250-8BITMIME
+End of script
+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 tester
+??? 250-
+<<< 250-myhost.test.ex Hello tester [127.0.0.1]
+??? 250-SIZE
+<<< 250-SIZE 52428800
+??? 250
+<<< 250-8BITMIME
+End of script
+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 tester
+??? 250-
+<<< 250-myhost.test.ex Hello tester [127.0.0.1]
+??? 250-SIZE
+<<< 250-SIZE 52428800
+??? 250-LIMITS MAILMAX=1000 RCPTMAX=5
+<<< 250-LIMITS MAILMAX=1000 RCPTMAX=5
+??? 250
+<<< 250-8BITMIME
+End of script
+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 tester
+??? 250-
+<<< 250-myhost.test.ex Hello tester [127.0.0.1]
+??? 250-SIZE
+<<< 250-SIZE 52428800
+??? 250-LIMITS RCPTMAX=5
+<<< 250-LIMITS RCPTMAX=5
+??? 250
+<<< 250-8BITMIME
+End of script
diff --git a/test/stdout/4711 b/test/stdout/4711
new file mode 100644
index 0000000..78b5203
--- /dev/null
+++ b/test/stdout/4711
@@ -0,0 +1,183 @@
+
+******** SERVER ********
+Listening on port 1225 ...
+Connection request from [127.0.0.1]
+220 Hi there
+EHLO the.local.host.name
+250-yeah mate
+250 LIMITS MAILMAX=10
+MAIL FROM:<CALLER@???>
+250 mail cmd good
+RCPT TO:<a@???>
+250 rcpt cmd 1 good
+RCPT TO:<b@???>
+250 rcpt cmd 2 good
+RCPT TO:<c@???>
+250 rcpt cmd 3 good
+RCPT TO:<d@???>
+250 rcpt cmd 4 good
+RCPT TO:<e@???>
+250 rcpt cmd 5 good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+ (envelope-from <CALLER@???>)
+ id 10HmaX-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaX-0005vi-00@???>
+From: CALLER_NAME <CALLER@???>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message received
+QUIT
+220 bye
+End of script
+Listening on port 1225 ...
+Connection request from [127.0.0.1]
+220 Hi there
+EHLO the.local.host.name
+250-yeah mate
+250 LIMITS RCPTMAX=2
+MAIL FROM:<CALLER@???>
+250 mail cmd good
+RCPT TO:<a@???>
+250 rcpt cmd 1 good
+RCPT TO:<b@???>
+250 rcpt cmd 2 good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+ (envelope-from <CALLER@???>)
+ id 10HmaY-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaY-0005vi-00@???>
+From: CALLER_NAME <CALLER@???>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message received
+MAIL FROM:<CALLER@???>
+250 mail cmd good
+RCPT TO:<c@???>
+250 rcpt cmd 3 good
+RCPT TO:<d@???>
+250 rcpt cmd 4 good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+ (envelope-from <CALLER@???>)
+ id 10HmaY-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaY-0005vi-00@???>
+From: CALLER_NAME <CALLER@???>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message received
+MAIL FROM:<CALLER@???>
+250 mail cmd good
+RCPT TO:<e@???>
+250 rcpt cmd 5 good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+ (envelope-from <CALLER@???>)
+ id 10HmaY-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaY-0005vi-00@???>
+From: CALLER_NAME <CALLER@???>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message received
+QUIT
+220 bye
+End of script
+Listening on port 1225 ...
+Connection request from [127.0.0.1]
+220 Hi there
+EHLO the.local.host.name
+250-yeah mate
+250 LIMITS RCPTMAX=2
+MAIL FROM:<CALLER@???>
+250 mail cmd good
+RCPT TO:<a@???>
+250 rcpt cmd 1 good
+RCPT TO:<b@???>
+250 rcpt cmd 2 good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+ (envelope-from <CALLER@???>)
+ id 10HmaZ-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaZ-0005vi-00@???>
+From: CALLER_NAME <CALLER@???>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message received
+MAIL FROM:<CALLER@???>
+250 mail cmd good
+RCPT TO:<c@???>
+250 rcpt cmd 3 good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+ (envelope-from <CALLER@???>)
+ id 10HmaZ-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaZ-0005vi-00@???>
+From: CALLER_NAME <CALLER@???>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message received
+QUIT
+220 bye
+End of script
+Listening on port 1225 ...
+Connection request from [127.0.0.1]
+220 Hi there
+EHLO the.local.host.name
+250-yeah mate
+250 LIMITS RCPTMAX=3
+MAIL FROM:<CALLER@???>
+250 mail cmd good
+RCPT TO:<a@???>
+250 rcpt cmd 1 good
+RCPT TO:<b@???>
+250 rcpt cmd 2 good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+ (envelope-from <CALLER@???>)
+ id 10HmbA-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmbA-0005vi-00@???>
+From: CALLER_NAME <CALLER@???>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message received
+QUIT
+220 bye
+Expected EOF read from client
+Listening on port 1225 ...
+Connection request from [127.0.0.1]
+220 Hi there
+EHLO the.local.host.name
+250-yeah mate
+250 LIMITS RCPTMAX=3
+MAIL FROM:<CALLER@???>
+250 mail cmd good
+RCPT TO:<c@???>
+250 rcpt cmd 3 good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+ (envelope-from <CALLER@???>)
+ id 10HmbA-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmbA-0005vi-00@???>
+From: CALLER_NAME <CALLER@???>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message received
+QUIT
+220 bye
+End of script
diff --git a/test/stdout/4712 b/test/stdout/4712
new file mode 100644
index 0000000..69be8af
--- /dev/null
+++ b/test/stdout/4712
@@ -0,0 +1,89 @@
+
+******** SERVER ********
+Listening on port 1225 ...
+Connection request from [127.0.0.1]
+220 Hi there
+EHLO the.local.host.name
+250-yeah mate
+250 LIMITS RCPTMAX=1
+MAIL FROM:<CALLER@???>
+250 mail cmd 1 good
+RCPT TO:<a@???>
+250 rcpt cmd good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+ (envelope-from <CALLER@???>)
+ id 10HmaX-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaX-0005vi-00@???>
+From: CALLER_NAME <CALLER@???>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message received
+MAIL FROM:<CALLER@???>
+250 mail cmd 2 good
+RCPT TO:<b@???>
+250 rcpt cmd good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+ (envelope-from <CALLER@???>)
+ id 10HmaX-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaX-0005vi-00@???>
+From: CALLER_NAME <CALLER@???>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message received
+QUIT
+220 bye
+End of script
+Listening on port 1225 ...
+Connection request from [127.0.0.1]
+220 Hi there
+EHLO the.local.host.name
+250-yeah mate
+250 LIMITS RCPTMAX=1 MAILMAX=1
+MAIL FROM:<CALLER@???>
+250 mail cmd 1 good
+RCPT TO:<a@???>
+250 rcpt cmd good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+ (envelope-from <CALLER@???>)
+ id 10HmaY-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaY-0005vi-00@???>
+From: CALLER_NAME <CALLER@???>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message received
+QUIT
+220 bye
+Expected EOF read from client
+Listening on port 1225 ...
+Connection request from [127.0.0.1]
+220 Hi there
+EHLO the.local.host.name
+250-yeah mate
+250 LIMITS RCPTMAX=1 MAILMAX=1
+MAIL FROM:<CALLER@???>
+250 mail cmd 2 good
+RCPT TO:<b@???>
+250 rcpt cmd good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+ (envelope-from <CALLER@???>)
+ id 10HmaY-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaY-0005vi-00@???>
+From: CALLER_NAME <CALLER@???>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message received
+QUIT
+220 bye
+End of script
diff --git a/test/stdout/4713 b/test/stdout/4713
new file mode 100644
index 0000000..02b10ea
--- /dev/null
+++ b/test/stdout/4713
@@ -0,0 +1,86 @@
+
+******** SERVER ********
+Listening on port 1225 ...
+Connection request from [127.0.0.1]
+220 Hi there
+EHLO the.local.host.name
+250-yeah mate
+250 LIMITS MAILMAX=10
+MAIL FROM:<CALLER@???>
+250 mail cmd good
+RCPT TO:<a@???>
+250 rcpt cmd 1 good
+RCPT TO:<b@???>
+250 rcpt cmd 2 good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+ (envelope-from <CALLER@???>)
+ id 10HmaX-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaX-0005vi-00@???>
+From: CALLER_NAME <CALLER@???>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message received
+QUIT
+220 bye
+End of script
+Listening on port 1225 ...
+Connection request from [127.0.0.1]
+220 Hi there
+EHLO the.local.host.name
+250-yeah mate
+250 LIMITS MAILMAX=10 RCPTDOMAINMAX=100
+MAIL FROM:<CALLER@???>
+250 mail cmd good
+RCPT TO:<a@???>
+250 rcpt cmd 1 good
+RCPT TO:<a2@???>
+250 rcpt cmd 2 good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+ (envelope-from <CALLER@???>)
+ id 10HmaY-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaY-0005vi-00@???>
+From: CALLER_NAME <CALLER@???>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message received
+MAIL FROM:<CALLER@???>
+250 second mail cmd good
+RCPT TO:<b@???>
+250 rcpt cmd 1 good
+RCPT TO:<b2@???>
+250 rcpt cmd 2 good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+ (envelope-from <CALLER@???>)
+ id 10HmaY-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaY-0005vi-00@???>
+From: CALLER_NAME <CALLER@???>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 second message received
+MAIL FROM:<CALLER@???>
+250 third mail cmd good
+RCPT TO:<c@???>
+250 rcpt cmd 1 good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+ (envelope-from <CALLER@???>)
+ id 10HmaY-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaY-0005vi-00@???>
+From: CALLER_NAME <CALLER@???>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 third message received
+QUIT
+220 bye
+End of script
diff --git a/test/stdout/4714 b/test/stdout/4714
new file mode 100644
index 0000000..f7d8e22
--- /dev/null
+++ b/test/stdout/4714
@@ -0,0 +1,117 @@
+
+******** SERVER ********
+Listening on port 1225 ...
+Connection request from [127.0.0.1]
+220 Hi there
+EHLO the.local.host.name
+250-yeah mate
+250 LIMITS MAILMAX=5 RCPTMAX=1
+MAIL FROM:<CALLER@???>
+250 mail cmd 1 good
+RCPT TO:<r1_1.test.ex@???>
+250 rcpt cmd good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+ (envelope-from <CALLER@???>)
+ id 10HmaX-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Subject: message 1
+Message-Id: <E10HmaX-0005vi-00@???>
+From: CALLER_NAME <CALLER@???>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message 1 received
+MAIL FROM:<CALLER@???>
+250 mail cmd 2 good
+RCPT TO:<r1_2.test.ex@???>
+250 rcpt cmd good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+ (envelope-from <CALLER@???>)
+ id 10HmaX-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Subject: message 1
+Message-Id: <E10HmaX-0005vi-00@???>
+From: CALLER_NAME <CALLER@???>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message 2 received
+MAIL FROM:<CALLER@???>
+250 mail cmd 3 good
+RCPT TO:<r3_1.test.ex@???>
+250 rcpt cmd good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+ (envelope-from <CALLER@???>)
+ id 10HmaZ-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Subject: message 3
+Message-Id: <E10HmaZ-0005vi-00@???>
+From: CALLER_NAME <CALLER@???>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message 3 received
+MAIL FROM:<CALLER@???>
+250 mail cmd 4 good
+RCPT TO:<r3_2.test.ex@???>
+250 rcpt cmd good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+ (envelope-from <CALLER@???>)
+ id 10HmaZ-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Subject: message 3
+Message-Id: <E10HmaZ-0005vi-00@???>
+From: CALLER_NAME <CALLER@???>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message 4 received
+MAIL FROM:<CALLER@???>
+250 mail cmd 5 good
+RCPT TO:<r2_1.test.ex@???>
+250 rcpt cmd good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+ (envelope-from <CALLER@???>)
+ id 10HmaY-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Subject: message 2
+Message-Id: <E10HmaY-0005vi-00@???>
+From: CALLER_NAME <CALLER@???>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message 5 received
+QUIT
+221 bye
+Expected EOF read from client
+Listening on port 1225 ...
+Connection request from [127.0.0.1]
+220 Hi there
+EHLO the.local.host.name
+250-yeah mate
+250
+MAIL FROM:<CALLER@???>
+250 mail cmd 1 good
+RCPT TO:<r2_2.test.ex@???>
+250 rcpt cmd good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+ (envelope-from <CALLER@???>)
+ id 10HmaY-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Subject: message 2
+Message-Id: <E10HmaY-0005vi-00@???>
+From: CALLER_NAME <CALLER@???>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message 6 received
+QUIT
+221 bye
+Expected EOF read from client
+End of script