tidying
[exim.git] / src / src / daemon.c
index dc49eeb85a32770ce98282c7c1539f844c5220cc..37fefd4b1d96566401f2087825f117c539b670ef 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2016 */
+/* Copyright (c) University of Cambridge 1995 - 2017 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions concerned with running Exim as a daemon */
@@ -19,9 +19,9 @@ typedef struct smtp_slot {
 } smtp_slot;
 
 /* An empty slot for initializing (Standard C does not allow constructor
-expressions in assigments except as initializers in declarations). */
+expressions in assignments except as initializers in declarations). */
 
-static smtp_slot empty_smtp_slot = { 0, NULL };
+static smtp_slot empty_smtp_slot = { .pid = 0, .host_address = NULL };
 
 
 
@@ -109,7 +109,7 @@ never_error(uschar *log_msg, uschar *smtp_msg, int was_errno)
 uschar *emsg = (was_errno <= 0)? US"" :
   string_sprintf(": %s", strerror(was_errno));
 log_write(0, LOG_MAIN|LOG_PANIC, "%s%s", log_msg, emsg);
-if (smtp_out != NULL) smtp_printf("421 %s\r\n", smtp_msg);
+if (smtp_out != NULL) smtp_printf("421 %s\r\n", FALSE, smtp_msg);
 }
 
 
@@ -143,10 +143,8 @@ union sockaddr_46 interface_sockaddr;
 EXIM_SOCKLEN_T ifsize = sizeof(interface_sockaddr);
 int dup_accept_socket = -1;
 int max_for_this_host = 0;
-int wfsize = 0;
-int wfptr = 0;
 int save_log_selector = *log_selector;
-uschar *whofrom = NULL;
+gstring * whofrom;
 
 void *reset_point = store_get(0);
 
