Experimental: ESMTP LIMITS extension
authorJeremy Harris <jgh146exb@wizmail.org>
Sun, 18 Apr 2021 00:51:28 +0000 (01:51 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Sun, 18 Apr 2021 00:51:28 +0000 (01:51 +0100)
48 files changed:
doc/doc-docbook/spec.xfpt
doc/doc-txt/experimental-spec.txt
src/src/config.h.defaults
src/src/dbfn.c
src/src/dbstuff.h
src/src/deliver.c
src/src/exim.c
src/src/functions.h
src/src/globals.c
src/src/globals.h
src/src/readconf.c
src/src/smtp_in.c
src/src/transport.c
src/src/transports/smtp.c
src/src/transports/smtp.h
test/aux-var-src/tls_conf_prefix
test/confs/0453
test/confs/0564
test/confs/0900
test/confs/0901
test/confs/0906
test/confs/3414
test/confs/4050
test/confs/4710 [new file with mode: 0644]
test/confs/4711 [new file with mode: 0644]
test/confs/4712 [new file with mode: 0644]
test/confs/4713 [new file with mode: 0644]
test/confs/4714 [new file with mode: 0644]
test/confs/5670
test/log/4710 [new file with mode: 0644]
test/log/4711 [new file with mode: 0644]
test/log/4712 [new file with mode: 0644]
test/log/4713 [new file with mode: 0644]
test/log/4714 [new file with mode: 0644]
test/runtest
test/scripts/0000-Basic/0453
test/scripts/4710-esmtp-limits/4710 [new file with mode: 0644]
test/scripts/4710-esmtp-limits/4711 [new file with mode: 0644]
test/scripts/4710-esmtp-limits/4712 [new file with mode: 0644]
test/scripts/4710-esmtp-limits/4713 [new file with mode: 0644]
test/scripts/4710-esmtp-limits/4714 [new file with mode: 0644]
test/scripts/4710-esmtp-limits/REQUIRES [new file with mode: 0644]
test/scripts/5670-OCSP-GnuTLS-1.3/5670
test/stdout/4710 [new file with mode: 0644]
test/stdout/4711 [new file with mode: 0644]
test/stdout/4712 [new file with mode: 0644]
test/stdout/4713 [new file with mode: 0644]
test/stdout/4714 [new file with mode: 0644]

index 36be62f7a1ed408be112819b4a03b8d7f4ba475c..56da191fa63da6e2d4c8b9e1416bce3d2c55a721 100644 (file)
@@ -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
index 47cd93f3d94f4c9330119047521487b276380144..5e0b044279828c9999a52010885c1e36a9abfeb4 100644 (file)
@@ -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
 --------------------------------------------------------------
index 02031b7df5ef4966cb648d1c4be940fd8b097ad4..e233fb3e5fe078ac8abbcc6eae995f994f65e7d5 100644 (file)
@@ -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
 
index be6a47afcd07ecaf12eded9d46cb7888528fa610..0f56ad5a61fa88187b2ffb4fde187cb9dcfb37ad 100644 (file)
@@ -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:
index 5a8441d6a3da0fbca5030f57b641ab3e2d2366b1..89934154a68e14736c188534cab2ff74c29e65f1 100644 (file)
@@ -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 {
index 0cddec7584a38cf6c635d0fa23b87ed06f9c5bc5..9ebd281fca33d32ba750050d28355d0688bef00f 100644 (file)
@@ -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)
 {
index 1244aee0b8838ab7d8a838b536871fcd27b484db..8526cbbf38bd345543c603f585908498efa810ae 100644 (file)
@@ -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) */
 
index 0f962b313d7ab9eb90fb888384748f4e16d961d4..7d6e3380e290c75fbcfdcdc8ced4dc5d88c1d577 100644 (file)
@@ -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 **);
index 7ee7c38b5833e928398f5e1dd24a8967c58edf08..c45e8a9308d86c7fb23ff25fed9b6222f803d382 100644 (file)
@@ -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;
index 58bf3c42872b2ac448e41cd8d416ca66411a72af..ed7cffb76c047364e35f0d0e77cdabdf23cdfb85 100644 (file)
@@ -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
index 0ae3166c330b91baffc4007d4082fbd4cca168bc..e8e310bebffc910c00f92e266c22d9be267f6fe2 100644 (file)
@@ -200,6 +200,9 @@ static optionlist optionlist_config[] = {
   { "ldap_require_cert",        opt_stringptr,   {&eldap_require_cert} },
   { "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} },
index 2bb15b6985ab28e5b5c8208456c1a06bb0e06fec..d60e7d5c5eec87acf701fa8e3809056c616bcc97 100644 (file)
@@ -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.
index 49a84ccc42edc1c39f168f685b2f9fb27da738e0..d8fd8585523abcd4d25f61401ee538101aaefe28 100644 (file)
@@ -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
index 2a2928c46b32e7f53e61bf5b6ed24e5c34f7350c..52940572be33439cf093d54a16993715773fbafd 100644 (file)
@@ -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:
index 1ea2a4d007ab9ad2b2fe12067a04f60e636ec038..aff3f545288a072199a15c12fd3f6cfa210a30ca 100644 (file)
@@ -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
 
index 541817668b0b57098e9583fe2c5f965b5f11ac32..e3f09b98b6ae83cffa0ea13fe6d7601e97a66e6e 100644 (file)
@@ -24,3 +24,6 @@ pipelining_connect_advertise_hosts = :
 .ifdef _HAVE_DMARC
 dmarc_tld_file =
 .endif
+.ifdef _EXP_LIMITS
+limits_advertise_hosts = !*
+.endif
index 3ecc86591fb36c03373e362c9e7b5348007af542..c611d4a41e94832b7a799532bd8914e22704b106 100644 (file)
@@ -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
index de71325af1d79ebf2561eb842dddc0cdc26f4ea1..fd38c621373dd57255ead67854490e320c97ed14 100644 (file)
@@ -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 -----
 
index 7775fc4b0a44604644f577f1136d1cc4aafb7950..a56ec0e5cb0a5a1e5d4e995f4bf21418feb9e674 100644 (file)
@@ -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 -----
 
index a1f391613e8a5ec1abf80dc242ba5fce9c4acaf2..361a9bf6c415078e41150d9cc649c5f7bfc1cedf 100644 (file)
@@ -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 = :
 
index 5bd5f2a4fc90ee35c9911fae6e6b27a7b162a22e..57f359ff0ee8d2e68dcfe9c31799599784aac8f9 100644 (file)
@@ -16,6 +16,9 @@ pipelining_connect_advertise_hosts =
 .ifdef _HAVE_DMARC
 dmarc_tld_file =
 .endif
+.ifdef _EXP_LIMITS
+limits_advertise_hosts = !*
+.endif
 
 # ----- Main settings -----
 
index 5d0f93c46bc98ecd644926dd88c6be3230451c3a..25205a4c1eda1b3d63eae0218033a5fff75957bf 100644 (file)
@@ -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 -----
 
index c26b7a9c2de5091bb1a5f80225269051e981b94d..fd3d7db54a785cc4e1332982a9ca39317ef35d2d 100644 (file)
@@ -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 (file)
index 0000000..017fb56
--- /dev/null
@@ -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 (file)
index 0000000..23f141d
--- /dev/null
@@ -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 (file)
index 0000000..c48be50
--- /dev/null
@@ -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 (file)
index 0000000..c5394fb
--- /dev/null
@@ -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 (file)
index 0000000..dd06bd8
--- /dev/null
@@ -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
index d4bedac51162022357eb5a240be393e6f4411d77..f4252668e625cf32d82be610ad933a09b2385a90 100644 (file)
@@ -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 (file)
index 0000000..6e70d69
--- /dev/null
@@ -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 (file)
index 0000000..520ad1f
--- /dev/null
@@ -0,0 +1,24 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaX-0005vi-00 => a@test.ex 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@test.ex 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@test.ex 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@test.ex 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@test.ex 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@the.local.host.name U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaY-0005vi-00 => a@test.ex 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@test.ex 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@test.ex 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@test.ex 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@test.ex 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@the.local.host.name U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => a@test.ex 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@test.ex 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@test.ex 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@the.local.host.name U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmbA-0005vi-00 => a@test.ex 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@test.ex 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@test.ex 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 (file)
index 0000000..8479516
--- /dev/null
@@ -0,0 +1,8 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaX-0005vi-00 => a@test.ex 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@test.ex 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@the.local.host.name U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaY-0005vi-00 => a@test.ex 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@test.ex 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 (file)
index 0000000..09a56f3
--- /dev/null
@@ -0,0 +1,11 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaX-0005vi-00 => a@a.test.ex 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@b.test.ex 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@the.local.host.name U=CALLER P=local S=sss
+1999-03-02 09:44:33 10HmaY-0005vi-00 => a@a.test.ex 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@b.test.ex 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@c.test.ex 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@a.test.ex 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@b.test.ex 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 (file)
index 0000000..f97d89f
--- /dev/null
@@ -0,0 +1,14 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@the.local.host.name 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@the.local.host.name 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@the.local.host.name 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@the.local.host.name 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@the.local.host.name 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@the.local.host.name 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@the.local.host.name 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@the.local.host.name 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@the.local.host.name 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
index 8ebba5023efcaf65f99861313d7a53f09514e676..8f49b26d3592b3392a648425186c1783a3fd1921 100755 (executable)
@@ -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/;
 
index 8c199fe39f62457e8a4fc7b78bf9c17f1f26c8ca..b07a7125caf0cc37c3da0380b3ccb7f1dcd3a0ed 100644 (file)
@@ -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 (file)
index 0000000..875613c
--- /dev/null
@@ -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 (file)
index 0000000..ffbfa07
--- /dev/null
@@ -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@test.ex b@test.ex c@test.ex d@test.ex e@test.ex
+****
+#
+# 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@test.ex b@test.ex c@test.ex d@test.ex e@test.ex
+****
+#
+# 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@test.ex b@test.ex c@test.ex
+****
+#
+# 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@test.ex b@test.ex c@test.ex
+****
diff --git a/test/scripts/4710-esmtp-limits/4712 b/test/scripts/4710-esmtp-limits/4712
new file mode 100644 (file)
index 0000000..c5d1cab
--- /dev/null
@@ -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@test.ex b@test.ex
+****
+#
+# 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@test.ex b@test.ex
+****
diff --git a/test/scripts/4710-esmtp-limits/4713 b/test/scripts/4710-esmtp-limits/4713
new file mode 100644 (file)
index 0000000..6003f27
--- /dev/null
@@ -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@a.test.ex b@b.test.ex
+****
+#
+# 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@a.test.ex>
+250 rcpt cmd 1 good
+RCPT TO:<a2@a.test.ex>
+250 rcpt cmd 2 good
+DATA
+352 go ahead
+.
+250 message received
+MAIL FROM
+250 second mail cmd good
+RCPT TO:<b@b.test.ex>
+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@c.test.ex>
+250 rcpt cmd 1 good
+DATA
+352 go ahead
+.
+250 third message received
+QUIT
+220 bye
+****
+exim -odi a@a.test.ex b@b.test.ex c@c.test.ex a2@a.test.ex b2@b.test.ex
+****
diff --git a/test/scripts/4710-esmtp-limits/4714 b/test/scripts/4710-esmtp-limits/4714
new file mode 100644 (file)
index 0000000..f97d989
--- /dev/null
@@ -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 (file)
index 0000000..4817265
--- /dev/null
@@ -0,0 +1 @@
+support Experimental_ESMTP_Limits
index 1df75fbf9129542b2093d74b693a2c55c54f841c..1ff6d81616fcbb9a1f74e9c08570e5ea55714901 100644 (file)
@@ -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@test.ex
+exim -odf -DOPT=rsa -DTRUSTED=TLS1.2 rsa.auth@test.ex
 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@test.ex
 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@test.ex
 Subject: test
diff --git a/test/stdout/4710 b/test/stdout/4710
new file mode 100644 (file)
index 0000000..35a51c4
--- /dev/null
@@ -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 (file)
index 0000000..78b5203
--- /dev/null
@@ -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@the.local.host.name>
+250 mail cmd good
+RCPT TO:<a@test.ex>
+250 rcpt cmd 1 good
+RCPT TO:<b@test.ex>
+250 rcpt cmd 2 good
+RCPT TO:<c@test.ex>
+250 rcpt cmd 3 good
+RCPT TO:<d@test.ex>
+250 rcpt cmd 4 good
+RCPT TO:<e@test.ex>
+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@the.local.host.name>)
+       id 10HmaX-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaX-0005vi-00@the.local.host.name>
+From: CALLER_NAME <CALLER@the.local.host.name>
+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@the.local.host.name>
+250 mail cmd good
+RCPT TO:<a@test.ex>
+250 rcpt cmd 1 good
+RCPT TO:<b@test.ex>
+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@the.local.host.name>)
+       id 10HmaY-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaY-0005vi-00@the.local.host.name>
+From: CALLER_NAME <CALLER@the.local.host.name>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message received
+MAIL FROM:<CALLER@the.local.host.name>
+250 mail cmd good
+RCPT TO:<c@test.ex>
+250 rcpt cmd 3 good
+RCPT TO:<d@test.ex>
+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@the.local.host.name>)
+       id 10HmaY-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaY-0005vi-00@the.local.host.name>
+From: CALLER_NAME <CALLER@the.local.host.name>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message received
+MAIL FROM:<CALLER@the.local.host.name>
+250 mail cmd good
+RCPT TO:<e@test.ex>
+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@the.local.host.name>)
+       id 10HmaY-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaY-0005vi-00@the.local.host.name>
+From: CALLER_NAME <CALLER@the.local.host.name>
+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@the.local.host.name>
+250 mail cmd good
+RCPT TO:<a@test.ex>
+250 rcpt cmd 1 good
+RCPT TO:<b@test.ex>
+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@the.local.host.name>)
+       id 10HmaZ-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaZ-0005vi-00@the.local.host.name>
+From: CALLER_NAME <CALLER@the.local.host.name>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message received
+MAIL FROM:<CALLER@the.local.host.name>
+250 mail cmd good
+RCPT TO:<c@test.ex>
+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@the.local.host.name>)
+       id 10HmaZ-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaZ-0005vi-00@the.local.host.name>
+From: CALLER_NAME <CALLER@the.local.host.name>
+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@the.local.host.name>
+250 mail cmd good
+RCPT TO:<a@test.ex>
+250 rcpt cmd 1 good
+RCPT TO:<b@test.ex>
+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@the.local.host.name>)
+       id 10HmbA-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmbA-0005vi-00@the.local.host.name>
+From: CALLER_NAME <CALLER@the.local.host.name>
+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@the.local.host.name>
+250 mail cmd good
+RCPT TO:<c@test.ex>
+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@the.local.host.name>)
+       id 10HmbA-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmbA-0005vi-00@the.local.host.name>
+From: CALLER_NAME <CALLER@the.local.host.name>
+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 (file)
index 0000000..69be8af
--- /dev/null
@@ -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@the.local.host.name>
+250 mail cmd 1 good
+RCPT TO:<a@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@the.local.host.name>)
+       id 10HmaX-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaX-0005vi-00@the.local.host.name>
+From: CALLER_NAME <CALLER@the.local.host.name>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message received
+MAIL FROM:<CALLER@the.local.host.name>
+250 mail cmd 2 good
+RCPT TO:<b@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@the.local.host.name>)
+       id 10HmaX-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaX-0005vi-00@the.local.host.name>
+From: CALLER_NAME <CALLER@the.local.host.name>
+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@the.local.host.name>
+250 mail cmd 1 good
+RCPT TO:<a@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@the.local.host.name>)
+       id 10HmaY-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaY-0005vi-00@the.local.host.name>
+From: CALLER_NAME <CALLER@the.local.host.name>
+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@the.local.host.name>
+250 mail cmd 2 good
+RCPT TO:<b@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@the.local.host.name>)
+       id 10HmaY-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaY-0005vi-00@the.local.host.name>
+From: CALLER_NAME <CALLER@the.local.host.name>
+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 (file)
index 0000000..02b10ea
--- /dev/null
@@ -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@the.local.host.name>
+250 mail cmd good
+RCPT TO:<a@a.test.ex>
+250 rcpt cmd 1 good
+RCPT TO:<b@b.test.ex>
+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@the.local.host.name>)
+       id 10HmaX-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaX-0005vi-00@the.local.host.name>
+From: CALLER_NAME <CALLER@the.local.host.name>
+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@the.local.host.name>
+250 mail cmd good
+RCPT TO:<a@a.test.ex>
+250 rcpt cmd 1 good
+RCPT TO:<a2@a.test.ex>
+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@the.local.host.name>)
+       id 10HmaY-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaY-0005vi-00@the.local.host.name>
+From: CALLER_NAME <CALLER@the.local.host.name>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message received
+MAIL FROM:<CALLER@the.local.host.name>
+250 second mail cmd good
+RCPT TO:<b@b.test.ex>
+250 rcpt cmd 1 good
+RCPT TO:<b2@b.test.ex>
+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@the.local.host.name>)
+       id 10HmaY-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaY-0005vi-00@the.local.host.name>
+From: CALLER_NAME <CALLER@the.local.host.name>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 second message received
+MAIL FROM:<CALLER@the.local.host.name>
+250 third mail cmd good
+RCPT TO:<c@c.test.ex>
+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@the.local.host.name>)
+       id 10HmaY-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmaY-0005vi-00@the.local.host.name>
+From: CALLER_NAME <CALLER@the.local.host.name>
+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 (file)
index 0000000..f7d8e22
--- /dev/null
@@ -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@the.local.host.name>
+250 mail cmd 1 good
+RCPT TO:<r1_1.test.ex@the.local.host.name>
+250 rcpt cmd good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+       (envelope-from <CALLER@the.local.host.name>)
+       id 10HmaX-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Subject: message 1
+Message-Id: <E10HmaX-0005vi-00@the.local.host.name>
+From: CALLER_NAME <CALLER@the.local.host.name>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message 1 received
+MAIL FROM:<CALLER@the.local.host.name>
+250 mail cmd 2 good
+RCPT TO:<r1_2.test.ex@the.local.host.name>
+250 rcpt cmd good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+       (envelope-from <CALLER@the.local.host.name>)
+       id 10HmaX-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Subject: message 1
+Message-Id: <E10HmaX-0005vi-00@the.local.host.name>
+From: CALLER_NAME <CALLER@the.local.host.name>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message 2 received
+MAIL FROM:<CALLER@the.local.host.name>
+250 mail cmd 3 good
+RCPT TO:<r3_1.test.ex@the.local.host.name>
+250 rcpt cmd good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+       (envelope-from <CALLER@the.local.host.name>)
+       id 10HmaZ-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Subject: message 3
+Message-Id: <E10HmaZ-0005vi-00@the.local.host.name>
+From: CALLER_NAME <CALLER@the.local.host.name>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message 3 received
+MAIL FROM:<CALLER@the.local.host.name>
+250 mail cmd 4 good
+RCPT TO:<r3_2.test.ex@the.local.host.name>
+250 rcpt cmd good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+       (envelope-from <CALLER@the.local.host.name>)
+       id 10HmaZ-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Subject: message 3
+Message-Id: <E10HmaZ-0005vi-00@the.local.host.name>
+From: CALLER_NAME <CALLER@the.local.host.name>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message 4 received
+MAIL FROM:<CALLER@the.local.host.name>
+250 mail cmd 5 good
+RCPT TO:<r2_1.test.ex@the.local.host.name>
+250 rcpt cmd good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+       (envelope-from <CALLER@the.local.host.name>)
+       id 10HmaY-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Subject: message 2
+Message-Id: <E10HmaY-0005vi-00@the.local.host.name>
+From: CALLER_NAME <CALLER@the.local.host.name>
+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@the.local.host.name>
+250 mail cmd 1 good
+RCPT TO:<r2_2.test.ex@the.local.host.name>
+250 rcpt cmd good
+DATA
+352 go ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+       (envelope-from <CALLER@the.local.host.name>)
+       id 10HmaY-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Subject: message 2
+Message-Id: <E10HmaY-0005vi-00@the.local.host.name>
+From: CALLER_NAME <CALLER@the.local.host.name>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 message 6 received
+QUIT
+221 bye
+Expected EOF read from client
+End of script