Fix error logging for dynamically-loaded modules. Bug 2507
[users/jgh/exim.git] / src / src / verify.c
index 236a87c521d4534cd310352857243aba53bdf128..deca5bc6cb714e60e778fd3a1dda933a9f829cb5 100644 (file)
@@ -98,7 +98,7 @@ if (type[0] == 'd' && cache_record->result != ccache_reject)
   {
   if (length == sizeof(dbdata_callout_cache_obs))
     {
   {
   if (length == sizeof(dbdata_callout_cache_obs))
     {
-    dbdata_callout_cache *new = store_get(sizeof(dbdata_callout_cache));
+    dbdata_callout_cache *new = store_get(sizeof(dbdata_callout_cache), FALSE);
     memcpy(new, cache_record, length);
     new->postmaster_stamp = new->random_stamp = new->time_stamp;
     cache_record = new;
     memcpy(new, cache_record, length);
     new->postmaster_stamp = new->random_stamp = new->time_stamp;
     cache_record = new;
@@ -140,7 +140,7 @@ if (options & vopt_callout_no_cache)
   {
   HDEBUG(D_verify) debug_printf("callout cache: disabled by no_cache\n");
   }
   {
   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");
   }
   {
   HDEBUG(D_verify) debug_printf("callout cache: not available\n");
   }
@@ -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)
 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");
     }
     {
     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)
 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");
   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;
   transport_feedback * tf, int * yield)
 {
 BOOL done = FALSE;
-host_item * host;
 
 if (addr->transport == cutthrough.addr.transport)
 
 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;
     if (Ustrcmp(host->address, cutthrough.host.address) == 0)
       {
       int host_af;
@@ -421,7 +420,7 @@ if (addr->transport == cutthrough.addr.transport)
 
        if (done)
          {
 
        if (done)
          {
-         address_item * na = store_get(sizeof(address_item));
+         address_item * na = store_get(sizeof(address_item), FALSE);
          *na = cutthrough.addr;
          cutthrough.addr = *addr;
          cutthrough.addr.host_used = &cutthrough.host;
          *na = cutthrough.addr;
          cutthrough.addr = *addr;
          cutthrough.addr.host_used = &cutthrough.host;
@@ -575,7 +574,7 @@ else
   {
   smtp_transport_options_block *ob =
     (smtp_transport_options_block *)addr->transport->options_block;
   {
   smtp_transport_options_block *ob =
     (smtp_transport_options_block *)addr->transport->options_block;
-  host_item * host;
+  smtp_context * sx = NULL;
 
   /* 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,
 
   /* 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,
@@ -588,6 +587,10 @@ else
       log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
         "callout_random_local_part: %s", expand_string_message);
 
       log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand "
         "callout_random_local_part: %s", expand_string_message);
 
+  /* Compile regex' used by client-side smtp */
+
+  smtp_deliver_init();
+
   /* Default the connect and overall callout timeouts if not set, and record the
   time we are starting so that we can enforce it. */
 
   /* Default the connect and overall callout timeouts if not set, and record the
   time we are starting so that we can enforce it. */
 
@@ -623,12 +626,11 @@ 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. */
 
   /* 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;
     {
     int host_af;
     int port = 25;
-    uschar *interface = NULL;  /* Outgoing interface to use; NULL => any */
-    smtp_context sx;
+    uschar * interface = NULL;  /* Outgoing interface to use; NULL => any */
 
     if (!host->address)
       {
 
     if (!host->address)
       {
@@ -668,14 +670,17 @@ coding means skipping this whole loop and doing the append separately.  */
       log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address,
         addr->message);
 
       log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address,
         addr->message);
 
-    sx.addrlist = addr;
-    sx.conn_args.host = host;
-    sx.conn_args.host_af = host_af,
-    sx.port = port;
-    sx.conn_args.interface = interface;
-    sx.helo_data = tf->helo_data;
-    sx.conn_args.tblock = addr->transport;
-    sx.verify = TRUE;
+    if (!sx) sx = store_get(sizeof(*sx), TRUE);        /* tainted buffers */
+    memset(sx, 0, sizeof(*sx));
+
+    sx->addrlist = addr;
+    sx->conn_args.host = host;
+    sx->conn_args.host_af = host_af,
+    sx->port = port;
+    sx->conn_args.interface = interface;
+    sx->helo_data = tf->helo_data;
+    sx->conn_args.tblock = addr->transport;
+    sx->verify = TRUE;
 
 tls_retry_connection:
     /* Set the address state so that errors are recorded in it */
 
 tls_retry_connection:
     /* Set the address state so that errors are recorded in it */
@@ -688,8 +693,8 @@ tls_retry_connection:
     SMTP command to send.  If we tried TLS but it failed, try again without
     if permitted */
 
     SMTP command to send.  If we tried TLS but it failed, try again without
     if permitted */
 
-    yield = smtp_setup_conn(&sx, FALSE);
-#ifdef SUPPORT_TLS
+    yield = smtp_setup_conn(sx, FALSE);
+#ifndef DISABLE_TLS
     if (  yield == DEFER
        && addr->basic_errno == ERRNO_TLSFAILURE
        && ob->tls_tempfail_tryclear
     if (  yield == DEFER
        && addr->basic_errno == ERRNO_TLSFAILURE
        && ob->tls_tempfail_tryclear
@@ -700,7 +705,7 @@ tls_retry_connection:
        "%s: callout unencrypted to %s [%s] (not in hosts_require_tls)",
        addr->message, host->name, host->address);
       addr->transport_return = PENDING_DEFER;
        "%s: callout unencrypted to %s [%s] (not in hosts_require_tls)",
        addr->message, host->name, host->address);
       addr->transport_return = PENDING_DEFER;
-      yield = smtp_setup_conn(&sx, TRUE);
+      yield = smtp_setup_conn(sx, TRUE);
       }
 #endif
     if (yield != OK)
       }
 #endif
     if (yield != OK)
@@ -730,11 +735,11 @@ tls_retry_connection:
     addr->authenticator = client_authenticator;
     addr->auth_id = client_authenticated_id;
 
     addr->authenticator = client_authenticator;
     addr->auth_id = client_authenticated_id;
 
-    sx.from_addr = from_address;
-    sx.first_addr = sx.sync_addr = addr;
-    sx.ok = FALSE;                     /*XXX these 3 last might not be needed for verify? */
-    sx.send_rset = TRUE;
-    sx.completed_addr = FALSE;
+    sx->from_addr = from_address;
+    sx->first_addr = sx->sync_addr = addr;
+    sx->ok = FALSE;                    /*XXX these 3 last might not be needed for verify? */
+    sx->send_rset = TRUE;
+    sx->completed_addr = FALSE;
 
     new_domain_record.result = old_domain_cache_result == ccache_reject_mfnull
       ? ccache_reject_mfnull : ccache_accept;
 
     new_domain_record.result = old_domain_cache_result == ccache_reject_mfnull
       ? ccache_reject_mfnull : ccache_accept;
@@ -791,12 +796,12 @@ tls_retry_connection:
       Avoid using a SIZE option on the MAIL for all random-rcpt checks.
       */
 
       Avoid using a SIZE option on the MAIL for all random-rcpt checks.
       */
 
-      sx.avoid_option = OPTION_SIZE;
+      sx->avoid_option = OPTION_SIZE;
 
       /* Remember when we last did a random test */
       new_domain_record.random_stamp = time(NULL);
 
 
       /* Remember when we last did a random test */
       new_domain_record.random_stamp = time(NULL);
 
-      if (smtp_write_mail_and_rcpt_cmds(&sx, &yield) == 0)
+      if (smtp_write_mail_and_rcpt_cmds(sx, &yield) == 0)
        switch(addr->transport_return)
          {
          case PENDING_OK:      /* random was accepted, unfortunately */
        switch(addr->transport_return)
          {
          case PENDING_OK:      /* random was accepted, unfortunately */
@@ -807,36 +812,36 @@ tls_retry_connection:
            goto no_conn;
          case FAIL:            /* rejected: the preferred result */
            new_domain_record.random_result = ccache_reject;
            goto no_conn;
          case FAIL:            /* rejected: the preferred result */
            new_domain_record.random_result = ccache_reject;
-           sx.avoid_option = 0;
+           sx->avoid_option = 0;
 
            /* Between each check, issue RSET, because some servers accept only
            one recipient after MAIL FROM:<>.
            XXX We don't care about that for postmaster_full.  Should we? */
 
            if ((done =
 
            /* Between each check, issue RSET, because some servers accept only
            one recipient after MAIL FROM:<>.
            XXX We don't care about that for postmaster_full.  Should we? */
 
            if ((done =
-             smtp_write_command(&sx, SCMD_FLUSH, "RSET\r\n") >= 0 &&
-             smtp_read_response(&sx, 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;
              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(sx.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
+#ifndef DISABLE_TLS
+           tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
 #endif
            HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP(close)>>\n");
 #endif
            HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP(close)>>\n");
-           (void)close(sx.cctx.sock);
-           sx.cctx.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
            addr->address = main_address;
            addr->transport_return = PENDING_DEFER;
 #ifndef DISABLE_EVENT
            (void) event_raise(addr->transport->event_action,
                              US"tcp:close", NULL);
 #endif
            addr->address = main_address;
            addr->transport_return = PENDING_DEFER;
-           sx.first_addr = sx.sync_addr = addr;
-           sx.ok = FALSE;
-           sx.send_rset = TRUE;
-           sx.completed_addr = FALSE;
+           sx->first_addr = sx->sync_addr = addr;
+           sx->ok = FALSE;
+           sx->send_rset = TRUE;
+           sx->completed_addr = FALSE;
            goto tls_retry_connection;
          case DEFER:           /* 4xx response to random */
            break;              /* Just to be clear. ccache_unknown, !done. */
            goto tls_retry_connection;
          case DEFER:           /* 4xx response to random */
            break;              /* Just to be clear. ccache_unknown, !done. */
@@ -845,10 +850,10 @@ tls_retry_connection:
       /* Re-setup for main verify, or for the error message when failing */
       addr->address = main_address;
       addr->transport_return = PENDING_DEFER;
       /* Re-setup for main verify, or for the error message when failing */
       addr->address = main_address;
       addr->transport_return = PENDING_DEFER;
-      sx.first_addr = sx.sync_addr = addr;
-      sx.ok = FALSE;
-      sx.send_rset = TRUE;
-      sx.completed_addr = FALSE;
+      sx->first_addr = sx->sync_addr = addr;
+      sx->ok = FALSE;
+      sx->send_rset = TRUE;
+      sx->completed_addr = FALSE;
       }
     else
       done = TRUE;
       }
     else
       done = TRUE;
@@ -859,10 +864,10 @@ tls_retry_connection:
     if (done)
       {
       if (!(options & vopt_is_recipient  &&  options & vopt_callout_no_cache))
     if (done)
       {
       if (!(options & vopt_is_recipient  &&  options & vopt_callout_no_cache))
-       sx.avoid_option = OPTION_SIZE;
+       sx->avoid_option = OPTION_SIZE;
 
       done = FALSE;
 
       done = FALSE;
-      switch(smtp_write_mail_and_rcpt_cmds(&sx, &yield))
+      switch(smtp_write_mail_and_rcpt_cmds(sx, &yield))
        {
        case 0:  switch(addr->transport_return) /* ok so far */
                    {
        {
        case 0:  switch(addr->transport_return) /* ok so far */
                    {
@@ -880,7 +885,7 @@ tls_retry_connection:
 
        case -1:                                /* MAIL response error */
                  *failure_ptr = US"mail";
 
        case -1:                                /* MAIL response error */
                  *failure_ptr = US"mail";
-                 if (errno == 0 && sx.buffer[0] == '5')
+                 if (errno == 0 && sx->buffer[0] == '5')
                    {
                    setflag(addr, af_verify_nsfail);
                    if (from_address[0] == 0)
                    {
                    setflag(addr, af_verify_nsfail);
                    if (from_address[0] == 0)
@@ -910,8 +915,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");
 
       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, SCMD_FLUSH, "RSET\r\n") >= 0
-          && smtp_read_response(&sx, 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)
        {
 
       if (done)
        {
@@ -921,23 +926,23 @@ tls_retry_connection:
        addr->address = string_sprintf("postmaster@%.1000s", addr->domain);
        addr->transport_return = PENDING_DEFER;
 
        addr->address = string_sprintf("postmaster@%.1000s", addr->domain);
        addr->transport_return = PENDING_DEFER;
 
-       sx.from_addr = pm_mailfrom;
-       sx.first_addr = sx.sync_addr = addr;
-       sx.ok = FALSE;
-       sx.send_rset = TRUE;
-       sx.completed_addr = FALSE;
-       sx.avoid_option = OPTION_SIZE;
+       sx->from_addr = pm_mailfrom;
+       sx->first_addr = sx->sync_addr = addr;
+       sx->ok = FALSE;
+       sx->send_rset = TRUE;
+       sx->completed_addr = FALSE;
+       sx->avoid_option = OPTION_SIZE;
 
 
-       if(  smtp_write_mail_and_rcpt_cmds(&sx, &yield) == 0
+       if(  smtp_write_mail_and_rcpt_cmds(sx, &yield) == 0
          && addr->transport_return == PENDING_OK
          )
          done = TRUE;
        else
          done = (options & vopt_callout_fullpm) != 0
          && addr->transport_return == PENDING_OK
          )
          done = TRUE;
        else
          done = (options & vopt_callout_fullpm) != 0
-             && smtp_write_command(&sx, SCMD_FLUSH,
+             && smtp_write_command(sx, SCMD_FLUSH,
                            "RCPT TO:<postmaster>\r\n") >= 0
                            "RCPT TO:<postmaster>\r\n") >= 0
-             && smtp_read_response(&sx, sx.buffer,
-                           sizeof(sx.buffer), '2', callout);
+             && smtp_read_response(sx, sx->buffer,
+                           sizeof(sx->buffer), '2', callout);
 
        /* Sort out the cache record */
 
 
        /* Sort out the cache record */
 
@@ -945,7 +950,7 @@ tls_retry_connection:
 
        if (done)
          new_domain_record.postmaster_result = ccache_accept;
 
        if (done)
          new_domain_record.postmaster_result = ccache_accept;
-       else if (errno == 0 && sx.buffer[0] == '5')
+       else if (errno == 0 && sx->buffer[0] == '5')
          {
          *failure_ptr = US"postmaster";
          setflag(addr, af_verify_pmfail);
          {
          *failure_ptr = US"postmaster";
          setflag(addr, af_verify_pmfail);
@@ -970,7 +975,7 @@ no_conn:
       {
       case ETIMEDOUT:
        HDEBUG(D_verify) debug_printf("SMTP timeout\n");
       {
       case ETIMEDOUT:
        HDEBUG(D_verify) debug_printf("SMTP timeout\n");
-       sx.send_quit = FALSE;
+       sx->send_quit = FALSE;
        break;
 
 #ifdef SUPPORT_I18N
        break;
 
 #ifdef SUPPORT_I18N
@@ -978,8 +983,7 @@ no_conn:
        {
        extern int acl_where;   /* src/acl.c */
        errno = 0;
        {
        extern int acl_where;   /* src/acl.c */
        errno = 0;
-       addr->message = string_sprintf(
-           "response to \"EHLO\" did not include SMTPUTF8");
+       addr->message = US"response to \"EHLO\" did not include SMTPUTF8";
        addr->user_message = acl_where == ACL_WHERE_RCPT
          ? US"533 no support for internationalised mailbox name"
          : US"550 mailbox unavailable";
        addr->user_message = acl_where == ACL_WHERE_RCPT
          ? US"533 no support for internationalised mailbox name"
          : US"550 mailbox unavailable";
@@ -987,20 +991,13 @@ no_conn:
        done = TRUE;
        }
        break;
        done = TRUE;
        }
        break;
-#endif
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
-      case ERRNO_REQUIRETLS:
-        addr->user_message = US"530 5.7.4 REQUIRETLS support required";
-       yield = FAIL;
-       done = TRUE;
-       break;
 #endif
       case ECONNREFUSED:
 #endif
       case ECONNREFUSED:
-       sx.send_quit = FALSE;
+       sx->send_quit = FALSE;
        break;
 
       case 0:
        break;
 
       case 0:
-       if (*sx.buffer == 0) Ustrcpy(sx.buffer, US"connection dropped");
+       if (*sx->buffer == 0) Ustrcpy(sx->buffer, US"connection dropped");
 
        /*XXX test here is ugly; seem to have a split of responsibility for
        building this message.  Need to rationalise.  Where is it done
 
        /*XXX test here is ugly; seem to have a split of responsibility for
        building this message.  Need to rationalise.  Where is it done
@@ -1009,16 +1006,36 @@ no_conn:
        */
        if (!addr->message) addr->message =
          string_sprintf("response to \"%s\" was: %s",
        */
        if (!addr->message) addr->message =
          string_sprintf("response to \"%s\" was: %s",
-                         big_buffer, string_printing(sx.buffer));
+                         big_buffer, string_printing(sx->buffer));
+
+       /* RFC 5321 section 4.2: the text portion of the response may have only
+       HT, SP, Printable US-ASCII.  Deal with awkward chars by cutting the
+       received message off before passing it onward.  Newlines are ok; they
+       just become a multiline response (but wrapped in the error code we
+       produce). */
 
 
+       for (uschar * s = sx->buffer;
+            *s && s < sx->buffer + sizeof(sx->buffer);
+            s++)
+         {
+         uschar c = *s;
+         if (c != '\t' && c != '\n' && (c < ' ' || c > '~'))
+           {
+           if (s - sx->buffer < sizeof(sx->buffer) - 12)
+             memcpy(s, "(truncated)", 12);
+           else
+             *s = '\0';
+           break;
+           }
+         }
        addr->user_message = options & vopt_is_recipient
        addr->user_message = options & vopt_is_recipient
-         ? string_sprintf("Callout verification failed:\n%s", sx.buffer)
+         ? string_sprintf("Callout verification failed:\n%s", sx->buffer)
          : string_sprintf("Called:   %s\nSent:     %s\nResponse: %s",
          : string_sprintf("Called:   %s\nSent:     %s\nResponse: %s",
-           host->address, big_buffer, sx.buffer);
+           host->address, big_buffer, sx->buffer);
 
        /* Hard rejection ends the process */
 
 
        /* Hard rejection ends the process */
 
-       if (sx.buffer[0] == '5')   /* Address rejected */
+       if (sx->buffer[0] == '5')   /* Address rejected */
          {
          yield = FAIL;
          done = TRUE;
          {
          yield = FAIL;
          done = TRUE;
@@ -1065,11 +1082,9 @@ no_conn:
        && !random_local_part
        && !pm_mailfrom
        && cutthrough.cctx.sock < 0
        && !random_local_part
        && !pm_mailfrom
        && cutthrough.cctx.sock < 0
-       && !sx.lmtp
+       && !sx->lmtp
        )
       {
        )
       {
-      address_item * parent, * caddr;
-
       HDEBUG(D_acl|D_v) debug_printf_indent("holding verify callout open for %s\n",
        cutthrough.delivery
        ? "cutthrough delivery" : "potential further verifies and delivery");
       HDEBUG(D_acl|D_v) debug_printf_indent("holding verify callout open for %s\n",
        cutthrough.delivery
        ? "cutthrough delivery" : "potential further verifies and delivery");
@@ -1077,7 +1092,7 @@ no_conn:
       cutthrough.callout_hold_only = !cutthrough.delivery;
       cutthrough.is_tls =      tls_out.active.sock >= 0;
       /* We assume no buffer in use in the outblock */
       cutthrough.callout_hold_only = !cutthrough.delivery;
       cutthrough.is_tls =      tls_out.active.sock >= 0;
       /* We assume no buffer in use in the outblock */
-      cutthrough.cctx =                sx.cctx;
+      cutthrough.cctx =                sx->cctx;
       cutthrough.nrcpt =       1;
       cutthrough.transport =   addr->transport->name;
       cutthrough.interface =   interface;
       cutthrough.nrcpt =       1;
       cutthrough.transport =   addr->transport->name;
       cutthrough.interface =   interface;
@@ -1097,10 +1112,10 @@ no_conn:
       cutthrough.addr =                *addr;
       cutthrough.addr.next =   NULL;
       cutthrough.addr.host_used = &cutthrough.host;
       cutthrough.addr =                *addr;
       cutthrough.addr.next =   NULL;
       cutthrough.addr.host_used = &cutthrough.host;
-      for (caddr = &cutthrough.addr, parent = addr->parent;
+      for (address_item * caddr = &cutthrough.addr, * parent = addr->parent;
           parent;
           caddr = caddr->parent, parent = parent->parent)
           parent;
           caddr = caddr->parent, parent = parent->parent)
-        *(caddr->parent = store_get(sizeof(address_item))) = *parent;
+        *(caddr->parent = store_get(sizeof(address_item), FALSE)) = *parent;
 
       ctctx.outblock.buffer = ctbuffer;
       ctctx.outblock.buffersize = sizeof(ctbuffer);
 
       ctctx.outblock.buffer = ctbuffer;
       ctctx.outblock.buffersize = sizeof(ctbuffer);
@@ -1113,26 +1128,23 @@ no_conn:
       /* Ensure no cutthrough on multiple verifies that were incompatible */
       if (options & vopt_callout_recipsender)
         cancel_cutthrough_connection(TRUE, US"not usable for cutthrough");
       /* Ensure no cutthrough on multiple verifies that were incompatible */
       if (options & vopt_callout_recipsender)
         cancel_cutthrough_connection(TRUE, US"not usable for cutthrough");
-      if (sx.send_quit)
-       {
-       (void) smtp_write_command(&sx, SCMD_FLUSH, "QUIT\r\n");
+      if (sx->send_quit)
+       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);
 
 
-       /* Wait a short time for response, and discard it */
-       smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2', 1);
-       }
-
-      if (sx.cctx.sock >= 0)
+      if (sx->cctx.sock >= 0)
        {
        {
-#ifdef SUPPORT_TLS
-       if (sx.cctx.tls_ctx)
+#ifndef DISABLE_TLS
+       if (sx->cctx.tls_ctx)
          {
          {
-         tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
-         sx.cctx.tls_ctx = NULL;
+         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");
          }
 #endif
        HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP(close)>>\n");
-       (void)close(sx.cctx.sock);
-       sx.cctx.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
 #ifndef DISABLE_EVENT
        (void) event_raise(addr->transport->event_action, US"tcp:close", NULL);
 #endif
@@ -1187,7 +1199,7 @@ if (!done)
 /* Come here from within the cache-reading code on fast-track exit. */
 
 END_CALLOUT:
 /* Come here from within the cache-reading code on fast-track exit. */
 
 END_CALLOUT:
-tls_modify_variables(&tls_in);
+tls_modify_variables(&tls_in); /* return variables to inbound values */
 return yield;
 }
 
 return yield;
 }
 
@@ -1229,7 +1241,7 @@ if(cutthrough.cctx.sock < 0)
   return TRUE;
 
 if(
   return TRUE;
 
 if(
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
    cutthrough.is_tls
    ? tls_write(cutthrough.cctx.tls_ctx, ctctx.outblock.buffer, n, FALSE)
    :
    cutthrough.is_tls
    ? tls_write(cutthrough.cctx.tls_ctx, ctctx.outblock.buffer, n, FALSE)
    :
@@ -1430,7 +1442,7 @@ if(fd >= 0)
   /* Wait a short time for response, and discard it */
   cutthrough_response(&tmp_ctx, '2', NULL, 1);
 
   /* Wait a short time for response, and discard it */
   cutthrough_response(&tmp_ctx, '2', NULL, 1);
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
   if (cutthrough.is_tls)
     {
     tls_close(cutthrough.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
   if (cutthrough.is_tls)
     {
     tls_close(cutthrough.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
@@ -1476,7 +1488,6 @@ uschar *
 cutthrough_finaldot(void)
 {
 uschar res;
 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 */
 HDEBUG(D_transport|D_acl|D_v) debug_printf_indent("  SMTP>> .\n");
 
 /* Assume data finshed with new-line */
@@ -1488,7 +1499,7 @@ if(  !cutthrough_puts(US".", 1)
 
 res = cutthrough_response(&cutthrough.cctx, '2', &cutthrough.addr.message,
        CUTTHROUGH_DATA_TIMEOUT);
 
 res = cutthrough_response(&cutthrough.cctx, '2', &cutthrough.addr.message,
        CUTTHROUGH_DATA_TIMEOUT);
-for (addr = &cutthrough.addr; addr; addr = addr->next)
+for (address_item * addr = &cutthrough.addr; addr; addr = addr->next)
   {
   addr->message = cutthrough.addr.message;
   switch(res)
   {
   addr->message = cutthrough.addr.message;
   switch(res)
@@ -1544,6 +1555,8 @@ if (addr != vaddr)
   vaddr->basic_errno = addr->basic_errno;
   vaddr->more_errno = addr->more_errno;
   vaddr->prop.address_data = addr->prop.address_data;
   vaddr->basic_errno = addr->basic_errno;
   vaddr->more_errno = addr->more_errno;
   vaddr->prop.address_data = addr->prop.address_data;
+  vaddr->prop.variables = NULL;
+  tree_dup((tree_node **)&vaddr->prop.variables, addr->prop.variables);
   copyflag(vaddr, addr, af_pass_message);
   }
 return yield;
   copyflag(vaddr, addr, af_pass_message);
   }
 return yield;
@@ -1710,8 +1723,8 @@ if (global_rewrite_rules)
     global_rewrite_rules, rewrite_existflags);
   if (address != old)
     {
     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;
+    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 (fp && !expn) fprintf(fp, "Address rewritten as: %s\n", address);
     }
   }
@@ -1889,7 +1902,6 @@ while (addr_new)
           else
             {
             int flags;
           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
             host_build_hostlist(&host_list, s, tf.hosts_randomize);
 
             /* Just ignore failures to find a host address. If we don't manage
@@ -1902,7 +1914,7 @@ while (addr_new)
             if (tf.qualify_single) flags |= HOST_FIND_QUALIFY_SINGLE;
             if (tf.search_parents) flags |= HOST_FIND_SEARCH_PARENTS;
 
             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 ||
               {
               nexthost = host->next;
               if (tf.gethostbyname ||
@@ -1940,12 +1952,12 @@ while (addr_new)
           }
         else
           {
           }
         else
           {
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
          deliver_set_expansions(addr);
 #endif
           rc = do_callout(addr, host_list, &tf, callout, callout_overall,
             callout_connect, options, se_mailfrom, pm_mailfrom);
          deliver_set_expansions(addr);
 #endif
           rc = do_callout(addr, host_list, &tf, callout, callout_overall,
             callout_connect, options, se_mailfrom, pm_mailfrom);
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
          deliver_set_expansions(NULL);
 #endif
           }
          deliver_set_expansions(NULL);
 #endif
           }
@@ -2105,6 +2117,8 @@ while (addr_new)
       of $address_data to be that of the child */
 
       vaddr->prop.address_data = addr->prop.address_data;
       of $address_data to be that of the child */
 
       vaddr->prop.address_data = addr->prop.address_data;
+      vaddr->prop.variables = NULL;
+      tree_dup((tree_node **)&vaddr->prop.variables, addr->prop.variables);
 
       /* If stopped because more than one new address, cannot cutthrough */
 
 
       /* If stopped because more than one new address, cannot cutthrough */
 
@@ -2136,7 +2150,6 @@ for (addr_list = addr_local, i = 0; i < 2; addr_list = addr_remote, i++)
   while (addr_list)
     {
     address_item *addr = addr_list;
   while (addr_list)
     {
     address_item *addr = addr_list;
-    address_item *p = addr->parent;
     transport_instance * tp = addr->transport;
 
     addr_list = addr->next;
     transport_instance * tp = addr->transport;
 
     addr_list = addr->next;
@@ -2159,7 +2172,7 @@ for (addr_list = addr_local, i = 0; i < 2; addr_list = addr_remote, i++)
 
     /* Now show its parents */
 
 
     /* Now show its parents */
 
-    for (p = addr->parent; p; p = p->parent)
+    for (address_item * p = addr->parent; p; p = p->parent)
       fprintf(fp, "\n    <-- %s", p->address);
     fprintf(fp, "\n  ");
 
       fprintf(fp, "\n    <-- %s", p->address);
     fprintf(fp, "\n  ");
 
@@ -2173,17 +2186,16 @@ for (addr_list = addr_local, i = 0; i < 2; addr_list = addr_remote, i++)
 
     if (addr->host_list && tp && !tp->overrides_hosts)
       {
 
     if (addr->host_list && tp && !tp->overrides_hosts)
       {
-      host_item *h;
       int maxlen = 0;
       int maxaddlen = 0;
       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;
         }
         {                              /* 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(fp, "  host %-*s ", maxlen, h->name);
 
        {
        fprintf(fp, "  host %-*s ", maxlen, h->name);
 
@@ -2208,7 +2220,7 @@ the -bv or -bt case). */
 
 out:
 verify_mode = NULL;
 
 out:
 verify_mode = NULL;
-tls_modify_variables(&tls_in);
+tls_modify_variables(&tls_in); /* return variables to inbound values */
 
 return yield;
 }
 
 return yield;
 }
@@ -2233,11 +2245,10 @@ Returns:     OK
 int
 verify_check_headers(uschar **msgptr)
 {
 int
 verify_check_headers(uschar **msgptr)
 {
-header_line *h;
 uschar *colon, *s;
 int yield = OK;
 
 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 &&
   {
   if (h->type != htype_from &&
       h->type != htype_reply_to &&
@@ -2356,17 +2367,16 @@ Returns:     OK
 int
 verify_check_header_names_ascii(uschar **msgptr)
 {
 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, ':');
   {
   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",
     if ((*s < 33) || (*s > 126))
       {
       *msgptr = string_sprintf("Invalid character in header \"%.*s\" found",
-                            colon - h->text, h->text);
+                            (int)(colon - h->text), h->text);
       return FAIL;
       }
   }
       return FAIL;
       }
   }
@@ -2386,22 +2396,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.
 
 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
 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;
 
   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;
 
     {
     uschar *colon, *s;
 
@@ -2416,10 +2424,10 @@ for (i = 0; i < recipients_count; i++)
 
     f.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;
 
       int terminator = *ss;
       int start, end, domain;
 
@@ -2431,21 +2439,22 @@ for (i = 0; i < recipients_count; i++)
       *ss = terminator;
 
       /* If we found a valid recipient that has a domain, compare it with the
       *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 */
 
 
       /* Advance to the next address */
 
-      s = ss + (terminator? 1:0);
+      s = ss + (terminator ? 1:0);
       while (isspace(*s)) s++;
       }   /* Next address */
 
       while (isspace(*s)) s++;
       }   /* Next address */
 
@@ -2478,10 +2487,9 @@ Returns:     pointer to an address item, or NULL
 address_item *
 verify_checked_sender(uschar *sender)
 {
 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;
 }
 
 
 }
 
 
@@ -2535,12 +2543,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;
 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;
     {
     int terminator, new_ok;
     uschar *s, *ss, *endname;
@@ -2672,7 +2677,7 @@ for (i = 0; i < 3 && !done; i++)
     f.parse_allow_group = FALSE;
     f.parse_found_group = FALSE;
     }       /* Next header, unless done */
     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";
 
 if (yield == FAIL && *log_msgptr == NULL)
   *log_msgptr = US"there is no valid sender in any header line";
@@ -2770,7 +2775,7 @@ for (;;)
   int size = sizeof(buffer) - (p - buffer);
 
   if (size <= 0) goto END_OFF;   /* Buffer filled without seeing \n. */
   int size = sizeof(buffer) - (p - buffer);
 
   if (size <= 0) goto END_OFF;   /* Buffer filled without seeing \n. */
-  count = ip_recv(&ident_conn_ctx, p, size, rfc1413_query_timeout);
+  count = ip_recv(&ident_conn_ctx, p, size, time(NULL) + 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
   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
@@ -2912,8 +2917,7 @@ if (*ss == '@')
     }
   else if (Ustrcmp(ss, "@[]") == 0)
     {
     }
   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;
     }
       if (Ustrcmp(ip->address, cb->host_address) == 0) return OK;
     return FAIL;
     }
@@ -2936,7 +2940,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). */
 
 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";
 if (*t == 0 || (*t == '/' && t != ss))
   {
   *error = US"malformed IPv4 address or address mask";
@@ -2970,7 +2974,8 @@ if (Ustrncmp(ss, "net", 3) == 0 && semicolon != NULL)
   if (mlen == 0 && t == ss+3) mlen = -1;  /* No mask supplied */
   iplookup = (*t++ == '-');
   }
   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 */
 
 
 /* Do the IP address lookup if that is indeed what we have */
 
@@ -3072,11 +3077,8 @@ if (*t == 0)
   rc = host_find_byname(&h, NULL, HOST_FIND_QUALIFY_SINGLE, NULL, FALSE);
   if (rc == HOST_FOUND || rc == HOST_FOUND_LOCAL)
     {
   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;
       if (host_is_in_net(hh->address, cb->host_address, 0)) return OK;
-      }
     return FAIL;
     }
   if (rc == HOST_FIND_AGAIN) return DEFER;
     return FAIL;
     }
   if (rc == HOST_FIND_AGAIN) return DEFER;
@@ -3270,7 +3272,7 @@ int
 verify_check_host(uschar **listptr)
 {
 return verify_check_this_host(CUSS listptr, sender_host_cache, NULL,
 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);
 }
 
 
 }
 
 
@@ -3305,9 +3307,8 @@ always 1. */
 
 if (host_aton(address, bin) == 1)
   {
 
 if (host_aton(address, bin) == 1)
   {
-  int i;
   int x = bin[0];
   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++;
     {
     sprintf(CS bptr, "%d.", x & 255);
     while (*bptr) bptr++;
@@ -3321,19 +3322,16 @@ unknown. This is just a guess. */
 
 #if HAVE_IPV6
 else
 
 #if HAVE_IPV6
 else
-  {
-  int i, j;
-  for (j = 3; j >= 0; j--)
+  for (int j = 3; j >= 0; j--)
     {
     int x = bin[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;
       }
     }
       {
       sprintf(CS bptr, "%x.", x & 15);
       while (*bptr) bptr++;
       x >>= 4;
       }
     }
-  }
 #endif
 
 /* Remove trailing period -- this is needed so that both arbitrary
 #endif
 
 /* Remove trailing period -- this is needed so that both arbitrary
@@ -3380,7 +3378,7 @@ one_check_dnsbl(uschar *domain, uschar *domain_txt, uschar *keydomain,
   uschar *prepend, uschar *iplist, BOOL bitmask, int match_type,
   int defer_return)
 {
   uschar *prepend, uschar *iplist, BOOL bitmask, int match_type,
   int defer_return)
 {
-dns_answer dnsa;
+dns_answer * dnsa = store_get_dns_answer();
 dns_scan dnss;
 tree_node *t;
 dnsbl_cache_block *cb;
 dns_scan dnss;
 tree_node *t;
 dnsbl_cache_block *cb;
@@ -3405,7 +3403,7 @@ if (  (t = tree_search(dnsbl_cache, query))
 /* Previous lookup was cached */
 
   {
 /* Previous lookup was cached */
 
   {
-  HDEBUG(D_dnsbl) debug_printf("using result of previous DNS lookup\n");
+  HDEBUG(D_dnsbl) debug_printf("dnslists: using result of previous lookup\n");
   }
 
 /* If not cached from a previous lookup, we must do a DNS lookup, and
   }
 
 /* If not cached from a previous lookup, we must do a DNS lookup, and
@@ -3413,7 +3411,7 @@ cache the result in permanent memory. */
 
 else
   {
 
 else
   {
-  uint ttl = 3600;
+  uint ttl = 3600;     /* max TTL for positive cache entries */
 
   store_pool = POOL_PERM;
 
 
   store_pool = POOL_PERM;
 
@@ -3424,16 +3422,16 @@ else
 
   else
     {  /* Set up a tree entry to cache the lookup */
 
   else
     {  /* Set up a tree entry to cache the lookup */
-    t = store_get(sizeof(tree_node) + Ustrlen(query));
+    t = store_get(sizeof(tree_node) + Ustrlen(query), is_tainted(query));
     Ustrcpy(t->name, query);
     Ustrcpy(t->name, query);
-    t->data.ptr = cb = store_get(sizeof(dnsbl_cache_block));
+    t->data.ptr = cb = store_get(sizeof(dnsbl_cache_block), FALSE);
     (void)tree_insertnode(&dnsbl_cache, t);
     }
 
   /* Do the DNS lookup . */
 
   HDEBUG(D_dnsbl) debug_printf("new DNS lookup for %s\n", query);
     (void)tree_insertnode(&dnsbl_cache, t);
     }
 
   /* Do the DNS lookup . */
 
   HDEBUG(D_dnsbl) debug_printf("new DNS lookup for %s\n", query);
-  cb->rc = dns_basic_lookup(&dnsa, query, T_A);
+  cb->rc = dns_basic_lookup(dnsa, query, T_A);
   cb->text_set = FALSE;
   cb->text = NULL;
   cb->rhs = NULL;
   cb->text_set = FALSE;
   cb->text = NULL;
   cb->rhs = NULL;
@@ -3448,36 +3446,58 @@ else
   addresses generated in that way as well.
 
   Mark the cache entry with the "now" plus the minimum of the address TTLs,
   addresses generated in that way as well.
 
   Mark the cache entry with the "now" plus the minimum of the address TTLs,
-  or some suitably far-future time if none were found. */
+  or the RFC 2308 negative-cache value from the SOA if none were found. */
 
 
-  if (cb->rc == DNS_SUCCEED)
+  switch (cb->rc)
     {
     {
-    dns_record *rr;
-    dns_address **addrp = &(cb->rhs);
-    for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
-         rr;
-         rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
-      if (rr->type == T_A)
-        {
-        dns_address *da = dns_address_from_rr(&dnsa, rr);
-        if (da)
-          {
-          *addrp = da;
-          while (da->next) da = da->next;
-          addrp = &da->next;
+    case DNS_SUCCEED:
+      {
+      dns_address ** addrp = &cb->rhs;
+      dns_address * da;
+      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 && (da = dns_address_from_rr(dnsa, rr)))
+         {
+         *addrp = da;
+         while (da->next) da = da->next;
+         addrp = &da->next;
          if (ttl > rr->ttl) ttl = rr->ttl;
          if (ttl > rr->ttl) ttl = rr->ttl;
-          }
-        }
+         }
+
+      if (cb->rhs)
+       {
+       cb->expiry = time(NULL) + ttl;
+       break;
+       }
+
+      /* If we didn't find any A records, change the return code. This can
+      happen when there is a CNAME record but there are no A records for what
+      it points to. */
+
+      cb->rc = DNS_NODATA;
+      }
+      /*FALLTHROUGH*/
+
+    case DNS_NOMATCH:
+    case DNS_NODATA:
+      {
+      /* Although there already is a neg-cache layer maintained by
+      dns_basic_lookup(), we have a dnslist cache entry allocated and
+      tree-inserted. So we may as well use it. */
 
 
-    /* If we didn't find any A records, change the return code. This can
-    happen when there is a CNAME record but there are no A records for what
-    it points to. */
+      time_t soa_negttl = dns_expire_from_soa(dnsa, T_A);
+      cb->expiry = soa_negttl ? soa_negttl : time(NULL) + ttl;
+      break;
+      }
 
 
-    if (!cb->rhs) cb->rc = DNS_NODATA;
+    default:
+      cb->expiry = time(NULL) + ttl;
+      break;
     }
 
     }
 
-  cb->expiry = time(NULL)+ttl;
   store_pool = old_pool;
   store_pool = old_pool;
+  HDEBUG(D_dnsbl) debug_printf("dnslists: wrote cache entry, ttl=%d\n",
+    (int)(cb->expiry - time(NULL)));
   }
 
 /* We now have the result of the DNS lookup, either newly done, or cached
   }
 
 /* We now have the result of the DNS lookup, either newly done, or cached
@@ -3488,7 +3508,7 @@ list (introduced by "&"), or a negative bitmask list (introduced by "!&").*/
 
 if (cb->rc == DNS_SUCCEED)
   {
 
 if (cb->rc == DNS_SUCCEED)
   {
-  dns_address *da = NULL;
+  dns_address * da = NULL;
   uschar *addlist = cb->rhs->address;
 
   /* For A and AAAA records, there may be multiple addresses from multiple
   uschar *addlist = cb->rhs->address;
 
   /* For A and AAAA records, there may be multiple addresses from multiple
@@ -3607,22 +3627,18 @@ if (cb->rc == DNS_SUCCEED)
   if (!cb->text_set)
     {
     cb->text_set = TRUE;
   if (!cb->text_set)
     {
     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;
-           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 (dns_basic_lookup(dnsa, query, T_TXT) == DNS_SUCCEED)
+      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)
+         {
+         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;
     }
 
   dnslist_value = addlist;
@@ -3716,7 +3732,6 @@ int sep = 0;
 int defer_return = FAIL;
 const uschar *list = *listptr;
 uschar *domain;
 int defer_return = FAIL;
 const uschar *list = *listptr;
 uschar *domain;
-uschar *s;
 uschar buffer[1024];
 uschar revadd[128];        /* Long enough for IPv6 address */
 
 uschar buffer[1024];
 uschar revadd[128];        /* Long enough for IPv6 address */
 
@@ -3730,7 +3745,7 @@ dns_init(FALSE, FALSE, FALSE);    /*XXX dnssec? */
 
 /* Loop through all the domains supplied, until something matches */
 
 
 /* Loop through all the domains supplied, until something matches */
 
-while ((domain = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL)
+while ((domain = string_nextinlist(&list, &sep, buffer, sizeof(buffer))))
   {
   int rc;
   BOOL bitmask = FALSE;
   {
   int rc;
   BOOL bitmask = FALSE;
@@ -3740,7 +3755,7 @@ while ((domain = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL
   uschar *iplist;
   uschar *key;
 
   uschar *iplist;
   uschar *key;
 
-  HDEBUG(D_dnsbl) debug_printf("DNS list check: %s\n", domain);
+  HDEBUG(D_dnsbl) debug_printf("dnslists check: %s\n", domain);
 
   /* Deal with special values that change the behaviour on defer */
 
 
   /* Deal with special values that change the behaviour on defer */
 
@@ -3794,8 +3809,7 @@ while ((domain = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL
   set domain_txt == domain. */
 
   domain_txt = domain;
   set domain_txt == domain. */
 
   domain_txt = domain;
-  comma = Ustrchr(domain, ',');
-  if (comma != NULL)
+  if ((comma = Ustrchr(domain, ',')))
     {
     *comma++ = 0;
     domain = comma;
     {
     *comma++ = 0;
     domain = comma;
@@ -3807,32 +3821,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. */
 
   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;
       }
     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 */
 
 
   /* 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 (!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 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)
       {
     {
     if (where == ACL_WHERE_NOTSMTP_START || where == ACL_WHERE_NOTSMTP)
       {
@@ -3841,7 +3851,7 @@ while ((domain = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL
          acl_wherenames[where]);
       return ERROR;
       }
          acl_wherenames[where]);
       return ERROR;
       }
-    if (sender_host_address == NULL) return FAIL;    /* can never match */
+    if (!sender_host_address) return FAIL;    /* can never match */
     if (revadd[0] == 0) invert_address(revadd, sender_host_address);
     rc = one_check_dnsbl(domain, domain_txt, sender_host_address, revadd,
       iplist, bitmask, match_type, defer_return);
     if (revadd[0] == 0) invert_address(revadd, sender_host_address);
     rc = one_check_dnsbl(domain, domain_txt, sender_host_address, revadd,
       iplist, bitmask, match_type, defer_return);
@@ -3863,11 +3873,9 @@ while ((domain = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL
     int keysep = 0;
     BOOL defer = FALSE;
     uschar *keydomain;
     int keysep = 0;
     BOOL defer = FALSE;
     uschar *keydomain;
-    uschar keybuffer[256];
     uschar keyrevadd[128];
 
     uschar keyrevadd[128];
 
-    while ((keydomain = string_nextinlist(CUSS &key, &keysep, keybuffer,
-            sizeof(keybuffer))) != NULL)
+    while ((keydomain = string_nextinlist(CUSS &key, &keysep, NULL, 0)))
       {
       uschar *prepend = keydomain;
 
       {
       uschar *prepend = keydomain;
 
@@ -3879,7 +3887,6 @@ while ((domain = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL
 
       rc = one_check_dnsbl(domain, domain_txt, keydomain, prepend, iplist,
         bitmask, match_type, defer_return);
 
       rc = one_check_dnsbl(domain, domain_txt, keydomain, prepend, iplist,
         bitmask, match_type, defer_return);
-
       if (rc == OK)
         {
         dnslist_domain = string_copy(domain_txt);
       if (rc == OK)
         {
         dnslist_domain = string_copy(domain_txt);