@@ -189,7 +187,7 @@ if (getsockname(accept_socket, (struct sockaddr *)(&interface_sockaddr),
   {
   log_write(0, LOG_MAIN | ((errno == ECONNRESET)? 0 : LOG_PANIC),
     "getsockname() failed: %s", strerror(errno));
-  smtp_printf("421 Local problem: getsockname() failed; please try again later\r\n");
+  smtp_printf("421 Local problem: getsockname() failed; please try again later\r\n", FALSE);
   goto ERROR_RETURN;
   }
 
@@ -201,17 +199,16 @@ DEBUG(D_interface) debug_printf("interface address=%s port=%d\n",
 the local interface data. This is for logging; at the end of this function the
 memory is reclaimed. */
 
-whofrom = string_append(whofrom, &wfsize, &wfptr, 3, "[", sender_host_address, "]");
+whofrom = string_append(NULL, 3, "[", sender_host_address, "]");
 
 if (LOGGING(incoming_port))
-  whofrom = string_append(whofrom, &wfsize, &wfptr, 2, ":", string_sprintf("%d",
-    sender_host_port));
+  whofrom = string_append(whofrom, 2, ":", string_sprintf("%d", sender_host_port));
 
 if (LOGGING(incoming_interface))
-  whofrom = string_append(whofrom, &wfsize, &wfptr, 4, " I=[",
+  whofrom = string_append(whofrom, 4, " I=[",
     interface_address, "]:", string_sprintf("%d", interface_port));
 
-whofrom[wfptr] = 0;    /* Terminate the newly-built string */
+(void) string_from_gstring(whofrom);    /* Terminate the newly-built string */
 
 /* Check maximum number of connections. We do not check for reserved
 connections or unacceptable hosts here. That is done in the subprocess because
@@ -222,10 +219,10 @@ if (smtp_accept_max > 0 && smtp_accept_count >= smtp_accept_max)
   DEBUG(D_any) debug_printf("rejecting SMTP connection: count=%d max=%d\n",
     smtp_accept_count, smtp_accept_max);
   smtp_printf("421 Too many concurrent SMTP connections; "
-    "please try again later.\r\n");
+    "please try again later.\r\n", FALSE);
   log_write(L_connection_reject,
             LOG_MAIN, "Connection from %s refused: too many connections",
-    whofrom);
+    whofrom->s);
   goto ERROR_RETURN;
   }
 
@@ -241,10 +238,10 @@ if (smtp_load_reserve >= 0)
     {
     DEBUG(D_any) debug_printf("rejecting SMTP connection: load average = %.2f\n",
       (double)load_average/1000.0);
-    smtp_printf("421 Too much load; please try again later.\r\n");
+    smtp_printf("421 Too much load; please try again later.\r\n", FALSE);
     log_write(L_connection_reject,
               LOG_MAIN, "Connection from %s refused: load average = %.2f",
-      whofrom, (double)load_average/1000.0);
+      whofrom->s, (double)load_average/1000.0);
     goto ERROR_RETURN;
     }
   }
@@ -264,7 +261,7 @@ if (smtp_accept_max_per_host != NULL)
     {
     if (!expand_string_forcedfail)
       log_write(0, LOG_MAIN|LOG_PANIC, "expansion of smtp_accept_max_per_host "
-        "failed for %s: %s", whofrom, expand_string_message);
+        "failed for %s: %s", whofrom->s, expand_string_message);
     }
   /* For speed, interpret a decimal number inline here */
   else
@@ -274,7 +271,7 @@ if (smtp_accept_max_per_host != NULL)
       max_for_this_host = max_for_this_host * 10 + *s++ - '0';
     if (*s != 0)
       log_write(0, LOG_MAIN|LOG_PANIC, "expansion of smtp_accept_max_per_host "
-        "for %s contains non-digit: %s", whofrom, expanded);
+        "for %s contains non-digit: %s", whofrom->s, expanded);
     }
   }
 
@@ -290,7 +287,7 @@ if ((max_for_this_host > 0) &&
   int other_host_count = 0;    /* keep a count of non matches to optimise */
 
   for (i = 0; i < smtp_accept_max; ++i)
-    if (smtp_slots[i].host_address != NULL)
+    if (smtp_slots[i].host_address)
       {
       if (Ustrcmp(sender_host_address, smtp_slots[i].host_address) == 0)
        host_accept_count++;
@@ -312,10 +309,10 @@ if ((max_for_this_host > 0) &&
       "IP address: count=%d max=%d\n",
       host_accept_count, max_for_this_host);
     smtp_printf("421 Too many concurrent SMTP connections "
-      "from this IP address; please try again later.\r\n");
+      "from this IP address; please try again later.\r\n", FALSE);
     log_write(L_connection_reject,
               LOG_MAIN, "Connection from %s refused: too many connections "
-      "from that IP address", whofrom);
+      "from that IP address", whofrom->s);
     goto ERROR_RETURN;
     }
   }
