DANE: if all TLSA records are unusable, retry verification non-dane.
[exim.git] / src / src / verify.c
index db35aa5751c904b26edc120b688953b2a0ceb746..a22bb7d5b5172f6c506e25d4c5f15ce82a971def 100644 (file)
@@ -14,7 +14,6 @@ caching was contributed by Kevin Fleming (but I hacked it around a bit). */
 
 #define CUTTHROUGH_CMD_TIMEOUT  30     /* timeout for cutthrough-routing calls */
 #define CUTTHROUGH_DATA_TIMEOUT 60     /* timeout for cutthrough-routing calls */
-address_item cutthrough_addr;
 static smtp_outblock ctblock;
 uschar ctbuffer[8192];
 
@@ -39,6 +38,7 @@ static tree_node *dnsbl_cache = NULL;
 #define MT_NOT 1
 #define MT_ALL 2
 
+static uschar cutthrough_response(char, uschar **);
 
 
 /*************************************************
@@ -189,12 +189,12 @@ from_address = US"";
 
 if (is_recipient)
   {
-  if ((options & vopt_callout_recipsender) != 0)
+  if (options & vopt_callout_recipsender)
     {
     address_key = string_sprintf("%s/<%s>", addr->address, sender_address);
     from_address = sender_address;
     }
-  else if ((options & vopt_callout_recippmaster) != 0)
+  else if (options & vopt_callout_recippmaster)
     {
     address_key = string_sprintf("%s/<postmaster@%s>", addr->address,
       qualify_domain_sender);
@@ -410,6 +410,113 @@ else
 
   if (smtp_out != NULL && !disable_callout_flush) mac_smtp_fflush();
 
+/* cutthrough-multi: if a nonfirst rcpt has the same routing as the first,
+and we are holding a cutthrough conn open, we can just append the rcpt to
+that conn for verification purposes (and later delivery also).  Simplest
+coding means skipping this whole loop and doing the append separately.
+
+We will need to remember it has been appended so that rcpt-acl tail code
+can do it there for the non-rcpt-verify case.  For this we keep an addresscount.
+*/
+
+  /* Can we re-use an open cutthrough connection? */
+  if (  cutthrough.fd >= 0
+     && (options & (vopt_callout_recipsender | vopt_callout_recippmaster))
+       == vopt_callout_recipsender
+     && !random_local_part
+     && !pm_mailfrom
+     )
+    {
+    if (addr->transport == cutthrough.addr.transport)
+      for (host = host_list; host; host = host->next)
+       if (Ustrcmp(host->address, cutthrough.host.address) == 0)
+         {
+         int host_af;
+         uschar *interface = NULL;  /* Outgoing interface to use; NULL => any */
+         int port = 25;
+
+         deliver_host = host->name;
+         deliver_host_address = host->address;
+         deliver_host_port = host->port;
+         deliver_domain = addr->domain;
+         transport_name = addr->transport->name;
+
+         host_af = (Ustrchr(host->address, ':') == NULL)? AF_INET:AF_INET6;
+
+         if (!smtp_get_interface(tf->interface, host_af, addr, NULL, &interface,
+                 US"callout") ||
+             !smtp_get_port(tf->port, addr, &port, US"callout"))
+           log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address,
+             addr->message);
+
+         if (  (  interface == cutthrough.interface
+               || (  interface
+                  && cutthrough.interface
+                  && Ustrcmp(interface, cutthrough.interface) == 0
+               )  )
+            && port == cutthrough.host.port
+            )
+           {
+           uschar * resp;
+
+           /* Match!  Send the RCPT TO, append the addr, set done */
+           done =
+             smtp_write_command(&ctblock, FALSE, "RCPT TO:<%.1000s>\r\n",
+               transport_rcpt_address(addr,
+                 (addr->transport == NULL)? FALSE :
+                  addr->transport->rcpt_include_affixes)) >= 0 &&
+             cutthrough_response('2', &resp) == '2';
+
+           /* This would go horribly wrong if a callout fail was ignored by ACL.
+           We punt by abandoning cutthrough on a reject, like the
+           first-rcpt does. */
+
+           if (done)
+             {
+             address_item * na = store_get(sizeof(address_item));
+             *na = cutthrough.addr;
+             cutthrough.addr = *addr;
+             cutthrough.addr.host_used = &cutthrough.host;
+             cutthrough.addr.next = na;
+
+             cutthrough.nrcpt++;
+             }
+           else
+             {
+             cancel_cutthrough_connection("recipient rejected");
+             if (errno == ETIMEDOUT)
+               {
+               HDEBUG(D_verify) debug_printf("SMTP timeout\n");
+               }
+             else if (errno == 0)
+               {
+               if (*resp == 0)
+                 Ustrcpy(resp, US"connection dropped");
+
+               addr->message =
+                 string_sprintf("response to \"%s\" from %s [%s] was: %s",
+                   big_buffer, host->name, host->address,
+                   string_printing(resp));
+
+               addr->user_message =
+                 string_sprintf("Callout verification failed:\n%s", resp);
+
+               /* Hard rejection ends the process */
+
+               if (resp[0] == '5')   /* Address rejected */
+                 {
+                 yield = FAIL;
+                 done = TRUE;
+                 }
+               }
+             }
+           }
+         break;
+         }
+    if (!done)
+      cancel_cutthrough_connection("incompatible connection");
+    }
+
   /* Now make connections to the hosts and do real callouts. The list of hosts
   is passed in as an argument. */
 
