Callouts: a "hold" option for receipient-verify, which keeps the connection open...
authorJeremy Harris <jgh146exb@wizmail.org>
Tue, 21 Mar 2017 16:16:38 +0000 (16:16 +0000)
committerJeremy Harris <jgh146exb@wizmail.org>
Tue, 21 Mar 2017 17:23:30 +0000 (17:23 +0000)
further recipients and for eventual delivery.

61 files changed:
doc/doc-docbook/spec.xfpt
doc/doc-txt/NewStuff
src/src/acl.c
src/src/daemon.c
src/src/deliver.c
src/src/exim.c
src/src/functions.h
src/src/globals.c
src/src/globals.h
src/src/macros.h
src/src/receive.c
src/src/smtp_in.c
src/src/tls-openssl.c
src/src/transport.c
src/src/transports/smtp.c
src/src/verify.c
test/confs/0580 [new file with mode: 0644]
test/confs/0581 [new symlink]
test/confs/0582 [new symlink]
test/confs/2035 [new file with mode: 0644]
test/confs/2036 [new symlink]
test/confs/2037 [new symlink]
test/confs/2135 [new file with mode: 0644]
test/confs/2136 [new symlink]
test/confs/2137 [new symlink]
test/log/0580 [new file with mode: 0644]
test/log/0581 [new file with mode: 0644]
test/log/0582 [new file with mode: 0644]
test/log/2035 [new file with mode: 0644]
test/log/2036 [new file with mode: 0644]
test/log/2037 [new file with mode: 0644]
test/log/2135 [new file with mode: 0644]
test/log/2136 [new file with mode: 0644]
test/log/2137 [new file with mode: 0644]
test/rejectlog/0582 [new file with mode: 0644]
test/rejectlog/2037 [new file with mode: 0644]
test/rejectlog/2137 [new file with mode: 0644]
test/scripts/0000-Basic/0580 [new file with mode: 0644]
test/scripts/0000-Basic/0581 [new file with mode: 0644]
test/scripts/0000-Basic/0582 [new file with mode: 0644]
test/scripts/2000-GnuTLS/2035 [new file with mode: 0644]
test/scripts/2000-GnuTLS/2036 [new file with mode: 0644]
test/scripts/2000-GnuTLS/2037 [new file with mode: 0644]
test/scripts/2100-OpenSSL/2135 [new file with mode: 0644]
test/scripts/2100-OpenSSL/2136 [new file with mode: 0644]
test/scripts/2100-OpenSSL/2137 [new file with mode: 0644]
test/stderr/0021
test/stderr/0275
test/stderr/0278
test/stderr/0361
test/stderr/0386
test/stderr/0388
test/stderr/0402
test/stderr/0403
test/stderr/0404
test/stderr/0408
test/stderr/0487
test/stderr/2600
test/stderr/5004
test/stderr/5005
test/stderr/5006

index c62c1ee..925b963 100644 (file)
@@ -30908,6 +30908,21 @@ command when performing the callout, instead of an empty address. There is no
 need to use this option unless you know that the called hosts make use of the
 sender when checking recipients. If used indiscriminately, it reduces the
 usefulness of callout caching.
+
+.vitem &*hold*&
+This option applies to recipient callouts only. For example:
+.code
+require  verify = recipient/callout=use_sender,hold
+.endd
+It causes the connection to be helod open and used for any further recipients
+and for eventual delivery (should that be done quickly).
+Doing this saves on TCP and SMTP startup costs, and TLS costs also
+when that is used for the connections.
+The advantage is only gained if there are no callout cache hits
+(which could be enforced by the no_cache option),
+if the use_sender option is used,
+if neither the random nor the use_postmaster option is used,
+and if no other callouts intervene.
 .endlist
 
 If you use any of the parameters that set a non-empty sender for the MAIL
index 872371f..c0d07b4 100644 (file)
@@ -23,6 +23,9 @@ Version 4.90
 
  4. A malware connection type for the FPSCAND protocol.
 
+ 5. An option for recipient verify callouts to hold the connection open for
+    further recipients and for delivery.
+
 
 Version 4.89
 ------------
index efab1d3..7aa8c78 100644 (file)
@@ -1500,7 +1500,7 @@ static verify_type_t verify_type_list[] = {
 enum { CALLOUT_DEFER_OK, CALLOUT_NOCACHE, CALLOUT_RANDOM, CALLOUT_USE_SENDER,
   CALLOUT_USE_POSTMASTER, CALLOUT_POSTMASTER, CALLOUT_FULLPOSTMASTER,
   CALLOUT_MAILFROM, CALLOUT_POSTMASTER_MAILFROM, CALLOUT_MAXWAIT, CALLOUT_CONNECT,
-  CALLOUT_TIME
+  CALLOUT_HOLD, CALLOUT_TIME   /* TIME must be last */
   };
 typedef struct {
   uschar * name;
@@ -1521,6 +1521,7 @@ static callout_opt_t callout_opt_list[] = {
     { US"mailfrom",      CALLOUT_MAILFROM,      0,                             TRUE,  FALSE },
     { US"maxwait",       CALLOUT_MAXWAIT,       0,                             TRUE,  TRUE },
     { US"connect",       CALLOUT_CONNECT,       0,                             TRUE,  TRUE },
+    { US"hold",                  CALLOUT_HOLD,          vopt_callout_hold,             FALSE, FALSE },
     { NULL,              CALLOUT_TIME,          0,                             FALSE, TRUE }
   };
 
@@ -1744,8 +1745,7 @@ while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))
         uschar buffer[256];
         while (isspace(*sublist)) sublist++;
 
-        while ((opt = string_nextinlist(&sublist, &optsep, buffer, sizeof(buffer)))
-              != NULL)
+        while ((opt = string_nextinlist(&sublist, &optsep, buffer, sizeof(buffer))))
           {
          callout_opt_t * op;
          double period = 1.0F;
@@ -1767,15 +1767,11 @@ while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))
               }
             while (isspace(*opt)) opt++;
            }
-         if (op->timeval)
+         if (op->timeval && (period = readconf_readtime(opt, 0, FALSE)) < 0)
            {
-            period = readconf_readtime(opt, 0, FALSE);
-            if (period < 0)
-              {
-              *log_msgptr = string_sprintf("bad time value in ACL condition "
-                "\"verify %s\"", arg);
-              return ERROR;
-              }
+           *log_msgptr = string_sprintf("bad time value in ACL condition "
+             "\"verify %s\"", arg);
+           return ERROR;
            }
 
          switch(op->value)
@@ -3069,7 +3065,7 @@ for (; cb != NULL; cb = cb->next)
        break;
 
        case CONTROL_FAKEREJECT:
-       cancel_cutthrough_connection("fakereject");
+       cancel_cutthrough_connection(TRUE, US"fakereject");
        case CONTROL_FAKEDEFER:
        fake_response = (control_type == CONTROL_FAKEDEFER) ? DEFER : FAIL;
        if (*p == '/')
@@ -3100,12 +3096,12 @@ for (; cb != NULL; cb = cb->next)
          *log_msgptr = string_sprintf("syntax error in \"control=%s\"", arg);
          return ERROR;
          }
-       cancel_cutthrough_connection("item frozen");
+       cancel_cutthrough_connection(TRUE, US"item frozen");
        break;
 
        case CONTROL_QUEUE_ONLY:
        queue_only_policy = TRUE;
-       cancel_cutthrough_connection("queueing forced");
+       cancel_cutthrough_connection(TRUE, US"queueing forced");
        break;
 
        case CONTROL_SUBMISSION:
@@ -4334,8 +4330,9 @@ switch (where)
 #ifndef DISABLE_PRDR
   case ACL_WHERE_PRDR:
 #endif
+
     if (host_checking_callout) /* -bhc mode */
-      cancel_cutthrough_connection("host-checking mode");
+      cancel_cutthrough_connection(TRUE, US"host-checking mode");
 
     else if (  rc == OK
            && cutthrough.delivery
@@ -4362,13 +4359,20 @@ switch (where)
     if (rc == OK)
       cutthrough_predata();
     else
-      cancel_cutthrough_connection("predata acl not ok");
+      cancel_cutthrough_connection(TRUE, US"predata acl not ok");
     break;
 
   case ACL_WHERE_QUIT:
   case ACL_WHERE_NOTQUIT:
-    cancel_cutthrough_connection("quit or notquit");
+    /* Drop cutthrough conns, and drop heldopen verify conns if
+    the previous was not DATA */
+    {
+    uschar prev = smtp_connection_had[smtp_ch_index-2];
+    BOOL dropverify = !(prev == SCH_DATA || prev == SCH_BDAT);
+
+    cancel_cutthrough_connection(dropverify, US"quit or conndrop");
     break;
+    }
 
   default:
     break;
index ebd06b5..1873786 100644 (file)
@@ -510,6 +510,7 @@ if (pid == 0)
       search_tidyup();                    /* Close cached databases */
       if (!ok)                            /* Connection was dropped */
         {
+       cancel_cutthrough_connection(TRUE, US"receive dropped");
         mac_smtp_fflush();
         smtp_log_no_mail();               /* Log no mail if configured */
         _exit(EXIT_SUCCESS);
@@ -528,6 +529,7 @@ if (pid == 0)
        if (fcntl(fd, F_SETFL, O_NONBLOCK) == 0)
          for(i = 16; read(fd, buf, sizeof(buf)) > 0 && i > 0; ) i--;
        }
+      cancel_cutthrough_connection(TRUE, US"message setup dropped");
       search_tidyup();
       smtp_log_no_mail();                 /* Log no mail if configured */
 
@@ -653,9 +655,9 @@ if (pid == 0)
         /* Don't ever molest the parent's SSL connection, but do clean up
         the data structures if necessary. */
 
-        #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
         tls_close(TRUE, FALSE);
-        #endif
+#endif
 
         /* Reset SIGHUP and SIGCHLD in the child in both cases. */
 
@@ -665,25 +667,28 @@ if (pid == 0)
         if (geteuid() != root_uid && !deliver_drop_privilege)
           {
           signal(SIGALRM, SIG_DFL);
-          (void)child_exec_exim(CEE_EXEC_PANIC, FALSE, NULL, FALSE,
-           2, US"-Mc", message_id);
+         delivery_re_exec(CEE_EXEC_PANIC);
           /* Control does not return here. */
           }
 
         /* No need to re-exec; SIGALRM remains set to the default handler */
 
-        (void)deliver_message(message_id, FALSE, FALSE);
+        (void) deliver_message(message_id, FALSE, FALSE);
         search_tidyup();
         _exit(EXIT_SUCCESS);
         }
 
       if (dpid > 0)
         {
+       release_cutthrough_connection(US"passed for delivery");
         DEBUG(D_any) debug_printf("forked delivery process %d\n", (int)dpid);
         }
       else
+       {
+       cancel_cutthrough_connection(TRUE, US"delivery fork failed");
         log_write(0, LOG_MAIN|LOG_PANIC, "daemon: delivery process fork "
           "failed: %s", strerror(errno));
+       }
       }
     }
   }
index ccc3266..351a02b 100644 (file)
@@ -3366,15 +3366,15 @@ while (!done)
     up by checking the IP address. */
 
     case 'H':
-    for (h = addrlist->host_list; h; h = h->next)
-      {
-      if (!h->address || Ustrcmp(h->address, ptr+2) != 0) continue;
-      h->status = ptr[0];
-      h->why = ptr[1];
-      }
-    ptr += 2;
-    while (*ptr++);
-    break;
+      for (h = addrlist->host_list; h; h = h->next)
+       {
+       if (!h->address || Ustrcmp(h->address, ptr+2) != 0) continue;
+       h->status = ptr[0];
+       h->why = ptr[1];
+       }
+      ptr += 2;
+      while (*ptr++);
+      break;
 
     /* Retry items are sent in a preceding R item for each address. This is
     kept separate to keep each message short enough to guarantee it won't
@@ -3388,62 +3388,61 @@ while (!done)
     that a "delete" item is dropped in favour of an "add" item. */
 
     case 'R':
-    if (!addr) goto ADDR_MISMATCH;
+      if (!addr) goto ADDR_MISMATCH;
 
-    DEBUG(D_deliver|D_retry)
-      debug_printf("reading retry information for %s from subprocess\n",
-        ptr+1);
+      DEBUG(D_deliver|D_retry)
+       debug_printf("reading retry information for %s from subprocess\n",
+         ptr+1);
 
-    /* Cut out any "delete" items on the list. */
+      /* Cut out any "delete" items on the list. */
 
-    for (rp = &addr->retries; (r = *rp); rp = &r->next)
-      if (Ustrcmp(r->key, ptr+1) == 0)           /* Found item with same key */
-        {
-        if ((r->flags & rf_delete) == 0) break;  /* It was not "delete" */
-        *rp = r->next;                           /* Excise a delete item */
-        DEBUG(D_deliver|D_retry)
-          debug_printf("  existing delete item dropped\n");
-        }
+      for (rp = &addr->retries; (r = *rp); rp = &r->next)
+       if (Ustrcmp(r->key, ptr+1) == 0)           /* Found item with same key */
+         {
+         if (!(r->flags & rf_delete)) break;      /* It was not "delete" */
+         *rp = r->next;                           /* Excise a delete item */
+         DEBUG(D_deliver|D_retry)
+           debug_printf("  existing delete item dropped\n");
+         }
 
-    /* We want to add a delete item only if there is no non-delete item;
-    however we still have to step ptr through the data. */
+      /* We want to add a delete item only if there is no non-delete item;
+      however we still have to step ptr through the data. */
 
-    if (!r || !(*ptr & rf_delete))
-      {
-      r = store_get(sizeof(retry_item));
-      r->next = addr->retries;
-      addr->retries = r;
-      r->flags = *ptr++;
-      r->key = string_copy(ptr);
-      while (*ptr++);
-      memcpy(&(r->basic_errno), ptr, sizeof(r->basic_errno));
-      ptr += sizeof(r->basic_errno);
-      memcpy(&(r->more_errno), ptr, sizeof(r->more_errno));
-      ptr += sizeof(r->more_errno);
-      r->message = (*ptr)? string_copy(ptr) : NULL;
-      DEBUG(D_deliver|D_retry)
-        debug_printf("  added %s item\n",
-          ((r->flags & rf_delete) == 0)? "retry" : "delete");
-      }
+      if (!r || !(*ptr & rf_delete))
+       {
+       r = store_get(sizeof(retry_item));
+       r->next = addr->retries;
+       addr->retries = r;
+       r->flags = *ptr++;
+       r->key = string_copy(ptr);
+       while (*ptr++);
+       memcpy(&(r->basic_errno), ptr, sizeof(r->basic_errno));
+       ptr += sizeof(r->basic_errno);
+       memcpy(&(r->more_errno), ptr, sizeof(r->more_errno));
+       ptr += sizeof(r->more_errno);
+       r->message = *ptr ? string_copy(ptr) : NULL;
+       DEBUG(D_deliver|D_retry) debug_printf("  added %s item\n",
+           r->flags & rf_delete ? "delete" : "retry");
+       }
 
-    else
-      {
-      DEBUG(D_deliver|D_retry)
-        debug_printf("  delete item not added: non-delete item exists\n");
-      ptr++;
-      while(*ptr++);
-      ptr += sizeof(r->basic_errno) + sizeof(r->more_errno);
-      }
+      else
+       {
+       DEBUG(D_deliver|D_retry)
+         debug_printf("  delete item not added: non-delete item exists\n");
+       ptr++;
+       while(*ptr++);
+       ptr += sizeof(r->basic_errno) + sizeof(r->more_errno);
+       }
 
-    while(*ptr++);
-    break;
+      while(*ptr++);
+      break;
 
     /* Put the amount of data written into the parlist block */
 
     case 'S':
-    memcpy(&(p->transport_count), ptr, sizeof(transport_count));
-    ptr += sizeof(transport_count);
-    break;
+      memcpy(&(p->transport_count), ptr, sizeof(transport_count));
+      ptr += sizeof(transport_count);
+      break;
 
     /* Address items are in the order of items on the address chain. We
     remember the current address value in case this function is called
@@ -3454,162 +3453,157 @@ while (!done)
 
 #ifdef SUPPORT_TLS
     case 'X':
-    if (!addr) goto ADDR_MISMATCH;          /* Below, in 'A' handler */
-    switch (subid)
-      {
-      case '1':
-       addr->cipher = NULL;
-       addr->peerdn = NULL;
-
-       if (*ptr)
-         addr->cipher = string_copy(ptr);
-       while (*ptr++);
-       if (*ptr)
-         addr->peerdn = string_copy(ptr);
-       break;
-
-      case '2':
-       if (*ptr)
-         (void) tls_import_cert(ptr, &addr->peercert);
-       else
-         addr->peercert = NULL;
-       break;
+      if (!addr) goto ADDR_MISMATCH;          /* Below, in 'A' handler */
+      switch (subid)
+       {
+       case '1':
+         addr->cipher = NULL;
+         addr->peerdn = NULL;
 
-      case '3':
-       if (*ptr)
-         (void) tls_import_cert(ptr, &addr->ourcert);
-       else
-         addr->ourcert = NULL;
-       break;
+         if (*ptr)
+           addr->cipher = string_copy(ptr);
+         while (*ptr++);
+         if (*ptr)
+           addr->peerdn = string_copy(ptr);
+         break;
+
+       case '2':
+         if (*ptr)
+           (void) tls_import_cert(ptr, &addr->peercert);
+         else
+           addr->peercert = NULL;
+         break;
+
+       case '3':
+         if (*ptr)
+           (void) tls_import_cert(ptr, &addr->ourcert);
+         else
+           addr->ourcert = NULL;
+         break;
 
 # ifndef DISABLE_OCSP
-      case '4':
-       addr->ocsp = *ptr ? *ptr - '0' : OCSP_NOT_REQ;
-       break;
+       case '4':
+         addr->ocsp = *ptr ? *ptr - '0' : OCSP_NOT_REQ;
+         break;
 # endif
-      }
-    while (*ptr++);
-    break;
+       }
+      while (*ptr++);
+      break;
 #endif /*SUPPORT_TLS*/
 
     case 'C':  /* client authenticator information */