@@ -341,7 +338,7 @@ if (LOGGING(smtp_connection))
     save_log_selector &= ~L_smtp_connection;
   else
     log_write(L_smtp_connection, LOG_MAIN, "SMTP connection from %s "
-      "(TCP/IP connection count = %d)", whofrom, smtp_accept_count + 1);
+      "(TCP/IP connection count = %d)", whofrom->s, smtp_accept_count + 1);
   }
 
 /* Now we can fork the accepting process; do a lookup tidy, just in case any
@@ -396,7 +393,7 @@ if (pid == 0)
           "(smtp_active_hostname): %s", raw_active_hostname,
           expand_string_message);
         smtp_printf("421 Local configuration error; "
-          "please try again later.\r\n");
+          "please try again later.\r\n", FALSE);
         mac_smtp_fflush();
         search_tidyup();
         _exit(EXIT_FAILURE);
@@ -510,6 +507,7 @@ if (pid == 0)
       search_tidyup();                    /* Close cached databases */
       if (!ok)                            /* Connection was dropped */
         {
+       cancel_cutthrough_connection(TRUE, US"receive dropped");
         mac_smtp_fflush();
         smtp_log_no_mail();               /* Log no mail if configured */
         _exit(EXIT_SUCCESS);
@@ -520,13 +518,15 @@ if (pid == 0)
       {
       if (smtp_out)
        {
-       int i;
+       int i, fd = fileno(smtp_in);
        uschar buf[128];
 
        mac_smtp_fflush();
        /* drain socket, for clean TCP FINs */
-       for(i = 16; read(fileno(smtp_in), buf, sizeof(buf)) > 0 && i > 0; ) i--;
+       if (fcntl(fd, F_SETFL, O_NONBLOCK) == 0)
+         for(i = 16; read(fd, buf, sizeof(buf)) > 0 && i > 0; ) i--;
        }
+      cancel_cutthrough_connection(TRUE, US"message setup dropped");
       search_tidyup();
       smtp_log_no_mail();                 /* Log no mail if configured */
 
@@ -541,9 +541,9 @@ if (pid == 0)
     DEBUG(D_receive)
       {
       int i;
-      if (sender_address != NULL)
+      if (sender_address)
         debug_printf("Sender: %s\n", sender_address);
-      if (recipients_list != NULL)
+      if (recipients_list)
         {
         debug_printf("Recipients:\n");
         for (i = 0; i < recipients_count; i++)
@@ -563,6 +563,17 @@ if (pid == 0)
 
     /* Reclaim up the store used in accepting this message */
 
+    return_path = sender_address = NULL;
+    authenticated_sender = NULL;
+    sending_ip_address = NULL;
+    deliver_host_address = deliver_host =
+    deliver_domain_orig = deliver_localpart_orig = NULL;
+    dnslist_domain = dnslist_matched = NULL;
+    callout_address = NULL;
+#ifndef DISABLE_DKIM
+    dkim_cur_signer = NULL;
+#endif
+    acl_var_m = NULL;
     store_reset(reset_point);
 
     /* If queue_only is set or if there are too many incoming connections in
@@ -641,9 +652,9 @@ if (pid == 0)
         /* Don't ever molest the parent's SSL connection, but do clean up
         the data structures if necessary. */
 
-        #ifdef SUPPORT_TLS
+#ifdef SUPPORT_TLS
         tls_close(TRUE, FALSE);
-        #endif
+#endif
 
         /* Reset SIGHUP and SIGCHLD in the child in both cases. */
 
@@ -653,25 +664,28 @@ if (pid == 0)
         if (geteuid() != root_uid && !deliver_drop_privilege)
           {
           signal(SIGALRM, SIG_DFL);
-          (void)child_exec_exim(CEE_EXEC_PANIC, FALSE, NULL, FALSE,
-           2, US"-Mc", message_id);
+         delivery_re_exec(CEE_EXEC_PANIC);
           /* Control does not return here. */
           }
 
         /* No need to re-exec; SIGALRM remains set to the default handler */
 
-        (void)deliver_message(message_id, FALSE, FALSE);
+        (void) deliver_message(message_id, FALSE, FALSE);
         search_tidyup();
         _exit(EXIT_SUCCESS);
         }
 
       if (dpid > 0)
         {
+       release_cutthrough_connection(US"passed for delivery");
         DEBUG(D_any) debug_printf("forked delivery process %d\n", (int)dpid);
         }
       else
+       {
+       cancel_cutthrough_connection(TRUE, US"delivery fork failed");
         log_write(0, LOG_MAIN|LOG_PANIC, "daemon: delivery process fork "
           "failed: %s", strerror(errno));
+       }
       }
     }
   }
@@ -733,6 +747,8 @@ else (void)close(dup_accept_socket);
 the incoming host address and an expanded active_hostname. */
 
 log_close_all();
+interface_address =
+sender_host_address = NULL;
 store_reset(reset_point);
 sender_host_address = NULL;
 }
@@ -841,13 +857,12 @@ while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
   /* If it's a listening daemon for which we are keeping track of individual
   subprocesses, deal with an accepting process that has terminated. */
 
