Multi-recipient cutthrough delivery. Bug 1542
authorJeremy Harris <jgh146exb@wizmail.org>
Tue, 13 Jan 2015 11:19:32 +0000 (11:19 +0000)
committerJeremy Harris <jgh146exb@wizmail.org>
Tue, 20 Jan 2015 13:39:22 +0000 (13:39 +0000)
Testing and fixes by: Heiko Schlittermann <hs@schlittermann.de>

13 files changed:
doc/doc-docbook/spec.xfpt
doc/doc-txt/ChangeLog
src/src/acl.c
src/src/globals.c
src/src/globals.h
src/src/receive.c
src/src/smtp_in.c
src/src/verify.c
test/confs/5400
test/log/5400
test/scripts/5400-cutthrough/5400
test/stderr/5400 [deleted file]
test/stdout/5400

index d78797fb77478dd4db6a2e6d334222db0e50fc78..d5a16cacc2eebf93d37f92d4dad8656516b2c86f 100644 (file)
@@ -28003,14 +28003,18 @@ is what is wanted for subsequent tests.
 .cindex "cutthrough" "requesting"
 This option requests delivery be attempted while the item is being received.
 
 .cindex "cutthrough" "requesting"
 This option requests delivery be attempted while the item is being received.
 
-The option usable in the RCPT ACL.
+The option is usable in the RCPT ACL.
 If enabled for a message recieved via smtp and routed to an smtp transport,
 If enabled for a message recieved via smtp and routed to an smtp transport,
-and the message has only one recipient,
+and only one transport, interface, destination host and port combination
+is used for all recipients of the message,
 then the delivery connection is made while the receiving connection is open
 and data is copied from one to the other.
 
 then the delivery connection is made while the receiving connection is open
 and data is copied from one to the other.
 
+An attempt to set this option for any recipient but the first
+for a mail will be quietly ignored.
 If a recipient-verify callout connection is subsequently
 If a recipient-verify callout connection is subsequently
-requested in the same ACL it is held open and used for the data,
+requested in the same ACL it is held open and used for
+any subsequent receipients and the data,
 otherwise one is made after the initial RCPT ACL completes.
 
 Note that routers are used in verify mode,
 otherwise one is made after the initial RCPT ACL completes.
 
 Note that routers are used in verify mode,
@@ -28022,12 +28026,13 @@ Headers may be modified by routers (subject to the above) and transports.
 Cutthrough delivery is not supported via transport-filters or when DKIM signing
 of outgoing messages is done, because it sends data to the ultimate destination
 before the entire message has been received from the source.
 Cutthrough delivery is not supported via transport-filters or when DKIM signing
 of outgoing messages is done, because it sends data to the ultimate destination
 before the entire message has been received from the source.
+It is not supported for messages recieved with the SMTP PRDR option in use.
 
 Should the ultimate destination system positively accept or reject the mail,
 a corresponding indication is given to the source system and nothing is queued.
 If there is a temporary error the item is queued for later delivery in the
 usual fashion. If the item is successfully delivered in cutthrough mode
 
 Should the ultimate destination system positively accept or reject the mail,
 a corresponding indication is given to the source system and nothing is queued.
 If there is a temporary error the item is queued for later delivery in the
 usual fashion. If the item is successfully delivered in cutthrough mode
-the log line is tagged with ">>" rather than "=>" and appears
+the delivery log lines are tagged with ">>" rather than "=>" and appear
 before the acceptance "<=" line.
 
 Delivery in this mode avoids the generation of a bounce mail to a
 before the acceptance "<=" line.
 
 Delivery in this mode avoids the generation of a bounce mail to a
index eeefef71c32d91ce0de7ac327822ba5ba38b9a15..57df78280b02b7c55951c7edc9a6377083a4d312 100644 (file)
@@ -44,6 +44,9 @@ JH/10 The smtp_receive_timeout main option is now expanded before use.
 JH/11 The incoming_interface log option now also enables logging of the
       local interface on delivery outgoing connections.
 
 JH/11 The incoming_interface log option now also enables logging of the
       local interface on delivery outgoing connections.
 
+JH/12 The cutthrough-routing facility now supports multi-recipient mails,
+      if the interface and destination host and port all match.
+
 
 
 Exim version 4.85
 
 
 Exim version 4.85
index 8fdae039074a29ccffef64cdec4a2503ee1b72fc..06c1c494c09f71579548c6894a7d9629e6eaf961 100644 (file)
@@ -3354,19 +3354,27 @@ for (; cb != NULL; cb = cb->next)
       break;
 
       case CONTROL_CUTTHROUGH_DELIVERY:
       break;
 
       case CONTROL_CUTTHROUGH_DELIVERY:
-      if (deliver_freeze)
-        *log_msgptr = US"frozen";
-      else if (queue_only_policy)
-        *log_msgptr = US"queue-only";
-      else if (fake_response == FAIL)
-        *log_msgptr = US"fakereject";
+      if (prdr_requested)
+       /* Too hard to think about for now.  We might in future cutthrough
+       the case where both sides handle prdr and this-node prdr acl
+       is "accept" */
+        *log_msgptr = string_sprintf(US"PRDR on %s reception\n", arg);
       else
        {
       else
        {
-       cutthrough_delivery = TRUE;
-       break;
+       if (deliver_freeze)
+         *log_msgptr = US"frozen";
+       else if (queue_only_policy)
+         *log_msgptr = US"queue-only";
+       else if (fake_response == FAIL)
+         *log_msgptr = US"fakereject";
+       else
+         {
+         if (rcpt_count == 1) cutthrough.delivery = TRUE;
+         break;
+         }
+       *log_msgptr = string_sprintf("\"control=%s\" on %s item",
+                                     arg, *log_msgptr);
        }
        }
-      *log_msgptr = string_sprintf("\"control=%s\" on %s item",
-                                   arg, *log_msgptr);
       return ERROR;
       }
     break;
       return ERROR;
       }
     break;
@@ -4351,9 +4359,9 @@ ratelimiters_cmd = NULL;
 log_reject_target = LOG_MAIN|LOG_REJECT;
 
 #ifndef DISABLE_PRDR
 log_reject_target = LOG_MAIN|LOG_REJECT;
 
 #ifndef DISABLE_PRDR
-if (where == ACL_WHERE_RCPT || where == ACL_WHERE_PRDR )
+if (where == ACL_WHERE_RCPT || where == ACL_WHERE_PRDR)
 #else
 #else
