#include "exim.h"
-#ifdef __GLIBC__
+#if defined(__GLIBC__) && !defined(__UCLIBC__)
# include <gnu/libc-version.h>
#endif
/* Exim uses a time + a pid to generate a unique identifier in two places: its
message IDs, and in file names for maildir deliveries. Because some OS now
re-use pids within the same second, sub-second times are now being used.
-However, for absolute certaintly, we must ensure the clock has ticked before
+However, for absolute certainty, we must ensure the clock has ticked before
allowing the relevant process to complete. At the time of implementation of
this code (February 2003), the speed of processors is such that the clock will
invariably have ticked already by the time a process has done its job. This
#ifdef SUPPORT_SOCKS
fprintf(f, " SOCKS");
#endif
+#ifdef TCP_FASTOPEN
+ fprintf(f, " TCP_Fast_Open");
+#endif
#ifdef EXPERIMENTAL_LMDB
fprintf(f, " Experimental_LMDB");
#endif
fprintf(f, "Compiler: <unknown>\n");
#endif
-#ifdef __GLIBC__
+#if defined(__GLIBC__) && !defined(__UCLIBC__)
fprintf(f, "Library version: Glibc: Compile: %d.%d\n",
__GLIBC__, __GLIBC_MINOR__);
if (__GLIBC_PREREQ(2, 1))
exim_usage(uschar *progname)
{
-/* Handle specific program invocation varients */
+/* Handle specific program invocation variants */
if (Ustrcmp(progname, US"-mailq") == 0)
{
fprintf(stderr,
break;
}
- /* An option consistion of -- terminates the options */
+ /* An option consisting of -- terminates the options */
if (Ustrcmp(arg, "--") == 0)
{
#ifdef ALT_CONFIG_PREFIX
int sep = 0;
int len = Ustrlen(ALT_CONFIG_PREFIX);
- uschar *list = argrest;
+ const uschar *list = argrest;
uschar *filename;
while((filename = string_nextinlist(&list, &sep, big_buffer,
big_buffer_size)) != NULL)
exit(EXIT_FAILURE);
}
- m = macro_create(name, s, TRUE, FALSE);
+ m = macro_create(string_copy(name), string_copy(s), TRUE, FALSE);
if (clmacro_count >= MAX_CLMACROS)
{
return EXIT_FAILURE;
}
- /* Set up $sending_ip_address and $sending_port */
+ /* Set up $sending_ip_address and $sending_port, unless proxied */
- if (getsockname(fileno(stdin), (struct sockaddr *)(&interface_sock),
- &size) == 0)
- sending_ip_address = host_ntoa(-1, &interface_sock, NULL,
- &sending_port);
- else
- {
- fprintf(stderr, "exim: getsockname() failed after -MC option: %s\n",
- strerror(errno));
- return EXIT_FAILURE;
- }
+ if (!continue_proxy_cipher)
+ if (getsockname(fileno(stdin), (struct sockaddr *)(&interface_sock),
+ &size) == 0)
+ sending_ip_address = host_ntoa(-1, &interface_sock, NULL,
+ &sending_port);
+ else
+ {
+ fprintf(stderr, "exim: getsockname() failed after -MC option: %s\n",
+ strerror(errno));
+ return EXIT_FAILURE;
+ }
if (running_in_test_harness) millisleep(500);
break;
else if (*argrest == 'C' && argrest[1] && !argrest[2])
{
- switch(argrest[1])
+ switch(argrest[1])
{
/* -MCA: set the smtp_authenticated flag; this is useful only when it
precedes -MC (see above). The flag indicates that the host to which
case 'S': smtp_peer_options |= PEER_OFFERED_SIZE; break;
#ifdef SUPPORT_TLS
+ /* -MCt: similar to -MCT below but the connection is still open
+ via a proxy proces which handles the TLS context and coding.
+ Require three arguments for the proxied local address and port,
+ and the TLS cipher. */
+
+ case 't': if (++i < argc) sending_ip_address = argv[i];
+ else badarg = TRUE;
+ if (++i < argc) sending_port = (int)(Uatol(argv[i]));
+ else badarg = TRUE;
+ if (++i < argc) continue_proxy_cipher = argv[i];
+ else badarg = TRUE;
+ /*FALLTHROUGH*/
+
/* -MCT: set the tls_offered flag; this is useful only when it
precedes -MC (see above). The flag indicates that the host to which
Exim is connected has offered TLS support. */
/* -oMr: Received protocol */
- else if (Ustrcmp(argrest, "Mr") == 0) received_protocol = argv[++i];
+ else if (Ustrcmp(argrest, "Mr") == 0)
+
+ if (received_protocol)
+ {
+ fprintf(stderr, "received_protocol is set already\n");
+ exit(EXIT_FAILURE);
+ }
+ else received_protocol = argv[++i];
/* -oMs: Set sender host name */
if (*argrest != 0)
{
- uschar *hn = Ustrchr(argrest, ':');
+ uschar *hn;
+
+ if (received_protocol)
+ {
+ fprintf(stderr, "received_protocol is set already\n");
+ exit(EXIT_FAILURE);
+ }
+
+ hn = Ustrchr(argrest, ':');
if (hn == NULL)
{
received_protocol = argrest;
}
else
{
+ int old_pool = store_pool;
+ store_pool = POOL_PERM;
received_protocol = string_copyn(argrest, hn - argrest);
+ store_pool = old_pool;
sender_host_name = hn + 1;
}
}
readconf_main(checking || list_options);
+if (builtin_macros_create_trigger) DEBUG(D_any)
+ debug_printf("Builtin macros created (expensive) due to config line '%.*s'\n",
+ Ustrlen(builtin_macros_create_trigger)-1, builtin_macros_create_trigger);
+
/* Now in directory "/" */
if (cleanup_environment() == FALSE)
else
{
int i, j;
- for (i = 0; i < group_count; i++)
- {
- if (group_list[i] == exim_gid) admin_user = TRUE;
- else if (admin_groups != NULL)
- {
- for (j = 1; j <= (int)(admin_groups[0]); j++)
+ for (i = 0; i < group_count && !admin_user; i++)
+ if (group_list[i] == exim_gid)
+ admin_user = TRUE;
+ else if (admin_groups)
+ for (j = 1; j <= (int)admin_groups[0] && !admin_user; j++)
if (admin_groups[j] == group_list[i])
- { admin_user = TRUE; break; }
- }
- if (admin_user) break;
- }
+ admin_user = TRUE;
}
/* Another group of privileged users are the trusted users. These are root,
{
int i, j;
- if (trusted_users != NULL)
- {
- for (i = 1; i <= (int)(trusted_users[0]); i++)
+ if (trusted_users)
+ for (i = 1; i <= (int)trusted_users[0] && !trusted_caller; i++)
if (trusted_users[i] == real_uid)
- { trusted_caller = TRUE; break; }
- }
+ trusted_caller = TRUE;
- if (!trusted_caller && trusted_groups != NULL)
- {
- for (i = 1; i <= (int)(trusted_groups[0]); i++)
- {
+ if (trusted_groups)
+ for (i = 1; i <= (int)trusted_groups[0] && !trusted_caller; i++)
if (trusted_groups[i] == real_gid)
trusted_caller = TRUE;
- else for (j = 0; j < group_count; j++)
- {
+ else for (j = 0; j < group_count && !trusted_caller; j++)
if (trusted_groups[i] == group_list[j])
- { trusted_caller = TRUE; break; }
- }
- if (trusted_caller) break;
- }
- }
+ trusted_caller = TRUE;
}
+/* At this point, we know if the user is privileged and some command-line
+options become possibly imperssible, depending upon the configuration file. */
+
+if (checking && commandline_checks_require_admin && !admin_user) {
+ fprintf(stderr, "exim: those command-line flags are set to require admin\n");
+ exit(EXIT_FAILURE);
+}
+
/* Handle the decoding of logging options. */
decode_bits(log_selector, log_selector_size, log_notall,
(msg_action_arg < 0 || /* and */
msg_action != MSG_DELIVER) && /* not delivering and */
(!checking || !address_test_mode) /* not address checking */
- )
- ))
- {
+ ) ) )
exim_setugid(exim_uid, exim_gid, TRUE, US"privilege not needed");
- }
/* When we are retaining a privileged uid, we still change to the exim gid. */
there's no security risk. For me, it's { exim -bV } on a just-built binary,
no need to complain then. */
if (rv == -1)
- {
if (!(unprivileged || removed_privilege))
{
fprintf(stderr,
else
DEBUG(D_any) debug_printf("changing group to %ld failed: %s\n",
(long int)exim_gid, strerror(errno));
- }
}
/* Handle a request to scan a file for malware */
verify_get_ident(1413);
}
- /* In case the given address is a non-canonical IPv6 address, canonicize
+ /* In case the given address is a non-canonical IPv6 address, canonicalize
it. The code works for both IPv4 and IPv6, as it happens. */
size = host_aton(sender_host_address, x);
if (smtp_start_session())
{
- reset_point = store_get(0);
- for (;;)
+ for (reset_point = store_get(0); ; store_reset(reset_point))
{
- store_reset(reset_point);
if (smtp_setup_msg() <= 0) break;
if (!receive_msg(FALSE)) break;
+
+ return_path = sender_address = NULL;
+ dnslist_domain = dnslist_matched = NULL;
+#ifndef DISABLE_DKIM
+ dkim_cur_signer = NULL;
+#endif
+ acl_var_m = NULL;
+ deliver_localpart_orig = NULL;
+ deliver_domain_orig = NULL;
+ callout_address = sending_ip_address = NULL;
+ sender_rate = sender_rate_limit = sender_rate_period = NULL;
}
smtp_log_no_mail();
}
}
else
{
- if (received_protocol == NULL)
+ int old_pool = store_pool;
+ store_pool = POOL_PERM;
+ if (!received_protocol)
received_protocol = string_sprintf("local%s", called_as);
+ store_pool = old_pool;
set_process_info("accepting a local non-SMTP message from <%s>",
sender_address);
}
else
{
thismessage_size_limit = expand_string_integer(message_size_limit, TRUE);
- if (expand_string_message != NULL)
- {
+ if (expand_string_message)
if (thismessage_size_limit == -1)
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to expand "
"message_size_limit: %s", expand_string_message);
else
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "invalid value for "
"message_size_limit: %s", expand_string_message);
- }
}
/* Loop for several messages when reading SMTP input. If we fork any child
of Linux (where SIG_IGN does work) that are picky. If, having set SIG_IGN, a
process then calls waitpid(), a grumble is written to the system log, because
this is logically inconsistent. In other words, it doesn't like the paranoia.
-As a consequenc of this, the waitpid() below is now excluded if we are sure
+As a consequence of this, the waitpid() below is now excluded if we are sure
that SIG_IGN works. */
if (!synchronous_delivery)
while (more)
{
- store_reset(reset_point);
message_id[0] = 0;
/* Handle the SMTP case; call smtp_setup_mst() to deal with the initial SMTP
more = receive_msg(extract_recipients);
if (message_id[0] == 0)
{
- if (more) continue;
+ cancel_cutthrough_connection(TRUE, US"receive dropped");
+ if (more) goto moreloop;
smtp_log_no_mail(); /* Log no mail if configured */
exim_exit(EXIT_FAILURE);
}
}
else
{
+ cancel_cutthrough_connection(TRUE, US"message setup dropped");
smtp_log_no_mail(); /* Log no mail if configured */
exim_exit((rc == 0)? EXIT_SUCCESS : EXIT_FAILURE);
}
if (!receive_timeout)
{
- struct timeval t = { 30*60, 0 }; /* 30 minutess */
+ struct timeval t = { 30*60, 0 }; /* 30 minutes */
fd_set r;
FD_ZERO(&r); FD_SET(0, &r);
not if queue_only is set (case 0). Case 1 doesn't happen here (too many
connections). */
- if (local_queue_only) switch(queue_only_reason)
+ if (local_queue_only)
{
- case 2:
- log_write(L_delay_delivery,
- LOG_MAIN, "no immediate delivery: more than %d messages "
- "received in one connection", smtp_accept_queue_per_connection);
- break;
+ cancel_cutthrough_connection(TRUE, US"no delivery; queueing");
+ switch(queue_only_reason)
+ {
+ case 2:
+ log_write(L_delay_delivery,
+ LOG_MAIN, "no immediate delivery: more than %d messages "
+ "received in one connection", smtp_accept_queue_per_connection);
+ break;
- case 3:
- log_write(L_delay_delivery,
- LOG_MAIN, "no immediate delivery: load average %.2f",
- (double)load_average/1000.0);
- break;
+ case 3:
+ log_write(L_delay_delivery,
+ LOG_MAIN, "no immediate delivery: load average %.2f",
+ (double)load_average/1000.0);
+ break;
+ }
}
+ else if (queue_only_policy || deliver_freeze)
+ cancel_cutthrough_connection(TRUE, US"no delivery; queueing");
+
/* Else do the delivery unless the ACL or local_scan() called for queue only
or froze the message. Always deliver in a separate process. A fork failure is
not a disaster, as the delivery will eventually happen on a subsequent queue
thereby defer the delivery if it tries to use (for example) a cached ldap
connection that the parent has called unbind on. */
- else if (!queue_only_policy && !deliver_freeze)
+ else
{
pid_t pid;
search_tidyup();
if (geteuid() != root_uid && !deliver_drop_privilege && !unprivileged)
{
- (void)child_exec_exim(CEE_EXEC_EXIT, FALSE, NULL, FALSE,
- 2, US"-Mc", message_id);
+ delivery_re_exec(CEE_EXEC_EXIT);
/* Control does not return here. */
}
if (pid < 0)
{
+ cancel_cutthrough_connection(TRUE, US"delivery fork failed");
log_write(0, LOG_MAIN|LOG_PANIC, "failed to fork automatic delivery "
"process: %s", strerror(errno));
}
+ else
+ {
+ release_cutthrough_connection(US"msg passed for delivery");
- /* In the parent, wait if synchronous delivery is required. This will
- always be the case in MUA wrapper mode. */
+ /* In the parent, wait if synchronous delivery is required. This will
+ always be the case in MUA wrapper mode. */
- else if (synchronous_delivery)
- {
- int status;
- while (wait(&status) != pid);
- if ((status & 0x00ff) != 0)
- log_write(0, LOG_MAIN|LOG_PANIC,
- "process %d crashed with signal %d while delivering %s",
- (int)pid, status & 0x00ff, message_id);
- if (mua_wrapper && (status & 0xffff) != 0) exim_exit(EXIT_FAILURE);
+ if (synchronous_delivery)
+ {
+ int status;
+ while (wait(&status) != pid);
+ if ((status & 0x00ff) != 0)
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "process %d crashed with signal %d while delivering %s",
+ (int)pid, status & 0x00ff, message_id);
+ if (mua_wrapper && (status & 0xffff) != 0) exim_exit(EXIT_FAILURE);
+ }
}
}
#ifndef SIG_IGN_WORKS
while (waitpid(-1, NULL, WNOHANG) > 0);
#endif
+
+moreloop:
+ return_path = sender_address = NULL;
+ authenticated_sender = NULL;
+ deliver_localpart_orig = NULL;
+ deliver_domain_orig = NULL;
+ deliver_host = deliver_host_address = NULL;
+ dnslist_domain = dnslist_matched = NULL;
+#ifdef WITH_CONTENT_SCAN
+ malware_name = NULL;
+#endif
+ callout_address = NULL;
+ sending_ip_address = NULL;
+ acl_var_m = NULL;
+ { int i; for(i=0; i<REGEX_VARS; i++) regex_vars[i] = NULL; }
+
+ store_reset(reset_point);
}
exim_exit(EXIT_SUCCESS); /* Never returns */