-    switch (subid)
-      {
-      case '1':
-       addr->authenticator = (*ptr)? string_copy(ptr) : NULL;
-       break;
-      case '2':
-       addr->auth_id = (*ptr)? string_copy(ptr) : NULL;
-       break;
-      case '3':
-       addr->auth_sndr = (*ptr)? string_copy(ptr) : NULL;
-       break;
-      }
-    while (*ptr++);
-    break;
+      switch (subid)
+       {
+       case '1': addr->authenticator = *ptr ? string_copy(ptr) : NULL; break;
+       case '2': addr->auth_id = *ptr ? string_copy(ptr) : NULL;       break;
+       case '3': addr->auth_sndr = *ptr ? string_copy(ptr) : NULL;     break;
+       }
+      while (*ptr++);
+      break;
 
 #ifndef DISABLE_PRDR
     case 'P':
-    addr->flags |= af_prdr_used;
-    break;
+      addr->flags |= af_prdr_used;
+      break;
 #endif
 
     case 'K':
-    addr->flags |= af_chunking_used;
-    break;
+      addr->flags |= af_chunking_used;
+      break;
 
     case 'D':
-    if (!addr) goto ADDR_MISMATCH;
-    memcpy(&(addr->dsn_aware), ptr, sizeof(addr->dsn_aware));
-    ptr += sizeof(addr->dsn_aware);
-    DEBUG(D_deliver) debug_printf("DSN read: addr->dsn_aware = %d\n", addr->dsn_aware);
-    break;
-
-    case 'A':
-    if (!addr)
-      {
-      ADDR_MISMATCH:
-      msg = string_sprintf("address count mismatch for data read from pipe "
-        "for transport process %d for transport %s", pid,
-          addrlist->transport->driver_name);
-      done = TRUE;
+      if (!addr) goto ADDR_MISMATCH;
+      memcpy(&(addr->dsn_aware), ptr, sizeof(addr->dsn_aware));
+      ptr += sizeof(addr->dsn_aware);
+      DEBUG(D_deliver) debug_printf("DSN read: addr->dsn_aware = %d\n", addr->dsn_aware);
       break;
-      }
 
-    switch (subid)
-      {
-#ifdef SUPPORT_SOCKS
-      case '2':        /* proxy information; must arrive before A0 and applies to that addr XXX oops*/
-       proxy_session = TRUE;   /*XXX should this be cleared somewhere? */
-       if (*ptr == 0)
-         ptr++;
-       else
-         {
-         proxy_local_address = string_copy(ptr);
-         while(*ptr++);
-         memcpy(&proxy_local_port, ptr, sizeof(proxy_local_port));
-         ptr += sizeof(proxy_local_port);
-         }
+    case 'A':
+      if (!addr)
+       {
+       ADDR_MISMATCH:
+       msg = string_sprintf("address count mismatch for data read from pipe "
+         "for transport process %d for transport %s", pid,
+           addrlist->transport->driver_name);
+       done = TRUE;
        break;
-#endif
+       }
 
-#ifdef EXPERIMENTAL_DSN_INFO
-      case '1':        /* must arrive before A0, and applies to that addr */
-               /* Two strings: smtp_greeting and helo_response */
-       addr->smtp_greeting = string_copy(ptr);
-       while(*ptr++);
-       addr->helo_response = string_copy(ptr);
-       while(*ptr++);
-       break;
-#endif
+      switch (subid)
+       {
+  #ifdef SUPPORT_SOCKS
+       case '2':       /* proxy information; must arrive before A0 and applies to that addr XXX oops*/
+         proxy_session = TRUE; /*XXX should this be cleared somewhere? */
+         if (*ptr == 0)
+           ptr++;
+         else
+           {
+           proxy_local_address = string_copy(ptr);
+           while(*ptr++);
+           memcpy(&proxy_local_port, ptr, sizeof(proxy_local_port));
+           ptr += sizeof(proxy_local_port);
+           }
+         break;
+  #endif
 
-      case '0':
-       addr->transport_return = *ptr++;
-       addr->special_action = *ptr++;
-       memcpy(&(addr->basic_errno), ptr, sizeof(addr->basic_errno));
-       ptr += sizeof(addr->basic_errno);
-       memcpy(&(addr->more_errno), ptr, sizeof(addr->more_errno));
-       ptr += sizeof(addr->more_errno);
-       memcpy(&(addr->flags), ptr, sizeof(addr->flags));
-       ptr += sizeof(addr->flags);
-       addr->message = (*ptr)? string_copy(ptr) : NULL;
-       while(*ptr++);
-       addr->user_message = (*ptr)? string_copy(ptr) : NULL;
-       while(*ptr++);
+  #ifdef EXPERIMENTAL_DSN_INFO
+       case '1':       /* must arrive before A0, and applies to that addr */
+                       /* Two strings: smtp_greeting and helo_response */
+         addr->smtp_greeting = string_copy(ptr);
+         while(*ptr++);
+         addr->helo_response = string_copy(ptr);
+         while(*ptr++);
+         break;
+  #endif
+
+       case '0':
+         DEBUG(D_deliver) debug_printf("A0 %s tret %d\n", addr->address, *ptr);
+         addr->transport_return = *ptr++;
+         addr->special_action = *ptr++;
+         memcpy(&(addr->basic_errno), ptr, sizeof(addr->basic_errno));
+         ptr += sizeof(addr->basic_errno);
+         memcpy(&(addr->more_errno), ptr, sizeof(addr->more_errno));
+         ptr += sizeof(addr->more_errno);
+         memcpy(&(addr->flags), ptr, sizeof(addr->flags));
+         ptr += sizeof(addr->flags);
+         addr->message = *ptr ? string_copy(ptr) : NULL;
+         while(*ptr++);
+         addr->user_message = *ptr ? string_copy(ptr) : NULL;
+         while(*ptr++);
 
-       /* Always two strings for host information, followed by the port number and DNSSEC mark */
+         /* Always two strings for host information, followed by the port number and DNSSEC mark */
 
-       if (*ptr != 0)
-         {
-         h = store_get(sizeof(host_item));
-         h->name = string_copy(ptr);
-         while (*ptr++);
-         h->address = string_copy(ptr);
-         while(*ptr++);
-         memcpy(&(h->port), ptr, sizeof(h->port));
-         ptr += sizeof(h->port);
-         h->dnssec = *ptr == '2' ? DS_YES
-                   : *ptr == '1' ? DS_NO
-                   : DS_UNK;
-         ptr++;
-         addr->host_used = h;
-         }
-       else ptr++;
+         if (*ptr)
+           {
+           h = store_get(sizeof(host_item));
+           h->name = string_copy(ptr);
+           while (*ptr++);
+           h->address = string_copy(ptr);
+           while(*ptr++);
+           memcpy(&h->port, ptr, sizeof(h->port));
+           ptr += sizeof(h->port);
+           h->dnssec = *ptr == '2' ? DS_YES
+                     : *ptr == '1' ? DS_NO
+                     : DS_UNK;
+           ptr++;
+           addr->host_used = h;
+           }
+         else ptr++;
 
-       /* Finished with this address */
+         /* Finished with this address */
 
-       addr = addr->next;
-       break;
-      }
-    break;
+         addr = addr->next;
+         break;
+       }
+      break;
 
     /* Local interface address/port */
     case 'I':
-    if (*ptr) sending_ip_address = string_copy(ptr);
-    while (*ptr++) ;
-    if (*ptr) sending_port = atoi(CS ptr);
-    while (*ptr++) ;
-    break;
+      if (*ptr) sending_ip_address = string_copy(ptr);
+      while (*ptr++) ;
+      if (*ptr) sending_port = atoi(CS ptr);
+      while (*ptr++) ;
+      break;
 
     /* Z marks the logical end of the data. It is followed by '0' if
     continue_transport was NULL at the end of transporting, otherwise '1'.
@@ -3618,23 +3612,23 @@ while (!done)
     most normal messages it will remain NULL all the time. */
 
     case 'Z':
-    if (*ptr == '0')
-      {
-      continue_transport = NULL;
-      continue_hostname = NULL;
-      }
-    done = TRUE;
-    DEBUG(D_deliver) debug_printf("Z0%c item read\n", *ptr);
-    break;
+      if (*ptr == '0')
+       {
+       continue_transport = NULL;
+       continue_hostname = NULL;
+       }
+      done = TRUE;
+      DEBUG(D_deliver) debug_printf("Z0%c item read\n", *ptr);
+      break;
 
     /* Anything else is a disaster. */
 
     default:
-    msg = string_sprintf("malformed data (%d) read from pipe for transport "
-      "process %d for transport %s", ptr[-1], pid,
-        addr->transport->driver_name);
-    done = TRUE;
-    break;
+      msg = string_sprintf("malformed data (%d) read from pipe for transport "
+       "process %d for transport %s", ptr[-1], pid,
+         addr->transport->driver_name);
+      done = TRUE;
+      break;
     }
   }
 
@@ -4434,6 +4428,23 @@ for (delivery_count = 0; addr_remote; delivery_count++)
   if (tp->setup)
     (void)((tp->setup)(addr->transport, addr, NULL, uid, gid, NULL));
 
+  /* If we have a connection still open from a verify stage (lazy-close)
+  treat it as if it is a continued connection (apart from the counter used
+  for the log line mark). */
+
+  if (cutthrough.fd >= 0 && cutthrough.callout_hold_only)
+    {
+    DEBUG(D_deliver)
+      debug_printf("lazy-callout-close: have conn still open from verification\n");
+    continue_transport = cutthrough.transport;
+    continue_hostname = string_copy(cutthrough.host.name);
+    continue_host_address = string_copy(cutthrough.host.address);
+    continue_sequence = 1;
+    sending_ip_address = cutthrough.snd_ip;
+    sending_port = cutthrough.snd_port;
+    smtp_peer_options = cutthrough.peer_options;
+    }
+
   /* If this is a run to continue delivery down an already-established
   channel, check that this set of addresses matches the transport and
   the channel. If it does not, defer the addresses. If a host list exists,
@@ -4775,7 +4786,6 @@ for (delivery_count = 0; addr_remote; delivery_count++)
 
       memcpy(big_buffer, &addr->dsn_aware, sizeof(addr->dsn_aware));
       rmt_dlv_checked_write(fd, 'D', '0', big_buffer, sizeof(addr->dsn_aware));
-      DEBUG(D_deliver) debug_printf("DSN write: addr->dsn_aware = %d\n", addr->dsn_aware);
 
       /* Retry information: for most success cases this will be null. */
 
@@ -4890,6 +4900,19 @@ for (delivery_count = 0; addr_remote; delivery_count++)
 
   (void)close(pfd[pipe_write]);
 
+  /* If we have a connection still open from a verify stage (lazy-close)
+  release its TLS library context (if any) as responsibility was passed to
+  the delivery child process. */
+
+  if (cutthrough.fd >= 0 && cutthrough.callout_hold_only)
+    {
+#ifdef SUPPORT_TLS
+    tls_close(FALSE, FALSE);
+#endif
+    (void) close(cutthrough.fd);
+    release_cutthrough_connection(US"passed to transport proc");
+    }
+
   /* Fork failed; defer with error message */
 
   if (pid < 0)
@@ -7040,6 +7063,7 @@ phase, to minimize cases of half-done things. */
 
 DEBUG(D_deliver)
   debug_printf(">>>>>>>>>>>>>>>> deliveries are done >>>>>>>>>>>>>>>>\n");
+cancel_cutthrough_connection(TRUE, "deliveries are done");
 
 /* Root privilege is no longer needed */
 
@@ -8416,6 +8440,67 @@ deliver_datafile = -1;
 return new_sender_address;
 }
 
+
+
+void
+delivery_re_exec(int exec_type)
+{
+uschar * s;
+
+if (cutthrough.fd >= 0 && cutthrough.callout_hold_only)
+  {
+  int pfd[2], channel_fd = cutthrough.fd, pid;
+
+  smtp_peer_options = cutthrough.peer_options;
+  continue_sequence = 0;
+
+#ifdef SUPPORT_TLS
+  if (cutthrough.is_tls)
+    {
+    smtp_peer_options |= PEER_OFFERED_TLS;
+    sending_ip_address = cutthrough.snd_ip;
+    sending_port = cutthrough.snd_port;
+
+    s = US"socketpair";
+    if (socketpair(AF_UNIX, SOCK_STREAM, 0, pfd) != 0)
+      goto fail;
+
+    s = US"fork";
+    if ((pid = fork()) < 0)
+      goto fail;
+
+    else if (pid == 0)         /* child */
+      {
+      smtp_proxy_tls(big_buffer, big_buffer_size, pfd[0], 5*60);
+      exim_exit(0);
+      }
+
+    (void) close(channel_fd);  /* release the client socket */
+    channel_fd = pfd[1];
+    }
+#endif
+
+  transport_do_pass_socket(cutthrough.transport, cutthrough.host.name,
+    cutthrough.host.address, message_id, channel_fd);
+  }
+else
+  {
+  cancel_cutthrough_connection(TRUE, "non-continued delivery");
+  (void) child_exec_exim(exec_type, FALSE, NULL, FALSE, 2, US"-Mc", message_id);
+  }
+/* Control does not return here. */
+
+fail:
+  log_write(0,
+    LOG_MAIN | (exec_type == CEE_EXEC_EXIT ? LOG_PANIC : LOG_PANIC_DIE),
+    "delivery re-exec failed: %s", strerror(errno));
+
+  /* Get here if exec_type == CEE_EXEC_EXIT.
+  Note: this must be _exit(), not exit(). */
+
+  _exit(EX_EXECFAILED);
+}
+
 /* vi: aw ai sw=2
 */
 /* End of deliver.c */
index 3833820..3dfe5db 100644 (file)
@@ -4352,11 +4352,8 @@ if (!unprivileged &&                      /* originally had root AND */
         (msg_action_arg < 0 ||            /*       and               */
           msg_action != MSG_DELIVER) &&   /* not delivering and      */
         (!checking || !address_test_mode) /* not address checking    */
-        )
-      ))
-  {
+   )  ) )
   exim_setugid(exim_uid, exim_gid, TRUE, US"privilege not needed");
-  }
 
 /* When we are retaining a privileged uid, we still change to the exim gid. */
 
@@ -4370,7 +4367,6 @@ else
   there's no security risk.  For me, it's { exim -bV } on a just-built binary,
   no need to complain then. */
   if (rv == -1)
-    {
     if (!(unprivileged || removed_privilege))
       {
       fprintf(stderr,
@@ -4380,7 +4376,6 @@ else
     else
       DEBUG(D_any) debug_printf("changing group to %ld failed: %s\n",
           (long int)exim_gid, strerror(errno));
-    }
   }
 
 /* Handle a request to scan a file for malware */
@@ -5329,15 +5324,13 @@ if (smtp_input)
 else
   {
   thismessage_size_limit = expand_string_integer(message_size_limit, TRUE);
-  if (expand_string_message != NULL)
-    {
+  if (expand_string_message)
     if (thismessage_size_limit == -1)
       log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to expand "
         "message_size_limit: %s", expand_string_message);
     else
       log_write(0, LOG_MAIN|LOG_PANIC_DIE, "invalid value for "
         "message_size_limit: %s", expand_string_message);
-    }
   }
 
 /* Loop for several messages when reading SMTP input. If we fork any child
@@ -5434,6 +5427,7 @@ while (more)
       more = receive_msg(extract_recipients);
       if (message_id[0] == 0)
         {
+       cancel_cutthrough_connection(TRUE, US"receive dropped");
         if (more) goto moreloop;
         smtp_log_no_mail();               /* Log no mail if configured */
         exim_exit(EXIT_FAILURE);
@@ -5441,6 +5435,7 @@ while (more)
       }
     else
       {
+      cancel_cutthrough_connection(TRUE, US"message setup dropped");
       smtp_log_no_mail();               /* Log no mail if configured */
       exim_exit((rc == 0)? EXIT_SUCCESS : EXIT_FAILURE);
       }
