-$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.502 2007/04/13 15:13:47 ph10 Exp $
+$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.503 2007/04/16 10:31:58 ph10 Exp $
Change log file for Exim from version 4.21
-------------------------------------------
PH/43 Yet another patch from the Sieve maintainer.
PH/44 I found a way to check for a TCP/IP connection going away before sending
- the response to the final '.' that terminates a message. At this time,
- there should not be any pending input - the client should be waiting for
- the response. Therefore, a select() can be used: if it shows that the
- input is "ready", there is either input waiting, or the socket has been
- closed. Both cases are errors. (It's a bit more complicated than this
- because of buffering, but that's the essence of it.) Previously, Exim
+ the response to the final '.' that terminates a message, but only in the
+ case where the client has not sent further data following the '.'
+ (unfortunately, this is allowed). However, in many cases there won't be
+ any further data because there won't be any more messages to send. A call
+ to select() can be used: if it shows that the input is "ready", there is
+ either input waiting, or the socket has been closed. An attempt to read
+ the next input character can distinguish the two cases. Previously, Exim
would have sent an OK response which the client would never have see.
- This could lead to message repetition. This fix should cure that.
+ This could lead to message repetition. This fix should cure that, at
+ least in a lot of common cases.
Exim version 4.66
-/* $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.37 2007/04/16 10:31:58 ph10 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
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.
-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(). */
+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.
-if (smtp_input && sender_host_address != NULL && !sender_host_notsocket)
+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 &&
+ !receive_smtp_buffered())
{
struct timeval tv;
fd_set select_check;
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)
- {
- msg = US"SMTP connection lost after final dot";
- smtp_reply = US""; /* No attempt to send a response */
- }
- else
+ int c = (RECEIVE_GETC)();
+ if (c != EOF) (RECEIVE_UNGETC)(c); else
{
- msg = US"Synchronization error (data after final dot)";
- smtp_reply = US"550 Synchronization error (data after final dot)";
- }
-
- /* Overwrite the log line workspace */
+ 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 */
- 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);
+ /* Re-use the log line workspace */
- /* We now have to delete the files for this aborted message. */
+ 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);
- sprintf(CS spool_name, "%s/input/%s/%s-D", spool_directory, message_subdir,
- message_id);
- Uunlink(spool_name);
+ /* Delete the files for this aborted message. */
- 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-D", 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);
+ sprintf(CS spool_name, "%s/input/%s/%s-H", 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;
+ }
}
}