From: Wietse Venema
This feature is available in Postfix 2.3 and later.
@@ -12654,8 +12654,8 @@ always allows up to 100 junk commands by default. (default: empty)A list of Milter (mail filter) applications for new mail that -arrives via the Postfix smtpd(8) server. See the MILTER_README -document for details.
+arrives via the Postfix smtpd(8) server. Specify space or comma as +separator. See the MILTER_README document for details.This feature is available in Postfix 2.3 and later.
diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5 index e1dae684a..24616c234 100644 --- a/postfix/man/man5/postconf.5 +++ b/postfix/man/man5/postconf.5 @@ -3624,8 +3624,8 @@ A list of Milter (mail filter) applications for new mail that does not arrive via the Postfix \fBsmtpd\fR(8) server. This includes local submission via the \fBsendmail\fR(1) command line, new mail that arrives via the Postfix \fBqmqpd\fR(8) server, and old mail that is re-injected -into the queue with "postsuper -r". See the MILTER_README document -for details. +into the queue with "postsuper -r". Specify space or comma as +separator. See the MILTER_README document for details. .PP This feature is available in Postfix 2.3 and later. .SH notify_classes (default: resource, software) @@ -7893,8 +7893,8 @@ overload to just 1. With Postfix 2.5 and earlier, the SMTP server always allows up to 100 junk commands by default. .SH smtpd_milters (default: empty) A list of Milter (mail filter) applications for new mail that -arrives via the Postfix \fBsmtpd\fR(8) server. See the MILTER_README -document for details. +arrives via the Postfix \fBsmtpd\fR(8) server. Specify space or comma as +separator. See the MILTER_README document for details. .PP This feature is available in Postfix 2.3 and later. .SH smtpd_noop_commands (default: empty) diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index d583da7fc..d712ee499 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -10716,8 +10716,8 @@ smtp_tls_fingerprint_cert_match = %PARAM smtpd_miltersA list of Milter (mail filter) applications for new mail that -arrives via the Postfix smtpd(8) server. See the MILTER_README -document for details.
+arrives via the Postfix smtpd(8) server. Specify space or comma as +separator. See the MILTER_README document for details.This feature is available in Postfix 2.3 and later.
@@ -10727,8 +10727,8 @@ document for details. does not arrive via the Postfix smtpd(8) server. This includes local submission via the sendmail(1) command line, new mail that arrives via the Postfix qmqpd(8) server, and old mail that is re-injected -into the queue with "postsuper -r". See the MILTER_README document -for details. +into the queue with "postsuper -r". Specify space or comma as +separator. See the MILTER_README document for details.This feature is available in Postfix 2.3 and later.
diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index f45300011..a4f7c7230 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20110126" +#define MAIL_RELEASE_DATE "20110130" #define MAIL_VERSION_NUMBER "2.9" #ifdef SNAPSHOT diff --git a/postfix/src/global/smtp_stream.c b/postfix/src/global/smtp_stream.c index b404244dd..7c5d19ad9 100644 --- a/postfix/src/global/smtp_stream.c +++ b/postfix/src/global/smtp_stream.c @@ -90,6 +90,9 @@ /* .ad /* In case of error, a vstream_longjmp() call is performed to the /* context specified with vstream_setjmp(). +/* After write error, further writes to the socket are disabled. +/* This eliminates the need for clumsy code to avoid unwanted +/* I/O while shutting down a TLS engine or closing a VSTREAM. /* Error codes passed along with vstream_longjmp() are: /* .IP SMTP_ERR_EOF /* An I/O error happened, or the peer has disconnected unexpectedly. @@ -163,12 +166,23 @@ static void smtp_timeout_reset(VSTREAM *stream) VSTREAM_CTL_END); } -/* smtp_timeout_detect - test the per-stream timeout flag */ +/* smtp_longjmp - raise an exception */ -static void smtp_timeout_detect(VSTREAM *stream) +static NORETURN smtp_longjmp(VSTREAM *stream, int err, const char *context) { - if (vstream_ftimeout(stream)) - vstream_longjmp(stream, SMTP_ERR_TIME); + + /* + * If we failed to write, don't bang our head against the wall another + * time when closing the stream. In the case of SMTP over TLS, poisoning + * the socket with shutdown() is more robust than purging the VSTREAM + * buffer or replacing the write function pointer with dummy_write(). + */ + if (msg_verbose) + msg_info("%s: %s", context, err == SMTP_ERR_TIME ? "timeout" : "EOF"); + if (vstream_wr_error(stream) + && shutdown(vstream_fileno(stream), SHUT_WR) < 0) + msg_warn("shutdown: %m"); + vstream_longjmp(stream, err); } /* smtp_timeout_setup - configure timeout trap */ @@ -193,16 +207,14 @@ void smtp_flush(VSTREAM *stream) */ smtp_timeout_reset(stream); err = vstream_fflush(stream); - smtp_timeout_detect(stream); /* * See if there was a problem. */ - if (err != 0) { - if (msg_verbose) - msg_info("smtp_flush: EOF"); - vstream_longjmp(stream, SMTP_ERR_EOF); - } + if (vstream_ftimeout(stream)) + smtp_longjmp(stream, SMTP_ERR_TIME, "smtp_flush"); + if (err != 0) + smtp_longjmp(stream, SMTP_ERR_EOF, "smtp_flush"); } /* smtp_vprintf - write one line to SMTP peer */ @@ -218,16 +230,14 @@ void smtp_vprintf(VSTREAM *stream, const char *fmt, va_list ap) vstream_vfprintf(stream, fmt, ap); vstream_fputs("\r\n", stream); err = vstream_ferror(stream); - smtp_timeout_detect(stream); /* * See if there was a problem. */ - if (err != 0) { - if (msg_verbose) - msg_info("smtp_vprintf: EOF"); - vstream_longjmp(stream, SMTP_ERR_EOF); - } + if (vstream_ftimeout(stream)) + smtp_longjmp(stream, SMTP_ERR_TIME, "smtp_vprintf"); + if (err != 0) + smtp_longjmp(stream, SMTP_ERR_EOF, "smtp_vprintf"); } /* smtp_printf - write one line to SMTP peer */ @@ -252,16 +262,14 @@ int smtp_fgetc(VSTREAM *stream) */ smtp_timeout_reset(stream); ch = VSTREAM_GETC(stream); - smtp_timeout_detect(stream); /* * See if there was a problem. */ - if (vstream_feof(stream) || vstream_ferror(stream)) { - if (msg_verbose) - msg_info("smtp_fgetc: EOF"); - vstream_longjmp(stream, SMTP_ERR_EOF); - } + if (vstream_ftimeout(stream)) + smtp_longjmp(stream, SMTP_ERR_TIME, "smtp_fgetc"); + if (vstream_feof(stream) || vstream_ferror(stream)) + smtp_longjmp(stream, SMTP_ERR_EOF, "smtp_fgetc"); return (ch); } @@ -321,17 +329,15 @@ int smtp_get(VSTRING *vp, VSTREAM *stream, ssize_t bound) default: break; } - smtp_timeout_detect(stream); /* * EOF is bad, whether or not it happens in the middle of a record. Don't * allow data that was truncated because of EOF. */ - if (vstream_feof(stream) || vstream_ferror(stream)) { - if (msg_verbose) - msg_info("smtp_get: EOF"); - vstream_longjmp(stream, SMTP_ERR_EOF); - } + if (vstream_ftimeout(stream)) + smtp_longjmp(stream, SMTP_ERR_TIME, "smtp_get"); + if (vstream_feof(stream) || vstream_ferror(stream)) + smtp_longjmp(stream, SMTP_ERR_EOF, "smtp_get"); return (last_char); } @@ -350,16 +356,14 @@ void smtp_fputs(const char *cp, ssize_t todo, VSTREAM *stream) smtp_timeout_reset(stream); err = (vstream_fwrite(stream, cp, todo) != todo || vstream_fputs("\r\n", stream) == VSTREAM_EOF); - smtp_timeout_detect(stream); /* * See if there was a problem. */ - if (err != 0) { - if (msg_verbose) - msg_info("smtp_fputs: EOF"); - vstream_longjmp(stream, SMTP_ERR_EOF); - } + if (vstream_ftimeout(stream)) + smtp_longjmp(stream, SMTP_ERR_TIME, "smtp_fputs"); + if (err != 0) + smtp_longjmp(stream, SMTP_ERR_EOF, "smtp_fputs"); } /* smtp_fwrite - write one string to SMTP peer */ @@ -376,16 +380,14 @@ void smtp_fwrite(const char *cp, ssize_t todo, VSTREAM *stream) */ smtp_timeout_reset(stream); err = (vstream_fwrite(stream, cp, todo) != todo); - smtp_timeout_detect(stream); /* * See if there was a problem. */ - if (err != 0) { - if (msg_verbose) - msg_info("smtp_fwrite: EOF"); - vstream_longjmp(stream, SMTP_ERR_EOF); - } + if (vstream_ftimeout(stream)) + smtp_longjmp(stream, SMTP_ERR_TIME, "smtp_fwrite"); + if (err != 0) + smtp_longjmp(stream, SMTP_ERR_EOF, "smtp_fwrite"); } /* smtp_fputc - write to SMTP peer */ @@ -399,14 +401,12 @@ void smtp_fputc(int ch, VSTREAM *stream) */ smtp_timeout_reset(stream); stat = VSTREAM_PUTC(ch, stream); - smtp_timeout_detect(stream); /* * See if there was a problem. */ - if (stat == VSTREAM_EOF) { - if (msg_verbose) - msg_info("smtp_fputc: EOF"); - vstream_longjmp(stream, SMTP_ERR_EOF); - } + if (vstream_ftimeout(stream)) + smtp_longjmp(stream, SMTP_ERR_TIME, "smtp_fputc"); + if (stat == VSTREAM_EOF) + smtp_longjmp(stream, SMTP_ERR_EOF, "smtp_fputc"); } diff --git a/postfix/src/util/vbuf.c b/postfix/src/util/vbuf.c index 22eaffbc5..c82a4492d 100644 --- a/postfix/src/util/vbuf.c +++ b/postfix/src/util/vbuf.c @@ -42,6 +42,18 @@ /* /* int vbuf_clearerr(bp) /* VBUF *bp; +/* +/* int vbuf_rd_err(bp) +/* VBUF *bp; +/* +/* int vbuf_wr_err(bp) +/* VBUF *bp; +/* +/* int vbuf_rd_timeout(bp) +/* VBUF *bp; +/* +/* int vbuf_wr_timeout(bp) +/* VBUF *bp; /* DESCRIPTION /* This module implements a buffer with read/write primitives that /* automatically handle buffer-empty or buffer-full conditions. @@ -83,12 +95,15 @@ /* /* 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(). +/* 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(). /* +/* The vbuf_rd_mumble() and vbuf_wr_mumble() macros report on +/* read and write error conditions, respectively. +/* /* 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(). @@ -141,7 +156,7 @@ int vbuf_unget(VBUF *bp, int ch) { if ((ch & 0xff) != ch || -bp->cnt >= bp->len) { - bp->flags |= VBUF_FLAG_ERR; + bp->flags |= VBUF_FLAG_RD_ERR; /* This error affects reads! */ return (VBUF_EOF); } else { bp->cnt--; diff --git a/postfix/src/util/vbuf.h b/postfix/src/util/vbuf.h index 6052b4ed7..d81ce0573 100644 --- a/postfix/src/util/vbuf.h +++ b/postfix/src/util/vbuf.h @@ -59,11 +59,20 @@ struct VBUF { /* * Buffer status management. */ -#define VBUF_FLAG_ERR (1<<0) /* some I/O error */ -#define VBUF_FLAG_EOF (1<<1) /* end of data */ -#define VBUF_FLAG_TIMEOUT (1<<2) /* timeout error */ +#define VBUF_FLAG_RD_ERR (1<<0) /* read error */ +#define VBUF_FLAG_WR_ERR (1<<1) /* write error */ +#define VBUF_FLAG_ERR (VBUF_FLAG_RD_ERR | VBUF_FLAG_WR_ERR) +#define VBUF_FLAG_EOF (1<<2) /* end of data */ +#define VBUF_FLAG_RD_TIMEOUT (1<<3) /* read timeout */ +#define VBUF_FLAG_WR_TIMEOUT (1<<4) /* write timeout */ +#define VBUF_FLAG_TIMEOUT (VBUF_FLAG_RD_TIMEOUT | VBUF_FLAG_WR_TIMEOUT) #define VBUF_FLAG_BAD (VBUF_FLAG_ERR | VBUF_FLAG_EOF | VBUF_FLAG_TIMEOUT) -#define VBUF_FLAG_FIXED (1<<3) /* fixed-size buffer */ +#define VBUF_FLAG_FIXED (1<<5) /* fixed-size buffer */ + +#define vbuf_rd_error(v) ((v)->flags & (VBUF_FLAG_RD_ERR | VBUF_FLAG_RD_TIMEOUT)) +#define vbuf_wr_error(v) ((v)->flags & (VBUF_FLAG_WR_ERR | VBUF_FLAG_WR_TIMEOUT)) +#define vbuf_rd_timeout(v) ((v)->flags & VBUF_FLAG_RD_TIMEOUT) +#define vbuf_wr_timeout(v) ((v)->flags & VBUF_FLAG_WR_TIMEOUT) #define vbuf_error(v) ((v)->flags & (VBUF_FLAG_ERR | VBUF_FLAG_TIMEOUT)) #define vbuf_eof(v) ((v)->flags & VBUF_FLAG_EOF) diff --git a/postfix/src/util/vstream.c b/postfix/src/util/vstream.c index 2c45683af..2e1f0a068 100644 --- a/postfix/src/util/vstream.c +++ b/postfix/src/util/vstream.c @@ -123,6 +123,18 @@ /* /* struct timeval vstream_ftimeval(stream) /* VSTREAM *stream; +/* +/* int vstream_rd_error(stream) +/* VSTREAM *stream; +/* +/* int vstream_wr_error(stream) +/* VSTREAM *stream; +/* +/* int vstream_rd_timeout(stream) +/* VSTREAM *stream; +/* +/* int vstream_wr_timeout(stream) +/* VSTREAM *stream; /* DESCRIPTION /* The \fIvstream\fR module implements light-weight buffered I/O /* similar to the standard I/O routines. @@ -381,6 +393,9 @@ /* /* vstream_ftimeval() is like vstream_ftime() but returns more /* detail. +/* +/* vstream_rd_mumble() and vstream_wr_mumble() report on +/* read and write error conditions, respectively. /* DIAGNOSTICS /* Panics: interface violations. Fatal errors: out of memory. /* SEE ALSO @@ -657,7 +672,7 @@ static int vstream_fflush_some(VSTREAM *stream, ssize_t to_flush) if (bp->flags & VSTREAM_FLAG_DEADLINE) { timeout = stream->time_limit.tv_sec + (stream->time_limit.tv_usec > 0); if (timeout <= 0) { - bp->flags |= (VSTREAM_FLAG_ERR | VSTREAM_FLAG_TIMEOUT); + bp->flags |= (VSTREAM_FLAG_WR_ERR | VSTREAM_FLAG_WR_TIMEOUT); errno = ETIMEDOUT; return (VSTREAM_EOF); } @@ -668,18 +683,19 @@ static int vstream_fflush_some(VSTREAM *stream, ssize_t to_flush) } else timeout = stream->timeout; if ((n = stream->write_fn(stream->fd, data, len, timeout, stream->context)) <= 0) { - bp->flags |= VSTREAM_FLAG_ERR; + bp->flags |= VSTREAM_FLAG_WR_ERR; if (errno == ETIMEDOUT) { - bp->flags |= VSTREAM_FLAG_TIMEOUT; + bp->flags |= VSTREAM_FLAG_WR_TIMEOUT; stream->time_limit.tv_sec = stream->time_limit.tv_usec = 0; } return (VSTREAM_EOF); } - if (timeout) + if (timeout) { GETTIMEOFDAY(&stream->iotime); - if (bp->flags & VSTREAM_FLAG_DEADLINE) { - VSTREAM_SUB_TIME(elapsed, stream->iotime, before); - VSTREAM_SUB_TIME(stream->time_limit, stream->time_limit, elapsed); + if (bp->flags & VSTREAM_FLAG_DEADLINE) { + VSTREAM_SUB_TIME(elapsed, stream->iotime, before); + VSTREAM_SUB_TIME(stream->time_limit, stream->time_limit, elapsed); + } } if (msg_verbose > 2 && stream != VSTREAM_ERR && n != to_flush) msg_info("%s: %d flushed %ld/%ld", myname, stream->fd, @@ -808,7 +824,7 @@ static int vstream_buf_get_ready(VBUF *bp) if (bp->flags & VSTREAM_FLAG_DEADLINE) { timeout = stream->time_limit.tv_sec + (stream->time_limit.tv_usec > 0); if (timeout <= 0) { - bp->flags |= (VSTREAM_FLAG_ERR | VSTREAM_FLAG_TIMEOUT); + bp->flags |= (VSTREAM_FLAG_RD_ERR | VSTREAM_FLAG_RD_TIMEOUT); errno = ETIMEDOUT; return (VSTREAM_EOF); } @@ -817,9 +833,9 @@ static int vstream_buf_get_ready(VBUF *bp) timeout = stream->timeout; switch (n = stream->read_fn(stream->fd, bp->data, bp->len, timeout, stream->context)) { case -1: - bp->flags |= VSTREAM_FLAG_ERR; + bp->flags |= VSTREAM_FLAG_RD_ERR; if (errno == ETIMEDOUT) { - bp->flags |= VSTREAM_FLAG_TIMEOUT; + bp->flags |= VSTREAM_FLAG_RD_TIMEOUT; stream->time_limit.tv_sec = stream->time_limit.tv_usec = 0; } return (VSTREAM_EOF); @@ -827,11 +843,12 @@ static int vstream_buf_get_ready(VBUF *bp) bp->flags |= VSTREAM_FLAG_EOF; return (VSTREAM_EOF); default: - if (timeout) + if (timeout) { GETTIMEOFDAY(&stream->iotime); - if (bp->flags & VSTREAM_FLAG_DEADLINE) { - VSTREAM_SUB_TIME(elapsed, stream->iotime, before); - VSTREAM_SUB_TIME(stream->time_limit, stream->time_limit, elapsed); + if (bp->flags & VSTREAM_FLAG_DEADLINE) { + VSTREAM_SUB_TIME(elapsed, stream->iotime, before); + VSTREAM_SUB_TIME(stream->time_limit, stream->time_limit, elapsed); + } } if (msg_verbose > 2) msg_info("%s: fd %d got %ld", myname, stream->fd, (long) n); @@ -940,7 +957,7 @@ static int vstream_buf_space(VBUF *bp, ssize_t want) if ((shortage = (want - bp->cnt)) > 0) { if ((bp->flags & VSTREAM_FLAG_FIXED) || shortage > __MAXINT__(ssize_t) -bp->len - stream->req_bufsize) { - bp->flags |= VSTREAM_FLAG_ERR; + bp->flags |= VSTREAM_FLAG_WR_ERR; } else { incr = VSTREAM_ROUNDUP(shortage, stream->req_bufsize); vstream_buf_alloc(bp, bp->len + incr); @@ -1057,7 +1074,8 @@ off_t vstream_fseek(VSTREAM *stream, off_t offset, int whence) * Update the cached file seek position. */ if ((stream->offset = lseek(stream->fd, offset, whence)) < 0) { - bp->flags |= VSTREAM_FLAG_NSEEK; + if (errno == ESPIPE) + bp->flags |= VSTREAM_FLAG_NSEEK; } else { bp->flags |= VSTREAM_FLAG_SEEK; } diff --git a/postfix/src/util/vstream.h b/postfix/src/util/vstream.h index fb7d05854..20b983e63 100644 --- a/postfix/src/util/vstream.h +++ b/postfix/src/util/vstream.h @@ -66,6 +66,11 @@ extern VSTREAM vstream_fstd[]; /* pre-defined streams */ #define VSTREAM_OUT (&vstream_fstd[1]) #define VSTREAM_ERR (&vstream_fstd[2]) +#define VSTREAM_FLAG_RD_ERR VBUF_FLAG_RD_ERR /* read error */ +#define VSTREAM_FLAG_WR_ERR VBUF_FLAG_WR_ERR /* write error */ +#define VSTREAM_FLAG_RD_TIMEOUT VBUF_FLAG_RD_TIMEOUT /* read timeout */ +#define VSTREAM_FLAG_WR_TIMEOUT VBUF_FLAG_WR_TIMEOUT /* write timeout */ + #define VSTREAM_FLAG_ERR VBUF_FLAG_ERR /* some I/O error */ #define VSTREAM_FLAG_EOF VBUF_FLAG_EOF /* end of file */ #define VSTREAM_FLAG_TIMEOUT VBUF_FLAG_TIMEOUT /* timeout error */ @@ -109,8 +114,12 @@ extern int vstream_fdclose(VSTREAM *); #define vstream_fileno(vp) ((vp)->fd) #define vstream_req_bufsize(vp) ((const ssize_t) ((vp)->req_bufsize)) #define vstream_context(vp) ((vp)->context) +#define vstream_rd_error(vp) vbuf_rd_error(&(vp)->buf) +#define vstream_wr_error(vp) vbuf_wr_error(&(vp)->buf) #define vstream_ferror(vp) vbuf_error(&(vp)->buf) #define vstream_feof(vp) vbuf_eof(&(vp)->buf) +#define vstream_rd_timeout(vp) vbuf_rd_timeout(&(vp)->buf) +#define vstream_wr_timeout(vp) vbuf_wr_timeout(&(vp)->buf) #define vstream_ftimeout(vp) vbuf_timeout(&(vp)->buf) #define vstream_clearerr(vp) vbuf_clearerr(&(vp)->buf) #define VSTREAM_PATH(vp) ((vp)->path ? (const char *) (vp)->path : "unknown_stream")