FreeBSD: better support for TFO
[exim.git] / src / src / transport.c
index 8ccdd03890121acf320f48f780e74e782254f2f2..14bd91cdb3e2160c0ee47f65a2c2a8e302e07cc0 100644 (file)
@@ -95,12 +95,11 @@ int optionlist_transports_size = nelem(optionlist_transports);
 void
 options_transports(void)
 {
-struct transport_info * ti;
 uschar buf[64];
 
 options_from_list(optionlist_transports, nelem(optionlist_transports), US"TRANSPORTS", NULL);
 
-for (ti = transports_available; ti->driver_name[0]; ti++)
+for (transport_info * ti = transports_available; ti->driver_name[0]; ti++)
   {
   spf(buf, sizeof(buf), US"_DRIVER_TRANSPORT_%T", ti->driver_name);
   builtin_macro_create(buf);
@@ -142,8 +141,6 @@ the work. */
 void
 transport_init(void)
 {
-transport_instance *t;
-
 readconf_driver_init(US"transport",
   (driver_instance **)(&transports),     /* chain anchor */
   (driver_info *)transports_available,   /* available drivers */
@@ -156,7 +153,7 @@ readconf_driver_init(US"transport",
 /* Now scan the configured transports and check inconsistencies. A shadow
 transport is permitted only for local transports. */
 
-for (t = transports; t; t = t->next)
+for (transport_instance * t = transports; t; t = t->next)
   {
   if (!t->info->local && t->shadow)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
@@ -175,6 +172,20 @@ for (t = transports; t; t = t->next)
 *             Write block of data                *
 *************************************************/
 
+static int
+tpt_write(int fd, uschar * block, int len, BOOL more, int options)
+{
+return
+#ifndef DISABLE_TLS
+  tls_out.active.sock == fd
+    ? tls_write(tls_out.active.tls_ctx, block, len, more) :
+#endif
+#ifdef MSG_MORE
+  more && !(options & topt_not_socket) ? send(fd, block, len, MSG_MORE) :
+#endif
+  write(fd, block, len);
+}
+
 /* Subroutine called by write_chunk() and at the end of the message actually
 to write a data block. Also called directly by some transports to write
 additional data to the file descriptor (e.g. prefix, suffix).
@@ -218,63 +229,58 @@ Returns:    TRUE on success, FALSE on failure (with errno preserved);
 */
 
 static BOOL
-transport_write_block_fd(transport_ctx * tctx, uschar *block, int len, BOOL more)
+transport_write_block_fd(transport_ctx * tctx, uschar * block, int len, BOOL more)
 {
-int i, rc, save_errno;
+int rc, save_errno;
 int local_timeout = transport_write_timeout;
+int connretry = 1;
 int fd = tctx->u.fd;
 
 /* This loop is for handling incomplete writes and other retries. In most
 normal cases, it is only ever executed once. */
 
-for (i = 0; i < 100; i++)
+for (int i = 0; i < 100; i++)
   {
   DEBUG(D_transport)
     debug_printf("writing data block fd=%d size=%d timeout=%d%s\n",
       fd, len, local_timeout, more ? " (more expected)" : "");
 
-  /* This code makes use of alarm() in order to implement the timeout. This
-  isn't a very tidy way of doing things. Using non-blocking I/O with select()
-  provides a neater approach. However, I don't know how to do this when TLS is
-  in use. */
-
-  if (transport_write_timeout <= 0)   /* No timeout wanted */
-    {
-    rc =
-#ifdef SUPPORT_TLS
-       tls_out.active.sock == fd ? tls_write(tls_out.active.tls_ctx, block, len, more) :
-#endif
-#ifdef MSG_MORE
-       more && !(tctx->options & topt_not_socket)
-         ? send(fd, block, len, MSG_MORE) :
-#endif
-       write(fd, block, len);
-    save_errno = errno;
-    }
-
-  /* Timeout wanted. */
+  /* When doing TCP Fast Open we may get this far before the 3-way handshake
+  is complete, and write returns ENOTCONN.  Detect that, wait for the socket
+  to become writable, and retry once only. */
 
-  else
+  for(;;)
     {
-    ALARM(local_timeout);
+    fd_set fds;
+    /* This code makes use of alarm() in order to implement the timeout. This
+    isn't a very tidy way of doing things. Using non-blocking I/O with select()
+    provides a neater approach. However, I don't know how to do this when TLS is
+    in use. */
 
-    rc =
-#ifdef SUPPORT_TLS
-       tls_out.active.sock == fd ? tls_write(tls_out.active.tls_ctx, block, len, more) :
-#endif
-#ifdef MSG_MORE
-       more && !(tctx->options & topt_not_socket)
-         ? send(fd, block, len, MSG_MORE) :
-#endif
-       write(fd, block, len);
-
-    save_errno = errno;
-    local_timeout = ALARM_CLR(0);
-    if (sigalrm_seen)
+    if (transport_write_timeout <= 0)   /* No timeout wanted */
       {
-      errno = ETIMEDOUT;
-      return FALSE;
+      rc = tpt_write(fd, block, len, more, tctx->options);
+      save_errno = errno;
+      }
+    else                               /* Timeout wanted. */
+      {
+      ALARM(local_timeout);
+       rc = tpt_write(fd, block, len, more, tctx->options);
+       save_errno = errno;
+      local_timeout = ALARM_CLR(0);
+      if (sigalrm_seen)
+       {
+       errno = ETIMEDOUT;
+       return FALSE;
+       }
       }
+
+    if (rc >= 0 || errno != ENOTCONN || connretry <= 0)
+      break;
+
+    FD_ZERO(&fds); FD_SET(fd, &fds);
+    select(fd+1, NULL, &fds, NULL, NULL);      /* could set timout? */
+    connretry--;
     }
 
   /* Hopefully, the most common case is success, so test that first. */
@@ -378,8 +384,11 @@ transport_ctx tctx = {{0}};
 gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer };
 va_list ap;
 
+/* Use taint-unchecked routines for writing into big_buffer, trusting
+that the result will never be expanded. */
+
 va_start(ap, format);
-if (!string_vformat(&gs, FALSE, format, ap))
+if (!string_vformat(&gs, SVFMT_TAINT_NOCHK, format, ap))
   log_write(0, LOG_MAIN|LOG_PANIC_DIE, "overlong formatted string in transport");
 va_end(ap);
 tctx.u.fd = fd;
@@ -427,7 +436,6 @@ write_chunk(transport_ctx * tctx, uschar *chunk, int len)
 {
 uschar *start = chunk;
 uschar *end = chunk + len;
-uschar *ptr;
 int mlen = DELIVER_OUT_BUFFER_SIZE - nl_escape_length - 2;
 
 /* The assumption is made that the check string will never stretch over move
@@ -464,7 +472,7 @@ if (nl_partial_match >= 0)
 for possible escaping. The code for the non-NL route should be as fast as
 possible. */
 
-for (ptr = start; ptr < end; ptr++)
+for (uschar * ptr = start; ptr < end; ptr++)
   {
   int ch, len;
 
@@ -642,7 +650,7 @@ so that we don't handle it again. */
 
 for (ppp = *pdlist; ppp; ppp = ppp->next) if (p == ppp->ptr) return TRUE;
 
-ppp = store_get(sizeof(struct aci));
+ppp = store_get(sizeof(struct aci), FALSE);
 ppp->next = *pdlist;
 *pdlist = ppp;
 ppp->ptr = p;
@@ -666,7 +674,7 @@ if (ppp) return TRUE;
 
 /* Remember what we have output, and output it. */
 
-ppp = store_get(sizeof(struct aci));
+ppp = store_get(sizeof(struct aci), FALSE);
 ppp->next = *pplist;
 *pplist = ppp;
 ppp->ptr = pp;
@@ -696,7 +704,6 @@ BOOL
 transport_headers_send(transport_ctx * tctx,
   BOOL (*sendfn)(transport_ctx * tctx, uschar * s, int len))
 {
-header_line *h;
 const uschar *list;
 transport_instance * tblock = tctx ? tctx->tblock : NULL;
 address_item * addr = tctx ? tctx->addr : NULL;
@@ -708,13 +715,12 @@ match any entries therein.  It is a colon-sep list; expand the items
 separately and squash any empty ones.
 Then check addr->prop.remove_headers too, provided that addr is not NULL. */
 
-for (h = header_list; h; h = h->next) if (h->type != htype_old)
+for (header_line * h = header_list; h; h = h->next) if (h->type != htype_old)
   {
-  int i;
   BOOL include_header = TRUE;
 
   list = tblock ? tblock->remove_headers : NULL;
-  for (i = 0; i < 2; i++)    /* For remove_headers && addr->prop.remove_headers */
+  for (int i = 0; i < 2; i++)    /* For remove_headers && addr->prop.remove_headers */
     {
     if (list)
       {
@@ -748,7 +754,7 @@ for (h = header_list; h; h = h->next) if (h->type != htype_old)
     {
     if (tblock && tblock->rewrite_rules)
       {
-      void *reset_point = store_get(0);
+      rmark reset_point = store_mark();
       header_line *hh;
 
       if ((hh = rewrite_header(h, NULL, NULL, tblock->rewrite_rules,
@@ -784,10 +790,9 @@ Headers added to an address by a router are guaranteed to end with a newline.
 
 if (addr)
   {
-  int i;
   header_line *hprev = addr->prop.extra_headers;
-  header_line *hnext;
-  for (i = 0; i < 2; i++)
+  header_line *hnext, * h;
+  for (int i = 0; i < 2; i++)
     for (h = hprev, hprev = NULL; h; h = hnext)
       {
       hnext = h->next;
@@ -954,10 +959,9 @@ if (!(tctx->options & topt_no_headers))
   if (tctx->options & topt_add_envelope_to)
     {
     BOOL first = TRUE;
-    address_item *p;
     struct aci *plist = NULL;
     struct aci *dlist = NULL;
-    void *reset_point = store_get(0);
+    rmark reset_point = store_mark();
 
     if (!write_chunk(tctx, US"Envelope-to: ", 13)) goto bad;
 
@@ -965,8 +969,9 @@ if (!(tctx->options & topt_no_headers))
     anchors for lists of addresses already handled; they have to be defined at
     this level because write_env_to() calls itself recursively. */
 
-    for (p = tctx->addr; p; p = p->next)
-      if (!write_env_to(p, &plist, &dlist, &first, tctx)) goto bad;
+    for (address_item * p = tctx->addr; p; p = p->next)
+      if (!write_env_to(p, &plist, &dlist, &first, tctx))
+       goto bad;
 
     /* Add a final newline and reset the store used for tracking duplicates */
 
@@ -1115,13 +1120,13 @@ DEBUG(D_transport)
 
 if (!(tctx->options & topt_no_body))
   {
-  int size = size_limit;
+  unsigned long size = size_limit > 0 ? size_limit : ULONG_MAX;
 
   nl_check_length = abs(nl_check_length);
   nl_partial_match = 0;
   if (lseek(deliver_datafile, SPOOL_DATA_START_OFFSET, SEEK_SET) < 0)
     return FALSE;
-  while (  (len = MAX(DELIVER_IN_BUFFER_SIZE, size)) > 0
+  while (  (len = MIN(DELIVER_IN_BUFFER_SIZE, size)) > 0
        && (len = read(deliver_datafile, deliver_in_buffer, len)) > 0)
     {
     if (!write_chunk(tctx, deliver_in_buffer, len))
@@ -1259,7 +1264,7 @@ if ((write_pid = fork()) == 0)
         != sizeof(int)
      )
     rc = FALSE;        /* compiler quietening */
-  _exit(0);
+  exim_underbar_exit(0);
   }
 save_errno = errno;
 
@@ -1279,7 +1284,7 @@ if (write_pid < 0)
 
 /* When testing, let the subprocess get going */
 
-if (f.running_in_test_harness) millisleep(250);
+testharness_pause_ms(250);
 
 DEBUG(D_transport)
   debug_printf("process %d writing to transport filter\n", (int)write_pid);
@@ -1474,7 +1479,6 @@ void
 transport_update_waiting(host_item *hostlist, uschar *tpname)
 {
 const uschar *prevname = US"";
-host_item *host;
 open_db dbblock;
 open_db *dbm_file;
 
@@ -1483,18 +1487,17 @@ DEBUG(D_transport) debug_printf("updating wait-%s database\n", tpname);
 /* Open the database for this transport */
 
 if (!(dbm_file = dbfn_open(string_sprintf("wait-%.200s", tpname),
-                     O_RDWR, &dbblock, TRUE)))
+                     O_RDWR, &dbblock, TRUE, TRUE)))
   return;
 
 /* Scan the list of hosts for which this message is waiting, and ensure
 that the message id is in each host record. */
 
-for (host = hostlist; host; host = host->next)
+for (host_item * host = hostlist; host; host = host->next)
   {
   BOOL already = FALSE;
   dbdata_wait *host_record;
-  uschar *s;
-  int i, host_length;
+  int host_length;
   uschar buffer[256];
 
   /* Skip if this is the same host as we just processed; otherwise remember
@@ -1507,7 +1510,7 @@ for (host = hostlist; host; host = host->next)
 
   if (!(host_record = dbfn_read(dbm_file, host->name)))
     {
-    host_record = store_get(sizeof(dbdata_wait) + MESSAGE_ID_LENGTH);
+    host_record = store_get(sizeof(dbdata_wait) + MESSAGE_ID_LENGTH, FALSE);
     host_record->count = host_record->sequence = 0;
     }
 
@@ -1517,7 +1520,7 @@ for (host = hostlist; host; host = host->next)
 
   /* Search the record to see if the current message is already in it. */
 
-  for (s = host_record->text; s < host_record->text + host_length;
+  for (uschar * s = host_record->text; s < host_record->text + host_length;
        s += MESSAGE_ID_LENGTH)
     if (Ustrncmp(s, message_id, MESSAGE_ID_LENGTH) == 0)
       { already = TRUE; break; }
@@ -1525,14 +1528,14 @@ for (host = hostlist; host; host = host->next)
   /* If we haven't found this message in the main record, search any
   continuation records that exist. */
 
-  for (i = host_record->sequence - 1; i >= 0 && !already; i--)
+  for (int i = host_record->sequence - 1; i >= 0 && !already; i--)
     {
     dbdata_wait *cont;
     sprintf(CS buffer, "%.200s:%d", host->name, i);
     if ((cont = dbfn_read(dbm_file, buffer)))
       {
       int clen = cont->count * MESSAGE_ID_LENGTH;
-      for (s = cont->text; s < cont->text + clen; s += MESSAGE_ID_LENGTH)
+      for (uschar * s = cont->text; s < cont->text + clen; s += MESSAGE_ID_LENGTH)
         if (Ustrncmp(s, message_id, MESSAGE_ID_LENGTH) == 0)
           { already = TRUE; break; }
       }
@@ -1566,7 +1569,7 @@ for (host = hostlist; host; host = host->next)
   else
     {
     dbdata_wait *newr =
-      store_get(sizeof(dbdata_wait) + host_length + MESSAGE_ID_LENGTH);
+      store_get(sizeof(dbdata_wait) + host_length + MESSAGE_ID_LENGTH, FALSE);
     memcpy(newr, host_record, sizeof(dbdata_wait) + host_length);
     host_record = newr;
     }
@@ -1657,7 +1660,7 @@ if (local_message_max > 0 && continue_sequence >= local_message_max)
 /* Open the waiting information database. */
 
 if (!(dbm_file = dbfn_open(string_sprintf("wait-%.200s", transport_name),
-                         O_RDWR, &dbblock, TRUE)))
+                         O_RDWR, &dbblock, TRUE, TRUE)))
   return FALSE;
 
 /* See if there is a record for this host; if not, there's nothing to do. */
@@ -1701,7 +1704,7 @@ while (1)
 
   /* create an array to read entire message queue into memory for processing  */
 
-  msgq = store_malloc(sizeof(msgq_t) * host_record->count);
+  msgq = store_get(sizeof(msgq_t) * host_record->count, FALSE);
   msgq_count = host_record->count;
   msgq_actual = msgq_count;
 
@@ -1709,7 +1712,7 @@ while (1)
     {
     msgq[i].bKeep = TRUE;
 
-    Ustrncpy(msgq[i].message_id, host_record->text + (i * MESSAGE_ID_LENGTH),
+    Ustrncpy_nt(msgq[i].message_id, host_record->text + (i * MESSAGE_ID_LENGTH), 
       MESSAGE_ID_LENGTH);
     msgq[i].message_id[MESSAGE_ID_LENGTH] = 0;
     }
@@ -1728,16 +1731,14 @@ while (1)
   for (i = msgq_count - 1; i >= 0; --i) if (msgq[i].bKeep)
     {
     uschar subdir[2];
+    uschar * mid = msgq[i].message_id;
 
-    subdir[0] = split_spool_directory ? msgq[i].message_id[5] : 0;
-    subdir[1] = 0;
-
-    if (Ustat(spool_fname(US"input", subdir, msgq[i].message_id, US"-D"),
-             &statbuf) != 0)
+    set_subdir_str(subdir, mid, 0);
+    if (Ustat(spool_fname(US"input", subdir, mid, US"-D"), &statbuf) != 0)
       msgq[i].bKeep = FALSE;
-    else if (!oicf_func || oicf_func(msgq[i].message_id, oicf_data))
+    else if (!oicf_func || oicf_func(mid, oicf_data))
       {
-      Ustrcpy(new_message_id, msgq[i].message_id);
+      Ustrcpy_nt(new_message_id, mid);
       msgq[i].bKeep = FALSE;
       bFound = TRUE;
       break;
@@ -1778,13 +1779,12 @@ while (1)
 
   while (host_length <= 0)
     {
-    int i;
     dbdata_wait * newr = NULL;
     uschar buffer[256];
 
     /* Search for a continuation */
 
-    for (i = host_record->sequence - 1; i >= 0 && !newr; i--)
+    for (int i = host_record->sequence - 1; i >= 0 && !newr; i--)
       {
       sprintf(CS buffer, "%.200s:%d", hostname, i);
       newr = dbfn_read(dbm_file, buffer);
@@ -1808,10 +1808,7 @@ while (1)
     }
 
   if (bFound)          /* Usual exit from main loop */
-    {
-    store_free (msgq);
     break;
-    }
 
   /* If host_length <= 0 we have emptied a record and not found a good message,
   and there are no continuation records. Otherwise there is a continuation
@@ -1834,8 +1831,6 @@ while (1)
     dbfn_close(dbm_file);
     return FALSE;
     }
-
-  store_free(msgq);
   }            /* we need to process a continuation record */
 
 /* Control gets here when an existing message has been encountered; its
@@ -1877,7 +1872,7 @@ if (smtp_peer_options & OPTION_CHUNKING)  argv[i++] = US"-MCK";
 if (smtp_peer_options & OPTION_DSN)            argv[i++] = US"-MCD";
 if (smtp_peer_options & OPTION_PIPE)           argv[i++] = US"-MCP";
 if (smtp_peer_options & OPTION_SIZE)           argv[i++] = US"-MCS";
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 if (smtp_peer_options & OPTION_TLS)
   if (tls_out.active.sock >= 0 || continue_proxy_cipher)
     {
@@ -1958,7 +1953,7 @@ if ((pid = fork()) == 0)
     DEBUG(D_transport) debug_printf("transport_pass_socket succeeded (final-pid %d)\n", pid);
     _exit(EXIT_SUCCESS);
     }
-  if (f.running_in_test_harness) sleep(1);
+  testharness_pause_ms(1000);
 
   transport_do_pass_socket(transport_name, hostname, hostaddress,
     id, socket_fd);
@@ -2015,21 +2010,20 @@ transport_set_up_command(const uschar ***argvptr, uschar *cmd,
   BOOL expand_arguments, int expand_failed, address_item *addr,
   uschar *etext, uschar **errptr)
 {
-address_item *ad;
 const uschar **argv;
 uschar *s, *ss;
 int address_count = 0;
 int argcount = 0;
-int i, max_args;
+int max_args;
 
 /* Get store in which to build an argument list. Count the number of addresses
 supplied, and allow for that many arguments, plus an additional 60, which
 should be enough for anybody. Multiple addresses happen only when the local
 delivery batch option is set. */
 
-for (ad = addr; ad != NULL; ad = ad->next) address_count++;
+for (address_item * ad = addr; ad; ad = ad->next) address_count++;
 max_args = address_count + 60;
-*argvptr = argv = store_get((max_args+1)*sizeof(uschar *));
+*argvptr = argv = store_get((max_args+1)*sizeof(uschar *), FALSE);
 
 /* Split the command up into arguments terminated by white space. Lose
 trailing space at the start and end. Double-quoted arguments can contain \\ and
@@ -2039,18 +2033,19 @@ arguments are verbatim. Copy each argument into a new string. */
 s = cmd;
 while (isspace(*s)) s++;
 
-while (*s != 0 && argcount < max_args)
+for (; *s != 0 && argcount < max_args; argcount++)
   {
   if (*s == '\'')
     {
     ss = s + 1;
     while (*ss != 0 && *ss != '\'') ss++;
-    argv[argcount++] = ss = store_get(ss - s++);
+    argv[argcount] = ss = store_get(ss - s++, is_tainted(cmd));
     while (*s != 0 && *s != '\'') *ss++ = *s++;
     if (*s != 0) s++;
     *ss++ = 0;
     }
-  else argv[argcount++] = string_copy(string_dequote(CUSS &s));
+  else
+    argv[argcount] = string_dequote(CUSS &s);
   while (isspace(*s)) s++;
   }
 
@@ -2090,8 +2085,8 @@ $recipients. */
 DEBUG(D_transport)
   {
   debug_printf("direct command:\n");
-  for (i = 0; argv[i] != US 0; i++)
-    debug_printf("  argv[%d] = %s\n", i, string_printing(argv[i]));
+  for (int i = 0; argv[i]; i++)
+    debug_printf("  argv[%d] = '%s'\n", i, string_printing(argv[i]));
   }
 
 if (expand_arguments)
@@ -2100,7 +2095,7 @@ if (expand_arguments)
     addr->parent != NULL &&
     Ustrcmp(addr->parent->address, "system-filter") == 0;
 
-  for (i = 0; argv[i] != US 0; i++)
+  for (int i = 0; argv[i] != US 0; i++)
     {
 
     /* Handle special fudge for passing an address list */
@@ -2124,10 +2119,11 @@ if (expand_arguments)
         memmove(argv + i + 1 + additional, argv + i + 1,
           (argcount - i)*sizeof(uschar *));
 
-      for (ad = addr; ad != NULL; ad = ad->next) {
-          argv[i++] = ad->address;
-          argcount++;
-      }
+      for (address_item * ad = addr; ad; ad = ad->next)
+        {
+       argv[i++] = ad->address;
+       argcount++;
+       }
 
       /* Subtract one since we replace $pipe_addresses */
       argcount--;
@@ -2140,10 +2136,10 @@ if (expand_arguments)
         (Ustrcmp(argv[i], "$address_pipe") == 0 ||
          Ustrcmp(argv[i], "${address_pipe}") == 0))
       {
-      int address_pipe_i;
       int address_pipe_argcount = 0;
       int address_pipe_max_args;
       uschar **address_pipe_argv;
+      BOOL tainted;
 
       /* We can never have more then the argv we will be loading into */
       address_pipe_max_args = max_args - argcount + 1;
@@ -2152,10 +2148,11 @@ if (expand_arguments)
         debug_printf("address_pipe_max_args=%d\n", address_pipe_max_args);
 
       /* We allocate an additional for (uschar *)0 */
-      address_pipe_argv = store_get((address_pipe_max_args+1)*sizeof(uschar *));
+      address_pipe_argv = store_get((address_pipe_max_args+1)*sizeof(uschar *), FALSE);
 
       /* +1 because addr->local_part[0] == '|' since af_force_command is set */
       s = expand_string(addr->local_part + 1);
+      tainted = is_tainted(s);
 
       if (s == NULL || *s == '\0')
         {
@@ -2174,7 +2171,7 @@ if (expand_arguments)
           {
           ss = s + 1;
           while (*ss != 0 && *ss != '\'') ss++;
-          address_pipe_argv[address_pipe_argcount++] = ss = store_get(ss - s++);
+          address_pipe_argv[address_pipe_argcount++] = ss = store_get(ss - s++, tainted);
           while (*s != 0 && *s != '\'') *ss++ = *s++;
           if (*s != 0) s++;
           *ss++ = 0;
@@ -2231,7 +2228,7 @@ if (expand_arguments)
       /* Now we fill in the slots we just moved argv out of
        * [argv 0][argv 1][argv 2=pipeargv[0]][argv 3=pipeargv[1]][old argv 3][0]
        */
-      for (address_pipe_i = 0;
+      for (int address_pipe_i = 0;
            address_pipe_argv[address_pipe_i] != US 0;
            address_pipe_i++)
         {
@@ -2253,12 +2250,12 @@ if (expand_arguments)
       expanded_arg = expand_cstring(argv[i]);
       f.enable_dollar_recipients = FALSE;
 
-      if (expanded_arg == NULL)
+      if (!expanded_arg)
         {
         uschar *msg = string_sprintf("Expansion of \"%s\" "
           "from command \"%s\" in %s failed: %s",
           argv[i], cmd, etext, expand_string_message);
-        if (addr != NULL)
+        if (addr)
           {
           addr->transport_return = expand_failed;
           addr->message = msg;
@@ -2273,7 +2270,7 @@ if (expand_arguments)
   DEBUG(D_transport)
     {
     debug_printf("direct command after expansion:\n");
-    for (i = 0; argv[i] != US 0; i++)
+    for (int i = 0; argv[i] != US 0; i++)
       debug_printf("  argv[%d] = %s\n", i, string_printing(argv[i]));
     }
   }