Fix cutthrough delivery for more than one iteration of address redirection. Bug...
[exim.git] / src / src / verify.c
index 922bf69e9ac16a9770c441f4d0e1f722751afaa2..6411899c80081e2b00acbb5876b205eabb7f7f94 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2017 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions concerned with verifying things. The original code for callout
@@ -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);
 
 
 
@@ -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 */
 
@@ -388,28 +388,32 @@ if (addr->transport == cutthrough.addr.transport)
 
       host_af = Ustrchr(host->address, ':') ? AF_INET6 : AF_INET;
 
-      if (!smtp_get_interface(tf->interface, host_af, addr, &interface,
-             US"callout") ||
-         !smtp_get_port(tf->port, addr, &port, US"callout"))
+      if (  !smtp_get_interface(tf->interface, host_af, addr, &interface,
+             US"callout")
+        || !smtp_get_port(tf->port, addr, &port, US"callout")
+        )
        log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address,
          addr->message);
 
+      smtp_port_for_connect(host, port);
+
       if (  (  interface == cutthrough.interface
            || (  interface
               && cutthrough.interface
               && Ustrcmp(interface, cutthrough.interface) == 0
            )  )
-        && port == cutthrough.host.port
+        && host->port == cutthrough.host.port
         )
        {
        uschar * resp = NULL;
 
        /* 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(&ctblock, 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
@@ -608,7 +612,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
@@ -784,7 +788,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;
@@ -799,6 +803,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;
@@ -818,11 +823,11 @@ tls_retry_connection:
              debug_printf_indent("problem after random/rset/mfrom; reopen conn\n");
            random_local_part = NULL;
 #ifdef SUPPORT_TLS
-           tls_close(FALSE, TRUE);
+           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);
@@ -984,6 +989,13 @@ no_conn:
        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:
        sx.send_quit = FALSE;
@@ -1023,6 +1035,29 @@ no_conn:
     here is where we want to leave the conn open.  Ditto for a lazy-close
     verify. */
 
+    if (cutthrough.delivery)
+      {
+      if (addr->transport->filter_command)
+        {
+        cutthrough.delivery= FALSE;
+        HDEBUG(D_acl|D_v) debug_printf("Cutthrough cancelled by presence of transport filter\n");
+        }
+#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 ARC signing\n");
+        }
+#endif
+      }
+
     if (  (cutthrough.delivery || options & vopt_callout_hold)
        && rcpt_count == 1
        && done
@@ -1031,17 +1066,20 @@ no_conn:
           == vopt_callout_recipsender
        && !random_local_part
        && !pm_mailfrom
-       && cutthrough.fd < 0
+       && cutthrough.cctx.sock < 0
        && !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");
 
       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;
@@ -1056,17 +1094,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;
+      for (caddr = &cutthrough.addr, parent = addr->parent;
+          parent;
+          parent = parent->parent)
+        *(caddr->parent = store_get(sizeof(address_item))) = *parent;
+
       ctblock.buffer = ctbuffer;
       ctblock.buffersize = sizeof(ctbuffer);
       ctblock.ptr = ctbuffer;
       /* ctblock.cmd_count = 0; ctblock.authenticating = FALSE; */
-      ctblock.sock = cutthrough.fd;
+      ctblock.cctx = &cutthrough.cctx;
       }
     else
       {
@@ -1082,14 +1124,18 @@ no_conn:
          '2', 1);
        }
 
-      if (sx.inblock.sock >= 0)
+      if (sx.cctx.sock >= 0)
        {
 #ifdef SUPPORT_TLS
-       tls_close(FALSE, TRUE);
+       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
@@ -1154,7 +1200,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;
@@ -1182,14 +1228,16 @@ 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, ctblock.buffer, n, FALSE)
+   :
 #endif
-   send(cutthrough.fd, ctblock.buffer, n, 0) > 0
+     send(cutthrough.cctx.sock, ctblock.buffer, n, 0) > 0
   )
 {
   transport_count += n;
@@ -1221,8 +1269,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;
 }
@@ -1273,7 +1321,7 @@ 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;
 uschar inbuffer[4096];
