Squashed commit of PIPE_CONNECT
authorJeremy Harris <jgh146exb@wizmail.org>
Sat, 3 Nov 2018 23:13:34 +0000 (23:13 +0000)
committerJeremy Harris <jgh146exb@wizmail.org>
Mon, 5 Nov 2018 16:55:33 +0000 (16:55 +0000)
88 files changed:
doc/doc-docbook/spec.xfpt
doc/doc-txt/NewStuff
doc/doc-txt/OptionLists.txt
doc/doc-txt/experimental-spec.txt
src/src/config.h.defaults
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/ip.c
src/src/macro_predef.c
src/src/macros.h
src/src/queue.c
src/src/readconf.c
src/src/receive.c
src/src/smtp_in.c
src/src/smtp_out.c
src/src/structs.h
src/src/tls-openssl.c
src/src/transports/smtp.c
src/src/transports/smtp.h
src/src/verify.c
test/aux-var-src/tls_conf_prefix
test/confs/0002
test/confs/0564
test/confs/0900
test/confs/0906
test/confs/4050 [new file with mode: 0644]
test/confs/4051 [new symlink]
test/confs/4052 [new symlink]
test/confs/4053 [new file with mode: 0644]
test/confs/4056 [new file with mode: 0644]
test/confs/4058 [new file with mode: 0644]
test/confs/4060 [new file with mode: 0644]
test/confs/4061 [new file with mode: 0644]
test/confs/4062 [new symlink]
test/confs/4063 [new symlink]
test/confs/4064 [new file with mode: 0644]
test/confs/4065 [new file with mode: 0644]
test/confs/4066 [new symlink]
test/confs/4067 [new symlink]
test/confs/5910
test/log/4027
test/log/4050 [new file with mode: 0644]
test/log/4051 [new file with mode: 0644]
test/log/4052 [new file with mode: 0644]
test/log/4053 [new file with mode: 0644]
test/log/4056 [new file with mode: 0644]
test/log/4058 [new file with mode: 0644]
test/log/4060 [new file with mode: 0644]
test/log/4061 [new file with mode: 0644]
test/log/4062 [new file with mode: 0644]
test/log/4063 [new file with mode: 0644]
test/log/4064 [new file with mode: 0644]
test/log/4065 [new file with mode: 0644]
test/log/4066 [new file with mode: 0644]
test/log/4067 [new file with mode: 0644]
test/runtest
test/scripts/4050-pipe-conn/4050 [new file with mode: 0644]
test/scripts/4050-pipe-conn/4051 [new file with mode: 0644]
test/scripts/4050-pipe-conn/4052 [new file with mode: 0644]
test/scripts/4050-pipe-conn/4053 [new file with mode: 0644]
test/scripts/4050-pipe-conn/REQUIRES [new file with mode: 0644]
test/scripts/4056-pipe-conn-auth/4056 [new file with mode: 0644]
test/scripts/4056-pipe-conn-auth/REQUIRES [new file with mode: 0644]
test/scripts/4058-pipe-conn-tfo/4058 [new file with mode: 0644]
test/scripts/4058-pipe-conn-tfo/REQUIRES [new file with mode: 0644]
test/scripts/4060-pipe-conn-gnutls/4060 [new file with mode: 0644]
test/scripts/4060-pipe-conn-gnutls/4061 [new file with mode: 0644]
test/scripts/4060-pipe-conn-gnutls/REQUIRES [new file with mode: 0644]
test/scripts/4062-pipe-conn-openssl/4062 [new file with mode: 0644]
test/scripts/4062-pipe-conn-openssl/4063 [new file with mode: 0644]
test/scripts/4062-pipe-conn-openssl/REQUIRES [new file with mode: 0644]
test/scripts/4064-pipe-conn-gnutls-auth/4064 [new file with mode: 0644]
test/scripts/4064-pipe-conn-gnutls-auth/4065 [new file with mode: 0644]
test/scripts/4064-pipe-conn-gnutls-auth/REQUIRES [new file with mode: 0644]
test/scripts/4066-pipe-conn-openssl-auth/4066 [new file with mode: 0644]
test/scripts/4066-pipe-conn-openssl-auth/4067 [new file with mode: 0644]
test/scripts/4066-pipe-conn-openssl-auth/REQUIRES [new file with mode: 0644]
test/stderr/2013
test/stderr/2035
test/stderr/2113
test/stderr/2135
test/stderr/4052 [new file with mode: 0644]
test/stdout/4050 [new file with mode: 0644]
test/stdout/4052 [new file with mode: 0644]

index c84c9b4..4e99e6c 100644 (file)
@@ -36965,6 +36965,7 @@ immediately after the time and date.
 &%pipelining%&: A field is added to delivery and accept
 log lines when the ESMTP PIPELINING extension was used.
 The field is a single "L".
+
 On accept lines, where PIPELINING was offered but not used by the client,
 the field has a minus appended.
 .next
index cc9721a..fb336b8 100644 (file)
@@ -17,7 +17,8 @@ Version 4.92
  2. The ${readsocket } expansion item now takes a "tls" option, doing the
     obvious thing.
 
- 3. EXPERIMENTAL_REQUIRETLS.  See the experimental.spec file.
+ 3. EXPERIMENTAL_REQUIRETLS and EXPERIMENTAL_PIPE_CONNECT optional build
+    features.  See the experimental.spec file.
 
  4. If built with SUPPORT_I18N a "utf8_downconvert" option on the smtp transport.
 
index 8d20f6f..fec4794 100644 (file)
@@ -54,7 +54,7 @@ acl_not_smtp_mime                    string*         unset         main
 acl_smtp_auth                        string*         unset         main              4.00
 acl_smtp_connect                     string*         unset         main              4.11
 acl_smtp_data                        string*         unset         main              4.00
-acl_smtp_data_prdr                   string*         unset         main              4.82 with experimental_prdr
+acl_smtp_data_prdr                   string*         unset         main              4.82 with experimental_prdr, 4.83 unless disable_prdr
 acl_smtp_dkim                        string*         unset         main              4.70 unless disable_dkim
 acl_smtp_etrn                        string*         unset         main              4.00
 acl_smtp_expn                        string*         unset         main              4.00
@@ -300,6 +300,7 @@ hosts_max_try_hardlimit              integer         50            smtp
 hosts_nopass_tls                     host list       unset         smtp              4.00
 hosts_noproxy_tls                    host list       "*"           smtp              4.90
 hosts_override                       boolean         false         smtp              2.11
+hosts_pipe_connect                  host_list       unset         smtp              4.93 if experimental_pipe_connect
 hosts_randomize                      boolean         false         manualroute       4.00
                                                      false         smtp              3.14
 hosts_require_auth                   host list       unset         smtp              4.00
@@ -412,6 +413,7 @@ pid_file_path                        string          ++            main
 pipe_as_creator                      boolean         false         pipe
 pipe_transport                       string*         unset         redirect          4.00
 pipelining_advertise_hosts           host list       "*"           main              4.14
+pipelining__connect_advertise_hosts  host list       "*"           main              4.92 if experimental_pipe_connect
 port                                 integer         0             iplookup          4.00
                                      string          "smtp"        smtp
 preserve_message_logs                boolean         false         main
index 49935fb..d5a75f5 100644 (file)
@@ -904,6 +904,86 @@ Note that REQUIRETLS is only advertised once a TLS connection is achieved
 like "swaks -s 127.0.0.1 -tls -q HELO".
 
 
+
+
+Early pipelining support
+------------------------
+Ref: https://datatracker.ietf.org/doc/draft-harris-early-pipe/
+
+If compiled with EXPERIMENTAL_PIPE_CONNECT support is included for this feature.
+The server advertises the feature in its EHLO response, currently using the name
+"X_PIPE_CONNECT" (this will change, some time in the future).
+A client may cache this information, along with the rest of the EHLO response,
+and use it for later connections.  Those later ones can send esmtp commands before
+a banner is received.
+
+Up to 1.5 roundtrip times can be taken out of cleartext connections, 2.5 on
+STARTTLS connections.
+
+In combination with the traditional PIPELINING feature the following example
+sequences are possible (among others):
+
+(client)                (server)
+
+EHLO,MAIL,RCPT,DATA ->
+                     <- banner,EHLO-resp,MAIL-ack,RCPT-ack,DATA-goahead
+message-data        ->
+------
+
+EHLO,MAIL,RCPT,BDAT              ->
+                                  <- banner,EHLO-resp,MAIL-ack,RCPT-ack
+message-data                     ->
+------
+
+EHLO,STARTTLS                     ->
+                                   <- banner,EHLO-resp,TLS-goahead
+TLS1.2-client-hello               ->
+                                   <- TLS-server-hello,cert,hello-done
+client-Kex,change-cipher,finished ->
+                                   <- change-cipher,finshed
+EHLO,MAIL,RCPT,DATA               ->
+                                   <- EHLO-resp,MAIL-ack,RCPT-ack,DATA-goahead
+
+------
+(tls-on-connect)
+TLS1.2-client-hello               ->
+                                   <- TLS-server-hello,cert,hello-done
+client-Kex,change-cipher,finished ->
+                                   <- change-cipher,finshed
+                                   <- banner
+EHLO,MAIL,RCPT,DATA               ->
+                                   <- EHLO-resp,MAIL-ack,RCPT-ack,DATA-goahead
+
+Where the initial client packet is SMTP, it can combine with the TCP Fast Open
+feature and be sent in the TCP SYN.
+
+
+A main-section option "pipelining_connect_advertise_hosts" (default: *)
+and an smtp transport option "hosts_pipe_connect" (default: unset)
+control the feature.
+
+If the "pipelining" log_selector is enabled, the "L" field in server <=
+log lines has a period appended if the feature was advertised but not used;
+or has an asterisk appended if the feature was used.  In client => lines
+the "L" field has an asterisk appended if the feature was used.
+
+The "retry_data_expire" option controls cache invalidation.
+Entries are also rewritten (or cleared) if the adverised features
+change.
+
+
+NOTE: since the EHLO command must be constructed before the connection is
+made it cannot depend on the interface IP address that will be used.
+Transport configurations should be checked for this.  An example avoidance:
+
+ helo_data =   ${if def:sending_ip_address \
+                  {${lookup dnsdb{>! ptr=$sending_ip_address} \
+                       {${sg{$value} {^([^!]*).*\$} {\$1}}} fail}} \
+                  {$primary_hostname}}
+
+
+
+
 --------------------------------------------------------------
 End of file
 --------------------------------------------------------------
index 4fb30dd..7c2e534 100644 (file)
@@ -198,6 +198,7 @@ Do not put spaces between # and the 'define'.
 #define EXPERIMENTAL_DMARC
     #define DMARC_TLD_FILE "/etc/exim/opendmarc.tlds"
 #define EXPERIMENTAL_LMDB
+#define EXPERIMENTAL_PIPE_CONNECT
 #define EXPERIMENTAL_REQUIRETLS
 #define EXPERIMENTAL_QUEUEFILE
 #define EXPERIMENTAL_SRS
index afb84f9..02cfa14 100644 (file)
@@ -786,5 +786,23 @@ typedef struct {
   uschar   bloom[40];     /* Bloom filter which may be larger than this */
 } dbdata_ratelimit_unique;
 
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+/* This structure records the EHLO responses, cleartext and crypted,
+for an IP, as bitmasks (cf. OPTION_TLS) */
+
+typedef struct {
+  unsigned short cleartext_features;
+  unsigned short crypted_features;
+  unsigned short cleartext_auths;
+  unsigned short crypted_auths;
+} ehlo_resp_precis;
+
+typedef struct {
+  time_t time_stamp;
+  /*************/
+  ehlo_resp_precis data;
+} dbdata_ehlo_resp;
+#endif
+
 
 /* End of dbstuff.h */
index 4624719..9f292fc 100644 (file)
@@ -798,7 +798,7 @@ if (LOGGING(proxy) && proxy_local_address)
 g = d_log_interface(g);
 
 if (testflag(addr, af_tcp_fastopen))
-  g = string_catn(g, US" TFO", 4);
+  g = string_catn(g, US" TFO*", testflag(addr, af_tcp_fastopen_data) ? 5 : 4);
 
 return g;
 }
@@ -1247,8 +1247,15 @@ else
       }
     }
 
-  if (LOGGING(pipelining) && testflag(addr, af_pipelining))
-    g = string_catn(g, US" L", 2);
+  if (LOGGING(pipelining))
+    {
+    if (testflag(addr, af_pipelining))
+      g = string_catn(g, US" L", 2);
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+    if (testflag(addr, af_early_pipe))
+      g = string_catn(g, US"*", 1);
+#endif
+    }
 
 #ifndef DISABLE_PRDR
   if (testflag(addr, af_prdr_used))
@@ -3576,6 +3583,9 @@ while (!done)
     case 'L':
       switch (*subid)
        {
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+       case 2: setflag(addr, af_early_pipe);   /*FALLTHROUGH*/
+#endif
        case 1: setflag(addr, af_pipelining); break;
        }
       break;
@@ -3587,6 +3597,7 @@ while (!done)
     case 'T':
       setflag(addr, af_tcp_fastopen_conn);
       if (*subid > '0') setflag(addr, af_tcp_fastopen);
+      if (*subid > '1') setflag(addr, af_tcp_fastopen_data);
       break;
 
     case 'D':
@@ -4884,6 +4895,11 @@ all pipes, so I do not see a reason to use non-blocking IO here
 #endif
 
       if (testflag(addr, af_pipelining))
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+       if (testflag(addr, af_early_pipe))
+         rmt_dlv_checked_write(fd, 'L', '2', NULL, 0);
+       else
+#endif
          rmt_dlv_checked_write(fd, 'L', '1', NULL, 0);
 
       if (testflag(addr, af_chunking_used))
@@ -4891,7 +4907,9 @@ all pipes, so I do not see a reason to use non-blocking IO here
 
       if (testflag(addr, af_tcp_fastopen_conn))
        rmt_dlv_checked_write(fd, 'T',
-         testflag(addr, af_tcp_fastopen) ? '1' : '0', NULL, 0);
+         testflag(addr, af_tcp_fastopen) ? testflag(addr, af_tcp_fastopen_data)
+         ? '2' : '1' : '0',
+         NULL, 0);
 
       memcpy(big_buffer, &addr->dsn_aware, sizeof(addr->dsn_aware));
       rmt_dlv_checked_write(fd, 'D', '0', big_buffer, sizeof(addr->dsn_aware));
@@ -7305,7 +7323,7 @@ if (addr_senddsn)
     }
   else  /* Creation of child succeeded */
     {
-    FILE *f = fdopen(fd, "wb");
+    FILE * f = fdopen(fd, "wb");
     /* header only as required by RFC. only failure DSN needs to honor RET=FULL */
     uschar * bound;
     transport_ctx tctx = {{0}};
@@ -7386,8 +7404,10 @@ if (addr_senddsn)
 
     /* Write the original email out */
 
-    tctx.u.fd = fileno(f);
+    tctx.u.fd = fd;
     tctx.options = topt_add_return_path | topt_no_body;
+    /*XXX hmm, retval ignored.
+    Could error for any number of reasons, and they are not handled. */
     transport_write_message(&tctx, 0);
     fflush(f);
 
@@ -7855,6 +7875,7 @@ wording. */
        tctx.options = topt;
        tb.add_headers = dsnnotifyhdr;
 
+       /*XXX no checking for failure!  buggy! */
        transport_write_message(&tctx, 0);
        }
       fflush(fp);
@@ -8321,6 +8342,7 @@ else if (addr_defer != (address_item *)(+1))
         return_path = sender_address;   /* In case not previously set */
 
         /* Write the original email out */
+       /*XXX no checking for failure!  buggy! */
         transport_write_message(&tctx, 0);
         fflush(f);
 
@@ -8484,8 +8506,7 @@ if (!regex_SIZE) regex_SIZE =
   regex_must_compile(US"\\n250[\\s\\-]SIZE(\\s|\\n|$)", FALSE, TRUE);
 
 if (!regex_AUTH) regex_AUTH =
-  regex_must_compile(US"\\n250[\\s\\-]AUTH\\s+([\\-\\w\\s]+)(?:\\n|$)",
-    FALSE, TRUE);
+  regex_must_compile(AUTHS_REGEX, FALSE, TRUE);
 
 #ifdef SUPPORT_TLS
 if (!regex_STARTTLS) regex_STARTTLS =
@@ -8515,6 +8536,11 @@ if (!regex_DSN) regex_DSN  =
 
 if (!regex_IGNOREQUOTA) regex_IGNOREQUOTA =
   regex_must_compile(US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)", FALSE, TRUE);
+
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+if (!regex_EARLY_PIPE) regex_EARLY_PIPE =
+  regex_must_compile(US"\\n250[\\s\\-]" EARLY_PIPE_FEATURE_NAME "(\\s|\\n|$)", FALSE, TRUE);
+#endif
 }
 
 
index b6d926b..a3d1b9e 100644 (file)
@@ -140,14 +140,14 @@ regex_match_and_setup(const pcre *re, const uschar *subject, int options, int se
 int ovector[3*(EXPAND_MAXN+1)];
 uschar * s = string_copy(subject);     /* de-constifying */
 int n = pcre_exec(re, NULL, CS s, Ustrlen(s), 0,
-  PCRE_EOPT | options, ovector, sizeof(ovector)/sizeof(int));
+  PCRE_EOPT | options, ovector, nelem(ovector));
 BOOL yield = n >= 0;
 if (n == 0) n = EXPAND_MAXN + 1;
 if (yield)
   {
   int nn;
-  expand_nmax = (setup < 0)? 0 : setup + 1;
-  for (nn = (setup < 0)? 0 : 2; nn < n*2; nn += 2)
+  expand_nmax = setup < 0 ? 0 : setup + 1;
+  for (nn = setup < 0 ? 0 : 2; nn < n*2; nn += 2)
     {
     expand_nstring[expand_nmax] = s + ovector[nn];
     expand_nlength[expand_nmax++] = ovector[nn+1] - ovector[nn];
@@ -906,6 +906,9 @@ fprintf(fp, "Support for:");
 #ifdef EXPERIMENTAL_REQUIRETLS
   fprintf(fp, " Experimental_REQUIRETLS");
 #endif
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  fprintf(fp, " Experimental_PIPE_CONNECT");
+#endif
 fprintf(fp, "\n");
 
 fprintf(fp, "Lookups (built-in):");
index 38df27a..fbf2cbe 100644 (file)
@@ -435,8 +435,7 @@ extern void    smtp_command_sigterm_exit(void);
 extern void    smtp_data_timeout_exit(void);
 extern void    smtp_data_sigint_exit(void);
 extern uschar *smtp_cmd_hist(void);
-extern int     smtp_connect(host_item *, int, uschar *, int,
-                transport_instance *);
+extern int     smtp_connect(smtp_connect_args *, const blob *);
 extern int     smtp_sock_connect(host_item *, int, int, uschar *,
                 transport_instance * tb, int, const blob *);
 extern int     smtp_feof(void);
index 8dd8191..731d25c 100644 (file)
@@ -325,6 +325,11 @@ struct global_flags f =
        .sender_name_forced     = FALSE,
        .sender_set_untrusted   = FALSE,
        .smtp_authenticated     = FALSE,
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+       .smtp_in_early_pipe_advertised = FALSE,
+       .smtp_in_early_pipe_no_auth = FALSE,
+       .smtp_in_early_pipe_used = FALSE,
+#endif
        .smtp_in_pipelining_advertised = FALSE,
        .smtp_in_pipelining_used = FALSE,
        .spool_file_wireformat  = FALSE,
@@ -336,6 +341,7 @@ struct global_flags f =
 
        .tcp_fastopen_ok        = FALSE,
        .tcp_in_fastopen        = FALSE,
+       .tcp_in_fastopen_data   = FALSE,
        .tcp_in_fastopen_logged = FALSE,
        .tcp_out_fastopen_logged= FALSE,
        .timestamps_utc         = FALSE,
@@ -1180,6 +1186,9 @@ uschar *override_pid_file_path = NULL;
 uschar *percent_hack_domains   = NULL;
 uschar *pid_file_path          = US PID_FILE_PATH
                            "\0<--------------Space to patch pid_file_path->";
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+uschar *pipe_connect_advertise_hosts = US"*";
+#endif
 uschar *pipelining_advertise_hosts = US"*";
 uschar *primary_hostname       = NULL;
 uschar  process_info[PROCESS_INFO_SIZE];
@@ -1267,6 +1276,9 @@ const pcre *regex_From         = NULL;
 const pcre *regex_IGNOREQUOTA  = NULL;
 const pcre *regex_PIPELINING   = NULL;
 const pcre *regex_SIZE         = NULL;
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+const pcre *regex_EARLY_PIPE   = NULL;
+#endif
 const pcre *regex_ismsgid      = NULL;
 const pcre *regex_smtp_code    = NULL;
 uschar *regex_vars[REGEX_VARS];
index 465266e..c90783e 100644 (file)
@@ -251,6 +251,11 @@ extern struct global_flags {
  BOOL   sender_name_forced             :1; /* Set by -F */
  BOOL   sender_set_untrusted           :1; /* Sender set by untrusted caller */
  BOOL   smtp_authenticated             :1; /* Sending client has authenticated */
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+ BOOL   smtp_in_early_pipe_advertised  :1; /* server advertised PIPE_CONNECT */
+ BOOL  smtp_in_early_pipe_no_auth      :1; /* too many authenticator names */
+ BOOL   smtp_in_early_pipe_used                :1; /* client did send early data */
+#endif
  BOOL   smtp_in_pipelining_advertised  :1; /* server advertised PIPELINING */
  BOOL   smtp_in_pipelining_used                :1; /* server noted client using PIPELINING */
  BOOL   spool_file_wireformat          :1; /* current -D file has CRLF rather than NL */
@@ -262,6 +267,7 @@ extern struct global_flags {
 
  BOOL   tcp_fastopen_ok                        :1; /* appears to be supported by kernel */
  BOOL   tcp_in_fastopen                        :1; /* conn usefully used fastopen */
+ BOOL   tcp_in_fastopen_data           :1; /* fastopen carried data */
  BOOL   tcp_in_fastopen_logged         :1; /* one-time logging */
  BOOL   tcp_out_fastopen_logged                :1; /* one-time logging */
  BOOL   timestamps_utc                 :1; /* Use UTC for all times */
@@ -734,6 +740,9 @@ extern uschar *override_pid_file_path; /* Value of -oP argument */
 
 extern uschar *percent_hack_domains;   /* Local domains for which '% operates */
 extern uschar *pid_file_path;          /* For writing daemon pids */
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+extern uschar *pipe_connect_advertise_hosts; /* for banner/EHLO pipelining */
+#endif
 extern uschar *pipelining_advertise_hosts; /* As it says */
 #ifndef DISABLE_PRDR
 extern BOOL    prdr_enable;            /* As it says */
@@ -813,6 +822,9 @@ extern const pcre  *regex_CHUNKING;    /* For recognizing CHUNKING (RFC 3030) */
 extern const pcre  *regex_IGNOREQUOTA; /* For recognizing IGNOREQUOTA (LMTP) */
 extern const pcre  *regex_PIPELINING;  /* For recognizing PIPELINING */
 extern const pcre  *regex_SIZE;        /* For recognizing SIZE settings */
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+extern const pcre  *regex_EARLY_PIPE;  /* For recognizing PIPE_CONNCT */
+#endif
 extern const pcre  *regex_ismsgid;     /* Compiled r.e. for message it */
 extern const pcre  *regex_smtp_code;   /* For recognizing SMTP codes */
 extern uschar *regex_vars[];           /* $regexN variables */
@@ -989,7 +1001,7 @@ extern BOOL    system_filter_uid_set;  /* TRUE if uid set */
 
 extern blob    tcp_fastopen_nodata;    /* for zero-data TFO connect requests */
 extern BOOL    tcp_nodelay;            /* Controls TCP_NODELAY on daemon */
-extern tfo_state_t tcp_out_fastopen;   /* 0: no  1: conn used  2: useful */
+extern tfo_state_t tcp_out_fastopen;   /* TCP fast open */
 #ifdef USE_TCP_WRAPPERS
 extern uschar *tcp_wrappers_daemon_name; /* tcpwrappers daemon lookup name */
 #endif
index d601b46..22f459d 100644 (file)
@@ -264,10 +264,10 @@ if (fastopen_blob && f.tcp_fastopen_ok)
        /* seen for with-data, proper TFO opt, with-cookie case */
     {
     DEBUG(D_transport|D_v)
-      debug_printf("non-TFO mode connection attempt to %s, %lu data\n",
+      debug_printf("TFO mode connection attempt to %s, %lu data\n",
        address, (unsigned long)fastopen_blob->len);
     /*XXX also seen on successful TFO, sigh */
-    tcp_out_fastopen = fastopen_blob->len > 0 ?  TFO_USED : TFO_ATTEMPTED;
+    tcp_out_fastopen = fastopen_blob->len > 0 ?  TFO_ATTEMPTED_DATA : TFO_ATTEMPTED_NODATA;
     }
   else if (errno == EINPROGRESS)       /* expected if we had no cookie for peer */
        /* seen for no-data, proper TFO option, both cookie-request and with-cookie cases */
@@ -280,7 +280,7 @@ if (fastopen_blob && f.tcp_fastopen_ok)
       fastopen_blob->len > 0 ? "with"  : "no");
     if (!fastopen_blob->data)
       {
-      tcp_out_fastopen = TFO_ATTEMPTED;                /* we tried; unknown if useful yet */
+      tcp_out_fastopen = TFO_ATTEMPTED_NODATA;         /* we tried; unknown if useful yet */
       rc = 0;
       }
     else
index 9f5700e..fbc923c 100644 (file)
@@ -201,6 +201,9 @@ due to conflicts with other common macros. */
 #ifdef EXPERIMENTAL_REQUIRETLS
   builtin_macro_create(US"_HAVE_REQTLS");
 #endif
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  builtin_macro_create(US"_HAVE_PIPE_CONNECT");
+#endif
 
 #ifdef LOOKUP_LSEARCH
   builtin_macro_create(US"_HAVE_LOOKUP_LSEARCH");
index 1eff348..28d3d31 100644 (file)
@@ -1014,15 +1014,16 @@ enum { FILTER_UNSET, FILTER_FORWARD, FILTER_EXIM, FILTER_SIEVE };
 
 /* Codes for ESMTP facilities offered by peer */
 
-#define OPTION_TLS     BIT(0)
-#define OPTION_IGNQ    BIT(1)
-#define OPTION_PRDR    BIT(2)
-#define OPTION_UTF8    BIT(3)
-#define OPTION_DSN     BIT(4)
-#define OPTION_PIPE    BIT(5)
-#define OPTION_SIZE    BIT(6)
-#define OPTION_CHUNKING        BIT(7)
-#define OPTION_REQUIRETLS BIT(8)
+#define OPTION_TLS             BIT(0)
+#define OPTION_IGNQ            BIT(1)
+#define OPTION_PRDR            BIT(2)
+#define OPTION_UTF8            BIT(3)
+#define OPTION_DSN             BIT(4)
+#define OPTION_PIPE            BIT(5)
+#define OPTION_SIZE            BIT(6)
+#define OPTION_CHUNKING                BIT(7)
+#define OPTION_REQUIRETLS      BIT(8)
+#define OPTION_EARLY_PIPE      BIT(9)
 
 /* Codes for tls_requiretls requests (usually by sender) */
 
@@ -1063,4 +1064,10 @@ should not be one active. */
     ? (sigalarm_setter = NULL, alarm(seconds)) : alarm(seconds);
 #endif
 
+#define AUTHS_REGEX US"\\n250[\\s\\-]AUTH\\s+([\\-\\w \\t]+)(?:\\n|$)"
+
+#define EARLY_PIPE_FEATURE_NAME "X_PIPE_CONNECT"
+#define EARLY_PIPE_FEATURE_LEN  14
+
+
 /* End of macros.h */
index ac1bb76..92109ef 100644 (file)
@@ -1135,7 +1135,7 @@ switch(action)
     deliver_in_buffer = store_malloc(DELIVER_IN_BUFFER_SIZE);
     deliver_out_buffer = store_malloc(DELIVER_OUT_BUFFER_SIZE);
     tctx.u.fd = 1;
-    transport_write_message(&tctx, 0);
+    (void) transport_write_message(&tctx, 0);
     break;
     }
 
index 9b3eef3..f21ce4d 100644 (file)
@@ -240,6 +240,10 @@ static optionlist optionlist_config[] = {
 #endif
   { "pid_file_path",            opt_stringptr,   &pid_file_path },
   { "pipelining_advertise_hosts", opt_stringptr, &pipelining_advertise_hosts },
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  { "pipelining_connect_advertise_hosts", opt_stringptr,
+                                                &pipe_connect_advertise_hosts },
+#endif
 #ifndef DISABLE_PRDR
   { "prdr_enable",              opt_bool,        &prdr_enable },
 #endif
@@ -4208,6 +4212,9 @@ static void
 auths_init(void)
 {
 auth_instance *au, *bu;
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+int nauths = 0;
+#endif
 
 readconf_driver_init(US"authenticator",
   (driver_instance **)(&auths),      /* chain anchor */
@@ -4231,7 +4238,13 @@ for (au = auths; au; au = au->next)
           "(%s and %s) have the same public name (%s)",
           au->client ? US"client" : US"server", au->name, bu->name,
           au->public_name);
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  nauths++;
+#endif
   }
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+f.smtp_in_early_pipe_no_auth = nauths > 16;
+#endif
 }
 
 
index ab27cc3..a564681 100644 (file)
@@ -1314,7 +1314,7 @@ if (sender_fullhost)
   }
 if (f.tcp_in_fastopen && !f.tcp_in_fastopen_logged)
   {
-  g = string_catn(g, US" TFO", 4);
+  g = string_catn(g, US" TFO*", f.tcp_in_fastopen_data ? 5 : 4);
   f.tcp_in_fastopen_logged = TRUE;
   }
 if (sender_ident)
@@ -1322,7 +1322,17 @@ if (sender_ident)
 if (received_protocol)
   g = string_append(g, 2, US" P=", received_protocol);
 if (LOGGING(pipelining) && f.smtp_in_pipelining_advertised)
-  g = string_catn(g, US" L-", f.smtp_in_pipelining_used ? 2 : 3);
+  {
+  g = string_catn(g, US" L", 2);
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  if (f.smtp_in_early_pipe_used)
+    g = string_catn(g, US"*", 1);
+  else if (f.smtp_in_early_pipe_advertised)
+    g = string_catn(g, US".", 1);
+#endif
+  if (!f.smtp_in_pipelining_used)
+    g = string_catn(g, US"-", 1);
+  }
 return g;
 }
 
index 2a83392..a579b8f 100644 (file)
@@ -145,6 +145,9 @@ static struct {
   BOOL helo_verify                     :1;
   BOOL helo_seen                       :1;
   BOOL helo_accept_junk                        :1;
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  BOOL pipe_connect_acceptable         :1;
+#endif
   BOOL rcpt_smtp_response_same         :1;
   BOOL rcpt_in_progress                        :1;
   BOOL smtp_exit_function_called       :1;
@@ -404,6 +407,18 @@ return TRUE;
 }
 
 
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+static BOOL
+pipeline_connect_sends(void)
+{
+if (!sender_host_address || f.sender_host_notsocket || !fl.pipe_connect_acceptable)
+  return FALSE;
+
+if (wouldblock_reading()) return FALSE;
+f.smtp_in_early_pipe_used = TRUE;
+return TRUE;
+}
+#endif
 
 /*************************************************
 *          Log incomplete transactions           *
@@ -1842,7 +1857,7 @@ for (i = 0; i < smtp_ch_index; i++)
 if (!(s = string_from_gstring(g))) s = US"";
 
 log_write(0, LOG_MAIN, "no MAIL in %sSMTP connection from %s D=%s%s",
-  f.tcp_in_fastopen ? US"TFO " : US"",
+  f.tcp_in_fastopen ? f.tcp_in_fastopen_data ? US"TFO* " : US"TFO " : US"",
   host_and_ident(FALSE), string_timesince(&smtp_connection_start), s);
 }
 
@@ -2392,13 +2407,17 @@ tfo_in_check(void)
 struct tcp_info tinfo;
 socklen_t len = sizeof(tinfo);
 
-if (  getsockopt(fileno(smtp_out), IPPROTO_TCP, TCP_INFO, &tinfo, &len) == 0
-   && tinfo.tcpi_state == TCP_SYN_RECV
-   )
-  {
-  DEBUG(D_receive) debug_printf("TCP_FASTOPEN mode connection (state TCP_SYN_RECV)\n");
-  f.tcp_in_fastopen = TRUE;
-  }
+if (getsockopt(fileno(smtp_out), IPPROTO_TCP, TCP_INFO, &tinfo, &len) == 0)
+  if (tinfo.tcpi_options & TCPI_OPT_SYN_DATA)
+    {
+    DEBUG(D_receive) debug_printf("TCP_FASTOPEN mode connection (ACKd data-on-SYN)\n");
+    f.tcp_in_fastopen_data = f.tcp_in_fastopen = TRUE;
+    }
+  else if (tinfo.tcpi_state == TCP_SYN_RECV)
+    {
+    DEBUG(D_receive) debug_printf("TCP_FASTOPEN mode connection (state TCP_SYN_RECV)\n");
+    f.tcp_in_fastopen = TRUE;
+    }
 # endif
 }
 #endif
@@ -2987,22 +3006,39 @@ while (*p);
 /* Before we write the banner, check that there is no input pending, unless
 this synchronisation check is disabled. */
 
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+fl.pipe_connect_acceptable =
+  sender_host_address && verify_check_host(&pipe_connect_advertise_hosts) == OK;
+
 if (!check_sync())
-  {
-  unsigned n = smtp_inend - smtp_inptr;
-  if (n > 32) n = 32;
-
-  log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol "
-    "synchronization error (input sent without waiting for greeting): "
-    "rejected connection from %s input=\"%s\"", host_and_ident(TRUE),
-    string_printing(string_copyn(smtp_inptr, n)));
-  smtp_printf("554 SMTP synchronization error\r\n", FALSE);
-  return FALSE;
-  }
+  if (fl.pipe_connect_acceptable)
+    f.smtp_in_early_pipe_used = TRUE;
+  else
+#else
+if (!check_sync())
+#endif
+    {
+    unsigned n = smtp_inend - smtp_inptr;
+    if (n > 32) n = 32;
+
+    log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol "
+      "synchronization error (input sent without waiting for greeting): "
+      "rejected connection from %s input=\"%s\"", host_and_ident(TRUE),
+      string_printing(string_copyn(smtp_inptr, n)));
+    smtp_printf("554 SMTP synchronization error\r\n", FALSE);
+    return FALSE;
+    }
 
 /* Now output the banner */
