and made available as optional patch for earlier releases.
Further refinements have only dimishing returns and can
evolve in the 2.9 release cycle. File: util/vstream.c.
+
+20110128
+
+ Infrastructure: separate VSTREAM flags for read or write
+ errors. Files: util/vbuf.[hc], util/vstream.[hc].
+
+ Cleanup: after write error, the smtp_stream routines now
+ disable further network writes. This eliminates the need
+ for clumsy code to avoid unwanted I/O while shutting down
+ a TLS engine or closing a VSTREAM. File: util/smtp_stream.c.
If you upgrade from Postfix 2.7 or earlier, read RELEASE_NOTES-2.8
before proceeding.
+
+Incompatible changes with snapshot 20110130
+===========================================
+
+The VSTREAM error flags are now split into separate read and write
+error flags. As a result of this change, all programs that use
+VSTREAMs MUST be recompiled.
Don't forget Apple's code donation for fetching mail from
IMAP server.
+ propagate alias owner from pcre, regexp, etc. databases.
+
vstream_peek_len() and vstream_peek_data() to count the
unread data and to access it, respectively. vstream_peek_data()
can access the saved read buffer if a double-buffered stream
IF/ENDIF support for CIDR tables.
- Make postconf aware of magical suffixes (the ones that
- combine with transport names) and show them in "postconf
- -n" output. Making this work with "postconf -d" is trickier.
+ Make postconf aware of local_, smtp_, etc. parameter names
+ that have prefixes derived from mail delivery transport
+ names. Unfortunately, it is wrong to assume that all "unix"
+ master.cf entries are delivery agents (though it may be OK
+ for postconf to peek in master.cf when given a parameter
+ with an unknown prefix). This requires a new main.cf parameter
+ that lists all known mail delivery transport names. postconf
+ can safely ignore names that don't exist in master.cf, and
+ qmgr_transport_create() can safely warn about a name that
+ isn't listed in that new main.cf parameter.
Need a regular expression table to translate address
verification responses into hard/soft/accept reply codes.
does not arrive via the Postfix <a href="smtpd.8.html">smtpd(8)</a> server. This includes local
submission via the <a href="sendmail.1.html">sendmail(1)</a> command line, new mail that arrives
via the Postfix <a href="qmqpd.8.html">qmqpd(8)</a> server, and old mail that is re-injected
-into the queue with "postsuper -r". See the <a href="MILTER_README.html">MILTER_README</a> document
-for details. </p>
+into the queue with "postsuper -r". Specify space or comma as
+separator. See the <a href="MILTER_README.html">MILTER_README</a> document for details. </p>
<p> This feature is available in Postfix 2.3 and later. </p>
(default: empty)</b></DT><DD>
<p> A list of Milter (mail filter) applications for new mail that
-arrives via the Postfix <a href="smtpd.8.html">smtpd(8)</a> server. See the <a href="MILTER_README.html">MILTER_README</a>
-document for details. </p>
+arrives via the Postfix <a href="smtpd.8.html">smtpd(8)</a> server. Specify space or comma as
+separator. See the <a href="MILTER_README.html">MILTER_README</a> document for details. </p>
<p> This feature is available in Postfix 2.3 and later. </p>
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)
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)
%PARAM smtpd_milters
<p> 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. </p>
+arrives via the Postfix smtpd(8) server. Specify space or comma as
+separator. See the MILTER_README document for details. </p>
<p> This feature is available in Postfix 2.3 and later. </p>
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. </p>
+into the queue with "postsuper -r". Specify space or comma as
+separator. See the MILTER_README document for details. </p>
<p> This feature is available in Postfix 2.3 and later. </p>
* 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
/* .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.
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 */
*/
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 */
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 */
*/
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);
}
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);
}
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 */
*/
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 */
*/
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");
}
/*
/* 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.
/*
/* 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().
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--;
/*
* 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)
/*
/* 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.
/*
/* 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
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);
}
} 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,
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);
}
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);
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);
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);
* 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;
}
#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 */
#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")