-  if (smtp_slots != NULL)
+  if (smtp_slots)
     {
     for (i = 0; i < smtp_accept_max; i++)
-      {
       if (smtp_slots[i].pid == pid)
         {
-        if (smtp_slots[i].host_address != NULL)
+        if (smtp_slots[i].host_address)
           store_free(smtp_slots[i].host_address);
         smtp_slots[i] = empty_smtp_slot;
         if (--smtp_accept_count < 0) smtp_accept_count = 0;
@@ -855,7 +870,6 @@ while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
           smtp_accept_count, (smtp_accept_count == 1)? "" : "es");
         break;
         }
-      }
     if (i < smtp_accept_max) continue;  /* Found an accepting process */
     }
 
@@ -1055,14 +1069,10 @@ if (daemon_listen && !inetd_wait_mode)
   that contain neither a dot nor a colon are used to override daemon_smtp_port.
   Any other items are used to override local_interfaces. */
 
-  if (override_local_interfaces != NULL)
+  if (override_local_interfaces)
     {
-    uschar *new_smtp_port = NULL;
-    uschar *new_local_interfaces = NULL;
-    int portsize = 0;
-    int portptr = 0;
-    int ifacesize = 0;
-    int ifaceptr = 0;
+    gstring * new_smtp_port = NULL;
+    gstring * new_local_interfaces = NULL;
 
     if (override_pid_file_path == NULL) write_pid = FALSE;
 
@@ -1071,46 +1081,34 @@ if (daemon_listen && !inetd_wait_mode)
     while ((s = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)))
       {
       uschar joinstr[4];
-      uschar **ptr;
-      int *sizeptr;
-      int *ptrptr;
+      gstring ** gp;
 
       if (Ustrpbrk(s, ".:") == NULL)
-        {
-        ptr = &new_smtp_port;
-        sizeptr = &portsize;
-        ptrptr = &portptr;
-        }
+        gp = &new_smtp_port;
       else
-        {
-        ptr = &new_local_interfaces;
-        sizeptr = &ifacesize;
-        ptrptr = &ifaceptr;
-        }
+        gp = &new_local_interfaces;
 
-      if (*ptr == NULL)
+      if (!*gp)
         {
         joinstr[0] = sep;
         joinstr[1] = ' ';
-        *ptr = string_catn(*ptr, sizeptr, ptrptr, US"<", 1);
+        *gp = string_catn(*gp, US"<", 1);
         }
 
-      *ptr = string_catn(*ptr, sizeptr, ptrptr, joinstr, 2);
-      *ptr = string_cat (*ptr, sizeptr, ptrptr, s);
+      *gp = string_catn(*gp, joinstr, 2);
+      *gp = string_cat (*gp, s);
       }
 
-    if (new_smtp_port != NULL)
+    if (new_smtp_port)
       {
-      new_smtp_port[portptr] = 0;
-      daemon_smtp_port = new_smtp_port;
+      daemon_smtp_port = string_from_gstring(new_smtp_port);
       DEBUG(D_any) debug_printf("daemon_smtp_port overridden by -oX:\n  %s\n",
         daemon_smtp_port);
       }
 
-    if (new_local_interfaces != NULL)
+    if (new_local_interfaces)
       {
-      new_local_interfaces[ifaceptr] = 0;
-      local_interfaces = new_local_interfaces;
+      local_interfaces = string_from_gstring(new_local_interfaces);
       local_iface_source = US"-oX data";
       DEBUG(D_any) debug_printf("local_interfaces overridden by -oX:\n  %s\n",
         local_interfaces);
@@ -1118,7 +1116,7 @@ if (daemon_listen && !inetd_wait_mode)
     }
 
   /* Create a list of default SMTP ports, to be used if local_interfaces
-  contains entries without explict ports. First count the number of ports, then
+  contains entries without explicit ports. First count the number of ports, then
   build a translated list in a vector. */
 
   list = daemon_smtp_port;