@@ -428,6 +535,7 @@ else
     uschar *interface = NULL;  /* Outgoing interface to use; NULL => any */
 #if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
     BOOL dane = FALSE;
+    BOOL dane_required;
     dns_answer tlsa_dnsa;
 #endif
     uschar inbuffer[4096];
@@ -468,6 +576,7 @@ else
     deliver_host_address = host->address;
     deliver_host_port = host->port;
     deliver_domain = addr->domain;
+    transport_name = addr->transport->name;
 
     if (!smtp_get_interface(tf->interface, host_af, addr, NULL, &interface,
             US"callout") ||
@@ -484,20 +593,18 @@ else
 
 #if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
       {
-      BOOL dane_required;
       int rc;
 
       tls_out.dane_verified = FALSE;
       tls_out.tlsa_usage = 0;
 
-      dane_required = verify_check_this_host(&ob->hosts_require_dane, NULL,
-                               host->name, host->address, NULL) == OK;
+      dane_required =
+       verify_check_given_host(&ob->hosts_require_dane, host) == OK;
 
       if (host->dnssec == DS_YES)
        {
        if(  dane_required
-         || verify_check_this_host(&ob->hosts_try_dane, NULL,
-                               host->name, host->address, NULL) == OK
+         || verify_check_given_host(&ob->hosts_try_dane, host) == OK
          )
          if ((rc = tlsa_lookup(host, &tlsa_dnsa, dane_required, &dane)) != OK)
            return rc;
@@ -538,8 +645,8 @@ else
 
     inblock.sock = outblock.sock =
       smtp_connect(host, host_af, port, interface, callout_connect, TRUE, NULL
-#ifdef EXPERIMENTAL_TPDA
-    /*XXX tpda action? NULL for now. */
+#ifdef EXPERIMENTAL_EVENT
+    /*XXX event action? NULL for now. */
                  , NULL
 #endif
                  );
@@ -548,6 +655,7 @@ else
       {
       addr->message = string_sprintf("could not connect to %s [%s]: %s",
           host->name, host->address, strerror(errno));
+      transport_name = NULL;
       deliver_host = deliver_host_address = NULL;
       deliver_domain = save_deliver_domain;
       continue;
@@ -581,19 +689,22 @@ else
       if (!(done= smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), '2', callout)))
         goto RESPONSE_FAILED;
 
-#ifdef EXPERIMENTAL_TPDA
-      if (tpda_raise_event(addr->transport->tpda_event_action,
-                           US"smtp:connect", responsebuffer) == DEFER)
+#ifdef EXPERIMENTAL_EVENT
+      lookup_dnssec_authenticated = host->dnssec==DS_YES ? US"yes"
+       : host->dnssec==DS_NO ? US"no" : NULL;
+      if (event_raise(addr->transport->event_action,
+                           US"smtp:connect", responsebuffer))
        {
+       lookup_dnssec_authenticated = NULL;
        /* Logging?  Debug? */
        goto RESPONSE_FAILED;
        }
+      lookup_dnssec_authenticated = NULL;
 #endif
       }
 
     /* Not worth checking greeting line for ESMTP support */
-    if (!(esmtp = verify_check_this_host(&(ob->hosts_avoid_esmtp), NULL,
-      host->name, host->address, NULL) != OK))
+    if (!(esmtp = verify_check_given_host(&(ob->hosts_avoid_esmtp), host) != OK))
       DEBUG(D_transport)
         debug_printf("not sending EHLO (host matches hosts_avoid_esmtp)\n");
 
@@ -651,11 +762,9 @@ else
     for error analysis. */
 
 #ifdef SUPPORT_TLS
-    if (tls_offered &&
-       verify_check_this_host(&(ob->hosts_avoid_tls), NULL, host->name,
-         host->address, NULL) != OK &&
-       verify_check_this_host(&(ob->hosts_verify_avoid_tls), NULL, host->name,
-         host->address, NULL) != OK
+    if (  tls_offered
+       && verify_check_given_host(&ob->hosts_avoid_tls, host) != OK
+       && verify_check_given_host(&ob->hosts_verify_avoid_tls, host) != OK
        )
       {
       uschar buffer2[4096];
@@ -688,35 +797,53 @@ else
        int oldtimeout = ob->command_timeout;
        int rc;
 
+       tls_negotiate:
        ob->command_timeout = callout;
         rc = tls_client_start(inblock.sock, host, addr, addr->transport
-#ifdef EXPERIMENTAL_DANE
+# ifdef EXPERIMENTAL_DANE
                            , dane ? &tlsa_dnsa : NULL
-#endif
+# endif
                            );
        ob->command_timeout = oldtimeout;
 
-        /* TLS negotiation failed; give an error.  Try in clear on a new connection,
-           if the options permit it for this host. */
+        /* TLS negotiation failed; give an error.  Try in clear on a new
+       connection, if the options permit it for this host. */
         if (rc != OK)
           {
-         if (  rc == DEFER
-            && ob->tls_tempfail_tryclear
-            && !smtps
-            && verify_check_this_host(&(ob->hosts_require_tls), NULL,
-              host->name, host->address, NULL) != OK
-            )
+         if (rc == DEFER)
            {
            (void)close(inblock.sock);
-#ifdef EXPERIMENTAL_TPDA
-           (void) tpda_raise_event(addr->transport->tpda_event_action,
+# ifdef EXPERIMENTAL_EVENT
+           (void) event_raise(addr->transport->event_action,
                                    US"tcp:close", NULL);
-#endif
-           log_write(0, LOG_MAIN, "TLS session failure: delivering unencrypted "
-             "to %s [%s] (not in hosts_require_tls)", host->name, host->address);
-           suppress_tls = TRUE;
-           goto tls_retry_connection;
+# endif
+# ifdef EXPERIMENTAL_DANE
+           if (dane)
+             {
+             if (!dane_required)
+               {
+               log_write(0, LOG_MAIN, "DANE attempt failed;"
+                 " trying CA-root TLS to %s [%s] (not in hosts_require_dane)",
+                 host->name, host->address);
+               dane = FALSE;
+               goto tls_negotiate;
+               }
+             }
+           else
+# endif
+             if (  ob->tls_tempfail_tryclear
+                && !smtps
+                && verify_check_given_host(&ob->hosts_require_tls, host) != OK
+                )
+             {
+             log_write(0, LOG_MAIN, "TLS session failure:"
+               " delivering unencrypted to %s [%s] (not in hosts_require_tls)",
+               host->name, host->address);
+             suppress_tls = TRUE;
+             goto tls_retry_connection;
+             }
            }
+
          /*save_errno = ERRNO_TLSFAILURE;*/
          /*message = US"failure while setting up TLS session";*/
          send_quit = FALSE;
@@ -740,11 +867,10 @@ else
     /* If the host is required to use a secure channel, ensure that we have one. */
     if (tls_out.active < 0)
       if (
-#ifdef EXPERIMENTAL_DANE
+# ifdef EXPERIMENTAL_DANE
         dane ||
-#endif
-         verify_check_this_host(&(ob->hosts_require_tls), NULL, host->name,
-             host->address, NULL) == OK
+# endif
+         verify_check_given_host(&ob->hosts_require_tls, host) == OK
         )
         {
         /*save_errno = ERRNO_TLSREQUIRED;*/
@@ -757,7 +883,7 @@ else
         goto TLS_FAILED;
         }
 
-    #endif /*SUPPORT_TLS*/
+#endif /*SUPPORT_TLS*/
 
     done = TRUE; /* so far so good; have response to HELO */
 
@@ -765,17 +891,17 @@ else
 
     /* For now, transport_filter by cutthrough-delivery is not supported */
     /* Need proper integration with the proper transport mechanism. */
-    if (cutthrough_delivery)
+    if (cutthrough.delivery)
       {
       if (addr->transport->filter_command)
         {
-        cutthrough_delivery= FALSE;
+        cutthrough.delivery = FALSE;
         HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of transport filter\n");
         }
 #ifndef DISABLE_DKIM
       if (ob->dkim_domain)
         {
-        cutthrough_delivery= FALSE;
+        cutthrough.delivery = FALSE;
         HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of DKIM signing\n");
         }
 #endif
@@ -879,9 +1005,7 @@ else
         /* If accepted, we aren't going to do any further tests below. */
 
         if (random_ok)
-          {
           new_domain_record.random_result = ccache_accept;
-          }
 
         /* Otherwise, cache a real negative response, and get back to the right
         state to send RCPT. Unless there's some problem such as a dropped
@@ -889,6 +1013,8 @@ else
 
         else if (errno == 0)
           {
+         cancel_cutthrough_connection("random-recipient");
+
           if (randombuffer[0] == '5')
             new_domain_record.random_result = ccache_reject;
 
@@ -934,8 +1060,9 @@ else
 
         if (done && pm_mailfrom != NULL)
           {
-          /*XXX not suitable for cutthrough - sequencing problems */
-       cutthrough_delivery= FALSE;
+          /*XXX not suitable for cutthrough - we cannot afford to do an RSET
+         and lose the original mail-from */
+       cancel_cutthrough_connection("postmaster verify");
        HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of postmaster verify\n");
 
           done =
@@ -1030,30 +1157,35 @@ else
 
     /* End the SMTP conversation and close the connection. */
 
-    /* Cutthrough - on a successfull connect and recipient-verify with use-sender
-    and we have no cutthrough conn so far
+    /* Cutthrough - on a successfull connect and recipient-verify with
+    use-sender and we are 1st rcpt and have no cutthrough conn so far
     here is where we want to leave the conn open */
-    if (  cutthrough_delivery
+    if (  cutthrough.delivery
+       && rcpt_count == 1
        && done
        && yield == OK
        && (options & (vopt_callout_recipsender|vopt_callout_recippmaster)) == vopt_callout_recipsender
        && !random_local_part
        && !pm_mailfrom
-       && cutthrough_fd < 0
+       && cutthrough.fd < 0
+       && !lmtp
        )
       {
-      cutthrough_fd= outblock.sock;    /* We assume no buffer in use in the outblock */
-      cutthrough_addr = *addr;         /* Save the address_item for later logging */
-      cutthrough_addr.next =     NULL;
-      cutthrough_addr.host_used = store_get(sizeof(host_item));
-      *(cutthrough_addr.host_used) = *host;
+      cutthrough.fd = outblock.sock;   /* We assume no buffer in use in the outblock */
+      cutthrough.nrcpt = 1;
+      cutthrough.interface = interface;
+      cutthrough.host = *host;
+      cutthrough.addr = *addr;         /* Save the address_item for later logging */
+      cutthrough.addr.next =     NULL;
+      cutthrough.addr.host_used = &cutthrough.host;
       if (addr->parent)
-        *(cutthrough_addr.parent = store_get(sizeof(address_item)))= *addr->parent;
+        *(cutthrough.addr.parent = store_get(sizeof(address_item))) =
+         *addr->parent;
       ctblock.buffer = ctbuffer;
       ctblock.buffersize = sizeof(ctbuffer);
       ctblock.ptr = ctbuffer;
       /* ctblock.cmd_count = 0; ctblock.authenticating = FALSE; */
-      ctblock.sock = cutthrough_fd;
+      ctblock.sock = cutthrough.fd;
       }
     else
       {
@@ -1066,8 +1198,8 @@ else
       tls_close(FALSE, TRUE);
 #endif
       (void)close(inblock.sock);
-#ifdef EXPERIMENTAL_TPDA
-      (void) tpda_raise_event(addr->transport->tpda_event_action,
+#ifdef EXPERIMENTAL_EVENT
+      (void) event_raise(addr->transport->event_action,
                              US"tcp:close", NULL);
 #endif
       }
@@ -1178,7 +1310,8 @@ address_item addr2;
 get rewritten. */
 
 addr2 = *addr;
-HDEBUG(D_acl) debug_printf("----------- start cutthrough setup ------------\n");
+HDEBUG(D_acl) debug_printf("----------- %s cutthrough setup ------------\n",
+  rcpt_count > 1 ? "more" : "start");
 (void) verify_address(&addr2, NULL,
        vopt_is_recipient | vopt_callout_recipsender | vopt_callout_no_cache,
        CUTTHROUGH_CMD_TIMEOUT, -1, -1,
@@ -1193,14 +1326,14 @@ return;
 static BOOL
 cutthrough_send(int n)
 {
-if(cutthrough_fd < 0)
+if(cutthrough.fd < 0)
   return TRUE;
 
 if(
 #ifdef SUPPORT_TLS
-   (tls_out.active == cutthrough_fd) ? tls_write(FALSE, ctblock.buffer, n) :
+   (tls_out.active == cutthrough.fd) ? tls_write(FALSE, ctblock.buffer, n) :
 #endif
-   send(cutthrough_fd, ctblock.buffer, n, 0) > 0
+   send(cutthrough.fd, ctblock.buffer, n, 0) > 0
   )
 {
   transport_count += n;
@@ -1232,7 +1365,7 @@ return TRUE;
 BOOL
 cutthrough_puts(uschar * cp, int n)
 {
-if (cutthrough_fd < 0)       return TRUE;
+if (cutthrough.fd < 0)       return TRUE;
 if (_cutthrough_puts(cp, n)) return TRUE;
 cancel_cutthrough_connection("transmit failed");
 return FALSE;
@@ -1240,7 +1373,7 @@ return FALSE;
 
 
 static BOOL
-_cutthrough_flush_send( void )
+_cutthrough_flush_send(void)
 {
 int n= ctblock.ptr-ctblock.buffer;
 
@@ -1253,7 +1386,7 @@ return TRUE;
 
 /* Send out any bufferred output.  Return boolean success. */
 BOOL
-cutthrough_flush_send( void )
+cutthrough_flush_send(void)
 {
 if (_cutthrough_flush_send()) return TRUE;
 cancel_cutthrough_connection("transmit failed");
@@ -1262,7 +1395,7 @@ return FALSE;
 
 
 BOOL
-cutthrough_put_nl( void )
+cutthrough_put_nl(void)
 {
 return cutthrough_puts(US"\r\n", 2);
 }
@@ -1280,7 +1413,7 @@ inblock.buffer = inbuffer;
 inblock.buffersize = sizeof(inbuffer);
 inblock.ptr = inbuffer;
 inblock.ptrend = inbuffer;
-inblock.sock = cutthrough_fd;
+inblock.sock = cutthrough.fd;
 /* this relies on (inblock.sock == tls_out.active) */
 if(!smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), expect, CUTTHROUGH_DATA_TIMEOUT))
   cancel_cutthrough_connection("target timeout on read");
@@ -1288,7 +1421,7 @@ if(!smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), expect,
 if(copy != NULL)
   {
   uschar * cp;
-  *copy= cp= string_copy(responsebuffer);
+  *copy = cp = string_copy(responsebuffer);
   /* Trim the trailing end of line */
   cp += Ustrlen(responsebuffer);
   if(cp > *copy  &&  cp[-1] == '\n') *--cp = '\0';
@@ -1301,9 +1434,9 @@ return responsebuffer[0];
 
 /* Negotiate dataphase with the cutthrough target, returning success boolean */
 BOOL
-cutthrough_predata( void )
+cutthrough_predata(void)
 {
-if(cutthrough_fd < 0)
+if(cutthrough.fd < 0)
   return FALSE;
 
 HDEBUG(D_transport|D_acl|D_v) debug_printf("  SMTP>> DATA\n");
@@ -1334,9 +1467,9 @@ return TRUE;
 /* Expands newlines to wire format (CR,NL).           */
 /* Also sends header-terminating blank line.          */
 BOOL
-cutthrough_headers_send( void )
+cutthrough_headers_send(void)
 {
-if(cutthrough_fd < 0)
+if(cutthrough.fd < 0)
   return FALSE;
 
 /* We share a routine with the mainline transport to handle header add/remove/rewrites,
@@ -1344,10 +1477,12 @@ if(cutthrough_fd < 0)
 */
 HDEBUG(D_acl) debug_printf("----------- start cutthrough headers send -----------\n");
 
-if (!transport_headers_send(&cutthrough_addr, cutthrough_fd,
-       cutthrough_addr.transport->add_headers, cutthrough_addr.transport->remove_headers,
+if (!transport_headers_send(&cutthrough.addr, cutthrough.fd,
+       cutthrough.addr.transport->add_headers,
+       cutthrough.addr.transport->remove_headers,
        &cutthrough_write_chunk, TRUE,
-       cutthrough_addr.transport->rewrite_rules, cutthrough_addr.transport->rewrite_existflags))
+       cutthrough.addr.transport->rewrite_rules,
+       cutthrough.addr.transport->rewrite_existflags))
   return FALSE;
 
 HDEBUG(D_acl) debug_printf("----------- done cutthrough headers send ------------\n");
@@ -1356,9 +1491,9 @@ return TRUE;
 
 
 static void
-close_cutthrough_connection( const char * why )
+close_cutthrough_connection(const char * why)
 {
-if(cutthrough_fd >= 0)
+if(cutthrough.fd >= 0)
   {
   /* We could be sending this after a bunch of data, but that is ok as
      the only way to cancel the transfer in dataphase is to drop the tcp
@@ -1373,18 +1508,18 @@ if(cutthrough_fd >= 0)
   #ifdef SUPPORT_TLS
   tls_close(FALSE, TRUE);
   #endif
-  (void)close(cutthrough_fd);
-  cutthrough_fd= -1;
+  (void)close(cutthrough.fd);
+  cutthrough.fd = -1;
   HDEBUG(D_acl) debug_printf("----------- cutthrough shutdown (%s) ------------\n", why);
   }
 ctblock.ptr = ctbuffer;
 }
 
 void
-cancel_cutthrough_connection( const char * why )
+cancel_cutthrough_connection(const char * why)
 {
 close_cutthrough_connection(why);
-cutthrough_delivery= FALSE;
+cutthrough.delivery = FALSE;
 }
 
 
@@ -1396,33 +1531,45 @@ cutthrough_delivery= FALSE;
    Return smtp response-class digit.
 */
 uschar *
-cutthrough_finaldot( void )
+cutthrough_finaldot(void)
 {
+uschar res;
+address_item * addr;
 HDEBUG(D_transport|D_acl|D_v) debug_printf("  SMTP>> .\n");
 
 /* Assume data finshed with new-line */
-if(!cutthrough_puts(US".", 1) || !cutthrough_put_nl() || !cutthrough_flush_send())
-  return cutthrough_addr.message;
+if(  !cutthrough_puts(US".", 1)
+  || !cutthrough_put_nl()
+  || !cutthrough_flush_send()
+  )
+  return cutthrough.addr.message;
 
-switch(cutthrough_response('2', &cutthrough_addr.message))
+res = cutthrough_response('2', &cutthrough.addr.message);
+for (addr = &cutthrough.addr; addr; addr = addr->next)
   {
-  case '2':
-    delivery_log(LOG_MAIN, &cutthrough_addr, (int)'>', NULL);
-    close_cutthrough_connection("delivered");
-    break;
+  addr->message = cutthrough.addr.message;
+  switch(res)
+    {
+    case '2':
+      delivery_log(LOG_MAIN, addr, (int)'>', NULL);
+      close_cutthrough_connection("delivered");
+      break;
 
-  case '4':
-    delivery_log(LOG_MAIN, &cutthrough_addr, 0, US"tmp-reject from cutthrough after DATA:");
-    break;
+    case '4':
+      delivery_log(LOG_MAIN, addr, 0,
+       US"tmp-reject from cutthrough after DATA:");
+      break;
 
-  case '5':
-    delivery_log(LOG_MAIN|LOG_REJECT, &cutthrough_addr, 0, US"rejected after DATA:");
-    break;
+    case '5':
+      delivery_log(LOG_MAIN|LOG_REJECT, addr, 0,
+       US"rejected after DATA:");
+      break;
 
-  default:
-    break;
+    default:
+      break;
+    }
   }
-  return cutthrough_addr.message;
+return cutthrough.addr.message;
 }
 
 
@@ -1852,8 +1999,10 @@ while (addr_new != NULL)
 #ifdef SUPPORT_TLS
          deliver_set_expansions(addr);
 #endif
+         verify_mode = is_recipient ? US"R" : US"S";
           rc = do_callout(addr, host_list, &tf, callout, callout_overall,
             callout_connect, options, se_mailfrom, pm_mailfrom);
+         verify_mode = NULL;
           }
         }
       else
@@ -3156,6 +3305,15 @@ return rc;
 
 
 
+/*************************************************
+*      Check the given host item matches a list  *
+*************************************************/
+int
+verify_check_given_host(uschar **listptr, host_item *host)
+{
+return verify_check_this_host(listptr, NULL, host->name, host->address, NULL);
+}
+
 /*************************************************
 *      Check the remote host matches a list      *
 *************************************************/