@@ -5716,21 +5711,28 @@ while (more)
   not if queue_only is set (case 0). Case 1 doesn't happen here (too many
   connections). */
 
-  if (local_queue_only) switch(queue_only_reason)
+  if (local_queue_only)
     {
-    case 2:
-    log_write(L_delay_delivery,
-              LOG_MAIN, "no immediate delivery: more than %d messages "
-      "received in one connection", smtp_accept_queue_per_connection);
-    break;
+    cancel_cutthrough_connection(TRUE, US"no delivery; queueing");
+    switch(queue_only_reason)
+      {
+      case 2:
+       log_write(L_delay_delivery,
+               LOG_MAIN, "no immediate delivery: more than %d messages "
+         "received in one connection", smtp_accept_queue_per_connection);
+       break;
 
-    case 3:
-    log_write(L_delay_delivery,
-              LOG_MAIN, "no immediate delivery: load average %.2f",
-              (double)load_average/1000.0);
-    break;
+      case 3:
+       log_write(L_delay_delivery,
+               LOG_MAIN, "no immediate delivery: load average %.2f",
+               (double)load_average/1000.0);
+      break;
+      }
     }
 
+  else if (queue_only_policy || deliver_freeze)
+    cancel_cutthrough_connection(TRUE, US"no delivery; queueing");
+
   /* Else do the delivery unless the ACL or local_scan() called for queue only
   or froze the message. Always deliver in a separate process. A fork failure is
   not a disaster, as the delivery will eventually happen on a subsequent queue
@@ -5739,7 +5741,7 @@ while (more)
   thereby defer the delivery if it tries to use (for example) a cached ldap
   connection that the parent has called unbind on. */
 
-  else if (!queue_only_policy && !deliver_freeze)
+  else
     {
     pid_t pid;
     search_tidyup();
@@ -5755,8 +5757,7 @@ while (more)
 
       if (geteuid() != root_uid && !deliver_drop_privilege && !unprivileged)
         {
-        (void)child_exec_exim(CEE_EXEC_EXIT, FALSE, NULL, FALSE,
-               2, US"-Mc", message_id);
+       delivery_re_exec(CEE_EXEC_EXIT);
         /* Control does not return here. */
         }
 
@@ -5770,22 +5771,27 @@ while (more)
 
     if (pid < 0)
       {
+      cancel_cutthrough_connection(TRUE, US"delivery fork failed");
       log_write(0, LOG_MAIN|LOG_PANIC, "failed to fork automatic delivery "
         "process: %s", strerror(errno));
       }
+    else
+      {
+      release_cutthrough_connection(US"msg passed for delivery");
 
-    /* In the parent, wait if synchronous delivery is required. This will
-    always be the case in MUA wrapper mode. */
+      /* In the parent, wait if synchronous delivery is required. This will
+      always be the case in MUA wrapper mode. */
 
-    else if (synchronous_delivery)
-      {
-      int status;
-      while (wait(&status) != pid);
-      if ((status & 0x00ff) != 0)
-        log_write(0, LOG_MAIN|LOG_PANIC,
-          "process %d crashed with signal %d while delivering %s",
-          (int)pid, status & 0x00ff, message_id);
-      if (mua_wrapper && (status & 0xffff) != 0) exim_exit(EXIT_FAILURE);
+      if (synchronous_delivery)
+       {
+       int status;
+       while (wait(&status) != pid);
+       if ((status & 0x00ff) != 0)
+         log_write(0, LOG_MAIN|LOG_PANIC,
+           "process %d crashed with signal %d while delivering %s",
+           (int)pid, status & 0x00ff, message_id);
+       if (mua_wrapper && (status & 0xffff) != 0) exim_exit(EXIT_FAILURE);
+       }
       }
     }
 
index a7d9c11..3314801 100644 (file)
@@ -110,18 +110,19 @@ extern void    bdat_flush_data(void);
 extern void    bits_clear(unsigned int *, size_t, int *);
 extern void    bits_set(unsigned int *, size_t, int *);
 
-extern void    cancel_cutthrough_connection(const char *);
+extern void    cancel_cutthrough_connection(BOOL, const uschar *);
 extern int     check_host(void *, const uschar *, const uschar **, uschar **);
 extern uschar **child_exec_exim(int, BOOL, int *, BOOL, int, ...);
 extern pid_t   child_open_uid(const uschar **, const uschar **, int,
                 uid_t *, gid_t *, int *, int *, uschar *, BOOL);
 extern BOOL    cleanup_environment(void);
+extern BOOL    cutthrough_data_puts(uschar *, int);
+extern BOOL    cutthrough_data_put_nl(void);
 extern uschar *cutthrough_finaldot(void);
 extern BOOL    cutthrough_flush_send(void);
 extern BOOL    cutthrough_headers_send(void);
 extern BOOL    cutthrough_predata(void);
-extern BOOL    cutthrough_puts(uschar *, int);
-extern BOOL    cutthrough_put_nl(void);
+extern void    release_cutthrough_connection(const uschar *);
 
 extern void    daemon_go(void);
 
@@ -149,6 +150,7 @@ extern int     deliver_split_address(address_item *);
 extern void    deliver_succeeded(address_item *);
 
 extern uschar *deliver_get_sender_address (uschar *id);
+extern void    delivery_re_exec(int);
 
 extern BOOL    directory_make(const uschar *, const uschar *, int, BOOL);
 #ifndef DISABLE_DKIM
@@ -405,6 +407,7 @@ extern void    smtp_get_cache(void);
 extern int     smtp_handle_acl_fail(int, int, uschar *, uschar *);
 extern void    smtp_log_no_mail(void);
 extern void    smtp_message_code(uschar **, int *, uschar **, uschar **, BOOL);
+extern void    smtp_proxy_tls(uschar *, size_t, int, int);
 extern BOOL    smtp_read_response(smtp_inblock *, uschar *, int, int, int);
 extern void    smtp_respond(uschar *, int, BOOL, uschar *);
 extern void    smtp_notquit_exit(uschar *, uschar *, uschar *, ...);
@@ -472,6 +475,8 @@ extern void    tls_modify_variables(tls_support *);
 extern BOOL    transport_check_waiting(const uschar *, const uschar *, int, uschar *,
                  BOOL *, oicf, void*);
 extern void    transport_init(void);
+extern void    transport_do_pass_socket(const uschar *, const uschar *,
+                const uschar *, uschar *, int);
 extern BOOL    transport_pass_socket(const uschar *, const uschar *, const uschar *, uschar *,
                  int);
 extern uschar *transport_rcpt_address(address_item *, BOOL);
index 9e417b0..8dd3922 100644 (file)
@@ -538,8 +538,10 @@ uschar *continue_transport     = NULL;
 
 uschar *csa_status             = NULL;
 cut_t   cutthrough = {
+  FALSE,                               /* verify-only: normal delivery */
   FALSE,                               /* delivery: when to attempt */
   FALSE,                               /* on defer: spool locally */
+  FALSE,                               /* not a TLS conn yet */
   -1,                                  /* fd: open connection */
   0,                                   /* nrcpt: number of addresses */
 };
index 72be706..5f9ad3b 100644 (file)
@@ -299,11 +299,17 @@ extern uschar *continue_transport;     /* Transport for continued delivery */
 extern uschar *csa_status;             /* Client SMTP Authorization result */
 
 typedef struct {
+  unsigned     callout_hold_only:1;    /* Conn is only for verify callout */
   unsigned     delivery:1;             /* When to attempt */
   unsigned     defer_pass:1;           /* Pass 4xx to caller rather than spooling */
+  unsigned     is_tls:1;              /* Conn has TLS active */
   int          fd;                     /* Open connection */
   int          nrcpt;                  /* Count of addresses */
+  uschar *     transport;             /* Name of transport */
   uschar *     interface;              /* (address of) */
+  uschar *     snd_ip;                /* sending_ip_address */
+  int         snd_port;               /* sending_port */
+  unsigned     peer_options;          /* smtp_peer_options */
   host_item    host;                   /* Host used */
   address_item addr;                   /* (Chain of) addresses */
 } cut_t;
index 004d6df..08f631b 100644 (file)
@@ -721,7 +721,8 @@ enum { v_none, v_sender, v_recipient, v_expn };
 #define vopt_callout_no_cache     0x0040   /* disable callout cache */
 #define vopt_callout_recipsender  0x0080   /* use real sender to verify recip */
 #define vopt_callout_recippmaster 0x0100   /* use postmaster to verify recip */
-#define vopt_success_on_redirect  0x0200
+#define vopt_callout_hold        0x0200   /* lazy close connection */
+#define vopt_success_on_redirect  0x0400
 
 /* Values for fields in callout cache records */
 
index 7980c32..33c60e0 100644 (file)
@@ -831,7 +831,7 @@ while ((ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF)
       {
       message_size++;
       if (fout != NULL && fputc('\n', fout) == EOF) return END_WERROR;
-      (void) cutthrough_put_nl();
+      (void) cutthrough_data_put_nl();
       if (ch != '\r') ch_state = 1; else continue;
       }
     break;
@@ -850,7 +850,7 @@ while ((ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF)
     if (ch == '.')
       {
       uschar c= ch;
-      (void) cutthrough_puts(&c, 1);
+      (void) cutthrough_data_puts(&c, 1);
       }
     ch_state = 1;
     break;
@@ -860,7 +860,7 @@ while ((ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF)
     message_size++;
     body_linecount++;
     if (fout != NULL && fputc('\n', fout) == EOF) return END_WERROR;
-    (void) cutthrough_put_nl();
+    (void) cutthrough_data_put_nl();
     if (ch == '\r')
       {
       ch_state = 2;
@@ -881,11 +881,11 @@ while ((ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF)
     if (message_size > thismessage_size_limit) return END_SIZE;
     }
   if(ch == '\n')
-    (void) cutthrough_put_nl();
+    (void) cutthrough_data_put_nl();
   else
     {
     uschar c = ch;
-    (void) cutthrough_puts(&c, 1);
+    (void) cutthrough_data_puts(&c, 1);
     }
   }
 
@@ -991,7 +991,7 @@ for(;;)
        {
        message_size++;
        if (fout && fputc('\n', fout) == EOF) return END_WERROR;
-       (void) cutthrough_put_nl();
+       (void) cutthrough_data_put_nl();
        if (ch == '\r') continue;       /* don't write CR */
        ch_state = MID_LINE;
        }
@@ -1008,11 +1008,11 @@ for(;;)
     if (message_size > thismessage_size_limit) return END_SIZE;
     }
   if(ch == '\n')
-    (void) cutthrough_put_nl();
+    (void) cutthrough_data_put_nl();
   else
     {
     uschar c = ch;
-    (void) cutthrough_puts(&c, 1);
+    (void) cutthrough_data_puts(&c, 1);
     }
   }
 /*NOTREACHED*/
@@ -1140,7 +1140,8 @@ switch(where)
   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 && cutthrough.delivery
+       && (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");
@@ -1148,11 +1149,11 @@ switch(where)
     }
   }
 
-if (acl_removed_headers != NULL)
+if (acl_removed_headers)
   {
   DEBUG(D_receive|D_acl) debug_printf_indent(">>Headers removed by %s ACL:\n", acl_name);
 
-  for (h = header_list; h != NULL; h = h->next) if (h->type != htype_old)
+  for (h = header_list; h; h = h->next) if (h->type != htype_old)
     {
     const uschar * list = acl_removed_headers;
     int sep = ':';         /* This is specified as a colon-separated list */
@@ -1170,10 +1171,10 @@ if (acl_removed_headers != NULL)
   DEBUG(D_receive|D_acl) debug_printf_indent(">>\n");
   }
 
-if (acl_added_headers == NULL) return;
+if (!acl_added_headers) return;
 DEBUG(D_receive|D_acl) debug_printf_indent(">>Headers added by %s ACL:\n", acl_name);
 
-for (h = acl_added_headers; h != NULL; h = next)
+for (h = acl_added_headers; h; h = next)
   {
   next = h->next;
 
@@ -1653,7 +1654,7 @@ search_tidyup();
 cutthrough delivery with the no-spool option.  It shouldn't be possible
 to set up the combination, but just in case kill any ongoing connection. */
 if (extract_recip || !smtp_input)
-  cancel_cutthrough_connection("not smtp input");
+  cancel_cutthrough_connection(TRUE, US"not smtp input");
 
 /* Initialize the chain of headers by setting up a place-holder for Received:
 header. Temporarily mark it as "old", i.e. not to be used. We keep header_last
@@ -2988,26 +2989,25 @@ inbound is, but inbound chunking ought to be ok with outbound plain.
 Could we do onward CHUNKING given inbound CHUNKING?
 */
 if (chunking_state > CHUNKING_OFFERED)
-  cancel_cutthrough_connection("chunking active");
+  cancel_cutthrough_connection(FALSE, US"chunking active");
 
 /* 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)
+
+if (cutthrough.fd >= 0 && cutthrough.delivery)
   {
   if (received_count > received_headers_max)
     {
-    cancel_cutthrough_connection("too many headers");
+    cancel_cutthrough_connection(TRUE, US"too many headers");
     if (smtp_input) receive_swallow_smtp();  /* Swallow incoming SMTP */
     log_write(0, LOG_MAIN|LOG_REJECT, "rejected from <%s>%s%s%s%s: "
       "Too many \"Received\" headers",
       sender_address,
-      (sender_fullhost == NULL)? "" : " H=",
-      (sender_fullhost == NULL)? US"" : sender_fullhost,
-      (sender_ident == NULL)? "" : " U=",
-      (sender_ident == NULL)? US"" : sender_ident);
+      sender_fullhost ? "H=" : "", sender_fullhost ? sender_fullhost : US"",
+      sender_ident ? "U=" : "", sender_ident ? sender_ident : US"");
     message_id[0] = 0;                       /* Indicate no message accepted */
     smtp_reply = US"550 Too many \"Received\" headers - suspected mail loop";
     goto TIDYUP;                             /* Skip to end of function */
@@ -3105,7 +3105,7 @@ if (!ferror(data_file) && !(receive_feof)() && message_ended != END_DOT)
       if (smtp_input)
        {
        Uunlink(spool_name);                 /* Lose data file when closed */
-       cancel_cutthrough_connection("sender closed connection");
+       cancel_cutthrough_connection(TRUE, US"sender closed connection");
        message_id[0] = 0;                   /* Indicate no message accepted */
        smtp_reply = handle_lost_connection(US"");
        smtp_yield = FALSE;
@@ -3118,7 +3118,7 @@ if (!ferror(data_file) && !(receive_feof)() && message_ended != END_DOT)
 
     case END_SIZE:
       Uunlink(spool_name);                /* Lose the data file when closed */
-      cancel_cutthrough_connection("mail too big");
+      cancel_cutthrough_connection(TRUE, US"mail too big");
       if (smtp_input) receive_swallow_smtp();  /* Swallow incoming SMTP */
 
       log_write(L_size_reject, LOG_MAIN|LOG_REJECT, "rejected from <%s>%s%s%s%s: "
@@ -3151,7 +3151,7 @@ if (!ferror(data_file) && !(receive_feof)() && message_ended != END_DOT)
 
     case END_PROTOCOL:
       Uunlink(spool_name);             /* Lose the data file when closed */
-      cancel_cutthrough_connection("sender protocol error");
+      cancel_cutthrough_connection(TRUE, US"sender protocol error");
       smtp_reply = US"";               /* Response already sent */
       message_id[0] = 0;               /* Indicate no message accepted */
       goto TIDYUP;                     /* Skip to end of function */
@@ -3184,7 +3184,7 @@ if (fflush(data_file) == EOF || ferror(data_file) ||
 
   log_write(0, LOG_MAIN, "Message abandoned: %s", msg);
   Uunlink(spool_name);                /* Lose the data file */
-  cancel_cutthrough_connection("error writing spoolfile");
+  cancel_cutthrough_connection(TRUE, US"error writing spoolfile");
 
   if (smtp_input)
     {
@@ -3423,7 +3423,7 @@ else
              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");
+             cancel_cutthrough_connection(TRUE, US"dkim acl not ok");
              break;
              }
             }
@@ -3542,14 +3542,14 @@ else
         {
         recipients_count = 0;
         blackholed_by = US"DATA ACL";
-        if (log_msg != NULL)
+        if (log_msg)
           blackhole_log_msg = string_sprintf(": %s", log_msg);
-       cancel_cutthrough_connection("data acl discard");
+       cancel_cutthrough_connection(TRUE, US"data acl discard");
         }
       else if (rc != OK)
         {
         Uunlink(spool_name);
-       cancel_cutthrough_connection("data acl not ok");
+       cancel_cutthrough_connection(TRUE, US"data acl not ok");
 #ifdef WITH_CONTENT_SCAN
         unspool_mbox();
 #endif
@@ -4148,9 +4148,9 @@ for this message. */
 
    XXX We do not handle queue-only, freezing, or blackholes.
 */
-if(cutthrough.fd >= 0)
+if(cutthrough.fd >= 0 && cutthrough.delivery)
   {
-  uschar * msg= cutthrough_finaldot(); /* Ask the target system to accept the message */
+  uschar * msg = cutthrough_finaldot();        /* Ask the target system to accept the message */
                                        /* Logging was done in finaldot() */
   switch(msg[0])
     {
@@ -4297,7 +4297,6 @@ if (smtp_input)
        Uunlink(spool_fname(US"input", message_subdir, message_id, US"-D"));
        Uunlink(spool_fname(US"input", message_subdir, message_id, US"-H"));
        Uunlink(spool_fname(US"msglog", message_subdir, message_id, US""));
-       message_id[0] = 0;        /* Prevent a delivery from starting */
        break;
 
       case TMP_REJ:
@@ -4307,12 +4306,15 @@ if (smtp_input)
          Uunlink(spool_fname(US"input", message_subdir, message_id, US"-H"));
          Uunlink(spool_fname(US"msglog", message_subdir, message_id, US""));
          }
-       message_id[0] = 0;        /* Prevent a delivery from starting */
       default:
        break;
       }
-    cutthrough.delivery = FALSE;
-    cutthrough.defer_pass = FALSE;
+    if (cutthrough_done != NOT_TRIED)
+      {
+      message_id[0] = 0;         /* Prevent a delivery from starting */
+      cutthrough.delivery = cutthrough.callout_hold_only = FALSE;
+      cutthrough.defer_pass = FALSE;
+      }
     }
 
   /* For batched SMTP, generate an error message on failure, and do
index f0885b3..3c40a5c 100644 (file)
@@ -1887,7 +1887,6 @@ smtp_reset(void *reset_point)
 recipients_list = NULL;
 rcpt_count = rcpt_defer_count = rcpt_fail_count =
   raw_recipients_count = recipients_count = recipients_list_max = 0;
-cancel_cutthrough_connection("smtp reset");
 message_linecount = 0;
 message_size = -1;
 acl_added_headers = NULL;
@@ -2018,6 +2017,7 @@ bsmtp_transaction_linecount = receive_linecount;
 
 if ((receive_feof)()) return 0;   /* Treat EOF as QUIT */
 
+cancel_cutthrough_connection(TRUE, US"smtp_setup_batch_msg");
 smtp_reset(reset_point);                /* Reset for start of message */
 
 /* Deal with SMTP commands. This loop is exited by setting done to a POSITIVE
@@ -2042,6 +2042,7 @@ while (done <= 0)
     /* Fall through */
 
     case RSET_CMD:
+    cancel_cutthrough_connection(TRUE, US"RSET received");
     smtp_reset(reset_point);
     bsmtp_transaction_linecount = receive_linecount;
     break;
@@ -2065,6 +2066,7 @@ while (done <= 0)
 
     /* Reset to start of message */
 
+    cancel_cutthrough_connection(TRUE, US"MAIL received");
     smtp_reset(reset_point);
 
     /* Apply SMTP rewrite */
@@ -4253,6 +4255,7 @@ while (done <= 0)
          : pnormal)
        + (tls_in.active >= 0 ? pcrpted : 0)
        ];
+    cancel_cutthrough_connection(TRUE, US"sent EHLO response");
     smtp_reset(reset_point);
     toomany = FALSE;
     break;   /* HELO/EHLO */
@@ -4307,6 +4310,7 @@ while (done <= 0)
     /* Reset for start of message - even if this is going to fail, we
     obviously need to throw away any previous data. */
 
+    cancel_cutthrough_connection(TRUE, US"MAIL received");
     smtp_reset(reset_point);
     toomany = FALSE;
     sender_data = recipient_data = NULL;
@@ -5162,6 +5166,7 @@ while (done <= 0)
     do an implied RSET when STARTTLS is received. */
 
     incomplete_transaction_log(US"STARTTLS");
+    cancel_cutthrough_connection(TRUE, US"STARTTLS received");
     smtp_reset(reset_point);
     toomany = FALSE;
     cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = FALSE;
@@ -5298,6 +5303,7 @@ while (done <= 0)
 
     case RSET_CMD:
     smtp_rset_handler();
+    cancel_cutthrough_connection(TRUE, US"RSET received");
     smtp_reset(reset_point);
     toomany = FALSE;
     break;
index 4a41ba1..38a16b2 100644 (file)
@@ -1699,6 +1699,7 @@ uschar *expcerts, *expcrl;
 
 if (!expand_check(certs, US"tls_verify_certificates", &expcerts, errstr))
   return DEFER;
+DEBUG(D_tls) debug_printf("tls_verify_certificates: %s\n", expcerts);
 
 if (expcerts && *expcerts)
   {
index e6e3278..1a53690 100644 (file)
@@ -1238,7 +1238,6 @@ set up a filtering process, fork another process to call the internal function
 to write to the filter, and in this process just suck from the filter and write
 down the given fd. At the end, tidy up the pipes and the processes.
 
-XXX
 Arguments:     as for internal_transport_write_message() above
 
 Returns:       TRUE on success; FALSE (with errno) for any failure
@@ -1934,6 +1933,72 @@ return TRUE;
 *    Deliver waiting message down same socket    *
 *************************************************/
 
+/* Just the regain-root-privilege exec portion */
+void
+transport_do_pass_socket(const uschar *transport_name, const uschar *hostname,
+  const uschar *hostaddress, uschar *id, int socket_fd)
+{
+pid_t pid;
+int status;
+int i = 20;
+const uschar **argv;
+
+/* Set up the calling arguments; use the standard function for the basics,
+but we have a number of extras that may be added. */
+
+argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, &i, FALSE, 0);
+
+if (smtp_authenticated)                                argv[i++] = US"-MCA";
+if (smtp_peer_options & PEER_OFFERED_CHUNKING) argv[i++] = US"-MCK";
+if (smtp_peer_options & PEER_OFFERED_DSN)      argv[i++] = US"-MCD";
+if (smtp_peer_options & PEER_OFFERED_PIPE)     argv[i++] = US"-MCP";
+if (smtp_peer_options & PEER_OFFERED_SIZE)     argv[i++] = US"-MCS";
+#ifdef SUPPORT_TLS
+if (smtp_peer_options & PEER_OFFERED_TLS)
+  if (tls_out.active >= 0 || continue_proxy_cipher)
+    {
+    argv[i++] = US"-MCt";
+    argv[i++] = sending_ip_address;
+    argv[i++] = string_sprintf("%d", sending_port);
+    argv[i++] = tls_out.active >= 0 ? tls_out.cipher : continue_proxy_cipher;
+    }
+  else
+    argv[i++] = US"-MCT";
+#endif
+
+if (queue_run_pid != (pid_t)0)
+  {
+  argv[i++] = US"-MCQ";
+  argv[i++] = string_sprintf("%d", queue_run_pid);
+  argv[i++] = string_sprintf("%d", queue_run_pipe);
+  }
+
+argv[i++] = US"-MC";
+argv[i++] = US transport_name;
+argv[i++] = US hostname;
+argv[i++] = US hostaddress;
+argv[i++] = string_sprintf("%d", continue_sequence + 1);
+argv[i++] = id;
+argv[i++] = NULL;
+
+/* Arrange for the channel to be on stdin. */
+
+if (socket_fd != 0)
+  {
+  (void)dup2(socket_fd, 0);
+  (void)close(socket_fd);
+  }
+
+DEBUG(D_exec) debug_print_argv(argv);
+exim_nullstd();                          /* Ensure std{out,err} exist */
+execv(CS argv[0], (char *const *)argv);
+
+DEBUG(D_any) debug_printf("execv failed: %s\n", strerror(errno));
+_exit(errno);         /* Note: must be _exit(), NOT exit() */
+}
+
+
+
 /* Fork a new exim process to deliver the message, and do a re-exec, both to
 get a clean delivery process, and to regain root privilege in cases where it
 has been given away.
@@ -1959,9 +2024,6 @@ DEBUG(D_transport) debug_printf("transport_pass_socket entered\n");
 
 if ((pid = fork()) == 0)
   {
-  int i = 20;
-  const uschar **argv;
-
   /* Disconnect entirely from the parent process. If we are running in the
   test harness, wait for a bit to allow the previous process time to finish,
   write the log, etc., so that the output is always in the same order for
@@ -1970,59 +2032,8 @@ if ((pid = fork()) == 0)
   if ((pid = fork()) != 0) _exit(EXIT_SUCCESS);
   if (running_in_test_harness) sleep(1);
 
-  /* Set up the calling arguments; use the standard function for the basics,
-  but we have a number of extras that may be added. */
-
-  argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, &i, FALSE, 0);
-
-  if (smtp_authenticated) argv[i++] = US"-MCA";
-
-  if (smtp_peer_options & PEER_OFFERED_CHUNKING) argv[i++] = US"-MCK";
-  if (smtp_peer_options & PEER_OFFERED_DSN) argv[i++] = US"-MCD";
-  if (smtp_peer_options & PEER_OFFERED_PIPE) argv[i++] = US"-MCP";
-  if (smtp_peer_options & PEER_OFFERED_SIZE) argv[i++] = US"-MCS";
-#ifdef SUPPORT_TLS
-  if (smtp_peer_options & PEER_OFFERED_TLS)
-    if (tls_out.active >= 0 || continue_proxy_cipher)
-      {
-      argv[i++] = US"-MCt";
-      argv[i++] = sending_ip_address;
-      argv[i++] = string_sprintf("%d", sending_port);
-      argv[i++] = tls_out.active >= 0 ? tls_out.cipher : continue_proxy_cipher;
-      }
-    else
-      argv[i++] = US"-MCT";
-#endif
-
-  if (queue_run_pid != (pid_t)0)
-    {
-    argv[i++] = US"-MCQ";
-    argv[i++] = string_sprintf("%d", queue_run_pid);
-    argv[i++] = string_sprintf("%d", queue_run_pipe);
-    }
-
-  argv[i++] = US"-MC";
-  argv[i++] = US transport_name;
-  argv[i++] = US hostname;
-  argv[i++] = US hostaddress;
-  argv[i++] = string_sprintf("%d", continue_sequence + 1);
-  argv[i++] = id;
-  argv[i++] = NULL;
-
-  /* Arrange for the channel to be on stdin. */
-
-  if (socket_fd != 0)
-    {
-    (void)dup2(socket_fd, 0);
-    (void)close(socket_fd);
-    }
-
-  DEBUG(D_exec) debug_print_argv(argv);
-  exim_nullstd();                          /* Ensure std{out,err} exist */
-  execv(CS argv[0], (char *const *)argv);
-
-  DEBUG(D_any) debug_printf("execv failed: %s\n", strerror(errno));
-  _exit(errno);         /* Note: must be _exit(), NOT exit() */
+  transport_do_pass_socket(transport_name, hostname, hostaddress,
+    id, socket_fd);
   }
 
 /* If the process creation succeeded, wait for the first-level child, which
index 34c96db..c4626b3 100644 (file)
@@ -220,8 +220,10 @@ smtp_transport_options_block smtp_transport_option_defaults = {
   NULL,                /* hosts_verify_avoid_tls */
   NULL,                /* hosts_avoid_pipelining */
   NULL,                /* hosts_avoid_esmtp */