@@ -1156,6 +1154,8 @@ if (daemon_listen && !inetd_wait_mode)
   while ((s = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)))
     if (!isdigit(*s))
       {
+      gstring * g = NULL;
+
       list = tls_in.on_connect_ports;
       tls_in.on_connect_ports = NULL;
       sep = 0;
@@ -1163,14 +1163,15 @@ if (daemon_listen && !inetd_wait_mode)
        {
         if (!isdigit(*s))
          {
-         struct servent *smtp_service = getservbyname(CS s, "tcp");
+         struct servent * smtp_service = getservbyname(CS s, "tcp");
          if (!smtp_service)
            log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "TCP port \"%s\" not found", s);
-         s= string_sprintf("%d", (int)ntohs(smtp_service->s_port));
+         s = string_sprintf("%d", (int)ntohs(smtp_service->s_port));
          }
-       tls_in.on_connect_ports = string_append_listele(tls_in.on_connect_ports,
-           ':', s);
+       g = string_append_listele(g, ':', s);
        }
+      if (g)
+       tls_in.on_connect_ports = g->s;
       break;
       }
 
@@ -1189,11 +1190,12 @@ if (daemon_listen && !inetd_wait_mode)
   In the same scan, fill in missing port numbers from the default list. When
   there is more than one item in the list, extra items are created. */
 
-  for (ipa = addresses; ipa != NULL; ipa = ipa->next)
+  for (ipa = addresses; ipa; ipa = ipa->next)
     {
     int i;
 
-    if (Ustrcmp(ipa->address, "0.0.0.0") == 0) ipa->address[0] = 0;
+    if (Ustrcmp(ipa->address, "0.0.0.0") == 0)
+      ipa->address[0] = 0;
     else if (Ustrcmp(ipa->address, "::0") == 0)
       {
       ipa->address[0] = ':';
@@ -1205,12 +1207,14 @@ if (daemon_listen && !inetd_wait_mode)
     if (daemon_smtp_port[0] <= 0)
       log_write(0, LOG_MAIN|LOG_PANIC_DIE, "no port specified for interface "
         "%s and daemon_smtp_port is unset; cannot start daemon",
-        (ipa->address[0] == 0)? US"\"all IPv4\"" :
-        (ipa->address[1] == 0)? US"\"all IPv6\"" : ipa->address);
+        ipa->address[0] == 0 ? US"\"all IPv4\"" :
+        ipa->address[1] == 0 ? US"\"all IPv6\"" : ipa->address);
+
     ipa->port = default_smtp_port[0];
     for (i = 1; default_smtp_port[i] > 0; i++)
       {
       ip_address_item *new = store_get(sizeof(ip_address_item));
+
       memcpy(new->address, ipa->address, Ustrlen(ipa->address) + 1);
       new->port = default_smtp_port[i];
       new->next = ipa->next;
@@ -1225,15 +1229,14 @@ if (daemon_listen && !inetd_wait_mode)
   also simplifies the construction of the "daemon started" log line. */
 
   pipa = &addresses;
-  for (ipa = addresses; ipa != NULL; pipa = &(ipa->next), ipa = ipa->next)
+  for (ipa = addresses; ipa; pipa = &ipa->next, ipa = ipa->next)
     {
     ip_address_item *ipa2;
 
     /* Handle an IPv4 wildcard */
 
     if (ipa->address[0] == 0)
-      {
-      for (ipa2 = ipa; ipa2->next != NULL; ipa2 = ipa2->next)
+      for (ipa2 = ipa; ipa2->next; ipa2 = ipa2->next)
         {
         ip_address_item *ipa3 = ipa2->next;
         if (ipa3->address[0] == ':' &&
@@ -1246,13 +1249,11 @@ if (daemon_listen && !inetd_wait_mode)
           break;
           }
         }
-      }
 
     /* Handle an IPv6 wildcard. */
 
     else if (ipa->address[0] == ':' && ipa->address[1] == 0)
-      {
-      for (ipa2 = ipa; ipa2->next != NULL; ipa2 = ipa2->next)
+      for (ipa2 = ipa; ipa2->next; ipa2 = ipa2->next)
         {
         ip_address_item *ipa3 = ipa2->next;
         if (ipa3->address[0] == 0 && ipa3->port == ipa->port)
@@ -1264,12 +1265,11 @@ if (daemon_listen && !inetd_wait_mode)
           break;
           }
         }
-      }
     }
 
   /* Get a vector to remember all the sockets in */
 
-  for (ipa = addresses; ipa != NULL; ipa = ipa->next)
+  for (ipa = addresses; ipa; ipa = ipa->next)
     listen_socket_count++;
   listen_sockets = store_get(sizeof(int) * listen_socket_count);
 
@@ -1391,20 +1391,20 @@ if (daemon_listen && !inetd_wait_mode)
     available. Just log failure (can get protocol not available, just like
     socket creation can). */
 
-    #ifdef IPV6_V6ONLY
+#ifdef IPV6_V6ONLY
     if (af == AF_INET6 && wildcard &&
-        setsockopt(listen_sockets[sk], IPPROTO_IPV6, IPV6_V6ONLY, (char *)(&on),
+        setsockopt(listen_sockets[sk], IPPROTO_IPV6, IPV6_V6ONLY, CS (&on),
           sizeof(on)) < 0)
       log_write(0, LOG_MAIN, "Setting IPV6_V6ONLY on daemon's IPv6 wildcard "
         "socket failed (%s): carrying on without it", strerror(errno));
-    #endif  /* IPV6_V6ONLY */
+#endif  /* IPV6_V6ONLY */
 
     /* Set SO_REUSEADDR so that the daemon can be restarted while a connection
     is being handled.  Without this, a connection will prevent reuse of the
     smtp port for listening. */
 
     if (setsockopt(listen_sockets[sk], SOL_SOCKET, SO_REUSEADDR,
-                   (uschar *)(&on), sizeof(on)) < 0)
+                   US (&on), sizeof(on)) < 0)
       log_write(0, LOG_MAIN|LOG_PANIC_DIE, "setting SO_REUSEADDR on socket "
         "failed when starting daemon: %s", strerror(errno));
 