@@ -1283,8 +1331,7 @@ inblock.buffer = inbuffer;
 inblock.buffersize = sizeof(inbuffer);
 inblock.ptr = inbuffer;
 inblock.ptrend = inbuffer;
-inblock.sock = fd;
-/* this relies on (inblock.sock == tls_out.active) */
+inblock.cctx = cctx;
 if(!smtp_read_response(&inblock, responsebuffer, sizeof(responsebuffer), expect, timeout))
   cancel_cutthrough_connection(TRUE, US"target timeout on read");
 
@@ -1306,7 +1353,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");
@@ -1314,7 +1361,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';
 }
 
 
@@ -1341,7 +1388,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,
@@ -1349,7 +1396,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".";
@@ -1368,24 +1415,31 @@ 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.
   */
+  client_conn_ctx tmp_ctx = cutthrough.cctx;
   ctblock.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, TRUE);
+  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);
@@ -1406,9 +1460,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;
 }
 
@@ -1434,7 +1489,8 @@ if(  !cutthrough_puts(US".", 1)
   )
   return cutthrough.addr.message;
 
-res = cutthrough_response(cutthrough.fd, '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)
   {
   addr->message = cutthrough.addr.message;
@@ -1630,9 +1686,9 @@ else ko_prefix = cr = US"";
 
 if (parse_find_at(address) == NULL)
   {
-  if ((options & vopt_qualify) == 0)
+  if (!(options & vopt_qualify))
     {
-    if (f != NULL)
+    if (f)
       respond_printf(f, "%sA domain is required for \"%s\"%s\n",
         ko_prefix, address, cr);
     *failure_ptr = US"qualify";
@@ -1650,7 +1706,7 @@ DEBUG(D_verify)
 /* 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,
@@ -1659,21 +1715,21 @@ if (global_rewrite_rules != NULL)
     {
     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);
+    if (f && !expn) fprintf(f, "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
@@ -1725,7 +1781,7 @@ while (addr_new)
   if (testflag(addr, af_pfr))
     {
     allok = FALSE;
-    if (f != NULL)
+    if (f)
       {
       BOOL allow;
 
@@ -1736,8 +1792,8 @@ while (addr_new)
         }
       else
         {
-        allow = (addr->address[0] == '|')?
-          testflag(addr, af_allow_pipe) : testflag(addr, af_allow_file);
+        allow = addr->address[0] == '|'
+          testflag(addr, af_allow_pipe) : testflag(addr, af_allow_file);
         fprintf(f, "%s -> %s", addr->parent->address, addr->address);
         }
 
@@ -1845,7 +1901,7 @@ while (addr_new)
             additional host items being inserted into the chain. Hence we must
             save the next host first. */
 
-            flags = HOST_FIND_BY_A;
+            flags = HOST_FIND_BY_A | HOST_FIND_BY_AAAA;
             if (tf.qualify_single) flags |= HOST_FIND_QUALIFY_SINGLE;
             if (tf.search_parents) flags |= HOST_FIND_SEARCH_PARENTS;
 
@@ -1857,16 +1913,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);
                }
               }
             }
@@ -1892,12 +1948,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");
         }
       }
     }
@@ -2165,7 +2224,7 @@ return yield;
 *************************************************/
 
 /* This function checks those header lines that contain addresses, and verifies
-that all the addresses therein are syntactially correct.
+that all the addresses therein are 5322-syntactially correct.
 
 Arguments:
   msgptr     where to put an error message
@@ -2181,7 +2240,7 @@ header_line *h;
 uschar *colon, *s;
 int yield = OK;
 
-for (h = header_list; h != NULL && yield == OK; h = h->next)
+for (h = header_list; h && yield == OK; h = h->next)
   {
   if (h->type != htype_from &&
       h->type != htype_reply_to &&
@@ -2200,7 +2259,7 @@ for (h = header_list; h != NULL && yield == OK; h = h->next)
 
   parse_allow_group = TRUE;
 
-  while (*s != 0)
+  while (*s)
     {
     uschar *ss = parse_find_address_end(s, FALSE);
     uschar *recipient, *errmess;
@@ -2217,7 +2276,7 @@ for (h = header_list; h != NULL && yield == OK; h = h->next)
     /* Permit an unqualified address only if the message is local, or if the
     sending host is configured to be permitted to send them. */
 