-if (where == ACL_WHERE_RCPT )
+if (where == ACL_WHERE_RCPT)
 #endif
   {
   adb = address_defaults;
 #endif
   {
   adb = address_defaults;
@@ -4397,9 +4405,7 @@ case ACL_WHERE_RCPT:
 #ifndef DISABLE_PRDR
 case ACL_WHERE_PRDR:
 #endif
 #ifndef DISABLE_PRDR
 case ACL_WHERE_PRDR:
 #endif
-  if( rcpt_count > 1 )
-    cancel_cutthrough_connection("more than one recipient");
-  else if (rc == OK  &&  cutthrough_delivery  &&  cutthrough_fd < 0)
+  if (rc == OK  &&  cutthrough.delivery  && rcpt_count > cutthrough.nrcpt)
     open_cutthrough_connection(addr);
   break;
 
     open_cutthrough_connection(addr);
   break;
 
index a8670e414511e65ad6115a5d3399469036a5e6db..a066f35955b875264d75b56270aaceb7a6f60c71 100644 (file)
@@ -506,8 +506,11 @@ int     continue_sequence      = 1;
 uschar *continue_transport     = NULL;
 
 uschar *csa_status             = NULL;
 uschar *continue_transport     = NULL;
 
 uschar *csa_status             = NULL;
-BOOL    cutthrough_delivery    = FALSE;
-int     cutthrough_fd          = -1;
+cut_t   cutthrough = {
+  FALSE,                               /* delivery: when to attempt */
+  -1,                                  /* fd: open connection */
+  0,                                   /* nrcpt: number of addresses */
+};
 
 BOOL    daemon_listen          = FALSE;
 uschar *daemon_smtp_port       = US"smtp";
 
 BOOL    daemon_listen          = FALSE;
 uschar *daemon_smtp_port       = US"smtp";
index 5495f54db483fbc5156762412d07dfd8bcae9c7d..b5ea8a49b432e78641b6566858e7d7681039c7b3 100644 (file)
@@ -291,8 +291,16 @@ extern int     continue_sequence;      /* Sequence num for continued delivery */
 extern uschar *continue_transport;     /* Transport for continued delivery */
 
 extern uschar *csa_status;             /* Client SMTP Authorization result */
 extern uschar *continue_transport;     /* Transport for continued delivery */
 
 extern uschar *csa_status;             /* Client SMTP Authorization result */
-extern BOOL    cutthrough_delivery;    /* Deliver in foreground */
-extern int     cutthrough_fd;          /* Connection for ditto */
+
+typedef struct {
+  BOOL         delivery;               /* When to attempt */
+  int          fd;                     /* Open connection */
+  int          nrcpt;                  /* Count of addresses */
+  uschar *     interface;              /* (address of) */
+  host_item    host;                   /* Host used */
+  address_item addr;                   /* (Chain of) addresses */
+} cut_t;
+extern cut_t cutthrough;               /* Deliver-concurrently */
 
 extern BOOL    daemon_listen;          /* True if listening required */
 extern uschar *daemon_smtp_port;       /* Can be a list of ports */
 
 extern BOOL    daemon_listen;          /* True if listening required */
 extern uschar *daemon_smtp_port;       /* Can be a list of ports */
index 8ab3166d010d1a1aa6d0eec2cb46f1ab88c07d65..977150c1d44e22ae17099c6e40af4c538f2e09bc 100644 (file)
@@ -997,7 +997,7 @@ switch(where)
   case ACL_WHERE_DKIM:
   case ACL_WHERE_MIME:
   case ACL_WHERE_DATA:
   case ACL_WHERE_DKIM:
   case ACL_WHERE_MIME:
   case ACL_WHERE_DATA:
-    if (cutthrough_fd >= 0 && (acl_removed_headers || acl_added_headers))
+    if (cutthrough.fd >= 0 && (acl_removed_headers || acl_added_headers))
     {
     log_write(0, LOG_MAIN|LOG_PANIC, "Header modification in data ACLs"
                        " will not take effect on cutthrough deliveries");
     {
     log_write(0, LOG_MAIN|LOG_PANIC, "Header modification in data ACLs"
                        " will not take effect on cutthrough deliveries");
@@ -2807,12 +2807,11 @@ if (filter_test != FTEST_NONE)
   }
 
 /* Cutthrough delivery:
   }
 
 /* Cutthrough delivery:
-       We have to create the Received header now rather than at the end of reception,
-       so the timestamp behaviour is a change to the normal case.
-       XXX Ensure this gets documented XXX.
-       Having created it, send the headers to the destination.
-*/
-if (cutthrough_fd >= 0)
+We have to create the Received header now rather than at the end of reception,
+so the timestamp behaviour is a change to the normal case.
+XXX Ensure this gets documented XXX.
+Having created it, send the headers to the destination. */
+if (cutthrough.fd >= 0)
   {
   if (received_count > received_headers_max)
     {
   {
   if (received_count > received_headers_max)
     {
@@ -3184,56 +3183,61 @@ else
           rc = OK;
           while ((item = string_nextinlist(&ptr, &sep,
                                            itembuf,
           rc = OK;
           while ((item = string_nextinlist(&ptr, &sep,
                                            itembuf,
-                                           sizeof(itembuf))) != NULL)
+                                           sizeof(itembuf))))
             {
             /* Prevent running ACL for an empty item */
             if (!item || (item[0] == '\0')) continue;
             {
             /* Prevent running ACL for an empty item */
             if (!item || (item[0] == '\0')) continue;
-            /* Only run ACL once for each domain or identity, no matter how often it
-               appears in the expanded list. */
-            if (seen_items != NULL)
+
+            /* Only run ACL once for each domain or identity,
+           no matter how often it appears in the expanded list. */
+            if (seen_items)
               {
               uschar *seen_item = NULL;
               uschar seen_item_buf[256];
               uschar *seen_items_list = seen_items;
               {
               uschar *seen_item = NULL;
               uschar seen_item_buf[256];
               uschar *seen_items_list = seen_items;
-              int seen_this_item = 0;
+              BOOL seen_this_item = FALSE;
 
               while ((seen_item = string_nextinlist(&seen_items_list, &sep,
                                                     seen_item_buf,
 
               while ((seen_item = string_nextinlist(&seen_items_list, &sep,
                                                     seen_item_buf,
-                                                    sizeof(seen_item_buf))) != NULL)
-                {
-                  if (Ustrcmp(seen_item,item) == 0)
-                    {
-                      seen_this_item = 1;
-                      break;
-                    }
-                }
-
-              if (seen_this_item > 0)
+                                                    sizeof(seen_item_buf))))
+               if (Ustrcmp(seen_item,item) == 0)
+                 {
+                 seen_this_item = TRUE;
+                 break;
+                 }
+
+              if (seen_this_item)
                 {
                 DEBUG(D_receive)
                 {
                 DEBUG(D_receive)
-                  debug_printf("acl_smtp_dkim: skipping signer %s, already seen\n", item);
+                  debug_printf("acl_smtp_dkim: skipping signer %s, "
+                   "already seen\n", item);
                 continue;
                 }
 
                 continue;
                 }
 
-              seen_items = string_append(seen_items,&seen_items_size,&seen_items_offset,1,":");
+              seen_items = string_append(seen_items, &seen_items_size,
+               &seen_items_offset, 1, ":");
               }
 
               }
 
-            seen_items = string_append(seen_items,&seen_items_size,&seen_items_offset,1,item);
+            seen_items = string_append(seen_items, &seen_items_size,
+             &seen_items_offset, 1, item);
             seen_items[seen_items_offset] = '\0';
 
             DEBUG(D_receive)
             seen_items[seen_items_offset] = '\0';
 
             DEBUG(D_receive)
-              debug_printf("calling acl_smtp_dkim for dkim_cur_signer=%s\n", item);
+              debug_printf("calling acl_smtp_dkim for dkim_cur_signer=%s\n",
+               item);
 
             dkim_exim_acl_setup(item);
 
             dkim_exim_acl_setup(item);
-            rc = acl_check(ACL_WHERE_DKIM, NULL, acl_smtp_dkim, &user_msg, &log_msg);
+            rc = acl_check(ACL_WHERE_DKIM, NULL, acl_smtp_dkim,
+                 &user_msg, &log_msg);
 
             if (rc != OK)
 
             if (rc != OK)
-              {
-                DEBUG(D_receive)
-                  debug_printf("acl_smtp_dkim: acl_check returned %d on %s, skipping remaining items\n", rc, item);
-               cancel_cutthrough_connection("dkim acl not ok");
-                break;
-              }
+             {
+             DEBUG(D_receive)
+               debug_printf("acl_smtp_dkim: acl_check returned %d on %s, "
+                 "skipping remaining items\n", rc, item);
+             cancel_cutthrough_connection("dkim acl not ok");
+             break;
+             }
             }
           add_acl_headers(ACL_WHERE_DKIM, US"DKIM");
           if (rc == DISCARD)
             }
           add_acl_headers(ACL_WHERE_DKIM, US"DKIM");
           if (rc == DISCARD)
@@ -3957,7 +3961,7 @@ for this message. */
 
    XXX We do not handle queue-only, freezing, or blackholes.
 */
 
    XXX We do not handle queue-only, freezing, or blackholes.
 */
-if(cutthrough_fd >= 0)
+if(cutthrough.fd >= 0)
   {
   uschar * msg= cutthrough_finaldot(); /* Ask the target system to accept the messsage */
                                        /* Logging was done in finaldot() */
   {
   uschar * msg= cutthrough_finaldot(); /* Ask the target system to accept the messsage */
                                        /* Logging was done in finaldot() */
@@ -4103,7 +4107,7 @@ if (smtp_input)
       case TMP_REJ: message_id[0] = 0;   /* Prevent a delivery from starting */
       default:break;
       }
       case TMP_REJ: message_id[0] = 0;   /* Prevent a delivery from starting */
       default:break;
       }
-    cutthrough_delivery = FALSE;
+    cutthrough.delivery = FALSE;
     }
 
   /* For batched SMTP, generate an error message on failure, and do
     }
 
   /* For batched SMTP, generate an error message on failure, and do
index 4fc2cfd4175f8ab54a983f95658aaa2d1de089a4..4e4cdb825ec879b3d24fd6b4bd1d79b6ad64d843 100644 (file)
@@ -4448,7 +4448,7 @@ while (done <= 0)
     ACL may have delayed.  To handle cutthrough delivery enforce a dummy call
     to get the DATA command sent. */
 
     ACL may have delayed.  To handle cutthrough delivery enforce a dummy call
     to get the DATA command sent. */
 
-    if (acl_smtp_predata == NULL && cutthrough_fd < 0) rc = OK; else
+    if (acl_smtp_predata == NULL && cutthrough.fd < 0) rc = OK; else
       {
       uschar * acl= acl_smtp_predata ? acl_smtp_predata : US"accept";
       enable_dollar_recipients = TRUE;
       {
       uschar * acl= acl_smtp_predata ? acl_smtp_predata : US"accept";
       enable_dollar_recipients = TRUE;
index 96740f8f382acdf44f13ac99e940005efbfe992a..4474f2c5bed908514254e7646cd360eb7037c303 100644 (file)
@@ -14,7 +14,6 @@ caching was contributed by Kevin Fleming (but I hacked it around a bit). */
 
 #define CUTTHROUGH_CMD_TIMEOUT  30     /* timeout for cutthrough-routing calls */
 #define CUTTHROUGH_DATA_TIMEOUT 60     /* timeout for cutthrough-routing calls */
 
 #define CUTTHROUGH_CMD_TIMEOUT  30     /* timeout for cutthrough-routing calls */
 #define CUTTHROUGH_DATA_TIMEOUT 60     /* timeout for cutthrough-routing calls */
-address_item cutthrough_addr;
 static smtp_outblock ctblock;
 uschar ctbuffer[8192];
 
 static smtp_outblock ctblock;
 uschar ctbuffer[8192];
 
@@ -39,6 +38,7 @@ static tree_node *dnsbl_cache = NULL;
 #define MT_NOT 1
 #define MT_ALL 2
 
 #define MT_NOT 1
 #define MT_ALL 2
 
+static uschar cutthrough_response(char, uschar **);
 
 
 /*************************************************
 
 
 /*************************************************
@@ -189,12 +189,12 @@ from_address = US"";
 
 if (is_recipient)
   {
 
 if (is_recipient)
   {
-  if ((options & vopt_callout_recipsender) != 0)
+  if (options & vopt_callout_recipsender)
     {
     address_key = string_sprintf("%s/<%s>", addr->address, sender_address);
     from_address = sender_address;
     }
     {
     address_key = string_sprintf("%s/<%s>", addr->address, sender_address);
     from_address = sender_address;
     }
-  else if ((options & vopt_callout_recippmaster) != 0)
+  else if (options & vopt_callout_recippmaster)
     {
     address_key = string_sprintf("%s/<postmaster@%s>", addr->address,
       qualify_domain_sender);
     {
     address_key = string_sprintf("%s/<postmaster@%s>", addr->address,
       qualify_domain_sender);
@@ -410,6 +410,113 @@ else
 
   if (smtp_out != NULL && !disable_callout_flush) mac_smtp_fflush();
 
 
   if (smtp_out != NULL && !disable_callout_flush) mac_smtp_fflush();
 
+/* cutthrough-multi: if a nonfirst rcpt has the same routing as the first,
+and we are holding a cutthrough conn open, we can just append the rcpt to
+that conn for verification purposes (and later delivery also).  Simplest
+coding means skipping this whole loop and doing the append separately.
+
+We will need to remember it has been appended so that rcpt-acl tail code
+can do it there for the non-rcpt-verify case.  For this we keep an addresscount.
+*/
+
+  /* Can we re-use an open cutthrough connection? */
+  if (  cutthrough.fd >= 0
+     && (options & (vopt_callout_recipsender | vopt_callout_recippmaster))
+       == vopt_callout_recipsender
+     && !random_local_part
+     && !pm_mailfrom
+     )
+    {
+    if (addr->transport == cutthrough.addr.transport)
+      for (host = host_list; host; host = host->next)
+       if (Ustrcmp(host->address, cutthrough.host.address) == 0)
+         {
+         int host_af;
+         uschar *interface = NULL;  /* Outgoing interface to use; NULL => any */
+         int port = 25;
+
+         deliver_host = host->name;
+         deliver_host_address = host->address;
+         deliver_host_port = host->port;
+         deliver_domain = addr->domain;
+         transport_name = addr->transport->name;
+
+         host_af = (Ustrchr(host->address, ':') == NULL)? AF_INET:AF_INET6;
+
+         if (!smtp_get_interface(tf->interface, host_af, addr, NULL, &interface,
+                 US"callout") ||
+             !smtp_get_port(tf->port, addr, &port, US"callout"))
+           log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address,
+             addr->message);
+
+         if (  (  interface == cutthrough.interface
+               || (  interface
+                  && cutthrough.interface
+                  && Ustrcmp(interface, cutthrough.interface) == 0
+               )  )
+            && port == cutthrough.host.port
+            )
+           {
+           uschar * resp;
+
+           /* Match!  Send the RCPT TO, append the addr, set done */
+           done =
+             smtp_write_command(&ctblock, FALSE, "RCPT TO:<%.1000s>\r\n",
+               transport_rcpt_address(addr,
+                 (addr->transport == NULL)? FALSE :
+                  addr->transport->rcpt_include_affixes)) >= 0 &&
+             cutthrough_response('2', &resp) == '2';
+
+           /* This would go horribly wrong if a callout fail was ignored by ACL.
+           We punt by abandoning cutthrough on a reject, like the
+           first-rcpt does. */
+
+           if (done)
+             {
+             address_item * na = store_get(sizeof(address_item));
+             *na = cutthrough.addr;
+             cutthrough.addr = *addr;
+             cutthrough.addr.host_used = &cutthrough.host;
+             cutthrough.addr.next = na;
+
+             cutthrough.nrcpt++;
+             }
+           else
+             {
+             cancel_cutthrough_connection("recipient rejected");
+             if (errno == ETIMEDOUT)
+               {
+               HDEBUG(D_verify) debug_printf("SMTP timeout\n");
+               }
+             else if (errno == 0)
+               {
+               if (*resp == 0)
+                 Ustrcpy(resp, US"connection dropped");
+
+               addr->message =
+                 string_sprintf("response to \"%s\" from %s [%s] was: %s",
+                   big_buffer, host->name, host->address,
+                   string_printing(resp));
+
+               addr->user_message =
+                 string_sprintf("Callout verification failed:\n%s", resp);
+
+               /* Hard rejection ends the process */
+
+               if (resp[0] == '5')   /* Address rejected */
+                 {
+                 yield = FAIL;
+                 done = TRUE;
+                 }
+               }
+             }
+           }
+         break;
+         }
+    if (!done)
+      cancel_cutthrough_connection("incompatible connection");
+    }
+
   /* Now make connections to the hosts and do real callouts. The list of hosts
   is passed in as an argument. */
 
   /* Now make connections to the hosts and do real callouts. The list of hosts
   is passed in as an argument. */
 
@@ -692,9 +799,9 @@ else
 
        ob->command_timeout = callout;
         rc = tls_client_start(inblock.sock, host, addr, addr->transport
 
        ob->command_timeout = callout;
         rc = tls_client_start(inblock.sock, host, addr, addr->transport
-#ifdef EXPERIMENTAL_DANE
+# ifdef EXPERIMENTAL_DANE
                            , dane ? &tlsa_dnsa : NULL
                            , dane ? &tlsa_dnsa : NULL
-#endif
+# endif
                            );
        ob->command_timeout = oldtimeout;
 
                            );
        ob->command_timeout = oldtimeout;
 
@@ -709,10 +816,10 @@ else
             )
            {
            (void)close(inblock.sock);
             )
            {
            (void)close(inblock.sock);
-#ifdef EXPERIMENTAL_EVENT
+# ifdef EXPERIMENTAL_EVENT
            (void) event_raise(addr->transport->event_action,
                                    US"tcp:close", NULL);
            (void) event_raise(addr->transport->event_action,
                                    US"tcp:close", NULL);
-#endif
+# endif
            log_write(0, LOG_MAIN, "TLS session failure: delivering unencrypted "
              "to %s [%s] (not in hosts_require_tls)", host->name, host->address);
            suppress_tls = TRUE;
            log_write(0, LOG_MAIN, "TLS session failure: delivering unencrypted "
              "to %s [%s] (not in hosts_require_tls)", host->name, host->address);
            suppress_tls = TRUE;
@@ -741,9 +848,9 @@ else
     /* If the host is required to use a secure channel, ensure that we have one. */
     if (tls_out.active < 0)
       if (
     /* If the host is required to use a secure channel, ensure that we have one. */
     if (tls_out.active < 0)
       if (
-#ifdef EXPERIMENTAL_DANE
+# ifdef EXPERIMENTAL_DANE
         dane ||
         dane ||
-#endif
+# endif
          verify_check_given_host(&ob->hosts_require_tls, host) == OK
         )
         {
          verify_check_given_host(&ob->hosts_require_tls, host) == OK
         )
         {
@@ -757,7 +864,7 @@ else
         goto TLS_FAILED;
         }
 
         goto TLS_FAILED;
         }
 
-    #endif /*SUPPORT_TLS*/
+#endif /*SUPPORT_TLS*/
 
     done = TRUE; /* so far so good; have response to HELO */
 
 
     done = TRUE; /* so far so good; have response to HELO */
 
@@ -765,17 +872,17 @@ else
 
     /* For now, transport_filter by cutthrough-delivery is not supported */
     /* Need proper integration with the proper transport mechanism. */
 
     /* For now, transport_filter by cutthrough-delivery is not supported */
     /* Need proper integration with the proper transport mechanism. */
-    if (cutthrough_delivery)
+    if (cutthrough.delivery)
       {
       if (addr->transport->filter_command)
         {
       {
       if (addr->transport->filter_command)
         {
-        cutthrough_delivery= FALSE;
+        cutthrough.delivery = FALSE;
         HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of transport filter\n");
         }
 #ifndef DISABLE_DKIM
       if (ob->dkim_domain)
         {
         HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of transport filter\n");
         }
 #ifndef DISABLE_DKIM
       if (ob->dkim_domain)
         {
-        cutthrough_delivery= FALSE;
+        cutthrough.delivery = FALSE;
         HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of DKIM signing\n");
         }
 #endif
         HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of DKIM signing\n");
         }
 #endif
@@ -887,6 +994,8 @@ else
 
         else if (errno == 0)
           {
 
         else if (errno == 0)
           {
+         cancel_cutthrough_connection("random-recipient");
+
           if (randombuffer[0] == '5')
             new_domain_record.random_result = ccache_reject;
 
           if (randombuffer[0] == '5')
             new_domain_record.random_result = ccache_reject;
 
@@ -932,8 +1041,9 @@ else
 
         if (done && pm_mailfrom != NULL)
           {
 
         if (done && pm_mailfrom != NULL)
           {
-          /*XXX not suitable for cutthrough - sequencing problems */
-       cutthrough_delivery= FALSE;
+          /*XXX not suitable for cutthrough - we cannot afford to do an RSET
+         and lose the original mail-from */
+       cancel_cutthrough_connection("postmaster verify");
        HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of postmaster verify\n");
 
           done =
        HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of postmaster verify\n");
 
           done =
@@ -1028,30 +1138,34 @@ else
 
     /* End the SMTP conversation and close the connection. */
 
 
     /* End the SMTP conversation and close the connection. */
 
-    /* Cutthrough - on a successfull connect and recipient-verify with use-sender
-    and we have no cutthrough conn so far
+    /* Cutthrough - on a successfull connect and recipient-verify with
+    use-sender and we are 1st rcpt and have no cutthrough conn so far
     here is where we want to leave the conn open */
     here is where we want to leave the conn open */
-    if (  cutthrough_delivery
+    if (  cutthrough.delivery
+       && rcpt_count == 1
        && done
        && yield == OK
        && (options & (vopt_callout_recipsender|vopt_callout_recippmaster)) == vopt_callout_recipsender
        && !random_local_part
        && !pm_mailfrom
        && done
        && yield == OK
        && (options & (vopt_callout_recipsender|vopt_callout_recippmaster)) == vopt_callout_recipsender
        && !random_local_part
        && !pm_mailfrom
-       && cutthrough_fd < 0
+       && cutthrough.fd < 0
        )
       {
        )
       {
-      cutthrough_fd= outblock.sock;    /* We assume no buffer in use in the outblock */
-      cutthrough_addr = *addr;         /* Save the address_item for later logging */
-      cutthrough_addr.next =     NULL;
-      cutthrough_addr.host_used = store_get(sizeof(host_item));
-      *(cutthrough_addr.host_used) = *host;
+      cutthrough.fd = outblock.sock;   /* We assume no buffer in use in the outblock */
+      cutthrough.nrcpt = 1;
+      cutthrough.interface = interface;
+      cutthrough.host = *host;
+      cutthrough.addr = *addr;         /* Save the address_item for later logging */
+      cutthrough.addr.next =     NULL;
+      cutthrough.addr.host_used = &cutthrough.host;
       if (addr->parent)
       if (addr->parent)
-        *(cutthrough_addr.parent = store_get(sizeof(address_item)))= *addr->parent;
+        *(cutthrough.addr.parent = store_get(sizeof(address_item))) =
+         *addr->parent;
       ctblock.buffer = ctbuffer;
       ctblock.buffersize = sizeof(ctbuffer);
       ctblock.ptr = ctbuffer;
       /* ctblock.cmd_count = 0; ctblock.authenticating = FALSE; */
       ctblock.buffer = ctbuffer;
       ctblock.buffersize = sizeof(ctbuffer);
       ctblock.ptr = ctbuffer;
       /* ctblock.cmd_count = 0; ctblock.authenticating = FALSE; */
-      ctblock.sock = cutthrough_fd;
+      ctblock.sock = cutthrough.fd;
       }
     else
       {
       }
     else
       {
@@ -1176,7 +1290,8 @@ address_item addr2;
 get rewritten. */
 
 addr2 = *addr;
 get rewritten. */
 
 addr2 = *addr;
-HDEBUG(D_acl) debug_printf("----------- start cutthrough setup ------------\n");
+HDEBUG(D_acl) debug_printf("----------- %s cutthrough setup ------------\n",
+  rcpt_count > 1 ? "more" : "start");
 (void) verify_address(&addr2, NULL,
        vopt_is_recipient | vopt_callout_recipsender | vopt_callout_no_cache,
        CUTTHROUGH_CMD_TIMEOUT, -1, -1,
 (void) verify_address(&addr2, NULL,
        vopt_is_recipient | vopt_callout_recipsender | vopt_callout_no_cache,
        CUTTHROUGH_CMD_TIMEOUT, -1, -1,
@@ -1191,14 +1306,14 @@ return;
 static BOOL
 cutthrough_send(int n)
 {
 static BOOL
 cutthrough_send(int n)
 {
-if(cutthrough_fd < 0)
+if(cutthrough.fd < 0)
   return TRUE;
 
 if(
 #ifdef SUPPORT_TLS
   return TRUE;
 
 if(
 #ifdef SUPPORT_TLS
-   (tls_out.active == cutthrough_fd) ? tls_write(FALSE, ctblock.buffer, n) :
+   (tls_out.active == cutthrough.fd) ? tls_write(FALSE, ctblock.buffer, n) :
 #endif
 #endif
-   send(cutthrough_fd, ctblock.buffer, n, 0) > 0
+   send(cutthrough.fd, ctblock.buffer, n, 0) > 0
   )
 {
   transport_count += n;
   )
 {
   transport_count += n;
@@ -1230,7 +1345,7 @@ return TRUE;
 BOOL
 cutthrough_puts(uschar * cp, int n)
 {
 BOOL
 cutthrough_puts(uschar * cp, int n)
 {
-if (cutthrough_fd < 0)       return TRUE;
+if (cutthrough.fd < 0)       return TRUE;
 if (_cutthrough_puts(cp, n)) return TRUE;
 cancel_cutthrough_connection("transmit failed");
 return FALSE;
 if (_cutthrough_puts(cp, n)) return TRUE;
 cancel_cutthrough_connection("transmit failed");
 return FALSE;
@@ -1238,7 +1353,7 @@ return FALSE;
 
 
 static BOOL
 
 
 static BOOL
-_cutthrough_flush_send( void )
+_cutthrough_flush_send(void)
 {
 int n= ctblock.ptr-ctblock.buffer;
 
 {
 int n= ctblock.ptr-ctblock.buffer;
 
@@ -1251,7 +1366,7 @@ return TRUE;
 
 /* Send out any bufferred output.  Return boolean success. */
 BOOL
 
 /* Send out any bufferred output.  Return boolean success. */
 BOOL
-cutthrough_flush_send( void )
+cutthrough_flush_send(void)
 {
 if (_cutthrough_flush_send()) return TRUE;
 cancel_cutthrough_connection("transmit failed");
 {
 if (_cutthrough_flush_send()) return TRUE;
 cancel_cutthrough_connection("transmit failed");
@@ -1260,7 +1375,7 @@ return FALSE;
 
 
 BOOL
 
 
 BOOL
-cutthrough_put_nl( void )
+cutthrough_put_nl(void)
 {
 return cutthrough_puts(US"\r\n", 2);
 }
 {
 return cutthrough_puts(US"\r\n", 2);
 }
@@ -1278,7 +1393,7 @@ inblock.buffer = inbuffer;
 inblock.buffersize = sizeof(inbuffer);
 inblock.ptr = inbuffer;
 inblock.ptrend = inbuffer;
 inblock.buffersize = sizeof(inbuffer);
 inblock.ptr = inbuffer;
 inblock.ptrend = inbuffer;
-inblock.sock = cutthrough_fd;
+inblock.sock = cutthrough.fd;
 /* this relies on (inblock.sock == tls_out.active) */
 if(!smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), expect, CUTTHROUGH_DATA_TIMEOUT))
   cancel_cutthrough_connection("target timeout on read");
 /* this relies on (inblock.sock == tls_out.active) */
 if(!smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), expect, CUTTHROUGH_DATA_TIMEOUT))
   cancel_cutthrough_connection("target timeout on read");
@@ -1286,7 +1401,7 @@ if(!smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), expect,
 if(copy != NULL)
   {
   uschar * cp;
 if(copy != NULL)
   {
   uschar * cp;
-  *copy= cp= string_copy(responsebuffer);
+  *copy = cp = string_copy(responsebuffer);
   /* Trim the trailing end of line */
   cp += Ustrlen(responsebuffer);
   if(cp > *copy  &&  cp[-1] == '\n') *--cp = '\0';
   /* Trim the trailing end of line */
   cp += Ustrlen(responsebuffer);
   if(cp > *copy  &&  cp[-1] == '\n') *--cp = '\0';
@@ -1299,9 +1414,9 @@ return responsebuffer[0];
 
 /* Negotiate dataphase with the cutthrough target, returning success boolean */
 BOOL
 
 /* Negotiate dataphase with the cutthrough target, returning success boolean */
 BOOL
-cutthrough_predata( void )
+cutthrough_predata(void)
 {
 {
-if(cutthrough_fd < 0)
+if(cutthrough.fd < 0)
   return FALSE;
 
 HDEBUG(D_transport|D_acl|D_v) debug_printf("  SMTP>> DATA\n");
   return FALSE;
 
 HDEBUG(D_transport|D_acl|D_v) debug_printf("  SMTP>> DATA\n");
@@ -1332,9 +1447,9 @@ return TRUE;
 /* Expands newlines to wire format (CR,NL).           */
 /* Also sends header-terminating blank line.          */
 BOOL
 /* Expands newlines to wire format (CR,NL).           */
 /* Also sends header-terminating blank line.          */
 BOOL
-cutthrough_headers_send( void )
+cutthrough_headers_send(void)
 {
 {
-if(cutthrough_fd < 0)
+if(cutthrough.fd < 0)
   return FALSE;
 
 /* We share a routine with the mainline transport to handle header add/remove/rewrites,
   return FALSE;
 
 /* We share a routine with the mainline transport to handle header add/remove/rewrites,
@@ -1342,10 +1457,12 @@ if(cutthrough_fd < 0)
 */
 HDEBUG(D_acl) debug_printf("----------- start cutthrough headers send -----------\n");
 
 */
 HDEBUG(D_acl) debug_printf("----------- start cutthrough headers send -----------\n");
 
-if (!transport_headers_send(&cutthrough_addr, cutthrough_fd,
-       cutthrough_addr.transport->add_headers, cutthrough_addr.transport->remove_headers,
+if (!transport_headers_send(&cutthrough.addr, cutthrough.fd,
+       cutthrough.addr.transport->add_headers,
+       cutthrough.addr.transport->remove_headers,
        &cutthrough_write_chunk, TRUE,
        &cutthrough_write_chunk, TRUE,
-       cutthrough_addr.transport->rewrite_rules, cutthrough_addr.transport->rewrite_existflags))
+       cutthrough.addr.transport->rewrite_rules,
+       cutthrough.addr.transport->rewrite_existflags))
   return FALSE;
 
 HDEBUG(D_acl) debug_printf("----------- done cutthrough headers send ------------\n");
   return FALSE;
 
 HDEBUG(D_acl) debug_printf("----------- done cutthrough headers send ------------\n");
@@ -1354,9 +1471,9 @@ return TRUE;
 
 
 static void
 
 
 static void
-close_cutthrough_connection( const char * why )
+close_cutthrough_connection(const char * why)
 {
 {
-if(cutthrough_fd >= 0)
+if(cutthrough.fd >= 0)
   {
   /* We could be sending this after a bunch of data, but that is ok as
      the only way to cancel the transfer in dataphase is to drop the tcp
   {
   /* We could be sending this after a bunch of data, but that is ok as
      the only way to cancel the transfer in dataphase is to drop the tcp
@@ -1371,18 +1488,18 @@ if(cutthrough_fd >= 0)
   #ifdef SUPPORT_TLS
   tls_close(FALSE, TRUE);
   #endif
   #ifdef SUPPORT_TLS
   tls_close(FALSE, TRUE);
   #endif
-  (void)close(cutthrough_fd);
-  cutthrough_fd= -1;
+  (void)close(cutthrough.fd);
+  cutthrough.fd = -1;
   HDEBUG(D_acl) debug_printf("----------- cutthrough shutdown (%s) ------------\n", why);
   }
 ctblock.ptr = ctbuffer;
 }
 
 void
   HDEBUG(D_acl) debug_printf("----------- cutthrough shutdown (%s) ------------\n", why);
   }
 ctblock.ptr = ctbuffer;
 }
 
 void
-cancel_cutthrough_connection( const char * why )
+cancel_cutthrough_connection(const char * why)
 {
 close_cutthrough_connection(why);
 {
 close_cutthrough_connection(why);
-cutthrough_delivery= FALSE;
+cutthrough.delivery = FALSE;
 }
 
 
 }
 
 
@@ -1394,33 +1511,45 @@ cutthrough_delivery= FALSE;
    Return smtp response-class digit.
 */
 uschar *
    Return smtp response-class digit.
 */
 uschar *
-cutthrough_finaldot( void )
+cutthrough_finaldot(void)
 {
 {
+uschar res;
+address_item * addr;
 HDEBUG(D_transport|D_acl|D_v) debug_printf("  SMTP>> .\n");
 
 /* Assume data finshed with new-line */
 HDEBUG(D_transport|D_acl|D_v) debug_printf("  SMTP>> .\n");
 
 /* Assume data finshed with new-line */
-if(!cutthrough_puts(US".", 1) || !cutthrough_put_nl() || !cutthrough_flush_send())
-  return cutthrough_addr.message;
+if(  !cutthrough_puts(US".", 1)
+  || !cutthrough_put_nl()
+  || !cutthrough_flush_send()
+  )
+  return cutthrough.addr.message;
 
 
-switch(cutthrough_response('2', &cutthrough_addr.message))
+res = cutthrough_response('2', &cutthrough.addr.message);
+for (addr = &cutthrough.addr; addr; addr = addr->next)
   {
   {
-  case '2':
-    delivery_log(LOG_MAIN, &cutthrough_addr, (int)'>', NULL);
-    close_cutthrough_connection("delivered");
-    break;
+  addr->message = cutthrough.addr.message;
+  switch(res)
+    {
+    case '2':
+      delivery_log(LOG_MAIN, addr, (int)'>', NULL);
+      close_cutthrough_connection("delivered");
+      break;
 
 
-  case '4':
-    delivery_log(LOG_MAIN, &cutthrough_addr, 0, US"tmp-reject from cutthrough after DATA:");
-    break;
+    case '4':
+      delivery_log(LOG_MAIN, addr, 0,
+       US"tmp-reject from cutthrough after DATA:");
+      break;
 
 
-  case '5':
-    delivery_log(LOG_MAIN|LOG_REJECT, &cutthrough_addr, 0, US"rejected after DATA:");
-    break;
+    case '5':
+      delivery_log(LOG_MAIN|LOG_REJECT, addr, 0,
+       US"rejected after DATA:");
+      break;
 
 
-  default:
-    break;
+    default:
+      break;
+    }
   }
   }
-  return cutthrough_addr.message;
+return cutthrough.addr.message;
 }
 
 
 }
 
 
index 09a0aafcf85b33bad8b742e9721641b7ee649fbb..5948ce8d2f843a5cbe9a39b892caef79b3dc5ed9 100644 (file)
@@ -8,6 +8,8 @@ log_file_path = DIR/spool/log/%slog
 gecos_pattern = ""
 gecos_name = CALLER_NAME
 
 gecos_pattern = ""
 gecos_name = CALLER_NAME
 
+log_selector = +received_recipients
+
 # ----- Main settings -----
 
 domainlist local_domains = test.ex : *.test.ex
 # ----- Main settings -----
 
 domainlist local_domains = test.ex : *.test.ex
@@ -27,12 +29,18 @@ ar:
 
 begin routers
 
 
 begin routers
 
+dns:
+  driver = dnslookup
+  domains = localhost.test.ex : thishost.test.ex
+  self = send
+  transport = smtp
+
 all:
   driver = manualroute
   domains = ! +local_domains
 all:
   driver = manualroute
   domains = ! +local_domains
-  route_list = * 127.0.0.1
+  route_list = special.com HOSTIPV4 ; * 127.0.0.1
   self = send
   self = send
-  transport = smtp
+  transport = ${if eq {special_tpt}{$local_part} {smtp2}{smtp}}
   headers_remove = X-hdr-rtr
   headers_add =    X-hdr-rtr-new: $h_X-hdr-rtr:+++
   no_more
   headers_remove = X-hdr-rtr
   headers_add =    X-hdr-rtr-new: $h_X-hdr-rtr:+++
   no_more
@@ -48,5 +56,10 @@ smtp:
   port = PORT_S
   headers_add =  ${if def:h_X-hdr-rtr {X-hdr-tpt-new: new} {}}
 
   port = PORT_S
   headers_add =  ${if def:h_X-hdr-rtr {X-hdr-tpt-new: new} {}}
 
+smtp2:
+  driver = smtp
+  interface = HOSTIPV4
+  port = PORT_S
+
 
 # End
 
 # End
index 6b51348c33bda4cea10b11d7d34f21e825bd3c58..c6f366272056ebbc8c0b1fefeab95116b175c81a 100644 (file)
@@ -1,18 +1,54 @@
 1999-03-02 09:44:33 rcpt for userx@domain.com
 1999-03-02 09:44:33 10HmaX-0005vi-00 >> userx@domain.com R=all T=smtp H=127.0.0.1 [127.0.0.1] C="250 OK"
 1999-03-02 09:44:33 rcpt for userx@domain.com
 1999-03-02 09:44:33 10HmaX-0005vi-00 >> userx@domain.com R=all T=smtp H=127.0.0.1 [127.0.0.1] C="250 OK"
-1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss for userx@domain.com
 1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
 1999-03-02 09:44:33 rcpt for userz@domain.com
 1999-03-02 09:44:33 10HmaY-0005vi-00 >> userz@domain.com R=all T=smtp H=127.0.0.1 [127.0.0.1] C="250 OK"
 1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
 1999-03-02 09:44:33 rcpt for userz@domain.com
 1999-03-02 09:44:33 10HmaY-0005vi-00 >> userz@domain.com R=all T=smtp H=127.0.0.1 [127.0.0.1] C="250 OK"
-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 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss for userz@domain.com
 1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
 1999-03-02 09:44:33 rcpt for usery@domain.com
 1999-03-02 09:44:33 rcpt for userx@domain.com
 1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
 1999-03-02 09:44:33 rcpt for usery@domain.com
 1999-03-02 09:44:33 rcpt for userx@domain.com
-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 => usery@domain.com R=all T=smtp H=127.0.0.1 [127.0.0.1] C="250 OK"
-1999-03-02 09:44:33 10HmaZ-0005vi-00 -> userx@domain.com R=all T=smtp H=127.0.0.1 [127.0.0.1] C="250 OK"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 >> userx@domain.com R=all T=smtp H=127.0.0.1 [127.0.0.1] C="250 OK"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 >> usery@domain.com R=all T=smtp H=127.0.0.1 [127.0.0.1] C="250 OK"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss for usery@domain.com userx@domain.com
 1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
 1999-03-02 09:44:33 rcpt for userx@domain.com
 1999-03-02 09:44:33 10HmbA-0005vi-00 >> userx@domain.com R=all T=smtp H=127.0.0.1 [127.0.0.1] C="250 OK"
 1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
 1999-03-02 09:44:33 rcpt for userx@domain.com
 1999-03-02 09:44:33 10HmbA-0005vi-00 >> userx@domain.com R=all T=smtp H=127.0.0.1 [127.0.0.1] C="250 OK"
-1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss for userx@domain.com
 1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
 1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
+1999-03-02 09:44:33 rcpt for no@domain.com
+1999-03-02 09:44:33 rcpt for userx@domain.com
+1999-03-02 09:44:33 10HmbB-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss for no@domain.com userx@domain.com
+1999-03-02 09:44:33 10HmbB-0005vi-00 => no@domain.com R=all T=smtp H=127.0.0.1 [127.0.0.1] C="250 OK"
+1999-03-02 09:44:33 10HmbB-0005vi-00 -> userx@domain.com R=all T=smtp H=127.0.0.1 [127.0.0.1] C="250 OK"
+1999-03-02 09:44:33 10HmbB-0005vi-00 Completed
+1999-03-02 09:44:33 rcpt for userx@domain.com
+1999-03-02 09:44:33 rcpt for no@domain.com
+1999-03-02 09:44:33 10HmbC-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss for userx@domain.com no@domain.com
+1999-03-02 09:44:33 10HmbC-0005vi-00 => userx@domain.com R=all T=smtp H=127.0.0.1 [127.0.0.1] C="250 OK"
+1999-03-02 09:44:33 10HmbC-0005vi-00 -> no@domain.com R=all T=smtp H=127.0.0.1 [127.0.0.1] C="250 OK"
+1999-03-02 09:44:33 10HmbC-0005vi-00 Completed
+1999-03-02 09:44:33 rcpt for userx@domain.com
+1999-03-02 09:44:33 rcpt for special_tpt@domain.com
+1999-03-02 09:44:33 10HmbD-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss for userx@domain.com special_tpt@domain.com
+1999-03-02 09:44:33 10HmbD-0005vi-00 => userx@domain.com R=all T=smtp H=127.0.0.1 [127.0.0.1] C="250 OK"
+1999-03-02 09:44:33 10HmbD-0005vi-00 => special_tpt@domain.com R=all T=smtp2 H=127.0.0.1 [127.0.0.1] C="250 OK"
+1999-03-02 09:44:33 10HmbD-0005vi-00 Completed
+1999-03-02 09:44:33 rcpt for userx@domain1.com
+1999-03-02 09:44:33 rcpt for usery@domain2.com
+1999-03-02 09:44:33 10HmbE-0005vi-00 >> usery@domain2.com R=all T=smtp H=127.0.0.1 [127.0.0.1] C="250 OK"
+1999-03-02 09:44:33 10HmbE-0005vi-00 >> userx@domain1.com R=all T=smtp H=127.0.0.1 [127.0.0.1] C="250 OK"
+1999-03-02 09:44:33 10HmbE-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss for userx@domain1.com usery@domain2.com
+1999-03-02 09:44:33 10HmbE-0005vi-00 Completed
+1999-03-02 09:44:33 rcpt for userx@domain.com
+1999-03-02 09:44:33 rcpt for usery@special.com
+1999-03-02 09:44:33 10HmbF-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss for userx@domain.com usery@special.com
+1999-03-02 09:44:33 10HmbF-0005vi-00 => userx@domain.com R=all T=smtp H=127.0.0.1 [127.0.0.1] C="250 OK"
+1999-03-02 09:44:33 10HmbF-0005vi-00 => usery@special.com R=all T=smtp H=ip4.ip4.ip4.ip4 [ip4.ip4.ip4.ip4] C="250 OK"
+1999-03-02 09:44:33 10HmbF-0005vi-00 Completed
+1999-03-02 09:44:33 rcpt for userx@localhost.test.ex
+1999-03-02 09:44:33 rcpt for usery@thishost.test.ex
+1999-03-02 09:44:33 10HmbG-0005vi-00 >> usery@thishost.test.ex R=dns T=smtp H=localhost.test.ex [127.0.0.1] C="250 OK"
+1999-03-02 09:44:33 10HmbG-0005vi-00 >> userx@localhost.test.ex R=dns T=smtp H=localhost.test.ex [127.0.0.1] C="250 OK"
+1999-03-02 09:44:33 10HmbG-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss for userx@localhost.test.ex usery@thishost.test.ex
+1999-03-02 09:44:33 10HmbG-0005vi-00 Completed
index 3e56b43b84e8e5413ec2c29be03d19dc4568dada..79072d7491e74a3e7f9628fb182138255389ec3d 100644 (file)
@@ -16,7 +16,7 @@ DATA
 QUIT
 250 OK
 ****
 QUIT
 250 OK
 ****
-exim -d-all+acl+transport -bs
+exim -bs
 EHLO myhost.test.ex
 MAIL FROM:<CALLER@myhost.test.ex>
 RCPT TO:<userx@domain.com>
 EHLO myhost.test.ex
 MAIL FROM:<CALLER@myhost.test.ex>
 RCPT TO:<userx@domain.com>
@@ -43,7 +43,7 @@ DATA
 QUIT
 250 OK
 ****
 QUIT
 250 OK
 ****
-exim -d-all+acl+transport -bs
+exim -bs
 EHLO myhost.test.ex
 MAIL FROM:<CALLER@myhost.test.ex>
 RCPT TO:<userz@domain.com>
 EHLO myhost.test.ex
 MAIL FROM:<CALLER@myhost.test.ex>
 RCPT TO:<userz@domain.com>
@@ -52,8 +52,8 @@ DATA
 .
 QUIT
 ****
 .
 QUIT
 ****
-# cutthrough cancelled by multiple recipients
-server PORT_S 2
+# cutthrough for 2 recipients in one domain
+server PORT_S
 220 ESMTP
 EHLO
 250 OK
 220 ESMTP
 EHLO
 250 OK
@@ -61,8 +61,32 @@ MAIL FROM:
 250 Sender OK
 RCPT TO:
 250 Recipient OK
 250 Sender OK
 RCPT TO:
 250 Recipient OK
+RCPT TO:
+250 Recipient OK
+DATA
+354 Send data
+.
+250 OK
 QUIT
 QUIT
-*eof
+250 OK
+****
+exim -bs
+EHLO myhost.test.ex
+MAIL FROM:<CALLER@myhost.test.ex>
+RCPT TO:<usery@domain.com>
+RCPT TO:<userx@domain.com>
+DATA
+
+.
+QUIT
+****
+#
+#
+#
+#
+#
+# cutthrough_delivery basic operation, again
+server PORT_S
 220 ESMTP
 EHLO
 250 OK
 220 ESMTP
 EHLO
 250 OK
@@ -70,8 +94,53 @@ MAIL FROM:
 250 Sender OK
 RCPT TO:
 250 Recipient OK
 250 Sender OK
 RCPT TO:
 250 Recipient OK
+DATA
+354 Send data
+.
+250 OK
+QUIT
+250 OK
+****
+exim -bs
+EHLO myhost.test.ex
+MAIL FROM:<CALLER@myhost.test.ex>
+RCPT TO:<userx@domain.com>
+DATA
+X-hdr-rtr: qqq
+X-hdr-tpt: zzz
+
+body
+.
+QUIT
+****
+#
+#
+#
+#
+# cutthrough for 2 recipients in one domain
+# first one denied
+# so we get a 2nd conn with the traditional delivery
+# (for both because it's not a real verify=recipient)
+server PORT_S 2
+220 ESMTP
+EHLO
+250 OK
+MAIL FROM:
+250 Sender OK
 RCPT TO:
 RCPT TO:
-250 Recipient OK
+550 Not that one
+QUIT
+250 OK
+*eof
+220 ESMTP
+EHLO
+250 OK
+MAIL FROM:
+250 Sender OK
+RCPT TO:
+250 ok rcpt-1
+RCPT TO:
+250 ok rcpt-2
 DATA
 354 Send data
 .
 DATA
 354 Send data
 .
@@ -79,23 +148,138 @@ DATA
 QUIT
 250 OK
 ****
 QUIT
 250 OK
 ****
-exim -d-all+acl+transport -bs
+exim -bs
 EHLO myhost.test.ex
 MAIL FROM:<CALLER@myhost.test.ex>
 EHLO myhost.test.ex
 MAIL FROM:<CALLER@myhost.test.ex>
-RCPT TO:<usery@domain.com>
+RCPT TO:<no@domain.com>
 RCPT TO:<userx@domain.com>
 DATA
 
 .
 QUIT
 ****
 RCPT TO:<userx@domain.com>
 DATA
 
 .
 QUIT
 ****
-sleep 1
 #
 #
 #
 #
 #
 #
 #
 #
 #
 #
-# cutthrough_delivery basic operation, again
+# cutthrough for 2 recipients in one domain
+# second one denied
+# so we get a 2nd conn with the traditional delivery
+# (for both because it's not a real verify=recipient)
+server PORT_S 2
+220 ESMTP
+EHLO
+250 OK
+MAIL FROM:
+250 Sender OK
+RCPT TO:
+250 first recipient ok
+RCPT TO:
+550 Not that one
+QUIT
+250 OK
+*eof
+220 ESMTP
+EHLO
+250 OK
+MAIL FROM:
+250 Sender OK
+RCPT TO:
+250 ok rcpt-1
+RCPT TO:
+250 ok rcpt-2
+DATA
+354 Send data
+.
+250 OK
+QUIT
+250 OK
+****
+exim -bs
+EHLO myhost.test.ex
+MAIL FROM:<CALLER@myhost.test.ex>
+RCPT TO:<userx@domain.com>
+RCPT TO:<no@domain.com>
+DATA
+
+.
+QUIT
+****
+#
+#
+#
+#
+#
+# cutthrough for 2 recipients in one domain
+# second one uses a different transport
+# so we get a 2nd conn with 2nd rcpt, doing the fake verify
+# then 3rd & 4th conns with the traditional deliveries on the different transports
+server PORT_S 4
+220 ESMTP
+EHLO
+250 OK
+MAIL FROM:
+250 Sender OK
+RCPT TO:
+250 first recipient ok
+QUIT
+250 OK
+*eof
+220 ESMTP
+EHLO
+250 OK
+MAIL FROM:
+250 Sender OK
+RCPT TO:
+250 second recipient ok
+QUIT
+250 OK
+*eof
+220 ESMTP
+EHLO
+250 OK
+MAIL FROM:
+250 Sender OK
+RCPT TO:
+250 ok rcpt-1
+DATA
+354 Send data
+.
+250 OK
+QUIT
+250 OK
+*eof
+220 ESMTP
+EHLO
+250 OK
+MAIL FROM:
+250 Sender OK
+RCPT TO:
+250 ok rcpt-2
+DATA
+354 Send data
+.
+250 OK
+QUIT
+250 OK
+****
+exim -bs
+EHLO myhost.test.ex
+MAIL FROM:<CALLER@myhost.test.ex>
+RCPT TO:<userx@domain.com>
+RCPT TO:<special_tpt@domain.com>
+DATA
+
+.
+QUIT
+****
+#
+#
+#
+#
+#
+# cutthrough for 2 recipients in different domains, handled by the same tpt & host
 server PORT_S
 220 ESMTP
 EHLO
 server PORT_S
 220 ESMTP
 EHLO
@@ -103,7 +287,9 @@ EHLO
 MAIL FROM:
 250 Sender OK
 RCPT TO:
 MAIL FROM:
 250 Sender OK
 RCPT TO:
-250 Recipient OK
+250 ok rcpt-1
+RCPT TO:
+250 ok rcpt-2
 DATA
 354 Send data
 .
 DATA
 354 Send data
 .
@@ -111,15 +297,120 @@ DATA
 QUIT
 250 OK
 ****
 QUIT
 250 OK
 ****
-exim -d-all+acl+transport -bs
+exim -bs
+EHLO myhost.test.ex
+MAIL FROM:<CALLER@myhost.test.ex>
+RCPT TO:<userx@domain1.com>
+RCPT TO:<usery@domain2.com>
+DATA
+
+.
+QUIT
+****
+#
+#
+#
+#
+#
+# cutthrough for 2 recipients in different domains, handled by the same tpt but different hosts
+# so we get a 2nd conn with 2nd rcpt, doing the fake verify
+# then 3rd & 4th conns with the traditional deliveries on the different transports
+server PORT_S 4
+220 ESMTP
+EHLO
+250 OK
+MAIL FROM:
+250 Sender OK
+RCPT TO:
+250 first recipient ok
+QUIT
+250 OK
+*eof
+220 ESMTP
+EHLO
+250 OK
+MAIL FROM:
+250 Sender OK
+RCPT TO:
+250 second recipient ok
+QUIT
+250 OK
+*eof
+220 ESMTP
+EHLO
+250 OK
+MAIL FROM:
+250 Sender OK
+RCPT TO:
+250 ok rcpt-1
+DATA
+354 Send data
+.
+250 OK
+QUIT
+250 OK
+*eof
+220 ESMTP
+EHLO
+250 OK
+MAIL FROM:
+250 Sender OK
+RCPT TO:
+250 ok rcpt-2
+DATA
+354 Send data
+.
+250 OK
+QUIT
+250 OK
+****
+exim -bs
 EHLO myhost.test.ex
 MAIL FROM:<CALLER@myhost.test.ex>
 RCPT TO:<userx@domain.com>
 EHLO myhost.test.ex
 MAIL FROM:<CALLER@myhost.test.ex>
 RCPT TO:<userx@domain.com>
+RCPT TO:<usery@special.com>
+DATA
+
+.
+QUIT
+****
+#
+#
+#
+#
+#
+# cutthrough for 2 recipients in different domains, handled by the same tpt & host
+# but via a dnslookup router (all previous were manualroute)
+server PORT_S
+220 ESMTP
+EHLO
+250 OK
+MAIL FROM:
+250 Sender OK
+RCPT TO:
+250 ok rcpt-1
+RCPT TO:
+250 ok rcpt-2
+DATA
+354 Send data
+.
+250 OK
+QUIT
+250 OK
+****
+exim -bs
+EHLO myhost.test.ex
+MAIL FROM:<CALLER@myhost.test.ex>
+RCPT TO:<userx@localhost.test.ex>
+RCPT TO:<usery@thishost.test.ex>
 DATA
 DATA
-X-hdr-rtr: qqq
-X-hdr-tpt: zzz
 
 
-body
 .
 QUIT
 ****
 .
 QUIT
 ****
+#
+#
+#
+#
+#
+sleep 1
diff --git a/test/stderr/5400 b/test/stderr/5400
deleted file mode 100644 (file)
index 9fa77d5..0000000
+++ /dev/null
@@ -1,235 +0,0 @@
-Exim version x.yz ....
-configuration file is TESTSUITE/test-config
-admin user
-LOG: smtp_connection MAIN
-  SMTP connection from CALLER
-using ACL "ar"
-processing "accept"
-check control = cutthrough_delivery
-check logwrite = rcpt for $local_part@$domain
-               = rcpt for userx@domain.com
-LOG: MAIN
-  rcpt for userx@domain.com
-created log directory TESTSUITE/spool/log
-accept: condition test succeeded in ACL "ar"
-end of ACL "ar": ACCEPT
------------ start cutthrough setup ------------
-Connecting to 127.0.0.1 [127.0.0.1]:1224 from ip4.ip4.ip4.ip4 ... connected
-  SMTP<< 220 ESMTP
-  SMTP>> EHLO myhost.test.ex
-  SMTP<< 250 OK
-  SMTP>> MAIL FROM:<CALLER@myhost.test.ex>
-  SMTP<< 250 Sender OK
-  SMTP>> RCPT TO:<userx@domain.com>
-  SMTP<< 250 Recipient OK
------------ end cutthrough setup ------------
-processing "accept"
-accept: condition test succeeded in inline ACL
-end of inline ACL: ACCEPT
-  SMTP>> DATA
-  SMTP<< 354 Send data
------------ start cutthrough headers send -----------
-added header line(s):
-X-hdr-rtr-new: +++
----
------------ done cutthrough headers send ------------
-  SMTP>> .
-  SMTP<< 250 OK
-LOG: MAIN
-  >> userx@domain.com R=all T=smtp H=127.0.0.1 [127.0.0.1] C="250 OK"
-  SMTP>> QUIT
------------ cutthrough shutdown (delivered) ------------
-LOG: MAIN
-  <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss
-LOG: MAIN
-  Completed
-LOG: smtp_connection MAIN
-  SMTP connection from CALLER closed by QUIT
->>>>>>>>>>>>>>>> Exim pid=pppp terminating with rc=0 >>>>>>>>>>>>>>>>
-Exim version x.yz ....
-configuration file is TESTSUITE/test-config
-admin user
-LOG: smtp_connection MAIN
-  SMTP connection from CALLER
-using ACL "ar"
-processing "accept"
-check control = cutthrough_delivery
-check logwrite = rcpt for $local_part@$domain
-               = rcpt for userz@domain.com
-LOG: MAIN
-  rcpt for userz@domain.com
-accept: condition test succeeded in ACL "ar"
-end of ACL "ar": ACCEPT
------------ start cutthrough setup ------------
-Connecting to 127.0.0.1 [127.0.0.1]:1224 from ip4.ip4.ip4.ip4 ... connected
-  SMTP<< 220 SMTP only spoken here
-  SMTP>> EHLO myhost.test.ex
-  SMTP<< 550 Not here, mate
-  SMTP>> HELO myhost.test.ex
-  SMTP<< 250 OK
-  SMTP>> MAIL FROM:<CALLER@myhost.test.ex>
-  SMTP<< 250 Sender OK
-  SMTP>> RCPT TO:<userz@domain.com>
-  SMTP<< 250 Recipient OK
------------ end cutthrough setup ------------
-processing "accept"
-accept: condition test succeeded in inline ACL
-end of inline ACL: ACCEPT
-  SMTP>> DATA
-  SMTP<< 354 Send data
------------ start cutthrough headers send -----------
-added header line(s):
-X-hdr-rtr-new: +++
----
------------ done cutthrough headers send ------------
-  SMTP>> .
-  SMTP<< 250 OK
-LOG: MAIN
-  >> userz@domain.com R=all T=smtp H=127.0.0.1 [127.0.0.1] C="250 OK"
-  SMTP>> QUIT
------------ cutthrough shutdown (delivered) ------------
-LOG: MAIN
-  <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss
-LOG: MAIN
-  Completed
-LOG: smtp_connection MAIN
-  SMTP connection from CALLER closed by QUIT
->>>>>>>>>>>>>>>> Exim pid=pppp terminating with rc=0 >>>>>>>>>>>>>>>>
-Exim version x.yz ....
-configuration file is TESTSUITE/test-config
-admin user
-LOG: smtp_connection MAIN
-  SMTP connection from CALLER
-using ACL "ar"
-processing "accept"
-check control = cutthrough_delivery
-check logwrite = rcpt for $local_part@$domain
-               = rcpt for usery@domain.com
-LOG: MAIN
-  rcpt for usery@domain.com
-accept: condition test succeeded in ACL "ar"
-end of ACL "ar": ACCEPT
------------ start cutthrough setup ------------
-Connecting to 127.0.0.1 [127.0.0.1]:1224 from ip4.ip4.ip4.ip4 ... connected
-  SMTP<< 220 ESMTP
-  SMTP>> EHLO myhost.test.ex
-  SMTP<< 250 OK
-  SMTP>> MAIL FROM:<CALLER@myhost.test.ex>
-  SMTP<< 250 Sender OK
-  SMTP>> RCPT TO:<usery@domain.com>
-  SMTP<< 250 Recipient OK
------------ end cutthrough setup ------------
-using ACL "ar"
-processing "accept"
-check control = cutthrough_delivery
-check logwrite = rcpt for $local_part@$domain
-               = rcpt for userx@domain.com
-LOG: MAIN
-  rcpt for userx@domain.com
-accept: condition test succeeded in ACL "ar"
-end of ACL "ar": ACCEPT
-  SMTP>> QUIT
------------ cutthrough shutdown (more than one recipient) ------------
-LOG: MAIN
-  <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss
-LOG: smtp_connection MAIN
-  SMTP connection from CALLER closed by QUIT
->>>>>>>>>>>>>>>> Exim pid=pppp terminating with rc=0 >>>>>>>>>>>>>>>>
-Exim version x.yz ....
-configuration file is TESTSUITE/test-config
-trusted user
-admin user
->>>>>>>>>>>>>>>> Remote deliveries >>>>>>>>>>>>>>>>
---------> usery@domain.com <--------
-smtp transport entered
-  usery@domain.com
-  userx@domain.com
-checking status of 127.0.0.1
-127.0.0.1 [127.0.0.1]:1111 status = usable
-delivering 10HmaZ-0005vi-00 to 127.0.0.1 [127.0.0.1] (usery@domain.com, ...)
-Connecting to 127.0.0.1 [127.0.0.1]:1224 from ip4.ip4.ip4.ip4 ... connected
-  SMTP<< 220 ESMTP
-  SMTP>> EHLO myhost.test.ex
-  SMTP<< 250 OK
-not using PIPELINING
-use_dsn=0
-  SMTP>> MAIL FROM:<CALLER@myhost.test.ex>
-  SMTP<< 250 Sender OK
-  SMTP>> RCPT TO:<usery@domain.com>
-  SMTP<< 250 Recipient OK
-  SMTP>> RCPT TO:<userx@domain.com>
-  SMTP<< 250 Recipient OK
-  SMTP>> DATA
-  SMTP<< 354 Send data
-  SMTP>> writing message and terminating "."
-added header line(s):
-X-hdr-rtr-new: +++
----
-writing data block fd=dddd size=sss timeout=300
-  SMTP<< 250 OK
-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
-Leaving smtp transport
-LOG: MAIN
-  => usery@domain.com R=all T=smtp H=127.0.0.1 [127.0.0.1] C="250 OK"
-LOG: MAIN
-  -> userx@domain.com R=all T=smtp H=127.0.0.1 [127.0.0.1] C="250 OK"
-LOG: MAIN
-  Completed
->>>>>>>>>>>>>>>> Exim pid=pppp terminating with rc=0 >>>>>>>>>>>>>>>>
-Exim version x.yz ....
-configuration file is TESTSUITE/test-config
-admin user
-LOG: smtp_connection MAIN
-  SMTP connection from CALLER
-using ACL "ar"
-processing "accept"
-check control = cutthrough_delivery
-check logwrite = rcpt for $local_part@$domain
-               = rcpt for userx@domain.com
-LOG: MAIN
-  rcpt for userx@domain.com
-accept: condition test succeeded in ACL "ar"
-end of ACL "ar": ACCEPT
------------ start cutthrough setup ------------
-Connecting to 127.0.0.1 [127.0.0.1]:1224 from ip4.ip4.ip4.ip4 ... connected
-  SMTP<< 220 ESMTP
-  SMTP>> EHLO myhost.test.ex
-  SMTP<< 250 OK
-  SMTP>> MAIL FROM:<CALLER@myhost.test.ex>
-  SMTP<< 250 Sender OK
-  SMTP>> RCPT TO:<userx@domain.com>
-  SMTP<< 250 Recipient OK
------------ end cutthrough setup ------------
-processing "accept"
-accept: condition test succeeded in inline ACL
-end of inline ACL: ACCEPT
-  SMTP>> DATA
-  SMTP<< 354 Send data
------------ start cutthrough headers send -----------
-removed header line:
-X-hdr-rtr: qqq
----
-added header line(s):
-X-hdr-rtr-new: +++
----
-added header line:
-X-hdr-tpt-new: new
----
------------ done cutthrough headers send ------------
-  SMTP>> .
-  SMTP<< 250 OK
-LOG: MAIN
-  >> userx@domain.com R=all T=smtp H=127.0.0.1 [127.0.0.1] C="250 OK"
-  SMTP>> QUIT
------------ cutthrough shutdown (delivered) ------------
-LOG: MAIN
-  <= CALLER@myhost.test.ex U=CALLER P=local-esmtp S=sss
-LOG: MAIN
-  Completed
-LOG: smtp_connection MAIN
-  SMTP connection from CALLER closed by QUIT
->>>>>>>>>>>>>>>> Exim pid=pppp terminating with rc=0 >>>>>>>>>>>>>>>>
index 4895072a3a0e24ea537bddface2e96ab12fe6523..3452a9cc1f34d2a6386153b20826e95814a79f8e 100644 (file)
 354 Enter message, ending with "." on a line by itself\r
 250 OK id=10HmbA-0005vi-00\r
 221 myhost.test.ex closing connection\r
 354 Enter message, ending with "." on a line by itself\r
 250 OK id=10HmbA-0005vi-00\r
 221 myhost.test.ex closing connection\r
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250-myhost.test.ex Hello CALLER at myhost.test.ex\r
+250-SIZE 52428800\r
+250-8BITMIME\r
+250-PIPELINING\r
+250 HELP\r
+250 OK\r
+250 Accepted\r
+250 Accepted\r
+354 Enter message, ending with "." on a line by itself\r
+250 OK id=10HmbB-0005vi-00\r
+221 myhost.test.ex closing connection\r
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250-myhost.test.ex Hello CALLER at myhost.test.ex\r
+250-SIZE 52428800\r
+250-8BITMIME\r
+250-PIPELINING\r
+250 HELP\r
+250 OK\r
+250 Accepted\r
+250 Accepted\r
+354 Enter message, ending with "." on a line by itself\r
+250 OK id=10HmbC-0005vi-00\r
+221 myhost.test.ex closing connection\r
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250-myhost.test.ex Hello CALLER at myhost.test.ex\r
+250-SIZE 52428800\r
+250-8BITMIME\r
+250-PIPELINING\r
+250 HELP\r
+250 OK\r
+250 Accepted\r
+250 Accepted\r
+354 Enter message, ending with "." on a line by itself\r
+250 OK id=10HmbD-0005vi-00\r
+221 myhost.test.ex closing connection\r
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250-myhost.test.ex Hello CALLER at myhost.test.ex\r
+250-SIZE 52428800\r
+250-8BITMIME\r
+250-PIPELINING\r
+250 HELP\r
+250 OK\r
+250 Accepted\r
+250 Accepted\r
+354 Enter message, ending with "." on a line by itself\r
+250 OK id=10HmbE-0005vi-00\r
+221 myhost.test.ex closing connection\r
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250-myhost.test.ex Hello CALLER at myhost.test.ex\r
+250-SIZE 52428800\r
+250-8BITMIME\r
+250-PIPELINING\r
+250 HELP\r
+250 OK\r
+250 Accepted\r
+250 Accepted\r
+354 Enter message, ending with "." on a line by itself\r
+250 OK id=10HmbF-0005vi-00\r
+221 myhost.test.ex closing connection\r
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250-myhost.test.ex Hello CALLER at myhost.test.ex\r
+250-SIZE 52428800\r
+250-8BITMIME\r
+250-PIPELINING\r
+250 HELP\r
+250 OK\r
+250 Accepted\r
+250 Accepted\r
+354 Enter message, ending with "." on a line by itself\r
+250 OK id=10HmbG-0005vi-00\r
+221 myhost.test.ex closing connection\r
 
 ******** SERVER ********
 Listening on port 1224 ... 
 
 ******** SERVER ********
 Listening on port 1224 ... 
@@ -108,17 +180,6 @@ MAIL FROM:<CALLER@myhost.test.ex>
 250 Sender OK
 RCPT TO:<usery@domain.com>
 250 Recipient OK
 250 Sender OK
 RCPT TO:<usery@domain.com>
 250 Recipient OK
-QUIT
-Expected EOF read from client
-Listening on port 1224 ... 
-Connection request from [ip4.ip4.ip4.ip4]
-220 ESMTP
-EHLO myhost.test.ex
-250 OK
-MAIL FROM:<CALLER@myhost.test.ex>
-250 Sender OK
-RCPT TO:<usery@domain.com>
-250 Recipient OK
 RCPT TO:<userx@domain.com>
 250 Recipient OK
 DATA
 RCPT TO:<userx@domain.com>
 250 Recipient OK
 DATA
@@ -166,3 +227,284 @@ body
 QUIT
 250 OK
 End of script
 QUIT
 250 OK
 End of script
+Listening on port 1224 ... 
+Connection request from [ip4.ip4.ip4.ip4]
+220 ESMTP
+EHLO myhost.test.ex
+250 OK
+MAIL FROM:<CALLER@myhost.test.ex>
+250 Sender OK
+RCPT TO:<no@domain.com>
+550 Not that one
+QUIT
+250 OK
+Expected EOF read from client
+Listening on port 1224 ... 
+Connection request from [ip4.ip4.ip4.ip4]
+220 ESMTP
+EHLO myhost.test.ex
+250 OK
+MAIL FROM:<CALLER@myhost.test.ex>
+250 Sender OK
+RCPT TO:<no@domain.com>
+250 ok rcpt-1
+RCPT TO:<userx@domain.com>
+250 ok rcpt-2
+DATA
+354 Send data
+Received: from CALLER (helo=myhost.test.ex)
+       by myhost.test.ex with local-esmtp (Exim x.yz)
+       (envelope-from <CALLER@myhost.test.ex>)
+       id 10HmbB-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmbB-0005vi-00@myhost.test.ex>
+From: CALLER_NAME <CALLER@myhost.test.ex>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+X-hdr-rtr-new: +++
+
+.
+250 OK
+QUIT
+250 OK
+End of script
+Listening on port 1224 ... 
+Connection request from [ip4.ip4.ip4.ip4]
+220 ESMTP
+EHLO myhost.test.ex
+250 OK
+MAIL FROM:<CALLER@myhost.test.ex>
+250 Sender OK
+RCPT TO:<userx@domain.com>
+250 first recipient ok
+RCPT TO:<no@domain.com>
+550 Not that one
+QUIT
+250 OK
+Expected EOF read from client
+Listening on port 1224 ... 
+Connection request from [ip4.ip4.ip4.ip4]
+220 ESMTP
+EHLO myhost.test.ex
+250 OK
+MAIL FROM:<CALLER@myhost.test.ex>
+250 Sender OK
+RCPT TO:<userx@domain.com>
+250 ok rcpt-1
+RCPT TO:<no@domain.com>
+250 ok rcpt-2
+DATA
+354 Send data
+Received: from CALLER (helo=myhost.test.ex)
+       by myhost.test.ex with local-esmtp (Exim x.yz)
+       (envelope-from <CALLER@myhost.test.ex>)
+       id 10HmbC-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmbC-0005vi-00@myhost.test.ex>
+From: CALLER_NAME <CALLER@myhost.test.ex>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+X-hdr-rtr-new: +++
+
+.
+250 OK
+QUIT
+250 OK
+End of script
+Listening on port 1224 ... 
+Connection request from [ip4.ip4.ip4.ip4]
+220 ESMTP
+EHLO myhost.test.ex
+250 OK
+MAIL FROM:<CALLER@myhost.test.ex>
+250 Sender OK
+RCPT TO:<userx@domain.com>
+250 first recipient ok
+QUIT
+250 OK
+Expected EOF read from client
+Listening on port 1224 ... 
+Connection request from [ip4.ip4.ip4.ip4]
+220 ESMTP
+EHLO myhost.test.ex
+250 OK
+MAIL FROM:<CALLER@myhost.test.ex>
+250 Sender OK
+RCPT TO:<special_tpt@domain.com>
+250 second recipient ok
+QUIT
+250 OK
+Expected EOF read from client
+Listening on port 1224 ... 
+Connection request from [ip4.ip4.ip4.ip4]
+220 ESMTP
+EHLO myhost.test.ex
+250 OK
+MAIL FROM:<CALLER@myhost.test.ex>
+250 Sender OK
+RCPT TO:<userx@domain.com>
+250 ok rcpt-1
+DATA
+354 Send data
+Received: from CALLER (helo=myhost.test.ex)
+       by myhost.test.ex with local-esmtp (Exim x.yz)
+       (envelope-from <CALLER@myhost.test.ex>)
+       id 10HmbD-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmbD-0005vi-00@myhost.test.ex>
+From: CALLER_NAME <CALLER@myhost.test.ex>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+X-hdr-rtr-new: +++
+
+.
+250 OK
+QUIT
+250 OK
+Expected EOF read from client
+Listening on port 1224 ... 
+Connection request from [ip4.ip4.ip4.ip4]
+220 ESMTP
+EHLO myhost.test.ex
+250 OK
+MAIL FROM:<CALLER@myhost.test.ex>
+250 Sender OK
+RCPT TO:<special_tpt@domain.com>
+250 ok rcpt-2
+DATA
+354 Send data
+Received: from CALLER (helo=myhost.test.ex)
+       by myhost.test.ex with local-esmtp (Exim x.yz)
+       (envelope-from <CALLER@myhost.test.ex>)
+       id 10HmbD-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmbD-0005vi-00@myhost.test.ex>
+From: CALLER_NAME <CALLER@myhost.test.ex>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+X-hdr-rtr-new: +++
+
+.
+250 OK
+QUIT
+250 OK
+End of script
+Listening on port 1224 ... 
+Connection request from [ip4.ip4.ip4.ip4]
+220 ESMTP
+EHLO myhost.test.ex
+250 OK
+MAIL FROM:<CALLER@myhost.test.ex>
+250 Sender OK
+RCPT TO:<userx@domain1.com>
+250 ok rcpt-1
+RCPT TO:<usery@domain2.com>
+250 ok rcpt-2
+DATA
+354 Send data
+Received: from CALLER (helo=myhost.test.ex)
+       by myhost.test.ex with local-esmtp (Exim x.yz)
+       (envelope-from <CALLER@myhost.test.ex>)
+       id 10HmbE-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmbE-0005vi-00@myhost.test.ex>
+From: CALLER_NAME <CALLER@myhost.test.ex>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+X-hdr-rtr-new: +++
+
+.
+250 OK
+QUIT
+250 OK
+End of script
+Listening on port 1224 ... 
+Connection request from [ip4.ip4.ip4.ip4]
+220 ESMTP
+EHLO myhost.test.ex
+250 OK
+MAIL FROM:<CALLER@myhost.test.ex>
+250 Sender OK
+RCPT TO:<userx@domain.com>
+250 first recipient ok
+QUIT
+250 OK
+Expected EOF read from client
+Listening on port 1224 ... 
+Connection request from [ip4.ip4.ip4.ip4]
+220 ESMTP
+EHLO myhost.test.ex
+250 OK
+MAIL FROM:<CALLER@myhost.test.ex>
+250 Sender OK
+RCPT TO:<usery@special.com>
+250 second recipient ok
+QUIT
+250 OK
+Expected EOF read from client
+Listening on port 1224 ... 
+Connection request from [ip4.ip4.ip4.ip4]
+220 ESMTP
+EHLO myhost.test.ex
+250 OK
+MAIL FROM:<CALLER@myhost.test.ex>
+250 Sender OK
+RCPT TO:<userx@domain.com>
+250 ok rcpt-1
+DATA
+354 Send data
+Received: from CALLER (helo=myhost.test.ex)
+       by myhost.test.ex with local-esmtp (Exim x.yz)
+       (envelope-from <CALLER@myhost.test.ex>)
+       id 10HmbF-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmbF-0005vi-00@myhost.test.ex>
+From: CALLER_NAME <CALLER@myhost.test.ex>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+X-hdr-rtr-new: +++
+
+.
+250 OK
+QUIT
+250 OK
+Expected EOF read from client
+Listening on port 1224 ... 
+Connection request from [ip4.ip4.ip4.ip4]
+220 ESMTP
+EHLO myhost.test.ex
+250 OK
+MAIL FROM:<CALLER@myhost.test.ex>
+250 Sender OK
+RCPT TO:<usery@special.com>
+250 ok rcpt-2
+DATA
+354 Send data
+Received: from CALLER (helo=myhost.test.ex)
+       by myhost.test.ex with local-esmtp (Exim x.yz)
+       (envelope-from <CALLER@myhost.test.ex>)
+       id 10HmbF-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmbF-0005vi-00@myhost.test.ex>
+From: CALLER_NAME <CALLER@myhost.test.ex>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+X-hdr-rtr-new: +++
+
+.
+250 OK
+QUIT
+250 OK
+End of script
+Listening on port 1224 ... 
+Connection request from [ip4.ip4.ip4.ip4]
+220 ESMTP
+EHLO myhost.test.ex
+250 OK
+MAIL FROM:<CALLER@myhost.test.ex>
+250 Sender OK
+RCPT TO:<userx@localhost.test.ex>
+250 ok rcpt-1
+RCPT TO:<usery@thishost.test.ex>
+250 ok rcpt-2
+DATA
+354 Send data
+Received: from CALLER (helo=myhost.test.ex)
+       by myhost.test.ex with local-esmtp (Exim x.yz)
+       (envelope-from <CALLER@myhost.test.ex>)
+       id 10HmbG-0005vi-00; Tue, 2 Mar 1999 09:44:33 +0000
+Message-Id: <E10HmbG-0005vi-00@myhost.test.ex>
+From: CALLER_NAME <CALLER@myhost.test.ex>
+Date: Tue, 2 Mar 1999 09:44:33 +0000
+
+.
+250 OK
+QUIT
+250 OK
+End of script