+#ifdef SUPPORT_TLS
   NULL,                /* hosts_nopass_tls */
   US"*",              /* hosts_noproxy_tls */
+#endif
   5*60,                /* command_timeout */
   5*60,                /* connect_timeout; shorter system default overrides */
   5*60,                /* data timeout */
@@ -1801,9 +1803,11 @@ goto SEND_QUIT;
     }
   }
 
-/* For continuing deliveries down the same channel, the socket is the standard
-input, and we don't need to redo EHLO here (but may need to do so for TLS - see
-below). Set up the pointer to where subsequent commands will be left, for
+/* For continuing deliveries down the same channel, having re-exec'd  the socket
+is the standard input; for a socket held open from verify it is recorded
+in the cutthrough context block.  Either way we don't need to redo EHLO here
+(but may need to do so for TLS - see below).
+Set up the pointer to where subsequent commands will be left, for
 error messages. Note that smtp_peer_options will have been
 set from the command line if they were set in the process that passed the
 connection on. */
@@ -1815,19 +1819,30 @@ separate - we could match up by host ip+port as a bodge. */
 
 else
   {
-  sx->inblock.sock = sx->outblock.sock = 0;    /* stdin */
+  if (cutthrough.fd >= 0 && cutthrough.callout_hold_only)
+    {
+    sx->inblock.sock = sx->outblock.sock = cutthrough.fd;
+    sx->host->port = sx->port = cutthrough.host.port;
+    }
+  else
+    {
+    sx->inblock.sock = sx->outblock.sock = 0;  /* stdin */
+    sx->host->port = sx->port;    /* Record the port that was used */
+    }
   smtp_command = big_buffer;
-  sx->host->port = sx->port;    /* Record the port that was used */
   sx->helo_data = NULL;                /* ensure we re-expand ob->helo_data */
 
-  /* For a continued connection with TLS being proxied for us, nothing
-  more to do. */
+  /* For a continued connection with TLS being proxied for us, or a
+  held-open verify connection with TLS, nothing more to do. */
 
-  if (continue_proxy_cipher)
+  if (  continue_proxy_cipher
+     || (cutthrough.fd >= 0 && cutthrough.callout_hold_only && cutthrough.is_tls)
+     )
     {
     sx->peer_offered = smtp_peer_options;
     pipelining_active = !!(smtp_peer_options & PEER_OFFERED_PIPE);
-    HDEBUG(D_transport) debug_printf("continued connection, proxied TLS\n");
+    HDEBUG(D_transport) debug_printf("continued connection, %s TLS\n",
+      continue_proxy_cipher ? "proxied" : "verify conn with");
     return OK;
     }
   HDEBUG(D_transport) debug_printf("continued connection, no TLS\n");
@@ -2511,19 +2526,20 @@ return 0;
 * Proxy TLS connection for another transport process *
 ******************************************************/
 /*
-Use the smtp-context buffer as a staging area, and select on both the slave
-process and the TLS'd fd for data to read (per the coding in ip_recv() and
+Use the given buffer as a staging area, and select on both the given fd
+and the TLS'd client-fd for data to read (per the coding in ip_recv() and
 fd_ready() this is legitimate).  Do blocking full-size writes, and reads
 under a timeout.
 
 Arguments:
-  sx           smtp context block
+  buf          space to use for buffering
+  bufsiz       size of buffer
   proxy_fd     comms to proxied process
   timeout      per-read timeout, seconds
 */
 
-static void
-smtp_proxy_tls(smtp_context * sx, int proxy_fd, int timeout)
+void
+smtp_proxy_tls(uschar * buf, size_t bsize, int proxy_fd, int timeout)
 {
 fd_set fds;
 int max_fd = MAX(proxy_fd, tls_out.active) + 1;
@@ -2559,7 +2575,7 @@ for (fd_bits = 3; fd_bits; )
 
   /* handle inbound data */
   if (FD_ISSET(tls_out.active, &fds))
-    if ((rc = tls_read(FALSE, sx->buffer, sizeof(sx->buffer))) <= 0)
+    if ((rc = tls_read(FALSE, buf, bsize)) <= 0)
       {
       fd_bits &= ~1;
       FD_CLR(tls_out.active, &fds);
@@ -2568,14 +2584,14 @@ for (fd_bits = 3; fd_bits; )
     else
       {
       for (nbytes = 0; rc - nbytes > 0; nbytes += i)
-       if ((i = write(proxy_fd, sx->buffer + nbytes, rc - nbytes)) < 0) return;
+       if ((i = write(proxy_fd, buf + nbytes, rc - nbytes)) < 0) return;
       }
   else if (fd_bits & 1)
     FD_SET(tls_out.active, &fds);
 
   /* handle outbound data */
   if (FD_ISSET(proxy_fd, &fds))
-    if ((rc = read(proxy_fd, sx->buffer, sizeof(sx->buffer))) <= 0)
+    if ((rc = read(proxy_fd, buf, bsize)) <= 0)
       {
       fd_bits &= ~2;
       FD_CLR(proxy_fd, &fds);
@@ -2584,7 +2600,7 @@ for (fd_bits = 3; fd_bits; )
     else
       {
       for (nbytes = 0; rc - nbytes > 0; nbytes += i)
-       if ((i = tls_write(FALSE, sx->buffer + nbytes, rc - nbytes)) < 0) return;
+       if ((i = tls_write(FALSE, buf + nbytes, rc - nbytes)) < 0) return;
       }
   else if (fd_bits & 2)
     FD_SET(proxy_fd, &fds);
@@ -2724,33 +2740,52 @@ sx.send_rset = TRUE;
 sx.completed_addr = FALSE;
 
 
-/* Initiate a message transfer. */
+/* If we are a continued-connection-after-verify the MAIL and RCPT
+commands were already sent; do not re-send but do mark the addrs as
+having been accepted up to RCPT stage.  A traditional cont-conn
+always has a sequence number greater than one. */
 
-switch(smtp_write_mail_and_rcpt_cmds(&sx, &yield))
+if (continue_hostname && continue_sequence == 1)
   {
-  case 0:              break;
-  case -1: case -2:    goto RESPONSE_FAILED;
-  case -3:             goto END_OFF;
-  case -4:             goto SEND_QUIT;
-  default:             goto SEND_FAILED;
-  }
+  address_item * addr;
 
-/* If we are an MUA wrapper, abort if any RCPTs were rejected, either
-permanently or temporarily. We should have flushed and synced after the last
-RCPT. */
+  sx.peer_offered = smtp_peer_options;
+  sx.ok = TRUE;
+  sx.next_addr = NULL;
 
-if (mua_wrapper)
+  for (addr = addrlist; addr; addr = addr->next)
+    addr->transport_return = PENDING_OK;
+  }
+else
   {
-  address_item *badaddr;
-  for (badaddr = sx.first_addr; badaddr; badaddr = badaddr->next)
-    if (badaddr->transport_return != PENDING_OK)
-      {
-      /*XXX could we find a better errno than 0 here? */
-      set_errno_nohost(addrlist, 0, badaddr->message, FAIL,
-       testflag(badaddr, af_pass_message));
-      sx.ok = FALSE;
-      break;
-      }
+  /* Initiate a message transfer. */
+
+  switch(smtp_write_mail_and_rcpt_cmds(&sx, &yield))
+    {
+    case 0:            break;
+    case -1: case -2:  goto RESPONSE_FAILED;
+    case -3:           goto END_OFF;
+    case -4:           goto SEND_QUIT;
+    default:           goto SEND_FAILED;
+    }
+
+  /* If we are an MUA wrapper, abort if any RCPTs were rejected, either
+  permanently or temporarily. We should have flushed and synced after the last
+  RCPT. */
+
+  if (mua_wrapper)
+    {
+    address_item *badaddr;
+    for (badaddr = sx.first_addr; badaddr; badaddr = badaddr->next)
+      if (badaddr->transport_return != PENDING_OK)
+       {
+       /*XXX could we find a better errno than 0 here? */
+       set_errno_nohost(addrlist, 0, badaddr->message, FAIL,
+         testflag(badaddr, af_pass_message));
+       sx.ok = FALSE;
+       break;
+       }
+    }
   }
 
 /* If ok is TRUE, we know we have got at least one good recipient, and must now
@@ -3050,7 +3085,7 @@ else
         else
           sprintf(CS sx.buffer, "%.500s\n", addr->unique);
 
-        DEBUG(D_deliver) debug_printf("journalling %s\n", sx.buffer);
+        DEBUG(D_deliver) debug_printf("S:journalling %s\n", sx.buffer);
         len = Ustrlen(CS sx.buffer);
         if (write(journal_fd, sx.buffer, len) != len)
           log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for "
@@ -3376,7 +3411,7 @@ propagate it from the initial
        just passed the baton to.  Fork a child to to do it, and return to
        get logging done asap.  Which way to place the work makes assumptions
        about post-fork prioritisation which may not hold on all platforms. */
-
+#ifdef SUPPORT_TLS
        if (tls_out.active >= 0)
          {
          int pid = fork();
@@ -3390,10 +3425,11 @@ propagate it from the initial
            }
          else if (pid == 0)    /* child */
            {
-           smtp_proxy_tls(&sx, pfd[0], sx.ob->command_timeout);
+           smtp_proxy_tls(sx.buffer, sizeof(sx.buffer), pfd[0], sx.ob->command_timeout);
            exim_exit(0);
            }
          }
+#endif
        }
       }
 
