X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/315206fbf2d75c73de73deab89443ab645d96525..51894f20e0c03113b4c4e07898ac5e955b21ec41:/src/src/receive.c diff --git a/src/src/receive.c b/src/src/receive.c index 0a27c7950..8190c5941 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -2,9 +2,10 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) The Exim Maintainers 2020 - 2022 */ +/* Copyright (c) The Exim Maintainers 2020 - 2023 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Code for receiving a message and setting up spool files. */ @@ -513,7 +514,7 @@ Returns: nothing */ void -receive_add_recipient(uschar *recipient, int pno) +receive_add_recipient(uschar * recipient, int pno) { if (recipients_count >= recipients_list_max) { @@ -569,7 +570,7 @@ smtp_user_msg(uschar *code, uschar *user_msg) { int len = 3; smtp_message_code(&code, &len, &user_msg, NULL, TRUE); -smtp_respond(code, len, TRUE, user_msg); +smtp_respond(code, len, SR_FINAL, user_msg); } #endif @@ -1155,7 +1156,7 @@ Returns: the SMTP response */ static uschar * -handle_lost_connection(uschar *s) +handle_lost_connection(uschar * s) { log_write(L_lost_incoming_connection | L_smtp_connection, LOG_MAIN, "%s lost while reading message data%s", smtp_get_connection_info(), s); @@ -1229,9 +1230,9 @@ Returns: nothing */ static void -add_acl_headers(int where, uschar *acl_name) +add_acl_headers(int where, uschar * acl_name) { -header_line *last_received = NULL; +header_line * last_received = NULL; switch(where) { @@ -1253,15 +1254,22 @@ if (acl_removed_headers) for (header_line * h = header_list; h; h = h->next) if (h->type != htype_old) { - const uschar * list = acl_removed_headers; + const uschar * list = acl_removed_headers, * s; int sep = ':'; /* This is specified as a colon-separated list */ - uschar *s; + /* If a list element has a leading '^' then it is an RE for + the whole header, else just a header name. */ while ((s = string_nextinlist(&list, &sep, NULL, 0))) - if (header_testname(h, s, Ustrlen(s), FALSE)) + if ( ( *s == '^' + && regex_match( + regex_must_compile(s, MCS_CACHEABLE, FALSE), + h->text, h->slen, NULL) + ) + || header_testname(h, s, Ustrlen(s), FALSE) + ) { h->type = htype_old; - DEBUG(D_receive|D_acl) debug_printf_indent(" %s", h->text); + DEBUG(D_receive|D_acl) debug_printf_indent(" %s", h->text); } } acl_removed_headers = NULL; @@ -1371,6 +1379,8 @@ if (f.tcp_in_fastopen && !f.tcp_in_fastopen_logged) } if (sender_ident) g = string_append(g, 2, US" U=", sender_ident); +if (LOGGING(connection_id)) + g = string_fmt_append(g, " Ci=%lu", connection_id); if (received_protocol) g = string_append(g, 2, US" P=", received_protocol); if (LOGGING(pipelining) && f.smtp_in_pipelining_advertised) @@ -1447,7 +1457,7 @@ if (!(mbox_file = spool_mbox(&mbox_size, NULL, &mbox_filename))) #ifdef EXPERIMENTAL_DCC dcc_ok = 0; #endif - smtp_respond(US"451", 3, TRUE, US"temporary local problem"); + smtp_respond(US"451", 3, SR_FINAL, US"temporary local problem"); message_id[0] = 0; /* Indicate no message accepted */ *smtp_reply_ptr = US""; /* Indicate reply already sent */ return FALSE; /* Indicate skip to end of receive function */ @@ -1685,7 +1695,9 @@ int error_rc = error_handling == ERRORS_SENDER int header_size = 256; int had_zero = 0; int prevlines_length = 0; -const int id_resolution = BASE_62 == 62 ? 5000 : 10000; +const int id_resolution = BASE_62 == 62 && !host_number_string ? 1 + : BASE_62 != 62 && host_number_string ? 4 + : 2; int ptr = 0; @@ -2297,7 +2309,7 @@ OVERSIZE: sender_address, sender_fullhost ? " H=" : "", sender_fullhost ? sender_fullhost : US"", sender_ident ? " U=" : "", sender_ident ? sender_ident : US""); - smtp_printf("552 Message header not CRLF terminated\r\n", FALSE); + smtp_printf("552 Message header not CRLF terminated\r\n", SP_NO_MORE); bdat_flush_data(); smtp_reply = US""; goto TIDYUP; /* Skip to end of function */ @@ -2657,7 +2669,7 @@ if (extract_recip) that this has happened, in order to give a better error if there are no recipients left. */ - else if (recipient != NULL) + else if (recipient) { if (tree_search(tree_nonrecipients, recipient) == NULL) receive_add_recipient(recipient, -1); @@ -2667,7 +2679,7 @@ if (extract_recip) /* Move on past this address */ - s = ss + (*ss? 1:0); + s = ss + (*ss ? 1 : 0); while (isspace(*s)) s++; } /* Next address */ @@ -2685,41 +2697,37 @@ if (extract_recip) } /* Now build the unique message id. This has changed several times over the -lifetime of Exim. This description was rewritten for Exim 4.14 (February 2003). -Retaining all the history in the comment has become too unwieldy - read -previous release sources if you want it. - -The message ID has 3 parts: tttttt-pppppp-ss. Each part is a number in base 62. -The first part is the current time, in seconds. The second part is the current -pid. Both are large enough to hold 32-bit numbers in base 62. The third part -can hold a number in the range 0-3843. It used to be a computed sequence -number, but is now the fractional component of the current time in units of -1/2000 of a second (i.e. a value in the range 0-1999). After a message has been -received, Exim ensures that the timer has ticked at the appropriate level -before proceeding, to avoid duplication if the pid happened to be re-used -within the same time period. It seems likely that most messages will take at -least half a millisecond to be received, so no delay will normally be -necessary. At least for some time... +lifetime of Exim, and is changing for Exim 4.97. +The previous change was in about 2003. + +Detail for the pre-4.97 version is here in [square-brackets]. + +The message ID has 3 parts: tttttt-ppppppppppp-ssss (6, 11, 4 - total 23 with +the dashes). Each part is a number in base 62. +[ tttttt-pppppp-ss 6, 6, 2 => 16 ] -There is a modification when localhost_number is set. Formerly this was allowed -to be as large as 255. Now it is restricted to the range 0-16, and the final -component of the message id becomes (localhost_number * 200) + fractional time -in units of 1/200 of a second (i.e. a value in the range 0-3399). +The first part is the current time, in seconds. Six chars is enough until +year 3700 with case-sensitive filesystes, but will run out in 2038 on +case-insensitive ones (Cygwin, Darwin - where we have to use base-36. +Both of those are in the "unsupported" bucket, so ignore for now). -Some not-really-Unix operating systems use case-insensitive file names (Darwin, -Cygwin). For these, we have to use base 36 instead of base 62. Luckily, this -still allows the tttttt field to hold a large enough number to last for some -more decades, and the final two-digit field can hold numbers up to 1295, which -is enough for milliseconds (instead of 1/2000 of a second). +The second part is the current pid, and supports 64b [31b] PIDs. -However, the pppppp field cannot hold a 32-bit pid, but it can hold a 31-bit -pid, so it is probably safe because pids have to be positive. The -localhost_number is restricted to 0-10 for these hosts, and when it is set, the -final field becomes (localhost_number * 100) + fractional time in centiseconds. +The third part holds sub-second time, plus (when localhost_number is set) +the host number multiplied by a number large enough to keep it away from +the time portion. Host numbers are restricted to the range 0-16. +The time resolution is variously 1, 2 or 4 microseconds [0.5 or 1 ms] +depending on the use of localhost_nubmer and of case-insensitive filesystems. + +After a message has been received, Exim ensures that the timer has ticked at the +appropriate level before proceeding, to avoid duplication if the pid happened to +be re-used within the same time period. It seems likely that most messages will +take at least half a millisecond to be received, so no delay will normally be +necessary. At least for some time... -Note that string_base62() returns its data in a static storage block, so it -must be copied before calling string_base62() again. It always returns exactly -6 characters. +Note that string_base62_XX() returns its data in a static storage block, so it +must be copied before calling string_base62_XXX) again. It always returns exactly +11 (_64) or 6 (_32) characters. There doesn't seem to be anything in the RFC which requires a message id to start with a letter, but Smail was changed to ensure this. The external form of @@ -2732,27 +2740,35 @@ checking that a string is in this format must be updated in a corresponding way. It appears in the initializing code in exim.c. The macro MESSAGE_ID_LENGTH must also be changed to reflect the correct string length. The queue-sort code needs to know the layout. Then, of course, other programs that rely on the -message id format will need updating too. */ +message id format will need updating too (inc. at least exim_msgdate). */ -Ustrncpy(message_id, string_base62((long int)(message_id_tv.tv_sec)), 6); -message_id[6] = '-'; -Ustrncpy(message_id + 7, string_base62((long int)getpid()), 6); +Ustrncpy(message_id, string_base62_32((long int)(message_id_tv.tv_sec)), MESSAGE_ID_TIME_LEN); +message_id[MESSAGE_ID_TIME_LEN] = '-'; +Ustrncpy(message_id + MESSAGE_ID_TIME_LEN + 1, + string_base62_64((long int)getpid()), + MESSAGE_ID_PID_LEN + ); /* Deal with the case where the host number is set. The value of the number was checked when it was read, to ensure it isn't too big. */ if (host_number_string) - sprintf(CS(message_id + MESSAGE_ID_LENGTH - 3), "-%2s", - string_base62((long int)( - host_number * (1000000/id_resolution) + - message_id_tv.tv_usec/id_resolution)) + 4); + sprintf(CS(message_id + MESSAGE_ID_TIME_LEN + 1 + MESSAGE_ID_PID_LEN), + "-%" str(MESSAGE_ID_SUBTIME_LEN) "s", + string_base62_32((long int)( + host_number * (1000000/id_resolution) + + message_id_tv.tv_usec/id_resolution)) + + (6 - MESSAGE_ID_SUBTIME_LEN) + ); /* Host number not set: final field is just the fractional time at an appropriate resolution. */ else - sprintf(CS(message_id + MESSAGE_ID_LENGTH - 3), "-%2s", - string_base62((long int)(message_id_tv.tv_usec/id_resolution)) + 4); + sprintf(CS(message_id + MESSAGE_ID_TIME_LEN + 1 + MESSAGE_ID_PID_LEN), + "-%" str(MESSAGE_ID_SUBTIME_LEN) "s", + string_base62_32((long int)(message_id_tv.tv_usec/id_resolution)) + + (6 - MESSAGE_ID_SUBTIME_LEN)); /* Add the current message id onto the current process info string if it will fit. */ @@ -3135,9 +3151,8 @@ if (cutthrough.cctx.sock >= 0 && cutthrough.delivery) sender_address, sender_fullhost ? "H=" : "", sender_fullhost ? sender_fullhost : US"", sender_ident ? "U=" : "", sender_ident ? sender_ident : US""); - message_id[0] = 0; /* Indicate no message accepted */ smtp_reply = US"550 Too many \"Received\" headers - suspected mail loop"; - goto TIDYUP; /* Skip to end of function */ + goto NOT_ACCEPTED; /* Skip to end of function */ } received_header_gen(); add_acl_headers(ACL_WHERE_RCPT, US"MAIL or RCPT"); @@ -3184,7 +3199,7 @@ spool_data_file = fdopen(data_fd, "w+"); lock_data.l_type = F_WRLCK; lock_data.l_whence = SEEK_SET; lock_data.l_start = 0; -lock_data.l_len = SPOOL_DATA_START_OFFSET; +lock_data.l_len = spool_data_start_offset(message_id); if (fcntl(data_fd, F_SETLK, &lock_data) < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Cannot lock %s (%d): %s", spool_name, @@ -3234,12 +3249,11 @@ if (!ferror(spool_data_file) && !(receive_feof)() && message_ended != END_DOT) case END_EOF: if (smtp_input) { - Uunlink(spool_name); /* Lose data file when closed */ + Uunlink(spool_name); /* Lose data file when closed */ cancel_cutthrough_connection(TRUE, US"sender closed connection"); - message_id[0] = 0; /* Indicate no message accepted */ smtp_reply = handle_lost_connection(US""); smtp_yield = FALSE; - goto TIDYUP; /* Skip to end of function */ + goto NOT_ACCEPTED; /* Skip to end of function */ } break; @@ -3264,12 +3278,11 @@ if (!ferror(spool_data_file) && !(receive_feof)() && message_ended != END_DOT) if (smtp_input) { smtp_reply = US"552 Message size exceeds maximum permitted"; - message_id[0] = 0; /* Indicate no message accepted */ - goto TIDYUP; /* Skip to end of function */ + goto NOT_ACCEPTED; /* Skip to end of function */ } else { - fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); + fseek(spool_data_file, (long int)spool_data_start_offset(message_id), SEEK_SET); give_local_error(ERRMESS_TOOBIG, string_sprintf("message too big (max=%d)", thismessage_size_limit), US"message rejected: ", error_rc, spool_data_file, header_list); @@ -3283,8 +3296,7 @@ if (!ferror(spool_data_file) && !(receive_feof)() && message_ended != END_DOT) Uunlink(spool_name); /* Lose the data file when closed */ cancel_cutthrough_connection(TRUE, US"sender protocol error"); smtp_reply = US""; /* Response already sent */ - message_id[0] = 0; /* Indicate no message accepted */ - goto TIDYUP; /* Skip to end of function */ + goto NOT_ACCEPTED; /* Skip to end of function */ } } @@ -3325,13 +3337,12 @@ if (fflush(spool_data_file) == EOF || ferror(spool_data_file) || smtp_reply = US"451 Error while writing spool file"; receive_swallow_smtp(); } - message_id[0] = 0; /* Indicate no message accepted */ - goto TIDYUP; /* Skip to end of function */ + goto NOT_ACCEPTED; /* Skip to end of function */ } else { - fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); + fseek(spool_data_file, (long int)spool_data_start_offset(message_id), SEEK_SET); give_local_error(ERRMESS_IOERR, msg, US"", error_rc, spool_data_file, header_list); /* Does not return */ @@ -3372,7 +3383,7 @@ if (extract_recip && (bad_addresses || recipients_count == 0)) log_write(0, LOG_MAIN|LOG_PANIC, "%s %s found in headers", message_id, bad_addresses ? "bad addresses" : "no recipients"); - fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); + fseek(spool_data_file, (long int)spool_data_start_offset(message_id), SEEK_SET); /* If configured to send errors to the sender, but this fails, force a failure error code. We use a special one for no recipients so that it @@ -3439,7 +3450,7 @@ if (!received_header->text) /* Non-cutthrough case */ /* Set the value of message_body_size for the DATA ACL and for local_scan() */ message_body_size = (fstat(data_fd, &statbuf) == 0)? - statbuf.st_size - SPOOL_DATA_START_OFFSET : -1; + statbuf.st_size - spool_data_start_offset(message_id) : -1; /* If an ACL from any RCPT commands set up any warning headers to add, do so now, before running the DATA ACL. */ @@ -3448,7 +3459,7 @@ if (!received_header->text) /* Non-cutthrough case */ } else message_body_size = (fstat(data_fd, &statbuf) == 0)? - statbuf.st_size - SPOOL_DATA_START_OFFSET : -1; + statbuf.st_size - spool_data_start_offset(message_id) : -1; /* If an ACL is specified for checking things at this stage of reception of a message, run it, unless all the recipients were removed by "discard" in earlier @@ -3559,8 +3570,7 @@ else if (smtp_handle_acl_fail(ACL_WHERE_DKIM, rc, user_msg, log_msg) != 0) smtp_yield = FALSE; /* No more messages after dropped connection */ smtp_reply = US""; /* Indicate reply already sent */ - message_id[0] = 0; /* Indicate no message accepted */ - goto TIDYUP; /* Skip to end of function */ + goto NOT_ACCEPTED; /* Skip to end of function */ } } else @@ -3586,7 +3596,7 @@ else int all_pass = OK; int all_fail = FAIL; - smtp_printf("353 PRDR content analysis beginning\r\n", TRUE); + smtp_printf("353 PRDR content analysis beginning\r\n", SP_MORE); /* Loop through recipients, responses must be in same order received */ for (unsigned int c = 0; recipients_count > c; c++) { @@ -3641,10 +3651,7 @@ else ? US"accepted" : US"accepted for some recipients"); if (recipients_count == 0) - { - message_id[0] = 0; /* Indicate no message accepted */ - goto TIDYUP; - } + goto NOT_ACCEPTED; } else prdr_requested = FALSE; @@ -3653,7 +3660,7 @@ else /* Check the recipients count again, as the MIME ACL might have changed them. */ - if (acl_smtp_data != NULL && recipients_count > 0) + if (acl_smtp_data && recipients_count > 0) { rc = acl_check(ACL_WHERE_DATA, NULL, acl_smtp_data, &user_msg, &log_msg); add_acl_headers(ACL_WHERE_DATA, US"DATA"); @@ -3678,8 +3685,7 @@ else if (smtp_handle_acl_fail(ACL_WHERE_DATA, rc, user_msg, log_msg) != 0) smtp_yield = FALSE; /* No more messages after dropped connection */ smtp_reply = US""; /* Indicate reply already sent */ - message_id[0] = 0; /* Indicate no message accepted */ - goto TIDYUP; /* Skip to end of function */ + goto NOT_ACCEPTED; /* Skip to end of function */ } } } @@ -3732,7 +3738,7 @@ else /* Does not return */ else { - fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); + fseek(spool_data_file, (long int)spool_data_start_offset(message_id), SEEK_SET); give_local_error(ERRMESS_LOCAL_ACL, user_msg, US"message rejected by non-SMTP ACL: ", error_rc, spool_data_file, header_list); @@ -3764,7 +3770,7 @@ 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. */ -lseek(data_fd, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); +lseek(data_fd, (long int)spool_data_start_offset(message_id), SEEK_SET); /* Arrange to catch crashes in local_scan(), so that the -D file gets deleted, and the incident gets logged. */ @@ -3854,10 +3860,10 @@ the spool file gets corrupted. Ensure that all recipients are qualified. */ if (rc == LOCAL_SCAN_ACCEPT) { if (local_scan_data) - for (uschar * s = local_scan_data; *s != 0; s++) if (*s == '\n') *s = ' '; - for (int i = 0; i < recipients_count; i++) + for (uschar * s = local_scan_data; *s; s++) if (*s == '\n') *s = ' '; + for (recipient_item * r = recipients_list; + r < recipients_list + recipients_count; r++) { - recipient_item *r = recipients_list + i; r->address = rewrite_address_qualify(r->address, TRUE); if (r->errors_to) r->errors_to = rewrite_address_qualify(r->errors_to, TRUE); @@ -3906,27 +3912,25 @@ else break; } - g = string_append(NULL, 2, US"F=", - sender_address[0] == 0 ? US"<>" : sender_address); + g = string_append(NULL, 2, US"F=", *sender_address ? sender_address : US"<>"); g = add_host_info_for_log(g); - log_write(0, LOG_MAIN|LOG_REJECT, "%s %srejected by local_scan(): %.256s", - string_from_gstring(g), istemp, string_printing(errmsg)); + log_write(0, LOG_MAIN|LOG_REJECT, "%Y %srejected by local_scan(): %.256s", + g, istemp, string_printing(errmsg)); if (smtp_input) if (!smtp_batched_input) { - smtp_respond(smtp_code, 3, TRUE, errmsg); - message_id[0] = 0; /* Indicate no message accepted */ + smtp_respond(smtp_code, 3, SR_FINAL, errmsg); smtp_reply = US""; /* Indicate reply already sent */ - goto TIDYUP; /* Skip to end of function */ + goto NOT_ACCEPTED; /* Skip to end of function */ } else moan_smtp_batch(NULL, "%s %s", smtp_code, errmsg); /* Does not return */ else { - fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); + fseek(spool_data_file, (long int)spool_data_start_offset(message_id), SEEK_SET); give_local_error(ERRMESS_LOCAL_SCAN, errmsg, US"message rejected by local scan code: ", error_rc, spool_data_file, header_list); @@ -3941,6 +3945,19 @@ signal(SIGTERM, SIG_IGN); signal(SIGINT, SIG_IGN); #endif /* HAVE_LOCAL_SCAN */ +/* If we are faking a reject or defer, avoid sennding a DSN for the +actually-accepted message */ + +if (fake_response != OK) + for (recipient_item * r = recipients_list; + r < recipients_list + recipients_count; r++) + { + DEBUG(D_receive) if (r->dsn_flags & (rf_notify_success | rf_notify_delay)) + debug_printf("DSN: clearing flags due to fake-response for message\n"); + r->dsn_flags = r->dsn_flags & ~(rf_notify_success | rf_notify_delay) + | rf_notify_never; + } + /* Ensure the first time flag is set in the newly-received message. */ @@ -3949,7 +3966,7 @@ f.deliver_firsttime = TRUE; #ifdef EXPERIMENTAL_BRIGHTMAIL if (bmi_run == 1) { /* rewind data file */ - lseek(data_fd, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); + lseek(data_fd, (long int)spool_data_start_offset(message_id), SEEK_SET); bmi_verdicts = bmi_process_message(header_list, data_fd); } #endif @@ -3996,12 +4013,11 @@ else if (smtp_input) { smtp_reply = US"451 Error in writing spool file"; - message_id[0] = 0; /* Indicate no message accepted */ - goto TIDYUP; + goto NOT_ACCEPTED; } else { - fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); + fseek(spool_data_file, (long int)spool_data_start_offset(message_id), SEEK_SET); give_local_error(ERRMESS_IOERR, errmsg, US"", error_rc, spool_data_file, header_list); /* Does not return */ @@ -4013,11 +4029,6 @@ else receive_messagecount++; -/* Add data size to written header size. We do not count the initial file name -that is in the file, but we do add one extra for the notional blank line that -precedes the data. This total differs from message_size in that it include the -added Received: header and any other headers that got created locally. */ - if (fflush(spool_data_file)) { errmsg = string_sprintf("Spool write error: %s", strerror(errno)); @@ -4027,20 +4038,24 @@ if (fflush(spool_data_file)) if (smtp_input) { smtp_reply = US"451 Error in writing spool file"; - message_id[0] = 0; /* Indicate no message accepted */ - goto TIDYUP; + goto NOT_ACCEPTED; } else { - fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); + fseek(spool_data_file, (long int)spool_data_start_offset(message_id), SEEK_SET); give_local_error(ERRMESS_IOERR, errmsg, US"", error_rc, spool_data_file, header_list); /* Does not return */ } } -fstat(data_fd, &statbuf); -msg_size += statbuf.st_size - SPOOL_DATA_START_OFFSET + 1; +/* Add data size to written header size. We do not count the initial file name +that is in the file, but we do add one extra for the notional blank line that +precedes the data. This total differs from message_size in that it include the +added Received: header and any other headers that got created locally. */ + +fstat(data_fd, &statbuf); +msg_size += statbuf.st_size - spool_data_start_offset(message_id) + 1; /* Generate a "message received" log entry. We do this by building up a dynamic string as required. We log the arrival of a new message while the @@ -4054,7 +4069,7 @@ g = string_get(256); g = string_append(g, 2, fake_response == FAIL ? US"(= " : US"<= ", - sender_address[0] == 0 ? US"<>" : sender_address); + *sender_address ? sender_address : US"<>"); if (message_reference) g = string_append(g, 2, US" R=", message_reference); @@ -4213,7 +4228,8 @@ if (message_logs && !blackholed_by) } else { - uschar *now = tod_stamp(tod_log); + uschar * now = tod_stamp(tod_log); + /* Drop the initial "<= " */ fprintf(message_log, "%s Received from %s\n", now, g->s+3); if (f.deliver_freeze) fprintf(message_log, "%s frozen by %s\n", now, frozen_by); @@ -4265,10 +4281,10 @@ if ( smtp_input && sender_host_address && !f.sender_host_notsocket /* Re-use the log line workspace */ - g->ptr = 0; + gstring_reset(g); g = string_cat(g, US"SMTP connection lost after final dot"); g = add_host_info_for_log(g); - log_write(0, LOG_MAIN, "%s", string_from_gstring(g)); + log_write(0, LOG_MAIN, "%Y", g); /* Delete the files for this aborted message. */ @@ -4332,9 +4348,9 @@ if(!smtp_reply) #endif { log_write(0, LOG_MAIN | - (LOGGING(received_recipients) ? LOG_RECIPIENTS : 0) | - (LOGGING(received_sender) ? LOG_SENDER : 0), - "%s", g->s); + (LOGGING(received_recipients) ? LOG_RECIPIENTS : 0) | + (LOGGING(received_sender) ? LOG_SENDER : 0), + "%Y", g); /* Log any control actions taken by an ACL or local_scan(). */ @@ -4372,18 +4388,49 @@ a queue-runner could grab it in the window. A fflush() was done earlier in the expectation that any write errors on the data file will be flushed(!) out thereby. Nevertheless, it is theoretically -possible for fclose() to fail - but what to do? What has happened to the lock -if this happens? We can at least log it; if it is observed on some platform -then we can think about properly declaring the message not-received. */ +possible for fclose() to fail - and this has been seen on obscure filesystems +(probably one that delayed the actual media write as long as possible) +but what to do? What has happened to the lock if this happens? +It's a mes because we already logged the acceptance. +We can at least log the issue, try to remove spoolfiles and respond with +a temp-reject. We do not want to close before logging acceptance because +we want to hold the lock until we know that logging worked. +Could we make this less likely by doing an fdatasync() just after the fflush()? +That seems like a good thing on data-security grounds, but how much will it hit +performance? */ +goto TIDYUP; + +NOT_ACCEPTED: +message_id[0] = 0; /* Indicate no message accepted */ + TIDYUP: process_info[process_info_len] = 0; /* Remove message id */ if (spool_data_file && cutthrough_done == NOT_TRIED) { if (fclose(spool_data_file)) /* Frees the lock */ - log_write(0, LOG_MAIN|LOG_PANIC, - "spoolfile error on close: %s", strerror(errno)); + { + log_msg = string_sprintf("spoolfile error on close: %s", strerror(errno)); + log_write(0, LOG_MAIN|LOG_PANIC | + (LOGGING(received_recipients) ? LOG_RECIPIENTS : 0) | + (LOGGING(received_sender) ? LOG_SENDER : 0), + "%s", log_msg); + log_write(0, LOG_MAIN | + (LOGGING(received_recipients) ? LOG_RECIPIENTS : 0) | + (LOGGING(received_sender) ? LOG_SENDER : 0), + "rescind the above message-accept"); + + Uunlink(spool_name); + Uunlink(spool_fname(US"input", message_subdir, message_id, US"-H")); + Uunlink(spool_fname(US"msglog", message_subdir, message_id, US"")); + + /* Claim a data ACL temp-reject, just to get reject logging and resposponse */ + smtp_handle_acl_fail(ACL_WHERE_DATA, rc, NULL, log_msg); + smtp_reply = US""; /* Indicate reply already sent */ + + message_id[0] = 0; /* no message accepted */ + } spool_data_file = NULL; } @@ -4412,7 +4459,7 @@ if (smtp_input) { if (fake_response != OK) smtp_respond(fake_response == DEFER ? US"450" : US"550", - 3, TRUE, fake_response_text); + 3, SR_FINAL, fake_response_text); /* An OK response is required; use "message" text if present. */ @@ -4421,7 +4468,7 @@ if (smtp_input) uschar *code = US"250"; int len = 3; smtp_message_code(&code, &len, &user_msg, NULL, TRUE); - smtp_respond(code, len, TRUE, user_msg); + smtp_respond(code, len, SR_FINAL, user_msg); } /* Default OK response */ @@ -4449,10 +4496,10 @@ if (smtp_input) else if (smtp_reply[0] != 0) if (fake_response != OK && smtp_reply[0] == '2') - smtp_respond(fake_response == DEFER ? US"450" : US"550", 3, TRUE, - fake_response_text); + smtp_respond(fake_response == DEFER ? US"450" : US"550", + 3, SR_FINAL, fake_response_text); else - smtp_printf("%.1024s\r\n", FALSE, smtp_reply); + smtp_printf("%.1024s\r\n", SP_NO_MORE, smtp_reply); switch (cutthrough_done) {