-    if (recipient != NULL && domain == 0)
+    if (recipient && !domain)
       {
       if (h->type == htype_from || h->type == htype_sender)
         {
@@ -2233,7 +2292,7 @@ for (h = header_list; h != NULL && yield == OK; h = h->next)
     /* It's an error if no address could be extracted, except for the special
     case of an empty address. */
 
-    if (recipient == NULL && Ustrcmp(errmess, "empty address") != 0)
+    if (!recipient && Ustrcmp(errmess, "empty address") != 0)
       {
       uschar *verb = US"is";
       uschar *t = ss;
@@ -2263,7 +2322,7 @@ for (h = header_list; h != NULL && yield == OK; h = h->next)
       /* deconst cast ok as we're passing a non-const to string_printing() */
       *msgptr = US string_printing(
         string_sprintf("%s: failing address in \"%.*s:\" header %s: %.*s",
-          errmess, tt - h->text, h->text, verb, len, s));
+          errmess, (int)(tt - h->text), h->text, verb, len, s));
 
       yield = FAIL;
       break;          /* Out of address loop */
@@ -2271,7 +2330,7 @@ for (h = header_list; h != NULL && yield == OK; h = h->next)
 
     /* Advance to the next address */
 
-    s = ss + (terminator? 1:0);
+    s = ss + (terminator ? 1 : 0);
     while (isspace(*s)) s++;
     }   /* Next address */
 
@@ -2564,7 +2623,7 @@ for (i = 0; i < 3 && !done; i++)
           while (ss > s && isspace(ss[-1])) ss--;
           *log_msgptr = string_sprintf("syntax error in '%.*s' header when "
             "scanning for sender: %s in \"%.*s\"",
-            endname - h->text, h->text, *log_msgptr, ss - s, s);
+            (int)(endname - h->text), h->text, *log_msgptr, (int)(ss - s), s);
           yield = FAIL;
           done = TRUE;
           break;
@@ -2592,11 +2651,9 @@ for (i = 0; i < 3 && !done; i++)
         {
         *verrno = vaddr->basic_errno;
         if (smtp_return_error_details)
-          {
           *user_msgptr = string_sprintf("Rejected after DATA: "
             "could not verify \"%.*s\" header address\n%s: %s",
-            endname - h->text, h->text, vaddr->address, vaddr->message);
-          }
+            (int)(endname - h->text), h->text, vaddr->address, vaddr->message);
         }
 
       /* Success or defer */
@@ -2654,7 +2711,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;
@@ -2674,9 +2732,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));
@@ -2690,7 +2748,7 @@ 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,
+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))
@@ -2714,7 +2772,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
@@ -2779,7 +2837,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;
 }
 
@@ -3074,7 +3132,7 @@ if (isquery)
 /* Not a query-style lookup; must ensure the host name is present, and then we
 do a check on the name and all its aliases. */
 
-if (sender_host_name == NULL)
+if (!sender_host_name)
   {
   HDEBUG(D_host_lookup)
     debug_printf("sender host name required, to match against %s\n", ss);
@@ -3089,8 +3147,7 @@ if (sender_host_name == NULL)
 
 /* Match on the sender host name, using the general matching function */
 
-switch(match_check_string(sender_host_name, ss, -1, TRUE, TRUE, TRUE,
-       valueptr))
+switch(match_check_string(sender_host_name, ss, -1, TRUE, TRUE, TRUE, valueptr))
   {
   case OK:    return OK;
   case DEFER: return DEFER;
@@ -3099,14 +3156,12 @@ switch(match_check_string(sender_host_name, ss, -1, TRUE, TRUE, TRUE,
 /* If there are aliases, try matching on them. */
 
 aliases = sender_host_aliases;
-while (*aliases != NULL)
-  {
+while (*aliases)
   switch(match_check_string(*aliases++, ss, -1, TRUE, TRUE, TRUE, valueptr))
     {
     case OK:    return OK;
     case DEFER: return DEFER;
     }
-  }
 return FAIL;
 }
 
@@ -3387,9 +3442,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
@@ -3405,25 +3459,23 @@ else
     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 != NULL) da = da->next;
-          addrp = &(da->next);
+          while (da->next) da = da->next;
+          addrp = &da->next;
          if (ttl > rr->ttl) ttl = rr->ttl;
           }
         }