@@ -3608,8 +3644,10 @@ DEBUG(D_transport)
     for (host = hostlist; host; host = host->next)
       debug_printf("  %s:%d\n", host->name, host->port);
     }
-  if (continue_hostname) debug_printf("already connected to %s [%s]\n",
-      continue_hostname, continue_host_address);
+  if (continue_hostname)
+    debug_printf("already connected to %s [%s] (on fd %d)\n",
+      continue_hostname, continue_host_address,
+      cutthrough.fd >= 0 ? cutthrough.fd : 0);
   }
 
 /* Set the flag requesting that these hosts be added to the waiting
index 9ff1807..59add8c 100644 (file)
@@ -39,7 +39,7 @@ static tree_node *dnsbl_cache = NULL;
 #define MT_NOT 1
 #define MT_ALL 2
 
-static uschar cutthrough_response(char, uschar **, int);
+static uschar cutthrough_response(int, char, uschar **, int);
 
 
 
@@ -388,7 +388,7 @@ if (addr->transport == cutthrough.addr.transport)
       deliver_domain = addr->domain;
       transport_name = addr->transport->name;
 
-      host_af = (Ustrchr(host->address, ':') == NULL)? AF_INET:AF_INET6;
+      host_af = Ustrchr(host->address, ':') ? AF_INET6 : AF_INET;
 
       if (!smtp_get_interface(tf->interface, host_af, addr, &interface,
              US"callout") ||
@@ -411,7 +411,7 @@ if (addr->transport == cutthrough.addr.transport)
          smtp_write_command(&ctblock, FALSE, "RCPT TO:<%.1000s>\r\n",
            transport_rcpt_address(addr,
               addr->transport->rcpt_include_affixes)) >= 0 &&
-         cutthrough_response('2', &resp, CUTTHROUGH_DATA_TIMEOUT) == '2';
+         cutthrough_response(cutthrough.fd, '2', &resp, CUTTHROUGH_DATA_TIMEOUT) == '2';
 
        /* This would go horribly wrong if a callout fail was ignored by ACL.
        We punt by abandoning cutthrough on a reject, like the
@@ -429,7 +429,7 @@ if (addr->transport == cutthrough.addr.transport)
          }
        else
          {
-         cancel_cutthrough_connection("recipient rejected");
+         cancel_cutthrough_connection(TRUE, US"recipient rejected");
          if (!resp || errno == ETIMEDOUT)
            {
            HDEBUG(D_verify) debug_printf("SMTP timeout\n");
@@ -459,7 +459,7 @@ if (addr->transport == cutthrough.addr.transport)
       break;   /* host_list */
       }
 if (!done)
-  cancel_cutthrough_connection("incompatible connection");
+  cancel_cutthrough_connection(TRUE, US"incompatible connection");
 return done;
 }
 
@@ -490,6 +490,7 @@ Arguments:
                       vopt_callout_random => do the "random" thing
                       vopt_callout_recipsender => use real sender for recipient
                       vopt_callout_recippmaster => use postmaster for recipient
+                     vopt_callout_hold         => lazy close connection
   se_mailfrom         MAIL FROM address for sender verify; NULL => ""
   pm_mailfrom         if non-NULL, do the postmaster check with this sender
 
@@ -556,7 +557,10 @@ else
 if (cached_callout_lookup(addr, address_key, from_address,
       &options, &pm_mailfrom, &yield, failure_ptr,
       &new_domain_record, &old_domain_cache_result))
+  {
+  cancel_cutthrough_connection(TRUE, US"cache-hit");
   goto END_CALLOUT;
+  }
 
 if (!addr->transport)
   {
@@ -756,9 +760,12 @@ tls_retry_connection:
        }
 #endif
 
-      /* This would be ok for 1st rcpt of a cutthrough (XXX do we have a count?) , but no way to
-      handle a subsequent because of the RSET.  So refuse to support any. */
-      cancel_cutthrough_connection("random-recipient");
+      /* This would be ok for 1st rcpt of a cutthrough (the case handled here;
+      subsequents are done in cutthrough_multi()), but no way to
+      handle a subsequent because of the RSET vaporising the MAIL FROM.
+      So refuse to support any.  Most cutthrough use will not involve
+      random_local_part, so no loss. */
+      cancel_cutthrough_connection(TRUE, US"random-recipient");
 
       addr->address = string_sprintf("%s@%.1000s",
                                    random_local_part, rcpt_domain);
@@ -887,7 +894,7 @@ tls_retry_connection:
       /* Could possibly shift before main verify, just above, and be ok
       for cutthrough.  But no way to handle a subsequent rcpt, so just
       refuse any */
-      cancel_cutthrough_connection("postmaster verify");
+      cancel_cutthrough_connection(TRUE, US"postmaster verify");
       HDEBUG(D_acl|D_v) debug_printf_indent("Cutthrough cancelled by presence of postmaster verify\n");
 
       done = smtp_write_command(&sx.outblock, FALSE, "RSET\r\n") >= 0
@@ -1003,8 +1010,10 @@ no_conn:
 
     /* Cutthrough - on a successful 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 */
-    if (  cutthrough.delivery
+    here is where we want to leave the conn open.  Ditto for a lazy-close
+    verify. */
+
+    if (  (cutthrough.delivery || options & vopt_callout_hold)
        && rcpt_count == 1
        && done
        && yield == OK
@@ -1016,14 +1025,29 @@ no_conn:
        && !sx.lmtp
        )
       {
-      HDEBUG(D_acl|D_v) debug_printf_indent("holding verify callout open for cutthrough delivery\n");
-
-      cutthrough.fd = sx.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;
+      HDEBUG(D_acl|D_v) debug_printf_indent("holding verify callout open for %s\n",
+       cutthrough.delivery
+       ? "cutthrough delivery" : "potential further verifies and delivery");
+
+      cutthrough.callout_hold_only = !cutthrough.delivery;
+      cutthrough.is_tls =      tls_out.active >= 0;
+      cutthrough.fd =  sx.outblock.sock;       /* We assume no buffer in use in the outblock */
+      cutthrough.nrcpt =       1;
+      cutthrough.transport =   addr->transport->name;
+      cutthrough.interface =   interface;
+      cutthrough.snd_port =    sending_port;
+      cutthrough.peer_options =        smtp_peer_options;
+      cutthrough.host =                *host;
+       {
+       int oldpool = store_pool;
+       store_pool = POOL_PERM;
+       cutthrough.snd_ip = string_copy(sending_ip_address);
+       cutthrough.host.name = string_copy(host->name);
+       cutthrough.host.address = string_copy(host->address);
+       store_pool = oldpool;
+       }
+      cutthrough.addr =                *addr;          /* Save the address_item for later logging */
+      cutthrough.addr.next =   NULL;
       cutthrough.addr.host_used = &cutthrough.host;
       if (addr->parent)
         *(cutthrough.addr.parent = store_get(sizeof(address_item))) =
@@ -1036,9 +1060,9 @@ no_conn:
       }
     else
       {
-      /* Ensure no cutthrough on multiple address verifies */
+      /* Ensure no cutthrough on multiple verifies that were incompatible */
       if (options & vopt_callout_recipsender)
-        cancel_cutthrough_connection("not usable for cutthrough");
+        cancel_cutthrough_connection(TRUE, US"not usable for cutthrough");
       if (sx.send_quit)
        {
        (void) smtp_write_command(&sx.outblock, FALSE, "QUIT\r\n");
@@ -1184,20 +1208,26 @@ return TRUE;
 }
 
 /* Buffered output of counted data block.   Return boolean success */
-BOOL
+static BOOL
 cutthrough_puts(uschar * cp, int n)
 {
 if (cutthrough.fd < 0)       return TRUE;
 if (_cutthrough_puts(cp, n)) return TRUE;
-cancel_cutthrough_connection("transmit failed");
+cancel_cutthrough_connection(TRUE, US"transmit failed");
 return FALSE;
 }
 
+BOOL
+cutthrough_data_puts(uschar * cp, int n)
+{
+if (cutthrough.delivery) cutthrough_puts(cp, n);
+}
+
 
 static BOOL
 _cutthrough_flush_send(void)
 {
-int n= ctblock.ptr-ctblock.buffer;
+int n = ctblock.ptr - ctblock.buffer;
 
 if(n>0)
   if(!cutthrough_send(n))
@@ -1211,21 +1241,28 @@ BOOL
 cutthrough_flush_send(void)
 {
 if (_cutthrough_flush_send()) return TRUE;
-cancel_cutthrough_connection("transmit failed");
+cancel_cutthrough_connection(TRUE, US"transmit failed");
 return FALSE;
 }
 
 
-BOOL
+static BOOL
 cutthrough_put_nl(void)
 {
 return cutthrough_puts(US"\r\n", 2);
 }
 
 
+BOOL
+cutthrough_data_put_nl(void)
+{
+return cutthrough_data_puts(US"\r\n", 2);
+}
+
+
 /* Get and check response from cutthrough target */
 static uschar
-cutthrough_response(char expect, uschar ** copy, int timeout)
+cutthrough_response(int fd, char expect, uschar ** copy, int timeout)
 {
 smtp_inblock inblock;
 uschar inbuffer[4096];
@@ -1235,12 +1272,12 @@ inblock.buffer = inbuffer;
 inblock.buffersize = sizeof(inbuffer);
 inblock.ptr = inbuffer;
 inblock.ptrend = inbuffer;
-inblock.sock = cutthrough.fd;
+inblock.sock = fd;
 /* this relies on (inblock.sock == tls_out.active) */
 if(!smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), expect, timeout))
-  cancel_cutthrough_connection("target timeout on read");
+  cancel_cutthrough_connection(TRUE, US"target timeout on read");
 
-if(copy != NULL)
+if(copy)
   {
   uschar * cp;
   *copy = cp = string_copy(responsebuffer);
@@ -1258,7 +1295,7 @@ return responsebuffer[0];
 BOOL
 cutthrough_predata(void)
 {
-if(cutthrough.fd < 0)
+if(cutthrough.fd < 0 || cutthrough.callout_hold_only)
   return FALSE;
 
 HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP>> DATA\n");
@@ -1266,7 +1303,7 @@ cutthrough_puts(US"DATA\r\n", 6);
 cutthrough_flush_send();
 
 /* Assume nothing buffered.  If it was it gets ignored. */
-return cutthrough_response('3', NULL, CUTTHROUGH_DATA_TIMEOUT) == '3';
+return cutthrough_response(cutthrough.fd, '3', NULL, CUTTHROUGH_DATA_TIMEOUT) == '3';
 }
 
 
@@ -1293,7 +1330,7 @@ cutthrough_headers_send(void)
 {
 transport_ctx tctx;
 
-if(cutthrough.fd < 0)
+if(cutthrough.fd < 0 || cutthrough.callout_hold_only)
   return FALSE;
 
 /* We share a routine with the mainline transport to handle header add/remove/rewrites,
@@ -1318,7 +1355,8 @@ return TRUE;
 static void
 close_cutthrough_connection(const char * why)
 {
-if(cutthrough.fd >= 0)
+int fd = cutthrough.fd;
+if(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
@@ -1328,26 +1366,36 @@ if(cutthrough.fd >= 0)
   HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP>> QUIT\n");
   _cutthrough_puts(US"QUIT\r\n", 6);   /* avoid recursion */
   _cutthrough_flush_send();
+  cutthrough.fd = -1;                  /* avoid recursion via read timeout */
 
   /* Wait a short time for response, and discard it */
-  cutthrough_response('2', NULL, 1);
+  cutthrough_response(fd, '2', NULL, 1);
 
-  #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
   tls_close(FALSE, TRUE);
-  #endif
+#endif
   HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP(close)>>\n");
-  (void)close(cutthrough.fd);
-  cutthrough.fd = -1;
+  (void)close(fd);
   HDEBUG(D_acl) debug_printf_indent("----------- cutthrough shutdown (%s) ------------\n", why);
   }
 ctblock.ptr = ctbuffer;
 }
 
 void
-cancel_cutthrough_connection(const char * why)
+cancel_cutthrough_connection(BOOL close_noncutthrough_verifies, const uschar * why)
+{
+if (cutthrough.delivery || close_noncutthrough_verifies)
+  close_cutthrough_connection(why);
+cutthrough.delivery = cutthrough.callout_hold_only = FALSE;
+}
+
+
+void
+release_cutthrough_connection(const uschar * why)
 {
-close_cutthrough_connection(why);
-cutthrough.delivery = FALSE;
+HDEBUG(D_acl) debug_printf_indent("release cutthrough conn: %s\n", why);
+cutthrough.fd = -1;
+cutthrough.delivery = cutthrough.callout_hold_only = FALSE;
 }
 
 
@@ -1372,7 +1420,7 @@ if(  !cutthrough_puts(US".", 1)
   )
   return cutthrough.addr.message;
 
-res = cutthrough_response('2', &cutthrough.addr.message, CUTTHROUGH_DATA_TIMEOUT);
+res = cutthrough_response(cutthrough.fd, '2', &cutthrough.addr.message, CUTTHROUGH_DATA_TIMEOUT);
 for (addr = &cutthrough.addr; addr; addr = addr->next)
   {
   addr->message = cutthrough.addr.message;
@@ -1879,7 +1927,7 @@ while (addr_new)
         }
       respond_printf(f, "%s\n", cr);
       }
-    cancel_cutthrough_connection("routing hard fail");
+    cancel_cutthrough_connection(TRUE, US"routing hard fail");
 
     if (!full_info)
       {
@@ -1918,7 +1966,7 @@ while (addr_new)
         }
       respond_printf(f, "%s\n", cr);
       }
