X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fsrc%2Freceive.c;h=6ce2823f514d186654ee9055379e57819a94a42b;hb=b6040544759110aa97f93e9ba0dd8232cd5e5188;hp=9ff339d390226a504b2296112503a44044af1057;hpb=7e3ce68e68ab9b8906a637d352993abf361554e2;p=user%2Fhenk%2Fcode%2Fexim.git diff --git a/src/src/receive.c b/src/src/receive.c index 9ff339d39..6ce2823f5 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -37,7 +37,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); } @@ -124,6 +124,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 +181,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 +200,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 +626,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 +668,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) @@ -682,7 +689,8 @@ while ((ch = (receive_getc)()) != EOF) case 1: /* After written "\n" */ if (ch == '.') { ch_state = 3; continue; } if (ch == '\r') { ch_state = 2; continue; } - if (ch != '\n') ch_state = 0; else linelength = -1; + if (ch == '\n') { body_linecount++; linelength = -1; } + else ch_state = 0; break; case 2: @@ -778,7 +786,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) @@ -890,8 +898,11 @@ 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 @@ -902,43 +913,73 @@ 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 ch_state = 0, linelength = 0, ch; -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 EOD: return END_DOT; /* normal exit */ + case ERR: return END_PROTOCOL; + case '\0': body_zerocount++; break; + } + switch (ch_state) + { + case 0: /* After LF or CRLF */ + ch_state = 1; + /* fall through to handle as normal uschar. */ - case '\r': - body_linecount++; - if (linelength > max_received_linelength) - max_received_linelength = linelength; - linelength = -1; - break; + case 1: /* Mid-line state */ + if (ch == '\n') + { + ch_state = 0; + body_linecount++; + if (linelength > max_received_linelength) + max_received_linelength = linelength; + linelength = -1; + } + else if (ch == '\r') + { + ch_state = 2; + continue; /* don't write CR */ + } + break; - 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; + case 2: /* After (unwritten) CR */ + body_linecount++; + if (linelength > max_received_linelength) + max_received_linelength = linelength; + linelength = -1; + if (ch == '\n') + ch_state = 0; + else + { + message_size++; + if (fout != NULL && fputc('\n', fout) == EOF) return END_WERROR; + (void) cutthrough_put_nl(); + if (ch == '\r') continue; /* don't write CR */ + ch_state = 1; + } + 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') + (void) cutthrough_put_nl(); + else + { + uschar c = ch; + (void) cutthrough_puts(&c, 1); + } } /*NOTREACHED*/ } @@ -985,6 +1026,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"; } @@ -1614,8 +1656,10 @@ message_linecount = body_linecount = body_zerocount = max_received_linelength = 0; #ifndef DISABLE_DKIM -/* Call into DKIM to set up the context. */ -if (smtp_input && !smtp_batched_input && !dkim_disable_verify) dkim_exim_verify_init(); +/* Call into DKIM to set up the context. In CHUNKING mode +we clear the dot-stuffing flag */ +if (smtp_input && !smtp_batched_input && !dkim_disable_verify) + dkim_exim_verify_init(chunking_state <= CHUNKING_OFFERED); #endif #ifdef EXPERIMENTAL_DMARC @@ -1671,7 +1715,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. */ @@ -1750,10 +1794,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); @@ -1784,7 +1828,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; @@ -1879,7 +1923,7 @@ for (;;) if (ch != EOF) { - int nextch = (receive_getc)(); + int nextch = (receive_getc)(GETC_BUFFER_UNLIMITED); if (nextch == ' ' || nextch == '\t') { next->text[ptr++] = nextch; @@ -2073,6 +2117,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. */ @@ -2097,7 +2156,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"); } @@ -2124,7 +2183,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; @@ -2340,7 +2399,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)) @@ -2834,11 +2893,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; } @@ -3727,7 +3786,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. */ @@ -3812,16 +3871,17 @@ 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; sptr = 0; s = store_get(size); -s = string_append(s, &size, &sptr, 2, US"<= ", - (sender_address[0] == 0)? US"<>" : sender_address); -if (message_reference != NULL) +s = string_append(s, &size, &sptr, 2, + fake_response == FAIL ? US"(= " : US"<= ", + sender_address[0] == 0 ? US"<>" : sender_address); +if (message_reference) s = string_append(s, &size, &sptr, 2, US" R=", message_reference); s = add_host_info_for_log(s, &size, &sptr); @@ -3831,7 +3891,7 @@ if (LOGGING(tls_cipher) && tls_in.cipher) s = string_append(s, &size, &sptr, 2, US" X=", tls_in.cipher); if (LOGGING(tls_certificate_verified) && tls_in.cipher) s = string_append(s, &size, &sptr, 2, US" CV=", - tls_in.certificate_verified? "yes":"no"); + tls_in.certificate_verified ? "yes":"no"); if (LOGGING(tls_peerdn) && tls_in.peerdn) s = string_append(s, &size, &sptr, 3, US" DN=\"", string_printing(tls_in.peerdn), US"\""); @@ -3843,10 +3903,10 @@ if (LOGGING(tls_sni) && tls_in.sni) if (sender_host_authenticated) { s = string_append(s, &size, &sptr, 2, US" A=", sender_host_authenticated); - if (authenticated_id != NULL) + if (authenticated_id) { s = string_append(s, &size, &sptr, 2, US":", authenticated_id); - if (LOGGING(smtp_mailauth) && authenticated_sender != NULL) + if (LOGGING(smtp_mailauth) && authenticated_sender) s = string_append(s, &size, &sptr, 2, US":", authenticated_sender); } } @@ -3885,7 +3945,7 @@ any characters except " \ and CR and so in particular it can contain NL! Therefore, make sure we use a printing-characters only version for the log. Also, allow for domain literals in the message id. */ -if (msgid_header != NULL) +if (msgid_header) { uschar *old_id; BOOL save_allow_domain_literals = allow_domain_literals; @@ -3968,7 +4028,9 @@ if (message_logs && blackholed_by == NULL) if (deliver_freeze) fprintf(message_log, "%s frozen by %s\n", now, frozen_by); if (queue_only_policy) fprintf(message_log, - "%s no immediate delivery: queued by %s\n", now, queued_by); + "%s no immediate delivery: queued%s%s by %s\n", now, + *queue_name ? " in " : "", *queue_name ? CS queue_name : "", + queued_by); (void)fclose(message_log); } } @@ -4010,17 +4072,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); @@ -4044,8 +4106,9 @@ 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. @@ -4061,13 +4124,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; } @@ -4088,7 +4155,9 @@ if(!smtp_reply) if (deliver_freeze) log_write(0, LOG_MAIN, "frozen by %s", frozen_by); if (queue_only_policy) log_write(L_delay_delivery, LOG_MAIN, - "no immediate delivery: queued by %s", queued_by); + "no immediate delivery: queued%s%s by %s", + *queue_name ? " in " : "", *queue_name ? CS queue_name : "", + queued_by); } receive_call_bombout = FALSE; @@ -4144,15 +4213,15 @@ if (smtp_input) if (!smtp_batched_input) { - if (smtp_reply == NULL) + if (!smtp_reply) { if (fake_response != OK) - smtp_respond((fake_response == DEFER)? US"450" : US"550", 3, TRUE, - fake_response_text); + smtp_respond(fake_response == DEFER ? US"450" : US"550", + 3, TRUE, fake_response_text); /* An OK response is required; use "message" text if present. */ - else if (user_msg != NULL) + else if (user_msg) { uschar *code = US"250"; int len = 3; @@ -4179,34 +4248,45 @@ 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"")); + message_id[0] = 0; /* Prevent a delivery from starting */ + 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"")); + } + message_id[0] = 0; /* Prevent a delivery from starting */ + default: + break; } cutthrough.delivery = FALSE; + cutthrough.defer_pass = FALSE; } /* For batched SMTP, generate an error message on failure, and do nothing on success. The function moan_smtp_batch() does not return - it exits from the program with a non-zero return code. */ - else if (smtp_reply != NULL) moan_smtp_batch(NULL, "%s", smtp_reply); + else if (smtp_reply) + moan_smtp_batch(NULL, "%s", smtp_reply); } @@ -4215,7 +4295,7 @@ file has already been unlinked, and the header file was never written to disk. We must now indicate that nothing was received, to prevent a delivery from starting. */ -if (blackholed_by != NULL) +if (blackholed_by) { const uschar *detail = local_scan_data ? string_printing(local_scan_data)