+/*XXX the ehlo-resp code does its own tls/nontls bit.  Maybe subroutine that? */
 
-smtp_printf("%s", FALSE, string_from_gstring(ss));
+smtp_printf("%s",
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  fl.pipe_connect_acceptable && pipeline_connect_sends(),
+#else
+  FALSE,
+#endif
+  string_from_gstring(ss));
 
 /* Attempt to see if we sent the banner before the last ACK of the 3-way
 handshake arrived.  If so we must have managed a TFO. */
@@ -3953,7 +3989,13 @@ while (done <= 0)
            US &off, sizeof(off));
 #endif
 
-  switch(smtp_read_command(TRUE, GETC_BUFFER_UNLIMITED))
+  switch(smtp_read_command(
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+         !fl.pipe_connect_acceptable,
+#else
+         TRUE,
+#endif
+         GETC_BUFFER_UNLIMITED))
     {
     /* The AUTH command is not permitted to occur inside a transaction, and may
     occur successfully only once per connection. Actually, that isn't quite
@@ -4186,7 +4228,12 @@ while (done <= 0)
          host_build_sender_fullhost();  /* Rebuild */
          break;
          }
-       else if (!check_sync()) goto SYNC_FAILURE;
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+       else if (!fl.pipe_connect_acceptable && !check_sync())
+#else
+       else if (!check_sync())
+#endif
+         goto SYNC_FAILURE;
 
       /* Generate an OK reply. The default string includes the ident if present,
       and also the IP address if present. Reflecting back the ident is intended
@@ -4315,13 +4362,22 @@ while (done <= 0)
        /* Exim is quite happy with pipelining, so let the other end know that
        it is safe to use it, unless advertising is disabled. */
 
-       if (f.pipelining_enable &&
-           verify_check_host(&pipelining_advertise_hosts) == OK)
+       if (  f.pipelining_enable
+          && verify_check_host(&pipelining_advertise_hosts) == OK)
          {
          g = string_catn(g, smtp_code, 3);
          g = string_catn(g, US"-PIPELINING\r\n", 13);
          sync_cmd_limit = NON_SYNC_CMD_PIPELINING;
          f.smtp_in_pipelining_advertised = TRUE;
+
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+         if (fl.pipe_connect_acceptable)
+           {
+           f.smtp_in_early_pipe_advertised = TRUE;
+           g = string_catn(g, smtp_code, 3);
+           g = string_catn(g, US"-" EARLY_PIPE_FEATURE_NAME "\r\n", EARLY_PIPE_FEATURE_LEN+3);
+           }
+#endif
          }
 
 
@@ -4442,7 +4498,14 @@ while (done <= 0)
       has been seen. */
 
 #ifdef SUPPORT_TLS
-      if (tls_in.active.sock >= 0) (void)tls_write(NULL, g->s, g->ptr, FALSE); else
+      if (tls_in.active.sock >= 0)
+       (void)tls_write(NULL, g->s, g->ptr,
+# ifdef EXPERIMENTAL_PIPE_CONNECT
+                       fl.pipe_connect_acceptable && pipeline_connect_sends());
+# else
+                       FALSE);
+# endif
+      else
 #endif
 
        {
@@ -5247,7 +5310,10 @@ while (done <= 0)
       HAD(SCH_DATA);
       f.dot_ends = TRUE;
 
-      DATA_BDAT:               /* Common code for DATA and BDAT */
+    DATA_BDAT:         /* Common code for DATA and BDAT */
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+      fl.pipe_connect_acceptable = FALSE;
+#endif
       if (!discarded && recipients_count <= 0)
        {
        if (fl.rcpt_smtp_response_same && rcpt_smtp_response != NULL)
@@ -5619,7 +5685,11 @@ while (done <= 0)
        log_write(L_lost_incoming_connection, LOG_MAIN,
          "unexpected %s while reading SMTP command from %s%s%s D=%s",
          f.sender_host_unknown ? "EOF" : "disconnection",
-         f.tcp_in_fastopen && !f.tcp_in_fastopen_logged ? US"TFO " : US"",
+         f.tcp_in_fastopen_logged
+         ? US""
+         : f.tcp_in_fastopen
+         ? f.tcp_in_fastopen_data ? US"TFO* " : US"TFO "
+         : US"",
          host_and_ident(FALSE), smtp_read_error,
          string_timesince(&smtp_connection_start)
          );
index c5eafbc..1209c7f 100644 (file)
@@ -148,46 +148,50 @@ tfo_out_check(int sock)
 struct tcp_info tinfo;
 socklen_t len = sizeof(tinfo);
 
-if (getsockopt(sock, IPPROTO_TCP, TCP_INFO, &tinfo, &len) == 0)
-  switch (tcp_out_fastopen)
-    {
-      /* This is a somewhat dubious detection method; totally undocumented so likely
-      to fail in future kernels.  There seems to be no documented way.  What we really
-      want to know is if the server sent smtp-banner data before our ACK of his SYN,ACK
-      hit him.  What this (possibly?) detects is whether we sent a TFO cookie with our
-      SYN, as distinct from a TFO request.  This gets a false-positive when the server
-      key is rotated; we send the old one (which this test sees) but the server returns
-      the new one and does not send its SMTP banner before we ACK his SYN,ACK.
-       To force that rotation case:
-       '# echo -n "00000000-00000000-00000000-0000000" >/proc/sys/net/ipv4/tcp_fastopen_key'
-      The kernel seems to be counting unack'd packets. */
-
-    case TFO_ATTEMPTED:
-      if (tinfo.tcpi_unacked > 1)
-       {
-       DEBUG(D_transport|D_v)
-         debug_printf("TCP_FASTOPEN tcpi_unacked %d\n", tinfo.tcpi_unacked);
-       tcp_out_fastopen = TFO_USED;
-       }
-      break;
-
-#  ifdef notdef                /* This seems to always fire, meaning that we cannot tell
-                       whether the server accepted data we sent.  For now assume
-                       that it did. */
+switch (tcp_out_fastopen)
+  {
+    /* This is a somewhat dubious detection method; totally undocumented so likely
+    to fail in future kernels.  There seems to be no documented way.  What we really
+    want to know is if the server sent smtp-banner data before our ACK of his SYN,ACK
+    hit him.  What this (possibly?) detects is whether we sent a TFO cookie with our
+    SYN, as distinct from a TFO request.  This gets a false-positive when the server
+    key is rotated; we send the old one (which this test sees) but the server returns
+    the new one and does not send its SMTP banner before we ACK his SYN,ACK.
+     To force that rotation case:
+     '# echo -n "00000000-00000000-00000000-0000000" >/proc/sys/net/ipv4/tcp_fastopen_key'
+    The kernel seems to be counting unack'd packets. */
+
+  case TFO_ATTEMPTED_NODATA:
+    if (  getsockopt(sock, IPPROTO_TCP, TCP_INFO, &tinfo, &len) == 0
+       && tinfo.tcpi_state == TCP_SYN_SENT
+       && tinfo.tcpi_unacked > 1
+       )
+      {
+      DEBUG(D_transport|D_v)
+       debug_printf("TCP_FASTOPEN tcpi_unacked %d\n", tinfo.tcpi_unacked);
+      tcp_out_fastopen = TFO_USED_NODATA;
+      }
+    break;
 
-      /* If there was data-on-SYN but we had to retrasnmit it, declare no TFO */
+    /* When called after waiting for received data we should be able
+    to tell if data we sent was accepted. */
 
-    case TFO_USED:
-      if (!(tinfo.tcpi_options & TCPI_OPT_SYN_DATA))
+  case TFO_ATTEMPTED_DATA:
+    if (  getsockopt(sock, IPPROTO_TCP, TCP_INFO, &tinfo, &len) == 0
+       && tinfo.tcpi_state == TCP_ESTABLISHED
+       )
+      if (tinfo.tcpi_options & TCPI_OPT_SYN_DATA)
+       {
+       DEBUG(D_transport|D_v) debug_printf("TFO: data was acked\n");
+       tcp_out_fastopen = TFO_USED_DATA;
+       }
+      else
        {
        DEBUG(D_transport|D_v) debug_printf("TFO: had to retransmit\n");
        tcp_out_fastopen = TFO_NOT_USED;
        }
-      break;
-
-  default: break;      /* compiler quietening */
-#  endif
-    }
+    break;
+  }
 # endif
 }
 #endif
@@ -259,7 +263,8 @@ if (interface && ip_bind(sock, host_af, interface, 0) < 0)
 
 /* Connect to the remote host, and add keepalive to the socket before returning
 it, if requested.  If the build supports TFO, request it - and if the caller
-requested some early-data then include that in the TFO request. */
+requested some early-data then include that in the TFO request.  If there is
+early-data but no TFO support, send it after connecting. */
 
 else
   {
@@ -271,8 +276,16 @@ else
   if (ip_connect(sock, host_af, host->address, port, timeout, fastopen_blob) < 0)
     save_errno = errno;
   else if (early_data && !fastopen_blob && early_data->data && early_data->len)
+    {
+    HDEBUG(D_transport|D_acl|D_v)
+      debug_printf("sending %ld nonTFO early-data\n", (long)early_data->len);
+
+#ifdef TCP_QUICKACK
+    (void) setsockopt(sock, IPPROTO_TCP, TCP_QUICKACK, US &off, sizeof(off));
+#endif
     if (send(sock, early_data->data, early_data->len, 0) < 0)
       save_errno = errno;
+    }
   }
 
 /* Either bind() or connect() failed */
@@ -291,12 +304,13 @@ if (save_errno != 0)
   return -1;
   }
 
-/* Both bind() and connect() succeeded */
+/* Both bind() and connect() succeeded, and any early-data */
 
 else
   {
   union sockaddr_46 interface_sock;
   EXIM_SOCKLEN_T size = sizeof(interface_sock);
+
   HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("connected\n");
   if (getsockname(sock, (struct sockaddr *)(&interface_sock), &size) == 0)
     sending_ip_address = host_ntoa(-1, &interface_sock, NULL, &sending_port);
@@ -307,9 +321,10 @@ else
     close(sock);
     return -1;
     }
+
   if (ob->keepalive) ip_keepalive(sock, host->address, TRUE);
 #ifdef TCP_FASTOPEN
-  if (fastopen_blob) tfo_out_check(sock);
+  tfo_out_check(sock);
 #endif
   return sock;
   }
@@ -343,45 +358,62 @@ non-IPv6 systems, to enable the code to be less messy. However, on such systems
 host->address will always be an IPv4 address.
 
 Arguments:
-  host        host item containing name and address and port
-  host_af     AF_INET or AF_INET6
-  interface   outgoing interface address or NULL
-  timeout     timeout value or 0
-  tb          transport
+  sc         details for making connection: host, af, interface, transport
+  early_data  if non-NULL, data to be sent - preferably in the TCP SYN segment
 
 Returns:      connected socket number, or -1 with errno set
 */
 
 int
-smtp_connect(host_item *host, int host_af, uschar *interface,
-  int timeout, transport_instance * tb)
+smtp_connect(smtp_connect_args * sc, const blob * early_data)
 {
-int port = host->port;
-#ifdef SUPPORT_SOCKS
-smtp_transport_options_block * ob =
-  (smtp_transport_options_block *)tb->options_block;
-#endif
+int port = sc->host->port;
+smtp_transport_options_block * ob = sc->ob;
 
-callout_address = string_sprintf("[%s]:%d", host->address, port);
+callout_address = string_sprintf("[%s]:%d", sc->host->address, port);
 
 HDEBUG(D_transport|D_acl|D_v)
   {
   uschar * s = US" ";
-  if (interface) s = string_sprintf(" from %s ", interface);
+  if (sc->interface) s = string_sprintf(" from %s ", sc->interface);
 #ifdef SUPPORT_SOCKS
   if (ob->socks_proxy) s = string_sprintf("%svia proxy ", s);
 #endif
-  debug_printf_indent("Connecting to %s %s%s... ", host->name, callout_address, s);
+  debug_printf_indent("Connecting to %s %s%s... ", sc->host->name, callout_address, s);
   }
 
 /* Create and connect the socket */
 
 #ifdef SUPPORT_SOCKS
 if (ob->socks_proxy)
-  return socks_sock_connect(host, host_af, port, interface, tb, timeout);
+  {
+  int sock = socks_sock_connect(sc->host, sc->host_af, port, sc->interface,
+                               sc->tblock, ob->connect_timeout);
+  
+  if (sock >= 0)
+    {
+    if (early_data && early_data->data && early_data->len)
+      if (send(sock, early_data->data, early_data->len, 0) < 0)
+       {
+       int save_errno = errno;
+       HDEBUG(D_transport|D_acl|D_v)
+         {
+         debug_printf_indent("failed: %s", CUstrerror(save_errno));
+         if (save_errno == ETIMEDOUT)
+           debug_printf(" (timeout=%s)", readconf_printtime(ob->connect_timeout));
+         debug_printf("\n");
+         }
+       (void)close(sock);
+       sock = -1;
+       errno = save_errno;
+       }
+    }
+  return sock;
+  }
 #endif
 
-return smtp_sock_connect(host, host_af, port, interface, tb, timeout, NULL);
+return smtp_sock_connect(sc->host, sc->host_af, port, sc->interface,
+                         sc->tblock, ob->connect_timeout, early_data);
 }
 
 
@@ -415,13 +447,33 @@ if (outblock->cctx->tls_ctx)
   rc = tls_write(outblock->cctx->tls_ctx, outblock->buffer, n, more);
 else
 #endif
