Add RCPT error text to unexpected DATA error.
authorPhilip Hazel <ph10@hermes.cam.ac.uk>
Wed, 21 Mar 2007 15:10:39 +0000 (15:10 +0000)
committerPhilip Hazel <ph10@hermes.cam.ac.uk>
Wed, 21 Mar 2007 15:10:39 +0000 (15:10 +0000)
13 files changed:
doc/doc-txt/ChangeLog
src/src/smtp_in.c
test/confs/0558 [new file with mode: 0644]
test/log/0558 [new file with mode: 0644]
test/rejectlog/0558 [new file with mode: 0644]
test/scripts/0000-Basic/0458
test/scripts/0000-Basic/0558 [new file with mode: 0644]
test/stdout/0091
test/stdout/0139
test/stdout/0175
test/stdout/0395
test/stdout/0458
test/stdout/0558 [new file with mode: 0644]

index bbaa500..a341264 100644 (file)
@@ -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
 -----------------
index d30d282..bdac07b 100644 (file)
@@ -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 (file)
index 0000000..18fe649
--- /dev/null
@@ -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 (file)
index 0000000..3f5f92a
--- /dev/null
@@ -0,0 +1,6 @@
+1999-03-02 09:44:33 U=CALLER F=<> rejected RCPT <userx@test.ex>: Default deny message
+1999-03-02 09:44:33 U=CALLER F=<> rejected RCPT <usery@test.ex>: Default deny message
+1999-03-02 09:44:33 U=CALLER F=<> rejected RCPT <userz@test.ex>: Default deny message
+1999-03-02 09:44:33 U=CALLER F=<> rejected RCPT <userx@test.ex>: Default deny message
+1999-03-02 09:44:33 U=CALLER F=<> rejected RCPT <specialusery@test.ex>: Special deny message
+1999-03-02 09:44:33 U=CALLER F=<> rejected RCPT <userz@test.ex>: Default deny message
diff --git a/test/rejectlog/0558 b/test/rejectlog/0558
new file mode 100644 (file)
index 0000000..3f5f92a
--- /dev/null
@@ -0,0 +1,6 @@
+1999-03-02 09:44:33 U=CALLER F=<> rejected RCPT <userx@test.ex>: Default deny message
+1999-03-02 09:44:33 U=CALLER F=<> rejected RCPT <usery@test.ex>: Default deny message
+1999-03-02 09:44:33 U=CALLER F=<> rejected RCPT <userz@test.ex>: Default deny message
+1999-03-02 09:44:33 U=CALLER F=<> rejected RCPT <userx@test.ex>: Default deny message
+1999-03-02 09:44:33 U=CALLER F=<> rejected RCPT <specialusery@test.ex>: Special deny message
+1999-03-02 09:44:33 U=CALLER F=<> rejected RCPT <userz@test.ex>: Default deny message
index c05b4b6..d31709e 100644 (file)
@@ -16,6 +16,8 @@ rcpt to:<userx@test.ex>
 ??? 550
 DATA
 ??? 503
+??? 503
+??? 503
 QUIT
 ??? 221
 ****
@@ -29,6 +31,8 @@ rcpt to:<userx@test.ex>
 ??? 550
 DATA
 ??? 503
+??? 503
+??? 503
 QUIT
 ??? 221
 ****
@@ -45,6 +49,8 @@ rcpt to:<userx@test.ex>
 ??? 503
 DATA
 ??? 503
+??? 503
+??? 503
 QUIT
 ??? 221
 ****
@@ -58,6 +64,8 @@ rcpt to:<userx@test.ex>
 ??? 503
 DATA
 ??? 503
+??? 503
+??? 503
 QUIT
 ??? 221
 ****
@@ -78,6 +86,8 @@ rcpt to:<ph12@test.ex>
 ??? 550
 DATA
 ??? 503
+??? 503
+??? 503
 QUIT
 ??? 221
 ****
@@ -95,6 +105,8 @@ rcpt to:<ph12@test.ex>
 ??? 550
 DATA
 ??? 503
