From 2679d413f3f22e7bbc797f4403cc4333bee0073d Mon Sep 17 00:00:00 2001 From: Philip Hazel Date: Wed, 21 Mar 2007 15:10:39 +0000 Subject: [PATCH] Add RCPT error text to unexpected DATA error. --- doc/doc-txt/ChangeLog | 10 +++- src/src/smtp_in.c | 99 ++++++++++++++++++++++++++++-------- test/confs/0558 | 26 ++++++++++ test/log/0558 | 6 +++ test/rejectlog/0558 | 6 +++ test/scripts/0000-Basic/0458 | 14 +++++ test/scripts/0000-Basic/0558 | 19 +++++++ test/stdout/0091 | 2 + test/stdout/0139 | 5 ++ test/stdout/0175 | 4 ++ test/stdout/0395 | 2 + test/stdout/0458 | 34 +++++++++++-- test/stdout/0558 | 30 +++++++++++ 13 files changed, 231 insertions(+), 26 deletions(-) create mode 100644 test/confs/0558 create mode 100644 test/log/0558 create mode 100644 test/rejectlog/0558 create mode 100644 test/scripts/0000-Basic/0558 create mode 100644 test/stdout/0558 diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index bbaa500dc..a34126472 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -1,4 +1,4 @@ -$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.496 2007/03/14 12:15:56 ph10 Exp $ +$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.497 2007/03/21 15:10:39 ph10 Exp $ Change log file for Exim from version 4.21 ------------------------------------------- @@ -182,6 +182,14 @@ PH/40 When Exim receives a message, it writes the login name, uid, and gid of calls itself on a restart). I have changed the code so that it now always uses the Exim user. +PH/41 (Following a suggestion from Tony Finch) If all the RCPT commands in a + message are rejected with the same error (e.g. no authentication or bad + sender address), and a DATA command is nevertheless sent (as can happen + with PIPELINING or a stupid MUA), the error message that was given to the + RCPT commands is included in the rejection of the DATA command. This is + intended to be helpful for MUAs that show only the final error to their + users. + Exim version 4.66 ----------------- diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index d30d28280..bdac07be7 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -1,4 +1,4 @@ -/* $Cambridge: exim/src/src/smtp_in.c,v 1.55 2007/02/20 15:58:02 ph10 Exp $ */ +/* $Cambridge: exim/src/src/smtp_in.c,v 1.56 2007/03/21 15:10:39 ph10 Exp $ */ /************************************************* * Exim - an Internet mail transport agent * @@ -120,12 +120,15 @@ static BOOL helo_seen; static BOOL helo_accept_junk; static BOOL count_nonmail; static BOOL pipelining_advertised; +static BOOL rcpt_smtp_response_same; +static BOOL rcpt_in_progress; static int nonmail_command_count; static int synprot_error_count; static int unknown_command_count; static int sync_cmd_limit; static int smtp_write_error = 0; +static uschar *rcpt_smtp_response; static uschar *smtp_data_buffer; static uschar *smtp_cmd_data; @@ -368,28 +371,41 @@ DEBUG(D_receive) } va_start(ap, format); +if (!string_vformat(big_buffer, big_buffer_size, format, ap)) + { + log_write(0, LOG_MAIN|LOG_PANIC, "string too large in smtp_printf()"); + smtp_closedown(US"Unexpected error"); + exim_exit(EXIT_FAILURE); + } +va_end(ap); + +/* If this is the first output for a (non-batch) RCPT command, see if all RCPTs +have had the same. Note: this code is also present in smtp_respond(). It would +be tidier to have it only in one place, but when it was added, it was easier to +do it that way, so as not to have to mess with the code for the RCPT command, +which sometimes uses smtp_printf() and sometimes smtp_respond(). */ + +if (rcpt_in_progress) + { + if (rcpt_smtp_response == NULL) + rcpt_smtp_response = string_copy(big_buffer); + else if (rcpt_smtp_response_same && + Ustrcmp(rcpt_smtp_response, big_buffer) != 0) + rcpt_smtp_response_same = FALSE; + rcpt_in_progress = FALSE; + } -/* If in a TLS session we have to format the string, and then write it using a -TLS function. */ +/* Now write the string */ #ifdef SUPPORT_TLS if (tls_active >= 0) { - if (!string_vformat(big_buffer, big_buffer_size, format, ap)) - { - log_write(0, LOG_MAIN|LOG_PANIC, "string too large in smtp_printf"); - smtp_closedown(US"Unexpected error"); - exim_exit(EXIT_FAILURE); - } if (tls_write(big_buffer, Ustrlen(big_buffer)) < 0) smtp_write_error = -1; } else #endif -/* Otherwise, just use the standard library function. */ - -if (vfprintf(smtp_out, format, ap) < 0) smtp_write_error = -1; -va_end(ap); +if (fprintf(smtp_out, "%s", big_buffer) < 0) smtp_write_error = -1; } @@ -961,6 +977,9 @@ message_linecount = 0; message_size = -1; acl_added_headers = NULL; queue_only_policy = FALSE; +rcpt_smtp_response = NULL; +rcpt_smtp_response_same = TRUE; +rcpt_in_progress = FALSE; deliver_freeze = FALSE; /* Can be set by ACL */ freeze_tell = freeze_tell_config; /* Can be set by ACL */ fake_response = OK; /* Can be set by ACL */ @@ -1954,6 +1973,24 @@ if (codelen > 4) esclen = codelen - 4; } +/* If this is the first output for a (non-batch) RCPT command, see if all RCPTs +have had the same. Note: this code is also present in smtp_printf(). It would +be tidier to have it only in one place, but when it was added, it was easier to +do it that way, so as not to have to mess with the code for the RCPT command, +which sometimes uses smtp_printf() and sometimes smtp_respond(). */ + +if (rcpt_in_progress) + { + if (rcpt_smtp_response == NULL) + rcpt_smtp_response = string_copy(msg); + else if (rcpt_smtp_response_same && + Ustrcmp(rcpt_smtp_response, msg) != 0) + rcpt_smtp_response_same = FALSE; + rcpt_in_progress = FALSE; + } + +/* Not output the message, splitting it up into multiple lines if necessary. */ + for (;;) { uschar *nl = Ustrchr(msg, '\n'); @@ -2123,6 +2160,9 @@ unless the sender_verify_fail log selector has been turned off. */ if (sender_verified_failed != NULL && !testflag(sender_verified_failed, af_sverify_told)) { + BOOL save_rcpt_in_progress = rcpt_in_progress; + rcpt_in_progress = FALSE; /* So as not to treat these as the error */ + setflag(sender_verified_failed, af_sverify_told); if (rc != FAIL || (log_extra_selector & LX_sender_verify_fail) != 0) @@ -2152,6 +2192,8 @@ if (sender_verified_failed != NULL && "Verification failed for <%s>\n%s", sender_verified_failed->address, sender_verified_failed->user_message)); + + rcpt_in_progress = save_rcpt_in_progress; } /* Sort out text for logging */ @@ -3304,17 +3346,15 @@ while (done <= 0) break; - /* The RCPT command requires an address as an operand. All we do - here is to parse it for syntactic correctness. There may be any number - of RCPT commands, specifying multiple senders. We build them all into - a data structure that is in argc/argv format. The start/end values - given by parse_extract_address are not used, as we keep only the - extracted address. */ + /* The RCPT command requires an address as an operand. There may be any + number of RCPT commands, specifying multiple recipients. We build them all + into a data structure. The start/end values given by parse_extract_address + are not used, as we keep only the extracted address. */ case RCPT_CMD: HAD(SCH_RCPT); rcpt_count++; - was_rcpt = TRUE; + was_rcpt = rcpt_in_progress = TRUE; /* There must be a sender address; if the sender was rejected and pipelining was advertised, we assume the client was pipelining, and do not @@ -3504,14 +3544,29 @@ while (done <= 0) DATA command. The example in the pipelining RFC 2920 uses 554, but I use 503 here - because it is the same whether pipelining is in use or not. */ + because it is the same whether pipelining is in use or not. + + If all the RCPT commands that precede DATA provoked the same error message + (often indicating some kind of system error), it is helpful to include it + with the DATA rejection (an idea suggested by Tony Finch). */ case DATA_CMD: HAD(SCH_DATA); if (!discarded && recipients_count <= 0) { + if (rcpt_smtp_response_same && rcpt_smtp_response != NULL) + { + uschar *code = US"503"; + int len = Ustrlen(rcpt_smtp_response); + smtp_respond(code, 3, FALSE, US"All RCPT commands were rejected with " + "this error:"); + /* Responses from smtp_printf() will have \r\n on the end */ + if (len > 2 && rcpt_smtp_response[len-2] == '\r') + rcpt_smtp_response[len-2] = 0; + smtp_respond(code, 3, FALSE, rcpt_smtp_response); + } if (pipelining_advertised && last_was_rcpt) - smtp_printf("503 valid RCPT command must precede DATA\r\n"); + smtp_printf("503 Valid RCPT command must precede DATA\r\n"); else done = synprot_error(L_smtp_protocol_error, 503, NULL, US"valid RCPT command must precede DATA"); diff --git a/test/confs/0558 b/test/confs/0558 new file mode 100644 index 000000000..18fe64990 --- /dev/null +++ b/test/confs/0558 @@ -0,0 +1,26 @@ +# Exim test configuration 0558 + +exim_path = EXIM_PATH +host_lookup_order = bydns +primary_hostname = myhost.test.ex +rfc1413_query_timeout = 0s +spool_directory = DIR/spool +log_file_path = DIR/spool/log/%slog +gecos_pattern = "" +gecos_name = CALLER_NAME + +# ----- Main settings ----- + +acl_smtp_rcpt = a1 + + +# ----- ACLs ----- + +begin acl + +a1: + deny message = Special deny message + local_parts = ^special + deny message = Default deny message\non two lines + +# End diff --git a/test/log/0558 b/test/log/0558 new file mode 100644 index 000000000..3f5f92a55 --- /dev/null +++ b/test/log/0558 @@ -0,0 +1,6 @@ +1999-03-02 09:44:33 U=CALLER F=<> rejected RCPT : Default deny message +1999-03-02 09:44:33 U=CALLER F=<> rejected RCPT : Default deny message +1999-03-02 09:44:33 U=CALLER F=<> rejected RCPT : Default deny message +1999-03-02 09:44:33 U=CALLER F=<> rejected RCPT : Default deny message +1999-03-02 09:44:33 U=CALLER F=<> rejected RCPT : Special deny message +1999-03-02 09:44:33 U=CALLER F=<> rejected RCPT : Default deny message diff --git a/test/rejectlog/0558 b/test/rejectlog/0558 new file mode 100644 index 000000000..3f5f92a55 --- /dev/null +++ b/test/rejectlog/0558 @@ -0,0 +1,6 @@ +1999-03-02 09:44:33 U=CALLER F=<> rejected RCPT : Default deny message +1999-03-02 09:44:33 U=CALLER F=<> rejected RCPT : Default deny message +1999-03-02 09:44:33 U=CALLER F=<> rejected RCPT : Default deny message +1999-03-02 09:44:33 U=CALLER F=<> rejected RCPT : Default deny message +1999-03-02 09:44:33 U=CALLER F=<> rejected RCPT : Special deny message +1999-03-02 09:44:33 U=CALLER F=<> rejected RCPT : Default deny message diff --git a/test/scripts/0000-Basic/0458 b/test/scripts/0000-Basic/0458 index c05b4b6ff..d31709e58 100644 --- a/test/scripts/0000-Basic/0458 +++ b/test/scripts/0000-Basic/0458 @@ -16,6 +16,8 @@ rcpt to: ??? 550 DATA ??? 503 +??? 503 +??? 503 QUIT ??? 221 **** @@ -29,6 +31,8 @@ rcpt to: ??? 550 DATA ??? 503 +??? 503 +??? 503 QUIT ??? 221 **** @@ -45,6 +49,8 @@ rcpt to: ??? 503 DATA ??? 503 +??? 503 +??? 503 QUIT ??? 221 **** @@ -58,6 +64,8 @@ rcpt to: ??? 503 DATA ??? 503 +??? 503 +??? 503 QUIT ??? 221 **** @@ -78,6 +86,8 @@ rcpt to: ??? 550 DATA ??? 503 +??? 503 +??? 503 QUIT ??? 221 **** @@ -95,6 +105,8 @@ rcpt to: ??? 550 DATA ??? 503 +??? 503 +??? 503 QUIT ??? 221 **** @@ -109,6 +121,8 @@ rcpt to: ??? 503 DATA ??? 503 +??? 503 +??? 503 QUIT ??? 221 **** diff --git a/test/scripts/0000-Basic/0558 b/test/scripts/0000-Basic/0558 new file mode 100644 index 000000000..c6aaf7dd4 --- /dev/null +++ b/test/scripts/0000-Basic/0558 @@ -0,0 +1,19 @@ +# DATA error after identical RCPT failures +exim -bs +ehlo test.ex +mail from:<> +rcpt to: +rcpt to: +rcpt to: +data +quit +**** +exim -bs +ehlo test.ex +mail from:<> +rcpt to: +rcpt to: +rcpt to: +data +quit +**** diff --git a/test/stdout/0091 b/test/stdout/0091 index 10cf780a9..809bad067 100644 --- a/test/stdout/0091 +++ b/test/stdout/0091 @@ -19,6 +19,8 @@ 550-Verification failed for 550-Unrouteable address 550 Sender verify failed +503-All RCPT commands were rejected with this error: +503-Sender verify failed 503 valid RCPT command must precede DATA 500 unrecognized command 221 myhost.test.ex closing connection diff --git a/test/stdout/0139 b/test/stdout/0139 index 3736dd4fd..e19fbb74a 100644 --- a/test/stdout/0139 +++ b/test/stdout/0139 @@ -24,6 +24,11 @@ 550-This is a very long blacklisting message, continuing for ages and ages and 550-certainly being longer than 128 characters which was a previous limit on 550 the length that Exim was prepared to handle. +503-All RCPT commands were rejected with this error: +503-host is listed in rbl3.test.ex with value 127.0.0.3 +503-This is a very long blacklisting message, continuing for ages and ages and +503-certainly being longer than 128 characters which was a previous limit on +503-the length that Exim was prepared to handle. 503 valid RCPT command must precede DATA 500 unrecognized command 500 unrecognized command diff --git a/test/stdout/0175 b/test/stdout/0175 index 92280840f..10cb2c731 100644 --- a/test/stdout/0175 +++ b/test/stdout/0175 @@ -6,6 +6,8 @@ 220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 250 OK 550 Sender verify failed +503-All RCPT commands were rejected with this error: +503-Sender verify failed 503 valid RCPT command must precede DATA 500 unrecognized command 221 the.local.host.name closing connection @@ -17,6 +19,8 @@ 220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 250 OK 550 Sender verify failed +503-All RCPT commands were rejected with this error: +503-Sender verify failed 503 valid RCPT command must precede DATA 500 unrecognized command 221 the.local.host.name closing connection diff --git a/test/stdout/0395 b/test/stdout/0395 index 03eedb7cf..21f34430a 100644 --- a/test/stdout/0395 +++ b/test/stdout/0395 @@ -9,6 +9,8 @@ rcpt to: userx 220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 250 OK 501 userx: recipient address must contain a domain +503-All RCPT commands were rejected with this error: +503-501 userx: recipient address must contain a domain 503 valid RCPT command must precede DATA 500 unrecognized command 500 unrecognized command diff --git a/test/stdout/0458 b/test/stdout/0458 index b9b54b4db..8bf719501 100644 --- a/test/stdout/0458 +++ b/test/stdout/0458 @@ -18,7 +18,11 @@ Connecting to 127.0.0.1 port 1225 ... connected <<< 550 Administrative prohibition >>> DATA ??? 503 -<<< 503 valid RCPT command must precede DATA +<<< 503-All RCPT commands were rejected with this error: +??? 503 +<<< 503-Administrative prohibition +??? 503 +<<< 503 Valid RCPT command must precede DATA >>> QUIT ??? 221 <<< 221 myhost.test.ex closing connection @@ -37,6 +41,10 @@ Connecting to 127.0.0.1 port 1225 ... connected <<< 550 Administrative prohibition >>> DATA ??? 503 +<<< 503-All RCPT commands were rejected with this error: +??? 503 +<<< 503-Administrative prohibition +??? 503 <<< 503 valid RCPT command must precede DATA >>> QUIT ??? 221 @@ -62,7 +70,11 @@ Connecting to 127.0.0.1 port 1225 ... connected <<< 503 sender not yet given >>> DATA ??? 503 -<<< 503 valid RCPT command must precede DATA +<<< 503-All RCPT commands were rejected with this error: +??? 503 +<<< 503-503 sender not yet given +??? 503 +<<< 503 Valid RCPT command must precede DATA >>> QUIT ??? 221 <<< 221 myhost.test.ex closing connection @@ -81,6 +93,10 @@ Connecting to 127.0.0.1 port 1225 ... connected <<< 503 sender not yet given >>> DATA ??? 503 +<<< 503-All RCPT commands were rejected with this error: +??? 503 +<<< 503-503 sender not yet given +??? 503 <<< 503 valid RCPT command must precede DATA >>> QUIT ??? 221 @@ -112,7 +128,11 @@ Connecting to 127.0.0.1 port 1225 ... connected <<< 550 Administrative prohibition >>> DATA ??? 503 -<<< 503 valid RCPT command must precede DATA +<<< 503-All RCPT commands were rejected with this error: +??? 503 +<<< 503-Administrative prohibition +??? 503 +<<< 503 Valid RCPT command must precede DATA >>> QUIT ??? 221 <<< 221 myhost.test.ex closing connection @@ -137,6 +157,10 @@ Connecting to 127.0.0.1 port 1225 ... connected <<< 550 Administrative prohibition >>> DATA ??? 503 +<<< 503-All RCPT commands were rejected with this error: +??? 503 +<<< 503-Administrative prohibition +??? 503 <<< 503 valid RCPT command must precede DATA >>> QUIT ??? 221 @@ -159,6 +183,10 @@ Connecting to 127.0.0.1 port 1225 ... connected <<< 503 sender not yet given >>> DATA ??? 503 +<<< 503-All RCPT commands were rejected with this error: +??? 503 +<<< 503-503 sender not yet given +??? 503 <<< 503 valid RCPT command must precede DATA >>> QUIT ??? 221 diff --git a/test/stdout/0558 b/test/stdout/0558 new file mode 100644 index 000000000..aa3c21c74 --- /dev/null +++ b/test/stdout/0558 @@ -0,0 +1,30 @@ +220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 +250-myhost.test.ex Hello CALLER at test.ex +250-SIZE 52428800 +250-PIPELINING +250 HELP +250 OK +550-Default deny message +550 on two lines +550-Default deny message +550 on two lines +550-Default deny message +550 on two lines +503-All RCPT commands were rejected with this error: +503-Default deny message +503-on two lines +503 Valid RCPT command must precede DATA +221 myhost.test.ex closing connection +220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000 +250-myhost.test.ex Hello CALLER at test.ex +250-SIZE 52428800 +250-PIPELINING +250 HELP +250 OK +550-Default deny message +550 on two lines +550 Special deny message +550-Default deny message +550 on two lines +503 Valid RCPT command must precede DATA +221 myhost.test.ex closing connection -- 2.30.2