X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/58eb016e585187a87ade7602b2aecb2208605320..f7572e5a358cd3d9581140b87e590d58b6c278f0:/src/src/receive.c diff --git a/src/src/receive.c b/src/src/receive.c index 98a728b2f..62db50f96 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/receive.c,v 1.36 2007/04/13 15:13:47 ph10 Exp $ */ +/* $Cambridge: exim/src/src/receive.c,v 1.42 2007/09/28 12:21:57 tom Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -11,14 +11,38 @@ #include "exim.h" +#if (defined EXPERIMENTAL_DOMAINKEYS) && (defined EXPERIMENTAL_DKIM) + +#warning Chaining Domainkeys via DKIM receive functions +#define RECEIVE_GETC dkim_receive_getc +#define RECEIVE_UNGETC dkim_receive_ungetc + +#else + +#if (defined EXPERIMENTAL_DOMAINKEYS) || (defined EXPERIMENTAL_DKIM) + #ifdef EXPERIMENTAL_DOMAINKEYS +#warning Using Domainkeys receive functions #define RECEIVE_GETC dk_receive_getc #define RECEIVE_UNGETC dk_receive_ungetc +#endif +#ifdef EXPERIMENTAL_DKIM +#warning Using DKIM receive functions +#define RECEIVE_GETC dkim_receive_getc +#define RECEIVE_UNGETC dkim_receive_ungetc +#endif + #else + +/* Normal operation */ #define RECEIVE_GETC receive_getc #define RECEIVE_UNGETC receive_ungetc + +#endif + #endif + /************************************************* * Local static variables * *************************************************/ @@ -178,7 +202,7 @@ else } } -/* We now have the patch; do the business */ +/* We now have the path; do the business */ memset(&statbuf, 0, sizeof(statbuf)); @@ -283,12 +307,14 @@ that case is done by setting a flag to cause the log functions to call this function if there is an ultimate disaster. That is why it is globally accessible. -Arguments: SMTP response to give if in an SMTP session +Arguments: + reason text reason to pass to the not-quit ACL + msg default SMTP response to give if in an SMTP session Returns: it doesn't */ void -receive_bomb_out(uschar *msg) +receive_bomb_out(uschar *reason, uschar *msg) { /* If spool_name is set, it contains the name of the data file that is being written. Unlink it before closing so that it cannot be picked up by a delivery @@ -306,20 +332,16 @@ if (spool_name[0] != 0) if (data_file != NULL) (void)fclose(data_file); else if (data_fd >= 0) (void)close(data_fd); -/* Attempt to close down an SMTP connection tidily. */ +/* Attempt to close down an SMTP connection tidily. For non-batched SMTP, call +smtp_notquit_exit(), which runs the NOTQUIT ACL, if present, and handles the +SMTP response. */ if (smtp_input) { - if (!smtp_batched_input) - { - smtp_printf("421 %s %s - closing connection.\r\n", smtp_active_hostname, - msg); - mac_smtp_fflush(); - } - - /* Control does not return from moan_smtp_batch(). */ - - else moan_smtp_batch(NULL, "421 %s - message abandoned", msg); + if (smtp_batched_input) + moan_smtp_batch(NULL, "421 %s - message abandoned", msg); /* No return */ + smtp_notquit_exit(reason, US"421", US"%s %s - closing connection.", + smtp_active_hostname, msg); } /* Exit from the program (non-BSMTP cases) */ @@ -362,7 +384,7 @@ else LOG_MAIN, "timed out while reading local message"); } -receive_bomb_out(msg); /* Does not return */ +receive_bomb_out(US"data-timeout", msg); /* Does not return */ } @@ -384,7 +406,8 @@ 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); -receive_bomb_out(US"local verification problem"); /* Does not return */ +/* Does not return */ +receive_bomb_out(US"local-scan-timeout", US"local verification problem"); } @@ -405,7 +428,8 @@ 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); -receive_bomb_out(US"local verification problem"); /* Does not return */ +/* Does not return */ +receive_bomb_out(US"local-scan-error", US"local verification problem"); } @@ -442,7 +466,7 @@ else } } -receive_bomb_out(msg); /* Does not return */ +receive_bomb_out(US"signal-exit", msg); /* Does not return */ } @@ -564,6 +588,7 @@ read_message_data(FILE *fout) { int ch_state; register int ch; +register int linelength = 0; /* Handle the case when only EOF terminates the message */ @@ -576,6 +601,9 @@ if (!dot_ends) if (ch == 0) body_zerocount++; if (last_ch == '\r' && ch != '\n') { + if (linelength > max_received_linelength) + max_received_linelength = linelength; + linelength = 0; if (fputc('\n', fout) == EOF) return END_WERROR; message_size++; body_linecount++; @@ -583,12 +611,21 @@ if (!dot_ends) if (ch == '\r') continue; if (fputc(ch, fout) == EOF) return END_WERROR; - if (ch == '\n') body_linecount++; + if (ch == '\n') + { + if (linelength > max_received_linelength) + max_received_linelength = linelength; + linelength = 0; + body_linecount++; + } + else linelength++; if (++message_size > thismessage_size_limit) return END_SIZE; } if (last_ch != '\n') { + if (linelength > max_received_linelength) + max_received_linelength = linelength; if (fputc('\n', fout) == EOF) return END_WERROR; message_size++; body_linecount++; @@ -608,25 +645,37 @@ while ((ch = (RECEIVE_GETC)()) != EOF) { case 0: /* Normal state (previous char written) */ if (ch == '\n') - { body_linecount++; ch_state = 1; } + { + body_linecount++; + if (linelength > max_received_linelength) + max_received_linelength = linelength; + linelength = -1; + ch_state = 1; + } else if (ch == '\r') { ch_state = 2; continue; } break; case 1: /* After written "\n" */ if (ch == '.') { ch_state = 3; continue; } - if (ch != '\n') ch_state = 0; + if (ch != '\n') ch_state = 0; else linelength = -1; break; case 2: body_linecount++; /* After unwritten "\r" */ + if (linelength > max_received_linelength) + max_received_linelength = linelength; if (ch == '\n') - { ch_state = 1; } + { + ch_state = 1; + linelength = -1; + } else { if (message_size++, fputc('\n', fout) == EOF) return END_WERROR; if (ch == '\r') continue; ch_state = 0; + linelength = 0; } break; @@ -634,6 +683,7 @@ while ((ch = (RECEIVE_GETC)()) != EOF) if (ch == '\n') return END_DOT; if (ch == '\r') { ch_state = 4; continue; } message_size++; + linelength++; if (fputc('.', fout) == EOF) return END_WERROR; ch_state = 0; break; @@ -648,6 +698,7 @@ while ((ch = (RECEIVE_GETC)()) != EOF) break; } + linelength++; if (fputc(ch, fout) == EOF) return END_WERROR; if (++message_size > thismessage_size_limit) return END_SIZE; } @@ -701,6 +752,7 @@ read_message_data_smtp(FILE *fout) { int ch_state = 0; register int ch; +register int linelength = 0; while ((ch = (RECEIVE_GETC)()) != EOF) { @@ -722,6 +774,9 @@ while ((ch = (RECEIVE_GETC)()) != EOF) { ch_state = 0; body_linecount++; + if (linelength > max_received_linelength) + max_received_linelength = linelength; + linelength = -1; } else if (ch == '\r') { @@ -732,6 +787,9 @@ while ((ch = (RECEIVE_GETC)()) != EOF) case 2: /* After (unwritten) CR */ body_linecount++; + if (linelength > max_received_linelength) + max_received_linelength = linelength; + linelength = -1; if (ch == '\n') { ch_state = 0; @@ -773,6 +831,7 @@ while ((ch = (RECEIVE_GETC)()) != EOF) next. */ message_size++; + linelength++; if (fout != NULL) { if (fputc(ch, fout) == EOF) return END_WERROR; @@ -1263,6 +1322,7 @@ int header_size = 256; int start, end, domain, size, sptr; int id_resolution; int had_zero = 0; +int prevlines_length = 0; register int ptr = 0; @@ -1343,19 +1403,26 @@ data_fd = -1; spool_name[0] = 0; message_size = 0; warning_count = 0; -received_count = 1; /* For the one we will add */ +received_count = 1; /* For the one we will add */ if (thismessage_size_limit <= 0) thismessage_size_limit = INT_MAX; /* While reading the message, the following counts are computed. */ -message_linecount = body_linecount = body_zerocount = 0; +message_linecount = body_linecount = body_zerocount = + max_received_linelength = 0; #ifdef EXPERIMENTAL_DOMAINKEYS /* Call into DK to set up the context. Check if DK is to be run are carried out inside dk_exim_verify_init(). */ dk_exim_verify_init(); #endif +#ifdef EXPERIMENTAL_DKIM +/* Call into DKIM to set up the context. Check if DKIM is to be run are carried out + inside dk_exim_verify_init(). */ +dkim_exim_verify_init(); +#endif + /* Remember the time of reception. Exim uses time+pid for uniqueness of message ids, and fractions of a second are required. See the comments that precede the @@ -1585,6 +1652,12 @@ for (;;) receive_linecount++; message_linecount++; + /* Keep track of maximum line length */ + + if (ptr - prevlines_length > max_received_linelength) + max_received_linelength = ptr - prevlines_length; + prevlines_length = ptr + 1; + /* Now put in the terminating newline. There is always space for at least two more characters. */ @@ -1813,6 +1886,7 @@ for (;;) next->text = store_get(header_size); ptr = 0; had_zero = 0; + prevlines_length = 0; } /* Continue, starting to read the next header */ /* At this point, we have read all the headers into a data structure in main @@ -2931,6 +3005,9 @@ else #ifdef EXPERIMENTAL_DOMAINKEYS dk_exim_verify_finish(); #endif +#ifdef EXPERIMENTAL_DKIM + dkim_exim_verify_finish(); +#endif #ifdef WITH_CONTENT_SCAN if (acl_smtp_mime != NULL && @@ -3322,7 +3399,8 @@ if ((log_extra_selector & LX_tls_certificate_verified) != 0 && s = string_append(s, &size, &sptr, 2, US" CV=", tls_certificate_verified? "yes":"no"); if ((log_extra_selector & LX_tls_peerdn) != 0 && tls_peerdn != NULL) - s = string_append(s, &size, &sptr, 3, US" DN=\"", tls_peerdn, US"\""); + s = string_append(s, &size, &sptr, 3, US" DN=\"", + string_printing(tls_peerdn), US"\""); #endif if (sender_host_authenticated != NULL) @@ -3436,21 +3514,26 @@ to cause a call to receive_bomb_out() if the log cannot be opened. */ receive_call_bombout = TRUE; -/* Before sending an SMTP response in a TCP/IP session, we check to see if -there is unconsumed input (which there shouldn't be) or if the connection has -gone away. This can be done because the end of a message is always a -synchronization point. If the connection is still present, but there is no -pending input, the result of a select() call will be zero. If, however, the -connection has gone away, or if there is pending input, the result of select() -will be non-zero. The two cases can be distinguished by trying to read the next -input character. Of course, since TCP/IP is asynchronous, there is always a -chance that the connection will vanish between the time of this test and the -sending of the response, but the chance of this happening should be small. +/* Before sending an SMTP response in a TCP/IP session, we check to see if the +connection has gone away. This can only be done if there is no unconsumed input +waiting in the local input buffer. We can test for this by calling +receive_smtp_buffered(). RFC 2920 (pipelining) explicitly allows for additional +input to be sent following the final dot, so the presence of following input is +not an error. + +If the connection is still present, but there is no unread input for the +socket, the result of a select() call will be zero. If, however, the connection +has gone away, or if there is pending input, the result of select() will be +non-zero. The two cases can be distinguished by trying to read the next input +character. If we succeed, we can unread it so that it remains in the local +buffer for handling later. If not, the connection has been lost. -We also check for input that has already been received and is in the local -input buffer (plain SMTP or TLS) by calling receive_smtp_buffered(). */ +Of course, since TCP/IP is asynchronous, there is always a chance that the +connection will vanish between the time of this test and the sending of the +response, but the chance of this happening should be small. */ -if (smtp_input && sender_host_address != NULL && !sender_host_notsocket) +if (smtp_input && sender_host_address != NULL && !sender_host_notsocket && + !receive_smtp_buffered()) { struct timeval tv; fd_set select_check; @@ -3459,47 +3542,39 @@ if (smtp_input && sender_host_address != NULL && !sender_host_notsocket) tv.tv_sec = 0; tv.tv_usec = 0; - if (select(fileno(smtp_in) + 1, &select_check, NULL, NULL, &tv) != 0 || - receive_smtp_buffered()) + if (select(fileno(smtp_in) + 1, &select_check, NULL, NULL, &tv) != 0) { - uschar *msg; - if ((RECEIVE_GETC)() == EOF) + int c = (RECEIVE_GETC)(); + if (c != EOF) (RECEIVE_UNGETC)(c); else { - msg = US"SMTP connection lost after final dot"; - smtp_reply = US""; /* No attempt to send a response */ - } - else - { - msg = US"Synchronization error (data after final dot)"; - smtp_reply = US"550 Synchronization error (data after final dot)"; - } + uschar *msg = US"SMTP connection lost after final dot"; + smtp_reply = US""; /* No attempt to send a response */ + smtp_yield = FALSE; /* Nothing more on this connection */ - /* Overwrite the log line workspace */ + /* Re-use the log line workspace */ - sptr = 0; - s = string_cat(s, &size, &sptr, msg, Ustrlen(msg)); - s = add_host_info_for_log(s, &size, &sptr); - s[sptr] = 0; - log_write(0, LOG_MAIN, "%s", s); + sptr = 0; + s = string_cat(s, &size, &sptr, msg, Ustrlen(msg)); + s = add_host_info_for_log(s, &size, &sptr); + s[sptr] = 0; + log_write(0, LOG_MAIN, "%s", s); - /* We now have to delete the files for this aborted message. */ + /* Delete the files for this aborted message. */ - sprintf(CS spool_name, "%s/input/%s/%s-D", spool_directory, message_subdir, - message_id); - Uunlink(spool_name); + sprintf(CS spool_name, "%s/input/%s/%s-D", spool_directory, + message_subdir, message_id); + Uunlink(spool_name); - sprintf(CS spool_name, "%s/input/%s/%s-H", spool_directory, message_subdir, - message_id); - Uunlink(spool_name); + sprintf(CS spool_name, "%s/input/%s/%s-H", spool_directory, + message_subdir, message_id); + Uunlink(spool_name); - sprintf(CS spool_name, "%s/msglog/%s/%s", spool_directory, message_subdir, - message_id); - Uunlink(spool_name); - - /* Do not accept any more messages on this connection. */ + sprintf(CS spool_name, "%s/msglog/%s/%s", spool_directory, + message_subdir, message_id); + Uunlink(spool_name); - smtp_yield = FALSE; - goto TIDYUP; + goto TIDYUP; + } } }