-    cancel_cutthrough_connection("routing soft fail");
+    cancel_cutthrough_connection(TRUE, US"routing soft fail");
 
     if (!full_info)
       {
@@ -1991,7 +2039,7 @@ while (addr_new)
       /* If stopped because more than one new address, cannot cutthrough */
 
       if (addr_new && addr_new->next)
-       cancel_cutthrough_connection("multiple addresses from routing");
+       cancel_cutthrough_connection(TRUE, US"multiple addresses from routing");
 
       yield = OK;
       goto out;
@@ -2241,18 +2289,16 @@ verify_check_header_names_ascii(uschar **msgptr)
 header_line *h;
 uschar *colon, *s;
 
-for (h = header_list; h != NULL; h = h->next)
+for (h = header_list; h; h = h->next)
   {
-   colon = Ustrchr(h->text, ':');
-   for(s = h->text; s < colon; s++)
-     {
-        if ((*s < 33) || (*s > 126))
-        {
-                *msgptr = string_sprintf("Invalid character in header \"%.*s\" found",
-                                         colon - h->text, h->text);
-                return FAIL;
-        }
-     }
+  colon = Ustrchr(h->text, ':');
+  for(s = h->text; s < colon; s++)
+    if ((*s < 33) || (*s > 126))
+      {
+      *msgptr = string_sprintf("Invalid character in header \"%.*s\" found",
+                            colon - h->text, h->text);
+      return FAIL;
+      }
   }
 return OK;
 }