+??? 503
+??? 503
 QUIT
 ??? 221
 ****
@@ -109,6 +121,8 @@ rcpt to:<userx@test.ex>
 ??? 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 (file)
index 0000000..c6aaf7d
--- /dev/null
@@ -0,0 +1,19 @@
+# DATA error after identical RCPT failures
+exim -bs
+ehlo test.ex
+mail from:<>
+rcpt to:<userx@test.ex>
+rcpt to:<usery@test.ex>
+rcpt to:<userz@test.ex>
+data
+quit
+****
+exim -bs
+ehlo test.ex
+mail from:<>
+rcpt to:<userx@test.ex>
+rcpt to:<specialusery@test.ex>
+rcpt to:<userz@test.ex>
+data
+quit
+****
index 10cf780..809bad0 100644 (file)
@@ -19,6 +19,8 @@
 550-Verification failed for <junk@jink.jonk.test.ex>\r
 550-Unrouteable address\r
 550 Sender verify failed\r
+503-All RCPT commands were rejected with this error:\r
+503-Sender verify failed\r
 503 valid RCPT command must precede DATA\r
 500 unrecognized command\r
 221 myhost.test.ex closing connection\r
index 3736dd4..e19fbb7 100644 (file)
 550-This is a very long blacklisting message, continuing for ages and ages and\r
 550-certainly being longer than 128 characters which was a previous limit on\r
 550 the length that Exim was prepared to handle.\r
+503-All RCPT commands were rejected with this error:\r
+503-host is listed in rbl3.test.ex with value 127.0.0.3\r
+503-This is a very long blacklisting message, continuing for ages and ages and\r
+503-certainly being longer than 128 characters which was a previous limit on\r
+503-the length that Exim was prepared to handle.\r
 503 valid RCPT command must precede DATA\r
 500 unrecognized command\r
 500 unrecognized command\r
index 9228084..10cb2c7 100644 (file)
@@ -6,6 +6,8 @@
 220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
 250 OK\r
 550 Sender verify failed\r
+503-All RCPT commands were rejected with this error:\r
+503-Sender verify failed\r
 503 valid RCPT command must precede DATA\r
 500 unrecognized command\r
 221 the.local.host.name closing connection\r
@@ -17,6 +19,8 @@
 220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
 250 OK\r
 550 Sender verify failed\r
+503-All RCPT commands were rejected with this error:\r
+503-Sender verify failed\r
 503 valid RCPT command must precede DATA\r
 500 unrecognized command\r
 221 the.local.host.name closing connection\r
index 03eedb7..21f3443 100644 (file)
@@ -9,6 +9,8 @@ rcpt to: userx
 220 the.local.host.name ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
 250 OK\r
 501 userx: recipient address must contain a domain\r
+503-All RCPT commands were rejected with this error:\r
+503-501 userx: recipient address must contain a domain\r
 503 valid RCPT command must precede DATA\r
 500 unrecognized command\r
 500 unrecognized command\r
index b9b54b4..8bf7195 100644 (file)
@@ -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 (file)
index 0000000..aa3c21c
--- /dev/null
@@ -0,0 +1,30 @@
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250-myhost.test.ex Hello CALLER at test.ex\r
+250-SIZE 52428800\r
+250-PIPELINING\r
+250 HELP\r
+250 OK\r
+550-Default deny message\r
+550 on two lines\r
+550-Default deny message\r
+550 on two lines\r
+550-Default deny message\r
+550 on two lines\r
+503-All RCPT commands were rejected with this error:\r
+503-Default deny message\r
+503-on two lines\r
+503 Valid RCPT command must precede DATA\r
+221 myhost.test.ex closing connection\r
+220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000\r
+250-myhost.test.ex Hello CALLER at test.ex\r
+250-SIZE 52428800\r
+250-PIPELINING\r
+250 HELP\r
+250 OK\r
+550-Default deny message\r
+550 on two lines\r
+550 Special deny message\r
+550-Default deny message\r
+550 on two lines\r
+503 Valid RCPT command must precede DATA\r
+221 myhost.test.ex closing connection\r