* 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 */
} 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 };
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++;
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);
{
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 */
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++)
/* 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
/* 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. */
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));
+ }
}
}
}
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;
}
/* 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;
smtp_accept_count, (smtp_accept_count == 1)? "" : "es");
break;
}
- }
if (i < smtp_accept_max) continue; /* Found an accepting process */
}
}
/* 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;
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] = ':';
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;
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] == ':' &&
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)
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);
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),
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
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;
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: "
#ifdef TCP_FASTOPEN
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
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
/* 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
{
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. */
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))
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)
{
errno = EINTR;
}
else
- {
lcount = select(max_socket + 1, (SELECT_ARG2_TYPE *)&select_listen,
NULL, NULL, NULL);
- }
if (lcount < 0)
{
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);
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