Avoid potential crash in close of a verify callout
[exim.git] / src / src / verify.c
index 1555838acecda3c5abbebea7d232af4691f1a2cf..184809fb55ab434f3e6d4387bc106f6832659258 100644 (file)
@@ -14,7 +14,7 @@ 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 */
-static smtp_outblock ctblock;
+static smtp_context ctctx;
 uschar ctbuffer[8192];
 
 
@@ -39,7 +39,7 @@ static tree_node *dnsbl_cache = NULL;
 #define MT_NOT 1
 #define MT_ALL 2
 
-static uschar cutthrough_response(int, char, uschar **, int);
+static uschar cutthrough_response(client_conn_ctx *, char, uschar **, int);
 
 
 
@@ -140,7 +140,7 @@ if (options & vopt_callout_no_cache)
   {
   HDEBUG(D_verify) debug_printf("callout cache: disabled by no_cache\n");
   }
-else if (!(dbm_file = dbfn_open(US"callout", O_RDWR, &dbblock, FALSE)))
+else if (!(dbm_file = dbfn_open(US"callout", O_RDWR, &dbblock, FALSE, TRUE)))
   {
   HDEBUG(D_verify) debug_printf("callout cache: not available\n");
   }
@@ -172,7 +172,6 @@ else
     if (  cache_record->result == ccache_reject
        || *from_address == 0 && cache_record->result == ccache_reject_mfnull)
       {
-      setflag(addr, af_verify_nsfail);
       HDEBUG(D_verify)
        debug_printf("callout cache: domain gave initial rejection, or "
          "does not accept HELO or MAIL FROM:<>\n");
@@ -196,6 +195,7 @@ else
       case ccache_accept:
        HDEBUG(D_verify)
          debug_printf("callout cache: domain accepts random addresses\n");
+       *failure_ptr = US"random";
        dbfn_close(dbm_file);
        return TRUE;     /* Default yield is OK */
 
@@ -313,7 +313,7 @@ implying some kind of I/O error. We don't want to write the cache in that case.
 Otherwise the value is ccache_accept, ccache_reject, or ccache_reject_mfnull. */
 
 if (dom_rec->result != ccache_unknown)
-  if (!(dbm_file = dbfn_open(US"callout", O_RDWR|O_CREAT, &dbblock, FALSE)))
+  if (!(dbm_file = dbfn_open(US"callout", O_RDWR|O_CREAT, &dbblock, FALSE, TRUE)))
     {
     HDEBUG(D_verify) debug_printf("callout cache: not available\n");
     }
@@ -335,7 +335,7 @@ is disabled. */
 if (done  &&  addr_rec->result != ccache_unknown)
   {
   if (!dbm_file)
-    dbm_file = dbfn_open(US"callout", O_RDWR|O_CREAT, &dbblock, FALSE);
+    dbm_file = dbfn_open(US"callout", O_RDWR|O_CREAT, &dbblock, FALSE, TRUE);
   if (!dbm_file)
     {
     HDEBUG(D_verify) debug_printf("no callout cache available\n");
@@ -370,10 +370,9 @@ cutthrough_multi(address_item * addr, host_item * host_list,
   transport_feedback * tf, int * yield)
 {
 BOOL done = FALSE;
-host_item * host;
 
 if (addr->transport == cutthrough.addr.transport)
-  for (host = host_list; host; host = host->next)
+  for (host_item * host = host_list; host; host = host->next)
     if (Ustrcmp(host->address, cutthrough.host.address) == 0)
       {
       int host_af;
@@ -409,10 +408,11 @@ if (addr->transport == cutthrough.addr.transport)
 
        /* Match!  Send the RCPT TO, set done from the response */
        done =
-         smtp_write_command(&ctblock, SCMD_FLUSH, "RCPT TO:<%.1000s>\r\n",
-           transport_rcpt_address(addr,
-              addr->transport->rcpt_include_affixes)) >= 0 &&
-         cutthrough_response(cutthrough.fd, '2', &resp, CUTTHROUGH_DATA_TIMEOUT) == '2';
+            smtp_write_command(&ctctx, SCMD_FLUSH, "RCPT TO:<%.1000s>\r\n",
+             transport_rcpt_address(addr,
+                addr->transport->rcpt_include_affixes)) >= 0
+         && cutthrough_response(&cutthrough.cctx, '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
@@ -574,7 +574,6 @@ else
   {
   smtp_transport_options_block *ob =
     (smtp_transport_options_block *)addr->transport->options_block;
-  host_item * host;
 
   /* The information wasn't available in the cache, so we have to do a real
   callout and save the result in the cache for next time, unless no_cache is set,
@@ -600,7 +599,7 @@ else
   and cause the client to time out. So in this case we forgo the PIPELINING
   optimization. */
 
-  if (smtp_out && !disable_callout_flush) mac_smtp_fflush();
+  if (smtp_out && !f.disable_callout_flush) mac_smtp_fflush();
 
   clearflag(addr, af_verify_pmfail);  /* postmaster callout flag */
   clearflag(addr, af_verify_nsfail);  /* null sender callout flag */
@@ -611,7 +610,7 @@ that conn for verification purposes (and later delivery also).  Simplest
 coding means skipping this whole loop and doing the append separately.  */
 
   /* Can we re-use an open cutthrough connection? */
-  if (  cutthrough.fd >= 0
+  if (  cutthrough.cctx.sock >= 0
      && (options & (vopt_callout_recipsender | vopt_callout_recippmaster))
        == vopt_callout_recipsender
      && !random_local_part
@@ -622,7 +621,7 @@ coding means skipping this whole loop and doing the append separately.  */
   /* If we did not use a cached connection, make connections to the hosts
   and do real callouts. The list of hosts is passed in as an argument. */
 
-  for (host = host_list; host && !done; host = host->next)
+  for (host_item * host = host_list; host && !done; host = host->next)
     {
     int host_af;
     int port = 25;
@@ -668,12 +667,12 @@ coding means skipping this whole loop and doing the append separately.  */
         addr->message);
 
     sx.addrlist = addr;
-    sx.host = host;
-    sx.host_af = host_af,
+    sx.conn_args.host = host;
+    sx.conn_args.host_af = host_af,
     sx.port = port;
-    sx.interface = interface;
+    sx.conn_args.interface = interface;
     sx.helo_data = tf->helo_data;
-    sx.tblock = addr->transport;
+    sx.conn_args.tblock = addr->transport;
     sx.verify = TRUE;
 
 tls_retry_connection:
@@ -692,7 +691,7 @@ tls_retry_connection:
     if (  yield == DEFER
        && addr->basic_errno == ERRNO_TLSFAILURE
        && ob->tls_tempfail_tryclear
-       && verify_check_given_host(&ob->hosts_require_tls, host) != OK
+       && verify_check_given_host(CUSS &ob->hosts_require_tls, host) != OK
        )
       {
       log_write(0, LOG_MAIN,
@@ -787,7 +786,7 @@ tls_retry_connection:
       The sync_responses() would need to be taught about it and we'd
       need another return code filtering out to here.
 
-      Avoid using a SIZE option on the MAIL for all randon-rcpt checks.
+      Avoid using a SIZE option on the MAIL for all random-rcpt checks.
       */
 
       sx.avoid_option = OPTION_SIZE;
@@ -802,6 +801,7 @@ tls_retry_connection:
            new_domain_record.random_result = ccache_accept;
            yield = OK;         /* Only usable verify result we can return */
            done = TRUE;
+           *failure_ptr = US"random";
            goto no_conn;
          case FAIL:            /* rejected: the preferred result */
            new_domain_record.random_result = ccache_reject;
@@ -812,20 +812,19 @@ tls_retry_connection:
            XXX We don't care about that for postmaster_full.  Should we? */
 
            if ((done =
-             smtp_write_command(&sx.outblock, SCMD_FLUSH, "RSET\r\n") >= 0 &&
-             smtp_read_response(&sx.inblock, sx.buffer, sizeof(sx.buffer),
-               '2', callout)))
+             smtp_write_command(&sx, SCMD_FLUSH, "RSET\r\n") >= 0 &&
+             smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2', callout)))
              break;
 
            HDEBUG(D_acl|D_v)
              debug_printf_indent("problem after random/rset/mfrom; reopen conn\n");
            random_local_part = NULL;
 #ifdef SUPPORT_TLS
-           tls_close(FALSE, TLS_SHUTDOWN_NOWAIT);
+           tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
 #endif
            HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP(close)>>\n");
-           (void)close(sx.inblock.sock);
-           sx.inblock.sock = sx.outblock.sock = -1;
+           (void)close(sx.cctx.sock);
+           sx.cctx.sock = -1;
 #ifndef DISABLE_EVENT
            (void) event_raise(addr->transport->event_action,
                              US"tcp:close", NULL);
@@ -909,9 +908,8 @@ tls_retry_connection:
       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, SCMD_FLUSH, "RSET\r\n") >= 0
-          && smtp_read_response(&sx.inblock, sx.buffer,
-                               sizeof(sx.buffer), '2', callout);
+      done = smtp_write_command(&sx, SCMD_FLUSH, "RSET\r\n") >= 0
+          && smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2', callout);
 
       if (done)
        {
@@ -934,9 +932,9 @@ tls_retry_connection:
          done = TRUE;
        else
          done = (options & vopt_callout_fullpm) != 0
-             && smtp_write_command(&sx.outblock, SCMD_FLUSH,
+             && smtp_write_command(&sx, SCMD_FLUSH,
                            "RCPT TO:<postmaster>\r\n") >= 0
-             && smtp_read_response(&sx.inblock, sx.buffer,
+             && smtp_read_response(&sx, sx.buffer,
                            sizeof(sx.buffer), '2', callout);
 
        /* Sort out the cache record */
@@ -1033,11 +1031,20 @@ no_conn:
         cutthrough.delivery= FALSE;
         HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of transport filter\n");
         }
-      if (ob->dkim.dkim_domain || ob->arc_sign)
+#ifndef DISABLE_DKIM
+      if (ob->dkim.dkim_domain)
+        {
+        cutthrough.delivery= FALSE;
+        HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of DKIM signing\n");
+        }
+#endif
+#ifdef EXPERIMENTAL_ARC
+      if (ob->arc_sign)
         {
         cutthrough.delivery= FALSE;
-        HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of DKIM or ARC signing\n");
+        HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of ARC signing\n");
         }
+#endif
       }
 
     if (  (cutthrough.delivery || options & vopt_callout_hold)
@@ -1048,7 +1055,7 @@ no_conn:
           == vopt_callout_recipsender
        && !random_local_part
        && !pm_mailfrom
-       && cutthrough.fd < 0
+       && cutthrough.cctx.sock < 0
        && !sx.lmtp
        )
       {
@@ -1057,8 +1064,9 @@ no_conn:
        ? "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.is_tls =      tls_out.active.sock >= 0;
+      /* We assume no buffer in use in the outblock */
+      cutthrough.cctx =                sx.cctx;
       cutthrough.nrcpt =       1;
       cutthrough.transport =   addr->transport->name;
       cutthrough.interface =   interface;
@@ -1073,17 +1081,21 @@ no_conn:
        cutthrough.host.address = string_copy(host->address);
        store_pool = oldpool;
        }
-      cutthrough.addr =                *addr;          /* Save the address_item for later logging */
+
+      /* Save the address_item and parent chain for later logging */
+      cutthrough.addr =                *addr;
       cutthrough.addr.next =   NULL;
       cutthrough.addr.host_used = &cutthrough.host;
-      if (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;
+      for (address_item * caddr = &cutthrough.addr, * parent = addr->parent;
+          parent;
+          caddr = caddr->parent, parent = parent->parent)
+        *(caddr->parent = store_get(sizeof(address_item))) = *parent;
+
+      ctctx.outblock.buffer = ctbuffer;
+      ctctx.outblock.buffersize = sizeof(ctbuffer);
+      ctctx.outblock.ptr = ctbuffer;
+      /* ctctx.outblock.cmd_count = 0; ctctx.outblock.authenticating = FALSE; */
+      ctctx.outblock.cctx = &cutthrough.cctx;
       }
     else
       {
@@ -1091,22 +1103,22 @@ no_conn:
       if (options & vopt_callout_recipsender)
         cancel_cutthrough_connection(TRUE, US"not usable for cutthrough");
       if (sx.send_quit)
-       {
-       (void) smtp_write_command(&sx.outblock, SCMD_FLUSH, "QUIT\r\n");
-
-       /* Wait a short time for response, and discard it */
-       smtp_read_response(&sx.inblock, sx.buffer, sizeof(sx.buffer),
-         '2', 1);
-       }
+       if (smtp_write_command(&sx, SCMD_FLUSH, "QUIT\r\n") != -1)
+         /* Wait a short time for response, and discard it */
+         smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2', 1);
 
-      if (sx.inblock.sock >= 0)
+      if (sx.cctx.sock >= 0)
        {
 #ifdef SUPPORT_TLS
-       tls_close(FALSE, TLS_SHUTDOWN_NOWAIT);
+       if (sx.cctx.tls_ctx)
+         {
+         tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
+         sx.cctx.tls_ctx = NULL;
+         }
 #endif
        HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP(close)>>\n");
-       (void)close(sx.inblock.sock);
-       sx.inblock.sock = sx.outblock.sock = -1;
+       (void)close(sx.cctx.sock);
+       sx.cctx.sock = -1;
 #ifndef DISABLE_EVENT
        (void) event_raise(addr->transport->event_action, US"tcp:close", NULL);
 #endif
@@ -1171,7 +1183,7 @@ return yield;
    one was requested and a recipient-verify wasn't subsequently done.
 */
 int
-open_cutthrough_connection( address_item * addr )
+open_cutthrough_connection(address_item * addr)
 {
 address_item addr2;
 int rc;
@@ -1199,18 +1211,20 @@ return rc;
 static BOOL
 cutthrough_send(int n)
 {
-if(cutthrough.fd < 0)
+if(cutthrough.cctx.sock < 0)
   return TRUE;
 
 if(
 #ifdef SUPPORT_TLS
-   tls_out.active == cutthrough.fd ? tls_write(FALSE, ctblock.buffer, n, FALSE) :
+   cutthrough.is_tls
+   ? tls_write(cutthrough.cctx.tls_ctx, ctctx.outblock.buffer, n, FALSE)
+   :
 #endif
-   send(cutthrough.fd, ctblock.buffer, n, 0) > 0
+     send(cutthrough.cctx.sock, ctctx.outblock.buffer, n, 0) > 0
   )
 {
   transport_count += n;
-  ctblock.ptr= ctblock.buffer;
+  ctctx.outblock.ptr= ctctx.outblock.buffer;
   return TRUE;
 }
 
@@ -1225,11 +1239,11 @@ _cutthrough_puts(uschar * cp, int n)
 {
 while(n--)
  {
- if(ctblock.ptr >= ctblock.buffer+ctblock.buffersize)
-   if(!cutthrough_send(ctblock.buffersize))
+ if(ctctx.outblock.ptr >= ctctx.outblock.buffer+ctctx.outblock.buffersize)
+   if(!cutthrough_send(ctctx.outblock.buffersize))
      return FALSE;
 
- *ctblock.ptr++ = *cp++;
+ *ctctx.outblock.ptr++ = *cp++;
  }
 return TRUE;
 }
@@ -1238,8 +1252,8 @@ return TRUE;
 static BOOL
 cutthrough_puts(uschar * cp, int n)
 {
-if (cutthrough.fd < 0)       return TRUE;
-if (_cutthrough_puts(cp, n)) return TRUE;
+if (cutthrough.cctx.sock < 0) return TRUE;
+if (_cutthrough_puts(cp, n))  return TRUE;
 cancel_cutthrough_connection(TRUE, US"transmit failed");
 return FALSE;
 }
@@ -1255,7 +1269,7 @@ return;
 static BOOL
 _cutthrough_flush_send(void)
 {
-int n = ctblock.ptr - ctblock.buffer;
+int n = ctctx.outblock.ptr - ctctx.outblock.buffer;
 
 if(n>0)
   if(!cutthrough_send(n))
@@ -1290,19 +1304,18 @@ cutthrough_data_puts(US"\r\n", 2);
 
 /* Get and check response from cutthrough target */
 static uschar
-cutthrough_response(int fd, char expect, uschar ** copy, int timeout)
+cutthrough_response(client_conn_ctx * cctx, char expect, uschar ** copy, int timeout)
 {
-smtp_inblock inblock;
+smtp_context sx = {0};
 uschar inbuffer[4096];
 uschar responsebuffer[4096];
 
-inblock.buffer = inbuffer;
-inblock.buffersize = sizeof(inbuffer);
-inblock.ptr = inbuffer;
-inblock.ptrend = inbuffer;
-inblock.sock = fd;
-/* this relies on (inblock.sock == tls_out.active) */
-if(!smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), expect, timeout))
+sx.inblock.buffer = inbuffer;
+sx.inblock.buffersize = sizeof(inbuffer);
+sx.inblock.ptr = inbuffer;
+sx.inblock.ptrend = inbuffer;
+sx.inblock.cctx = cctx;
+if(!smtp_read_response(&sx, responsebuffer, sizeof(responsebuffer), expect, timeout))
   cancel_cutthrough_connection(TRUE, US"target timeout on read");
 
 if(copy)
@@ -1323,7 +1336,7 @@ return responsebuffer[0];
 BOOL
 cutthrough_predata(void)
 {
-if(cutthrough.fd < 0 || cutthrough.callout_hold_only)
+if(cutthrough.cctx.sock < 0 || cutthrough.callout_hold_only)
   return FALSE;
 
 HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP>> DATA\n");
@@ -1331,7 +1344,7 @@ cutthrough_puts(US"DATA\r\n", 6);
 cutthrough_flush_send();
 
 /* Assume nothing buffered.  If it was it gets ignored. */
-return cutthrough_response(cutthrough.fd, '3', NULL, CUTTHROUGH_DATA_TIMEOUT) == '3';
+return cutthrough_response(&cutthrough.cctx, '3', NULL, CUTTHROUGH_DATA_TIMEOUT) == '3';
 }
 
 
@@ -1358,7 +1371,7 @@ cutthrough_headers_send(void)
 {
 transport_ctx tctx;
 
-if(cutthrough.fd < 0 || cutthrough.callout_hold_only)
+if(cutthrough.cctx.sock < 0 || cutthrough.callout_hold_only)
   return FALSE;
 
 /* We share a routine with the mainline transport to handle header add/remove/rewrites,
@@ -1366,7 +1379,7 @@ if(cutthrough.fd < 0 || cutthrough.callout_hold_only)
 */
 HDEBUG(D_acl) debug_printf_indent("----------- start cutthrough headers send -----------\n");
 
-tctx.u.fd = cutthrough.fd;
+tctx.u.fd = cutthrough.cctx.sock;
 tctx.tblock = cutthrough.addr.transport;
 tctx.addr = &cutthrough.addr;
 tctx.check_string = US".";
@@ -1385,31 +1398,37 @@ return TRUE;
 static void
 close_cutthrough_connection(const uschar * why)
 {
-int fd = cutthrough.fd;
+int fd = cutthrough.cctx.sock;
 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
      conn before the final dot.
   */
-  ctblock.ptr = ctbuffer;
+  client_conn_ctx tmp_ctx = cutthrough.cctx;
+  ctctx.outblock.ptr = ctbuffer;
   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 */
+  cutthrough.cctx.sock = -1;           /* avoid recursion via read timeout */
   cutthrough.nrcpt = 0;                        /* permit re-cutthrough on subsequent message */
 
   /* Wait a short time for response, and discard it */
-  cutthrough_response(fd, '2', NULL, 1);
+  cutthrough_response(&tmp_ctx, '2', NULL, 1);
 
 #ifdef SUPPORT_TLS
-  tls_close(FALSE, TLS_SHUTDOWN_NOWAIT);
+  if (cutthrough.is_tls)
+    {
+    tls_close(cutthrough.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
+    cutthrough.cctx.tls_ctx = NULL;
+    cutthrough.is_tls = FALSE;
+    }
 #endif
   HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP(close)>>\n");
   (void)close(fd);
   HDEBUG(D_acl) debug_printf_indent("----------- cutthrough shutdown (%s) ------------\n", why);
   }
-ctblock.ptr = ctbuffer;
+ctctx.outblock.ptr = ctbuffer;
 }
 
 void
@@ -1424,9 +1443,10 @@ cutthrough.delivery = cutthrough.callout_hold_only = FALSE;
 void
 release_cutthrough_connection(const uschar * why)
 {
-if (cutthrough.fd < 0) return;
+if (cutthrough.cctx.sock < 0) return;
 HDEBUG(D_acl) debug_printf_indent("release cutthrough conn: %s\n", why);
-cutthrough.fd = -1;
+cutthrough.cctx.sock = -1;
+cutthrough.cctx.tls_ctx = NULL;
 cutthrough.delivery = cutthrough.callout_hold_only = FALSE;
 }
 
@@ -1442,7 +1462,6 @@ uschar *
 cutthrough_finaldot(void)
 {
 uschar res;
-address_item * addr;
 HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP>> .\n");
 
 /* Assume data finshed with new-line */
@@ -1452,8 +1471,9 @@ if(  !cutthrough_puts(US".", 1)
   )
   return cutthrough.addr.message;
 
-res = cutthrough_response(cutthrough.fd, '2', &cutthrough.addr.message, CUTTHROUGH_DATA_TIMEOUT);
-for (addr = &cutthrough.addr; addr; addr = addr->next)
+res = cutthrough_response(&cutthrough.cctx, '2', &cutthrough.addr.message,
+       CUTTHROUGH_DATA_TIMEOUT);
+for (address_item * addr = &cutthrough.addr; addr; addr = addr->next)
   {
   addr->message = cutthrough.addr.message;
   switch(res)
@@ -1604,18 +1624,18 @@ Returns:           OK      address verified
 */
 
 int
-verify_address(address_item *vaddr, FILE *f, int options, int callout,
-  int callout_overall, int callout_connect, uschar *se_mailfrom,
+verify_address(address_item * vaddr, FILE * fp, int options, int callout,
+  int callout_overall, int callout_connect, uschar * se_mailfrom,
   uschar *pm_mailfrom, BOOL *routed)
 {
 BOOL allok = TRUE;
-BOOL full_info = (f == NULL)? FALSE : (debug_selector != 0);
+BOOL full_info = fp ? debug_selector != 0 : FALSE;
 BOOL expn         = (options & vopt_expn) != 0;
 BOOL success_on_redirect = (options & vopt_success_on_redirect) != 0;
 int i;
 int yield = OK;
 int verify_type = expn? v_expn :
-     address_test_mode? v_none :
+   f.address_test_mode? v_none :
           options & vopt_is_recipient? v_recipient : v_sender;
 address_item *addr_list;
 address_item *addr_new = NULL;
@@ -1648,10 +1668,10 @@ else ko_prefix = cr = US"";
 
 if (parse_find_at(address) == NULL)
   {
-  if ((options & vopt_qualify) == 0)
+  if (!(options & vopt_qualify))
     {
-    if (f != NULL)
-      respond_printf(f, "%sA domain is required for \"%s\"%s\n",
+    if (fp)
+      respond_printf(fp, "%sA domain is required for \"%s\"%s\n",
         ko_prefix, address, cr);
     *failure_ptr = US"qualify";
     return FAIL;
@@ -1662,36 +1682,36 @@ if (parse_find_at(address) == NULL)
 DEBUG(D_verify)
   {
   debug_printf(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n");
-  debug_printf("%s %s\n", address_test_mode? "Testing" : "Verifying", address);
+  debug_printf("%s %s\n", f.address_test_mode? "Testing" : "Verifying", address);
   }
 
 /* Rewrite and report on it. Clear the domain and local part caches - these
 may have been set by domains and local part tests during an ACL. */
 
-if (global_rewrite_rules != NULL)
+if (global_rewrite_rules)
   {
   uschar *old = address;
   address = rewrite_address(address, options & vopt_is_recipient, FALSE,
     global_rewrite_rules, rewrite_existflags);
   if (address != old)
     {
-    for (i = 0; i < (MAX_NAMED_LIST * 2)/32; i++) vaddr->localpart_cache[i] = 0;
-    for (i = 0; i < (MAX_NAMED_LIST * 2)/32; i++) vaddr->domain_cache[i] = 0;
-    if (f != NULL && !expn) fprintf(f, "Address rewritten as: %s\n", address);
+    for (int i = 0; i < (MAX_NAMED_LIST * 2)/32; i++) vaddr->localpart_cache[i] = 0;
+    for (int i = 0; i < (MAX_NAMED_LIST * 2)/32; i++) vaddr->domain_cache[i] = 0;
+    if (fp && !expn) fprintf(fp, "Address rewritten as: %s\n", address);
     }
   }
 
 /* If this is the real sender address, we must update sender_address at
 this point, because it may be referred to in the routers. */
 
-if ((options & (vopt_fake_sender|vopt_is_recipient)) == 0)
+if (!(options & (vopt_fake_sender|vopt_is_recipient)))
   sender_address = address;
 
 /* If the address was rewritten to <> no verification can be done, and we have
 to return OK. This rewriting is permitted only for sender addresses; for other
 addresses, such rewriting fails. */
 
-if (address[0] == 0) return OK;
+if (!address[0]) return OK;
 
 /* Flip the legacy TLS-related variables over to the outbound set in case
 they're used in the context of a transport used by verification. Reset them
@@ -1743,29 +1763,29 @@ while (addr_new)
   if (testflag(addr, af_pfr))
     {
     allok = FALSE;
-    if (f != NULL)
+    if (fp)
       {
       BOOL allow;
 
       if (addr->address[0] == '>')
         {
         allow = testflag(addr, af_allow_reply);
-        fprintf(f, "%s -> mail %s", addr->parent->address, addr->address + 1);
+        fprintf(fp, "%s -> mail %s", addr->parent->address, addr->address + 1);
         }
       else
         {
-        allow = (addr->address[0] == '|')?
-          testflag(addr, af_allow_pipe) : testflag(addr, af_allow_file);
-        fprintf(f, "%s -> %s", addr->parent->address, addr->address);
+        allow = addr->address[0] == '|'
+          testflag(addr, af_allow_pipe) : testflag(addr, af_allow_file);
+        fprintf(fp, "%s -> %s", addr->parent->address, addr->address);
         }
 
       if (addr->basic_errno == ERRNO_BADTRANSPORT)
-        fprintf(f, "\n*** Error in setting up pipe, file, or autoreply:\n"
+        fprintf(fp, "\n*** Error in setting up pipe, file, or autoreply:\n"
           "%s\n", addr->message);
       else if (allow)
-        fprintf(f, "\n  transport = %s\n", addr->transport->name);
+        fprintf(fp, "\n  transport = %s\n", addr->transport->name);
       else
-        fprintf(f, " *** forbidden ***\n");
+        fprintf(fp, " *** forbidden ***\n");
       }
     continue;
     }
@@ -1854,7 +1874,6 @@ while (addr_new)
           else
             {
             int flags;
-            host_item *host, *nexthost;
             host_build_hostlist(&host_list, s, tf.hosts_randomize);
 
             /* Just ignore failures to find a host address. If we don't manage
@@ -1867,7 +1886,7 @@ while (addr_new)
             if (tf.qualify_single) flags |= HOST_FIND_QUALIFY_SINGLE;
             if (tf.search_parents) flags |= HOST_FIND_SEARCH_PARENTS;
 
-            for (host = host_list; host; host = nexthost)
+            for (host_item * host = host_list, * nexthost; host; host = nexthost)
               {
               nexthost = host->next;
               if (tf.gethostbyname ||
@@ -1875,16 +1894,16 @@ while (addr_new)
                 (void)host_find_byname(host, NULL, flags, NULL, TRUE);
               else
                {
-               dnssec_domains * dnssec_domains = NULL;
+               const dnssec_domains * dsp = NULL;
                if (Ustrcmp(tp->driver_name, "smtp") == 0)
                  {
                  smtp_transport_options_block * ob =
                      (smtp_transport_options_block *) tp->options_block;
-                 dnssec_domains = &ob->dnssec;
+                 dsp = &ob->dnssec;
                  }
 
                 (void) host_find_bydns(host, NULL, flags, NULL, NULL, NULL,
-                 dnssec_domains, NULL, NULL);
+                 dsp, NULL, NULL);
                }
               }
             }
@@ -1897,7 +1916,7 @@ while (addr_new)
       if (host_list)
         {
         HDEBUG(D_verify) debug_printf("Attempting full verification using callout\n");
-        if (host_checking && !host_checking_callout)
+        if (host_checking && !f.host_checking_callout)
           {
           HDEBUG(D_verify)
             debug_printf("... callout omitted by default when host testing\n"
@@ -1910,12 +1929,15 @@ while (addr_new)
 #endif
           rc = do_callout(addr, host_list, &tf, callout, callout_overall,
             callout_connect, options, se_mailfrom, pm_mailfrom);
+#ifdef SUPPORT_TLS
+         deliver_set_expansions(NULL);
+#endif
           }
         }
       else
         {
         HDEBUG(D_verify) debug_printf("Cannot do callout: neither router nor "
-          "transport provided a host list\n");
+          "transport provided a host list, or transport is not smtp\n");
         }
       }
     }
@@ -1935,29 +1957,29 @@ while (addr_new)
   if (rc == FAIL)
     {
     allok = FALSE;
-    if (f)
+    if (fp)
       {
       address_item *p = addr->parent;
 
-      respond_printf(f, "%s%s %s", ko_prefix,
+      respond_printf(fp, "%s%s %s", ko_prefix,
         full_info ? addr->address : address,
-        address_test_mode ? "is undeliverable" : "failed to verify");
-      if (!expn && admin_user)
+        f.address_test_mode ? "is undeliverable" : "failed to verify");
+      if (!expn && f.admin_user)
         {
         if (addr->basic_errno > 0)
-          respond_printf(f, ": %s", strerror(addr->basic_errno));
+          respond_printf(fp, ": %s", strerror(addr->basic_errno));
         if (addr->message)
-          respond_printf(f, ": %s", addr->message);
+          respond_printf(fp, ": %s", addr->message);
         }
 
       /* Show parents iff doing full info */
 
       if (full_info) while (p)
         {
-        respond_printf(f, "%s\n    <-- %s", cr, p->address);
+        respond_printf(fp, "%s\n    <-- %s", cr, p->address);
         p = p->parent;
         }
-      respond_printf(f, "%s\n", cr);
+      respond_printf(fp, "%s\n", cr);
       }
     cancel_cutthrough_connection(TRUE, US"routing hard fail");
 
@@ -1974,29 +1996,29 @@ while (addr_new)
   else if (rc == DEFER)
     {
     allok = FALSE;
-    if (f)
+    if (fp)
       {
       address_item *p = addr->parent;
-      respond_printf(f, "%s%s cannot be resolved at this time", ko_prefix,
+      respond_printf(fp, "%s%s cannot be resolved at this time", ko_prefix,
         full_info? addr->address : address);
-      if (!expn && admin_user)
+      if (!expn && f.admin_user)
         {
         if (addr->basic_errno > 0)
-          respond_printf(f, ": %s", strerror(addr->basic_errno));
+          respond_printf(fp, ": %s", strerror(addr->basic_errno));
         if (addr->message)
-          respond_printf(f, ": %s", addr->message);
+          respond_printf(fp, ": %s", addr->message);
         else if (addr->basic_errno <= 0)
-          respond_printf(f, ": unknown error");
+          respond_printf(fp, ": unknown error");
         }
 
       /* Show parents iff doing full info */
 
       if (full_info) while (p)
         {
-        respond_printf(f, "%s\n    <-- %s", cr, p->address);
+        respond_printf(fp, "%s\n    <-- %s", cr, p->address);
         p = p->parent;
         }
-      respond_printf(f, "%s\n", cr);
+      respond_printf(fp, "%s\n", cr);
       }
     cancel_cutthrough_connection(TRUE, US"routing soft fail");
 
@@ -2017,16 +2039,16 @@ while (addr_new)
 
     if (!addr_new)
       if (!addr_local && !addr_remote)
-        respond_printf(f, "250 mail to <%s> is discarded\r\n", address);
+        respond_printf(fp, "250 mail to <%s> is discarded\r\n", address);
       else
-        respond_printf(f, "250 <%s>\r\n", address);
+        respond_printf(fp, "250 <%s>\r\n", address);
 
     else do
       {
       address_item *addr2 = addr_new;
       addr_new = addr2->next;
       if (!addr_new) ok_prefix = US"250 ";
-      respond_printf(f, "%s<%s>\r\n", ok_prefix, addr2->address);
+      respond_printf(fp, "%s<%s>\r\n", ok_prefix, addr2->address);
       } while (addr_new);
     yield = OK;
     goto out;
@@ -2060,8 +2082,8 @@ while (addr_new)
          )  )
        )
       {
-      if (f) fprintf(f, "%s %s\n",
-        address, address_test_mode ? "is deliverable" : "verified");
+      if (fp) fprintf(fp, "%s %s\n",
+        address, f.address_test_mode ? "is deliverable" : "verified");
 
       /* If we have carried on to verify a child address, we want the value
       of $address_data to be that of the child */
@@ -2080,7 +2102,7 @@ while (addr_new)
   }     /* Loop for generated addresses */
 
 /* Display the full results of the successful routing, including any generated
-addresses. Control gets here only when full_info is set, which requires f not
+addresses. Control gets here only when full_info is set, which requires fp not
 to be NULL, and this occurs only when a top-level verify is called with the
 debugging switch on.
 
@@ -2090,7 +2112,7 @@ discarded, usually because of the use of :blackhole: in an alias file. */
 
 if (allok && !addr_local && !addr_remote)
   {
-  fprintf(f, "mail to %s is discarded\n", address);
+  fprintf(fp, "mail to %s is discarded\n", address);
   goto out;
   }
 
@@ -2098,15 +2120,14 @@ for (addr_list = addr_local, i = 0; i < 2; addr_list = addr_remote, i++)
   while (addr_list)
     {
     address_item *addr = addr_list;
-    address_item *p = addr->parent;
     transport_instance * tp = addr->transport;
 
     addr_list = addr->next;
 
-    fprintf(f, "%s", CS addr->address);
+    fprintf(fp, "%s", CS addr->address);
 #ifdef EXPERIMENTAL_SRS
     if(addr->prop.srs_sender)
-      fprintf(f, "    [srs = %s]", addr->prop.srs_sender);
+      fprintf(fp, "    [srs = %s]", addr->prop.srs_sender);
 #endif
 
     /* If the address is a duplicate, show something about it. */
@@ -2115,19 +2136,19 @@ for (addr_list = addr_local, i = 0; i < 2; addr_list = addr_remote, i++)
       {
       tree_node *tnode;
       if ((tnode = tree_search(tree_duplicates, addr->unique)))
-        fprintf(f, "   [duplicate, would not be delivered]");
+        fprintf(fp, "   [duplicate, would not be delivered]");
       else tree_add_duplicate(addr->unique, addr);
       }
 
     /* Now show its parents */
 
-    for (p = addr->parent; p; p = p->parent)
-      fprintf(f, "\n    <-- %s", p->address);
-    fprintf(f, "\n  ");
+    for (address_item * p = addr->parent; p; p = p->parent)
+      fprintf(fp, "\n    <-- %s", p->address);
+    fprintf(fp, "\n  ");
 
     /* Show router, and transport */
 
-    fprintf(f, "router = %s, transport = %s\n",
+    fprintf(fp, "router = %s, transport = %s\n",
       addr->router->name, tp ? tp->name : US"unset");
 
     /* Show any hosts that are set up by a router unless the transport
@@ -2135,32 +2156,31 @@ for (addr_list = addr_local, i = 0; i < 2; addr_list = addr_remote, i++)
 
     if (addr->host_list && tp && !tp->overrides_hosts)
       {
-      host_item *h;
       int maxlen = 0;
       int maxaddlen = 0;
-      for (h = addr->host_list; h; h = h->next)
+      for (host_item * h = addr->host_list; h; h = h->next)
         {                              /* get max lengths of host names, addrs */
         int len = Ustrlen(h->name);
         if (len > maxlen) maxlen = len;
         len = h->address ? Ustrlen(h->address) : 7;
         if (len > maxaddlen) maxaddlen = len;
         }
-      for (h = addr->host_list; h; h = h->next)
+      for (host_item * h = addr->host_list; h; h = h->next)
        {
-       fprintf(f, "  host %-*s ", maxlen, h->name);
+       fprintf(fp, "  host %-*s ", maxlen, h->name);
 
        if (h->address)
-         fprintf(f, "[%s%-*c", h->address, maxaddlen+1 - Ustrlen(h->address), ']');
+         fprintf(fp, "[%s%-*c", h->address, maxaddlen+1 - Ustrlen(h->address), ']');
        else if (tp->info->local)
-         fprintf(f, " %-*s ", maxaddlen, "");  /* Omit [unknown] for local */
+         fprintf(fp, " %-*s ", maxaddlen, "");  /* Omit [unknown] for local */
        else
-         fprintf(f, "[%s%-*c", "unknown", maxaddlen+1 - 7, ']');
+         fprintf(fp, "[%s%-*c", "unknown", maxaddlen+1 - 7, ']');
 
-        if (h->mx >= 0) fprintf(f, " MX=%d", h->mx);
-        if (h->port != PORT_NONE) fprintf(f, " port=%d", h->port);
-        if (running_in_test_harness  &&  h->dnssec == DS_YES) fputs(" AD", f);
-        if (h->status == hstatus_unusable) fputs(" ** unusable **", f);
-       fputc('\n', f);
+        if (h->mx >= 0) fprintf(fp, " MX=%d", h->mx);
+        if (h->port != PORT_NONE) fprintf(fp, " port=%d", h->port);
+        if (f.running_in_test_harness  &&  h->dnssec == DS_YES) fputs(" AD", fp);
+        if (h->status == hstatus_unusable) fputs(" ** unusable **", fp);
+       fputc('\n', fp);
         }
       }
     }
@@ -2195,11 +2215,10 @@ Returns:     OK
 int
 verify_check_headers(uschar **msgptr)
 {
-header_line *h;
 uschar *colon, *s;
 int yield = OK;
 
-for (h = header_list; h && yield == OK; h = h->next)
+for (header_line * h = header_list; h && yield == OK; h = h->next)
   {
   if (h->type != htype_from &&
       h->type != htype_reply_to &&
@@ -2216,7 +2235,7 @@ for (h = header_list; h && yield == OK; h = h->next)
   /* Loop for multiple addresses in the header, enabling group syntax. Note
   that we have to reset this after the header has been scanned. */
 
-  parse_allow_group = TRUE;
+  f.parse_allow_group = TRUE;
 
   while (*s)
     {
@@ -2239,11 +2258,11 @@ for (h = header_list; h && yield == OK; h = h->next)
       {
       if (h->type == htype_from || h->type == htype_sender)
         {
-        if (!allow_unqualified_sender) recipient = NULL;
+        if (!f.allow_unqualified_sender) recipient = NULL;
         }
       else
         {
-        if (!allow_unqualified_recipient) recipient = NULL;
+        if (!f.allow_unqualified_recipient) recipient = NULL;
         }
       if (recipient == NULL) errmess = US"unqualified address not permitted";
       }
@@ -2293,8 +2312,8 @@ for (h = header_list; h && yield == OK; h = h->next)
     while (isspace(*s)) s++;
     }   /* Next address */
 
-  parse_allow_group = FALSE;
-  parse_found_group = FALSE;
+  f.parse_allow_group = FALSE;
+  f.parse_found_group = FALSE;
   }     /* Next header unless yield has been set FALSE */
 
 return yield;
@@ -2318,13 +2337,12 @@ Returns:     OK
 int
 verify_check_header_names_ascii(uschar **msgptr)
 {
-header_line *h;
-uschar *colon, *s;
+uschar *colon;
 
-for (h = header_list; h; h = h->next)
+for (header_line * h = header_list; h; h = h->next)
   {
   colon = Ustrchr(h->text, ':');
-  for(s = h->text; s < colon; s++)
+  for(uschar * s = h->text; s < colon; s++)
     if ((*s < 33) || (*s > 126))
       {
       *msgptr = string_sprintf("Invalid character in header \"%.*s\" found",
@@ -2348,22 +2366,20 @@ The original proposed patch did the former, but I have chosen to do the latter,
 because (a) it requires no memory and (b) will use fewer resources when there
 are many addresses in To: and/or Cc: and only one or two envelope recipients.
 
-Arguments:   none
+Arguments:   case_sensitive   true if case sensitive matching should be used
 Returns:     OK    if there are no blind recipients
              FAIL  if there is at least one blind recipient
 */
 
 int
-verify_check_notblind(void)
+verify_check_notblind(BOOL case_sensitive)
 {
-int i;
-for (i = 0; i < recipients_count; i++)
+for (int i = 0; i < recipients_count; i++)
   {
-  header_line *h;
   BOOL found = FALSE;
   uschar *address = recipients_list[i].address;
 
-  for (h = header_list; !found && h != NULL; h = h->next)
+  for (header_line * h = header_list; !found && h; h = h->next)
     {
     uschar *colon, *s;
 
@@ -2376,12 +2392,12 @@ for (i = 0; i < recipients_count; i++)
     /* Loop for multiple addresses in the header, enabling group syntax. Note
     that we have to reset this after the header has been scanned. */
 
-    parse_allow_group = TRUE;
+    f.parse_allow_group = TRUE;
 
-    while (*s != 0)
+    while (*s)
       {
-      uschar *ss = parse_find_address_end(s, FALSE);
-      uschar *recipient,*errmess;
+      uschar * ss = parse_find_address_end(s, FALSE);
+      uschar * recipient, * errmess;
       int terminator = *ss;
       int start, end, domain;
 
@@ -2393,26 +2409,27 @@ for (i = 0; i < recipients_count; i++)
       *ss = terminator;
 
       /* If we found a valid recipient that has a domain, compare it with the
-      envelope recipient. Local parts are compared case-sensitively, domains
-      case-insensitively. By comparing from the start with length "domain", we
-      include the "@" at the end, which ensures that we are comparing the whole
-      local part of each address. */
-
-      if (recipient != NULL && domain != 0)
-        {
-        found = Ustrncmp(recipient, address, domain) == 0 &&
-                strcmpic(recipient + domain, address + domain) == 0;
-        if (found) break;
-        }
+      envelope recipient. Local parts are compared with case-sensitivity
+      according to the routine arg, domains case-insensitively.
+      By comparing from the start with length "domain", we include the "@" at
+      the end, which ensures that we are comparing the whole local part of each
+      address. */
+
+      if (recipient && domain != 0)
+        if ((found = (case_sensitive
+               ? Ustrncmp(recipient, address, domain) == 0
+               : strncmpic(recipient, address, domain) == 0)
+             && strcmpic(recipient + domain, address + domain) == 0))
+         break;
 
       /* Advance to the next address */
 
-      s = ss + (terminator? 1:0);
+      s = ss + (terminator ? 1:0);
       while (isspace(*s)) s++;
       }   /* Next address */
 
-    parse_allow_group = FALSE;
-    parse_found_group = FALSE;
+    f.parse_allow_group = FALSE;
+    f.parse_found_group = FALSE;
     }     /* Next header (if found is false) */
 
   if (!found) return FAIL;
@@ -2440,10 +2457,9 @@ Returns:     pointer to an address item, or NULL
 address_item *
 verify_checked_sender(uschar *sender)
 {
-address_item *addr;
-for (addr = sender_verified_list; addr != NULL; addr = addr->next)
-  if (Ustrcmp(sender, addr->address) == 0) break;
-return addr;
+for (address_item * addr = sender_verified_list; addr; addr = addr->next)
+  if (Ustrcmp(sender, addr->address) == 0) return addr;
+return NULL;
 }
 
 
@@ -2497,12 +2513,9 @@ verify_check_header_address(uschar **user_msgptr, uschar **log_msgptr,
 static int header_types[] = { htype_sender, htype_reply_to, htype_from };
 BOOL done = FALSE;
 int yield = FAIL;
-int i;
 
-for (i = 0; i < 3 && !done; i++)
-  {
-  header_line *h;
-  for (h = header_list; h != NULL && !done; h = h->next)
+for (int i = 0; i < 3 && !done; i++)
+  for (header_line * h = header_list; h != NULL && !done; h = h->next)
     {
     int terminator, new_ok;
     uschar *s, *ss, *endname;
@@ -2513,7 +2526,7 @@ for (i = 0; i < 3 && !done; i++)
     /* Scan the addresses in the header, enabling group syntax. Note that we
     have to reset this after the header has been scanned. */
 
-    parse_allow_group = TRUE;
+    f.parse_allow_group = TRUE;
 
     while (*s != 0)
       {
@@ -2631,10 +2644,10 @@ for (i = 0; i < 3 && !done; i++)
       s = ss;
       }     /* Next address */
 
-    parse_allow_group = FALSE;
-    parse_found_group = FALSE;
+    f.parse_allow_group = FALSE;
+    f.parse_found_group = FALSE;
     }       /* Next header, unless done */
-  }         /* Next header type unless done */
+           /* Next header type unless done */
 
 if (yield == FAIL && *log_msgptr == NULL)
   *log_msgptr = US"there is no valid sender in any header line";
@@ -2670,7 +2683,8 @@ Side effect: any received ident value is put in sender_ident (NULL otherwise)
 void
 verify_get_ident(int port)
 {
-int sock, host_af, qlen;
+client_conn_ctx ident_conn_ctx = {0};
+int host_af, qlen;
 int received_sender_port, received_interface_port, n;
 uschar *p;
 blob early_data;
@@ -2690,9 +2704,9 @@ to the incoming interface address. If the sender host address is an IPv6
 address, the incoming interface address will also be IPv6. */
 
 host_af = Ustrchr(sender_host_address, ':') == NULL ? AF_INET : AF_INET6;
-if ((sock = ip_socket(SOCK_STREAM, host_af)) < 0) return;
+if ((ident_conn_ctx.sock = ip_socket(SOCK_STREAM, host_af)) < 0) return;
 
-if (ip_bind(sock, host_af, interface_address, 0) < 0)
+if (ip_bind(ident_conn_ctx.sock, host_af, interface_address, 0) < 0)
   {
   DEBUG(D_ident) debug_printf("bind socket for ident failed: %s\n",
     strerror(errno));
@@ -2706,7 +2720,8 @@ qlen = snprintf(CS buffer, sizeof(buffer), "%d , %d\r\n",
 early_data.data = buffer;
 early_data.len = qlen;
 
-if (ip_connect(sock, host_af, sender_host_address, port,
+/*XXX we trust that the query is idempotent */
+if (ip_connect(ident_conn_ctx.sock, host_af, sender_host_address, port,
                rfc1413_query_timeout, &early_data) < 0)
   {
   if (errno == ETIMEDOUT && LOGGING(ident_timeout))
@@ -2730,7 +2745,7 @@ for (;;)
   int size = sizeof(buffer) - (p - buffer);
 
   if (size <= 0) goto END_OFF;   /* Buffer filled without seeing \n. */
-  count = ip_recv(sock, p, size, rfc1413_query_timeout);
+  count = ip_recv(&ident_conn_ctx, p, size, rfc1413_query_timeout);
   if (count <= 0) goto END_OFF;  /* Read error or EOF */
 
   /* Scan what we just read, to see if we have reached the terminating \r\n. Be
@@ -2795,7 +2810,7 @@ sender_ident = US string_printing(string_copyn(p, 127));
 DEBUG(D_ident) debug_printf("sender_ident = %s\n", sender_ident);
 
 END_OFF:
-(void)close(sock);
+(void)close(ident_conn_ctx.sock);
 return;
 }
 
@@ -2872,8 +2887,7 @@ if (*ss == '@')
     }
   else if (Ustrcmp(ss, "@[]") == 0)
     {
-    ip_address_item *ip;
-    for (ip = host_find_interfaces(); ip != NULL; ip = ip->next)
+    for (ip_address_item * ip = host_find_interfaces(); ip; ip = ip->next)
       if (Ustrcmp(ip->address, cb->host_address) == 0) return OK;
     return FAIL;
     }
@@ -2896,7 +2910,7 @@ only by digits and dots (a slash at the start indicates a file name and of
 course slashes may be present in lookups, but not preceded only by digits and
 dots). */
 
-for (t = ss; isdigit(*t) || *t == '.'; t++);
+for (t = ss; isdigit(*t) || *t == '.'; ) t++;
 if (*t == 0 || (*t == '/' && t != ss))
   {
   *error = US"malformed IPv4 address or address mask";
@@ -2930,7 +2944,8 @@ if (Ustrncmp(ss, "net", 3) == 0 && semicolon != NULL)
   if (mlen == 0 && t == ss+3) mlen = -1;  /* No mask supplied */
   iplookup = (*t++ == '-');
   }
-else t = ss;
+else
+  t = ss;
 
 /* Do the IP address lookup if that is indeed what we have */
 
@@ -2989,8 +3004,8 @@ if (iplookup)
     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", search_error_message);
 
   result = search_find(handle, filename, key, -1, NULL, 0, 0, NULL);
-  if (valueptr != NULL) *valueptr = result;
-  return (result != NULL)? OK : search_find_defer? DEFER: FAIL;
+  if (valueptr) *valueptr = result;
+  return result ? OK : f.search_find_defer ? DEFER: FAIL;
   }
 
 /* The pattern is not an IP address or network reference of any kind. That is,
@@ -3032,11 +3047,8 @@ if (*t == 0)
   rc = host_find_byname(&h, NULL, HOST_FIND_QUALIFY_SINGLE, NULL, FALSE);
   if (rc == HOST_FOUND || rc == HOST_FOUND_LOCAL)
     {
-    host_item *hh;
-    for (hh = &h; hh != NULL; hh = hh->next)
-      {
+    for (host_item * hh = &h; hh; hh = hh->next)
       if (host_is_in_net(hh->address, cb->host_address, 0)) return OK;
-      }
     return FAIL;
     }
   if (rc == HOST_FIND_AGAIN) return DEFER;
@@ -3205,9 +3217,9 @@ return rc;
 *      Check the given host item matches a list  *
 *************************************************/
 int
-verify_check_given_host(uschar **listptr, host_item *host)
+verify_check_given_host(const uschar **listptr, const host_item *host)
 {
-return verify_check_this_host(CUSS listptr, NULL, host->name, host->address, NULL);
+return verify_check_this_host(listptr, NULL, host->name, host->address, NULL);
 }
 
 /*************************************************
@@ -3230,7 +3242,7 @@ int
 verify_check_host(uschar **listptr)
 {
 return verify_check_this_host(CUSS listptr, sender_host_cache, NULL,
-  (sender_host_address == NULL)? US"" : sender_host_address, NULL);
+  sender_host_address ? sender_host_address : US"", NULL);
 }
 
 
@@ -3265,9 +3277,8 @@ always 1. */
 
 if (host_aton(address, bin) == 1)
   {
-  int i;
   int x = bin[0];
-  for (i = 0; i < 4; i++)
+  for (int i = 0; i < 4; i++)
     {
     sprintf(CS bptr, "%d.", x & 255);
     while (*bptr) bptr++;
@@ -3281,19 +3292,16 @@ unknown. This is just a guess. */
 
 #if HAVE_IPV6
 else
-  {
-  int i, j;
-  for (j = 3; j >= 0; j--)
+  for (int j = 3; j >= 0; j--)
     {
     int x = bin[j];
-    for (i = 0; i < 8; i++)
+    for (int i = 0; i < 8; i++)
       {
       sprintf(CS bptr, "%x.", x & 15);
       while (*bptr) bptr++;
       x >>= 4;
       }
     }
-  }
 #endif
 
 /* Remove trailing period -- this is needed so that both arbitrary
@@ -3400,9 +3408,8 @@ else
 
   /* If the lookup succeeded, cache the RHS address. The code allows for
   more than one address - this was for complete generality and the possible
-  use of A6 records. However, A6 records have been reduced to experimental
-  status (August 2001) and may die out. So they may never get used at all,
-  let alone in dnsbl records. However, leave the code here, just in case.
+  use of A6 records. However, A6 records are no longer supported. Leave the code
+  here, just in case.
 
   Quite apart from one A6 RR generating multiple addresses, there are DNS
   lists that return more than one A record, so we must handle multiple
@@ -3413,10 +3420,8 @@ else
 
   if (cb->rc == DNS_SUCCEED)
     {
-    dns_record *rr;
-    dns_address **addrp = &(cb->rhs);
-    for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
-         rr;
+    dns_address ** addrp = &(cb->rhs);
+    for (dns_record * rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); rr;
          rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
       if (rr->type == T_A)
         {
@@ -3569,21 +3574,17 @@ if (cb->rc == DNS_SUCCEED)
     {
     cb->text_set = TRUE;
     if (dns_basic_lookup(&dnsa, query, T_TXT) == DNS_SUCCEED)
-      {
-      dns_record *rr;
-      for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
-           rr;
+      for (dns_record * rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); rr;
            rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
-        if (rr->type == T_TXT) break;
-      if (rr)
-        {
-        int len = (rr->data)[0];
-        if (len > 511) len = 127;
-        store_pool = POOL_PERM;
-        cb->text = string_sprintf("%.*s", len, CUS (rr->data+1));
-        store_pool = old_pool;
-        }
-      }
+        if (rr->type == T_TXT)
+         {
+         int len = (rr->data)[0];
+         if (len > 511) len = 127;
+         store_pool = POOL_PERM;
+         cb->text = string_sprintf("%.*s", len, CUS (rr->data+1));
+         store_pool = old_pool;
+         break;
+         }
     }
 
   dnslist_value = addlist;
@@ -3677,7 +3678,6 @@ int sep = 0;
 int defer_return = FAIL;
 const uschar *list = *listptr;
 uschar *domain;
-uschar *s;
 uschar buffer[1024];
 uschar revadd[128];        /* Long enough for IPv6 address */
 
@@ -3768,32 +3768,28 @@ while ((domain = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL
   actually causing an error here, because that would no doubt hold up incoming
   mail. Instead, I'll just log it. */
 
-  for (s = domain; *s != 0; s++)
-    {
+  for (uschar * s = domain; *s; s++)
     if (!isalnum(*s) && *s != '-' && *s != '.' && *s != '_')
       {
       log_write(0, LOG_MAIN, "dnslists domain \"%s\" contains "
         "strange characters - is this right?", domain);
       break;
       }
-    }
 
   /* Check the alternate domain if present */
 
-  if (domain_txt != domain) for (s = domain_txt; *s != 0; s++)
-    {
+  if (domain_txt != domain) for (uschar * s = domain_txt; *s; s++)
     if (!isalnum(*s) && *s != '-' && *s != '.' && *s != '_')
       {
       log_write(0, LOG_MAIN, "dnslists domain \"%s\" contains "
         "strange characters - is this right?", domain_txt);
       break;
       }
-    }
 
   /* If there is no key string, construct the query by adding the domain name
   onto the inverted host address, and perform a single DNS lookup. */
 
-  if (key == NULL)
+  if (!key)
     {
     if (where == ACL_WHERE_NOTSMTP_START || where == ACL_WHERE_NOTSMTP)
       {