-  rc = send(outblock->cctx->sock, outblock->buffer, n,
+
+  {
+  if (outblock->conn_args)
+    {
+    blob early_data = { .data = outblock->buffer, .len = n };
+
+    /* We ignore the more-flag if we're doing a connect with early-data, which
+    means we won't get BDAT+data. A pity, but wise due to the idempotency
+    requirement: TFO with data can, in rare cases, replay the data to the
+    receiver. */
+
+    if (  (outblock->cctx->sock = smtp_connect(outblock->conn_args, &early_data))
+       < 0)
+      return FALSE;
+    outblock->conn_args = NULL;
+    rc = n;
+    }
+  else
+
+    rc = send(outblock->cctx->sock, outblock->buffer, n,
 #ifdef MSG_MORE
-           more ? MSG_MORE : 0
+             more ? MSG_MORE : 0
 #else
-           0
+             0
 #endif
-          );
+            );
+  }
 
 if (rc <= 0)
   {
@@ -633,27 +685,37 @@ Arguments:
 
 Returns:    TRUE if a valid, non-error response was received; else FALSE
 */
+/*XXX could move to smtp transport; no other users */
 
 BOOL
-smtp_read_response(void * sx, uschar *buffer, int size, int okdigit,
+smtp_read_response(void * sx0, uschar *buffer, int size, int okdigit,
    int timeout)
 {
-smtp_inblock * inblock = &((smtp_context *)sx)->inblock;
+smtp_context * sx = sx0;
 uschar *ptr = buffer;
-int count;
+int count = 0;
 
 errno = 0;  /* Ensure errno starts out zero */
 
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+if (sx->pending_BANNER || sx->pending_EHLO)
+  if (smtp_reap_early_pipe(sx, &count) != OK)
+    {
+    DEBUG(D_transport) debug_printf("failed reaping pipelined cmd responsess\n");
+    return FALSE;
+    }
+#endif
+
 /* This is a loop to read and concatenate the lines that make up a multi-line
 response. */
 
 for (;;)
   {
-  if ((count = read_response_line(inblock, ptr, size, timeout)) < 0)
+  if ((count = read_response_line(&sx->inblock, ptr, size, timeout)) < 0)
     return FALSE;
 
   HDEBUG(D_transport|D_acl|D_v)
-    debug_printf_indent("  %s %s\n", (ptr == buffer)? "SMTP<<" : "      ", ptr);
+    debug_printf_indent("  %s %s\n", ptr == buffer ? "SMTP<<" : "      ", ptr);
 
   /* Check the format of the response: it must start with three digits; if
   these are followed by a space or end of line, the response is complete. If
@@ -687,6 +749,10 @@ for (;;)
   size -= count + 1;
   }
 
+#ifdef TCP_FASTOPEN
+  tfo_out_check(sx->cctx.sock);
+#endif
+
 /* Return a value that depends on the SMTP return code. On some systems a
 non-zero value of errno has been seen at this point, so ensure it is zero,
 because the caller of this function looks at errno when FALSE is returned, to
index 48f475a..20db0e5 100644 (file)
@@ -66,8 +66,10 @@ typedef enum {       CHUNKING_NOT_OFFERED = -1,
                CHUNKING_LAST} chunking_state_t;
 
 typedef enum { TFO_NOT_USED = 0,
-               TFO_ATTEMPTED,
-               TFO_USED } tfo_state_t;
+               TFO_ATTEMPTED_NODATA,
+               TFO_ATTEMPTED_DATA,
+               TFO_USED_NODATA,
+               TFO_USED_DATA } tfo_state_t;
 
 /* Structure for holding information about a host for use mainly by routers,
 but also used when checking lists of hosts and when transporting. Looking up
@@ -618,7 +620,11 @@ typedef struct address_item {
     BOOL af_bad_reply:1;               /* filter could not generate autoreply */
     BOOL af_tcp_fastopen_conn:1;       /* delivery connection used TCP Fast Open */
     BOOL af_tcp_fastopen:1;            /* delivery usefully used TCP Fast Open */
+    BOOL af_tcp_fastopen_data:1;       /* delivery sent SMTP commands on TCP Fast Open */
     BOOL af_pipelining:1;              /* delivery used (traditional) pipelining */
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+    BOOL af_early_pipe:1;              /* delivery used connect-time pipelining */
+#endif
 #ifndef DISABLE_PRDR
     BOOL af_prdr_used:1;               /* delivery used SMTP PRDR */
 #endif
@@ -787,6 +793,15 @@ typedef struct sha1 {
   unsigned int length;
 } sha1;
 
+/* Information for making an smtp connection */
+typedef struct {
+  transport_instance *  tblock;
+  void *               ob;     /* smtp_transport_options_block * */
+  host_item *           host;
+  int                   host_af;
+  uschar *              interface;
+} smtp_connect_args;
+
 /* A client-initiated connection. If TLS, the second element is non-NULL */
 typedef struct {
   int  sock;
@@ -817,6 +832,8 @@ typedef struct smtp_outblock {
   BOOL    authenticating;         /* TRUE when authenticating */
   uschar *ptr;                    /* current position in the buffer */
   uschar *buffer;                 /* the buffer itself */
+
+  smtp_connect_args * conn_args;  /* to make connection, if not yet made */
 } smtp_outblock;
 
 /* Structure to hold information about the source of redirection information */
index 982a5ba..f035145 100644 (file)
@@ -2909,10 +2909,22 @@ DEBUG(D_tls) debug_printf("%s(%p, %lu%s)\n", __FUNCTION__,
 "more" is notified.  This hack is only ok if small amounts are involved AND only
 one stream does it, in one context (i.e. no store reset).  Currently it is used
 for the responses to the received SMTP MAIL , RCPT, DATA sequence, only. */
+/*XXX + if PIPE_COMMAND, banner & ehlo-resp for smmtp-on-connect. Suspect there's
+a store reset there. */
 
 if (!ct_ctx && (more || corked))
   {
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  int save_pool = store_pool;
+  store_pool = POOL_PERM;
+#endif
+
   corked = string_catn(corked, buff, len);
+
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  store_pool = save_pool;
+#endif
+
   if (more)
     return len;
   buff = CUS corked->s;
index fba5a7c..576b7c5 100644 (file)
@@ -106,6 +106,10 @@ optionlist smtp_transport_options[] = {
 #endif
   { "hosts_override",       opt_bool,
       (void *)offsetof(smtp_transport_options_block, hosts_override) },
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  { "hosts_pipe_connect",   opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, hosts_pipe_connect) },
+#endif
   { "hosts_randomize",      opt_bool,
       (void *)offsetof(smtp_transport_options_block, hosts_randomize) },
 #if defined(SUPPORT_TLS) && !defined(DISABLE_OCSP)
@@ -248,6 +252,9 @@ smtp_transport_options_block smtp_transport_option_defaults = {
   .hosts_avoid_tls =           NULL,
   .hosts_verify_avoid_tls =    NULL,
   .hosts_avoid_pipelining =    NULL,
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  .hosts_pipe_connect =                NULL,
+#endif
   .hosts_avoid_esmtp =         NULL,
 #ifdef SUPPORT_TLS
   .hosts_nopass_tls =          NULL,
@@ -336,6 +343,9 @@ static BOOL    update_waiting;              /* TRUE to update the "wait" database */
 static BOOL    pipelining_active;      /* current transaction is in pipe mode */
 
 
+static unsigned ehlo_response(uschar * buf, unsigned checks);
+
+
 /*************************************************
 *             Setup entry point                  *
 *************************************************/
@@ -362,8 +372,7 @@ static int
 smtp_transport_setup(transport_instance *tblock, address_item *addrlist,
   transport_feedback *tf, uid_t uid, gid_t gid, uschar **errmsg)
 {
-smtp_transport_options_block *ob =
-  (smtp_transport_options_block *)(tblock->options_block);
+smtp_transport_options_block *ob = SOB tblock->options_block;
 
 errmsg = errmsg;    /* Keep picky compilers happy */
 uid = uid;
@@ -413,8 +422,7 @@ Returns:    nothing
 void
 smtp_transport_init(transport_instance *tblock)
 {
-smtp_transport_options_block *ob =
-  (smtp_transport_options_block *)(tblock->options_block);
+smtp_transport_options_block *ob = SOB tblock->options_block;
 
 /* Retry_use_local_part defaults FALSE if unset */
 
@@ -423,9 +431,11 @@ if (tblock->retry_use_local_part == TRUE_UNSET)
 
 /* Set the default port according to the protocol */
 
-if (ob->port == NULL)
-  ob->port = (strcmpic(ob->protocol, US"lmtp") == 0)? US"lmtp" :
-    (strcmpic(ob->protocol, US"smtps") == 0)? US"smtps" : US"smtp";
+if (!ob->port)
+  ob->port = strcmpic(ob->protocol, US"lmtp") == 0
+  ? US"lmtp"
+  : strcmpic(ob->protocol, US"smtps") == 0
+  ? US"smtps" : US"smtp";
 
 /* Set up the setup entry point, to be called before subprocesses for this
 transport. */
@@ -658,8 +668,6 @@ Returns:   nothing
 static void
 write_logs(const host_item *host, const uschar *suffix, int basic_errno)
 {
-
-
 uschar *message = LOGGING(outgoing_port)
   ? string_sprintf("H=%s [%s]:%d", host->name, host->address,
                    host->port == PORT_NONE ? 25 : host->port)
@@ -741,6 +749,267 @@ router_name = transport_name = NULL;
 #endif
 
 /*************************************************
+*           Reap SMTP specific responses         *
+*************************************************/
+static int
+smtp_discard_responses(smtp_context * sx, smtp_transport_options_block * ob,
+  int count)
+{
+uschar flushbuffer[4096];
+
+while (count-- > 0)
+  {
+  if (!smtp_read_response(sx, flushbuffer, sizeof(flushbuffer),
+            '2', ob->command_timeout)
+      && (errno != 0 || flushbuffer[0] == 0))
+    break;
+  }
+return count;
+}
+
+
+/* Return boolean success */
+
+static BOOL
+smtp_reap_banner(smtp_context * sx)
+{
+BOOL good_response = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
+  '2', (SOB sx->conn_args.ob)->command_timeout);
+#ifdef EXPERIMENTAL_DSN_INFO
+sx->smtp_greeting = string_copy(sx->buffer);
+#endif
+return good_response;
+}
+
+static BOOL
+smtp_reap_ehlo(smtp_context * sx)
+{
+if (!smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2',
+       (SOB sx->conn_args.ob)->command_timeout))
+  {
+  if (errno != 0 || sx->buffer[0] == 0 || sx->lmtp)
+    {
+#ifdef EXPERIMENTAL_DSN_INFO
+    sx->helo_response = string_copy(sx->buffer);
+#endif
+    return FALSE;
+    }
+  sx->esmtp = FALSE;
+  }
+#ifdef EXPERIMENTAL_DSN_INFO
+sx->helo_response = string_copy(sx->buffer);
+#endif
+return TRUE;
+}
+
+
+
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+static uschar *
+ehlo_cache_key(const smtp_context * sx)
+{
+host_item * host = sx->conn_args.host;
+return Ustrchr(host->address, ':')
+  ? string_sprintf("[%s]:%d.EHLO", host->address,
+    host->port == PORT_NONE ? sx->port : host->port)
+  : string_sprintf("%s:%d.EHLO", host->address,
+    host->port == PORT_NONE ? sx->port : host->port);
+}
+
+static void
+write_ehlo_cache_entry(const smtp_context * sx)
+{
+open_db dbblock, * dbm_file;
+
+if ((dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE)))
+  {
+  uschar * ehlo_resp_key = ehlo_cache_key(sx);
+  dbdata_ehlo_resp er = { .data = sx->ehlo_resp };
+
+  dbfn_write(dbm_file, ehlo_resp_key, &er, (int)sizeof(er));
+  dbfn_close(dbm_file);
+  }
+}
+
+static void
+invalidate_ehlo_cache_entry(smtp_context * sx)
+{
+open_db dbblock, * dbm_file;
+
+if (  sx->early_pipe_active
+   && (dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE)))
+  {
+  uschar * ehlo_resp_key = ehlo_cache_key(sx);
+  dbfn_delete(dbm_file, ehlo_resp_key);
+  dbfn_close(dbm_file);
+  }
+}
+
+static BOOL
+read_ehlo_cache_entry(smtp_context * sx)
+{
+open_db dbblock;
+open_db * dbm_file;
+
+if (!(dbm_file = dbfn_open(US"misc", O_RDONLY, &dbblock, FALSE)))
+  { DEBUG(D_transport) debug_printf("ehlo-cache: no misc DB\n"); }
+else
+  {
+  uschar * ehlo_resp_key = ehlo_cache_key(sx);
+  dbdata_ehlo_resp * er;
+
+  if (!(er = dbfn_read(dbm_file, ehlo_resp_key)))
+    { DEBUG(D_transport) debug_printf("no ehlo-resp record\n"); }
+  else if (time(NULL) - er->time_stamp > retry_data_expire)
+    {
+    DEBUG(D_transport) debug_printf("ehlo-resp record too old\n");
+    dbfn_close(dbm_file);
+    if ((dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE)))
+      dbfn_delete(dbm_file, ehlo_resp_key);
+    }
+  else
+    {
+    sx->ehlo_resp = er->data;
+    dbfn_close(dbm_file);
+    DEBUG(D_transport) debug_printf(
+       "EHLO response bits from cache: cleartext 0x%04x crypted 0x%04x\n",
+       er->data.cleartext_features, er->data.crypted_features);
+    return TRUE;
+    }
+  dbfn_close(dbm_file);
+  }
+return FALSE;
+}
+
+
+
+/* Return an auths bitmap for the set of AUTH methods offered by the server
+which match our authenticators. */
+
+static unsigned short
+study_ehlo_auths(smtp_context * sx)
+{
+uschar * names;
+auth_instance * au;
+uschar authnum;
+unsigned short authbits = 0;
+
+if (!sx->esmtp) return 0;
+if (!regex_AUTH) regex_AUTH = regex_must_compile(AUTHS_REGEX, FALSE, TRUE);
+if (!regex_match_and_setup(regex_AUTH, sx->buffer, 0, -1)) return 0;
+expand_nmax = -1;                                              /* reset */
+names = string_copyn(expand_nstring[1], expand_nlength[1]);
+
+for (au = auths, authnum = 0; au; au = au->next, authnum++) if (au->client)
+  {
+  const uschar * list = names;
+  int sep = ' ';
+  uschar name[32];
+
+  while (string_nextinlist(&list, &sep, name, sizeof(name)))
+    if (strcmpic(au->public_name, name) == 0)
+      { authbits |= BIT(authnum); break; }
+  }
+
+DEBUG(D_transport)
+  debug_printf("server offers %s AUTH, methods '%s', bitmap 0x%04x\n",
+    tls_out.active.sock >= 0 ? "crypted" : "plaintext", names, authbits);
+
+if (tls_out.active.sock >= 0)
+  sx->ehlo_resp.crypted_auths = authbits;
+else
+  sx->ehlo_resp.cleartext_auths = authbits;
+return authbits;
+}
+
+
+
+
+/* Wait for and check responses for early-pipelining.
+
+Called from the lower-level smtp_read_response() function
+used for general code that assume synchronisation, if context
+flags indicate outstanding early-pipelining commands.  Also
+called fom sync_responses() which handles pipelined commands.
+
+Arguments:
+ sx    smtp connection context
+ countp        number of outstanding responses, adjusted on return
+
+Return:
+ OK    all well
+ FAIL  SMTP error in response
+*/
+int
+smtp_reap_early_pipe(smtp_context * sx, int * countp)
+{
+BOOL pending_BANNER = sx->pending_BANNER;
+BOOL pending_EHLO = sx->pending_EHLO;
+
+sx->pending_BANNER = FALSE;    /* clear early to avoid recursion */
+sx->pending_EHLO = FALSE;
+
+if (pending_BANNER)
+  {
+  DEBUG(D_transport) debug_printf("%s expect banner\n", __FUNCTION__);
+  (*countp)--;
+  if (!smtp_reap_banner(sx))
+    {
+    DEBUG(D_transport) debug_printf("bad banner\n");
+    goto fail;
+    }
+  }
+
+if (pending_EHLO)
+  {
+  unsigned peer_offered;
+  unsigned short authbits = 0, * ap;
+
+  DEBUG(D_transport) debug_printf("%s expect ehlo\n", __FUNCTION__);
+  (*countp)--;
+  if (!smtp_reap_ehlo(sx))
+    {
+    DEBUG(D_transport) debug_printf("bad response for EHLO\n");
+    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. */
+
+  ap = tls_out.active.sock < 0
+      ? &sx->ehlo_resp.cleartext_auths : &sx->ehlo_resp.crypted_auths;
+
+  peer_offered = ehlo_response(sx->buffer,
+         (tls_out.active.sock < 0 ?  OPTION_TLS : OPTION_REQUIRETLS)
+       | OPTION_CHUNKING | OPTION_PRDR | OPTION_DSN | OPTION_PIPE | OPTION_SIZE
+       | OPTION_UTF8 | OPTION_EARLY_PIPE
+       );
+  if (  peer_offered != sx->peer_offered
+     || (authbits = study_ehlo_auths(sx)) != *ap)
+    {
+    HDEBUG(D_transport)
+      debug_printf("EHLO extensions changed, 0x%04x/0x%04x -> 0x%04x/0x%04x\n",
+                   sx->peer_offered, *ap, peer_offered, authbits);
+    *ap = authbits;
+    if (peer_offered & OPTION_EARLY_PIPE)
+      write_ehlo_cache_entry(sx);
+    else
+      invalidate_ehlo_cache_entry(sx);
+
+    return OK;         /* just carry on */
+    }
+  }
+return OK;
+
+fail:
+  invalidate_ehlo_cache_entry(sx);
+  (void) smtp_discard_responses(sx, sx->conn_args.ob, *countp);
+  return FAIL;
+}
+#endif
+
+
+/*************************************************
 *           Synchronize SMTP responses           *
 *************************************************/
 
@@ -779,16 +1048,21 @@ Returns:      3 if at least one address had 2xx and one had 5xx
              -1 timeout while reading RCPT response
              -2 I/O or other non-response error for RCPT
              -3 DATA or MAIL failed - errno and buffer set
+            -4 banner or EHLO failed (early-pipelining)
 */
 
 static int
 sync_responses(smtp_context * sx, int count, int pending_DATA)
 {
-address_item *addr = sx->sync_addr;
-smtp_transport_options_block *ob =
-  (smtp_transport_options_block *)sx->tblock->options_block;
+address_item * addr = sx->sync_addr;
+smtp_transport_options_block * ob = sx->conn_args.ob;
 int yield = 0;
 
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+if (smtp_reap_early_pipe(sx, &count) != OK)
+  return -4;
+#endif
+
 /* Handle the response for a MAIL command. On error, reinstate the original
 command in big_buffer for error message use, and flush any further pending
 responses before returning, except after I/O errors and timeouts. */
@@ -804,20 +1078,13 @@ if (sx->pending_MAIL)
     Ustrcpy(big_buffer, mail_command);  /* Fits, because it came from there! */
     if (errno == 0 && sx->buffer[0] != 0)
       {
-      uschar flushbuffer[4096];
       int save_errno = 0;
       if (sx->buffer[0] == '4')
         {
         save_errno = ERRNO_MAIL4XX;
         addr->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8;
         }
-      while (count-- > 0)
-        {
-        if (!smtp_read_response(sx, flushbuffer, sizeof(flushbuffer),
-                   '2', ob->command_timeout)
-            && (errno != 0 || flushbuffer[0] == 0))
-          break;
-        }
+      count = smtp_discard_responses(sx, ob, count);
       errno = save_errno;
       }
 
@@ -825,7 +1092,7 @@ if (sx->pending_MAIL)
     while (count-- > 0)                /* Mark any pending addrs with the host used */
       {
       while (addr->transport_return != PENDING_DEFER) addr = addr->next;
-      addr->host_used = sx->host;
+      addr->host_used = sx->conn_args.host;
       addr = addr->next;
       }
     return -3;
@@ -845,7 +1112,7 @@ while (count-- > 0)
       return -2;
 
   /* The address was accepted */
-  addr->host_used = sx->host;
+  addr->host_used = sx->conn_args.host;
 
   DEBUG(D_transport) debug_printf("%s expect rcpt\n", __FUNCTION__);
   if (smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
@@ -872,7 +1139,7 @@ while (count-- > 0)
   else if (errno == ETIMEDOUT)
     {
     uschar *message = string_sprintf("SMTP timeout after RCPT TO:<%s>",
-               transport_rcpt_address(addr, sx->tblock->rcpt_include_affixes));
+               transport_rcpt_address(addr, sx->conn_args.tblock->rcpt_include_affixes));
     set_errno_nohost(sx->first_addr, ETIMEDOUT, message, DEFER, FALSE);
     retry_add_item(addr, addr->address_retry_key, 0);
     update_waiting = FALSE;
@@ -887,7 +1154,7 @@ while (count-- > 0)
   else if (errno != 0 || sx->buffer[0] == 0)
     {
     string_format(big_buffer, big_buffer_size, "RCPT TO:<%s>",
-      transport_rcpt_address(addr, sx->tblock->rcpt_include_affixes));
+      transport_rcpt_address(addr, sx->conn_args.tblock->rcpt_include_affixes));
     return -2;
     }
 
@@ -897,11 +1164,11 @@ while (count-- > 0)
     {
     addr->message =
       string_sprintf("SMTP error from remote mail server after RCPT TO:<%s>: "
-       "%s", transport_rcpt_address(addr, sx->tblock->rcpt_include_affixes),
+       "%s", transport_rcpt_address(addr, sx->conn_args.tblock->rcpt_include_affixes),
        string_printing(sx->buffer));
     setflag(addr, af_pass_message);
     if (!sx->verify)
-      msglog_line(sx->host, addr->message);
+      msglog_line(sx->conn_args.host, addr->message);
 
     /* The response was 5xx */
 
@@ -929,9 +1196,9 @@ while (count-- > 0)
        /* Log temporary errors if there are more hosts to be tried.
        If not, log this last one in the == line. */
 
-       if (sx->host->next)
+       if (sx->conn_args.host->next)
          log_write(0, LOG_MAIN, "H=%s [%s]: %s",
-           sx->host->name, sx->host->address, addr->message);
+           sx->conn_args.host->name, sx->conn_args.host->address, addr->message);
 
 #ifndef DISABLE_EVENT
        else
@@ -983,7 +1250,7 @@ if (pending_DATA != 0)
        }
       return -3;
       }
-    (void)check_response(sx->host, &errno, 0, sx->buffer, &code, &msg, &pass_message);
+    (void)check_response(sx->conn_args.host, &errno, 0, sx->buffer, &code, &msg, &pass_message);
     DEBUG(D_transport) debug_printf("%s\nerror for DATA ignored: pipelining "
       "is in use and there were no good recipients\n", msg);
     }
@@ -998,11 +1265,82 @@ return yield;
 
 
 
-/* Do the client side of smtp-level authentication */
-/*
+
+
+/* Try an authenticator's client entry */
+
+static int
+try_authenticator(smtp_context * sx, auth_instance * au)
+{
+smtp_transport_options_block * ob = sx->conn_args.ob;  /* transport options */
+host_item * host = sx->conn_args.host;                 /* host to deliver to */
+int rc;
+
+sx->outblock.authenticating = TRUE;
+rc = (au->info->clientcode)(au, sx, ob->command_timeout,
+                           sx->buffer, sizeof(sx->buffer));
+sx->outblock.authenticating = FALSE;
+DEBUG(D_transport) debug_printf("%s authenticator yielded %d\n", au->name, rc);
+
+/* A temporary authentication failure must hold up delivery to
+this host. After a permanent authentication failure, we carry on
+to try other authentication methods. If all fail hard, try to
+deliver the message unauthenticated unless require_auth was set. */
+
+switch(rc)
+  {
+  case OK:
+    f.smtp_authenticated = TRUE;   /* stops the outer loop */
+    client_authenticator = au->name;
+    if (au->set_client_id)
+      client_authenticated_id = expand_string(au->set_client_id);
+    break;
+
+  /* Failure after writing a command */
+
+  case FAIL_SEND:
+    return FAIL_SEND;
+
+  /* Failure after reading a response */
+
+  case FAIL:
+    if (errno != 0 || sx->buffer[0] != '5') return FAIL;
+    log_write(0, LOG_MAIN, "%s authenticator failed H=%s [%s] %s",
+      au->name, host->name, host->address, sx->buffer);
+    break;
+
+  /* Failure by some other means. In effect, the authenticator
+  decided it wasn't prepared to handle this case. Typically this
+  is the result of "fail" in an expansion string. Do we need to
+  log anything here? Feb 2006: a message is now put in the buffer
+  if logging is required. */
+
+  case CANCELLED:
+    if (*sx->buffer != 0)
+      log_write(0, LOG_MAIN, "%s authenticator cancelled "
+       "authentication H=%s [%s] %s", au->name, host->name,
+       host->address, sx->buffer);
+    break;
+
+  /* Internal problem, message in buffer. */
+
+  case ERROR:
+    set_errno_nohost(sx->addrlist, ERRNO_AUTHPROB, string_copy(sx->buffer),
+             DEFER, FALSE);
+    return ERROR;
+  }
+return OK;
+}
+
+
+
+
+/* Do the client side of smtp-level authentication.
+
 Arguments:
-  sx           smtp connection
-  buffer       EHLO response from server (gets overwritten)
+  sx           smtp connection context
+
+sx->buffer should have the EHLO response from server (gets overwritten)
 
 Returns:
   OK                   Success, or failed (but not required): global "smtp_authenticated" set
@@ -1014,35 +1352,90 @@ Returns:
 */
 
 static int
-smtp_auth(smtp_context * sx, uschar * buffer, unsigned bufsize)
+smtp_auth(smtp_context * sx)
 {
-smtp_transport_options_block * ob = sx->ob;
-int require_auth;
-uschar *fail_reason = US"server did not advertise AUTH support";
+host_item * host = sx->conn_args.host;                 /* host to deliver to */
+smtp_transport_options_block * ob = sx->conn_args.ob;  /* transport options */
+int require_auth = verify_check_given_host(CUSS &ob->hosts_require_auth, host);
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+unsigned short authbits = tls_out.active.sock >= 0
+      ? sx->ehlo_resp.crypted_auths : sx->ehlo_resp.cleartext_auths;
+#endif
+uschar * fail_reason = US"server did not advertise AUTH support";
 
 f.smtp_authenticated = FALSE;
 client_authenticator = client_authenticated_id = client_authenticated_sender = NULL;
-require_auth = verify_check_given_host(CUSS &ob->hosts_require_auth, sx->host);
 
-if (sx->esmtp && !regex_AUTH) regex_AUTH =
-    regex_must_compile(US"\\n250[\\s\\-]AUTH\\s+([\\-\\w\\s]+)(?:\\n|$)",
-         FALSE, TRUE);
+if (!regex_AUTH)
+  regex_AUTH = regex_must_compile(AUTHS_REGEX, FALSE, TRUE);
+
+/* Is the server offering AUTH? */
 
-if (sx->esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
+if (  sx->esmtp
+   &&
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+      sx->early_pipe_active ? authbits
+      :
+#endif
+       regex_match_and_setup(regex_AUTH, sx->buffer, 0, -1)
+   )
   {
-  uschar *names = string_copyn(expand_nstring[1], expand_nlength[1]);
+  uschar * names = NULL;
   expand_nmax = -1;                          /* reset */
 
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  if (!sx->early_pipe_active)
+#endif
+    names = string_copyn(expand_nstring[1], expand_nlength[1]);
+
   /* Must not do this check until after we have saved the result of the
-  regex match above. */
+  regex match above as the check could be another RE. */
 
-  if (require_auth == OK ||
-      verify_check_given_host(CUSS &ob->hosts_try_auth, sx->host) == OK)
+  if (  require_auth == OK
+     || verify_check_given_host(CUSS &ob->hosts_try_auth, host) == OK)
     {
-    auth_instance *au;
-    fail_reason = US"no common mechanisms were found";
+    auth_instance * au;
 
     DEBUG(D_transport) debug_printf("scanning authentication mechanisms\n");
+    fail_reason = US"no common mechanisms were found";
+
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+    if (sx->early_pipe_active)
+      {
+      /* Scan our authenticators (which support use by a client and were offered
+      by the server (checked at cache-write time)), not suppressed by
+      client_condition.  If one is found, attempt to authenticate by calling its
+      client function.  We are limited to supporting up to 16 authenticator
+      public-names by the number of bits in a short. */
+
+      uschar bitnum;
+      int rc;
+
+      for (bitnum = 0, au = auths;
+          !f.smtp_authenticated && au && bitnum < 16;
+          bitnum++, au = au->next) if (authbits & BIT(bitnum))
+       {
+       if (  au->client_condition
+          && !expand_check_condition(au->client_condition, au->name,
+                   US"client authenticator"))
+         {
+         DEBUG(D_transport) debug_printf("skipping %s authenticator: %s\n",
+           au->name, "client_condition is false");
+         continue;
+         }
+
+       /* Found data for a listed mechanism. Call its client entry. Set
+       a flag in the outblock so that data is overwritten after sending so
+       that reflections don't show it. */
+
+       fail_reason = US"authentication attempt(s) failed";
+
+       if ((rc = try_authenticator(sx, au)) != OK)
+         return rc;
+       }
+      }
+    else
+#endif
 
     /* Scan the configured authenticators looking for one which is configured
     for use as a client, which is not suppressed by client_condition, and
@@ -1053,10 +1446,11 @@ if (sx->esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
     for (au = auths; !f.smtp_authenticated && au; au = au->next)
       {
       uschar *p = names;
-      if (!au->client ||
-         (au->client_condition != NULL &&
-          !expand_check_condition(au->client_condition, au->name,
-            US"client authenticator")))
+
+      if (  !au->client
+         || (   au->client_condition
+           &&  !expand_check_condition(au->client_condition, au->name,
+                  US"client authenticator")))
        {
        DEBUG(D_transport) debug_printf("skipping %s authenticator: %s\n",
          au->name,
@@ -1069,8 +1463,9 @@ if (sx->esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
 
       while (*p)
        {
-       int rc;
        int len = Ustrlen(au->public_name);
+       int rc;
+
        while (isspace(*p)) p++;
 
        if (strncmpic(au->public_name, p, len) != 0 ||
@@ -1085,60 +1480,9 @@ if (sx->esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
        that reflections don't show it. */
 
        fail_reason = US"authentication attempt(s) failed";
-       sx->outblock.authenticating = TRUE;
-       rc = (au->info->clientcode)(au, sx,
-         ob->command_timeout, buffer, bufsize);
-       sx->outblock.authenticating = FALSE;
-       DEBUG(D_transport) debug_printf("%s authenticator yielded %d\n",
-         au->name, rc);
-
-       /* A temporary authentication failure must hold up delivery to
-       this host. After a permanent authentication failure, we carry on
-       to try other authentication methods. If all fail hard, try to
-       deliver the message unauthenticated unless require_auth was set. */
-
-       switch(rc)
-         {
-         case OK:
-         f.smtp_authenticated = TRUE;   /* stops the outer loop */
-         client_authenticator = au->name;
-         if (au->set_client_id != NULL)
-           client_authenticated_id = expand_string(au->set_client_id);
-         break;
 
-         /* Failure after writing a command */
-
-         case FAIL_SEND:
-         return FAIL_SEND;
-
-         /* Failure after reading a response */
-
-         case FAIL:
-         if (errno != 0 || buffer[0] != '5') return FAIL;
-         log_write(0, LOG_MAIN, "%s authenticator failed H=%s [%s] %s",
-           au->name, sx->host->name, sx->host->address, buffer);
-         break;
-
-         /* Failure by some other means. In effect, the authenticator
-         decided it wasn't prepared to handle this case. Typically this
-         is the result of "fail" in an expansion string. Do we need to
-         log anything here? Feb 2006: a message is now put in the buffer
-         if logging is required. */
-
-         case CANCELLED:
-         if (*buffer != 0)
-           log_write(0, LOG_MAIN, "%s authenticator cancelled "
-             "authentication H=%s [%s] %s", au->name, sx->host->name,
-             sx->host->address, buffer);
-         break;
-
-         /* Internal problem, message in buffer. */
-
-         case ERROR:
-         set_errno_nohost(sx->addrlist, ERRNO_AUTHPROB, string_copy(buffer),
-                   DEFER, FALSE);
-         return ERROR;
-         }
+       if ((rc = try_authenticator(sx, au)) != OK)
+         return rc;
 
        break;  /* If not authenticated, try next authenticator */
        }       /* Loop for scanning supported server mechanisms */
@@ -1310,8 +1654,7 @@ uschar * tlsc1 = US"";
 #endif
 uschar * save_sender_address = sender_address;
 uschar * local_identity = NULL;
-smtp_transport_options_block * ob =
-  (smtp_transport_options_block *)tblock->options_block;
+smtp_transport_options_block * ob = SOB tblock->options_block;
 
 sender_address = sender;
 
@@ -1371,17 +1714,19 @@ ehlo_response(uschar * buf, unsigned checks)
 {
 size_t bsize = Ustrlen(buf);
 
-#ifdef SUPPORT_TLS
-if (  checks & OPTION_TLS
-   && pcre_exec(regex_STARTTLS, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
-  checks &= ~OPTION_TLS;
+/* debug_printf("%s: check for 0x%04x\n", __FUNCTION__, checks); */
 
+#ifdef SUPPORT_TLS
 # ifdef EXPERIMENTAL_REQUIRETLS
 if (  checks & OPTION_REQUIRETLS
    && pcre_exec(regex_REQUIRETLS, NULL, CS buf,bsize, 0, PCRE_EOPT, NULL,0) < 0)
-  checks &= ~OPTION_REQUIRETLS;
 # endif
+  checks &= ~OPTION_REQUIRETLS;
+
+if (  checks & OPTION_TLS
+   && pcre_exec(regex_STARTTLS, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
 #endif
+  checks &= ~OPTION_TLS;
 
 if (  checks & OPTION_IGNQ
    && pcre_exec(regex_IGNOREQUOTA, NULL, CS buf, bsize, 0,
@@ -1395,14 +1740,14 @@ if (  checks & OPTION_CHUNKING
 #ifndef DISABLE_PRDR
 if (  checks & OPTION_PRDR
    && pcre_exec(regex_PRDR, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
-  checks &= ~OPTION_PRDR;
 #endif
+  checks &= ~OPTION_PRDR;
 
 #ifdef SUPPORT_I18N
 if (  checks & OPTION_UTF8
    && pcre_exec(regex_UTF8, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
-  checks &= ~OPTION_UTF8;
 #endif
+  checks &= ~OPTION_UTF8;
 
 if (  checks & OPTION_DSN
    && pcre_exec(regex_DSN, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
@@ -1417,6 +1762,14 @@ if (  checks & OPTION_SIZE
    && pcre_exec(regex_SIZE, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
   checks &= ~OPTION_SIZE;
 
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+if (  checks & OPTION_EARLY_PIPE
+   && pcre_exec(regex_EARLY_PIPE, NULL, CS buf, bsize, 0,
+               PCRE_EOPT, NULL, 0) < 0)
+#endif
+  checks &= ~OPTION_EARLY_PIPE;
+
+/* debug_printf("%s: found     0x%04x\n", __FUNCTION__, checks); */
 return checks;
 }
 
@@ -1444,8 +1797,7 @@ static int
 smtp_chunk_cmd_callback(transport_ctx * tctx, unsigned chunk_size,
   unsigned flags)
 {
-smtp_transport_options_block * ob =
-  (smtp_transport_options_block *)(tctx->tblock->options_block);
+smtp_transport_options_block * ob = SOB tctx->tblock->options_block;
 smtp_context * sx = tctx->smtp_context;
 int cmd_count = 0;
 int prev_cmd_count;
@@ -1455,12 +1807,23 @@ there may be more writes (like, the chunk data) done soon. */
 
 if (chunk_size > 0)
   {
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  BOOL new_conn = !!(sx->outblock.conn_args);
+#endif
   if((cmd_count = smtp_write_command(sx,
              flags & tc_reap_prev ? SCMD_FLUSH : SCMD_MORE,
              "BDAT %u%s\r\n", chunk_size, flags & tc_chunk_last ? " LAST" : "")
      ) < 0) return ERROR;
   if (flags & tc_chunk_last)
     data_command = string_copy(big_buffer);  /* Save for later error message */
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  /* That command write could have been the one that made the connection.
+  Copy the fd from the client conn ctx (smtp transport specific) to the
+  generic transport ctx. */
+
+  if (new_conn)
+    tctx->u.fd = sx->outblock.cctx->sock;
+#endif
   }
 
 prev_cmd_count = cmd_count += sx->cmd_count;
@@ -1485,6 +1848,9 @@ if (flags & tc_reap_prev  &&  prev_cmd_count > 0)
     case 0: break;                     /* No 2xx or 5xx, but no probs */
 
     case -1:                           /* Timeout on RCPT */
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+    case -4:                           /* non-2xx for pipelined banner or EHLO */
+#endif
     default: return ERROR;             /* I/O error, or any MAIL/DATA error */
     }
   cmd_count = 1;
@@ -1523,6 +1889,8 @@ return OK;
 
 
 
+
+
 /*************************************************
 *       Make connection for given message        *
 *************************************************/
@@ -1548,15 +1916,16 @@ smtp_setup_conn(smtp_context * sx, BOOL suppress_tls)
 #if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
 dns_answer tlsa_dnsa;
 #endif
+smtp_transport_options_block * ob = sx->conn_args.tblock->options_block;
 BOOL pass_message = FALSE;
 uschar * message = NULL;
 int yield = OK;
 int rc;
 
-sx->ob = (smtp_transport_options_block *) sx->tblock->options_block;
+sx->conn_args.ob = ob;
 
-sx->lmtp = strcmpic(sx->ob->protocol, US"lmtp") == 0;
-sx->smtps = strcmpic(sx->ob->protocol, US"smtps") == 0;
+sx->lmtp = strcmpic(ob->protocol, US"lmtp") == 0;
+sx->smtps = strcmpic(ob->protocol, US"smtps") == 0;
 sx->ok = FALSE;
 sx->send_rset = TRUE;
 sx->send_quit = TRUE;
@@ -1570,14 +1939,19 @@ sx->dsn_all_lasthop = TRUE;
 #if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
 sx->dane = FALSE;
 sx->dane_required =
-  verify_check_given_host(CUSS &sx->ob->hosts_require_dane, sx->host) == OK;
+  verify_check_given_host(CUSS &ob->hosts_require_dane, sx->conn_args.host) == OK;
+#endif
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+sx->early_pipe_active = sx->early_pipe_ok = FALSE;
+sx->ehlo_resp.cleartext_features = sx->ehlo_resp.crypted_features = 0;
+sx->pending_BANNER = sx->pending_EHLO = FALSE;
 #endif
 
-if ((sx->max_rcpt = sx->tblock->max_addresses) == 0) sx->max_rcpt = 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"";
-if (!sx->helo_data) sx->helo_data = sx->ob->helo_data;
+if (!sx->helo_data) sx->helo_data = ob->helo_data;
 #ifdef EXPERIMENTAL_DSN_INFO
 sx->smtp_greeting = NULL;
 sx->helo_response = NULL;
@@ -1600,6 +1974,7 @@ sx->outblock.buffersize = sizeof(sx->outbuffer);
 sx->outblock.ptr = sx->outbuffer;
 sx->outblock.cmd_count = 0;
 sx->outblock.authenticating = FALSE;
+sx->outblock.conn_args = NULL;
 
 /* Reset the parameters of a TLS session. */
 
@@ -1636,11 +2011,11 @@ specially so they can be identified for retries. */
 if (!continue_hostname)
   {
   if (sx->verify)
-    HDEBUG(D_verify) debug_printf("interface=%s port=%d\n", sx->interface, sx->port);
+    HDEBUG(D_verify) debug_printf("interface=%s port=%d\n", sx->conn_args.interface, sx->port);
 
-  /* Get the actual port the connection will use, into sx->host */
+  /* Get the actual port the connection will use, into sx->conn_args.host */
 
-  smtp_port_for_connect(sx->host, sx->port);
+  smtp_port_for_connect(sx->conn_args.host, sx->port);
 
 #if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
     /* Do TLSA lookup for DANE */
@@ -1648,15 +2023,15 @@ if (!continue_hostname)
     tls_out.dane_verified = FALSE;
     tls_out.tlsa_usage = 0;
 
-    if (sx->host->dnssec == DS_YES)
+    if (sx->conn_args.host->dnssec == DS_YES)
       {
       if(  sx->dane_required
-       || verify_check_given_host(CUSS &sx->ob->hosts_try_dane, sx->host) == OK
+       || verify_check_given_host(CUSS &ob->hosts_try_dane, sx->conn_args.host) == OK
        )
-       switch (rc = tlsa_lookup(sx->host, &tlsa_dnsa, sx->dane_required))
+       switch (rc = tlsa_lookup(sx->conn_args.host, &tlsa_dnsa, sx->dane_required))
          {
          case OK:              sx->dane = TRUE;
-                               sx->ob->tls_tempfail_tryclear = FALSE;
+                               ob->tls_tempfail_tryclear = FALSE;
                                break;
          case FAIL_FORCED:     break;
          default:              set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER,
@@ -1664,7 +2039,7 @@ if (!continue_hostname)
                                    rc == DEFER ? "DEFER" : "FAIL"),
                                  rc, FALSE);
 # ifndef DISABLE_EVENT
-                               (void) event_raise(sx->tblock->event_action,
+                               (void) event_raise(sx->conn_args.tblock->event_action,
                                  US"dane:fail", sx->dane_required
                                    ?  US"dane-required" : US"dnssec-invalid");
 # endif
@@ -1674,10 +2049,10 @@ if (!continue_hostname)
     else if (sx->dane_required)
       {
       set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER,
-       string_sprintf("DANE error: %s lookup not DNSSEC", sx->host->name),
+       string_sprintf("DANE error: %s lookup not DNSSEC", sx->conn_args.host->name),
        FAIL, FALSE);
 # ifndef DISABLE_EVENT
-      (void) event_raise(sx->tblock->event_action,
+      (void) event_raise(sx->conn_args.tblock->event_action,
        US"dane:fail", US"dane-required");
 # endif
       return FAIL;
@@ -1687,32 +2062,50 @@ if (!continue_hostname)
 
   /* Make the TCP connection */
 
-  sx->cctx.sock =
-    smtp_connect(sx->host, sx->host_af, sx->interface,
-                 sx->ob->connect_timeout, sx->tblock);
   sx->cctx.tls_ctx = NULL;
   sx->inblock.cctx = sx->outblock.cctx = &sx->cctx;
+  sx->avoid_option = sx->peer_offered = smtp_peer_options = 0;
 
-  if (sx->cctx.sock < 0)
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  if (verify_check_given_host(CUSS &ob->hosts_pipe_connect, sx->conn_args.host) == OK)
     {
-    uschar * msg = NULL;
-    if (sx->verify)
+    sx->early_pipe_ok = TRUE;
+    if (  read_ehlo_cache_entry(sx)
+       && sx->ehlo_resp.cleartext_features & OPTION_EARLY_PIPE)
       {
-      msg = US strerror(errno);
-      HDEBUG(D_verify) debug_printf("connect: %s\n", msg);
+      DEBUG(D_transport) debug_printf("Using cached cleartext PIPE_CONNECT\n");
+      sx->early_pipe_active = TRUE;
+      sx->peer_offered = sx->ehlo_resp.cleartext_features;
       }
-    set_errno_nohost(sx->addrlist,
-      errno == ETIMEDOUT ? ERRNO_CONNECTTIMEOUT : errno,
-      sx->verify ? string_sprintf("could not connect: %s", msg)
-            : NULL,
-      DEFER, FALSE);
-    sx->send_quit = FALSE;
-    return DEFER;
     }
 
+  if (sx->early_pipe_active)
+    sx->outblock.conn_args = &sx->conn_args;
+  else
+#endif
+    {
+    if ((sx->cctx.sock = smtp_connect(&sx->conn_args, NULL)) < 0)
+      {
+      uschar * msg = NULL;
+      if (sx->verify)
+       {
+       msg = US strerror(errno);
+       HDEBUG(D_verify) debug_printf("connect: %s\n", msg);
+       }
+      set_errno_nohost(sx->addrlist,
+       errno == ETIMEDOUT ? ERRNO_CONNECTTIMEOUT : errno,
+       sx->verify ? string_sprintf("could not connect: %s", msg)
+              : NULL,
+       DEFER, FALSE);
+      sx->send_quit = FALSE;
+      return DEFER;
+      }
+    }
   /* Expand the greeting message while waiting for the initial response. (Makes
   sense if helo_data contains ${lookup dnsdb ...} stuff). The expansion is
   delayed till here so that $sending_interface and $sending_port are set. */
+/*XXX early-pipe: they still will not be. Is there any way to find out what they
+will be?  Somehow I doubt it. */
 
   if (sx->helo_data)
     if (!(sx->helo_data = expand_string(sx->helo_data)))
@@ -1743,24 +2136,29 @@ if (!continue_hostname)
 
   if (!sx->smtps)
     {
-    BOOL good_response;
-
-#ifdef TCP_QUICKACK
-    (void) setsockopt(sx->cctx.sock, IPPROTO_TCP, TCP_QUICKACK, US &off, sizeof(off));
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+    if (sx->early_pipe_active)
+      {
+      sx->pending_BANNER = TRUE;       /* sync_responses() must eventually handle */
+      sx->outblock.cmd_count = 1;
+      }
+    else
 #endif
-    good_response = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
-      '2', sx->ob->command_timeout);
-#ifdef EXPERIMENTAL_DSN_INFO
-    sx->smtp_greeting = string_copy(sx->buffer);
+      {
+#ifdef TCP_QUICKACK
+      (void) setsockopt(sx->cctx.sock, IPPROTO_TCP, TCP_QUICKACK, US &off,
+                       sizeof(off));
 #endif
-    if (!good_response) goto RESPONSE_FAILED;
+      if (!smtp_reap_banner(sx))
+       goto RESPONSE_FAILED;
+      }
 
 #ifndef DISABLE_EVENT
       {
       uschar * s;
-      lookup_dnssec_authenticated = sx->host->dnssec==DS_YES ? US"yes"
-       : sx->host->dnssec==DS_NO ? US"no" : NULL;
-      s = event_raise(sx->tblock->event_action, US"smtp:connect", sx->buffer);
+      lookup_dnssec_authenticated = sx->conn_args.host->dnssec==DS_YES ? US"yes"
+       : sx->conn_args.host->dnssec==DS_NO ? US"no" : NULL;
+      s = event_raise(sx->conn_args.tblock->event_action, US"smtp:connect", sx->buffer);
       if (s)
        {
        set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL,
@@ -1820,7 +2218,7 @@ goto SEND_QUIT;
   mailers use upper case for some reason (the RFC is quite clear about case
   independence) so, for peace of mind, I gave in. */
 
-  sx->esmtp = verify_check_given_host(CUSS &sx->ob->hosts_avoid_esmtp, sx->host) != OK;
+  sx->esmtp = verify_check_given_host(CUSS &ob->hosts_avoid_esmtp, sx->conn_args.host) != OK;
 
   /* Alas; be careful, since this goto is not an error-out, so conceivably
   we might set data between here and the target which we assume to exist
@@ -1830,7 +2228,7 @@ goto SEND_QUIT;
     {
     smtp_peer_options |= OPTION_TLS;
     suppress_tls = FALSE;
-    sx->ob->tls_tempfail_tryclear = FALSE;
+    ob->tls_tempfail_tryclear = FALSE;
     smtp_command = US"SSL-on-connect";
     goto TLS_NEGOTIATE;
     }
@@ -1838,67 +2236,116 @@ goto SEND_QUIT;
 
   if (sx->esmtp)
     {
-    if (smtp_write_command(sx, SCMD_FLUSH, "%s %s\r\n",
-         sx->lmtp ? "LHLO" : "EHLO", sx->helo_data) < 0)
+    if (smtp_write_command(sx,
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+         sx->early_pipe_active ? SCMD_BUFFER :
+#endif
+           SCMD_FLUSH,
+         "%s %s\r\n", sx->lmtp ? "LHLO" : "EHLO", sx->helo_data) < 0)
       goto SEND_FAILED;
     sx->esmtp_sent = TRUE;
-    if (!smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2',
-           sx->ob->command_timeout))
+
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+    if (sx->early_pipe_active)
       {
-      if (errno != 0 || sx->buffer[0] == 0 || sx->lmtp)
+      sx->pending_EHLO = TRUE;
+
+      /* If we have too many authenticators to handle and might need to AUTH
+      for this transport, pipeline no further as we will need the
+      list of auth methods offered.  Reap the banner and EHLO. */
+
+      if (  (ob->hosts_require_auth || ob->hosts_try_auth)
+        && f.smtp_in_early_pipe_no_auth)
        {
-#ifdef EXPERIMENTAL_DSN_INFO
-       sx->helo_response = string_copy(sx->buffer);
-#endif
-       goto RESPONSE_FAILED;
+       DEBUG(D_transport) debug_printf("may need to auth, so pipeline no further\n");
+       if (smtp_write_command(sx, SCMD_FLUSH, NULL) < 0)
+         goto SEND_FAILED;
+       if (sync_responses(sx, 2, 0) != 0)
+         {
+         HDEBUG(D_transport)
+           debug_printf("failed reaping pipelined cmd responses\n");
+         goto RESPONSE_FAILED;
+         }
+       sx->early_pipe_active = FALSE;
        }
-      sx->esmtp = FALSE;
       }
-#ifdef EXPERIMENTAL_DSN_INFO
-    sx->helo_response = string_copy(sx->buffer);
+    else
 #endif
+      if (!smtp_reap_ehlo(sx))
+       goto RESPONSE_FAILED;
     }
   else
     DEBUG(D_transport)
       debug_printf("not sending EHLO (host matches hosts_avoid_esmtp)\n");
 
-  if (!sx->esmtp)
-    {
-    BOOL good_response;
-    int n = sizeof(sx->buffer);
-    uschar * rsp = sx->buffer;
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  if (!sx->early_pipe_active)
+#endif
+    if (!sx->esmtp)
+      {
+      BOOL good_response;
+      int n = sizeof(sx->buffer);
+      uschar * rsp = sx->buffer;
 
-    if (sx->esmtp_sent && (n = Ustrlen(sx->buffer)) < sizeof(sx->buffer)/2)
-      { rsp = sx->buffer + n + 1; n = sizeof(sx->buffer) - n; }
+      if (sx->esmtp_sent && (n = Ustrlen(sx->buffer)) < sizeof(sx->buffer)/2)
+       { rsp = sx->buffer + n + 1; n = sizeof(sx->buffer) - n; }
 
-    if (smtp_write_command(sx, SCMD_FLUSH, "HELO %s\r\n", sx->helo_data) < 0)
-      goto SEND_FAILED;
-    good_response = smtp_read_response(sx, rsp, n, '2', sx->ob->command_timeout);
+      if (smtp_write_command(sx, SCMD_FLUSH, "HELO %s\r\n", sx->helo_data) < 0)
+       goto SEND_FAILED;
+      good_response = smtp_read_response(sx, rsp, n, '2', ob->command_timeout);
 #ifdef EXPERIMENTAL_DSN_INFO
-    sx->helo_response = string_copy(rsp);
+      sx->helo_response = string_copy(rsp);
 #endif
-    if (!good_response)
-      {
-      /* Handle special logging for a closed connection after HELO
-      when had previously sent EHLO */
-
-      if (rsp != sx->buffer && rsp[0] == 0 && (errno == 0 || errno == ECONNRESET))
+      if (!good_response)
        {
-       errno = ERRNO_SMTPCLOSED;
-       goto EHLOHELO_FAILED;
+       /* Handle special logging for a closed connection after HELO
+       when had previously sent EHLO */
+
+       if (rsp != sx->buffer && rsp[0] == 0 && (errno == 0 || errno == ECONNRESET))
+         {
+         errno = ERRNO_SMTPCLOSED;
+         goto EHLOHELO_FAILED;
+         }
+       memmove(sx->buffer, rsp, Ustrlen(rsp));
+       goto RESPONSE_FAILED;
        }
-      memmove(sx->buffer, rsp, Ustrlen(rsp));
-      goto RESPONSE_FAILED;
       }
-    }
-
-  sx->avoid_option = sx->peer_offered = smtp_peer_options = 0;
 
   if (sx->esmtp || sx->lmtp)
     {
-    sx->peer_offered = ehlo_response(sx->buffer,
-      OPTION_TLS       /* others checked later */
-      );
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+    if (!sx->early_pipe_active)
+#endif
+      {
+      sx->peer_offered = ehlo_response(sx->buffer,
+       OPTION_TLS      /* others checked later */
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+       | (sx->early_pipe_ok
+         ?   OPTION_IGNQ
+           | OPTION_CHUNKING | OPTION_PRDR | OPTION_DSN | OPTION_PIPE | OPTION_SIZE
+#ifdef SUPPORT_I18N
+           | OPTION_UTF8
+#endif
+           | OPTION_EARLY_PIPE
+         : 0
+         )
+#endif
+       );
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+      if (sx->early_pipe_ok)
+       {
+       sx->ehlo_resp.cleartext_features = sx->peer_offered;
+
+       if (  (sx->peer_offered & (OPTION_PIPE | OPTION_EARLY_PIPE))
+          == (OPTION_PIPE | OPTION_EARLY_PIPE))
+         {
+         DEBUG(D_transport) debug_printf("PIPE_CONNECT usable in future for this IP\n");
+         sx->ehlo_resp.cleartext_auths = study_ehlo_auths(sx);
+         write_ehlo_cache_entry(sx);
+         }
+       }
+#endif
+      }
 
   /* Set tls_offered if the response to EHLO specifies support for STARTTLS. */
 
@@ -1927,13 +2374,13 @@ else
   if (cutthrough.cctx.sock >= 0 && cutthrough.callout_hold_only)
     {
     sx->cctx = cutthrough.cctx;
-    sx->host->port = sx->port = cutthrough.host.port;
+    sx->conn_args.host->port = sx->port = cutthrough.host.port;
     }
   else
     {
     sx->cctx.sock = 0;                         /* stdin */
     sx->cctx.tls_ctx = NULL;
-    smtp_port_for_connect(sx->host, sx->port); /* Record the port that was used */
+    smtp_port_for_connect(sx->conn_args.host, sx->port);       /* Record the port that was used */
     }
   sx->inblock.cctx = sx->outblock.cctx = &sx->cctx;
   smtp_command = big_buffer;
@@ -1967,15 +2414,28 @@ for error analysis. */
 #ifdef SUPPORT_TLS
 if (  smtp_peer_options & OPTION_TLS
    && !suppress_tls
-   && verify_check_given_host(CUSS &sx->ob->hosts_avoid_tls, sx->host) != OK
+   && verify_check_given_host(CUSS &ob->hosts_avoid_tls, sx->conn_args.host) != OK
    && (  !sx->verify
-      || verify_check_given_host(CUSS &sx->ob->hosts_verify_avoid_tls, sx->host) != OK
+      || verify_check_given_host(CUSS &ob->hosts_verify_avoid_tls, sx->conn_args.host) != OK
    )  )
   {
   uschar buffer2[4096];
+
   if (smtp_write_command(sx, SCMD_FLUSH, "STARTTLS\r\n") < 0)
     goto SEND_FAILED;
 
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  /* If doing early-pipelining reap the banner and EHLO-response but leave
+  the response for the STARTTLS we just sent alone. */
+
+  if (sx->early_pipe_active && sync_responses(sx, 2, 0) != 0)
+    {
+    HDEBUG(D_transport)
+      debug_printf("failed reaping pipelined cmd responses\n");
+    goto RESPONSE_FAILED;
+    }
+#endif
+
   /* If there is an I/O error, transmission of this message is deferred. If
   there is a temporary rejection of STARRTLS and tls_tempfail_tryclear is
   false, we also defer. However, if there is a temporary rejection of STARTTLS
@@ -1983,12 +2443,11 @@ if (  smtp_peer_options & OPTION_TLS
   STARTTLS, we carry on. This means we will try to send the message in clear,
   unless the host is in hosts_require_tls (tested below). */
 
-  if (!smtp_read_response(sx, buffer2, sizeof(buffer2), '2',
-      sx->ob->command_timeout))
+  if (!smtp_read_response(sx, buffer2, sizeof(buffer2), '2', ob->command_timeout))
     {
     if (  errno != 0
        || buffer2[0] == 0
-       || (buffer2[0] == '4' && !sx->ob->tls_tempfail_tryclear)
+       || (buffer2[0] == '4' && !ob->tls_tempfail_tryclear)
        )
       {
       Ustrncpy(sx->buffer, buffer2, sizeof(sx->buffer));
@@ -2004,8 +2463,8 @@ if (  smtp_peer_options & OPTION_TLS
     {
     address_item * addr;
     uschar * errstr;
-    sx->cctx.tls_ctx = tls_client_start(sx->cctx.sock, sx->host,
-                           sx->addrlist, sx->tblock,
+    sx->cctx.tls_ctx = tls_client_start(sx->cctx.sock, sx->conn_args.host,
+                           sx->addrlist, sx->conn_args.tblock,
 # ifdef SUPPORT_DANE
                             sx->dane ? &tlsa_dnsa : NULL,
 # endif
@@ -2023,9 +2482,9 @@ if (  smtp_peer_options & OPTION_TLS
         {
        log_write(0, LOG_MAIN,
          "DANE attempt failed; TLS connection to %s [%s]: %s",
-         sx->host->name, sx->host->address, errstr);
+         sx->conn_args.host->name, sx->conn_args.host->address, errstr);
 #  ifndef DISABLE_EVENT
-       (void) event_raise(sx->tblock->event_action,
+       (void) event_raise(sx->conn_args.tblock->event_action,
          US"dane:fail", US"validation-failure");       /* could do with better detail */
 #  endif
        }
@@ -2064,10 +2523,9 @@ start of the Exim process (in exim.c). */
 
 if (tls_out.active.sock >= 0)
   {
-  char *greeting_cmd;
-  BOOL good_response;
+  uschar * greeting_cmd;
 
-  if (!sx->helo_data && !(sx->helo_data = expand_string(sx->ob->helo_data)))
+  if (!sx->helo_data && !(sx->helo_data = expand_string(ob->helo_data)))
     {
     uschar *message = string_sprintf("failed to expand helo_data: %s",
       expand_string_message);
@@ -2076,36 +2534,64 @@ if (tls_out.active.sock >= 0)
     goto SEND_QUIT;
     }
 
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  /* For SMTPS there is no cleartext early-pipe; use the crypted permission bit.
+  We're unlikely to get the group sent and delivered before the server sends its
+  banner, but it's still worth sending as a group.
+  For STARTTLS allow for cleartext early-pipe but no crypted early-pipe, but not
+  the reverse.  */
+
+  if (sx->smtps ? sx->early_pipe_ok : sx->early_pipe_active)
+    {
+    sx->peer_offered = sx->ehlo_resp.crypted_features;
+    if ((sx->early_pipe_active =
+        !!(sx->ehlo_resp.crypted_features & OPTION_EARLY_PIPE)))
+      DEBUG(D_transport) debug_printf("Using cached crypted PIPE_CONNECT\n");
+    }
+#endif
+
   /* For SMTPS we need to wait for the initial OK response. */
   if (sx->smtps)
-    {
-    good_response = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
-      '2', sx->ob->command_timeout);
-#ifdef EXPERIMENTAL_DSN_INFO
-    sx->smtp_greeting = string_copy(sx->buffer);
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+    if (sx->early_pipe_active)
+      {
+      sx->pending_BANNER = TRUE;
+      sx->outblock.cmd_count = 1;
+      }
+    else
 #endif
-    if (!good_response) goto RESPONSE_FAILED;
-    }
+      if (!smtp_reap_banner(sx))
+       goto RESPONSE_FAILED;
 
-  if (sx->esmtp)
-    greeting_cmd = "EHLO";
+  if (sx->lmtp)
+    greeting_cmd = US"LHLO";
+  else if (sx->esmtp)
+    greeting_cmd = US"EHLO";
   else
     {
-    greeting_cmd = "HELO";
+    greeting_cmd = US"HELO";
     DEBUG(D_transport)
       debug_printf("not sending EHLO (host matches hosts_avoid_esmtp)\n");
     }
 
-  if (smtp_write_command(sx, SCMD_FLUSH, "%s %s\r\n",
-        sx->lmtp ? "LHLO" : greeting_cmd, sx->helo_data) < 0)
+  if (smtp_write_command(sx,
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+       sx->early_pipe_active ? SCMD_BUFFER :
+#endif
+         SCMD_FLUSH,
+       "%s %s\r\n", greeting_cmd, sx->helo_data) < 0)
     goto SEND_FAILED;
-  good_response = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
-    '2', sx->ob->command_timeout);
-#ifdef EXPERIMENTAL_DSN_INFO
-  sx->helo_response = string_copy(sx->buffer);
+
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  if (sx->early_pipe_active)
+    sx->pending_EHLO = TRUE;
+  else
 #endif
-  if (!good_response) goto RESPONSE_FAILED;
-  smtp_peer_options = 0;
+    {
+    if (!smtp_reap_ehlo(sx))
+      goto RESPONSE_FAILED;
+    smtp_peer_options = 0;
+    }
   }
 
 /* If the host is required to use a secure channel, ensure that we
@@ -2118,7 +2604,7 @@ else if (  sx->smtps
 # ifdef EXPERIMENTAL_REQUIRETLS
        || tls_requiretls & REQUIRETLS_MSG
 # endif
-       || verify_check_given_host(CUSS &sx->ob->hosts_require_tls, sx->host) == OK
+       || verify_check_given_host(CUSS &ob->hosts_require_tls, sx->conn_args.host) == OK
        )
   {
   errno =
@@ -2131,7 +2617,7 @@ else if (  sx->smtps
     ? "an attempt to start TLS failed" : "the server did not offer TLS support");
 # if defined(SUPPORT_DANE) && !defined(DISABLE_EVENT)
   if (sx->dane)
-    (void) event_raise(sx->tblock->event_action, US"dane:fail",
+    (void) event_raise(sx->conn_args.tblock->event_action, US"dane:fail",
       smtp_peer_options & OPTION_TLS
       ? US"validation-failure"         /* could do with better detail */
       : US"starttls-not-supported");
@@ -2153,23 +2639,41 @@ if (continue_hostname == NULL
   {
   if (sx->esmtp || sx->lmtp)
     {
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  if (!sx->early_pipe_active)
+#endif
+    {
     sx->peer_offered = ehlo_response(sx->buffer,
        0 /* no TLS */
-       | (sx->lmtp && sx->ob->lmtp_ignore_quota ? OPTION_IGNQ : 0)
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+       | (sx->lmtp && ob->lmtp_ignore_quota ? OPTION_IGNQ : 0)
+       | OPTION_DSN | OPTION_PIPE | OPTION_SIZE
+       | OPTION_CHUNKING | OPTION_PRDR | OPTION_UTF8 | OPTION_REQUIRETLS
+       | (tls_out.active.sock >= 0 ? OPTION_EARLY_PIPE : 0) /* not for lmtp */
+
+#else
+
+       | (sx->lmtp && ob->lmtp_ignore_quota ? OPTION_IGNQ : 0)
        | OPTION_CHUNKING
        | OPTION_PRDR
-#ifdef SUPPORT_I18N
+# ifdef SUPPORT_I18N
        | (sx->addrlist->prop.utf8_msg ? OPTION_UTF8 : 0)
          /*XXX if we hand peercaps on to continued-conn processes,
                must not depend on this addr */
-#endif
+# endif
        | OPTION_DSN
        | OPTION_PIPE
-       | (sx->ob->size_addition >= 0 ? OPTION_SIZE : 0)
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+       | (ob->size_addition >= 0 ? OPTION_SIZE : 0)
+# if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
        | (tls_requiretls & REQUIRETLS_MSG ? OPTION_REQUIRETLS : 0)
+# endif
 #endif
       );
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+    if (tls_out.active.sock >= 0)
+      sx->ehlo_resp.crypted_features = sx->peer_offered;
+#endif
+    }
 
     /* Set for IGNOREQUOTA if the response to LHLO specifies support and the
     lmtp_ignore_quota option was set. */
@@ -2186,26 +2690,26 @@ if (continue_hostname == NULL
     the current host matches hosts_avoid_pipelining, don't do it. */
 
     if (  sx->peer_offered & OPTION_PIPE
-       && verify_check_given_host(CUSS &sx->ob->hosts_avoid_pipelining, sx->host) != OK)
+       && verify_check_given_host(CUSS &ob->hosts_avoid_pipelining, sx->conn_args.host) != OK)
       smtp_peer_options |= OPTION_PIPE;
 
     DEBUG(D_transport) debug_printf("%susing PIPELINING\n",
       smtp_peer_options & OPTION_PIPE ? "" : "not ");
 
     if (  sx->peer_offered & OPTION_CHUNKING
-       && verify_check_given_host(CUSS &sx->ob->hosts_try_chunking, sx->host) != OK)
+       && verify_check_given_host(CUSS &ob->hosts_try_chunking, sx->conn_args.host) != OK)
       sx->peer_offered &= ~OPTION_CHUNKING;
 
     if (sx->peer_offered & OPTION_CHUNKING)
-      {DEBUG(D_transport) debug_printf("CHUNKING usable\n");}
+      DEBUG(D_transport) debug_printf("CHUNKING usable\n");
 
 #ifndef DISABLE_PRDR
     if (  sx->peer_offered & OPTION_PRDR
-       && verify_check_given_host(CUSS &sx->ob->hosts_try_prdr, sx->host) != OK)
+       && verify_check_given_host(CUSS &ob->hosts_try_prdr, sx->conn_args.host) != OK)
       sx->peer_offered &= ~OPTION_PRDR;
 
     if (sx->peer_offered & OPTION_PRDR)
-      {DEBUG(D_transport) debug_printf("PRDR usable\n");}
+      DEBUG(D_transport) debug_printf("PRDR usable\n");
 #endif
 
     /* Note if the server supports DSN */
@@ -2223,12 +2727,26 @@ if (continue_hostname == NULL
       }
 #endif
 
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+    if (  sx->early_pipe_ok
+       && !sx->early_pipe_active
+       && tls_out.active.sock >= 0
+       && smtp_peer_options & OPTION_PIPE
+       && ( sx->ehlo_resp.cleartext_features | sx->ehlo_resp.crypted_features)
+         & OPTION_EARLY_PIPE)
+      {
+      DEBUG(D_transport) debug_printf("PIPE_CONNECT usable in future for this IP\n");
+      sx->ehlo_resp.crypted_auths = study_ehlo_auths(sx);
+      write_ehlo_cache_entry(sx);
+      }
+#endif
+
     /* Note if the response to EHLO specifies support for the AUTH extension.
     If it has, check that this host is one we want to authenticate to, and do
     the business. The host name and address must be available when the
     authenticator's client driver is running. */
 
-    switch (yield = smtp_auth(sx, sx->buffer, sizeof(sx->buffer)))
+    switch (yield = smtp_auth(sx))
       {
       default:         goto SEND_QUIT;
       case OK:         break;
@@ -2252,7 +2770,7 @@ if (sx->addrlist->prop.utf8_msg)
   /* If the transport sets a downconversion mode it overrides any set by ACL
   for the message. */
 
-  if ((s = sx->ob->utf8_downconvert))
+  if ((s = ob->utf8_downconvert))
     {
     if (!(s = expand_string(s)))
       {
@@ -2318,7 +2836,7 @@ return OK;
 
   RESPONSE_FAILED:
     message = NULL;
-    sx->send_quit = check_response(sx->host, &errno, sx->addrlist->more_errno,
+    sx->send_quit = check_response(sx->conn_args.host, &errno, sx->addrlist->more_errno,
       sx->buffer, &code, &message, &pass_message);
     yield = DEFER;
     goto FAILED;
@@ -2326,7 +2844,7 @@ return OK;
   SEND_FAILED:
     code = '4';
     message = US string_sprintf("send() to %s [%s] failed: %s",
-      sx->host->name, sx->host->address, strerror(errno));
+      sx->conn_args.host->name, sx->conn_args.host->address, strerror(errno));
     sx->send_quit = FALSE;
     yield = DEFER;
     goto FAILED;
@@ -2373,14 +2891,14 @@ return OK;
 FAILED:
   sx->ok = FALSE;                /* For when reached by GOTO */
   set_errno(sx->addrlist, errno, message,
-           sx->host->next
+           sx->conn_args.host->next
            ? DEFER
            : code == '5'
 #ifdef SUPPORT_I18N
                        || errno == ERRNO_UTF8_FWD
 #endif
            ? FAIL : DEFER,
-           pass_message, sx->host
+           pass_message, sx->conn_args.host
 #ifdef EXPERIMENTAL_DSN_INFO
            , sx->smtp_greeting, sx->helo_response
 #endif
@@ -2419,7 +2937,7 @@ if (sx->send_quit)
 sx->cctx.sock = -1;
 
 #ifndef DISABLE_EVENT
-(void) event_raise(sx->tblock->event_action, US"tcp:close", NULL);
+(void) event_raise(sx->conn_args.tblock->event_action, US"tcp:close", NULL);
 #endif
 
 continue_transport = NULL;
@@ -2455,7 +2973,7 @@ if (  message_size > 0
 /*XXX problem here under spool_files_wireformat?
 Or just forget about lines?  Or inflate by a fixed proportion? */
 
-  sprintf(CS p, " SIZE=%d", message_size+message_linecount+sx->ob->size_addition);
+  sprintf(CS p, " SIZE=%d", message_size+message_linecount+(SOB sx->conn_args.ob)->size_addition);
   while (*p) p++;
   }
 
@@ -2531,7 +3049,7 @@ Other expansion failures are serious. An empty result is ignored, but there is
 otherwise no check - this feature is expected to be used with LMTP and other
 cases where non-standard addresses (e.g. without domains) might be required. */
 
-if (smtp_mail_auth_str(p, sizeof(sx->buffer) - (p-sx->buffer), addrlist, sx->ob))
+if (smtp_mail_auth_str(p, sizeof(sx->buffer) - (p-sx->buffer), addrlist, sx->conn_args.ob))
   return ERROR;
 
 return OK;
@@ -2642,7 +3160,7 @@ switch(rc)
 
   case +1:                /* Cmd was sent */
     if (!smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2',
-       sx->ob->command_timeout))
+       (SOB sx->conn_args.ob)->command_timeout))
       {
       if (errno == 0 && sx->buffer[0] == '4')
        {
@@ -2680,12 +3198,6 @@ for (addr = sx->first_addr, address_count = 0;
   BOOL no_flush;
   uschar * rcpt_addr;
 
-  if (tcp_out_fastopen != TFO_NOT_USED && !f.tcp_out_fastopen_logged)
-    {
-    setflag(addr, af_tcp_fastopen_conn);
-    if (tcp_out_fastopen >= TFO_USED) setflag(addr, af_tcp_fastopen);
-    }
-
   addr->dsn_aware = sx->peer_offered & OPTION_DSN
     ? dsn_support_yes : dsn_support_no;
 
@@ -2700,7 +3212,7 @@ for (addr = sx->first_addr, address_count = 0;
   yield as OK, because this error can often mean that there is a problem with
   just one address, so we don't want to delay the host. */
 
-  rcpt_addr = transport_rcpt_address(addr, sx->tblock->rcpt_include_affixes);
+  rcpt_addr = transport_rcpt_address(addr, sx->conn_args.tblock->rcpt_include_affixes);
 
 #ifdef SUPPORT_I18N
   if (  testflag(sx->addrlist, af_utf8_downcvt)
@@ -2734,12 +3246,15 @@ for (addr = sx->first_addr, address_count = 0;
       case -1: return -3;                      /* Timeout on RCPT */
       case -2: return -2;                      /* non-MAIL read i/o error */
       default: return -1;                      /* any MAIL error */
+
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+      case -4: return -1;                      /* non-2xx for pipelined banner or EHLO */
+#endif
       }
     sx->pending_MAIL = FALSE;            /* Dealt with MAIL */
     }
   }      /* Loop for next address */
 
-f.tcp_out_fastopen_logged = TRUE;
 sx->next_addr = addr;
 return 0;
 }
@@ -2910,6 +3425,7 @@ smtp_deliver(address_item *addrlist, host_item *host, int host_af, int defport,
   BOOL *message_defer, BOOL suppress_tls)
 {
 address_item *addr;
+smtp_transport_options_block * ob = SOB tblock->options_block;
 int yield = OK;
 int save_errno;
 int rc;
@@ -2926,13 +3442,16 @@ suppress_tls = suppress_tls;  /* stop compiler warning when no TLS support */
 *message_defer = FALSE;
 
 sx.addrlist = addrlist;
-sx.host = host;
-sx.host_af = host_af,
+sx.conn_args.host = host;
+sx.conn_args.host_af = host_af,
 sx.port = defport;
-sx.interface = interface;
+sx.conn_args.interface = interface;
 sx.helo_data = NULL;
-sx.tblock = tblock;
+sx.conn_args.tblock = tblock;
 sx.verify = FALSE;
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+sx.sync_addr = sx.first_addr = addrlist;
+#endif
 
 /* Get the channel set up ready for a message (MAIL FROM being the next
 SMTP command to send */
@@ -2971,8 +3490,6 @@ if (tblock->filter_command)
     }
   }
 
-sx.first_addr = addrlist;
-
 /* For messages that have more than the maximum number of envelope recipients,
 we want to send several transactions down the same SMTP connection. (See
 comments in deliver.c as to how this reconciles, heuristically, with
@@ -3065,6 +3582,11 @@ if (  !(sx.peer_offered & OPTION_CHUNKING)
     case 0: break;                       /* No 2xx or 5xx, but no probs */
 
     case -1: goto END_OFF;               /* Timeout on RCPT */
+
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+    case -4:  HDEBUG(D_transport)
+               debug_printf("failed reaping pipelined cmd responses\n");
+#endif
     default: goto RESPONSE_FAILED;       /* I/O error, or any MAIL/DATA error */
     }
   pipelining_active = FALSE;
@@ -3088,11 +3610,13 @@ if (!(sx.peer_offered & OPTION_CHUNKING) && !sx.ok)
 else
   {
   transport_ctx tctx = {
-    {sx.cctx.sock},            /*XXX will this need TLS info? */
-    tblock,
-    addrlist,
-    US".", US"..",    /* Escaping strings */
-    topt_use_crlf | topt_escape_headers
+    .u = {.fd = sx.cctx.sock}, /*XXX will this need TLS info? */
+    .tblock =  tblock,
+    .addr =    addrlist,
+    .check_string = US".",
+    .escape_string = US"..",   /* Escaping strings */
+    .options =
+      topt_use_crlf | topt_escape_headers
     | (tblock->body_only       ? topt_no_headers : 0)
     | (tblock->headers_only    ? topt_no_body : 0)
     | (tblock->return_path_add ? topt_add_return_path : 0)
@@ -3127,7 +3651,7 @@ else
   sx.buffer[0] = 0;
 
   sigalrm_seen = FALSE;
-  transport_write_timeout = sx.ob->data_timeout;
+  transport_write_timeout = ob->data_timeout;
   smtp_command = US"sending data block";   /* For error messages */
   DEBUG(D_transport|D_v)
     if (sx.peer_offered & OPTION_CHUNKING)
@@ -3140,10 +3664,10 @@ else
   dkim_exim_sign_init();
 # ifdef EXPERIMENTAL_ARC
     {
-    uschar * s = sx.ob->arc_sign;
+    uschar * s = ob->arc_sign;
     if (s)
       {
-      if (!(sx.ob->dkim.arc_signspec = s = expand_string(s)))
+      if (!(ob->dkim.arc_signspec = s = expand_string(s)))
        {
        if (!f.expand_string_forcedfail)
          {
@@ -3156,12 +3680,12 @@ else
        {
        /* Ask dkim code to hash the body for ARC */
        (void) arc_ams_setup_sign_bodyhash();
-       sx.ob->dkim.force_bodyhash = TRUE;
+       ob->dkim.force_bodyhash = TRUE;
        }
       }
     }
 # endif
-  sx.ok = dkim_transport_write_message(&tctx, &sx.ob->dkim, CUSS &message);
+  sx.ok = dkim_transport_write_message(&tctx, &ob->dkim, CUSS &message);
 #else
   sx.ok = transport_write_message(&tctx, 0);
 #endif
@@ -3203,19 +3727,23 @@ else
       case 0: break;                       /* No 2xx or 5xx, but no probs */
 
       case -1: goto END_OFF;               /* Timeout on RCPT */
+
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+      case -4:  HDEBUG(D_transport)
+                 debug_printf("failed reaping pipelined cmd responses\n");
+#endif
       default: goto RESPONSE_FAILED;       /* I/O error, or any MAIL/DATA error */
       }
     }
 
 #ifndef DISABLE_PRDR
-  /* For PRDR we optionally get a partial-responses warning
-   * followed by the individual responses, before going on with
-   * the overall response.  If we don't get the warning then deal
-   * with per non-PRDR. */
+  /* For PRDR we optionally get a partial-responses warning followed by the
+  individual responses, before going on with the overall response.  If we don't
+  get the warning then deal with per non-PRDR. */
+
   if(sx.prdr_active)
     {
-    sx.ok = smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '3',
-      sx.ob->final_timeout);
+    sx.ok = smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '3', ob->final_timeout);
     if (!sx.ok && errno == 0) switch(sx.buffer[0])
       {
       case '2': sx.prdr_active = FALSE;
@@ -3236,7 +3764,7 @@ else
   if (!sx.lmtp)
     {
     sx.ok = smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2',
-      sx.ob->final_timeout);
+      ob->final_timeout);
     if (!sx.ok && errno == 0 && sx.buffer[0] == '4')
       {
       errno = ERRNO_DATA4XX;
@@ -3300,7 +3828,7 @@ else
 #endif
         {
         if (!smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2',
-            sx.ob->final_timeout))
+            ob->final_timeout))
           {
           if (errno != 0 || sx.buffer[0] == 0) goto RESPONSE_FAILED;
           addr->message = string_sprintf(
@@ -3344,7 +3872,16 @@ else
       addr->special_action = flag;
       addr->message = conf;
 
+      if (tcp_out_fastopen)
+       {
+       setflag(addr, af_tcp_fastopen_conn);
+       if (tcp_out_fastopen >= TFO_USED_NODATA) setflag(addr, af_tcp_fastopen);
+       if (tcp_out_fastopen >= TFO_USED_DATA) setflag(addr, af_tcp_fastopen_data);
+       }
       if (sx.pipelining_used) setflag(addr, af_pipelining);
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+      if (sx.early_pipe_active) setflag(addr, af_early_pipe);
+#endif
 #ifndef DISABLE_PRDR
       if (sx.prdr_active) setflag(addr, af_prdr_used);
 #endif
@@ -3382,7 +3919,7 @@ else
        upgrade all the address statuses. */
 
         sx.ok = smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2',
-          sx.ob->final_timeout);
+          ob->final_timeout);
         if (!sx.ok)
          {
          if(errno == 0 && sx.buffer[0] == '4')
@@ -3544,9 +4081,20 @@ if (!sx.ok)
 
     else
       {
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+      /* If we were early-pipelinng and the actual EHLO response did not match
+      the cached value we assumed, we could have detected it and passed a
+      custom errno through to here.  It would be nice to RSET and retry right
+      away, but to reliably do that we eould need an extra synch point before
+      we committed to data and that would discard half the gained roundrips.
+      Or we could summarily drop the TCP connection. but that is also ugly.
+      Instead, we ignore the possibility (having freshened the cache) and rely
+      on the server telling us with a nonmessage error if we have tried to
+      do something it no longer supports. */
+#endif
       set_rc = DEFER;
       yield = (save_errno == ERRNO_CHHEADER_FAIL ||
-               save_errno == ERRNO_FILTER_FAIL)? ERROR : DEFER;
+               save_errno == ERRNO_FILTER_FAIL) ? ERROR : DEFER;
       }
     }
 
@@ -3604,7 +4152,7 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
      || (
 #ifdef SUPPORT_TLS
           (  tls_out.active.sock < 0  &&  !continue_proxy_cipher
-           || verify_check_given_host(CUSS &sx.ob->hosts_nopass_tls, host) != OK
+           || verify_check_given_host(CUSS &ob->hosts_nopass_tls, host) != OK
           )
         &&
 #endif
@@ -3623,8 +4171,8 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
           host->address, strerror(errno));
         sx.send_quit = FALSE;
         }
-      else if (! (sx.ok = smtp_read_response(&sx, sx.buffer,
-                 sizeof(sx.buffer), '2', sx.ob->command_timeout)))
+      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,
@@ -3660,7 +4208,7 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
 #ifdef SUPPORT_TLS
       if (tls_out.active.sock >= 0)
        if (  f.continue_more
-          || verify_check_given_host(CUSS &sx.ob->hosts_noproxy_tls, host) == OK)
+          || 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
@@ -3675,7 +4223,7 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
            && smtp_write_command(&sx, SCMD_FLUSH, "EHLO %s\r\n", sx.helo_data)
                >= 0
            && smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer),
-                                     '2', sx.ob->command_timeout);
+                                     '2', ob->command_timeout);
 
          if (sx.ok && f.continue_more)
            return yield;               /* More addresses for another run */
@@ -3725,7 +4273,7 @@ propagate it from the initial
            if (f.running_in_test_harness) millisleep(100); /* let parent debug out */
            /* does not return */
            smtp_proxy_tls(sx.cctx.tls_ctx, sx.buffer, sizeof(sx.buffer), pfd,
-                           sx.ob->command_timeout);
+                           ob->command_timeout);
            }
 
          if (pid > 0)          /* parent */
@@ -3800,6 +4348,7 @@ HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP(close)>>\n");
 if (sx.send_quit)
   {
   shutdown(sx.cctx.sock, SHUT_WR);
+  millisleep(f.running_in_test_harness ? 200 : 20);
   if (fcntl(sx.cctx.sock, F_SETFL, O_NONBLOCK) == 0)
     for (rc = 16; read(sx.cctx.sock, sx.inbuffer, sizeof(sx.inbuffer)) > 0 && rc > 0;)
       rc--;                            /* drain socket */
@@ -3838,8 +4387,7 @@ Returns:    nothing
 void
 smtp_transport_closedown(transport_instance *tblock)
 {
-smtp_transport_options_block *ob =
-  (smtp_transport_options_block *)tblock->options_block;
+smtp_transport_options_block * ob = SOB tblock->options_block;
 client_conn_ctx cctx;
 smtp_context sx;
 uschar buffer[256];
@@ -3947,8 +4495,7 @@ BOOL expired = TRUE;
 uschar *expanded_hosts = NULL;
 uschar *pistring;
 uschar *tid = string_sprintf("%s transport", tblock->name);
-smtp_transport_options_block *ob =
-  (smtp_transport_options_block *)(tblock->options_block);
+smtp_transport_options_block *ob = SOB tblock->options_block;
 host_item *hostlist = addrlist->host_list;
 host_item *host;
 
index 3219b05..b9e2b0b 100644 (file)
@@ -46,6 +46,9 @@ typedef struct {
   uschar *hosts_avoid_tls;
   uschar *hosts_verify_avoid_tls;
   uschar *hosts_avoid_pipelining;
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  uschar *hosts_pipe_connect;
+#endif
   uschar *hosts_avoid_esmtp;
 #ifdef SUPPORT_TLS
   uschar *hosts_nopass_tls;
@@ -99,20 +102,26 @@ typedef struct {
 #endif
 } smtp_transport_options_block;
 
+#define SOB (smtp_transport_options_block *)
+
+
 /* smtp connect context */
 typedef struct {
   uschar *             from_addr;
   address_item *       addrlist;
-  host_item *          host;
-  int                  host_af;
+
+  smtp_connect_args    conn_args;
   int                  port;
-  uschar *             interface;
 
   BOOL verify:1;
   BOOL lmtp:1;
   BOOL smtps:1;
   BOOL ok:1;
   BOOL setting_up:1;
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  BOOL early_pipe_ok:1;
+  BOOL early_pipe_active:1;
+#endif
   BOOL esmtp:1;
   BOOL esmtp_sent:1;
   BOOL pipelining_used:1;
@@ -127,6 +136,10 @@ typedef struct {
   BOOL dane:1;
   BOOL dane_required:1;
 #endif
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  BOOL pending_BANNER:1;
+  BOOL pending_EHLO:1;
+#endif
   BOOL pending_MAIL:1;
   BOOL pending_BDAT:1;
   BOOL good_RCPT:1;
@@ -145,6 +158,7 @@ typedef struct {
   uschar *     smtp_greeting;
   uschar *     helo_response;
 #endif
+  ehlo_resp_precis     ehlo_resp;
 
   address_item *       first_addr;
   address_item *       next_addr;
@@ -156,13 +170,11 @@ typedef struct {
   uschar       buffer[DELIVER_BUFFER_SIZE];
   uschar       inbuffer[4096];
   uschar       outbuffer[4096];
-
-  transport_instance *                 tblock;
-  smtp_transport_options_block *       ob;
 } smtp_context;
 
 extern int smtp_setup_conn(smtp_context *, BOOL);
 extern int smtp_write_mail_and_rcpt_cmds(smtp_context *, int *);
+extern int smtp_reap_early_pipe(smtp_context *, int *);
 
 
 /* Data for reading the private options. */
index d14cb68..236a87c 100644 (file)
@@ -669,12 +669,12 @@ coding means skipping this whole loop and doing the append separately.  */
         addr->message);
 
     sx.addrlist = addr;
-    sx.host = host;
-    sx.host_af = host_af,
+    sx.conn_args.host = host;
+    sx.conn_args.host_af = host_af,
     sx.port = port;
-    sx.interface = interface;
+    sx.conn_args.interface = interface;
     sx.helo_data = tf->helo_data;
-    sx.tblock = addr->transport;
+    sx.conn_args.tblock = addr->transport;
     sx.verify = TRUE;
 
 tls_retry_connection:
@@ -1320,7 +1320,7 @@ cutthrough_data_puts(US"\r\n", 2);
 static uschar
 cutthrough_response(client_conn_ctx * cctx, char expect, uschar ** copy, int timeout)
 {
-smtp_context sx;
+smtp_context sx = {0};
 uschar inbuffer[4096];
 uschar responsebuffer[4096];
 
index aa29a2c..0a14cee 100644 (file)
@@ -17,3 +17,6 @@ chunking_advertise_hosts =
 .ifdef _HAVE_REQTLS
 tls_advertise_requiretls =
 .endif
+.ifdef _HAVE_PIPE_CONNECT
+pipelining_connect_advertise_hosts = :
+.endif
index 6ca2d1b..165f0a4 100644 (file)
@@ -12,6 +12,9 @@ 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
 
 # ----- Main settings -----
 
index 6a23896..e016285 100644 (file)
@@ -9,6 +9,9 @@ gecos_pattern = ""
 gecos_name = CALLER_NAME
 chunking_advertise_hosts =
 tls_advertise_hosts =
+.ifdef _HAVE_PIPE_CONNECT
+pipelining_connect_advertise_hosts =
+.endif
 
 # ----- Main settings -----
 
index dfb58eb..caf17be 100644 (file)
@@ -16,6 +16,9 @@ tls_advertise_hosts = ${if eq {SRV}{tls} {*}}
 .ifdef _HAVE_REQTLS
 tls_advertise_requiretls = :
 .endif
+.ifdef _HAVE_PIPE_CONNECT
+pipelining_connect_advertise_hosts = :
+.endif
 
 
 # ----- Main settings -----
index 8bb0a9e..ff99d93 100644 (file)
@@ -10,6 +10,9 @@ gecos_pattern = ""
 gecos_name = CALLER_NAME
 chunking_advertise_hosts = *
 tls_advertise_hosts = ${if eq {SRV}{tls} {*}}
+.ifdef _HAVE_PIPE_CONNECT
+pipelining_connect_advertise_hosts =
+.endif
 
 # ----- Main settings -----
 
diff --git a/test/confs/4050 b/test/confs/4050
new file mode 100644 (file)
index 0000000..06a85c0
--- /dev/null
@@ -0,0 +1,63 @@
+# test config 4050
+# Pipelining the early part of the SMTP conversation
+
+CONNECTCOND=
+CONTROL=
+RETRY=7d
+
+keep_environment = PATH
+exim_path = EXIM_PATH
+host_lookup_order = bydns
+spool_directory = DIR/spool
+
+.ifdef SERVER
+log_file_path = DIR/spool/log/SERVER%slog
+.else
+log_file_path = DIR/spool/log/%slog
+.endif
+
+gecos_pattern = ""
+gecos_name = CALLER_NAME
+dns_cname_loops = 9
+chunking_advertise_hosts =
+tls_advertise_hosts =
+
+pipelining_connect_advertise_hosts = CONNECTCOND
+retry_data_expire = RETRY
+log_selector = +received_recipients +pipelining
+queue_only
+
+acl_smtp_rcpt = accept
+
+#
+begin routers
+
+server:
+  driver =     redirect
+  condition =  ${if eq {SERVER}{server}}
+  data =       :blackhole:
+
+clientbounce:
+  driver =     redirect
+  condition =  ${if !def:sender_address}
+  data =       :blackhole:
+
+client:
+  driver =     manualroute
+  route_data = 127.0.0.1::PORT_D
+  self =       send
+  transport =  smtp
+
+#
+begin transports
+
+smtp:
+  driver =             smtp
+  hosts_pipe_connect = CONTROL
+  max_rcpt =           1
+
+#
+begin retry
+
+* * F,5d,1d
+
diff --git a/test/confs/4051 b/test/confs/4051
new file mode 120000 (symlink)
index 0000000..ca1c19b
--- /dev/null
@@ -0,0 +1 @@
+4050
\ No newline at end of file
diff --git a/test/confs/4052 b/test/confs/4052
new file mode 120000 (symlink)
index 0000000..ca1c19b
--- /dev/null
@@ -0,0 +1 @@
+4050
\ No newline at end of file
diff --git a/test/confs/4053 b/test/confs/4053
new file mode 100644 (file)
index 0000000..3a85dee
--- /dev/null
@@ -0,0 +1,47 @@
+# test config 4053
+# Early-pipe, CHUNKING
+
+keep_environment = PATH
+exim_path = EXIM_PATH
+host_lookup_order = bydns
+spool_directory = DIR/spool
+
+.ifdef SERVER
+log_file_path = DIR/spool/log/SERVER%slog
+.else
+log_file_path = DIR/spool/log/%slog
+.endif
+
+gecos_pattern = ""
+gecos_name = CALLER_NAME
+dns_cname_loops = 9
+chunking_advertise_hosts = *
+tls_advertise_hosts =
+
+pipelining_connect_advertise_hosts = *
+log_selector = +pipelining
+queue_only
+
+acl_smtp_rcpt = accept
+
+#
+begin routers
+
+server:
+  driver =     redirect
+  condition =  ${if eq {SERVER}{server}}
+  data =       :blackhole:
+
+client:
+  driver =     manualroute
+  route_data = 127.0.0.1::PORT_D
+  self =       send
+  transport =  smtp
+
+#
+begin transports
+
+smtp:
+  driver =             smtp
+  hosts_pipe_connect = *
+
diff --git a/test/confs/4056 b/test/confs/4056
new file mode 100644 (file)
index 0000000..c77c439
--- /dev/null
@@ -0,0 +1,62 @@
+# test config 4056
+# Early-pipe, AUTH (no TLS!)
+
+keep_environment = PATH
+exim_path = EXIM_PATH
+host_lookup_order = bydns
+spool_directory = DIR/spool
+
+.ifdef SERVER
+log_file_path = DIR/spool/log/SERVER%slog
+.else
+log_file_path = DIR/spool/log/%slog
+.endif
+
+gecos_pattern = ""
+gecos_name = CALLER_NAME
+dns_cname_loops = 9
+chunking_advertise_hosts =
+tls_advertise_hosts =
+
+pipelining_connect_advertise_hosts = *
+auth_advertise_hosts = *
+log_selector = +pipelining
+queue_only
+
+acl_smtp_rcpt = accept
+
+#
+begin routers
+
+server:
+  driver =     redirect
+  condition =  ${if eq {SERVER}{server}}
+  data =       :blackhole:
+
+client:
+  driver =     manualroute
+  route_data = 127.0.0.1::PORT_D
+  self =       send
+  transport =  smtp
+
+#
+begin transports
+
+smtp:
+  driver =             smtp
+  hosts_pipe_connect = *
+  hosts_require_auth = *
+
+#
+begin authenticators
+
+plain:
+  driver = plaintext
+  public_name = PLAIN
+
+  server_condition = "\
+    ${if and {{eq{$auth2}{userx}}{eq{$auth3}{secret}}}{yes}{no}}"
+  server_set_id = $auth2
+
+  client_send =        ^userx^secret
+
diff --git a/test/confs/4058 b/test/confs/4058
new file mode 100644 (file)
index 0000000..887427a
--- /dev/null
@@ -0,0 +1,50 @@
+# test config 4058
+# Early-pipelining with TCP Fast Open
+
+OPT=
+
+keep_environment = PATH
+exim_path = EXIM_PATH
+host_lookup_order = bydns
+spool_directory = DIR/spool
+
+.ifdef SERVER
+log_file_path = DIR/spool/log/SERVER%slog
+.else
+log_file_path = DIR/spool/log/%slog
+.endif
+log_selector = +received_recipients +millisec +pipelining
+
+gecos_pattern = ""
+gecos_name = CALLER_NAME
+dns_cname_loops = 9
+chunking_advertise_hosts = OPT
+tls_advertise_hosts =
+
+pipelining_connect_advertise_hosts = *
+queue_only
+
+acl_smtp_rcpt = accept
+
+#
+begin routers
+
+server:
+  driver =     redirect
+  condition =  ${if eq {SERVER}{server}}
+  data =       :blackhole:
+
+client:
+  driver =     manualroute
+  route_data = 127.0.0.1::PORT_D
+  self =       send
+  transport =  smtp
+
+#
+begin transports
+
+smtp:
+  driver =             smtp
+  hosts_try_fastopen = *
+  hosts_pipe_connect = *
+
diff --git a/test/confs/4060 b/test/confs/4060
new file mode 100644 (file)
index 0000000..935d49f
--- /dev/null
@@ -0,0 +1,58 @@
+# test config 4060
+# Pipelining the early part of the SMTP conversation, starttls
+
+CONTROL=*
+OPT=
+
+keep_environment = PATH
+exim_path = EXIM_PATH
+host_lookup_order = bydns
+spool_directory = DIR/spool
+
+.ifdef SERVER
+log_file_path = DIR/spool/log/SERVER%slog
+.else
+log_file_path = DIR/spool/log/%slog
+.endif
+
+gecos_pattern = ""
+gecos_name = CALLER_NAME
+dns_cname_loops = 9
+chunking_advertise_hosts = OPT
+tls_advertise_hosts = *
+tls_certificate = ${if eq {SERVER}{server}{DIR/aux-fixed/cert1}fail}
+
+# Avoid ECDHE key-exchange so that we can wireshark-decode
+.ifdef _HAVE_GNUTLS
+tls_require_ciphers = NORMAL:-KX-ALL:+RSA
+.endif
+
+pipelining_connect_advertise_hosts = *
+log_selector = +received_recipients +pipelining
+queue_only
+
+acl_smtp_rcpt = accept
+
+#
+begin routers
+
+server:
+  driver =     redirect
+  condition =  ${if eq {SERVER}{server}}
+  data =       :blackhole:
+
+client:
+  driver =     manualroute
+  route_data = 127.0.0.1::PORT_D
+  self =       send
+  transport =  smtp
+
+#
+begin transports
+
+smtp:
+  driver =             smtp
+  hosts_pipe_connect = CONTROL
+  tls_verify_hosts =
+  tls_try_verify_hosts =
+
diff --git a/test/confs/4061 b/test/confs/4061
new file mode 100644 (file)
index 0000000..3ff106f
--- /dev/null
@@ -0,0 +1,59 @@
+# test config 4061
+# Pipelining the early part of the SMTP conversation, tls-on-connect
+
+CONTROL=*
+
+keep_environment = PATH
+exim_path = EXIM_PATH
+host_lookup_order = bydns
+spool_directory = DIR/spool
+
+.ifdef SERVER
+log_file_path = DIR/spool/log/SERVER%slog
+.else
+log_file_path = DIR/spool/log/%slog
+.endif
+
+gecos_pattern = ""
+gecos_name = CALLER_NAME
+dns_cname_loops = 9
+chunking_advertise_hosts =
+tls_on_connect_ports = PORT_D
+tls_advertise_hosts = *
+tls_certificate = ${if eq {SERVER}{server}{DIR/aux-fixed/cert1}fail}
+
+# Avoid ECDHE key-exchange so that we can wireshark-decode
+.ifdef _HAVE_GNUTLS
+tls_require_ciphers = NORMAL:-KX-ALL:+RSA
+.endif
+
+pipelining_connect_advertise_hosts = *
+log_selector = +received_recipients +pipelining
+queue_only
+
+acl_smtp_rcpt = accept
+
+#
+begin routers
+
+server:
+  driver =     redirect
+  condition =  ${if eq {SERVER}{server}}
+  data =       :blackhole:
+
+client:
+  driver =     manualroute
+  route_data = 127.0.0.1::PORT_D
+  self =       send
+  transport =  smtp
+
+#
+begin transports
+
+smtp:
+  driver =             smtp
+  hosts_pipe_connect = CONTROL
+  protocol =           smtps
+  tls_verify_hosts =
+  tls_try_verify_hosts =
+
diff --git a/test/confs/4062 b/test/confs/4062
new file mode 120000 (symlink)
index 0000000..f9a2604
--- /dev/null
@@ -0,0 +1 @@
+4060
\ No newline at end of file
diff --git a/test/confs/4063 b/test/confs/4063
new file mode 120000 (symlink)
index 0000000..723e8a8
--- /dev/null
@@ -0,0 +1 @@
+4061
\ No newline at end of file
diff --git a/test/confs/4064 b/test/confs/4064
new file mode 100644 (file)
index 0000000..8c3c300
--- /dev/null
@@ -0,0 +1,72 @@
+# test config 4064
+# Early-pipe, AUTH, GnuTLS
+
+keep_environment = PATH
+exim_path = EXIM_PATH
+host_lookup_order = bydns
+spool_directory = DIR/spool
+
+.ifdef SERVER
+log_file_path = DIR/spool/log/SERVER%slog
+.else
+log_file_path = DIR/spool/log/%slog
+.endif
+
+gecos_pattern = ""
+gecos_name = CALLER_NAME
+dns_cname_loops = 9
+chunking_advertise_hosts = OPT
+tls_advertise_hosts = *
+tls_certificate = ${if eq {SERVER}{server}{DIR/aux-fixed/cert1}fail}
+
+# Avoid ECDHE key-exchange so that we can wireshark-decode
+.ifdef _HAVE_GNUTLS
+tls_require_ciphers = NORMAL:-KX-ALL:+RSA
+.endif
+
+pipelining_connect_advertise_hosts = *
+auth_advertise_hosts = *
+
+log_selector = +received_recipients +pipelining
+queue_only
+
+acl_smtp_rcpt = accept
+
+#
+begin routers
+
+server:
+  driver =     redirect
+  condition =  ${if eq {SERVER}{server}}
+  data =       :blackhole:
+
+client:
+  driver =     manualroute
+  route_data = 127.0.0.1::PORT_D
+  self =       send
+  transport =  smtp
+
+#
+begin transports
+
+smtp:
+  driver =             smtp
+  hosts_pipe_connect = *
+  tls_verify_hosts =
+  tls_try_verify_hosts =
+  hosts_require_auth = *
+
+#
+begin authenticators
+
+plain:
+  driver = plaintext
+  public_name = PLAIN
+
+  server_advertise_condition = ${if eq{$tls_in_cipher}{}{no}{yes}}
+  server_condition = "\
+    ${if and {{eq{$auth2}{userx}}{eq{$auth3}{secret}}}{yes}{no}}"
+  server_set_id = $auth2
+
+  client_send =        ^userx^secret
+
diff --git a/test/confs/4065 b/test/confs/4065
new file mode 100644 (file)
index 0000000..dcf0d6f
--- /dev/null
@@ -0,0 +1,73 @@
+# test config 4065
+# Early-pipe, AUTH, GnuTLS, tls-on-connect
+
+keep_environment = PATH
+exim_path = EXIM_PATH
+host_lookup_order = bydns
+spool_directory = DIR/spool
+
+.ifdef SERVER
+log_file_path = DIR/spool/log/SERVER%slog
+.else
+log_file_path = DIR/spool/log/%slog
+.endif
+
+gecos_pattern = ""
+gecos_name = CALLER_NAME
+dns_cname_loops = 9
+chunking_advertise_hosts =
+tls_on_connect_ports = PORT_D
+tls_advertise_hosts = *
+tls_certificate = ${if eq {SERVER}{server}{DIR/aux-fixed/cert1}fail}
+
+# Avoid ECDHE key-exchange so that we can wireshark-decode
+.ifdef _HAVE_GNUTLS
+tls_require_ciphers = NORMAL:-KX-ALL:+RSA
+.endif
+
+pipelining_connect_advertise_hosts = *
+auth_advertise_hosts = *
+
+log_selector = +received_recipients +pipelining
+queue_only
+
+acl_smtp_rcpt = accept
+
+#
+begin routers
+
+server:
+  driver =     redirect
+  condition =  ${if eq {SERVER}{server}}
+  data =       :blackhole:
+
+client:
+  driver =     manualroute
+  route_data = 127.0.0.1::PORT_D
+  self =       send
+  transport =  smtp
+
+#
+begin transports
+
+smtp:
+  driver =             smtp
+  hosts_pipe_connect = *
+  protocol =           smtps
+  tls_verify_hosts =
+  tls_try_verify_hosts =
+  hosts_require_auth = *
+
+#
+begin authenticators
+
+plain:
+  driver = plaintext
+  public_name = PLAIN
+
+  server_condition = "\
+    ${if and {{eq{$auth2}{userx}}{eq{$auth3}{secret}}}{yes}{no}}"
+  server_set_id = $auth2
+
+  client_send =        ^userx^secret
+
diff --git a/test/confs/4066 b/test/confs/4066
new file mode 120000 (symlink)
index 0000000..8d736b1
--- /dev/null
@@ -0,0 +1 @@
+4064
\ No newline at end of file
diff --git a/test/confs/4067 b/test/confs/4067
new file mode 120000 (symlink)
index 0000000..ddeca21
--- /dev/null
@@ -0,0 +1 @@
+4065
\ No newline at end of file
index 2437758..55838b3 100644 (file)
@@ -21,6 +21,9 @@ log_file_path = DIR/spool/log/%slog
 gecos_pattern = ""
 gecos_name = CALLER_NAME
 chunking_advertise_hosts =
+.ifdef _HAVE_PIPE_CONNECT
+pipelining_connect_advertise_hosts =
+.endif
 
 primary_hostname = myhost.test.ex
 
index 7df9196..06903fe 100644 (file)
@@ -2,8 +2,8 @@
 1999-03-02 09:44:33 10HmaX-0005vi-00 => user_tfo@test.ex R=my_main_router T=my_smtp H=127.0.0.1 [127.0.0.1]:1224 PRX=[127.0.0.1]:1225 C="250 accepted OK"
 1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
 1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss
-1999-03-02 09:44:33 10HmaY-0005vi-00 => user_tfo@test.ex R=my_main_router T=my_smtp H=127.0.0.1 [127.0.0.1]:1224 PRX=[127.0.0.1]:1225 TFO C="250 accepted OK"
+1999-03-02 09:44:33 10HmaY-0005vi-00 => user_tfo@test.ex R=my_main_router T=my_smtp H=127.0.0.1 [127.0.0.1]:1224 PRX=[127.0.0.1]:1225 C="250 accepted OK"
 1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
 1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss
-1999-03-02 09:44:33 10HmaZ-0005vi-00 => user_tfo@test.ex R=my_main_router T=my_smtp H=127.0.0.1 [127.0.0.1]:1224 PRX=[127.0.0.1]:1225 TFO C="250 accepted OK"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => user_tfo@test.ex R=my_main_router T=my_smtp H=127.0.0.1 [127.0.0.1]:1224 PRX=[127.0.0.1]:1225 TFO* C="250 accepted OK"
 1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
diff --git a/test/log/4050 b/test/log/4050
new file mode 100644 (file)
index 0000000..bd994fe
--- /dev/null
@@ -0,0 +1,6 @@
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= a@test.ex H=(testclient) [127.0.0.1] P=esmtp L* S=sss for a@test.ex
diff --git a/test/log/4051 b/test/log/4051
new file mode 100644 (file)
index 0000000..00fa3eb
--- /dev/null
@@ -0,0 +1,44 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for notadvertised@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaX-0005vi-00 => notadvertised@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] L C="250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for nocache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => nocache@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] L C="250 OK id=10HmbA-0005vi-00"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for hascache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbB-0005vi-00 => hascache@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] L* C="250 OK id=10HmbC-0005vi-00"
+1999-03-02 09:44:33 10HmbB-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbD-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for clientno@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbD-0005vi-00 => clientno@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] L C="250 OK id=10HmbE-0005vi-00"
+1999-03-02 09:44:33 10HmbD-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtp L S=sss id=E10HmaX-0005vi-00@the.local.host.name for notadvertised@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: <notadvertised@test.ex> R=server
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtp L. S=sss id=E10HmaZ-0005vi-00@the.local.host.name for nocache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: <nocache@test.ex> R=server
+1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbC-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtp L* S=sss id=E10HmbB-0005vi-00@the.local.host.name for hascache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbC-0005vi-00 => :blackhole: <hascache@test.ex> R=server
+1999-03-02 09:44:33 10HmbC-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbE-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtp L. S=sss id=E10HmbD-0005vi-00@the.local.host.name for clientno@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbE-0005vi-00 => :blackhole: <clientno@test.ex> R=server
+1999-03-02 09:44:33 10HmbE-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
diff --git a/test/log/4052 b/test/log/4052
new file mode 100644 (file)
index 0000000..91fc0b7
--- /dev/null
@@ -0,0 +1,52 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for nocache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaX-0005vi-00 => nocache@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] L C="250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for hascache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => hascache@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] L* C="250 OK id=10HmbA-0005vi-00"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for client_old@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbB-0005vi-00 => client_old@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] L C="250 OK id=10HmbC-0005vi-00"
+1999-03-02 09:44:33 10HmbB-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbD-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for nocache2@test.ex withcache2@test.ex badbanner@test.ex
+1999-03-02 09:44:33 10HmbD-0005vi-00 => nocache2@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] L C="250 message accepted"
+1999-03-02 09:44:33 10HmbD-0005vi-00 => withcache2@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] L* C="250 message accepted"
+1999-03-02 09:44:33 10HmbD-0005vi-00 H=127.0.0.1 [127.0.0.1]: SMTP error from remote mail server after pipelined DATA: 450 sorry no banner for you today
+1999-03-02 09:44:33 10HmbD-0005vi-00 == badbanner@test.ex R=client T=smtp defer (0) H=127.0.0.1 [127.0.0.1]: SMTP error from remote mail server after pipelined DATA: 450 sorry no banner for you today
+1999-03-02 09:44:33 10HmbE-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for nocache@test.ex withcache@test.ex baddata@test.ex badrcpt@test.ex badmail@test.ex withcache2@test.ex
+1999-03-02 09:44:33 10HmbE-0005vi-00 => nocache@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] L C="250 message accepted"
+1999-03-02 09:44:33 10HmbE-0005vi-00 => withcache@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] L* C="250 message accepted"
+1999-03-02 09:44:33 10HmbE-0005vi-00 ** baddata@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1]: SMTP error from remote mail server after pipelined DATA: 550 obscure data error
+1999-03-02 09:44:33 10HmbE-0005vi-00 ** badrcpt@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1]: SMTP error from remote mail server after RCPT TO:<badrcpt@test.ex>: 550 rcpt refused
+1999-03-02 09:44:33 10HmbE-0005vi-00 ** badmail@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1]: SMTP error from remote mail server after pipelined MAIL FROM:<CALLER@the.local.host.name>: 550 mail cmd refused
+1999-03-02 09:44:33 10HmbE-0005vi-00 => withcache2@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] L* C="250 message accepted"
+1999-03-02 09:44:33 10HmbF-0005vi-00 <= <> R=10HmbE-0005vi-00 U=EXIMUSER P=local S=sss for CALLER@the.local.host.name
+1999-03-02 09:44:33 10HmbF-0005vi-00 => :blackhole: <CALLER@the.local.host.name> R=clientbounce
+1999-03-02 09:44:33 10HmbF-0005vi-00 Completed
+1999-03-02 09:44:33 10HmbE-0005vi-00 Completed
+1999-03-02 09:44:33 10HmbG-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for extchange@test.ex
+1999-03-02 09:44:33 10HmbG-0005vi-00 => extchange@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] L* C="250 message accepted"
+1999-03-02 09:44:33 10HmbG-0005vi-00 Completed
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtp L. S=sss id=E10HmaX-0005vi-00@the.local.host.name for nocache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: <nocache@test.ex> R=server
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtp L* S=sss id=E10HmaZ-0005vi-00@the.local.host.name for hascache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: <hascache@test.ex> R=server
+1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbC-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtp L. S=sss id=E10HmbB-0005vi-00@the.local.host.name for client_old@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbC-0005vi-00 => :blackhole: <client_old@test.ex> R=server
+1999-03-02 09:44:33 10HmbC-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
diff --git a/test/log/4053 b/test/log/4053
new file mode 100644 (file)
index 0000000..758be7c
--- /dev/null
@@ -0,0 +1,23 @@
+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 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaX-0005vi-00 => a@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] L K C="250- 3nn byte chunk, total 3nn\\n250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+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 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => b@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] L* K C="250- 3nn byte chunk, total 3nn\\n250 OK id=10HmbA-0005vi-00"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtp L. K S=sss id=E10HmaX-0005vi-00@the.local.host.name
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: <a@test.ex> R=server
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtp L* K S=sss id=E10HmaZ-0005vi-00@the.local.host.name
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: <b@test.ex> R=server
+1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
diff --git a/test/log/4056 b/test/log/4056
new file mode 100644 (file)
index 0000000..03f13ae
--- /dev/null
@@ -0,0 +1,23 @@
+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 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaX-0005vi-00 => a@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] A=plain L C="250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+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 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => b@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] A=plain L* C="250 OK id=10HmbA-0005vi-00"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtpa L. A=plain:userx S=sss id=E10HmaX-0005vi-00@the.local.host.name
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: <a@test.ex> R=server
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtpa L* A=plain:userx S=sss id=E10HmaZ-0005vi-00@the.local.host.name
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: <b@test.ex> R=server
+1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
diff --git a/test/log/4058 b/test/log/4058
new file mode 100644 (file)
index 0000000..21ce510
--- /dev/null
@@ -0,0 +1,44 @@
+2017-07-30 18:51:05.712 10HmaX-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for nocache@test.ex
+2017-07-30 18:51:05.712 Start queue run: pid=pppp
+2017-07-30 18:51:05.712 10HmaX-0005vi-00 => nocache@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] L C="250 OK id=10HmaY-0005vi-00"
+2017-07-30 18:51:05.712 10HmaX-0005vi-00 Completed
+2017-07-30 18:51:05.712 End queue run: pid=pppp
+2017-07-30 18:51:05.712 10HmaZ-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for hascache@test.ex
+2017-07-30 18:51:05.712 Start queue run: pid=pppp
+2017-07-30 18:51:05.712 10HmaZ-0005vi-00 => hascache@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] TFO* L* C="250 OK id=10HmbA-0005vi-00"
+2017-07-30 18:51:05.712 10HmaZ-0005vi-00 Completed
+2017-07-30 18:51:05.712 End queue run: pid=pppp
+2017-07-30 18:51:05.712 10HmbB-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for nocache@test.ex
+2017-07-30 18:51:05.712 Start queue run: pid=pppp
+2017-07-30 18:51:05.712 10HmbB-0005vi-00 => nocache@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] TFO L K C="250- 3nn byte chunk, total 3nn\\n250 OK id=10HmbC-0005vi-00"
+2017-07-30 18:51:05.712 10HmbB-0005vi-00 Completed
+2017-07-30 18:51:05.712 End queue run: pid=pppp
+2017-07-30 18:51:05.712 10HmbD-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for chunking@test.ex
+2017-07-30 18:51:05.712 Start queue run: pid=pppp
+2017-07-30 18:51:05.712 10HmbD-0005vi-00 => chunking@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] TFO* L* K C="250- 3nn byte chunk, total 3nn\\n250 OK id=10HmbE-0005vi-00"
+2017-07-30 18:51:05.712 10HmbD-0005vi-00 Completed
+2017-07-30 18:51:05.712 End queue run: pid=pppp
+
+******** SERVER ********
+2017-07-30 18:51:05.712 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+2017-07-30 18:51:05.712 10HmaY-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtp L. S=sss id=E10HmaX-0005vi-00@the.local.host.name for nocache@test.ex
+2017-07-30 18:51:05.712 Start queue run: pid=pppp
+2017-07-30 18:51:05.712 10HmaY-0005vi-00 => :blackhole: <nocache@test.ex> R=server
+2017-07-30 18:51:05.712 10HmaY-0005vi-00 Completed
+2017-07-30 18:51:05.712 End queue run: pid=pppp
+2017-07-30 18:51:05.712 10HmbA-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] TFO* P=esmtp L* S=sss id=E10HmaZ-0005vi-00@the.local.host.name for hascache@test.ex
+2017-07-30 18:51:05.712 Start queue run: pid=pppp
+2017-07-30 18:51:05.712 10HmbA-0005vi-00 => :blackhole: <hascache@test.ex> R=server
+2017-07-30 18:51:05.712 10HmbA-0005vi-00 Completed
+2017-07-30 18:51:05.712 End queue run: pid=pppp
+2017-07-30 18:51:05.712 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+2017-07-30 18:51:05.712 10HmbC-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] TFO P=esmtp L. K S=sss id=E10HmbB-0005vi-00@the.local.host.name for nocache@test.ex
+2017-07-30 18:51:05.712 Start queue run: pid=pppp
+2017-07-30 18:51:05.712 10HmbC-0005vi-00 => :blackhole: <nocache@test.ex> R=server
+2017-07-30 18:51:05.712 10HmbC-0005vi-00 Completed
+2017-07-30 18:51:05.712 End queue run: pid=pppp
+2017-07-30 18:51:05.712 10HmbE-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] TFO* P=esmtp L* K S=sss id=E10HmbD-0005vi-00@the.local.host.name for chunking@test.ex
+2017-07-30 18:51:05.712 Start queue run: pid=pppp
+2017-07-30 18:51:05.712 10HmbE-0005vi-00 => :blackhole: <chunking@test.ex> R=server
+2017-07-30 18:51:05.712 10HmbE-0005vi-00 Completed
+2017-07-30 18:51:05.712 End queue run: pid=pppp
diff --git a/test/log/4060 b/test/log/4060
new file mode 100644 (file)
index 0000000..cb227a6
--- /dev/null
@@ -0,0 +1,54 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for nocache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaX-0005vi-00 => nocache@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no L C="250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for hascache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => hascache@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no L* C="250 OK id=10HmbA-0005vi-00"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for clientno@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbB-0005vi-00 => clientno@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no L C="250 OK id=10HmbC-0005vi-00"
+1999-03-02 09:44:33 10HmbB-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbD-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for nocache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbD-0005vi-00 => nocache@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no L K C="250- 3nn byte chunk, total 3nn\\n250 OK id=10HmbE-0005vi-00"
+1999-03-02 09:44:33 10HmbD-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbF-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for chunking@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbF-0005vi-00 => chunking@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no L* K C="250- 3nn byte chunk, total 3nn\\n250 OK id=10HmbG-0005vi-00"
+1999-03-02 09:44:33 10HmbF-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtps L. X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no S=sss id=E10HmaX-0005vi-00@the.local.host.name for nocache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: <nocache@test.ex> R=server
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtps L* X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no S=sss id=E10HmaZ-0005vi-00@the.local.host.name for hascache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: <hascache@test.ex> R=server
+1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbC-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtps L. X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no S=sss id=E10HmbB-0005vi-00@the.local.host.name for clientno@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbC-0005vi-00 => :blackhole: <clientno@test.ex> R=server
+1999-03-02 09:44:33 10HmbC-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 10HmbE-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtps L. X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no K S=sss id=E10HmbD-0005vi-00@the.local.host.name for nocache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbE-0005vi-00 => :blackhole: <nocache@test.ex> R=server
+1999-03-02 09:44:33 10HmbE-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbG-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtps L* X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no K S=sss id=E10HmbF-0005vi-00@the.local.host.name for chunking@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbG-0005vi-00 => :blackhole: <chunking@test.ex> R=server
+1999-03-02 09:44:33 10HmbG-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
diff --git a/test/log/4061 b/test/log/4061
new file mode 100644 (file)
index 0000000..d1edf2d
--- /dev/null
@@ -0,0 +1,33 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for nocache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaX-0005vi-00 => nocache@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no L C="250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for hascache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => hascache@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no L* C="250 OK id=10HmbA-0005vi-00"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for clientno@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbB-0005vi-00 => clientno@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no L C="250 OK id=10HmbC-0005vi-00"
+1999-03-02 09:44:33 10HmbB-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTPS on port 1225
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtps L. X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no S=sss id=E10HmaX-0005vi-00@the.local.host.name for nocache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: <nocache@test.ex> R=server
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtps L* X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no S=sss id=E10HmaZ-0005vi-00@the.local.host.name for hascache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: <hascache@test.ex> R=server
+1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbC-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtps L. X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no S=sss id=E10HmbB-0005vi-00@the.local.host.name for clientno@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbC-0005vi-00 => :blackhole: <clientno@test.ex> R=server
+1999-03-02 09:44:33 10HmbC-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
diff --git a/test/log/4062 b/test/log/4062
new file mode 100644 (file)
index 0000000..6302a8b
--- /dev/null
@@ -0,0 +1,54 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for a@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaX-0005vi-00 => a@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no L C="250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for b@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => b@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no L* C="250 OK id=10HmbA-0005vi-00"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for c@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbB-0005vi-00 => c@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no L C="250 OK id=10HmbC-0005vi-00"
+1999-03-02 09:44:33 10HmbB-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbD-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for nocache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbD-0005vi-00 => nocache@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no L K C="250- 3nn byte chunk, total 3nn\\n250 OK id=10HmbE-0005vi-00"
+1999-03-02 09:44:33 10HmbD-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbF-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for chunking@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbF-0005vi-00 => chunking@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no L* K C="250- 3nn byte chunk, total 3nn\\n250 OK id=10HmbG-0005vi-00"
+1999-03-02 09:44:33 10HmbF-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtps L. X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no S=sss id=E10HmaX-0005vi-00@the.local.host.name for a@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: <a@test.ex> R=server
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtps L* X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no S=sss id=E10HmaZ-0005vi-00@the.local.host.name for b@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: <b@test.ex> R=server
+1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbC-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtps L. X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no S=sss id=E10HmbB-0005vi-00@the.local.host.name for c@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbC-0005vi-00 => :blackhole: <c@test.ex> R=server
+1999-03-02 09:44:33 10HmbC-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 10HmbE-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtps L. X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no K S=sss id=E10HmbD-0005vi-00@the.local.host.name for nocache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbE-0005vi-00 => :blackhole: <nocache@test.ex> R=server
+1999-03-02 09:44:33 10HmbE-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbG-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtps L* X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no K S=sss id=E10HmbF-0005vi-00@the.local.host.name for chunking@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbG-0005vi-00 => :blackhole: <chunking@test.ex> R=server
+1999-03-02 09:44:33 10HmbG-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
diff --git a/test/log/4063 b/test/log/4063
new file mode 100644 (file)
index 0000000..090c36d
--- /dev/null
@@ -0,0 +1,33 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for a@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaX-0005vi-00 => a@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no L C="250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for b@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => b@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no L* C="250 OK id=10HmbA-0005vi-00"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for c@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbB-0005vi-00 => c@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no L C="250 OK id=10HmbC-0005vi-00"
+1999-03-02 09:44:33 10HmbB-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTPS on port 1225
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtps L. X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no S=sss id=E10HmaX-0005vi-00@the.local.host.name for a@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: <a@test.ex> R=server
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtps L* X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no S=sss id=E10HmaZ-0005vi-00@the.local.host.name for b@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: <b@test.ex> R=server
+1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbC-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtps L. X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no S=sss id=E10HmbB-0005vi-00@the.local.host.name for c@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbC-0005vi-00 => :blackhole: <c@test.ex> R=server
+1999-03-02 09:44:33 10HmbC-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
diff --git a/test/log/4064 b/test/log/4064
new file mode 100644 (file)
index 0000000..0d89b0e
--- /dev/null
@@ -0,0 +1,48 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for nocache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaX-0005vi-00 => nocache@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no A=plain L C="250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for hascache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => hascache@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no A=plain L* C="250 OK id=10HmbA-0005vi-00"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for nocache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbB-0005vi-00 => nocache@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no A=plain L K C="250- 3nn byte chunk, total 3nn\\n250 OK id=10HmbC-0005vi-00"
+1999-03-02 09:44:33 10HmbB-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbD-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for chunking@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbD-0005vi-00 => chunking@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no A=plain L* K C="250- 3nn byte chunk, total 3nn\\n250 OK id=10HmbE-0005vi-00"
+1999-03-02 09:44:33 10HmbD-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 no IP address found for host OPT (during SMTP connection from localhost (the.local.host.name) [127.0.0.1])
+1999-03-02 09:44:33 no IP address found for host OPT (during SMTP connection from localhost (the.local.host.name) [127.0.0.1])
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtpsa L. X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no A=plain:userx S=sss id=E10HmaX-0005vi-00@the.local.host.name for nocache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: <nocache@test.ex> R=server
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 no IP address found for host OPT (during SMTP connection from localhost (the.local.host.name) [127.0.0.1])
+1999-03-02 09:44:33 no IP address found for host OPT (during SMTP connection from localhost (the.local.host.name) [127.0.0.1])
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtpsa L* X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no A=plain:userx S=sss id=E10HmaZ-0005vi-00@the.local.host.name for hascache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: <hascache@test.ex> R=server
+1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 10HmbC-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtpsa L. X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no A=plain:userx K S=sss id=E10HmbB-0005vi-00@the.local.host.name for nocache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbC-0005vi-00 => :blackhole: <nocache@test.ex> R=server
+1999-03-02 09:44:33 10HmbC-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbE-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtpsa L* X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no A=plain:userx K S=sss id=E10HmbD-0005vi-00@the.local.host.name for chunking@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbE-0005vi-00 => :blackhole: <chunking@test.ex> R=server
+1999-03-02 09:44:33 10HmbE-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
diff --git a/test/log/4065 b/test/log/4065
new file mode 100644 (file)
index 0000000..dd35342
--- /dev/null
@@ -0,0 +1,23 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for nocache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaX-0005vi-00 => nocache@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no A=plain L C="250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for hascache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => hascache@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no A=plain L* C="250 OK id=10HmbA-0005vi-00"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTPS on port 1225
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtpsa L. X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no A=plain:userx S=sss id=E10HmaX-0005vi-00@the.local.host.name for nocache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: <nocache@test.ex> R=server
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtpsa L* X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no A=plain:userx S=sss id=E10HmaZ-0005vi-00@the.local.host.name for hascache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: <hascache@test.ex> R=server
+1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
diff --git a/test/log/4066 b/test/log/4066
new file mode 100644 (file)
index 0000000..8daec2d
--- /dev/null
@@ -0,0 +1,48 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for nocache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaX-0005vi-00 => nocache@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no A=plain L C="250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for hascache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => hascache@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no A=plain L* C="250 OK id=10HmbA-0005vi-00"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for nocache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbB-0005vi-00 => nocache@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no A=plain L K C="250- 3nn byte chunk, total 3nn\\n250 OK id=10HmbC-0005vi-00"
+1999-03-02 09:44:33 10HmbB-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbD-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for chunking@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbD-0005vi-00 => chunking@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no A=plain L* K C="250- 3nn byte chunk, total 3nn\\n250 OK id=10HmbE-0005vi-00"
+1999-03-02 09:44:33 10HmbD-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 no IP address found for host OPT (during SMTP connection from localhost (the.local.host.name) [127.0.0.1])
+1999-03-02 09:44:33 no IP address found for host OPT (during SMTP connection from localhost (the.local.host.name) [127.0.0.1])
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtpsa L. X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no A=plain:userx S=sss id=E10HmaX-0005vi-00@the.local.host.name for nocache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: <nocache@test.ex> R=server
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 no IP address found for host OPT (during SMTP connection from localhost (the.local.host.name) [127.0.0.1])
+1999-03-02 09:44:33 no IP address found for host OPT (during SMTP connection from localhost (the.local.host.name) [127.0.0.1])
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtpsa L* X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no A=plain:userx S=sss id=E10HmaZ-0005vi-00@the.local.host.name for hascache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: <hascache@test.ex> R=server
+1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 10HmbC-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtpsa L. X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no A=plain:userx K S=sss id=E10HmbB-0005vi-00@the.local.host.name for nocache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbC-0005vi-00 => :blackhole: <nocache@test.ex> R=server
+1999-03-02 09:44:33 10HmbC-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbE-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtpsa L* X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no A=plain:userx K S=sss id=E10HmbD-0005vi-00@the.local.host.name for chunking@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbE-0005vi-00 => :blackhole: <chunking@test.ex> R=server
+1999-03-02 09:44:33 10HmbE-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
diff --git a/test/log/4067 b/test/log/4067
new file mode 100644 (file)
index 0000000..11d59e3
--- /dev/null
@@ -0,0 +1,23 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for nocache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaX-0005vi-00 => nocache@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no A=plain L C="250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@the.local.host.name U=CALLER P=local S=sss for hascache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => hascache@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no A=plain L* C="250 OK id=10HmbA-0005vi-00"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTPS on port 1225
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtpsa L. X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no A=plain:userx S=sss id=E10HmaX-0005vi-00@the.local.host.name for nocache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: <nocache@test.ex> R=server
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@the.local.host.name H=localhost (the.local.host.name) [127.0.0.1] P=esmtpsa L* X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no A=plain:userx S=sss id=E10HmaZ-0005vi-00@the.local.host.name for hascache@test.ex
+1999-03-02 09:44:33 Start queue run: pid=pppp
+1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: <hascache@test.ex> R=server
+1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
+1999-03-02 09:44:33 End queue run: pid=pppp
index 3814261..76e1177 100755 (executable)
@@ -1119,6 +1119,9 @@ RESET_AFTER_EXTRA_LINE_READ:
     # TCP Fast Open
     next if /^(ppppp )?setsockopt FASTOPEN: Network Error/;
 
+    # Experimental_PIPE_CONNECT
+    next if / in (pipelining_connect_advertise_hosts|hosts_pipe_connect)?\? no /;
+
     # Environment cleaning
     next if /\w+ in keep_environment\? (yes|no)/;
 
@@ -1628,9 +1631,11 @@ $munges =
     { 'stdout' => '/^(
                   dkim_(canon|domain|private_key|selector|sign_headers|strict|hash|identity|timestamps)
                   |gnutls_require_(kx|mac|protocols)
+                 |hosts_pipe_connect
                   |hosts_(requ(est|ire)|try)_(dane|ocsp)
                  |dane_require_tls_ciphers
                   |hosts_(avoid|nopass|noproxy|require|verify_avoid)_tls
+                  |pipelining_connect_advertise_hosts
                   |socks_proxy
                   |tls_[^ ]*
                  |utf8_downconvert
diff --git a/test/scripts/4050-pipe-conn/4050 b/test/scripts/4050-pipe-conn/4050
new file mode 100644 (file)
index 0000000..9dc0457
--- /dev/null
@@ -0,0 +1,103 @@
+# pipelined connect, server
+
+# not advertised when not wanted
+exim -bd -DSERVER=server -oX PORT_D
+****
+#
+client 127.0.0.1 PORT_D
+??? 220
+EHLO testclient
+??? 250-
+??? 250-SIZE
+??? 250-8BITMIME
+??? 250-PIPELINING
+??? 250 HELP
+QUIT
+??? 221
+****
+killdaemon
+#
+# advertised when wanted
+exim -bd -DSERVER=server -DCONNECTCOND=127.0.0.1 -oX PORT_D
+****
+#
+client 127.0.0.1 PORT_D
+??? 220
+EHLO testclient
+??? 250-
+??? 250-SIZE
+??? 250-8BITMIME
+??? 250-PIPELINING
+??? 250-X_PIPE_CONNECT
+??? 250 HELP
+QUIT
+??? 221
+****
+killdaemon
+#
+# Client does not wait for banner
+exim -bd -DSERVER=server -DCONNECTCOND=127.0.0.1 -oX PORT_D
+****
+#
+client 127.0.0.1 PORT_D
+EHLO testclient
+??? 220
+??? 250-
+??? 250-SIZE
+??? 250-8BITMIME
+??? 250-PIPELINING
+??? 250-X_PIPE_CONNECT
+??? 250 HELP
+QUIT
+??? 221
+****
+# No banner wait; pipelined MAIL
+client 127.0.0.1 PORT_D
+EHLO testclient\r\nMAIL FROM:<a@test.ex>
+??? 220
+??? 250-
+??? 250-SIZE
+??? 250-8BITMIME
+??? 250-PIPELINING
+??? 250-X_PIPE_CONNECT
+??? 250 HELP
+??? 250
+QUIT
+??? 221
+****
+# No banner wait; pipelined MAIL,RCPT
+client 127.0.0.1 PORT_D
+EHLO testclient\r\nMAIL FROM:<a@test.ex>\r\nRCPT TO:<a@test.ex>
+??? 220
+??? 250-
+??? 250-SIZE
+??? 250-8BITMIME
+??? 250-PIPELINING
+??? 250-X_PIPE_CONNECT
+??? 250 HELP
+??? 250
+??? 250
+QUIT
+??? 221
+****
+# No banner wait; pipelined MAIL,RCPT,DATA
+client 127.0.0.1 PORT_D
+EHLO testclient\r\nMAIL FROM:<a@test.ex>\r\nRCPT TO:<a@test.ex>\r\nDATA
+??? 220
+??? 250-
+??? 250-SIZE
+??? 250-8BITMIME
+??? 250-PIPELINING
+??? 250-X_PIPE_CONNECT
+??? 250 HELP
+??? 250
+??? 250
+??? 354
+.
+??? 250
+QUIT
+??? 221
+****
+#
+killdaemon
+no_msglog_check
diff --git a/test/scripts/4050-pipe-conn/4051 b/test/scripts/4050-pipe-conn/4051
new file mode 100644 (file)
index 0000000..45e4b74
--- /dev/null
@@ -0,0 +1,59 @@
+# pipelined-connect, client
+#
+# Not attempted when not advertised
+exim -bd -DSERVER=server -oX PORT_D
+****
+#
+exim notadvertised@test.ex
+Subject: test 1
+
+****
+exim -DCONTROL=127.0.0.1 -q
+****
+killdaemon
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+#
+# Not attempted without a cache entry
+exim -bd -DSERVER=server -DCONNECTCOND=* -oX PORT_D
+****
+#
+exim nocache@test.ex
+Subject test 2
+
+****
+exim -DCONTROL=127.0.0.1 -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+#
+# Go for it.  This one should do early-pipelinng.
+exim hascache@test.ex
+Subject test 3
+
+****
+exim -DCONTROL=127.0.0.1 -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+# Check that client doesn't try when not told to, even now there is
+# a cache entry
+exim clientno@test.ex
+Subject test 4
+
+****
+exim -DCONTROL=: -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+killdaemon
+#
+no_msglog_check
diff --git a/test/scripts/4050-pipe-conn/4052 b/test/scripts/4050-pipe-conn/4052
new file mode 100644 (file)
index 0000000..d41cf81
--- /dev/null
@@ -0,0 +1,243 @@
+# pipelined-connect, cache invalidation
+#
+#
+# Not attempted without a cache entry
+exim -bd -DSERVER=server -DCONNECTCOND=* -oX PORT_D
+****
+#
+exim nocache@test.ex
+Subject test 1
+
+****
+exim -DCONTROL=127.0.0.1 -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+#
+# Go for it.  This one should do early-pipelinng.
+exim hascache@test.ex
+Subject test 2
+
+****
+exim -DCONTROL=127.0.0.1 -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+# Check that client doesn't try when the cache entry is too old
+# It will however create a new entry
+sleep 2
+#
+exim client_old@test.ex
+Subject test 3
+
+****
+exim -DRETRY=1s -DCONTROL=127.0.0.1 -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+killdaemon
+#
+#
+#
+sudo rm DIR/spool/db/misc
+#
+# Check that the cache is invalidated on SMTP errors from the server for the banner
+#
+server PORT_D 3
+>220 banner
+<EHLO
+>250-esmtp-resp
+>250-PIPELINING
+>250-X_PIPE_CONNECT
+>250 ok
+<MAIL
+>250 mail-from accepted
+<RCPT
+>250 rcpt-to accepted
+<DATA
+>354 data go-ahead
+.
+>250 message accepted
+<QUIT
+>220 bye
+*eof
+>220 banner
+<EHLO
+>250-esmtp-resp
+>250-PIPELINING
+>250-X_PIPE_CONNECT
+>250 ok
+<MAIL
+>250 mail-from accepted
+<RCPT
+>250 rcpt-to accepted
+<DATA
+>354 data go-ahead
+.
+>250 message accepted
+<QUIT
+>220 bye
+*eof
+*sleep 2
+<EHLO
+<MAIL
+<RCPT
+<DATA
+>450 sorry no banner for you today
+>550 sync error
+>550 sync error
+>550 sync error
+>550 sync error
+<QUIT
+>>220 bye
+*eof
+****
+exim -DCONTROL=127.0.0.1 -odi nocache2@test.ex withcache2@test.ex badbanner@test.ex
+body 2
+****
+#
+# Check that the cache is not invalidated on SMTP errors from the server after early-pipelining
+#
+server PORT_D 6
+>220 banner
+<EHLO
+>250-esmtp-resp
+>250-PIPELINING
+>250-X_PIPE_CONNECT
+>250 ok
+<MAIL
+>250 mail-from accepted
+<RCPT
+>250 rcpt-to accepted
+<DATA
+>354 data go-ahead
+.
+>250 message accepted
+<QUIT
+>220 bye
+*eof
+>220 banner
+<EHLO
+>250-esmtp-resp
+>250-PIPELINING
+>250-X_PIPE_CONNECT
+>250 ok
+<MAIL
+>250 mail-from accepted
+<RCPT
+>250 rcpt-to accepted
+<DATA
+>354 data go-ahead
+.
+>250 message accepted
+<QUIT
+>220 bye
+*eof
+*sleep 2
+<EHLO
+<MAIL
+<RCPT
+<DATA
+>220 banner
+>250-esmtp-resp
+>250-PIPELINING
+>250-X_PIPE_CONNECT
+>250 ok
+>250 mail good
+>250 rcpt good
+>550 obscure data error
+<QUIT
+>>220 bye
+*eof
+*sleep 2
+<EHLO
+<MAIL
+<RCPT
+<DATA
+>220 banner
+>250-esmtp-resp
+>250-PIPELINING
+>250-X_PIPE_CONNECT
+>250 ok
+>250 mail good
+>550 rcpt refused
+>550 data cmd rejected - no valid recipient
+<QUIT
+>>220 bye
+*eof
+*sleep 2
+<EHLO
+<MAIL
+<RCPT
+<DATA
+>220 banner
+>250-esmtp-resp
+>250-PIPELINING
+>250-X_PIPE_CONNECT
+>250 ok
+>550 mail cmd refused
+>550 rcpt cmd rejected - no valid mail
+>550 data cmd rejected - no valid mail
+<QUIT
+>>220 bye
+*eof
+*sleep 2
+>220 banner
+<EHLO
+>250-esmtp-resp
+>250-PIPELINING
+>250-X_PIPE_CONNECT
+>250 ok
+<MAIL
+>250 mail-from accepted
+<RCPT
+>250 rcpt-to accepted
+<DATA
+>354 data go-ahead
+.
+>250 message accepted
+<QUIT
+>220 bye
+*eof
+****
+exim -DCONTROL=127.0.0.1 -odi nocache@test.ex withcache@test.ex baddata@test.ex badrcpt@test.ex badmail@test.ex withcache2@test.ex
+body
+****
+#
+#
+# Check that the cache is updated on a change of advertised extensions
+# The conn will be made pipelined anyway (because we don't find out early)
+# but it's the DB dump that matters.  Unfortunately the dump doesn't show
+# us the (binary) record content, only the key.  So you need to look at the
+# debug output after all.
+server PORT_D 1
+>220 banner
+<EHLO
+>250-esmtp-resp
+>250-PIPELINING
+>250-X_PIPE_CONNECT
+>250-STARTTLS
+>250 ok
+<MAIL
+>250 mail-from accepted
+<RCPT
+>250 rcpt-to accepted
+<DATA
+>354 data go-ahead
+.
+>250 message accepted
+<QUIT
+>220 bye
+*eof
+****
+exim -d-all+transport -DCONTROL=127.0.0.1 -odi extchange@test.ex
+body
+****
+dump misc
+#
+no_msglog_check
diff --git a/test/scripts/4050-pipe-conn/4053 b/test/scripts/4050-pipe-conn/4053
new file mode 100644 (file)
index 0000000..eea284f
--- /dev/null
@@ -0,0 +1,31 @@
+# CHUNKING
+#
+# Not attempted without a cache entry
+exim -bd -DSERVER=server -oX PORT_D
+****
+#
+exim a@test.ex
+Subject test 1
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+#
+# Go for it.  This one should do early-pipelinng.
+exim b@test.ex
+Subject test 2
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+killdaemon
+#
+no_msglog_check
diff --git a/test/scripts/4050-pipe-conn/REQUIRES b/test/scripts/4050-pipe-conn/REQUIRES
new file mode 100644 (file)
index 0000000..fd2535f
--- /dev/null
@@ -0,0 +1 @@
+support Experimental_PIPE_CONNECT
diff --git a/test/scripts/4056-pipe-conn-auth/4056 b/test/scripts/4056-pipe-conn-auth/4056
new file mode 100644 (file)
index 0000000..403cf62
--- /dev/null
@@ -0,0 +1,31 @@
+# AUTH
+#
+# Not attempted without a cache entry
+exim -bd -DSERVER=server -oX PORT_D
+****
+#
+exim a@test.ex
+Subject test 1
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+#
+# Go for it.  This one should do (limited) early-pipelinng.
+exim b@test.ex
+Subject test 2
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+killdaemon
+#
+no_msglog_check
diff --git a/test/scripts/4056-pipe-conn-auth/REQUIRES b/test/scripts/4056-pipe-conn-auth/REQUIRES
new file mode 100644 (file)
index 0000000..9ae94aa
--- /dev/null
@@ -0,0 +1,2 @@
+support Experimental_PIPE_CONNECT
+authenticator plaintext
diff --git a/test/scripts/4058-pipe-conn-tfo/4058 b/test/scripts/4058-pipe-conn-tfo/4058
new file mode 100644 (file)
index 0000000..3a388a2
--- /dev/null
@@ -0,0 +1,78 @@
+# Early-pipelining, with TFO
+#
+#
+# Packet delays so we can see TFO operational in packet captures
+sudo perl
+system ("tc qdisc add dev lo root netem delay 50ms");
+****
+#
+# Wipe any stored TFO cookie, to start from known state.
+sudo perl
+system ("ip tcp_metrics delete 127.0.0.1");
+****
+#
+#
+exim -bd -DSERVER=server -oX PORT_D
+****
+#
+# No early-pipe cache yet.  Run one through to prime it;
+# this should not use early-pipe despite both the client & server being enabled.
+# Should also prime the TFO cookie cache.
+exim nocache@test.ex
+Subject: tfo test
+
+Testing
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+#
+# Having primed the cache, this one should use it.
+exim hascache@test.ex
+Subject: tfo test 2
+
+Testing
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+killdaemon
+#
+# Again, with CHUNKING
+sudo rm DIR/spool/db/misc
+exim -bd -DSERVER=server -DOPT=* -oX PORT_D
+****
+#
+exim nocache@test.ex
+Subject: tfo test 3
+
+Testing
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+exim chunking@test.ex
+Subject: tfo test 4
+
+Testing
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+killdaemon
+#
+sudo perl
+system ("tc qdisc delete dev lo root");
+****
+#
+no_msglog_check
diff --git a/test/scripts/4058-pipe-conn-tfo/REQUIRES b/test/scripts/4058-pipe-conn-tfo/REQUIRES
new file mode 100644 (file)
index 0000000..c18c49b
--- /dev/null
@@ -0,0 +1,2 @@
+support Experimental_PIPE_CONNECT
+support TCP_Fast_Open
diff --git a/test/scripts/4060-pipe-conn-gnutls/4060 b/test/scripts/4060-pipe-conn-gnutls/4060
new file mode 100644 (file)
index 0000000..b07c5c0
--- /dev/null
@@ -0,0 +1,71 @@
+# starttls
+#
+# Not attempted without a cache entry
+exim -bd -DSERVER=server -oX PORT_D
+****
+#
+exim nocache@test.ex
+Subject test 1
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+#
+# Go for it.  This one should do early-pipelinng.
+exim hascache@test.ex
+Subject test 2
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+# Check that client doesn't try when not told to, even now there is
+# a cache entry
+exim clientno@test.ex
+Subject test 3
+
+****
+exim -DCONTROL=: -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+killdaemon
+#
+#
+# And again with CHUNKING
+sudo rm DIR/spool/db/misc
+exim -bd -DSERVER=server -DOPT=* -oX PORT_D
+****
+#
+exim nocache@test.ex
+Subject test 4
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+#
+# Go for it
+exim chunking@test.ex
+Subject test 5
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+killdaemon
+no_msglog_check
diff --git a/test/scripts/4060-pipe-conn-gnutls/4061 b/test/scripts/4060-pipe-conn-gnutls/4061
new file mode 100644 (file)
index 0000000..e4922b8
--- /dev/null
@@ -0,0 +1,43 @@
+# tls-on-connect
+#
+# Not attempted without a cache entry
+exim -bd -DSERVER=server -oX PORT_D
+****
+#
+exim nocache@test.ex
+Subject test 1
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+#
+# Go for it.  This one should do early-pipelinng.
+exim hascache@test.ex
+Subject test 2
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+# Check that client doesn't try when not told to, even now there is
+# a cache entry
+exim clientno@test.ex
+Subject test 3
+
+****
+exim -DCONTROL=: -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+killdaemon
+#
+no_msglog_check
diff --git a/test/scripts/4060-pipe-conn-gnutls/REQUIRES b/test/scripts/4060-pipe-conn-gnutls/REQUIRES
new file mode 100644 (file)
index 0000000..36c96e7
--- /dev/null
@@ -0,0 +1,2 @@
+support Experimental_PIPE_CONNECT
+support GnuTLS
diff --git a/test/scripts/4062-pipe-conn-openssl/4062 b/test/scripts/4062-pipe-conn-openssl/4062
new file mode 100644 (file)
index 0000000..87b156c
--- /dev/null
@@ -0,0 +1,72 @@
+# starttls
+#
+# Not attempted without a cache entry
+exim -bd -DSERVER=server -oX PORT_D
+****
+#
+exim a@test.ex
+Subject test 1
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+#
+# Go for it.  This one should do early-pipelinng.
+exim b@test.ex
+Subject test 2
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+# Check that client doesn't try when not told to, even now there is
+# a cache entry
+exim c@test.ex
+Subject test 3
+
+****
+exim -DCONTROL=: -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+killdaemon
+#
+#
+# And again with CHUNKING
+sudo rm DIR/spool/db/misc
+exim -bd -DSERVER=server -DOPT=* -oX PORT_D
+****
+#
+exim nocache@test.ex
+Subject test 4
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+#
+# Go for it
+exim chunking@test.ex
+Subject test 5
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+killdaemon
+#
+no_msglog_check
diff --git a/test/scripts/4062-pipe-conn-openssl/4063 b/test/scripts/4062-pipe-conn-openssl/4063
new file mode 100644 (file)
index 0000000..cdf2d96
--- /dev/null
@@ -0,0 +1,43 @@
+# tls-on-connect
+#
+# Not attempted without a cache entry
+exim -bd -DSERVER=server -oX PORT_D
+****
+#
+exim a@test.ex
+Subject test 1
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+#
+# Go for it.  This one should do early-pipelinng.
+exim b@test.ex
+Subject test 2
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+# Check that client doesn't try when not told to, even now there is
+# a cache entry
+exim c@test.ex
+Subject test 3
+
+****
+exim -DCONTROL=: -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+killdaemon
+#
+no_msglog_check
diff --git a/test/scripts/4062-pipe-conn-openssl/REQUIRES b/test/scripts/4062-pipe-conn-openssl/REQUIRES
new file mode 100644 (file)
index 0000000..3863ae7
--- /dev/null
@@ -0,0 +1,2 @@
+support Experimental_PIPE_CONNECT
+support OpenSSL
diff --git a/test/scripts/4064-pipe-conn-gnutls-auth/4064 b/test/scripts/4064-pipe-conn-gnutls-auth/4064
new file mode 100644 (file)
index 0000000..62be1e2
--- /dev/null
@@ -0,0 +1,60 @@
+# starttls
+#
+# Not attempted without a cache entry
+exim -bd -DSERVER=server -oX PORT_D
+****
+#
+exim nocache@test.ex
+Subject test 1
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+#
+# Go for it.  This one should do early-pipelinng.
+exim hascache@test.ex
+Subject test 2
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+killdaemon
+#
+#
+#
+# And again with CHUNKING
+sudo rm DIR/spool/db/misc
+exim -bd -DSERVER=server -DOPT=* -oX PORT_D
+****
+#
+exim nocache@test.ex
+Subject test 4
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+#
+# Go for it
+exim chunking@test.ex
+Subject test 5
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+killdaemon
+no_msglog_check
diff --git a/test/scripts/4064-pipe-conn-gnutls-auth/4065 b/test/scripts/4064-pipe-conn-gnutls-auth/4065
new file mode 100644 (file)
index 0000000..819e429
--- /dev/null
@@ -0,0 +1,32 @@
+# tls-on-connect
+#
+# Not attempted without a cache entry
+exim -bd -DSERVER=server -oX PORT_D
+****
+#
+exim nocache@test.ex
+Subject test 1
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+#
+# Go for it.  This one should do early-pipelinng.
+exim hascache@test.ex
+Subject test 2
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+#
+killdaemon
+#
+no_msglog_check
diff --git a/test/scripts/4064-pipe-conn-gnutls-auth/REQUIRES b/test/scripts/4064-pipe-conn-gnutls-auth/REQUIRES
new file mode 100644 (file)
index 0000000..fafecf0
--- /dev/null
@@ -0,0 +1,3 @@
+support Experimental_PIPE_CONNECT
+support GnuTLS
+authenticator plaintext
diff --git a/test/scripts/4066-pipe-conn-openssl-auth/4066 b/test/scripts/4066-pipe-conn-openssl-auth/4066
new file mode 100644 (file)
index 0000000..3dc16bb
--- /dev/null
@@ -0,0 +1,61 @@
+# starttls
+#
+# Not attempted without a cache entry
+exim -bd -DSERVER=server -oX PORT_D
+****
+#
+exim nocache@test.ex
+Subject test 1
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+#
+# Go for it.  This one should do early-pipelinng.
+exim hascache@test.ex
+Subject test 2
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+#
+killdaemon
+#
+#
+#
+# And again with CHUNKING
+sudo rm DIR/spool/db/misc
+exim -bd -DSERVER=server -DOPT=* -oX PORT_D
+****
+#
+exim nocache@test.ex
+Subject test 4
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+#
+# Go for it
+exim chunking@test.ex
+Subject test 5
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+killdaemon
+no_msglog_check
diff --git a/test/scripts/4066-pipe-conn-openssl-auth/4067 b/test/scripts/4066-pipe-conn-openssl-auth/4067
new file mode 100644 (file)
index 0000000..819e429
--- /dev/null
@@ -0,0 +1,32 @@
+# tls-on-connect
+#
+# Not attempted without a cache entry
+exim -bd -DSERVER=server -oX PORT_D
+****
+#
+exim nocache@test.ex
+Subject test 1
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+#
+# Go for it.  This one should do early-pipelinng.
+exim hascache@test.ex
+Subject test 2
+
+****
+exim -q
+****
+exim -DNOTDAEMON -DSERVER=server -q
+****
+#
+#
+#
+killdaemon
+#
+no_msglog_check
diff --git a/test/scripts/4066-pipe-conn-openssl-auth/REQUIRES b/test/scripts/4066-pipe-conn-openssl-auth/REQUIRES
new file mode 100644 (file)
index 0000000..e58e0f9
--- /dev/null
@@ -0,0 +1,3 @@
+support Experimental_PIPE_CONNECT
+support OpenSSL
+authenticator plaintext
index d050ebd..fb8bd40 100644 (file)
@@ -68,12 +68,12 @@ cmd buf flush ddd bytes
   SMTP>> QUIT
 cmd buf flush ddd bytes
   SMTP(close)>>
+>>>>>>>>>>>>>>>> Exim pid=pppp (TLS proxy) terminating with rc=0 >>>>>>>>>>>>>>>>
 LOG: MAIN
   => usery@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1]* X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no C="250 OK id=10HmbC-0005vi-00"
 LOG: MAIN
   Completed
 >>>>>>>>>>>>>>>> Exim pid=pppp (main) terminating with rc=0 >>>>>>>>>>>>>>>>
->>>>>>>>>>>>>>>> Exim pid=pppp (TLS proxy) terminating with rc=0 >>>>>>>>>>>>>>>>
 LOG: queue_run MAIN
   End queue run: pid=pppp -qqf
 >>>>>>>>>>>>>>>> Exim pid=pppp (main) terminating with rc=0 >>>>>>>>>>>>>>>>
@@ -147,12 +147,12 @@ cmd buf flush ddd bytes
   SMTP>> QUIT
 cmd buf flush ddd bytes
   SMTP(close)>>
+>>>>>>>>>>>>>>>> Exim pid=pppp (TLS proxy) terminating with rc=0 >>>>>>>>>>>>>>>>
 LOG: MAIN
   => userb@test.ex R=cl_override T=send_to_server H=127.0.0.1 [127.0.0.1]* X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no C="250 OK id=10HmbI-0005vi-00"
 LOG: MAIN
   Completed
 >>>>>>>>>>>>>>>> Exim pid=pppp (main) terminating with rc=0 >>>>>>>>>>>>>>>>
->>>>>>>>>>>>>>>> Exim pid=pppp (TLS proxy) terminating with rc=0 >>>>>>>>>>>>>>>>
 LOG: queue_run MAIN
   End queue run: pid=pppp -qqf
 >>>>>>>>>>>>>>>> Exim pid=pppp (main) terminating with rc=0 >>>>>>>>>>>>>>>>
index 4f72f35..c1fb863 100644 (file)
@@ -68,12 +68,12 @@ ok=1 send_quit=1 send_rset=0 continue_more=0 yield=0 first_address is NULL
   SMTP>> QUIT
 cmd buf flush ddd bytes
   SMTP(close)>>
+>>>>>>>>>>>>>>>> Exim pid=pppp (TLS proxy) terminating with rc=0 >>>>>>>>>>>>>>>>
 Leaving t1 transport
 LOG: MAIN
   => userb@test.ex R=client T=t1 H=127.0.0.1 [127.0.0.1]:1225 X=TLS1.x:ke_RSA_AES_256_CBC_SHAnnn:256 CV=no C="250 OK id=10HmaY-0005vi-00"
 LOG: MAIN
   Completed
 >>>>>>>>>>>>>>>> Exim pid=pppp (main) terminating with rc=0 >>>>>>>>>>>>>>>>
->>>>>>>>>>>>>>>> Exim pid=pppp (TLS proxy) terminating with rc=0 >>>>>>>>>>>>>>>>
 
 ******** SERVER ********
index 026cd28..8ea042e 100644 (file)
@@ -68,12 +68,12 @@ cmd buf flush ddd bytes
   SMTP>> QUIT
 cmd buf flush ddd bytes
   SMTP(close)>>
+>>>>>>>>>>>>>>>> Exim pid=pppp (TLS proxy) terminating with rc=0 >>>>>>>>>>>>>>>>
 LOG: MAIN
   => usery@test.ex R=client T=send_to_server H=127.0.0.1 [127.0.0.1]* X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no C="250 OK id=10HmbC-0005vi-00"
 LOG: MAIN
   Completed
 >>>>>>>>>>>>>>>> Exim pid=pppp (main) terminating with rc=0 >>>>>>>>>>>>>>>>
->>>>>>>>>>>>>>>> Exim pid=pppp (TLS proxy) terminating with rc=0 >>>>>>>>>>>>>>>>
 LOG: queue_run MAIN
   End queue run: pid=pppp -qqf
 >>>>>>>>>>>>>>>> Exim pid=pppp (main) terminating with rc=0 >>>>>>>>>>>>>>>>
@@ -147,12 +147,12 @@ cmd buf flush ddd bytes
   SMTP>> QUIT
 cmd buf flush ddd bytes
   SMTP(close)>>
+>>>>>>>>>>>>>>>> Exim pid=pppp (TLS proxy) terminating with rc=0 >>>>>>>>>>>>>>>>
 LOG: MAIN
   => userb@test.ex R=cl_override T=send_to_server H=127.0.0.1 [127.0.0.1]* X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no C="250 OK id=10HmbI-0005vi-00"
 LOG: MAIN
   Completed
 >>>>>>>>>>>>>>>> Exim pid=pppp (main) terminating with rc=0 >>>>>>>>>>>>>>>>
->>>>>>>>>>>>>>>> Exim pid=pppp (TLS proxy) terminating with rc=0 >>>>>>>>>>>>>>>>
 LOG: queue_run MAIN
   End queue run: pid=pppp -qqf
 >>>>>>>>>>>>>>>> Exim pid=pppp (main) terminating with rc=0 >>>>>>>>>>>>>>>>
index db08f08..1e323ed 100644 (file)
@@ -68,12 +68,12 @@ ok=1 send_quit=1 send_rset=0 continue_more=0 yield=0 first_address is NULL
   SMTP>> QUIT
 cmd buf flush ddd bytes
   SMTP(close)>>
+>>>>>>>>>>>>>>>> Exim pid=pppp (TLS proxy) terminating with rc=0 >>>>>>>>>>>>>>>>
 Leaving t1 transport
 LOG: MAIN
   => userb@test.ex R=client T=t1 H=127.0.0.1 [127.0.0.1]:1225 X=TLSv1:ke-RSA-AES256-SHA:xxx CV=no C="250 OK id=10HmaY-0005vi-00"
 LOG: MAIN
   Completed
 >>>>>>>>>>>>>>>> Exim pid=pppp (main) terminating with rc=0 >>>>>>>>>>>>>>>>
->>>>>>>>>>>>>>>> Exim pid=pppp (TLS proxy) terminating with rc=0 >>>>>>>>>>>>>>>>
 
 ******** SERVER ********
diff --git a/test/stderr/4052 b/test/stderr/4052
new file mode 100644 (file)
index 0000000..35739f1
--- /dev/null
@@ -0,0 +1,67 @@
+Exim version x.yz ....
+configuration file is TESTSUITE/test-config
+admin user
+LOG: MAIN
+  <= CALLER@the.local.host.name U=CALLER P=local S=sss
+Exim version x.yz ....
+configuration file is TESTSUITE/test-config
+trusted user
+admin user
+>>>>>>>>>>>>>>>> Remote deliveries >>>>>>>>>>>>>>>>
+--------> extchange@test.ex <--------
+smtp transport entered
+  extchange@test.ex
+hostlist:
+  '127.0.0.1' IP 127.0.0.1 port 1225
+checking status of 127.0.0.1
+no host retry record
+no message retry record
+127.0.0.1 [127.0.0.1]:1111 retry-status = usable
+delivering 10HmbG-0005vi-00 to 127.0.0.1 [127.0.0.1] (extchange@test.ex)
+Transport port=25 replaced by host-specific port=1225
+EHLO response bits from cache: cleartext 0x0220 crypted 0x0000
+Using cached cleartext PIPE_CONNECT
+  SMTP>> EHLO the.local.host.name
+using PIPELINING
+not using DSN
+  SMTP>> MAIL FROM:<CALLER@the.local.host.name>
+  SMTP>> RCPT TO:<extchange@test.ex>
+  SMTP>> DATA
+cmd buf flush ddd bytes
+Connecting to 127.0.0.1 [127.0.0.1]:1225 ... sending 87 nonTFO early-data
+connected
+smtp_reap_early_pipe expect banner
+  SMTP<< 220 banner
+smtp_reap_early_pipe expect ehlo
+  SMTP<< 250-esmtp-resp
+         250-PIPELINING
+         250-X_PIPE_CONNECT
+         250-STARTTLS
+         250 ok
+EHLO extensions changed, 0x0220/0x0000 -> 0x0221/0x0000
+sync_responses expect mail
+  SMTP<< 250 mail-from accepted
+sync_responses expect rcpt
+  SMTP<< 250 rcpt-to accepted
+sync_responses expect data
+  SMTP<< 354 data go-ahead
+  SMTP>> writing message and terminating "."
+cannot use sendfile for body: spoolfile not wireformat
+writing data block fd=dddd size=sss timeout=300
+  SMTP<< 250 message accepted
+ok=1 send_quit=1 send_rset=0 continue_more=0 yield=0 first_address is NULL
+transport_check_waiting entered
+  sequence=1 local_max=500 global_max=-1
+no messages waiting for 127.0.0.1
+  SMTP>> QUIT
+cmd buf flush ddd bytes
+  SMTP(close)>>
+Leaving smtp transport
+LOG: MAIN
+  => extchange@test.ex R=client T=smtp H=127.0.0.1 [127.0.0.1] L* C="250 message accepted"
+LOG: MAIN
+  Completed
+>>>>>>>>>>>>>>>> Exim pid=pppp (main) terminating with rc=0 >>>>>>>>>>>>>>>>
+>>>>>>>>>>>>>>>> Exim pid=pppp (main) terminating with rc=0 >>>>>>>>>>>>>>>>
+
+******** SERVER ********
diff --git a/test/stdout/4050 b/test/stdout/4050
new file mode 100644 (file)
index 0000000..783dfd6
--- /dev/null
@@ -0,0 +1,133 @@
+Connecting to 127.0.0.1 port 1225 ... connected
+??? 220
+<<< 220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+>>> EHLO testclient
+??? 250-
+<<< 250-the.local.host.name Hello testclient [127.0.0.1]
+??? 250-SIZE
+<<< 250-SIZE 52428800
+??? 250-8BITMIME
+<<< 250-8BITMIME
+??? 250-PIPELINING
+<<< 250-PIPELINING
+??? 250 HELP
+<<< 250 HELP
+>>> QUIT
+??? 221
+<<< 221 the.local.host.name closing connection
+End of script
+Connecting to 127.0.0.1 port 1225 ... connected
+??? 220
+<<< 220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+>>> EHLO testclient
+??? 250-
+<<< 250-the.local.host.name Hello testclient [127.0.0.1]
+??? 250-SIZE
+<<< 250-SIZE 52428800
+??? 250-8BITMIME
+<<< 250-8BITMIME
+??? 250-PIPELINING
+<<< 250-PIPELINING
+??? 250-X_PIPE_CONNECT
+<<< 250-X_PIPE_CONNECT
+??? 250 HELP
+<<< 250 HELP
+>>> QUIT
+??? 221
+<<< 221 the.local.host.name closing connection
+End of script
+Connecting to 127.0.0.1 port 1225 ... connected
+>>> EHLO testclient
+??? 220
+<<< 220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+??? 250-
+<<< 250-the.local.host.name Hello testclient [127.0.0.1]
+??? 250-SIZE
+<<< 250-SIZE 52428800
+??? 250-8BITMIME
+<<< 250-8BITMIME
+??? 250-PIPELINING
+<<< 250-PIPELINING
+??? 250-X_PIPE_CONNECT
+<<< 250-X_PIPE_CONNECT
+??? 250 HELP
+<<< 250 HELP
+>>> QUIT
+??? 221
+<<< 221 the.local.host.name closing connection
+End of script
+Connecting to 127.0.0.1 port 1225 ... connected
+>>> EHLO testclient\r\nMAIL FROM:<a@test.ex>
+??? 220
+<<< 220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+??? 250-
+<<< 250-the.local.host.name Hello testclient [127.0.0.1]
+??? 250-SIZE
+<<< 250-SIZE 52428800
+??? 250-8BITMIME
+<<< 250-8BITMIME
+??? 250-PIPELINING
+<<< 250-PIPELINING
+??? 250-X_PIPE_CONNECT
+<<< 250-X_PIPE_CONNECT
+??? 250 HELP
+<<< 250 HELP
+??? 250
+<<< 250 OK
+>>> QUIT
+??? 221
+<<< 221 the.local.host.name closing connection
+End of script
+Connecting to 127.0.0.1 port 1225 ... connected
+>>> EHLO testclient\r\nMAIL FROM:<a@test.ex>\r\nRCPT TO:<a@test.ex>
+??? 220
+<<< 220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+??? 250-
+<<< 250-the.local.host.name Hello testclient [127.0.0.1]
+??? 250-SIZE
+<<< 250-SIZE 52428800
+??? 250-8BITMIME
+<<< 250-8BITMIME
+??? 250-PIPELINING
+<<< 250-PIPELINING
+??? 250-X_PIPE_CONNECT
+<<< 250-X_PIPE_CONNECT
+??? 250 HELP
+<<< 250 HELP
+??? 250
+<<< 250 OK
+??? 250
+<<< 250 Accepted
+>>> QUIT
+??? 221
+<<< 221 the.local.host.name closing connection
+End of script
+Connecting to 127.0.0.1 port 1225 ... connected
+>>> EHLO testclient\r\nMAIL FROM:<a@test.ex>\r\nRCPT TO:<a@test.ex>\r\nDATA
+??? 220
+<<< 220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+??? 250-
+<<< 250-the.local.host.name Hello testclient [127.0.0.1]
+??? 250-SIZE
+<<< 250-SIZE 52428800
+??? 250-8BITMIME
+<<< 250-8BITMIME
+??? 250-PIPELINING
+<<< 250-PIPELINING
+??? 250-X_PIPE_CONNECT
+<<< 250-X_PIPE_CONNECT
+??? 250 HELP
+<<< 250 HELP
+??? 250
+<<< 250 OK
+??? 250
+<<< 250 Accepted
+??? 354
+<<< 354 Enter message, ending with "." on a line by itself
+>>> .
+??? 250
+<<< 250 OK id=10HmaX-0005vi-00
+>>> QUIT
+??? 221
+<<< 221 the.local.host.name closing connection
+End of script
diff --git a/test/stdout/4052 b/test/stdout/4052
new file mode 100644 (file)
index 0000000..0b38af6
--- /dev/null
@@ -0,0 +1,201 @@
++++++++++++++++++++++++++++
+07-Mar-2000 12:21:52 127.0.0.1:1225.EHLO
+
+******** SERVER ********
+Listening on port 1225 ... 
+Connection request from [127.0.0.1]
+>220 banner
+<EHLO
+>250-esmtp-resp
+>250-PIPELINING
+>250-X_PIPE_CONNECT
+>250 ok
+<MAIL
+>250 mail-from accepted
+<RCPT
+>250 rcpt-to accepted
+<DATA
+>354 data go-ahead
+R
+>250 message accepted
+<QUIT
+>220 bye
+Expected EOF read from client
+Listening on port 1225 ... 
+Connection request from [127.0.0.1]
+>220 banner
+<EHLO
+>250-esmtp-resp
+>250-PIPELINING
+>250-X_PIPE_CONNECT
+>250 ok
+<MAIL
+>250 mail-from accepted
+<RCPT
+>250 rcpt-to accepted
+<DATA
+>354 data go-ahead
+R
+>250 message accepted
+<QUIT
+>220 bye
+Expected EOF read from client
+Listening on port 1225 ... 
+Connection request from [127.0.0.1]
+*sleep 2
+<EHLO
+<MAIL
+<RCPT
+<DATA
+>450 sorry no banner for you today
+>550 sync error
+>550 sync error
+>550 sync error
+>550 sync error
+<QUIT
+>>220 bye
+Expected EOF read from client
+End of script
+Listening on port 1225 ... 
+Connection request from [127.0.0.1]
+>220 banner
+<EHLO
+>250-esmtp-resp
+>250-PIPELINING
+>250-X_PIPE_CONNECT
+>250 ok
+<MAIL
+>250 mail-from accepted
+<RCPT
+>250 rcpt-to accepted
+<DATA
+>354 data go-ahead
+R
+>250 message accepted
+<QUIT
+>220 bye
+Expected EOF read from client
+Listening on port 1225 ... 
+Connection request from [127.0.0.1]
+>220 banner
+<EHLO
+>250-esmtp-resp
+>250-PIPELINING
+>250-X_PIPE_CONNECT
+>250 ok
+<MAIL
+>250 mail-from accepted
+<RCPT
+>250 rcpt-to accepted
+<DATA
+>354 data go-ahead
+R
+>250 message accepted
+<QUIT
+>220 bye
+Expected EOF read from client
+Listening on port 1225 ... 
+Connection request from [127.0.0.1]
+*sleep 2
+<EHLO
+<MAIL
+<RCPT
+<DATA
+>220 banner
+>250-esmtp-resp
+>250-PIPELINING
+>250-X_PIPE_CONNECT
+>250 ok
+>250 mail good
+>250 rcpt good
+>550 obscure data error
+<QUIT
+>>220 bye
+Expected EOF read from client
+Listening on port 1225 ... 
+Connection request from [127.0.0.1]
+*sleep 2
+<EHLO
+<MAIL
+<RCPT
+<DATA
+>220 banner
+>250-esmtp-resp
+>250-PIPELINING
+>250-X_PIPE_CONNECT
+>250 ok
+>250 mail good
+>550 rcpt refused
+>550 data cmd rejected - no valid recipient
+<QUIT
+>>220 bye
+Expected EOF read from client
+Listening on port 1225 ... 
+Connection request from [127.0.0.1]
+*sleep 2
+<EHLO
+<MAIL
+<RCPT
+<DATA
+>220 banner
+>250-esmtp-resp
+>250-PIPELINING
+>250-X_PIPE_CONNECT
+>250 ok
+>550 mail cmd refused
+>550 rcpt cmd rejected - no valid mail
+>550 data cmd rejected - no valid mail
+<QUIT
+>>220 bye
+Expected EOF read from client
+Listening on port 1225 ... 
+Connection request from [127.0.0.1]
+*sleep 2
+>220 banner
+<EHLO
+>250-esmtp-resp
+>250-PIPELINING
+>250-X_PIPE_CONNECT
+>250 ok
+<MAIL
+>250 mail-from accepted
+<RCPT
+>250 rcpt-to accepted
+<DATA
+>354 data go-ahead
+R
+>250 message accepted
+<QUIT
+>220 bye
+Expected EOF read from client
+End of script
+Listening on port 1225 ... 
+Connection request from [127.0.0.1]
+>220 banner
+<EHLO the.local.host.name
+>250-esmtp-resp
+>250-PIPELINING
+>250-X_PIPE_CONNECT
+>250-STARTTLS
+>250 ok
+<MAIL FROM:<CALLER@the.local.host.name>
+>250 mail-from accepted
+<RCPT TO:<extchange@test.ex>
+>250 rcpt-to accepted
+<DATA
+>354 data go-ahead
+Received: from CALLER by the.local.host.name with local (Exim x.yz)
+       (envelope-from <CALLER@the.local.host.name>)
+       id 10HmbG-0005vi-00
+       for extchange@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmbG-0005vi-00@the.local.host.name>
+From: CALLER_NAME <CALLER@the.local.host.name>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+body
+.
+>250 message accepted
+<QUIT
+>220 bye
+Expected EOF read from client
+End of script