From: Wietse Venema Date: Sat, 13 May 2000 00:00:00 +0000 (+0000) Subject: snapshot-20000513 X-Git-Tag: v20010228~54 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d298a235f530eb29c1606a09502f8f862d8b83a6;p=thirdparty%2Fpostfix.git snapshot-20000513 --- diff --git a/postfix/HISTORY b/postfix/HISTORY index ac69fdd74..cfdcfd534 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -3942,5 +3942,23 @@ Apologies for any names omitted. Bugfix: Postfix would incorrectly reject domain names with adjacent - characters. File: util/valid_hostname.c. - The 20000505 pipeline tarpit delay flush was wrong and - caused SMTP protocol errors. + Bugfix: the 20000505 pipeline tarpit delay flush was wrong + and caused the client and server to get out of phase. Yuck! + +20000513 + + Feature: VSTREAMs now have the concept of last fill/flush + time, which is needed to prevent timeouts with pipelined + SMTP sessions as detailed in the next item. + + Bugfix: automatic SMTP command/reply flushing to prevent + delays from accumulating within pipelined SMTP sessions. + For example, client-side delays happen when a client does + DNS lookups to replace hostname aliases in a MAIL FROM or + RCPT TO commands; server-side delays happen when an UCE + restriction involves a time-consuming DNS lookup, or when + a server generates a tarpit delay. Files: */*chat.c. + + Portability: define ANAL_CAST for compilation environments + that complain about explicit casts between pointers and + integral types. File: util/sys_defs.h, master/*server.c. diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index c1fce4090..ed88888b8 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -1,3 +1,15 @@ +Major changes with snapshot-20000513 +==================================== + +LaMont Jones and Patrik Rak reported two different scenarios in +which pipelined SMTP sessions could time out forever. Postfix now +automatically flushes delayed SMTP commands/replies to prevent +delays from accumulating and causing timeouts in pipelined SMTP +sessions. For example, client-side delays happen when a client +does DNS lookups to replace hostname aliases in a MAIL FROM or RCPT +TO commands; server-side delays happen when an UCE restriction +involves DNS lookup, or when a server generates a tarpit delay. + Incompatible changes with snapshot-20000507 =========================================== diff --git a/postfix/global/mail_version.h b/postfix/global/mail_version.h index acb928366..918df3b7c 100644 --- a/postfix/global/mail_version.h +++ b/postfix/global/mail_version.h @@ -15,7 +15,7 @@ * Version of this program. */ #define VAR_MAIL_VERSION "mail_version" -#define DEF_MAIL_VERSION "Snapshot-20000511" +#define DEF_MAIL_VERSION "Snapshot-20000513" extern char *var_mail_version; /* LICENSE diff --git a/postfix/lmtp/lmtp_chat.c b/postfix/lmtp/lmtp_chat.c index 9fd23f2bf..62131f8e2 100644 --- a/postfix/lmtp/lmtp_chat.c +++ b/postfix/lmtp/lmtp_chat.c @@ -164,6 +164,15 @@ void lmtp_chat_cmd(LMTP_STATE *state, char *fmt,...) * Send the command to the LMTP server. */ smtp_fputs(STR(state->buffer), LEN(state->buffer), session->stream); + + /* + * Flush unsent output if no I/O happened for a while. This avoids + * timeouts with pipelined LMTP sessions that have lots of client-side + * delays. The code is here so that it applies to the entire + * conversation, never mind that it violates layering. + */ + if (time((time_t *) 0) - vstream_ftime(session->stream) > 10) + vstream_fflush(session->stream); } /* lmtp_chat_resp - read and process LMTP server response */ diff --git a/postfix/local/file.c b/postfix/local/file.c index 75a716e20..20933a2d9 100644 --- a/postfix/local/file.c +++ b/postfix/local/file.c @@ -50,6 +50,7 @@ #include #include #include +#include /* Utility library. */ diff --git a/postfix/master/multi_server.c b/postfix/master/multi_server.c index 3f1986024..f6c80ee7e 100644 --- a/postfix/master/multi_server.c +++ b/postfix/master/multi_server.c @@ -267,7 +267,7 @@ static void multi_server_wakeup(int fd) static void multi_server_accept_local(int unused_event, char *context) { - int listen_fd = (int) context; + int listen_fd = CAST_CHAR_PTR_TO_INT(context); int time_left = -1; int fd; @@ -301,7 +301,7 @@ static void multi_server_accept_local(int unused_event, char *context) static void multi_server_accept_inet(int unused_event, char *context) { - int listen_fd = (int) context; + int listen_fd = CAST_CHAR_PTR_TO_INT(context); int time_left = -1; int fd; @@ -595,7 +595,7 @@ NORETURN multi_server_main(int argc, char **argv, MULTI_SERVER_FN service,...) if (var_idle_limit > 0) event_request_timer(multi_server_timeout, (char *) 0, var_idle_limit); for (fd = MASTER_LISTEN_FD; fd < MASTER_LISTEN_FD + socket_count; fd++) { - event_enable_read(fd, multi_server_accept, (char *) fd); + event_enable_read(fd, multi_server_accept, CAST_INT_TO_CHAR_PTR(fd)); close_on_exec(fd, CLOSE_ON_EXEC); } event_enable_read(MASTER_STATUS_FD, multi_server_abort, (char *) 0); diff --git a/postfix/master/single_server.c b/postfix/master/single_server.c index ca927a605..2227d11e6 100644 --- a/postfix/master/single_server.c +++ b/postfix/master/single_server.c @@ -241,7 +241,7 @@ static void single_server_wakeup(int fd) static void single_server_accept_local(int unused_event, char *context) { - int listen_fd = (int) context; + int listen_fd = CAST_CHAR_PTR_TO_INT(context); int time_left = -1; int fd; @@ -274,7 +274,7 @@ static void single_server_accept_local(int unused_event, char *context) static void single_server_accept_inet(int unused_event, char *context) { - int listen_fd = (int) context; + int listen_fd = CAST_CHAR_PTR_TO_INT(context); int time_left = -1; int fd; @@ -567,7 +567,7 @@ NORETURN single_server_main(int argc, char **argv, SINGLE_SERVER_FN service,...) if (var_idle_limit > 0) event_request_timer(single_server_timeout, (char *) 0, var_idle_limit); for (fd = MASTER_LISTEN_FD; fd < MASTER_LISTEN_FD + socket_count; fd++) { - event_enable_read(fd, single_server_accept, (char *) fd); + event_enable_read(fd, single_server_accept, CAST_INT_TO_CHAR_PTR(fd)); close_on_exec(fd, CLOSE_ON_EXEC); } event_enable_read(MASTER_STATUS_FD, single_server_abort, (char *) 0); diff --git a/postfix/master/trigger_server.c b/postfix/master/trigger_server.c index 98603d18c..8ea502a68 100644 --- a/postfix/master/trigger_server.c +++ b/postfix/master/trigger_server.c @@ -240,7 +240,7 @@ static void trigger_server_wakeup(int fd) static void trigger_server_accept_fifo(int unused_event, char *context) { char *myname = "trigger_server_accept_fifo"; - int listen_fd = (int) context; + int listen_fd = CAST_CHAR_PTR_TO_INT(context); if (trigger_server_lock != 0 && myflock(vstream_fileno(trigger_server_lock), MYFLOCK_NONE) < 0) @@ -263,7 +263,7 @@ static void trigger_server_accept_fifo(int unused_event, char *context) static void trigger_server_accept_local(int unused_event, char *context) { char *myname = "trigger_server_accept_local"; - int listen_fd = (int) context; + int listen_fd = CAST_CHAR_PTR_TO_INT(context); int time_left = 0; int fd; @@ -578,7 +578,7 @@ NORETURN trigger_server_main(int argc, char **argv, TRIGGER_SERVER_FN service,.. if (var_idle_limit > 0) event_request_timer(trigger_server_timeout, (char *) 0, var_idle_limit); for (fd = MASTER_LISTEN_FD; fd < MASTER_LISTEN_FD + socket_count; fd++) { - event_enable_read(fd, trigger_server_accept, (char *) fd); + event_enable_read(fd, trigger_server_accept, CAST_INT_TO_CHAR_PTR(fd)); close_on_exec(fd, CLOSE_ON_EXEC); } event_enable_read(MASTER_STATUS_FD, trigger_server_abort, (char *) 0); diff --git a/postfix/smtp/smtp_chat.c b/postfix/smtp/smtp_chat.c index de11387a1..faf11a1f0 100644 --- a/postfix/smtp/smtp_chat.c +++ b/postfix/smtp/smtp_chat.c @@ -148,6 +148,15 @@ void smtp_chat_cmd(SMTP_STATE *state, char *fmt,...) * Send the command to the SMTP server. */ smtp_fputs(STR(state->buffer), LEN(state->buffer), session->stream); + + /* + * Flush unsent output if no I/O happened for a while. This avoids + * timeouts with pipelined SMTP sessions that have lots of client-side + * delays. The code is here so that it applies to the entire + * conversation, never mind that it violates layering. + */ + if (time((time_t *) 0) - vstream_ftime(session->stream) > 10) + vstream_fflush(session->stream); } /* smtp_chat_resp - read and process SMTP server response */ diff --git a/postfix/smtpd/smtpd_chat.c b/postfix/smtpd/smtpd_chat.c index 8f267eb4c..276e413ef 100644 --- a/postfix/smtpd/smtpd_chat.c +++ b/postfix/smtpd/smtpd_chat.c @@ -56,6 +56,7 @@ #include #include #include +#include #include /* 44BSD stdarg.h uses abort() */ #include @@ -133,7 +134,6 @@ void smtpd_chat_query(SMTPD_STATE *state) void smtpd_chat_reply(SMTPD_STATE *state, char *format,...) { va_list ap; - int slept = 0; va_start(ap, format); vstring_vsprintf(state->buffer, format, ap); @@ -150,19 +150,19 @@ void smtpd_chat_reply(SMTPD_STATE *state, char *format,...) * errors within a session. */ if (state->error_count > var_smtpd_soft_erlim) - sleep(slept = state->error_count); + sleep(state->error_count); else if (STR(state->buffer)[0] == '4' || STR(state->buffer)[0] == '5') - sleep(slept = var_smtpd_err_sleep); + sleep(var_smtpd_err_sleep); smtp_fputs(STR(state->buffer), LEN(state->buffer), state->client); /* - * Flush unsent output AFTER writing instead of before sleeping (so that - * vstream_fflush() flushes the output half of a bidirectional stream). - * Pipelined error responses could result in client-side timeouts. + * Flush unsent output if no I/O happened for a while. This avoids + * timeouts with pipelined SMTP sessions that have lots of server-side + * delays (tarpit delays or DNS lookups for UCE restrictions). */ - if (slept) - (vstream_fflush(state->client)); + if (time((time_t *) 0) - vstream_ftime(state->client) > 10) + vstream_fflush(state->client); } /* print_line - line_wrap callback */ diff --git a/postfix/util/sys_defs.h b/postfix/util/sys_defs.h index 85c56e311..115b21d06 100644 --- a/postfix/util/sys_defs.h +++ b/postfix/util/sys_defs.h @@ -19,16 +19,13 @@ * directory. Adding support for a new system type means updating the * makedefs script, and adding a section below for the new system. */ -#if (defined(__NetBSD_Version__) && __NetBSD_Version__ >= 104250000) -#define ALIAS_DB_MAP "hash:/etc/mail/aliases" /* sendmail 8.10 */ -#endif - #if defined(FREEBSD2) || defined(FREEBSD3) || defined(FREEBSD4) \ || defined(FREEBSD5) \ || defined(BSDI2) || defined(BSDI3) || defined(BSDI4) \ || defined(OPENBSD2) || defined(NETBSD1) #define SUPPORTED #include +#include #define USE_PATHS_H #define USE_FLOCK_LOCK #define HAS_SUN_LEN @@ -36,6 +33,9 @@ #define HAS_DB #define HAS_SA_LEN #define DEF_DB_TYPE "hash" +#if (defined(__NetBSD_Version__) && __NetBSD_Version__ >= 104250000) +#define ALIAS_DB_MAP "hash:/etc/mail/aliases" /* sendmail 8.10 */ +#endif #ifndef ALIAS_DB_MAP #define ALIAS_DB_MAP "hash:/etc/aliases" #endif @@ -56,6 +56,7 @@ #endif #if defined(NETBSD1) +#define ANAL_CAST #define USE_DOT_LOCK #endif @@ -649,6 +650,14 @@ extern int h_errno; #error "unsupported platform" #endif +#ifndef ANAL_CAST +#define CAST_CHAR_PTR_TO_INT(cptr) ((int) (long) (cptr)) +#define CAST_INT_TO_CHAR_PTR(ival) ((char *) (long) (ival)) +#else +#define CAST_CHAR_PTR_TO_INT(cptr) ((int) (cptr)) +#define CAST_INT_TO_CHAR_PTR(ival) ((char *) (ival)) +#endif + #ifdef DUP2_DUPS_CLOSE_ON_EXEC /* dup2_pass_on_exec() can be found in util/sys_compat.c */ extern int dup2_pass_on_exec(int oldd, int newd); diff --git a/postfix/util/vbuf.c b/postfix/util/vbuf.c index 66d706389..1627f706d 100644 --- a/postfix/util/vbuf.c +++ b/postfix/util/vbuf.c @@ -37,6 +37,9 @@ /* int vbuf_eof(bp) /* VBUF *bp; /* +/* int vbuf_timeout(bp) +/* VBUF *bp; +/* /* int vbuf_clearerr(bp) /* VBUF *bp; /* DESCRIPTION @@ -78,9 +81,17 @@ /* number of bytes transferred. A short count is returned in case of /* an error. /* -/* vbuf_err() (vbuf_eof()) is a macro that returns non-zero if an error -/* (end-of-file) condition was detected while reading or writing the -/* buffer. The error status can be reset by calling vbuf_clearerr(). +/* vbuf_timeout() is a macro that returns non-zero if a timeout error +/* condition was detected while reading or writing the buffer. The +/* error status can be reset by calling vbuf_clearerr(). +/* +/* vbuf_err() is a macro that returns non-zero if a non-EOF error +/* (including timeout) condition was detected while reading or writing +/* the buffer. The error status can be reset by calling vbuf_clearerr(). +/* +/* vbuf_eof() is a macro that returns non-zero if an end-of-file +/* condition was detected while reading or writing the buffer. The error +/* status can be reset by calling vbuf_clearerr(). /* APPLICATION CALLBACK SYNOPSIS /* int get_ready(bp) /* VBUF *bp; diff --git a/postfix/util/vstream.c b/postfix/util/vstream.c index 96e94c07c..1f582a458 100644 --- a/postfix/util/vstream.c +++ b/postfix/util/vstream.c @@ -100,6 +100,9 @@ /* void longjmp(stream, val) /* VSTREAM *stream; /* int val; +/* +/* time_t vstream_ftime(stream) +/* VSTREAM *stream; /* DESCRIPTION /* The \fIvstream\fR module implements light-weight buffered I/O /* similar to the standard I/O routines. @@ -277,6 +280,11 @@ /* /* NB: non-local jumps such as vstream_longjmp() are not safe /* for jumping out of any vstream routine. +/* +/* vstream_ftime() returns the time of initialization, the last buffer +/* fill operation, or the last buffer flush operation for the specified +/* stream. This information is maintained only when stream timeouts are +/* enabled. /* DIAGNOSTICS /* Panics: interface violations. Fatal errors: out of memory. /* SEE ALSO @@ -305,6 +313,7 @@ #include #include #include +#include #include #include @@ -522,6 +531,8 @@ static int vstream_fflush_some(VSTREAM *stream, int to_flush) * any. */ for (data = (char *) bp->data, len = to_flush; len > 0; len -= n, data += n) { + if (stream->timeout) + stream->iotime = time((time_t *) 0); if ((n = stream->write_fn(stream->fd, data, len, stream->timeout, stream->context)) <= 0) { bp->flags |= VSTREAM_FLAG_ERR; if (errno == ETIMEDOUT) @@ -648,6 +659,8 @@ static int vstream_buf_get_ready(VBUF *bp) * data as is available right now, whichever is less. Update the cached * file seek position, if any. */ + if (stream->timeout) + stream->iotime = time((time_t *) 0); switch (n = stream->read_fn(stream->fd, bp->data, bp->len, stream->timeout, stream->context)) { case -1: bp->flags |= VSTREAM_FLAG_ERR; @@ -899,6 +912,7 @@ VSTREAM *vstream_fdopen(int fd, int flags) stream->timeout = 0; stream->context = 0; stream->jbuf = 0; + stream->iotime = 0; return (stream); } @@ -1048,6 +1062,8 @@ void vstream_control(VSTREAM *stream, int name,...) stream->waitpid_fn = va_arg(ap, VSTREAM_WAITPID_FN); break; case VSTREAM_CTL_TIMEOUT: + if (stream->timeout == 0) + stream->iotime = time((time_t *) 0); stream->timeout = va_arg(ap, int); break; case VSTREAM_CTL_EXCEPT: diff --git a/postfix/util/vstream.h b/postfix/util/vstream.h index 246cf492d..4ae0b5f04 100644 --- a/postfix/util/vstream.h +++ b/postfix/util/vstream.h @@ -14,6 +14,7 @@ /* * System library. */ +#include #include #include #include @@ -46,6 +47,7 @@ typedef struct VSTREAM { VSTREAM_WAITPID_FN waitpid_fn; /* vstream_popen/close() */ int timeout; /* read/write timout */ jmp_buf *jbuf; /* exception handling */ + time_t iotime; /* time of last fill/flush */ } VSTREAM; extern VSTREAM vstream_fstd[]; /* pre-defined streams */ @@ -93,6 +95,7 @@ extern VSTREAM *vstream_fdopen(int, int); #define vstream_ftimeout(vp) vbuf_timeout(&(vp)->buf) #define vstream_clearerr(vp) vbuf_clearerr(&(vp)->buf) #define VSTREAM_PATH(vp) ((vp)->path ? (vp)->path : "unknown_stream") +#define vstream_ftime(vp) ((vp)->iotime) extern void vstream_control(VSTREAM *, int,...);