diff --git a/test/confs/0580 b/test/confs/0580
new file mode 100644 (file)
index 0000000..54ef7ae
--- /dev/null
@@ -0,0 +1,46 @@
+# Exim test configuration 0580
+
+OPT =
+
+.include DIR/aux-var/std_conf_prefix
+
+primary_hostname = myhost.test.ex
+
+# ----- Main settings -----
+
+acl_smtp_rcpt = check_rcpt
+
+log_selector = +received_recipients
+OPT
+
+# ----- ACLs -----
+
+begin acl
+
+check_rcpt:
+  accept  verify = recipient/callout=use_sender,hold
+
+
+# ----- Routers -----
+
+begin routers
+
+r1:
+  driver = manualroute
+  route_list = * 127.0.0.1
+  self = send
+  transport = t1
+
+
+begin transports
+
+t1:
+  driver = smtp
+  port =   PORT_S
+
+
+# ----- Retry -----
+begin retry
+
+* * F,5d,10s
+# End
diff --git a/test/confs/0581 b/test/confs/0581
new file mode 120000 (symlink)
index 0000000..35e8a25
--- /dev/null
@@ -0,0 +1 @@
+0580
\ No newline at end of file
diff --git a/test/confs/0582 b/test/confs/0582
new file mode 120000 (symlink)
index 0000000..35e8a25
--- /dev/null
@@ -0,0 +1 @@
+0580
\ No newline at end of file
diff --git a/test/confs/2035 b/test/confs/2035
new file mode 100644 (file)
index 0000000..a10dc53
--- /dev/null
@@ -0,0 +1,84 @@
+# Exim test configuration 2035
+
+OPT =
+
+.include DIR/aux-var/tls_conf_prefix
+
+.ifdef SERVER
+tls_certificate = DIR/aux-fixed/exim-ca/example.com/server1.example.com/server1.example.com.chain.pem
+tls_privatekey = DIR/aux-fixed/exim-ca/example.com/server1.example.com/server1.example.com.unlocked.key
+.else
+tls_advertise_hosts =
+.endif
+
+primary_hostname = myhost.test.ex
+
+# ----- Main settings -----
+
+acl_smtp_rcpt = check_rcpt
+acl_smtp_data = check_data
+
+log_selector = +received_recipients +outgoing_port
+OPT
+
+# ----- ACLs -----
+
+begin acl
+
+check_rcpt:
+  accept
+       condition =     ${if or { {!eq {SERVER}{server}} {= {$received_port}{PORT_S}} }}
+       verify =        recipient/callout=use_sender,hold
+  defer        condition =     ${if eq {SERVER}{server}}
+       local_parts =   rcpt_defer
+  accept
+
+check_data:
+  warn logwrite = $message_exim_id received on port $received_port
+  defer        condition =     ${if eq {SERVER}{server}}
+       condition =     ${if eq {data_defer}{${local_part:$recipients}}}
+  accept
+
+# ----- Routers -----
+
+begin routers
+
+.ifdef SERVER
+
+target:
+  driver = redirect
+  condition = ${if = {$received_port}{PORT_D}}
+  data = :blackhole:
+
+dut:
+  driver = manualroute
+  route_list = * 127.0.0.1
+  self = send
+  transport = t1
+
+.else
+
+client:
+  driver = manualroute
+  route_list = * 127.0.0.1
+  self = send
+  transport = t1
+  errors_to = ""
+
+.endif
+
+
+begin transports
+
+t1:
+  driver = smtp
+  port =   PORT_D
+  tls_verify_certificates = DIR/aux-fixed/exim-ca/example.com/CA/CA.pem
+  tls_verify_cert_hostnames = :
+
+
+# ----- Retry -----
+begin retry
+
+* * F,5d,10s
+# End
diff --git a/test/confs/2036 b/test/confs/2036
new file mode 120000 (symlink)
index 0000000..3f3e22d
--- /dev/null
@@ -0,0 +1 @@
+2035
\ No newline at end of file
diff --git a/test/confs/2037 b/test/confs/2037
new file mode 120000 (symlink)
index 0000000..3f3e22d
--- /dev/null
@@ -0,0 +1 @@
+2035
\ No newline at end of file
diff --git a/test/confs/2135 b/test/confs/2135
new file mode 100644 (file)
index 0000000..fcb5f00
--- /dev/null
@@ -0,0 +1,85 @@
+# Exim test configuration 2135
+
+OPT =
+
+.include DIR/aux-var/tls_conf_prefix
+
+.ifdef SERVER
+tls_certificate = DIR/aux-fixed/exim-ca/example.com/server1.example.com/server1.example.com.chain.pem
+tls_privatekey = DIR/aux-fixed/exim-ca/example.com/server1.example.com/server1.example.com.unlocked.key
+.else
+tls_advertise_hosts =
+.endif
+
+primary_hostname = myhost.test.ex
+
+# ----- Main settings -----
+
+acl_smtp_rcpt = check_rcpt
+acl_smtp_data = check_data
+
+log_selector = +received_recipients +outgoing_port
+OPT
+
+# ----- ACLs -----
+
+begin acl
+
+check_rcpt:
+  accept
+       condition =     ${if or { {!eq {SERVER}{server}} {= {$received_port}{PORT_S}} }}
+       verify =        recipient/callout=use_sender,hold
+  defer        condition =     ${if eq {SERVER}{server}}
+       local_parts =   rcpt_defer
+  accept
+
+check_data:
+  warn logwrite = $message_exim_id received on port $received_port
+  defer        condition =     ${if eq {SERVER}{server}}
+       condition =     ${if eq {data_defer}{${local_part:$recipients}}}
+  accept
+
+# ----- Routers -----
+
+begin routers
+
+.ifdef SERVER
+
+target:
+  driver = redirect
+  condition = ${if = {$received_port}{PORT_D}}
+  data = :blackhole:
+
+dut:
+  driver = manualroute
+  route_list = * 127.0.0.1
+  self = send
+  transport = t1
+
+.else
+
+client:
+  driver = manualroute
+  route_list = * 127.0.0.1
+  self = send
+  transport = t1
+  errors_to = ""
+
+.endif
+
+
+begin transports
+
+t1:
+  driver = smtp
+  port =   PORT_D
+  tls_verify_certificates = DIR/aux-fixed/exim-ca/example.com/CA/CA.pem
+  tls_verify_cert_hostnames = :
+
+
+# ----- Retry -----
+begin retry
+
+* * F,5d,10s
+
+# End
diff --git a/test/confs/2136 b/test/confs/2136
new file mode 120000 (symlink)
index 0000000..b9dc6e9
--- /dev/null
@@ -0,0 +1 @@
+2135
\ No newline at end of file
diff --git a/test/confs/2137 b/test/confs/2137
new file mode 120000 (symlink)
index 0000000..b9dc6e9
--- /dev/null
@@ -0,0 +1 @@
+2135
\ No newline at end of file
diff --git a/test/log/0580 b/test/log/0580
new file mode 100644 (file)
index 0000000..f006778
--- /dev/null
@@ -0,0 +1,15 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-smtp S=sss for usery@test.ex
+1999-03-02 09:44:33 10HmaX-0005vi-00 => usery@test.ex R=r1 T=t1 H=127.0.0.1 [127.0.0.1] C="250 yeah got that message"
+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-smtp S=sss for usery@test.ex usery2@test.ex
+1999-03-02 09:44:33 10HmaY-0005vi-00 => usery@test.ex R=r1 T=t1 H=127.0.0.1 [127.0.0.1] C="250 yeah got that message"
+1999-03-02 09:44:33 10HmaY-0005vi-00 -> usery2@test.ex R=r1 T=t1 H=127.0.0.1 [127.0.0.1] C="250 yeah got that message"
+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-smtp S=sss for usery3@test.ex usery@test.ex
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => usery3@test.ex R=r1 T=t1 H=127.0.0.1 [127.0.0.1] C="250 yeah got that message"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 -> usery@test.ex R=r1 T=t1 H=127.0.0.1 [127.0.0.1] C="250 yeah got that message"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-smtp S=sss for usery4@test.ex usery5@test.ex
+1999-03-02 09:44:33 10HmbA-0005vi-00 => usery4@test.ex R=r1 T=t1 H=127.0.0.1 [127.0.0.1] C="250 yeah got that message"
+1999-03-02 09:44:33 10HmbA-0005vi-00 -> usery5@test.ex R=r1 T=t1 H=127.0.0.1 [127.0.0.1] C="250 yeah got that message"
+1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
diff --git a/test/log/0581 b/test/log/0581
new file mode 100644 (file)
index 0000000..f554e00
--- /dev/null
@@ -0,0 +1,23 @@
+
+******** 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 10HmaX-0005vi-00 <= userc@ok.example H=(test.ex) [127.0.0.1] P=esmtp S=sss for userd@test.ex
+1999-03-02 09:44:33 10HmaX-0005vi-00 => userd@test.ex R=r1 T=t1 H=127.0.0.1 [127.0.0.1] C="250 yeah got that message"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+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 <= usere@ok.example H=(test.ex) [127.0.0.1] P=esmtp S=sss for userf@test.ex
+1999-03-02 09:44:33 10HmaY-0005vi-00 => userf@test.ex R=r1 T=t1 H=127.0.0.1 [127.0.0.1] C="250 yeah got that message"
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+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 10HmaZ-0005vi-00 <= userc@ok.example H=(test.ex) [127.0.0.1] P=esmtp S=sss for userd@test.ex userd2@test.ex
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => userd@test.ex R=r1 T=t1 H=127.0.0.1 [127.0.0.1] C="250 yeah got that message"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 -> userd2@test.ex R=r1 T=t1 H=127.0.0.1 [127.0.0.1] C="250 yeah got that message"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= userc@ok.example H=(test.ex) [127.0.0.1] P=esmtp S=sss for userd3@test.ex userd2@test.ex
+1999-03-02 09:44:33 10HmbA-0005vi-00 => userd3@test.ex R=r1 T=t1 H=127.0.0.1 [127.0.0.1] C="250 yeah got that message"
+1999-03-02 09:44:33 10HmbA-0005vi-00 -> userd2@test.ex R=r1 T=t1 H=127.0.0.1 [127.0.0.1] C="250 yeah got that message"
+1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
+1999-03-02 09:44:33 10HmbB-0005vi-00 <= userc@ok.example H=(test.ex) [127.0.0.1] P=esmtp S=sss for userd4@test.ex userd5@test.ex
+1999-03-02 09:44:33 10HmbB-0005vi-00 => userd4@test.ex R=r1 T=t1 H=127.0.0.1 [127.0.0.1] C="250 yeah got that message"
+1999-03-02 09:44:33 10HmbB-0005vi-00 -> userd5@test.ex R=r1 T=t1 H=127.0.0.1 [127.0.0.1] C="250 yeah got that message"
+1999-03-02 09:44:33 10HmbB-0005vi-00 Completed
diff --git a/test/log/0582 b/test/log/0582
new file mode 100644 (file)
index 0000000..8953064
--- /dev/null
@@ -0,0 +1,3 @@
+1999-03-02 09:44:33 U=CALLER F=<userg@ok.example> temporarily rejected RCPT <userg@test.ex>: Could not complete recipient verify callout: 127.0.0.1 [127.0.0.1] : SMTP error from remote mail server after RCPT TO:<userg@test.ex>: 451 not right now
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-smtp S=sss for userh@test.ex
+1999-03-02 09:44:33 10HmaX-0005vi-00 == userh@test.ex R=r1 T=t1 defer (-46) H=127.0.0.1 [127.0.0.1]: SMTP error from remote mail server after end of data: 451 not right now
diff --git a/test/log/2035 b/test/log/2035
new file mode 100644 (file)
index 0000000..13c6c83
--- /dev/null
@@ -0,0 +1,11 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 10HmaX-0005vi-00 received on port 0
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-smtp S=sss for userb@test.ex
+1999-03-02 09:44:33 10HmaX-0005vi-00 => userb@test.ex R=client T=t1 H=127.0.0.1 [127.0.0.1]:1225 X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 CV=no C="250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-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 10HmaY-0005vi-00 received on port 1225
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= usera@ok.example H=localhost (myhost.test.ex) [127.0.0.1] P=esmtps X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 CV=no S=sss id=E10HmaX-0005vi-00@myhost.test.ex for userb@test.ex
+1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: <userb@test.ex> R=target
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
diff --git a/test/log/2036 b/test/log/2036
new file mode 100644 (file)
index 0000000..b2cbc2c
--- /dev/null
@@ -0,0 +1,20 @@
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225 port 1224
+1999-03-02 09:44:33 10HmaX-0005vi-00 10HmaX-0005vi-00 received on port 1224
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= userc@ok.example H=(test.ex) [127.0.0.1] P=esmtp S=sss for userd@test.ex
+1999-03-02 09:44:33 10HmaY-0005vi-00 10HmaY-0005vi-00 received on port 1225
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= userc@ok.example H=localhost (myhost.test.ex) [127.0.0.1] P=esmtps X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 CV=no S=sss for userd@test.ex
+1999-03-02 09:44:33 10HmaX-0005vi-00 => userd@test.ex R=dut T=t1 H=127.0.0.1 [127.0.0.1]:1225 X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 CV=no C="250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: <userd@test.ex> R=target
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225 port 1224
+1999-03-02 09:44:33 10HmaZ-0005vi-00 10HmaZ-0005vi-00 received on port 1224
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= usere@ok.example H=(test.ex) [127.0.0.1] P=esmtp S=sss for userf@test.ex
+1999-03-02 09:44:33 10HmbA-0005vi-00 10HmbA-0005vi-00 received on port 1225
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= usere@ok.example H=localhost (myhost.test.ex) [127.0.0.1] P=esmtps X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 CV=no S=sss for userf@test.ex
+1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: <userf@test.ex> R=target
+1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => userf@test.ex R=dut T=t1 H=127.0.0.1 [127.0.0.1]:1225 C="250 OK id=10HmbA-0005vi-00"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
diff --git a/test/log/2037 b/test/log/2037
new file mode 100644 (file)
index 0000000..896dcf6
--- /dev/null
@@ -0,0 +1,10 @@
+1999-03-02 09:44:33 U=CALLER F=<userg@ok.example> temporarily rejected RCPT <rcpt_defer@test.ex>: Could not complete recipient verify callout: 127.0.0.1 [127.0.0.1] : SMTP error from remote mail server after RCPT TO:<rcpt_defer@test.ex>: 451 Temporary local problem - please try later
+1999-03-02 09:44:33 10HmaY-0005vi-00 10HmaY-0005vi-00 received on port 0
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-smtp S=sss for data_defer@test.ex
+1999-03-02 09:44:33 10HmaY-0005vi-00 == data_defer@test.ex R=client T=t1 defer (-46) H=127.0.0.1 [127.0.0.1]:1111: SMTP error from remote mail server after end of data: 451 Temporary local problem - please try later
+
+******** 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 H=localhost (myhost.test.ex) [127.0.0.1] X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 CV=no F=<userg@ok.example> temporarily rejected RCPT <rcpt_defer@test.ex>
+1999-03-02 09:44:33 10HmaX-0005vi-00 10HmaX-0005vi-00 received on port 1225
+1999-03-02 09:44:33 10HmaX-0005vi-00 H=localhost (myhost.test.ex) [127.0.0.1] X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 CV=no F=<userh@ok.example> temporarily rejected after DATA
diff --git a/test/log/2135 b/test/log/2135
new file mode 100644 (file)
index 0000000..d2a01cd
--- /dev/null
@@ -0,0 +1,11 @@
+1999-03-02 09:44:33 10HmaX-0005vi-00 10HmaX-0005vi-00 received on port 0
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-smtp S=sss for userb@test.ex
+1999-03-02 09:44:33 10HmaX-0005vi-00 => userb@test.ex R=client T=t1 H=127.0.0.1 [127.0.0.1]:1225 X=TLSv1:AES256-SHA:256 CV=no C="250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-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 10HmaY-0005vi-00 received on port 1225
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= usera@ok.example H=localhost (myhost.test.ex) [127.0.0.1] P=esmtps X=TLSv1:AES256-SHA:256 CV=no S=sss id=E10HmaX-0005vi-00@myhost.test.ex for userb@test.ex
+1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: <userb@test.ex> R=target
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
diff --git a/test/log/2136 b/test/log/2136
new file mode 100644 (file)
index 0000000..bfd9e31
--- /dev/null
@@ -0,0 +1,20 @@
+
+******** SERVER ********
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225 port 1224
+1999-03-02 09:44:33 10HmaX-0005vi-00 10HmaX-0005vi-00 received on port 1224
+1999-03-02 09:44:33 10HmaX-0005vi-00 <= userc@ok.example H=(test.ex) [127.0.0.1] P=esmtp S=sss for userd@test.ex
+1999-03-02 09:44:33 10HmaY-0005vi-00 10HmaY-0005vi-00 received on port 1225
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= userc@ok.example H=localhost (myhost.test.ex) [127.0.0.1] P=esmtps X=TLSv1:AES256-SHA:256 CV=no S=sss for userd@test.ex
+1999-03-02 09:44:33 10HmaX-0005vi-00 => userd@test.ex R=dut T=t1 H=127.0.0.1 [127.0.0.1]:1225 X=TLSv1:AES256-SHA:256 CV=no C="250 OK id=10HmaY-0005vi-00"
+1999-03-02 09:44:33 10HmaX-0005vi-00 Completed
+1999-03-02 09:44:33 10HmaY-0005vi-00 => :blackhole: <userd@test.ex> R=target
+1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225 port 1224
+1999-03-02 09:44:33 10HmaZ-0005vi-00 10HmaZ-0005vi-00 received on port 1224
+1999-03-02 09:44:33 10HmaZ-0005vi-00 <= usere@ok.example H=(test.ex) [127.0.0.1] P=esmtp S=sss for userf@test.ex
+1999-03-02 09:44:33 10HmbA-0005vi-00 10HmbA-0005vi-00 received on port 1225
+1999-03-02 09:44:33 10HmbA-0005vi-00 <= usere@ok.example H=localhost (myhost.test.ex) [127.0.0.1] P=esmtps X=TLSv1:AES256-SHA:256 CV=no S=sss for userf@test.ex
+1999-03-02 09:44:33 10HmbA-0005vi-00 => :blackhole: <userf@test.ex> R=target
+1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
+1999-03-02 09:44:33 10HmaZ-0005vi-00 => userf@test.ex R=dut T=t1 H=127.0.0.1 [127.0.0.1]:1225 C="250 OK id=10HmbA-0005vi-00"
+1999-03-02 09:44:33 10HmaZ-0005vi-00 Completed
diff --git a/test/log/2137 b/test/log/2137
new file mode 100644 (file)
index 0000000..bef7dce
--- /dev/null
@@ -0,0 +1,10 @@
+1999-03-02 09:44:33 U=CALLER F=<> temporarily rejected RCPT <rcpt_defer@test.ex>: Could not complete recipient verify callout: 127.0.0.1 [127.0.0.1] : SMTP error from remote mail server after RCPT TO:<rcpt_defer@test.ex>: 451 Temporary local problem - please try later
+1999-03-02 09:44:33 10HmaY-0005vi-00 10HmaY-0005vi-00 received on port 0
+1999-03-02 09:44:33 10HmaY-0005vi-00 <= CALLER@myhost.test.ex U=CALLER P=local-smtp S=sss for data_defer@test.ex
+1999-03-02 09:44:33 10HmaY-0005vi-00 == data_defer@test.ex R=client T=t1 defer (-46) H=127.0.0.1 [127.0.0.1]:1111: SMTP error from remote mail server after end of data: 451 Temporary local problem - please try later
+
+******** 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 H=localhost (myhost.test.ex) [127.0.0.1] X=TLSv1:AES256-SHA:256 CV=no F=<> temporarily rejected RCPT <rcpt_defer@test.ex>
+1999-03-02 09:44:33 10HmaX-0005vi-00 10HmaX-0005vi-00 received on port 1225
+1999-03-02 09:44:33 10HmaX-0005vi-00 H=localhost (myhost.test.ex) [127.0.0.1] X=TLSv1:AES256-SHA:256 CV=no F=<> temporarily rejected after DATA
diff --git a/test/rejectlog/0582 b/test/rejectlog/0582
new file mode 100644 (file)
index 0000000..614c294
--- /dev/null
@@ -0,0 +1 @@
+1999-03-02 09:44:33 U=CALLER F=<userg@ok.example> temporarily rejected RCPT <userg@test.ex>: Could not complete recipient verify callout: 127.0.0.1 [127.0.0.1] : SMTP error from remote mail server after RCPT TO:<userg@test.ex>: 451 not right now
diff --git a/test/rejectlog/2037 b/test/rejectlog/2037
new file mode 100644 (file)
index 0000000..19259d5
--- /dev/null
@@ -0,0 +1,21 @@
+1999-03-02 09:44:33 U=CALLER F=<userg@ok.example> temporarily rejected RCPT <rcpt_defer@test.ex>: Could not complete recipient verify callout: 127.0.0.1 [127.0.0.1] : SMTP error from remote mail server after RCPT TO:<rcpt_defer@test.ex>: 451 Temporary local problem - please try later
+
+******** SERVER ********
+1999-03-02 09:44:33 H=localhost (myhost.test.ex) [127.0.0.1] X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 CV=no F=<userg@ok.example> temporarily rejected RCPT <rcpt_defer@test.ex>
+1999-03-02 09:44:33 10HmaX-0005vi-00 H=localhost (myhost.test.ex) [127.0.0.1] X=TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256 CV=no F=<userh@ok.example> temporarily rejected after DATA
+Envelope-from: <userh@ok.example>
+Envelope-to: <data_defer@test.ex>
+P Received: from localhost ([127.0.0.1] helo=myhost.test.ex)
+       by myhost.test.ex with esmtps (TLS1.x:xxxxRSA_AES_256_CBC_SHAnnn:256)
+       (Exim x.yz)
+       (envelope-from <userh@ok.example>)
+       id 10HmaX-0005vi-00
+       for data_defer@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
+P Received: from CALLER by myhost.test.ex with local-smtp (Exim x.yz)
+       (envelope-from <CALLER@myhost.test.ex>)
+       id 10HmaY-0005vi-00
+       for data_defer@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
+  Subject: test
+I Message-Id: <E10HmaY-0005vi-00@myhost.test.ex>
+F From: CALLER_NAME <CALLER@myhost.test.ex>
+  Date: Tue, 2 Mar 1999 09:44:33 +0000
diff --git a/test/rejectlog/2137 b/test/rejectlog/2137
new file mode 100644 (file)
index 0000000..6ec7345
--- /dev/null
@@ -0,0 +1,20 @@
+1999-03-02 09:44:33 U=CALLER F=<> temporarily rejected RCPT <rcpt_defer@test.ex>: Could not complete recipient verify callout: 127.0.0.1 [127.0.0.1] : SMTP error from remote mail server after RCPT TO:<rcpt_defer@test.ex>: 451 Temporary local problem - please try later
+
+******** SERVER ********
+1999-03-02 09:44:33 H=localhost (myhost.test.ex) [127.0.0.1] X=TLSv1:AES256-SHA:256 CV=no F=<> temporarily rejected RCPT <rcpt_defer@test.ex>
+1999-03-02 09:44:33 10HmaX-0005vi-00 H=localhost (myhost.test.ex) [127.0.0.1] X=TLSv1:AES256-SHA:256 CV=no F=<> temporarily rejected after DATA
+Envelope-from: <>
+Envelope-to: <data_defer@test.ex>
+P Received: from localhost ([127.0.0.1] helo=myhost.test.ex)
+       by myhost.test.ex with esmtps (TLSv1:AES256-SHA:256)
+       (Exim x.yz)
+       id 10HmaX-0005vi-00
+       for data_defer@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
+P Received: from CALLER by myhost.test.ex with local-smtp (Exim x.yz)
+       (envelope-from <CALLER@myhost.test.ex>)
+       id 10HmaY-0005vi-00
+       for data_defer@test.ex; Tue, 2 Mar 1999 09:44:33 +0000
+  Subject: test
+I Message-Id: <E10HmaY-0005vi-00@myhost.test.ex>
+F From: CALLER_NAME <CALLER@myhost.test.ex>
+  Date: Tue, 2 Mar 1999 09:44:33 +0000
diff --git a/test/scripts/0000-Basic/0580 b/test/scripts/0000-Basic/0580
new file mode 100644 (file)
index 0000000..8d8a117
--- /dev/null
@@ -0,0 +1,156 @@
+# callout lazy-close, -bs send
+need_ipv4
+#
+# a recipient verify and continued-delivery
+# cmdline -bs send
+server PORT_S
+220 Welcome
+EHLO
+250 Hi
+MAIL FROM
+250 OK
+RCPT TO
+250 OK
+DATA
+354 hit me
+.
+250 yeah got that message
+QUIT
+221 Bye
+****
+#
+exim -bs
+mail from:<userx@ok.example>
+rcpt to:<usery@test.ex>
+data
+Subject: test
+
+body
+.
+quit
+****
+sleep 1
+#
+#
+# multiple recipients
+# 1st callout result is cached (above); should not activate LCC
+server PORT_S 2
+220 Welcome
+EHLO
+250 Hi
+MAIL FROM
+250 OK
+RCPT TO
+250 OK
+QUIT
+221 Bye
+*eof
+220 Welcome
+EHLO
+250 Hi
+MAIL FROM
+250 OK
+RCPT TO
+250 OK
+RCPT TO
+250 OK
+DATA
+354 hit me
+.
+250 yeah got that message
+QUIT
+221 Bye
+****
+#
+exim -bs
+mail from:<userx@ok.example>
+rcpt to:<usery@test.ex>
+rcpt to:<usery2@test.ex>
+data
+Subject: test
+
+body
+.
+quit
+****
+sleep 1
+#
+#
+# 2nd callout result is cached (above); should not activate LCC
+server PORT_S 2
+220 Welcome
+EHLO
+250 Hi
+MAIL FROM
+250 OK
+RCPT TO
+250 OK
+QUIT
+221 Bye
+*eof
+220 Welcome
+EHLO
+250 Hi
+MAIL FROM
+250 OK
+RCPT TO
+250 OK
+RCPT TO
+250 OK
+DATA
+354 hit me
+.
+250 yeah got that message
+QUIT
+221 Bye
+****
+#
+exim -bs
+mail from:<userx@ok.example>
+rcpt to:<usery3@test.ex>
+rcpt to:<usery@test.ex>
+data
+Subject: test
+
+body
+.
+quit
+****
+sleep 1
+#
+#
+# no cache hits; should do LCC
+server PORT_S
+220 Welcome
+EHLO
+250 Hi
+MAIL FROM
+250 OK
+RCPT TO
+250 OK
+RCPT TO
+250 OK
+DATA
+354 hit me
+.
+250 yeah got that message
+QUIT
+221 Bye
+****
+#
+exim -bs
+mail from:<userx@ok.example>
+rcpt to:<usery4@test.ex>
+rcpt to:<usery5@test.ex>
+data
+Subject: test
+
+body
+.
+quit
+****
+sleep 1
+#
+#
+no_stdout_check
+no_msglog_check
diff --git a/test/scripts/0000-Basic/0581 b/test/scripts/0000-Basic/0581
new file mode 100644 (file)
index 0000000..f0ce3bc
--- /dev/null
@@ -0,0 +1,264 @@
+# callout lazy-close, smtp send
+need_ipv4
+#
+# a recipient verify and continued-delivery
+# smtp send
+server PORT_S
+220 Welcome
+EHLO
+250 Hi
+MAIL FROM
+250 OK
+RCPT TO
+250 OK
+DATA
+354 hit me
+.
+250 yeah got that message
+QUIT
+221 Bye
+****
+#
+exim -DSERVER=server -bd -oX PORT_D
+****
+#
+client 127.0.0.1 PORT_D
+??? 220
+EHLO test.ex
+??? 250-
+??? 250-
+??? 250-
+??? 250-
+??? 250 
+MAIL FROM:<userc@ok.example>
+??? 250
+RCPT TO:<userd@test.ex>
+??? 250
+DATA
+??? 354
+Subject: test
+
+body
+.
+??? 250
+QUIT
+??? 221
+****
+sleep 1
+killdaemon
+#
+#
+# smtp send, deliver_drop_priv
+server PORT_S
+220 Welcome
+EHLO
+250 Hi
+MAIL FROM
+250 OK
+RCPT TO
+250 OK
+DATA
+354 hit me
+.
+250 yeah got that message
+QUIT
+221 Bye
+****
+#
+exim -DSERVER=server -DOPT=deliver_drop_privilege -bd -oX PORT_D
+****
+#
+client 127.0.0.1 PORT_D
+??? 220
+EHLO test.ex
+??? 250-
+??? 250-
+??? 250-
+??? 250-
+??? 250 
+MAIL FROM:<usere@ok.example>
+??? 250
+RCPT TO:<userf@test.ex>
+??? 250
+DATA
+??? 354
+Subject: test
+
+body
+.
+??? 250
+QUIT
+??? 221
+****
+sleep 1
+killdaemon
+#
+#
+# multiple recipients
+# 1st callout result is cached (above); should not activate LCC
+# smtp send
+server PORT_S 2
+220 Welcome
+EHLO
+250 Hi
+MAIL FROM
+250 OK
+RCPT TO:<userd2@test.ex>
+250 OK
+QUIT
+221 Bye
+*eof
+220 Welcome
+EHLO
+250 Hi
+MAIL FROM
+250 OK
+RCPT TO:<userd@test.ex>
+250 OK
+RCPT TO:<userd2@test.ex>
+250 OK
+DATA
+354 hit me
+.
+250 yeah got that message
+QUIT
+221 Bye
+****
+#
+exim -DSERVER=server -bd -oX PORT_D
+****
+#
+client 127.0.0.1 PORT_D
+??? 220
+EHLO test.ex
+??? 250-
+??? 250-
+??? 250-
+??? 250-
+??? 250 
+MAIL FROM:<userc@ok.example>
+??? 250
+RCPT TO:<userd@test.ex>
+??? 250
+RCPT TO:<userd2@test.ex>
+??? 250
+DATA
+??? 354
+Subject: test
+
+body
+.
+??? 250
+QUIT
+??? 221
+****
+sleep 1
+#
+#
+# 2nd callout result is cached (above); should not activate LCC
+# smtp send
+server PORT_S 2
+220 Welcome
+EHLO
+250 Hi
+MAIL FROM
+250 OK
+RCPT TO:<userd3@test.ex>
+250 OK
+QUIT
+221 Bye
+*eof
+220 Welcome
+EHLO
+250 Hi
+MAIL FROM
+250 OK
+RCPT TO:<userd3@test.ex>
+250 OK
+RCPT TO:<userd2@test.ex>
+250 OK
+DATA
+354 hit me
+.
+250 yeah got that message
+QUIT
+221 Bye
+****
+#
+client 127.0.0.1 PORT_D
+??? 220
+EHLO test.ex
+??? 250-
+??? 250-
+??? 250-
+??? 250-
+??? 250 
+MAIL FROM:<userc@ok.example>
+??? 250
+RCPT TO:<userd3@test.ex>
+??? 250
+RCPT TO:<userd2@test.ex>
+??? 250
+DATA
+??? 354
+Subject: test
+
+body
+.
+??? 250
+QUIT
+??? 221
+****
+sleep 1
+#
+#
+# no cache hits; should do LCC
+# smtp send
+server PORT_S 2
+220 Welcome
+EHLO
+250 Hi
+MAIL FROM
+250 OK
+RCPT TO:<userd4@test.ex>
+250 OK
+RCPT TO:<userd5@test.ex>
+250 OK
+DATA
+354 hit me
+.
+250 yeah got that message
+QUIT
+221 Bye
+****
+#
+client 127.0.0.1 PORT_D
+??? 220
+EHLO test.ex
+??? 250-
+??? 250-
+??? 250-
+??? 250-
+??? 250 
+MAIL FROM:<userc@ok.example>
+??? 250
+RCPT TO:<userd4@test.ex>
+??? 250
+RCPT TO:<userd5@test.ex>
+??? 250
+DATA
+??? 354
+Subject: test
+
+body
+.
+??? 250
+QUIT
+??? 221
+****
+sleep 1
+killdaemon
+#
+#
+no_stdout_check
+no_msglog_check
diff --git a/test/scripts/0000-Basic/0582 b/test/scripts/0000-Basic/0582
new file mode 100644 (file)
index 0000000..3eefcc9
--- /dev/null
@@ -0,0 +1,56 @@
+# callout lazy-close, defers
+need_ipv4
+#
+# a recipient verify and continued-delivery
+# cmdline -bs send, rcpt-time defer
+server PORT_S
+220 Welcome
+EHLO
+250 Hi
+MAIL FROM
+250 OK
+RCPT TO
+451 not right now
+QUIT
+221 Bye
+****
+#
+exim -bs
+mail from:<userg@ok.example>
+rcpt to:<userg@test.ex>
+quit
+****
+sleep 1
+#
+# cmdline -bs send, data-time defer
+server PORT_S
+220 Welcome
+EHLO
+250 Hi
+MAIL FROM
+250 OK
+RCPT TO
+250 OK
+DATA
+354 hit me
+.
+451 not right now
+QUIT
+221 Bye
+****
+#
+exim -bs
+mail from:<userh@ok.example>
+rcpt to:<userh@test.ex>
+data
+Subject: test
+
+body
+.
+quit
+****
+sleep 1
+#
+#
+no_stdout_check
+no_msglog_check
diff --git a/test/scripts/2000-GnuTLS/2035 b/test/scripts/2000-GnuTLS/2035
new file mode 100644 (file)
index 0000000..cb22786
--- /dev/null
@@ -0,0 +1,26 @@
+# client: callout lazy-close, -bs send
+gnutls
+need_ipv4
+#
+# a tls-capable target for the verify/delivery connection
+exim -bd -DSERVER=server -oX PORT_D
+****
+#
+# a recipient verify and continued-delivery
+# cmdline -bs send
+exim -bs
+mail from:<usera@ok.example>
+rcpt to:<userb@test.ex>
+data
+Subject: test
+
+body
+.
+quit
+****
+sleep 1
+killdaemon
+#
+#
+no_stdout_check
+no_msglog_check
diff --git a/test/scripts/2000-GnuTLS/2036 b/test/scripts/2000-GnuTLS/2036
new file mode 100644 (file)
index 0000000..1891d2f
--- /dev/null
@@ -0,0 +1,70 @@
+# client: callout lazy-close, smtp send
+gnutls
+need_ipv4
+#
+# smtp send
+# a tls-capable target for the verify/delivery connection on PORT_D
+# plus a daemon under test on PORT_S
+exim -bd -DSERVER=server -oX PORT_D:PORT_S
+****
+#
+client 127.0.0.1 PORT_S
+??? 220
+EHLO test.ex
+??? 250-
+??? 250-
+??? 250-
+??? 250-
+??? 250-
+??? 250 
+MAIL FROM:<userc@ok.example>
+??? 250
+RCPT TO:<userd@test.ex>
+??? 250
+DATA
+??? 354
+Subject: test
+
+body
+.
+??? 250
+QUIT
+??? 221
+****
+sleep 1
+killdaemon
+#
+#
+# smtp send, deliver_drop_priv
+exim -bd -DSERVER=server -DOPT=deliver_drop_privilege -oX PORT_D:PORT_S
+****
+#
+client 127.0.0.1 PORT_S
+??? 220
+EHLO test.ex
+??? 250-
+??? 250-
+??? 250-
+??? 250-
+??? 250-
+??? 250 
+MAIL FROM:<usere@ok.example>
+??? 250
+RCPT TO:<userf@test.ex>
+??? 250
+DATA
+??? 354
+Subject: test
+
+body
+.
+??? 250
+QUIT
+??? 221
+****
+sleep 1
+killdaemon
+#
+#
+no_stdout_check
+no_msglog_check
diff --git a/test/scripts/2000-GnuTLS/2037 b/test/scripts/2000-GnuTLS/2037
new file mode 100644 (file)
index 0000000..8dca681
--- /dev/null
@@ -0,0 +1,31 @@
+# client: callout lazy-close, defers
+gnutls
+need_ipv4
+#
+exim -bd -DSERVER=server -oX PORT_D
+****
+# cmdline -bs send, rcpt-time defer
+exim -bs
+mail from:<userg@ok.example>
+rcpt to:<rcpt_defer@test.ex>
+quit
+****
+sleep 1
+#
+# cmdline -bs send, data-time defer
+exim -bs
+mail from:<userh@ok.example>
+rcpt to:<data_defer@test.ex>
+data
+Subject: test
+
+body
+.
+quit
+****
+sleep 1
+killdaemon
+#
+#
+no_stdout_check
+no_msglog_check
diff --git a/test/scripts/2100-OpenSSL/2135 b/test/scripts/2100-OpenSSL/2135
new file mode 100644 (file)
index 0000000..e521c45
--- /dev/null
@@ -0,0 +1,25 @@
+# client: callout lazy-close, -bs send
+need_ipv4
+#
+# a tls-capable target for the verify/delivery connection
+exim -bd -DSERVER=server -oX PORT_D
+****
+#
+# a recipient verify and continued-delivery
+# cmdline -bs send
+exim -bs
+mail from:<usera@ok.example>
+rcpt to:<userb@test.ex>
+data
+Subject: test
+
+body
+.
+quit
+****
+sleep 1
+killdaemon
+#
+#
+no_stdout_check
+no_msglog_check
diff --git a/test/scripts/2100-OpenSSL/2136 b/test/scripts/2100-OpenSSL/2136
new file mode 100644 (file)
index 0000000..aa13114
--- /dev/null
@@ -0,0 +1,71 @@
+# client: callout lazy-close, smtp send
+need_ipv4
+#
+# a recipient verify and continued-delivery
+# smtp send
+# a tls-capable target for the verify/delivery connection on PORT_D
+# plus a daemon under test on PORT_S
+exim -bd -DSERVER=server -oX PORT_D:PORT_S
+****
+#
+client 127.0.0.1 PORT_S
+??? 220
+EHLO test.ex
+??? 250-
+??? 250-
+??? 250-
+??? 250-
+??? 250-
+??? 250 
+MAIL FROM:<userc@ok.example>
+??? 250
+RCPT TO:<userd@test.ex>
+??? 250
+DATA
+??? 354
+Subject: test
+
+body
+.
+??? 250
+QUIT
+??? 221
+****
+sleep 1
+killdaemon
+#
+#
+# smtp send, deliver_drop_priv
+exim -bd -DSERVER=server -DOPT=deliver_drop_privilege -oX PORT_D:PORT_S
+****
+#
+client 127.0.0.1 PORT_S
+??? 220
+EHLO test.ex
+??? 250-
+??? 250-
+??? 250-
+??? 250-
+??? 250-
+??? 250 
+MAIL FROM:<usere@ok.example>
+??? 250
+RCPT TO:<userf@test.ex>
+??? 250
+DATA
+??? 354
+Subject: test
+
+body
+.
+??? 250
+QUIT
+??? 221
+****
+sleep 1
+killdaemon
+#
+#
+#
+no_stdout_check
+no_msglog_check
diff --git a/test/scripts/2100-OpenSSL/2137 b/test/scripts/2100-OpenSSL/2137
new file mode 100644 (file)
index 0000000..ec9a964
--- /dev/null
@@ -0,0 +1,30 @@
+# client: callout lazy-close, defers
+need_ipv4
+#
+exim -bd -DSERVER=server -oX PORT_D
+****
+# cmdline -bs send, rcpt-time defer
+exim -bs
+mail from:<>
+rcpt to:<rcpt_defer@test.ex>
+quit
+****
+sleep 1
+#
+# cmdline -bs send, data-time defer
+exim -bs
+mail from:<>
+rcpt to:<data_defer@test.ex>
+data
+Subject: test
+
+body
+.
+quit
+****
+sleep 1
+killdaemon
+#
+#
+no_stdout_check
+no_msglog_check
index 7c5a79e..ce72feb 100644 (file)
@@ -183,6 +183,7 @@ end of ACL "rcpt": ACCEPT
 >>
 LOG: MAIN
   <= ok@test3 H=[10.9.8.8] U=CALLER P=smtp S=sss
