X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/527504e8d8ff7a1cd967ea57cb7f29b92b052bae..d3e58fcb87faf7131a2712fcfaef200ffd191f05:/src/src/receive.c diff --git a/src/src/receive.c b/src/src/receive.c index ae2c93b4e..39a0ed577 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -2,12 +2,13 @@ * 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. */ /* Code for receiving a message and setting up spool files. */ #include "exim.h" +#include #ifdef EXPERIMENTAL_DCC extern int dcc_ok; @@ -27,6 +28,12 @@ static uschar *spool_name = US""; enum CH_STATE {LF_SEEN, MID_LINE, CR_SEEN}; +#ifdef HAVE_LOCAL_SCAN +jmp_buf local_scan_env; /* error-handling context for local_scan */ +unsigned had_local_scan_crash; +unsigned had_local_scan_timeout; +#endif + /************************************************* * Non-SMTP character reading functions * @@ -40,7 +47,27 @@ changing the pointer variables.) */ int stdin_getc(unsigned lim) { -return getc(stdin); +int c = getc(stdin); + +if (had_data_timeout) + { + fprintf(stderr, "exim: timed out while reading - message abandoned\n"); + log_write(L_lost_incoming_connection, + LOG_MAIN, "timed out while reading local message"); + receive_bomb_out(US"data-timeout", NULL); /* Does not return */ + } +if (had_data_sigint) + { + if (filter_test == FTEST_NONE) + { + fprintf(stderr, "\nexim: %s received - message abandoned\n", + had_data_sigint == SIGTERM ? "SIGTERM" : "SIGINT"); + log_write(0, LOG_MAIN, "%s received while reading local message", + had_data_sigint == SIGTERM ? "SIGTERM" : "SIGINT"); + } + receive_bomb_out(US"signal-exit", NULL); /* Does not return */ + } +return c; } int @@ -316,11 +343,13 @@ if (spool_name[0] != '\0') /* Now close the file if it is open, either as a fd or a stream. */ -if (data_file != NULL) +if (data_file) { (void)fclose(data_file); data_file = NULL; -} else if (data_fd >= 0) { + } +else if (data_fd >= 0) + { (void)close(data_fd); data_fd = -1; } @@ -361,37 +390,29 @@ Returns: nothing static void data_timeout_handler(int sig) { -uschar *msg = NULL; - -sig = sig; /* Keep picky compilers happy */ - -if (smtp_input) - { - msg = US"SMTP incoming data timeout"; - log_write(L_lost_incoming_connection, - LOG_MAIN, "SMTP data timeout (message abandoned) on connection " - "from %s F=<%s>", - (sender_fullhost != NULL)? sender_fullhost : US"local process", - sender_address); - } -else - { - fprintf(stderr, "exim: timed out while reading - message abandoned\n"); - log_write(L_lost_incoming_connection, - LOG_MAIN, "timed out while reading local message"); - } - -receive_bomb_out(US"data-timeout", msg); /* Does not return */ +had_data_timeout = sig; } +#ifdef HAVE_LOCAL_SCAN /************************************************* * local_scan() timeout * *************************************************/ /* Handler function for timeouts that occur while running a local_scan() -function. +function. Posix recommends against calling longjmp() from a signal-handler, +but the GCC manual says you can so we will, and trust that it's better than +calling probably non-signal-safe funxtions during logging from within the +handler, even with other compilers. + +See also https://cwe.mitre.org/data/definitions/745.html which also lists +it as unsafe. + +This is all because we have no control over what might be written for a +local-scan function, so cannot sprinkle had-signal checks after each +call-site. At least with the default "do-nothing" function we won't +ever get here. Argument: the signal number Returns: nothing @@ -400,11 +421,8 @@ Returns: nothing static void local_scan_timeout_handler(int sig) { -sig = sig; /* Keep picky compilers happy */ -log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() function timed out - " - "message temporarily rejected (size %d)", message_size); -/* Does not return */ -receive_bomb_out(US"local-scan-timeout", US"local verification problem"); +had_local_scan_timeout = sig; +siglongjmp(local_scan_env, 1); } @@ -423,12 +441,12 @@ Returns: nothing static void local_scan_crash_handler(int sig) { -log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() function crashed with " - "signal %d - message temporarily rejected (size %d)", sig, message_size); -/* Does not return */ -receive_bomb_out(US"local-scan-error", US"local verification problem"); +had_local_scan_crash = sig; +siglongjmp(local_scan_env, 1); } +#endif /*HAVE_LOCAL_SCAN*/ + /************************************************* * SIGTERM or SIGINT received * @@ -444,26 +462,7 @@ Returns: nothing static void data_sigterm_sigint_handler(int sig) { -uschar *msg = NULL; - -if (smtp_input) - { - msg = US"Service not available - SIGTERM or SIGINT received"; - log_write(0, LOG_MAIN, "%s closed after %s", smtp_get_connection_info(), - (sig == SIGTERM)? "SIGTERM" : "SIGINT"); - } -else - { - if (filter_test == FTEST_NONE) - { - fprintf(stderr, "\nexim: %s received - message abandoned\n", - (sig == SIGTERM)? "SIGTERM" : "SIGINT"); - log_write(0, LOG_MAIN, "%s received while reading local message", - (sig == SIGTERM)? "SIGTERM" : "SIGINT"); - } - } - -receive_bomb_out(US"signal-exit", msg); /* Does not return */ +had_data_sigint = sig; } @@ -1023,7 +1022,8 @@ int ch; /* Remember that this message uses wireformat. */ -DEBUG(D_receive) debug_printf("CHUNKING: writing spoolfile in wire format\n"); +DEBUG(D_receive) debug_printf("CHUNKING: %s\n", + fout ? "writing spoolfile in wire format" : "flushing input"); spool_file_wireformat = TRUE; for (;;) @@ -1033,6 +1033,7 @@ for (;;) unsigned len = MAX(chunking_data_left, thismessage_size_limit - message_size + 1); uschar * buf = bdat_getbuf(&len); + if (!buf) return END_EOF; message_size += len; if (fout && fwrite(buf, len, 1, fout) != 1) return END_WERROR; } @@ -1077,9 +1078,10 @@ Returns: nothing void receive_swallow_smtp(void) { -/*XXX CHUNKING: not enough. read chunks until RSET? */ if (message_ended >= END_NOTENDED) - message_ended = read_message_data_smtp(NULL); + message_ended = chunking_state <= CHUNKING_OFFERED + ? read_message_data_smtp(NULL) + : read_message_bdat_smtp_wire(NULL); } @@ -1221,48 +1223,49 @@ for (h = acl_added_headers; h; h = next) switch(h->type) { case htype_add_top: - h->next = header_list; - header_list = h; - DEBUG(D_receive|D_acl) debug_printf_indent(" (at top)"); - break; + h->next = header_list; + header_list = h; + DEBUG(D_receive|D_acl) debug_printf_indent(" (at top)"); + break; case htype_add_rec: - if (last_received == NULL) - { - last_received = header_list; - while (!header_testname(last_received, US"Received", 8, FALSE)) - last_received = last_received->next; - while (last_received->next != NULL && - header_testname(last_received->next, US"Received", 8, FALSE)) - last_received = last_received->next; - } - h->next = last_received->next; - last_received->next = h; - DEBUG(D_receive|D_acl) debug_printf_indent(" (after Received:)"); - break; + if (!last_received) + { + last_received = header_list; + while (!header_testname(last_received, US"Received", 8, FALSE)) + last_received = last_received->next; + while (last_received->next && + header_testname(last_received->next, US"Received", 8, FALSE)) + last_received = last_received->next; + } + h->next = last_received->next; + last_received->next = h; + DEBUG(D_receive|D_acl) debug_printf_indent(" (after Received:)"); + break; case htype_add_rfc: - /* add header before any header which is NOT Received: or Resent- */ - last_received = header_list; - while ( (last_received->next != NULL) && - ( (header_testname(last_received->next, US"Received", 8, FALSE)) || - (header_testname_incomplete(last_received->next, US"Resent-", 7, FALSE)) ) ) - last_received = last_received->next; - /* last_received now points to the last Received: or Resent-* header - in an uninterrupted chain of those header types (seen from the beginning - of all headers. Our current header must follow it. */ - h->next = last_received->next; - last_received->next = h; - DEBUG(D_receive|D_acl) debug_printf_indent(" (before any non-Received: or Resent-*: header)"); - break; + /* add header before any header which is NOT Received: or Resent- */ + last_received = header_list; + while ( last_received->next && + ( (header_testname(last_received->next, US"Received", 8, FALSE)) || + (header_testname_incomplete(last_received->next, US"Resent-", 7, FALSE)) ) ) + last_received = last_received->next; + /* last_received now points to the last Received: or Resent-* header + in an uninterrupted chain of those header types (seen from the beginning + of all headers. Our current header must follow it. */ + h->next = last_received->next; + last_received->next = h; + DEBUG(D_receive|D_acl) debug_printf_indent(" (before any non-Received: or Resent-*: header)"); + break; default: - h->next = NULL; - header_last->next = h; - break; + h->next = NULL; + header_last->next = h; + DEBUG(D_receive|D_acl) debug_printf_indent(" "); + break; } - if (h->next == NULL) header_last = h; + if (!h->next) header_last = h; /* Check for one of the known header types (From:, To:, etc.) though in practice most added headers are going to be "other". Lower case @@ -1273,7 +1276,7 @@ for (h = acl_added_headers; h; h = next) h->type = header_checkname(h, FALSE); if (h->type >= 'a') h->type = htype_other; - DEBUG(D_receive|D_acl) debug_printf_indent(" %s", header_last->text); + DEBUG(D_receive|D_acl) debug_printf("%s", h->text); } acl_added_headers = NULL; @@ -1346,7 +1349,7 @@ run_mime_acl(uschar *acl, BOOL *smtp_yield_ptr, uschar **smtp_reply_ptr, uschar **blackholed_by_ptr) { FILE *mbox_file; -uschar rfc822_file_path[2048]; +uschar * rfc822_file_path = NULL; unsigned long mbox_size; header_line *my_headerlist; uschar *user_msg, *log_msg; @@ -1354,8 +1357,6 @@ int mime_part_count_buffer = -1; uschar * mbox_filename; int rc = OK; -memset(CS rfc822_file_path,0,2048); - /* check if it is a MIME message */ for (my_headerlist = header_list; my_headerlist; my_headerlist = my_headerlist->next) @@ -1395,7 +1396,7 @@ mime_part_count = -1; rc = mime_acl_check(acl, mbox_file, NULL, &user_msg, &log_msg); (void)fclose(mbox_file); -if (Ustrlen(rfc822_file_path) > 0) +if (rfc822_file_path) { mime_part_count = mime_part_count_buffer; @@ -1403,36 +1404,31 @@ if (Ustrlen(rfc822_file_path) > 0) { log_write(0, LOG_PANIC, "acl_smtp_mime: can't unlink RFC822 spool file, skipping."); - goto END_MIME_ACL; + goto END_MIME_ACL; } + rfc822_file_path = NULL; } /* check if we must check any message/rfc822 attachments */ if (rc == OK) { - uschar * scandir; + uschar * scandir = string_copyn(mbox_filename, + Ustrrchr(mbox_filename, '/') - mbox_filename); struct dirent * entry; DIR * tempdir; - scandir = string_copyn(mbox_filename, Ustrrchr(mbox_filename, '/') - mbox_filename); - - tempdir = opendir(CS scandir); - for (;;) - { - if (!(entry = readdir(tempdir))) - break; + for (tempdir = opendir(CS scandir); entry = readdir(tempdir); ) if (strncmpic(US entry->d_name, US"__rfc822_", 9) == 0) { - (void) string_format(rfc822_file_path, sizeof(rfc822_file_path), - "%s/%s", scandir, entry->d_name); - DEBUG(D_receive) debug_printf("RFC822 attachment detected: running MIME ACL for '%s'\n", - rfc822_file_path); + rfc822_file_path = string_sprintf("%s/%s", scandir, entry->d_name); + DEBUG(D_receive) + debug_printf("RFC822 attachment detected: running MIME ACL for '%s'\n", + rfc822_file_path); break; } - } closedir(tempdir); - if (entry) + if (rfc822_file_path) { if ((mbox_file = Ufopen(rfc822_file_path, "rb"))) { @@ -1461,10 +1457,10 @@ else if (rc != OK) #ifdef EXPERIMENTAL_DCC dcc_ok = 0; #endif - if ( smtp_input - && smtp_handle_acl_fail(ACL_WHERE_MIME, rc, user_msg, log_msg) != 0) + if (smtp_input) { - *smtp_yield_ptr = FALSE; /* No more messages after dropped connection */ + if (smtp_handle_acl_fail(ACL_WHERE_MIME, rc, user_msg, log_msg) != 0) + *smtp_yield_ptr = FALSE; /* No more messages after dropped connection */ *smtp_reply_ptr = US""; /* Indicate reply already sent */ } message_id[0] = 0; /* Indicate no message accepted */ @@ -1681,6 +1677,7 @@ int dmarc_up = 0; uschar *timestamp; int tslen; + /* Release any open files that might have been cached while preparing to accept the message - e.g. by verifying addresses - because reading a message might take a fair bit of real time. */ @@ -1747,14 +1744,16 @@ message id creation below. */ /* For other uses of the received time we can operate with granularity of one second, and for that we use the global variable received_time. This is for -things like ultimate message timeouts.XXX */ +things like ultimate message timeouts. */ received_time = message_id_tv; /* If SMTP input, set the special handler for timeouts. The alarm() calls happen in the smtp_getc() function when it refills its buffer. */ -if (smtp_input) os_non_restarting_signal(SIGALRM, data_timeout_handler); +had_data_timeout = 0; +if (smtp_input) + os_non_restarting_signal(SIGALRM, data_timeout_handler); /* If not SMTP input, timeout happens only if configured, and we just set a single timeout for the whole message. */ @@ -1767,6 +1766,7 @@ else if (receive_timeout > 0) /* SIGTERM and SIGINT are caught always. */ +had_data_sigint = 0; signal(SIGTERM, data_sigterm_sigint_handler); signal(SIGINT, data_sigterm_sigint_handler); @@ -2046,32 +2046,30 @@ for (;;) these lines in SMTP messages. There is now an option to ignore them from specified hosts or networks. Sigh. */ - if (header_last == header_list && - (!smtp_input - || - (sender_host_address != NULL && - verify_check_host(&ignore_fromline_hosts) == OK) - || - (sender_host_address == NULL && ignore_fromline_local) - ) && - regex_match_and_setup(regex_From, next->text, 0, -1)) + if ( header_last == header_list + && ( !smtp_input + || ( sender_host_address + && verify_check_host(&ignore_fromline_hosts) == OK + ) + || (!sender_host_address && ignore_fromline_local) + ) + && regex_match_and_setup(regex_From, next->text, 0, -1) + ) { if (!sender_address_forced) { uschar *uucp_sender = expand_string(uucp_from_sender); - if (uucp_sender == NULL) - { + if (!uucp_sender) log_write(0, LOG_MAIN|LOG_PANIC, "expansion of \"%s\" failed after matching " "\"From \" line: %s", uucp_from_sender, expand_string_message); - } else { int start, end, domain; uschar *errmess; uschar *newsender = parse_extract_address(uucp_sender, &errmess, &start, &end, &domain, TRUE); - if (newsender != NULL) + if (newsender) { if (domain == 0 && newsender[0] != 0) newsender = rewrite_address_qualify(newsender, FALSE); @@ -2166,13 +2164,11 @@ for (;;) } else - { give_local_error(ERRMESS_VLONGHDRLINE, string_sprintf("message header line longer than %d characters " "received: message not accepted", header_line_maxsize), US"", error_rc, stdin, header_list->next); /* Does not return */ - } } /* Note if any resent- fields exist. */ @@ -2258,119 +2254,119 @@ for (h = header_list->next; h; h = h->next) switch (header_checkname(h, is_resent)) { case htype_bcc: - h->type = htype_bcc; /* Both Bcc: and Resent-Bcc: */ - break; + h->type = htype_bcc; /* Both Bcc: and Resent-Bcc: */ + break; case htype_cc: - h->type = htype_cc; /* Both Cc: and Resent-Cc: */ - break; + h->type = htype_cc; /* Both Cc: and Resent-Cc: */ + break; - /* Record whether a Date: or Resent-Date: header exists, as appropriate. */ + /* Record whether a Date: or Resent-Date: header exists, as appropriate. */ case htype_date: - if (!resents_exist || is_resent) date_header_exists = TRUE; - break; + if (!resents_exist || is_resent) date_header_exists = TRUE; + break; - /* Same comments as about Return-Path: below. */ + /* Same comments as about Return-Path: below. */ case htype_delivery_date: - if (delivery_date_remove) h->type = htype_old; - break; + if (delivery_date_remove) h->type = htype_old; + break; - /* Same comments as about Return-Path: below. */ + /* Same comments as about Return-Path: below. */ case htype_envelope_to: - if (envelope_to_remove) h->type = htype_old; - break; + if (envelope_to_remove) h->type = htype_old; + break; - /* Mark all "From:" headers so they get rewritten. Save the one that is to - be used for Sender: checking. For Sendmail compatibility, if the "From:" - header consists of just the login id of the user who called Exim, rewrite - it with the gecos field first. Apply this rule to Resent-From: if there - are resent- fields. */ + /* Mark all "From:" headers so they get rewritten. Save the one that is to + be used for Sender: checking. For Sendmail compatibility, if the "From:" + header consists of just the login id of the user who called Exim, rewrite + it with the gecos field first. Apply this rule to Resent-From: if there + are resent- fields. */ case htype_from: - h->type = htype_from; - if (!resents_exist || is_resent) - { - from_header = h; - if (!smtp_input) - { - int len; - uschar *s = Ustrchr(h->text, ':') + 1; - while (isspace(*s)) s++; - len = h->slen - (s - h->text) - 1; - if (Ustrlen(originator_login) == len && - strncmpic(s, originator_login, len) == 0) - { - uschar *name = is_resent? US"Resent-From" : US"From"; - header_add(htype_from, "%s: %s <%s@%s>\n", name, originator_name, - originator_login, qualify_domain_sender); - from_header = header_last; - h->type = htype_old; - DEBUG(D_receive|D_rewrite) - debug_printf("rewrote \"%s:\" header using gecos\n", name); - } - } - } - break; + h->type = htype_from; + if (!resents_exist || is_resent) + { + from_header = h; + if (!smtp_input) + { + int len; + uschar *s = Ustrchr(h->text, ':') + 1; + while (isspace(*s)) s++; + len = h->slen - (s - h->text) - 1; + if (Ustrlen(originator_login) == len && + strncmpic(s, originator_login, len) == 0) + { + uschar *name = is_resent? US"Resent-From" : US"From"; + header_add(htype_from, "%s: %s <%s@%s>\n", name, originator_name, + originator_login, qualify_domain_sender); + from_header = header_last; + h->type = htype_old; + DEBUG(D_receive|D_rewrite) + debug_printf("rewrote \"%s:\" header using gecos\n", name); + } + } + } + break; - /* Identify the Message-id: header for generating "in-reply-to" in the - autoreply transport. For incoming logging, save any resent- value. In both - cases, take just the first of any multiples. */ + /* Identify the Message-id: header for generating "in-reply-to" in the + autoreply transport. For incoming logging, save any resent- value. In both + cases, take just the first of any multiples. */ case htype_id: - if (msgid_header == NULL && (!resents_exist || is_resent)) - { - msgid_header = h; - h->type = htype_id; - } - break; + if (!msgid_header && (!resents_exist || is_resent)) + { + msgid_header = h; + h->type = htype_id; + } + break; - /* Flag all Received: headers */ + /* Flag all Received: headers */ case htype_received: - h->type = htype_received; - received_count++; - break; + h->type = htype_received; + received_count++; + break; - /* "Reply-to:" is just noted (there is no resent-reply-to field) */ + /* "Reply-to:" is just noted (there is no resent-reply-to field) */ case htype_reply_to: - h->type = htype_reply_to; - break; + h->type = htype_reply_to; + break; - /* The Return-path: header is supposed to be added to messages when - they leave the SMTP system. We shouldn't receive messages that already - contain Return-path. However, since Exim generates Return-path: on - local delivery, resent messages may well contain it. We therefore - provide an option (which defaults on) to remove any Return-path: headers - on input. Removal actually means flagging as "old", which prevents the - header being transmitted with the message. */ + /* The Return-path: header is supposed to be added to messages when + they leave the SMTP system. We shouldn't receive messages that already + contain Return-path. However, since Exim generates Return-path: on + local delivery, resent messages may well contain it. We therefore + provide an option (which defaults on) to remove any Return-path: headers + on input. Removal actually means flagging as "old", which prevents the + header being transmitted with the message. */ case htype_return_path: - if (return_path_remove) h->type = htype_old; + if (return_path_remove) h->type = htype_old; - /* If we are testing a mail filter file, use the value of the - Return-Path: header to set up the return_path variable, which is not - otherwise set. However, remove any <> that surround the address - because the variable doesn't have these. */ + /* If we are testing a mail filter file, use the value of the + Return-Path: header to set up the return_path variable, which is not + otherwise set. However, remove any <> that surround the address + because the variable doesn't have these. */ - if (filter_test != FTEST_NONE) - { - uschar *start = h->text + 12; - uschar *end = start + Ustrlen(start); - while (isspace(*start)) start++; - while (end > start && isspace(end[-1])) end--; - if (*start == '<' && end[-1] == '>') - { - start++; - end--; - } - return_path = string_copyn(start, end - start); - printf("Return-path taken from \"Return-path:\" header line\n"); - } - break; + if (filter_test != FTEST_NONE) + { + uschar *start = h->text + 12; + uschar *end = start + Ustrlen(start); + while (isspace(*start)) start++; + while (end > start && isspace(end[-1])) end--; + if (*start == '<' && end[-1] == '>') + { + start++; + end--; + } + return_path = string_copyn(start, end - start); + printf("Return-path taken from \"Return-path:\" header line\n"); + } + break; /* If there is a "Sender:" header and the message is locally originated, and from an untrusted caller and suppress_local_fixups is not set, or if we @@ -2384,31 +2380,29 @@ for (h = header_list->next; h; h = h->next) set.) */ case htype_sender: - h->type = ((!active_local_sender_retain && - ( - (sender_local && !trusted_caller && !suppress_local_fixups) - || submission_mode - ) - ) && - (!resents_exist||is_resent))? - htype_old : htype_sender; - break; + h->type = !active_local_sender_retain + && ( sender_local && !trusted_caller && !suppress_local_fixups + || submission_mode + ) + && (!resents_exist || is_resent) + ? htype_old : htype_sender; + break; - /* Remember the Subject: header for logging. There is no Resent-Subject */ + /* Remember the Subject: header for logging. There is no Resent-Subject */ case htype_subject: - subject_header = h; - break; + subject_header = h; + break; - /* "To:" gets flagged, and the existence of a recipient header is noted, - whether it's resent- or not. */ + /* "To:" gets flagged, and the existence of a recipient header is noted, + whether it's resent- or not. */ case htype_to: - h->type = htype_to; - /**** - to_or_cc_header_exists = TRUE; - ****/ - break; + h->type = htype_to; + /**** + to_or_cc_header_exists = TRUE; + ****/ + break; } } @@ -2628,7 +2622,7 @@ checked when it was read, to ensure it isn't too big. The timing granularity is left in id_resolution so that an appropriate wait can be done after receiving the message, if necessary (we hope it won't be). */ -if (host_number_string != NULL) +if (host_number_string) { id_resolution = (BASE_62 == 62)? 5000 : 10000; sprintf(CS(message_id + MESSAGE_ID_LENGTH - 3), "-%2s", @@ -2664,9 +2658,8 @@ one, but only for local (without suppress_local_fixups) or submission mode messages. This can be user-configured if required, but we had better flatten any illegal characters therein. */ -if (msgid_header == NULL && - ((sender_host_address == NULL && !suppress_local_fixups) - || submission_mode)) +if ( !msgid_header + && ((!sender_host_address && !suppress_local_fixups) || submission_mode)) { uschar *p; uschar *id_text = US""; @@ -2674,20 +2667,20 @@ if (msgid_header == NULL && /* Permit only letters, digits, dots, and hyphens in the domain */ - if (message_id_domain != NULL) + if (message_id_domain) { uschar *new_id_domain = expand_string(message_id_domain); - if (new_id_domain == NULL) + if (!new_id_domain) { if (!expand_string_forcedfail) log_write(0, LOG_MAIN|LOG_PANIC, "expansion of \"%s\" (message_id_header_domain) " "failed: %s", message_id_domain, expand_string_message); } - else if (*new_id_domain != 0) + else if (*new_id_domain) { id_domain = new_id_domain; - for (p = id_domain; *p != 0; p++) + for (p = id_domain; *p; p++) if (!isalnum(*p) && *p != '.') *p = '-'; /* No need to test '-' ! */ } } @@ -2695,21 +2688,20 @@ if (msgid_header == NULL && /* Permit all characters except controls and RFC 2822 specials in the additional text part. */ - if (message_id_text != NULL) + if (message_id_text) { uschar *new_id_text = expand_string(message_id_text); - if (new_id_text == NULL) + if (!new_id_text) { if (!expand_string_forcedfail) log_write(0, LOG_MAIN|LOG_PANIC, "expansion of \"%s\" (message_id_header_text) " "failed: %s", message_id_text, expand_string_message); } - else if (*new_id_text != 0) + else if (*new_id_text) { id_text = new_id_text; - for (p = id_text; *p != 0; p++) - if (mac_iscntrl_or_special(*p)) *p = '-'; + for (p = id_text; *p; p++) if (mac_iscntrl_or_special(*p)) *p = '-'; } } @@ -2752,9 +2744,8 @@ possible addition of a Sender: header, because untrusted_set_sender allows an untrusted user to set anything in the envelope (which might then get info From:) but we still want to ensure a valid Sender: if it is required. */ -if (from_header == NULL && - ((sender_host_address == NULL && !suppress_local_fixups) - || submission_mode)) +if ( !from_header + && ((!sender_host_address && !suppress_local_fixups) || submission_mode)) { uschar *oname = US""; @@ -2763,7 +2754,7 @@ if (from_header == NULL && force its value or if we have a non-SMTP message for which -f was not used to set the sender. */ - if (sender_host_address == NULL) + if (!sender_host_address) { if (!trusted_caller || sender_name_forced || (!smtp_input && !sender_address_forced)) @@ -2773,46 +2764,38 @@ if (from_header == NULL && /* For non-locally submitted messages, the only time we use the originator name is when it was forced by the /name= option on control=submission. */ - else - { - if (submission_name != NULL) oname = submission_name; - } + else if (submission_name) oname = submission_name; /* Envelope sender is empty */ - if (sender_address[0] == 0) + if (!*sender_address) { uschar *fromstart, *fromend; - fromstart = string_sprintf("%sFrom: %s%s", resent_prefix, - oname, (oname[0] == 0)? "" : " <"); - fromend = (oname[0] == 0)? US"" : US">"; + fromstart = string_sprintf("%sFrom: %s%s", + resent_prefix, oname, *oname ? " <" : ""); + fromend = *oname ? US">" : US""; if (sender_local || local_error_message) - { header_add(htype_from, "%s%s@%s%s\n", fromstart, local_part_quote(originator_login), qualify_domain_sender, fromend); - } - else if (submission_mode && authenticated_id != NULL) + + else if (submission_mode && authenticated_id) { - if (submission_domain == NULL) - { + if (!submission_domain) header_add(htype_from, "%s%s@%s%s\n", fromstart, local_part_quote(authenticated_id), qualify_domain_sender, fromend); - } - else if (submission_domain[0] == 0) /* empty => whole address set */ - { + + else if (!*submission_domain) /* empty => whole address set */ header_add(htype_from, "%s%s%s\n", fromstart, authenticated_id, fromend); - } + else - { header_add(htype_from, "%s%s@%s%s\n", fromstart, - local_part_quote(authenticated_id), submission_domain, - fromend); - } + local_part_quote(authenticated_id), submission_domain, fromend); + from_header = header_last; /* To get it checked for Sender: */ } } @@ -2825,10 +2808,9 @@ if (from_header == NULL && { header_add(htype_from, "%sFrom: %s%s%s%s\n", resent_prefix, oname, - (oname[0] == 0)? "" : " <", - (sender_address_unrewritten == NULL)? - sender_address : sender_address_unrewritten, - (oname[0] == 0)? "" : ">"); + *oname ? " <" : "", + sender_address_unrewritten ? sender_address_unrewritten : sender_address, + *oname ? ">" : ""); from_header = header_last; /* To get it checked for Sender: */ } @@ -2845,11 +2827,11 @@ trusted callers to forge From: without supplying -f, we have to test explicitly here. If the From: header contains more than one address, then the call to parse_extract_address fails, and a Sender: header is inserted, as required. */ -if (from_header != NULL && - (active_local_from_check && - ((sender_local && !trusted_caller && !suppress_local_fixups) || - (submission_mode && authenticated_id != NULL)) - )) +if ( from_header + && ( active_local_from_check + && ( sender_local && !trusted_caller && !suppress_local_fixups + || submission_mode && authenticated_id + ) ) ) { BOOL make_sender = TRUE; int start, end, domain; @@ -2859,37 +2841,26 @@ if (from_header != NULL && &start, &end, &domain, FALSE); uschar *generated_sender_address; - if (submission_mode) - { - if (submission_domain == NULL) - { - generated_sender_address = string_sprintf("%s@%s", - local_part_quote(authenticated_id), qualify_domain_sender); - } - else if (submission_domain[0] == 0) /* empty => full address */ - { - generated_sender_address = string_sprintf("%s", - authenticated_id); - } - else - { - generated_sender_address = string_sprintf("%s@%s", - local_part_quote(authenticated_id), submission_domain); - } - } - else - generated_sender_address = string_sprintf("%s@%s", - local_part_quote(originator_login), qualify_domain_sender); + generated_sender_address = submission_mode + ? !submission_domain + ? string_sprintf("%s@%s", + local_part_quote(authenticated_id), qualify_domain_sender) + : !*submission_domain /* empty => full address */ + ? string_sprintf("%s", authenticated_id) + : string_sprintf("%s@%s", + local_part_quote(authenticated_id), submission_domain) + : string_sprintf("%s@%s", + local_part_quote(originator_login), qualify_domain_sender); /* Remove permitted prefixes and suffixes from the local part of the From: address before doing the comparison with the generated sender. */ - if (from_address != NULL) + if (from_address) { int slen; - uschar *at = (domain == 0)? NULL : from_address + domain - 1; + uschar *at = domain ? from_address + domain - 1 : NULL; - if (at != NULL) *at = 0; + if (at) *at = 0; from_address += route_check_prefix(from_address, local_from_prefix); slen = route_check_suffix(from_address, local_from_suffix); if (slen > 0) @@ -2897,10 +2868,10 @@ if (from_header != NULL && memmove(from_address+slen, from_address, Ustrlen(from_address)-slen); from_address += slen; } - if (at != NULL) *at = '@'; + if (at) *at = '@'; - if (strcmpic(generated_sender_address, from_address) == 0 || - (domain == 0 && strcmpic(from_address, originator_login) == 0)) + if ( strcmpic(generated_sender_address, from_address) == 0 + || (!domain && strcmpic(from_address, originator_login) == 0)) make_sender = FALSE; } @@ -2908,8 +2879,7 @@ if (from_header != NULL && appropriate rewriting rules. */ if (make_sender) - { - if (submission_mode && submission_name == NULL) + if (submission_mode && !submission_name) header_add(htype_sender, "%sSender: %s\n", resent_prefix, generated_sender_address); else @@ -2917,14 +2887,13 @@ if (from_header != NULL && resent_prefix, submission_mode? submission_name : originator_name, generated_sender_address); - } /* Ensure that a non-null envelope sender address corresponds to the submission mode sender address. */ - if (submission_mode && sender_address[0] != 0) + if (submission_mode && *sender_address) { - if (sender_address_unrewritten == NULL) + if (!sender_address_unrewritten) sender_address_unrewritten = sender_address; sender_address = generated_sender_address; if (Ustrcmp(sender_address_unrewritten, generated_sender_address) != 0) @@ -2937,8 +2906,7 @@ if (from_header != NULL && /* If there are any rewriting rules, apply them to the sender address, unless it has already been rewritten as part of verification for SMTP input. */ -if (global_rewrite_rules != NULL && sender_address_unrewritten == NULL && - sender_address[0] != 0) +if (global_rewrite_rules && !sender_address_unrewritten && *sender_address) { sender_address = rewrite_address(sender_address, FALSE, TRUE, global_rewrite_rules, rewrite_existflags); @@ -2987,9 +2955,8 @@ to be more confusing if Exim adds one to all remotely-originated messages. As per Message-Id, we prepend if resending, else append. */ -if (!date_header_exists && - ((sender_host_address == NULL && !suppress_local_fixups) - || submission_mode)) +if ( !date_header_exists + && ((!sender_host_address && !suppress_local_fixups) || submission_mode)) header_add_at_position(!resents_exist, NULL, FALSE, htype_other, "%sDate: %s\n", resent_prefix, tod_stamp(tod_full)); @@ -3001,7 +2968,7 @@ new Received:) has not yet been set. */ DEBUG(D_receive) { debug_printf(">>Headers after rewriting and local additions:\n"); - for (h = header_list->next; h != NULL; h = h->next) + for (h = header_list->next; h; h = h->next) debug_printf("%c %s", h->type, h->text); debug_printf("\n"); } @@ -3103,7 +3070,7 @@ format); write it (remembering that it might contain binary zeros). The result of fwrite() isn't inspected; instead we call ferror() below. */ fprintf(data_file, "%s-D\n", message_id); -if (next != NULL) +if (next) { uschar *s = next->text; int len = next->slen; @@ -3158,10 +3125,10 @@ if (!ferror(data_file) && !(receive_feof)() && message_ended != END_DOT) log_write(L_size_reject, LOG_MAIN|LOG_REJECT, "rejected from <%s>%s%s%s%s: " "message too big: read=%d max=%d", sender_address, - (sender_fullhost == NULL)? "" : " H=", - (sender_fullhost == NULL)? US"" : sender_fullhost, - (sender_ident == NULL)? "" : " U=", - (sender_ident == NULL)? US"" : sender_ident, + sender_fullhost ? " H=" : "", + sender_fullhost ? sender_fullhost : US"", + sender_ident ? " U=" : "", + sender_ident ? sender_ident : US"", message_size, thismessage_size_limit); @@ -3214,7 +3181,7 @@ if (fflush(data_file) == EOF || ferror(data_file) || uschar *msg = string_sprintf("%s error (%s) while receiving message from %s", input_error? "Input read" : "Spool write", msg_errno, - (sender_fullhost != NULL)? sender_fullhost : sender_ident); + sender_fullhost ? sender_fullhost : sender_ident); log_write(0, LOG_MAIN, "Message abandoned: %s", msg); Uunlink(spool_name); /* Lose the data file */ @@ -3246,6 +3213,7 @@ if (fflush(data_file) == EOF || ferror(data_file) || /* No I/O errors were encountered while writing the data file. */ DEBUG(D_receive) debug_printf("Data file written for message %s\n", message_id); +if (LOGGING(receive_time)) timesince(&received_time_taken, &received_time); /* If there were any bad addresses extracted by -t, or there were no recipients @@ -3259,12 +3227,12 @@ recipients or stderr error writing, throw the data file away afterwards, and exit. (This can't be SMTP, which always ensures there's at least one syntactically good recipient address.) */ -if (extract_recip && (bad_addresses != NULL || recipients_count == 0)) +if (extract_recip && (bad_addresses || recipients_count == 0)) { DEBUG(D_receive) { if (recipients_count == 0) debug_printf("*** No recipients\n"); - if (bad_addresses != NULL) + if (bad_addresses) { error_block *eblock = bad_addresses; debug_printf("*** Bad address(es)\n"); @@ -3295,7 +3263,7 @@ if (extract_recip && (bad_addresses != NULL || recipients_count == 0)) } else { - if (bad_addresses == NULL) + if (!bad_addresses) { if (extracted_ignored) fprintf(stderr, "exim: all -t recipients overridden by command line\n"); @@ -3340,7 +3308,7 @@ Note: the checking for too many Received: headers is handled by the delivery code. */ /*XXX eventually add excess Received: check for cutthrough case back when classifying them */ -if (received_header->text == NULL) /* Non-cutthrough case */ +if (!received_header->text) /* Non-cutthrough case */ { received_header_gen(); @@ -3477,9 +3445,10 @@ else #endif /* DISABLE_DKIM */ #ifdef WITH_CONTENT_SCAN - if (recipients_count > 0 && - acl_smtp_mime != NULL && - !run_mime_acl(acl_smtp_mime, &smtp_yield, &smtp_reply, &blackholed_by)) + if ( recipients_count > 0 + && acl_smtp_mime + && !run_mime_acl(acl_smtp_mime, &smtp_yield, &smtp_reply, &blackholed_by) + ) goto TIDYUP; #endif /* WITH_CONTENT_SCAN */ @@ -3599,9 +3568,10 @@ else { #ifdef WITH_CONTENT_SCAN - if (acl_not_smtp_mime != NULL && - !run_mime_acl(acl_not_smtp_mime, &smtp_yield, &smtp_reply, - &blackholed_by)) + if ( acl_not_smtp_mime + && !run_mime_acl(acl_not_smtp_mime, &smtp_yield, &smtp_reply, + &blackholed_by) + ) goto TIDYUP; #endif /* WITH_CONTENT_SCAN */ @@ -3666,47 +3636,70 @@ dcc_ok = 0; #endif +#ifdef HAVE_LOCAL_SCAN /* The final check on the message is to run the scan_local() function. The version supplied with Exim always accepts, but this is a hook for sysadmins to supply their own checking code. The local_scan() function is run even when all the recipients have been discarded. */ -/*XXS could we avoid this for the standard case, given that few people will use it? */ lseek(data_fd, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); /* Arrange to catch crashes in local_scan(), so that the -D file gets deleted, and the incident gets logged. */ -os_non_restarting_signal(SIGSEGV, local_scan_crash_handler); -os_non_restarting_signal(SIGFPE, local_scan_crash_handler); -os_non_restarting_signal(SIGILL, local_scan_crash_handler); -os_non_restarting_signal(SIGBUS, local_scan_crash_handler); - -DEBUG(D_receive) debug_printf("calling local_scan(); timeout=%d\n", - local_scan_timeout); -local_scan_data = NULL; - -os_non_restarting_signal(SIGALRM, local_scan_timeout_handler); -if (local_scan_timeout > 0) alarm(local_scan_timeout); -rc = local_scan(data_fd, &local_scan_data); -alarm(0); -os_non_restarting_signal(SIGALRM, sigalrm_handler); - -enable_dollar_recipients = FALSE; - -store_pool = POOL_MAIN; /* In case changed */ -DEBUG(D_receive) debug_printf("local_scan() returned %d %s\n", rc, - local_scan_data); - -os_non_restarting_signal(SIGSEGV, SIG_DFL); -os_non_restarting_signal(SIGFPE, SIG_DFL); -os_non_restarting_signal(SIGILL, SIG_DFL); -os_non_restarting_signal(SIGBUS, SIG_DFL); +if (sigsetjmp(local_scan_env, 1) == 0) + { + had_local_scan_crash = 0; + os_non_restarting_signal(SIGSEGV, local_scan_crash_handler); + os_non_restarting_signal(SIGFPE, local_scan_crash_handler); + os_non_restarting_signal(SIGILL, local_scan_crash_handler); + os_non_restarting_signal(SIGBUS, local_scan_crash_handler); + + DEBUG(D_receive) debug_printf("calling local_scan(); timeout=%d\n", + local_scan_timeout); + local_scan_data = NULL; + + had_local_scan_timeout = 0; + os_non_restarting_signal(SIGALRM, local_scan_timeout_handler); + if (local_scan_timeout > 0) alarm(local_scan_timeout); + rc = local_scan(data_fd, &local_scan_data); + alarm(0); + os_non_restarting_signal(SIGALRM, sigalrm_handler); + + enable_dollar_recipients = FALSE; + + store_pool = POOL_MAIN; /* In case changed */ + DEBUG(D_receive) debug_printf("local_scan() returned %d %s\n", rc, + local_scan_data); + + os_non_restarting_signal(SIGSEGV, SIG_DFL); + os_non_restarting_signal(SIGFPE, SIG_DFL); + os_non_restarting_signal(SIGILL, SIG_DFL); + os_non_restarting_signal(SIGBUS, SIG_DFL); + } +else + { + if (had_local_scan_crash) + { + log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() function crashed with " + "signal %d - message temporarily rejected (size %d)", + had_local_scan_crash, message_size); + /* Does not return */ + receive_bomb_out(US"local-scan-error", US"local verification problem"); + } + if (had_local_scan_timeout) + { + log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() function timed out - " + "message temporarily rejected (size %d)", message_size); + /* Does not return */ + receive_bomb_out(US"local-scan-timeout", US"local verification problem"); + } + } /* The length check is paranoia against some runaway code, and also because (for a success return) lines in the spool file are read into big_buffer. */ -if (local_scan_data != NULL) +if (local_scan_data) { int len = Ustrlen(local_scan_data); if (len > LOCAL_SCAN_MAX_RETURN) len = LOCAL_SCAN_MAX_RETURN; @@ -3738,7 +3731,7 @@ the spool file gets corrupted. Ensure that all recipients are qualified. */ if (rc == LOCAL_SCAN_ACCEPT) { - if (local_scan_data != NULL) + if (local_scan_data) { uschar *s; for (s = local_scan_data; *s != 0; s++) if (*s == '\n') *s = ' '; @@ -3831,6 +3824,7 @@ the message to be abandoned. */ signal(SIGTERM, SIG_IGN); signal(SIGINT, SIG_IGN); +#endif /* HAVE_LOCAL_SCAN */ /* Ensure the first time flag is set in the newly-received message. */ @@ -3991,6 +3985,18 @@ if (LOGGING(8bitmime)) g = string_append(g, 2, US" M8S=", big_buffer); } +#ifndef DISABLE_DKIM +if (LOGGING(dkim) && dkim_verify_overall) + g = string_append(g, 2, US" DKIM=", dkim_verify_overall); +# ifdef EXPERIMENTAL_ARC +if (LOGGING(dkim) && arc_state && Ustrcmp(arc_state, "pass") == 0) + g = string_catn(g, US" ARC", 4); +# endif +#endif + +if (LOGGING(receive_time)) + g = string_append(g, 2, US" RT=", string_timediff(&received_time_taken)); + if (*queue_name) g = string_append(g, 2, US" Q=", queue_name); @@ -4014,7 +4020,7 @@ if (msgid_header) /* If subject logging is turned on, create suitable printing-character text. By expanding $h_subject: we make use of the MIME decoding. */ -if (LOGGING(subject) && subject_header != NULL) +if (LOGGING(subject) && subject_header) { int i; uschar *p = big_buffer; @@ -4349,9 +4355,11 @@ starting. */ if (blackholed_by) { - const uschar *detail = local_scan_data - ? string_printing(local_scan_data) - : string_sprintf("(%s discarded recipients)", blackholed_by); + const uschar *detail = +#ifdef HAVE_LOCAL_SCAN + local_scan_data ? string_printing(local_scan_data) : +#endif + string_sprintf("(%s discarded recipients)", blackholed_by); log_write(0, LOG_MAIN, "=> blackhole %s%s", detail, blackhole_log_msg); log_write(0, LOG_MAIN, "Completed"); message_id[0] = 0;