-      }
 
     /* 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. */
 
-    if (cb->rhs == NULL) cb->rc = DNS_NODATA;
+    if (!cb->rhs) cb->rc = DNS_NODATA;
     }
 
   cb->expiry = time(NULL)+ttl;
@@ -3445,7 +3497,7 @@ if (cb->rc == DNS_SUCCEED)
   records. For A6 records (currently not expected to be used) there may be
   multiple addresses from a single record. */
 
-  for (da = cb->rhs->next; da != NULL; da = da->next)
+  for (da = cb->rhs->next; da; da = da->next)
     addlist = string_sprintf("%s, %s", addlist, da->address);
 
   HDEBUG(D_dnsbl) debug_printf("DNS lookup for %s succeeded (yielding %s)\n",
@@ -3454,9 +3506,9 @@ if (cb->rc == DNS_SUCCEED)
   /* Address list check; this can be either for equality, or via a bitmask.
   In the latter case, all the bits must match. */
 
-  if (iplist != NULL)
+  if (iplist)
     {
-    for (da = cb->rhs; da != NULL; da = da->next)
+    for (da = cb->rhs; da; da = da->next)
       {
       int ipsep = ',';
       uschar ip[46];
@@ -3466,12 +3518,11 @@ if (cb->rc == DNS_SUCCEED)
       /* Handle exact matching */
 
       if (!bitmask)
-        {
-        while ((res = string_nextinlist(&ptr, &ipsep, ip, sizeof(ip))) != NULL)
-          {
-          if (Ustrcmp(CS da->address, ip) == 0) break;
-          }
-        }
+       {
+        while ((res = string_nextinlist(&ptr, &ipsep, ip, sizeof(ip))))
+          if (Ustrcmp(CS da->address, ip) == 0)
+           break;
+       }
 
       /* Handle bitmask matching */
 
@@ -3491,7 +3542,7 @@ if (cb->rc == DNS_SUCCEED)
 
         /* Scan the returned addresses, skipping any that are IPv6 */
 
-        while ((res = string_nextinlist(&ptr, &ipsep, ip, sizeof(ip))) != NULL)
+        while ((res = string_nextinlist(&ptr, &ipsep, ip, sizeof(ip))))
           {
           if (host_aton(ip, address) != 1) continue;
           if ((address[0] & mask) == address[0]) break;
@@ -3524,17 +3575,13 @@ if (cb->rc == DNS_SUCCEED)
         switch(match_type)
           {
           case 0:
-          res = US"was no match";
-          break;
+           res = US"was no match"; break;
           case MT_NOT:
-          res = US"was an exclude match";
-          break;
+           res = US"was an exclude match"; break;
           case MT_ALL:
-          res = US"was an IP address that did not match";
-          break;
+           res = US"was an IP address that did not match"; break;
           case MT_NOT|MT_ALL:
-          res = US"were no IP addresses that did not match";
-          break;
+           res = US"were no IP addresses that did not match"; break;
           }
         debug_printf("=> but we are not accepting this block class because\n");
         debug_printf("=> there %s for %s%c%s\n",
@@ -3566,15 +3613,15 @@ if (cb->rc == DNS_SUCCEED)
       {
       dns_record *rr;
       for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
-           rr != NULL;
+           rr;
            rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT))
         if (rr->type == T_TXT) break;
-      if (rr != NULL)
+      if (rr)
         {
         int len = (rr->data)[0];
         if (len > 511) len = 127;
         store_pool = POOL_PERM;
-        cb->text = string_sprintf("%.*s", len, (const uschar *)(rr->data+1));
+        cb->text = string_sprintf("%.*s", len, CUS (rr->data+1));
         store_pool = old_pool;
         }
       }