X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=blobdiff_plain;f=src%2Fsrc%2Freceive.c;h=731c76d774a63136540c814423b1db1f874126e2;hp=3351ab1c535998b84e8c55f579e60aa575860666;hb=040721f2bc088d5e717c0475975ed6158352dce6;hpb=e983e85a314998aed1d586990969fea128a8b4c7 diff --git a/src/src/receive.c b/src/src/receive.c index 3351ab1c5..731c76d77 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -2,7 +2,7 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2016 */ +/* Copyright (c) University of Cambridge 1995 - 2017 */ /* See the file NOTICE for conditions of use and distribution. */ /* Code for receiving a message and setting up spool files. */ @@ -25,6 +25,7 @@ static FILE *data_file = NULL; static int data_fd = -1; static uschar *spool_name = US""; +enum CH_STATE {LF_SEEN, MID_LINE, CR_SEEN}; /************************************************* @@ -37,7 +38,7 @@ the file. (When SMTP input is occurring, different functions are used by changing the pointer variables.) */ int -stdin_getc(void) +stdin_getc(unsigned lim) { return getc(stdin); } @@ -83,12 +84,10 @@ receive_check_set_sender(uschar *newsender) { uschar *qnewsender; if (trusted_caller) return TRUE; -if (newsender == NULL || untrusted_set_sender == NULL) return FALSE; -qnewsender = (Ustrchr(newsender, '@') != NULL)? - newsender : string_sprintf("%s@%s", newsender, qualify_domain_sender); -return - match_address_list(qnewsender, TRUE, TRUE, CUSS &untrusted_set_sender, NULL, -1, - 0, NULL) == OK; +if (!newsender || !untrusted_set_sender) return FALSE; +qnewsender = Ustrchr(newsender, '@') + ? newsender : string_sprintf("%s@%s", newsender, qualify_domain_sender); +return match_address_list_basic(qnewsender, CUSS &untrusted_set_sender, 0) == OK; } @@ -124,6 +123,7 @@ receive_statvfs(BOOL isspool, int *inodeptr) { #ifdef HAVE_STATFS struct STATVFS statbuf; +struct stat dummy; uschar *path; uschar *name; uschar buffer[1024]; @@ -180,12 +180,18 @@ else memset(&statbuf, 0, sizeof(statbuf)); if (STATVFS(CS path, &statbuf) != 0) - { - log_write(0, LOG_MAIN|LOG_PANIC, "cannot accept message: failed to stat " - "%s directory %s: %s", name, spool_directory, strerror(errno)); - smtp_closedown(US"spool or log directory problem"); - exim_exit(EXIT_FAILURE); - } + if (stat(CS path, &dummy) == -1 && errno == ENOENT) + { /* Can happen on first run after installation */ + *inodeptr = -1; + return -1; + } + else + { + log_write(0, LOG_MAIN|LOG_PANIC, "cannot accept message: failed to stat " + "%s directory %s: %s", name, path, strerror(errno)); + smtp_closedown(US"spool or log directory problem"); + exim_exit(EXIT_FAILURE); + } *inodeptr = (statbuf.F_FILES > 0)? statbuf.F_FAVAIL : -1; @@ -193,9 +199,9 @@ if (STATVFS(CS path, &statbuf) != 0) return (int)(((double)statbuf.F_BAVAIL * (double)statbuf.F_FRSIZE)/1024.0); +#else /* Unable to find partition sizes in this environment. */ -#else *inodeptr = -1; return -1; #endif @@ -619,7 +625,7 @@ if (!dot_ends) { register int last_ch = '\n'; - for (; (ch = (receive_getc)()) != EOF; last_ch = ch) + for (; (ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF; last_ch = ch) { if (ch == 0) body_zerocount++; if (last_ch == '\r' && ch != '\n') @@ -661,7 +667,7 @@ if (!dot_ends) ch_state = 1; -while ((ch = (receive_getc)()) != EOF) +while ((ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF) { if (ch == 0) body_zerocount++; switch (ch_state) @@ -779,7 +785,7 @@ int ch_state = 0; int ch; int linelength = 0; -while ((ch = (receive_getc)()) != EOF) +while ((ch = (receive_getc)(GETC_BUFFER_UNLIMITED)) != EOF) { if (ch == 0) body_zerocount++; switch (ch_state) @@ -823,7 +829,7 @@ while ((ch = (receive_getc)()) != EOF) { message_size++; if (fout != NULL && fputc('\n', fout) == EOF) return END_WERROR; - (void) cutthrough_put_nl(); + cutthrough_data_put_nl(); if (ch != '\r') ch_state = 1; else continue; } break; @@ -842,7 +848,7 @@ while ((ch = (receive_getc)()) != EOF) if (ch == '.') { uschar c= ch; - (void) cutthrough_puts(&c, 1); + cutthrough_data_puts(&c, 1); } ch_state = 1; break; @@ -852,7 +858,7 @@ while ((ch = (receive_getc)()) != EOF) message_size++; body_linecount++; if (fout != NULL && fputc('\n', fout) == EOF) return END_WERROR; - (void) cutthrough_put_nl(); + cutthrough_data_put_nl(); if (ch == '\r') { ch_state = 2; @@ -873,11 +879,11 @@ while ((ch = (receive_getc)()) != EOF) if (message_size > thismessage_size_limit) return END_SIZE; } if(ch == '\n') - (void) cutthrough_put_nl(); + cutthrough_data_put_nl(); else { uschar c = ch; - (void) cutthrough_puts(&c, 1); + cutthrough_data_puts(&c, 1); } } @@ -891,11 +897,15 @@ return END_EOF; /* Variant of the above read_message_data_smtp() specialised for RFC 3030 -CHUNKING. We assume that the incoming has proper CRLF, so only have to scan -for and strip CR. On the downside there are more protocol reasons to stop. +CHUNKING. Accept input lines separated by either CRLF or CR or LF and write +LF-delimited spoolfile. Until we have wireformat spoolfiles, we need the +body_linecount accounting for proper re-expansion for the wire, so use +a cut-down version of the state-machine above; we don't need to do leading-dot +detection and unstuffing. Arguments: - fout a FILE to which to write the message; NULL if skipping + fout a FILE to which to write the message; NULL if skipping; + must be open for both writing and reading. Returns: One of the END_xxx values indicating why it stopped reading */ @@ -903,43 +913,105 @@ Returns: One of the END_xxx values indicating why it stopped reading static int read_message_bdat_smtp(FILE *fout) { -int ch; -int linelength = 0; +int linelength = 0, ch; +enum CH_STATE ch_state = LF_SEEN; +BOOL fix_nl = FALSE; -for (;;) switch (ch = bdat_getc()) +for(;;) { - case EOF: return END_EOF; - case EOD: return END_DOT; - case ERR: return END_PROTOCOL; + switch ((ch = (bdat_getc)(GETC_BUFFER_UNLIMITED))) + { + case EOF: return END_EOF; + case ERR: return END_PROTOCOL; + case EOD: + /* Nothing to get from the sender anymore. We check the last + character written to the spool. + + RFC 3030 states, that BDAT chunks are normal text, terminated by CRLF. + If we would be strict, we would refuse such broken messages. + But we are liberal, so we fix it. It would be easy just to append + the "\n" to the spool. + + But there are some more things (line counting, message size calculation and such), + that would need to be duplicated here. So we simply do some ungetc + trickery. + */ + if (fout) + { + if (fseek(fout, -1, SEEK_CUR) < 0) return END_PROTOCOL; + if (fgetc(fout) == '\n') return END_DOT; + } - case '\r': - body_linecount++; - if (linelength > max_received_linelength) - max_received_linelength = linelength; - linelength = -1; - break; + if (linelength == -1) /* \r already seen (see below) */ + { + DEBUG(D_receive) debug_printf("Add missing LF\n"); + bdat_ungetc('\n'); + continue; + } + DEBUG(D_receive) debug_printf("Add missing CRLF\n"); + bdat_ungetc('\r'); /* not even \r was seen */ + fix_nl = TRUE; - case 0: - body_zerocount++; - /*FALLTHROUGH*/ - default: - message_size++; - linelength++; - if (fout) - { - if (fputc(ch, fout) == EOF) return END_WERROR; - if (message_size > thismessage_size_limit) return END_SIZE; - } -#ifdef notyet - if(ch == '\n') - (void) cutthrough_put_nl(); - else - { - uschar c = ch; - (void) cutthrough_puts(&c, 1); - } -#endif - break; + continue; + case '\0': body_zerocount++; break; + } + switch (ch_state) + { + case LF_SEEN: /* After LF or CRLF */ + ch_state = MID_LINE; + /* fall through to handle as normal uschar. */ + + case MID_LINE: /* Mid-line state */ + if (ch == '\n') + { + ch_state = LF_SEEN; + body_linecount++; + if (linelength > max_received_linelength) + max_received_linelength = linelength; + linelength = -1; + } + else if (ch == '\r') + { + ch_state = CR_SEEN; + if (fix_nl) bdat_ungetc('\n'); + continue; /* don't write CR */ + } + break; + + case CR_SEEN: /* After (unwritten) CR */ + body_linecount++; + if (linelength > max_received_linelength) + max_received_linelength = linelength; + linelength = -1; + if (ch == '\n') + ch_state = LF_SEEN; + else + { + message_size++; + if (fout && fputc('\n', fout) == EOF) return END_WERROR; + cutthrough_data_put_nl(); + if (ch == '\r') continue; /* don't write CR */ + ch_state = MID_LINE; + } + break; + } + + /* Add the character to the spool file, unless skipping */ + + message_size++; + linelength++; + if (fout) + { + if (fputc(ch, fout) == EOF) return END_WERROR; + if (message_size > thismessage_size_limit) return END_SIZE; + } + if(ch == '\n') + cutthrough_data_put_nl(); + else + { + uschar c = ch; + cutthrough_data_puts(&c, 1); + } } /*NOTREACHED*/ } @@ -986,6 +1058,7 @@ 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); +smtp_notquit_exit(US"connection-lost", NULL, NULL); return US"421 Lost incoming connection"; } @@ -1065,7 +1138,8 @@ switch(where) case ACL_WHERE_DKIM: case ACL_WHERE_MIME: case ACL_WHERE_DATA: - if (cutthrough.fd >= 0 && (acl_removed_headers || acl_added_headers)) + if ( cutthrough.fd >= 0 && cutthrough.delivery + && (acl_removed_headers || acl_added_headers)) { log_write(0, LOG_MAIN|LOG_PANIC, "Header modification in data ACLs" " will not take effect on cutthrough deliveries"); @@ -1073,11 +1147,11 @@ switch(where) } } -if (acl_removed_headers != NULL) +if (acl_removed_headers) { - DEBUG(D_receive|D_acl) debug_printf(">>Headers removed by %s ACL:\n", acl_name); + DEBUG(D_receive|D_acl) debug_printf_indent(">>Headers removed by %s ACL:\n", acl_name); - for (h = header_list; h != NULL; h = h->next) if (h->type != htype_old) + for (h = header_list; h; h = h->next) if (h->type != htype_old) { const uschar * list = acl_removed_headers; int sep = ':'; /* This is specified as a colon-separated list */ @@ -1088,17 +1162,17 @@ if (acl_removed_headers != NULL) if (header_testname(h, s, Ustrlen(s), FALSE)) { h->type = htype_old; - DEBUG(D_receive|D_acl) debug_printf(" %s", h->text); + DEBUG(D_receive|D_acl) debug_printf_indent(" %s", h->text); } } acl_removed_headers = NULL; - DEBUG(D_receive|D_acl) debug_printf(">>\n"); + DEBUG(D_receive|D_acl) debug_printf_indent(">>\n"); } -if (acl_added_headers == NULL) return; -DEBUG(D_receive|D_acl) debug_printf(">>Headers added by %s ACL:\n", acl_name); +if (!acl_added_headers) return; +DEBUG(D_receive|D_acl) debug_printf_indent(">>Headers added by %s ACL:\n", acl_name); -for (h = acl_added_headers; h != NULL; h = next) +for (h = acl_added_headers; h; h = next) { next = h->next; @@ -1107,7 +1181,7 @@ for (h = acl_added_headers; h != NULL; h = next) case htype_add_top: h->next = header_list; header_list = h; - DEBUG(D_receive|D_acl) debug_printf(" (at top)"); + DEBUG(D_receive|D_acl) debug_printf_indent(" (at top)"); break; case htype_add_rec: @@ -1122,7 +1196,7 @@ for (h = acl_added_headers; h != NULL; h = next) } h->next = last_received->next; last_received->next = h; - DEBUG(D_receive|D_acl) debug_printf(" (after Received:)"); + DEBUG(D_receive|D_acl) debug_printf_indent(" (after Received:)"); break; case htype_add_rfc: @@ -1137,7 +1211,7 @@ for (h = acl_added_headers; h != NULL; h = next) 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(" (before any non-Received: or Resent-*: header)"); + DEBUG(D_receive|D_acl) debug_printf_indent(" (before any non-Received: or Resent-*: header)"); break; default: @@ -1157,11 +1231,11 @@ for (h = acl_added_headers; h != NULL; h = next) h->type = header_checkname(h, FALSE); if (h->type >= 'a') h->type = htype_other; - DEBUG(D_receive|D_acl) debug_printf(" %s", header_last->text); + DEBUG(D_receive|D_acl) debug_printf_indent(" %s", header_last->text); } acl_added_headers = NULL; -DEBUG(D_receive|D_acl) debug_printf(">>\n"); +DEBUG(D_receive|D_acl) debug_printf_indent(">>\n"); } @@ -1232,36 +1306,30 @@ unsigned long mbox_size; header_line *my_headerlist; uschar *user_msg, *log_msg; 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 */ -my_headerlist = header_list; -while (my_headerlist != NULL) - { - /* skip deleted headers */ - if (my_headerlist->type == '*') - { - my_headerlist = my_headerlist->next; - continue; - } - if (strncmpic(my_headerlist->text, US"Content-Type:", 13) == 0) + +for (my_headerlist = header_list; my_headerlist; my_headerlist = my_headerlist->next) + if ( my_headerlist->type != '*' /* skip deleted headers */ + && strncmpic(my_headerlist->text, US"Content-Type:", 13) == 0 + ) { DEBUG(D_receive) debug_printf("Found Content-Type: header - executing acl_smtp_mime.\n"); goto DO_MIME_ACL; } - my_headerlist = my_headerlist->next; - } DEBUG(D_receive) debug_printf("No Content-Type: header - presumably not a MIME message.\n"); return TRUE; DO_MIME_ACL: + /* make sure the eml mbox file is spooled up */ -mbox_file = spool_mbox(&mbox_size, NULL); -if (mbox_file == NULL) { - /* error while spooling */ +if (!(mbox_file = spool_mbox(&mbox_size, NULL, &mbox_filename))) + { /* error while spooling */ log_write(0, LOG_MAIN|LOG_PANIC, "acl_smtp_mime: error while creating mbox spool file, message temporarily rejected."); Uunlink(spool_name); @@ -1273,7 +1341,7 @@ if (mbox_file == NULL) { 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 */ -}; + } mime_is_rfc822 = 0; @@ -1297,14 +1365,13 @@ if (Ustrlen(rfc822_file_path) > 0) /* check if we must check any message/rfc822 attachments */ if (rc == OK) { - uschar temp_path[1024]; + uschar * scandir; struct dirent * entry; DIR * tempdir; - (void) string_format(temp_path, sizeof(temp_path), "%s/scan/%s", - spool_directory, message_id); + scandir = string_copyn(mbox_filename, Ustrrchr(mbox_filename, '/') - mbox_filename); - tempdir = opendir(CS temp_path); + tempdir = opendir(CS scandir); for (;;) { if (!(entry = readdir(tempdir))) @@ -1312,8 +1379,8 @@ if (rc == OK) if (strncmpic(US entry->d_name, US"__rfc822_", 9) == 0) { (void) string_format(rfc822_file_path, sizeof(rfc822_file_path), - "%s/scan/%s/%s", spool_directory, message_id, entry->d_name); - debug_printf("RFC822 attachment detected: running MIME ACL for '%s'\n", + "%s/%s", scandir, entry->d_name); + DEBUG(D_receive) debug_printf("RFC822 attachment detected: running MIME ACL for '%s'\n", rfc822_file_path); break; } @@ -1578,7 +1645,7 @@ search_tidyup(); cutthrough delivery with the no-spool option. It shouldn't be possible to set up the combination, but just in case kill any ongoing connection. */ if (extract_recip || !smtp_input) - cancel_cutthrough_connection("not smtp input"); + cancel_cutthrough_connection(TRUE, US"not smtp input"); /* Initialize the chain of headers by setting up a place-holder for Received: header. Temporarily mark it as "old", i.e. not to be used. We keep header_last @@ -1674,7 +1741,7 @@ next->text. */ for (;;) { - int ch = (receive_getc)(); + int ch = (receive_getc)(GETC_BUFFER_UNLIMITED); /* If we hit EOF on a SMTP connection, it's an error, since incoming SMTP must have a correct "." terminator. */ @@ -1753,10 +1820,10 @@ for (;;) if (ptr == 0 && ch == '.' && (smtp_input || dot_ends)) { - ch = (receive_getc)(); + ch = (receive_getc)(GETC_BUFFER_UNLIMITED); if (ch == '\r') { - ch = (receive_getc)(); + ch = (receive_getc)(GETC_BUFFER_UNLIMITED); if (ch != '\n') { receive_ungetc(ch); @@ -1787,7 +1854,7 @@ for (;;) if (ch == '\r') { - ch = (receive_getc)(); + ch = (receive_getc)(GETC_BUFFER_UNLIMITED); if (ch == '\n') { if (first_line_ended_crlf == TRUE_UNSET) first_line_ended_crlf = TRUE; @@ -1882,7 +1949,7 @@ for (;;) if (ch != EOF) { - int nextch = (receive_getc)(); + int nextch = (receive_getc)(GETC_BUFFER_UNLIMITED); if (nextch == ' ' || nextch == '\t') { next->text[ptr++] = nextch; @@ -2076,6 +2143,21 @@ for (;;) } } + /* Reject CHUNKING messages that do not CRLF their first header line */ + + if (!first_line_ended_crlf && chunking_state > CHUNKING_OFFERED) + { + log_write(L_size_reject, LOG_MAIN|LOG_REJECT, "rejected from <%s>%s%s%s%s: " + "Non-CRLF-terminated header, under CHUNKING: message abandoned", + 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"); + bdat_flush_data(); + smtp_reply = US""; + goto TIDYUP; /* Skip to end of function */ + } + /* The line has been handled. If we have hit EOF, break out of the loop, indicating no pending data line. */ @@ -2100,7 +2182,7 @@ normal case). */ DEBUG(D_receive) { debug_printf(">>Headers received:\n"); - for (h = header_list->next; h != NULL; h = h->next) + for (h = header_list->next; h; h = h->next) debug_printf("%s", h->text); debug_printf("\n"); } @@ -2127,7 +2209,7 @@ if (filter_test != FTEST_NONE && header_list->next == NULL) /* Scan the headers to identify them. Some are merely marked for later processing; some are dealt with here. */ -for (h = header_list->next; h != NULL; h = h->next) +for (h = header_list->next; h; h = h->next) { BOOL is_resent = strncmpic(h->text, US"resent-", 7) == 0; if (is_resent) contains_resent_headers = TRUE; @@ -2343,7 +2425,7 @@ if (extract_recip) /* Now scan the headers */ - for (h = header_list->next; h != NULL; h = h->next) + for (h = header_list->next; h; h = h->next) { if ((h->type == htype_to || h->type == htype_cc || h->type == htype_bcc) && (!contains_resent_headers || strncmpic(h->text, US"resent-", 7) == 0)) @@ -2837,11 +2919,11 @@ We start at the second header, skipping our own Received:. This rewriting is documented as happening *after* recipient addresses are taken from the headers by the -t command line option. An added Sender: gets rewritten here. */ -for (h = header_list->next; h != NULL; h = h->next) +for (h = header_list->next; h; h = h->next) { header_line *newh = rewrite_header(h, NULL, NULL, global_rewrite_rules, rewrite_existflags, TRUE); - if (newh != NULL) h = newh; + if (newh) h = newh; } @@ -2898,26 +2980,25 @@ inbound is, but inbound chunking ought to be ok with outbound plain. Could we do onward CHUNKING given inbound CHUNKING? */ if (chunking_state > CHUNKING_OFFERED) - cancel_cutthrough_connection("chunking active"); + cancel_cutthrough_connection(FALSE, US"chunking active"); /* Cutthrough delivery: We have to create the Received header now rather than at the end of reception, so the timestamp behaviour is a change to the normal case. XXX Ensure this gets documented XXX. Having created it, send the headers to the destination. */ -if (cutthrough.fd >= 0) + +if (cutthrough.fd >= 0 && cutthrough.delivery) { if (received_count > received_headers_max) { - cancel_cutthrough_connection("too many headers"); + cancel_cutthrough_connection(TRUE, US"too many headers"); if (smtp_input) receive_swallow_smtp(); /* Swallow incoming SMTP */ log_write(0, LOG_MAIN|LOG_REJECT, "rejected from <%s>%s%s%s%s: " "Too many \"Received\" headers", 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_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 */ @@ -3015,7 +3096,7 @@ if (!ferror(data_file) && !(receive_feof)() && message_ended != END_DOT) if (smtp_input) { Uunlink(spool_name); /* Lose data file when closed */ - cancel_cutthrough_connection("sender closed connection"); + 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; @@ -3028,7 +3109,7 @@ if (!ferror(data_file) && !(receive_feof)() && message_ended != END_DOT) case END_SIZE: Uunlink(spool_name); /* Lose the data file when closed */ - cancel_cutthrough_connection("mail too big"); + cancel_cutthrough_connection(TRUE, US"mail too big"); if (smtp_input) receive_swallow_smtp(); /* Swallow incoming SMTP */ log_write(L_size_reject, LOG_MAIN|LOG_REJECT, "rejected from <%s>%s%s%s%s: " @@ -3061,7 +3142,7 @@ if (!ferror(data_file) && !(receive_feof)() && message_ended != END_DOT) case END_PROTOCOL: Uunlink(spool_name); /* Lose the data file when closed */ - cancel_cutthrough_connection("sender protocol error"); + 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 */ @@ -3094,7 +3175,7 @@ if (fflush(data_file) == EOF || ferror(data_file) || log_write(0, LOG_MAIN, "Message abandoned: %s", msg); Uunlink(spool_name); /* Lose the data file */ - cancel_cutthrough_connection("error writing spoolfile"); + cancel_cutthrough_connection(TRUE, US"error writing spoolfile"); if (smtp_input) { @@ -3333,7 +3414,7 @@ else DEBUG(D_receive) debug_printf("acl_smtp_dkim: acl_check returned %d on %s, " "skipping remaining items\n", rc, item); - cancel_cutthrough_connection("dkim acl not ok"); + cancel_cutthrough_connection(TRUE, US"dkim acl not ok"); break; } } @@ -3452,14 +3533,14 @@ else { recipients_count = 0; blackholed_by = US"DATA ACL"; - if (log_msg != NULL) + if (log_msg) blackhole_log_msg = string_sprintf(": %s", log_msg); - cancel_cutthrough_connection("data acl discard"); + cancel_cutthrough_connection(TRUE, US"data acl discard"); } else if (rc != OK) { Uunlink(spool_name); - cancel_cutthrough_connection("data acl not ok"); + cancel_cutthrough_connection(TRUE, US"data acl not ok"); #ifdef WITH_CONTENT_SCAN unspool_mbox(); #endif @@ -3730,7 +3811,7 @@ if (bmi_run == 1) } #endif -/* Update the timstamp in our Received: header to account for any time taken by +/* Update the timestamp in our Received: header to account for any time taken by an ACL or by local_scan(). The new time is the time that all reception processing is complete. */ @@ -3815,7 +3896,7 @@ string as required. Since we commonly want to add two items at a time, use a macro to simplify the coding. We log the arrival of a new message while the file is still locked, just in case the machine is *really* fast, and delivers it first! Include any message id that is in the message - since the syntax of a -message id is actually an addr-spec, we can use the parse routine to canonicize +message id is actually an addr-spec, we can use the parse routine to canonicalize it. */ size = 256; @@ -4016,17 +4097,17 @@ if (smtp_input && sender_host_address != NULL && !sender_host_notsocket && if (select(fileno(smtp_in) + 1, &select_check, NULL, NULL, &tv) != 0) { - int c = (receive_getc)(); + int c = (receive_getc)(GETC_BUFFER_UNLIMITED); if (c != EOF) (receive_ungetc)(c); else { - uschar *msg = US"SMTP connection lost after final dot"; + smtp_notquit_exit(US"connection-lost", NULL, NULL); smtp_reply = US""; /* No attempt to send a response */ smtp_yield = FALSE; /* Nothing more on this connection */ /* Re-use the log line workspace */ sptr = 0; - s = string_cat(s, &size, &sptr, msg); + s = string_cat(s, &size, &sptr, US"SMTP connection lost after final dot"); s = add_host_info_for_log(s, &size, &sptr); s[sptr] = 0; log_write(0, LOG_MAIN, "%s", s); @@ -4050,16 +4131,17 @@ for this message. */ Send dot onward. If accepted, wipe the spooled files, log as delivered and accept the sender's dot (below). - If rejected: copy response to sender, wipe the spooled files, log approriately. - If temp-reject: accept to sender, keep the spooled files. + If rejected: copy response to sender, wipe the spooled files, log appropriately. + If temp-reject: normally accept to sender, keep the spooled file - unless defer=pass + in which case pass temp-reject back to initiator and dump the files. Having the normal spool files lets us do data-filtering, and store/forward on temp-reject. XXX We do not handle queue-only, freezing, or blackholes. */ -if(cutthrough.fd >= 0) +if(cutthrough.fd >= 0 && cutthrough.delivery) { - uschar * msg= cutthrough_finaldot(); /* Ask the target system to accept the message */ + uschar * msg = cutthrough_finaldot(); /* Ask the target system to accept the message */ /* Logging was done in finaldot() */ switch(msg[0]) { @@ -4067,13 +4149,17 @@ if(cutthrough.fd >= 0) cutthrough_done = ACCEPTED; break; /* message_id needed for SMTP accept below */ + case '4': /* Temp-reject. Keep spoolfiles and accept, unless defer-pass mode. + ... for which, pass back the exact error */ + if (cutthrough.defer_pass) smtp_reply = string_copy_malloc(msg); + /*FALLTRHOUGH*/ + default: /* Unknown response, or error. Treat as temp-reject. */ - case '4': /* Temp-reject. Keep spoolfiles and accept. */ cutthrough_done = TMP_REJ; /* Avoid the usual immediate delivery attempt */ break; /* message_id needed for SMTP accept below */ case '5': /* Perm-reject. Do the same to the source. Dump any spoolfiles */ - smtp_reply= msg; /* Pass on the exact error */ + smtp_reply = string_copy_malloc(msg); /* Pass on the exact error */ cutthrough_done = PERM_REJ; break; } @@ -4187,27 +4273,39 @@ if (smtp_input) /* smtp_reply is set non-empty */ 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); else smtp_printf("%.1024s\r\n", smtp_reply); - } switch (cutthrough_done) { - case ACCEPTED: log_write(0, LOG_MAIN, "Completed");/* Delivery was done */ + case ACCEPTED: + log_write(0, LOG_MAIN, "Completed");/* Delivery was done */ case PERM_REJ: - { /* Delete spool files */ - Uunlink(spool_fname(US"input", message_subdir, message_id, US"-D")); - Uunlink(spool_fname(US"input", message_subdir, message_id, US"-H")); - Uunlink(spool_fname(US"msglog", message_subdir, message_id, US"")); - } - case TMP_REJ: message_id[0] = 0; /* Prevent a delivery from starting */ - default:break; + /* Delete spool files */ + Uunlink(spool_fname(US"input", message_subdir, message_id, US"-D")); + Uunlink(spool_fname(US"input", message_subdir, message_id, US"-H")); + Uunlink(spool_fname(US"msglog", message_subdir, message_id, US"")); + break; + + case TMP_REJ: + if (cutthrough.defer_pass) + { + Uunlink(spool_fname(US"input", message_subdir, message_id, US"-D")); + Uunlink(spool_fname(US"input", message_subdir, message_id, US"-H")); + Uunlink(spool_fname(US"msglog", message_subdir, message_id, US"")); + } + default: + break; + } + if (cutthrough_done != NOT_TRIED) + { + message_id[0] = 0; /* Prevent a delivery from starting */ + cutthrough.delivery = cutthrough.callout_hold_only = FALSE; + cutthrough.defer_pass = FALSE; } - cutthrough.delivery = FALSE; } /* For batched SMTP, generate an error message on failure, and do