+release cutthrough conn: msg passed for delivery
 Exim version x.yz ....
 configuration file is TESTSUITE/test-config
 trusted user
index 5f76af0..3ec0da1 100644 (file)
@@ -163,6 +163,7 @@ LOG: MAIN
   <= CALLER@test.ex U=CALLER P=local S=sss
 created log directory TESTSUITE/spool/log
 search_tidyup called
+release cutthrough conn: msg passed for delivery
 exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -C TESTSUITE/test-config -d=0xfbb95cfd -odi -Mc 10HmaX-0005vi-00
 Exim version x.yz ....
 changed uid/gid: forcing real = effective
index 2d99672..0274527 100644 (file)
@@ -114,6 +114,7 @@ LOG: MAIN
   <= CALLER@test.ex U=CALLER P=local S=sss
 created log directory TESTSUITE/spool/log
 search_tidyup called
+release cutthrough conn: msg passed for delivery
 exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -C TESTSUITE/test-config -d=0xfbb95cfd -odi -Mc 10HmaX-0005vi-00
 Exim version x.yz ....
 changed uid/gid: forcing real = effective
index 2506e1c..d90d472 100644 (file)
@@ -56,6 +56,7 @@ Size of headers = sss
 LOG: MAIN
   <= CALLER@test.ex U=CALLER P=local S=sss
 search_tidyup called
+release cutthrough conn: msg passed for delivery
 exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -C TESTSUITE/test-config -d=0xfbb95cfd -N -odi -Mc 10HmaY-0005vi-00
 Exim version x.yz ....
 changed uid/gid: forcing real = effective
index 44e856d..0b980ac 100644 (file)
@@ -221,6 +221,7 @@ LOG: MAIN
   <= x@y H=[V4NET.11.12.13] U=CALLER P=smtp S=sss
 SMTP>> 250 OK id=10HmaX-0005vi-00
 search_tidyup called
+release cutthrough conn: msg passed for delivery
 exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -C TESTSUITE/test-config -d=0xfbb95cfd -odi -Mc 10HmaX-0005vi-00
 Exim version x.yz ....
 changed uid/gid: forcing real = effective
@@ -402,6 +403,7 @@ LOG: MAIN
   <= x@y H=[V4NET.11.12.13] U=CALLER P=smtp S=sss
 SMTP>> 250 OK id=10HmaY-0005vi-00
 search_tidyup called
+release cutthrough conn: msg passed for delivery
 exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -C TESTSUITE/test-config -d=0xfbb95cfd -odi -Mc 10HmaY-0005vi-00
 Exim version x.yz ....
 changed uid/gid: forcing real = effective
index 92b3f23..df772c3 100644 (file)
@@ -223,6 +223,7 @@ Size of headers = sss
 LOG: MAIN
   <= <> R=10HmaX-0005vi-00 U=EXIMUSER P=local S=sss
 search_tidyup called
+release cutthrough conn: msg passed for delivery
 exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -C TESTSUITE/test-config -d=0xebb95ced -odi -Mc 10HmaY-0005vi-00
 Exim version x.yz ....
 changed uid/gid: forcing real = effective
index a007de3..ea99dea 100644 (file)
@@ -174,6 +174,7 @@ LOG: MAIN
   <= CALLER@test.ex U=CALLER P=local S=sss
 created log directory TESTSUITE/spool/log
 search_tidyup called
+release cutthrough conn: msg passed for delivery
 exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -C TESTSUITE/test-config -d=0xfbb95dfd -odi -Mc 10HmaX-0005vi-00
 Exim version x.yz ....
 changed uid/gid: forcing real = effective
index 716cabd..32387a9 100644 (file)
@@ -47,6 +47,7 @@ LOG: MAIN
   <= CALLER@test.ex U=CALLER P=local S=sss
 created log directory TESTSUITE/spool/log
 search_tidyup called
+release cutthrough conn: msg passed for delivery
 exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -C TESTSUITE/test-config -d=0xfbb95cfd -N -odi -Mc 10HmaX-0005vi-00
 Exim version x.yz ....
 changed uid/gid: forcing real = effective
index d3d8f4c..96c42cb 100644 (file)
@@ -147,6 +147,7 @@ LOG: MAIN
   <= CALLER@test.ex U=CALLER P=local S=sss
 created log directory TESTSUITE/spool/log
 search_tidyup called
+release cutthrough conn: msg passed for delivery
 exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -C TESTSUITE/test-config -d=0xfbb95cfd -odi -Mc 10HmaX-0005vi-00
 Exim version x.yz ....
 changed uid/gid: forcing real = effective
@@ -1524,6 +1525,7 @@ Size of headers = sss
 LOG: MAIN
   <= <> R=10HmaX-0005vi-00 U=CALLER P=local S=sss
 search_tidyup called
+release cutthrough conn: msg passed for delivery
 exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -C TESTSUITE/test-config -d=0xfbb95cfd -odi -Mc 10HmaY-0005vi-00
 Exim version x.yz ....
 changed uid/gid: forcing real = effective
index f3d6d6b..e71299f 100644 (file)
@@ -47,6 +47,7 @@ LOG: MAIN
   <= CALLER@test.ex U=CALLER P=local S=sss
 created log directory TESTSUITE/spool/log
 search_tidyup called
+release cutthrough conn: msg passed for delivery
 exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -C TESTSUITE/test-config -d=0xfbb95cfd -odi -Mc 10HmaX-0005vi-00
 Exim version x.yz ....
 changed uid/gid: forcing real = effective
index 7319b67..c29883a 100644 (file)
@@ -74,6 +74,7 @@ LOG: MAIN
 created log directory TESTSUITE/spool/log
 SMTP>> 250 OK id=10HmaX-0005vi-00
 search_tidyup called
+release cutthrough conn: msg passed for delivery
 exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -C TESTSUITE/test-config -d=0xfbb95cfd -odi -Mc 10HmaX-0005vi-00
 Exim version x.yz ....
 changed uid/gid: forcing real = effective
index 4e99747..957092f 100644 (file)
@@ -335,6 +335,7 @@ LOG: MAIN
   <= CALLER@myhost.test.ex U=CALLER P=local S=sss
 created log directory TESTSUITE/spool/log
 search_tidyup called
+release cutthrough conn: msg passed for delivery
 exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -C TESTSUITE/test-config -d=0xfbb95cfd -odi -Mc 10HmaX-0005vi-00
 Exim version x.yz ....
 changed uid/gid: forcing real = effective
index ad5c9b3..62a0ef9 100644 (file)
@@ -49,6 +49,7 @@ LOG: MAIN
   <= CALLER@test.ex U=CALLER P=local S=sss
 created log directory TESTSUITE/spool/log
 search_tidyup called
+release cutthrough conn: msg passed for delivery
 exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -C TESTSUITE/test-config -d=0xfbb95cfd -odi -Mc 10HmaX-0005vi-00
 Exim version x.yz ....
 changed uid/gid: forcing real = effective
index 8b28b70..74f6648 100644 (file)
@@ -47,6 +47,7 @@ LOG: MAIN
   <= CALLER@test.ex U=CALLER P=local S=sss
 created log directory TESTSUITE/spool/log
 search_tidyup called
+release cutthrough conn: msg passed for delivery
 exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -C TESTSUITE/test-config -d=0xfbb95cfd -odi -Mc 10HmaX-0005vi-00
 Exim version x.yz ....
 changed uid/gid: forcing real = effective
@@ -235,6 +236,7 @@ Size of headers = sss
 LOG: MAIN
   <= CALLER@test.ex U=CALLER P=local S=sss
 search_tidyup called
+release cutthrough conn: msg passed for delivery
 exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -C TESTSUITE/test-config -d=0xfbb95cfd -odi -Mc 10HmaY-0005vi-00
 Exim version x.yz ....
 changed uid/gid: forcing real = effective
@@ -424,6 +426,7 @@ Size of headers = sss
 LOG: MAIN
   <= CALLER@test.ex U=CALLER P=local S=sss
 search_tidyup called
+release cutthrough conn: msg passed for delivery
 exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -C TESTSUITE/test-config -d=0xfbb95cfd -odi -Mc 10HmaZ-0005vi-00
 Exim version x.yz ....
 changed uid/gid: forcing real = effective
@@ -622,6 +625,7 @@ Size of headers = sss
 LOG: MAIN
   <= CALLER@test.ex U=CALLER P=local S=sss
 search_tidyup called
+release cutthrough conn: msg passed for delivery
 exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -C TESTSUITE/test-config -d=0xfbb95cfd -odi -Mc 10HmbA-0005vi-00
 Exim version x.yz ....
 changed uid/gid: forcing real = effective
index dce0d00..4211a18 100644 (file)
@@ -47,6 +47,7 @@ LOG: MAIN
   <= CALLER@test.ex U=CALLER P=local S=sss
 created log directory TESTSUITE/spool/log
 search_tidyup called
+release cutthrough conn: msg passed for delivery
 exec TESTSUITE/eximdir/exim -DEXIM_PATH=TESTSUITE/eximdir/exim -C TESTSUITE/test-config -d=0xfbb95cfd -odi -Mc 10HmaX-0005vi-00
 Exim version x.yz ....
 changed uid/gid: forcing real = effective