@@ -1412,7 +1412,7 @@ if (daemon_listen && !inetd_wait_mode)
     disable this because it breaks some broken clients. */
 
     if (tcp_nodelay) setsockopt(listen_sockets[sk], IPPROTO_TCP, TCP_NODELAY,
-      (uschar *)(&on), sizeof(on));
+      US (&on), sizeof(on));
 
     /* Now bind the socket to the required port; if Exim is being restarted
     it may not always be possible to bind immediately, even with SO_REUSEADDR
@@ -1424,6 +1424,9 @@ if (daemon_listen && !inetd_wait_mode)
     necessary for (some release of) USAGI Linux; other IP stacks fail at the
     listen() stage instead. */
 
+#ifdef TCP_FASTOPEN
+    tcp_fastopen_ok = TRUE;
+#endif
     for(;;)
       {
       uschar *msg, *addr;
@@ -1436,8 +1439,11 @@ if (daemon_listen && !inetd_wait_mode)
         goto SKIP_SOCKET;
         }
       msg = US strerror(errno);
-      addr = wildcard? ((af == AF_INET6)? US"(any IPv6)" : US"(any IPv4)") :
-        ipa->address;
+      addr = wildcard
+        ? af == AF_INET6
+       ? US"(any IPv6)"
+       : US"(any IPv4)"
+       : ipa->address;
       if (daemon_startup_retries <= 0)
         log_write(0, LOG_MAIN|LOG_PANIC_DIE,
           "socket bind() to port %d for address %s failed: %s: "
@@ -1458,10 +1464,12 @@ if (daemon_listen && !inetd_wait_mode)
         debug_printf("listening on %s port %d\n", ipa->address, ipa->port);
 
 #ifdef TCP_FASTOPEN
-    if (setsockopt(listen_sockets[sk], SOL_TCP, TCP_FASTOPEN, &smtp_connect_backlog,
-                   sizeof(smtp_connect_backlog)))
-      log_write(0, LOG_MAIN|LOG_PANIC, "failed to set socket FASTOPEN: %s",
-       strerror(errno));
+    if (setsockopt(listen_sockets[sk], IPPROTO_TCP, TCP_FASTOPEN,
+                   &smtp_connect_backlog, sizeof(smtp_connect_backlog)))
+      {
+      DEBUG(D_any) debug_printf("setsockopt FASTOPEN: %s\n", strerror(errno));
+      tcp_fastopen_ok = FALSE;
+      }
 #endif
 
     /* Start listening on the bound socket, establishing the maximum backlog of
@@ -1489,7 +1497,7 @@ if (daemon_listen && !inetd_wait_mode)
     are going to ignore. We remove the address from the chain, and back up the
     counts. */
 
-    SKIP_SOCKET:
+  SKIP_SOCKET:
     sk--;                          /* Back up the count */
     listen_socket_count--;         /* Reduce the total */
     if (ipa == addresses) addresses = ipa->next; else
@@ -1505,7 +1513,8 @@ if (daemon_listen && !inetd_wait_mode)
 /* If we are not listening, we want to write a pid file only if -oP was
 explicitly given. */
 
-else if (override_pid_file_path == NULL) write_pid = FALSE;
+else if (!override_pid_file_path)
+  write_pid = FALSE;
 
 /* Write the pid to a known file for assistance in identification, if required.
 We do this before giving up root privilege, because on some systems it is
@@ -1525,25 +1534,22 @@ if (running_in_test_harness || write_pid)
   {
   FILE *f;
 
-  if (override_pid_file_path != NULL)
+  if (override_pid_file_path)
     pid_file_path = override_pid_file_path;
 
   if (pid_file_path[0] == 0)
     pid_file_path = string_sprintf("%s/exim-daemon.pid", spool_directory);
 
-  f = modefopen(pid_file_path, "wb", 0644);
-  if (f != NULL)
+  if ((f = modefopen(pid_file_path, "wb", 0644)))
     {
     (void)fprintf(f, "%d\n", (int)getpid());
     (void)fclose(f);
     DEBUG(D_any) debug_printf("pid written to %s\n", pid_file_path);
     }
   else
-    {
     DEBUG(D_any)
       debug_printf("%s\n", string_open_failed(errno, "pid file %s",
         pid_file_path));
-    }
   }
 
 /* Set up the handler for SIGHUP, which causes a restart of the daemon. */
@@ -1613,7 +1619,7 @@ else if (daemon_listen)
   int i, j;
   int smtp_ports = 0;
   int smtps_ports = 0;
-  ip_address_item * ipa;
+  ip_address_item * ipa, * i2;
   uschar * p = big_buffer;
   uschar * qinfo = queue_interval > 0
     ? string_sprintf("-q%s", readconf_printtime(queue_interval))
@@ -1629,52 +1635,60 @@ else if (daemon_listen)
   for (j = 0; j < 2; j++)
     {
     for (i = 0, ipa = addresses; i < 10 && ipa; i++, ipa = ipa->next)
-       {
-       /* First time round, look for SMTP ports; second time round, look for
-       SMTPS ports. For the first one of each, insert leading text. */
-
-       if (host_is_tls_on_connect_port(ipa->port) == (j > 0))
-         {
-        if (j == 0)
-          {
-          if (smtp_ports++ == 0)
-             {
-             memcpy(p, "SMTP on", 8);
-             p += 7;
-             }
-          }
-        else
-          {
-          if (smtps_ports++ == 0)
-             {
-             (void)sprintf(CS p, "%sSMTPS on",
-               smtp_ports == 0 ? "" : " and for ");
-             while (*p) p++;
-             }
-          }
-
-         /* Now the information about the port (and sometimes interface) */
-
-         if (ipa->address[0] == ':' && ipa->address[1] == 0)
-           {
-           if (ipa->next != NULL && ipa->next->address[0] == 0 &&
-               ipa->next->port == ipa->port)
-             {
-             (void)sprintf(CS p, " port %d (IPv6 and IPv4)", ipa->port);
-             ipa = ipa->next;
-             }
-           else if (ipa->v6_include_v4)
-             (void)sprintf(CS p, " port %d (IPv6 with IPv4)", ipa->port);
-           else
-             (void)sprintf(CS p, " port %d (IPv6)", ipa->port);
-           }
-         else if (ipa->address[0] == 0)
-           (void)sprintf(CS p, " port %d (IPv4)", ipa->port);
-         else
-           (void)sprintf(CS p, " [%s]:%d", ipa->address, ipa->port);
-         while (*p != 0) p++;
-         }
-       }
+      {
+      /* First time round, look for SMTP ports; second time round, look for
+      SMTPS ports. For the first one of each, insert leading text. */
+
+      if (host_is_tls_on_connect_port(ipa->port) == (j > 0))
+       {
+       if (j == 0)
+         {
+         if (smtp_ports++ == 0)
+           {
+           memcpy(p, "SMTP on", 8);
+           p += 7;
+           }
+         }
+       else
+         if (smtps_ports++ == 0)
+           p += sprintf(CS p, "%sSMTPS on",
+             smtp_ports == 0 ? "" : " and for ");
+
+       /* Now the information about the port (and sometimes interface) */
+
+       if (ipa->address[0] == ':' && ipa->address[1] == 0)
+         {                                             /* v6 wildcard */
+         if (ipa->next && ipa->next->address[0] == 0 &&
+             ipa->next->port == ipa->port)
+           {
+           p += sprintf(CS p, " port %d (IPv6 and IPv4)", ipa->port);
+           ipa = ipa->next;
+           }
+         else if (ipa->v6_include_v4)
+           p += sprintf(CS p, " port %d (IPv6 with IPv4)", ipa->port);
+         else
+           p += sprintf(CS p, " port %d (IPv6)", ipa->port);
+         }
+       else if (ipa->address[0] == 0)                  /* v4 wildcard */
+         p += sprintf(CS p, " port %d (IPv4)", ipa->port);
+       else                            /* check for previously-seen IP */
+         {
+         for (i2 = addresses; i2 != ipa; i2 = i2->next)
+           if (  host_is_tls_on_connect_port(i2->port) == (j > 0)
+              && Ustrcmp(ipa->address, i2->address) == 0
+              )
+             {                         /* found; append port to list */
+             if (p[-1] == '}') p--;
+             while (isdigit(*--p)) ;
+             p +=  1 + sprintf(CS p+1, "%s%d,%d}", *p == ',' ? "" : "{",
+               i2->port, ipa->port);
+             break;
+             }
+         if (i2 == ipa)                /* first-time IP */
+           p += sprintf(CS p, " [%s]:%d", ipa->address, ipa->port);
+         }
+       }
+      }
 
     if (ipa)
       {
@@ -1935,10 +1949,8 @@ for (;;)
       errno = EINTR;
       }
     else
-      {
       lcount = select(max_socket + 1, (SELECT_ARG2_TYPE *)&select_listen,
         NULL, NULL, NULL);
-      }
 
     if (lcount < 0)
       {
@@ -1964,10 +1976,9 @@ for (;;)
     while (lcount-- > 0)
       {
       int accept_socket = -1;
+
       if (!select_failed)
-        {
         for (sk = 0; sk < listen_socket_count; sk++)
-          {
           if (FD_ISSET(listen_sockets[sk], &select_listen))
             {
             len = sizeof(accepted);
@@ -1976,8 +1987,6 @@ for (;;)
             FD_CLR(listen_sockets[sk], &select_listen);
             break;
             }
-          }
-        }
 
       /* If select or accept has failed and this was not caused by an
       interruption, log the incident and try again. With asymmetric TCP/IP