for the "virtual:" transport to "/etc/postfix/virtual:".
Symptom reported by Christoph Anton Mitterer.
-20200102
+20100102
Workaround: don't report bogus Berkeley DB close errors as
fatal errors. All operations before close are already error
Bitrot: text about queue ID reuse in the postsuper manpage.
File: postsuper/postsuper.c.
+
+20110328
+
+ Cleanup: don't log warnings about socket shutdown() errors
+ after a connection breaks. Postfix calls shutdown() to avoid
+ unnecessary socket write timeouts. This is only an optimization,
+ and failure is not critical. File: global/smtp_stream.c.
+
+20110411
+
+ Cleanup: postscreen(8) and verify(8) daemons now lock their
+ respective cache file exclusively upon open, to avoid massive
+ cache corruption by unsupported sharing. Files: util/dict.h,
+ util/dict_open.c, verify/verify.c, postscreen/postscreen.c.
+
+20110414
+
+ Bugfix (introduced with Postfix SASL patch 20000314): don't
+ reuse a server Cyrus SASL handle after authentication
+ failure. File: smtpd/smtpd_proto.c.
+
+20110418
+
+ Bugfix (introduced Postfix 2.3 and Postfix 2.7): the Milter
+ client reported some "file too large" errors as temporary
+ errors. Problem reported by Michael Tokarev. Files:
+ milter/milter8.c, cleanup/cleanup_milter.c.
+
+20110420
+
+ Performance: a high load of DSN success notification requests
+ could stall the queue manager. Solution: make the trace
+ client asynchronous, just like the bounce and defer clients.
+ Problem reported by Eduardo M. Stelmaszczyk of terra.com.br.
+ Files: global/abounce.[hc], *qmgr/qmgr_active.c (the
+ qmgr_active.c files are identical).
+
+20110421
+
+ Cleanup: updated abounce warning message, and added a safety
+ timeout to abounce() etc. requests. File: global/abounce.c.
+
+20110426
+
+ Bugfix (introduced in Postfix 1.1, duplicated in Postfix
+ 2.3, unrelated mistake in Postfix 2.7): the local(8) delivery
+ agent ignored table lookup errors in mailbox_command_maps,
+ mailbox_transport_maps, fallback_transport_maps and (while
+ bouncing mail to alias) alias owner lookup. Problem reported
+ by William Ono. Files: local/command.c, local/mailbox.c,
+ local/unknown.c, local/bounce_workaround.c.
While postscreen(8) keeps the zombies away, more smtpd(8) processes remain
available for legitimate clients.
+ postscreen(8) maintains a temporary whitelist for clients that pass its
+ tests; by allowing whitelisted clients to skip tests, postscreen(8)
+ minimizes its impact on legitimate email traffic.
+
The postscreen(8) server is available with Postfix 2.8 and later. To keep
- the implementation simple, postscreen(8) delegates DNS white/backlist
+ the implementation simple, postscreen(8) delegates DNS white/blacklist
lookups to dnsblog(8) server processes, and delegates TLS encryption/
decryption to tlsproxy(8) server processes. This delegation is invisible to
the remote SMTP client, and is not shown in the diagram below.
zombies away from Postfix SMTP server processes, more Postfix SMTP server
processes remain available for legitimate clients.
+postscreen(8) maintains a temporary whitelist for clients that pass its tests;
+by allowing whitelisted clients to skip tests, postscreen(8) minimizes its
+impact on legitimate email traffic.
+
postscreen(8) should not be used on SMTP ports that receive mail from end-user
clients (MUAs). In a typical deployment, postscreen(8) is used on the "port 25"
service, while MUA clients submit mail via the submission service.
G\bGe\ben\bne\ber\bra\bal\bl o\bop\bpe\ber\bra\bat\bti\bio\bon\bn
The postscreen(8) triage process involves a number of tests, in the order as
-described below. Some tests introduce a delay of a few seconds. Once a client
-passes a test, its IP address is whitelisted from 24 hours for simple tests, to
-1 week for complex tests. Whitelisting minimizes the impact of postscreen(8)'s
-tests on legitimate mail clients.
+described below. Some tests introduce a delay of a few seconds. postscreen(8)
+maintains a temporary whitelist for clients that pass its tests; by allowing
+whitelisted clients to skip tests, postscreen(8) minimizes its impact on
+legitimate email traffic.
By default, postscreen(8) hands off all connections to a Postfix SMTP server
process after logging its findings. This mode is useful for non-destructive
O\bOt\bth\bhe\ber\br e\ber\brr\bro\bor\brs\bs
-When an SMTP client hangs up unexpectedly during any tests, postscreen(8) logs
-this as:
+When an SMTP client hangs up unexpectedly, postscreen(8) logs this as:
H\bHA\bAN\bNG\bGU\bUP\bP a\baf\bft\bte\ber\br time f\bfr\bro\bom\bm [address]:port i\bin\bn test name
Translation: the SMTP client at [address]:port disconnected unexpectedly, time
seconds after the start of the test named test name.
+There is no punishment for hanging up. A client that hangs up without sending
+the QUIT command can still pass all postscreen(8) tests.
+
The following errors are reported by the built-in SMTP engine. This engine
never accepts mail, therefore it has per-session limits on the number of
commands and on the session length.
Don't forget Apple's code donation for fetching mail from
IMAP server.
- Simplify postscreen logic: set the noforward flag if the
- client made an unforgivable error. Individual "fail" flags
- are needed only to avoid logging the same offense multiple
- times. Individual "pass" flags are still needed as proof
- that the client didn't skip tests by hanging up early.
+ Simplify postscreen logic. Individual "fail" flags help to
+ avoid repeated testing/logging the same mistake. Individual
+ "pass" flags provide evidence that the client didn't skip
+ tests by hanging up early. The current global "noforward"
+ flag implements the wrong model: instead we need an indicator
+ that a client has passed all tests or that all mistakes
+ were forgiven.
In the SMTP server, check if the connection is closed before
replying to ".", and discard the message if the reply can't
duplication, and may even prevent the delivery of some spam.
http://www.exim.org/lurker/message/20070416.103159.9d5ff0ce.en.html
+ Find out how to reproduce Berkeley DB bogus ENOENT errors.
+ postscreen does not log this with Berkeley DB 1 (FreeBSD
+ 4..8), 4.7.25 (Ubuntu 9.04) and 4.8.24 (Ubuntu 10.04).
+
postconf command-line option to show the compile-time
settings (CCARGS, AUXLIBS) in case binary packages
don't install the makedefs.out file.
- propagate alias owner from pcre, regexp, cidr, texthash,
- etc. databases, i.e. set the owner property at open time;
- it can't be looked up at run-time with fstat(dict->stat_fd)
- because there is no open file. What about *SQL, LDAP, etc.?
- Maybe use the source of the configuration file? We can reuse
- that to enforce root ownership of main/master.cf files.
-
events.c: cache the side effects of file descriptor event
enable/disable operations in user space, and do bulk kernel
updates at event_loop() time. This can eliminate costly
This would apply to postmaster notices and bounce messages
(DKIM), and address verification (BATV).
- As postscreen implements more ESMTP keywords, need to copy
- inter-operability features from smtpd to filter keywords
- and command syntax.
-
Consistency: in postconf.proto make <dt>..</dt> tags bold.
postscreen(8): listen on multiple IP addresses and enforce
keeps the zombies away, more <a href="smtpd.8.html">smtpd(8)</a> processes remain available
for legitimate clients. </p>
+<p> <a href="postscreen.8.html">postscreen(8)</a> maintains a temporary whitelist for clients that
+pass its tests; by allowing whitelisted clients to skip tests,
+<a href="postscreen.8.html">postscreen(8)</a> minimizes its impact on legitimate email traffic.
+</p>
+
<p> The <a href="postscreen.8.html">postscreen(8)</a> server is available with Postfix 2.8 and
later. To keep the implementation simple, <a href="postscreen.8.html">postscreen(8)</a> delegates
-DNS white/backlist lookups to <a href="dnsblog.8.html">dnsblog(8)</a> server processes, and
+DNS white/blacklist lookups to <a href="dnsblog.8.html">dnsblog(8)</a> server processes, and
delegates TLS encryption/decryption to <a href="tlsproxy.8.html">tlsproxy(8)</a> server processes.
This delegation is invisible to the remote SMTP client, and is not
shown in the diagram below. </p>
Postfix SMTP server processes remain available for legitimate
clients. </p>
+<p> <a href="postscreen.8.html">postscreen(8)</a> maintains a temporary whitelist for clients that
+pass its tests; by allowing whitelisted clients to skip tests,
+<a href="postscreen.8.html">postscreen(8)</a> minimizes its impact on legitimate email traffic.
+</p>
+
<p> <a href="postscreen.8.html">postscreen(8)</a> should not be used on SMTP ports that receive
mail from end-user clients (MUAs). In a typical deployment,
<a href="postscreen.8.html">postscreen(8)</a> is used on the "port 25" service, while MUA clients
<p> The <a href="postscreen.8.html">postscreen(8)</a> triage process involves a number of tests,
in the order as described below. Some tests introduce a delay of
-a few seconds. Once a client passes a test, its IP address is
-whitelisted from 24 hours for simple tests, to 1 week for complex
-tests. Whitelisting minimizes the impact of <a href="postscreen.8.html">postscreen(8)</a>'s tests
-on legitimate mail clients. </p>
+a few seconds. <a href="postscreen.8.html">postscreen(8)</a> maintains a temporary whitelist for
+clients that pass its tests; by allowing whitelisted clients to
+skip tests, <a href="postscreen.8.html">postscreen(8)</a> minimizes its impact on legitimate email
+traffic. </p>
<p> By default, <a href="postscreen.8.html">postscreen(8)</a> hands off all connections to a Postfix
SMTP server process after logging its findings. This mode is useful
<h2> <a name="other_error">Other errors</a> </h2>
-<p> When an SMTP client hangs up unexpectedly during any tests,
-<a href="postscreen.8.html">postscreen(8)</a> logs this as: </p>
+<p> When an SMTP client hangs up unexpectedly, <a href="postscreen.8.html">postscreen(8)</a> logs
+this as: </p>
<pre>
<b>HANGUP after</b> <i>time</i> <b>from</b> <i>[address]:port</i> <b>in</b> <i>test name</i>
unexpectedly, <i>time</i> seconds after the start of the
test named <i>test name</i>. </p>
+<p> There is no punishment for hanging up. A client that hangs up
+without sending the QUIT command can still pass all <a href="postscreen.8.html">postscreen(8)</a>
+tests. </p>
+
<!--
<p> While an unexpired penalty is in effect, an SMTP client is not
With Postfix version 2.8 and later, the <b>-e</b> is no
longer needed.
- <b>-h</b> Show parameter values only, not the "<i>name = " label</i>
- <i>that normally precedes the value.</i>
+ <b>-h</b> Show parameter values only, not the "<i>name =</i> " label
+ that normally precedes the value.
<b>-l</b> List the names of all supported mailbox locking
methods. Postfix supports the following methods:
<b>static</b> (read-only)
A table that always returns its name as
- lookup result. For example, <b>static:foobar</b>
+ lookup result. For example, <b><a href="DATABASE_README.html#types">static</a>:foobar</b>
always returns the string <b>foobar</b> as lookup
result.
\b \f \n \r \t \v \<i>ddd</i></tt> (up to three octal digits) and
<tt>\\</tt>. </p>
+<p> Note 1: this feature does not recognize text that requires MIME
+decoding. It inspects raw message content, just like <a href="postconf.5.html#header_checks">header_checks</a>
+and <a href="postconf.5.html#body_checks">body_checks</a>. </p>
+
+<p> Note 2: this feature is disabled with "<a href="postconf.5.html#receive_override_options">receive_override_options</a>
+= <a href="postconf.5.html#no_header_body_checks">no_header_body_checks</a>". </p>
+
<p> Example: </p>
<pre>
\b \f \n \r \t \v \<i>ddd</i></tt> (up to three octal digits) and
<tt>\\</tt>. </p>
+<p> Note 1: this feature does not recognize text that requires MIME
+decoding. It inspects raw message content, just like <a href="postconf.5.html#header_checks">header_checks</a>
+and <a href="postconf.5.html#body_checks">body_checks</a>. </p>
+
+<p> Note 2: this feature is disabled with "<a href="postconf.5.html#receive_override_options">receive_override_options</a>
+= <a href="postconf.5.html#no_header_body_checks">no_header_body_checks</a>". </p>
+
<p> Example: </p>
<pre>
<b><a href="postconf.5.html#smtpd_per_record_deadline">smtpd_per_record_deadline</a> (normal: no, overload: yes)</b>
Change the behavior of the <a href="postconf.5.html#smtpd_timeout">smtpd_timeout</a> time
limit, from a time limit per read or write system
- call, to a time limit to read or write a complete
+ call, to a time limit to send or receive a complete
record (an SMTP command line, SMTP response line,
SMTP message content line, or TLS protocol mes-
sage).
<b><a href="postconf.5.html#smtpd_per_record_deadline">smtpd_per_record_deadline</a> (normal: no, overload: yes)</b>
Change the behavior of the <a href="postconf.5.html#smtpd_timeout">smtpd_timeout</a> time
limit, from a time limit per read or write system
- call, to a time limit to read or write a complete
+ call, to a time limit to send or receive a complete
record (an SMTP command line, SMTP response line,
SMTP message content line, or TLS protocol mes-
sage).
With Postfix version 2.8 and later, the \fB-e\fR is no
longer needed.
.IP \fB-h\fR
-Show parameter values only, not the "\fIname = " label
+Show parameter values only, not the "\fIname = \fR" label
that normally precedes the value.
.IP \fB-l\fR
List the names of all supported mailbox locking methods.
\eb \ef \en \er \et \ev \e\fIddd\fR (up to three octal digits) and
\e\e.
.PP
+Note 1: this feature does not recognize text that requires MIME
+decoding. It inspects raw message content, just like header_checks
+and body_checks.
+.PP
+Note 2: this feature is disabled with "receive_override_options
+= no_header_body_checks".
+.PP
Example:
.PP
.nf
\eb \ef \en \er \et \ev \e\fIddd\fR (up to three octal digits) and
\e\e.
.PP
+Note 1: this feature does not recognize text that requires MIME
+decoding. It inspects raw message content, just like header_checks
+and body_checks.
+.PP
+Note 2: this feature is disabled with "receive_override_options
+= no_header_body_checks".
+.PP
Example:
.PP
.nf
Available in Postfix version 2.9 and later:
.IP "\fBsmtpd_per_record_deadline (normal: no, overload: yes)\fR"
Change the behavior of the smtpd_timeout time limit, from a
-time limit per read or write system call, to a time limit to read
-or write a complete record (an SMTP command line, SMTP response
+time limit per read or write system call, to a time limit to send
+or receive a complete record (an SMTP command line, SMTP response
line, SMTP message content line, or TLS protocol message).
.SH "ADDRESS REWRITING CONTROLS"
.na
Available in Postfix version 2.9 and later:
.IP "\fBsmtpd_per_record_deadline (normal: no, overload: yes)\fR"
Change the behavior of the smtpd_timeout time limit, from a
-time limit per read or write system call, to a time limit to read
-or write a complete record (an SMTP command line, SMTP response
+time limit per read or write system call, to a time limit to send
+or receive a complete record (an SMTP command line, SMTP response
line, SMTP message content line, or TLS protocol message).
.SH "TARPIT CONTROLS"
.na
keeps the zombies away, more smtpd(8) processes remain available
for legitimate clients. </p>
+<p> postscreen(8) maintains a temporary whitelist for clients that
+pass its tests; by allowing whitelisted clients to skip tests,
+postscreen(8) minimizes its impact on legitimate email traffic.
+</p>
+
<p> The postscreen(8) server is available with Postfix 2.8 and
later. To keep the implementation simple, postscreen(8) delegates
-DNS white/backlist lookups to dnsblog(8) server processes, and
+DNS white/blacklist lookups to dnsblog(8) server processes, and
delegates TLS encryption/decryption to tlsproxy(8) server processes.
This delegation is invisible to the remote SMTP client, and is not
shown in the diagram below. </p>
Postfix SMTP server processes remain available for legitimate
clients. </p>
+<p> postscreen(8) maintains a temporary whitelist for clients that
+pass its tests; by allowing whitelisted clients to skip tests,
+postscreen(8) minimizes its impact on legitimate email traffic.
+</p>
+
<p> postscreen(8) should not be used on SMTP ports that receive
mail from end-user clients (MUAs). In a typical deployment,
postscreen(8) is used on the "port 25" service, while MUA clients
<p> The postscreen(8) triage process involves a number of tests,
in the order as described below. Some tests introduce a delay of
-a few seconds. Once a client passes a test, its IP address is
-whitelisted from 24 hours for simple tests, to 1 week for complex
-tests. Whitelisting minimizes the impact of postscreen(8)'s tests
-on legitimate mail clients. </p>
+a few seconds. postscreen(8) maintains a temporary whitelist for
+clients that pass its tests; by allowing whitelisted clients to
+skip tests, postscreen(8) minimizes its impact on legitimate email
+traffic. </p>
<p> By default, postscreen(8) hands off all connections to a Postfix
SMTP server process after logging its findings. This mode is useful
<h2> <a name="other_error">Other errors</a> </h2>
-<p> When an SMTP client hangs up unexpectedly during any tests,
-postscreen(8) logs this as: </p>
+<p> When an SMTP client hangs up unexpectedly, postscreen(8) logs
+this as: </p>
<pre>
<b>HANGUP after</b> <i>time</i> <b>from</b> <i>[address]:port</i> <b>in</b> <i>test name</i>
unexpectedly, <i>time</i> seconds after the start of the
test named <i>test name</i>. </p>
+<p> There is no punishment for hanging up. A client that hangs up
+without sending the QUIT command can still pass all postscreen(8)
+tests. </p>
+
<!--
<p> While an unexpired penalty is in effect, an SMTP client is not
\b \f \n \r \t \v \<i>ddd</i></tt> (up to three octal digits) and
<tt>\\</tt>. </p>
+<p> Note 1: this feature does not recognize text that requires MIME
+decoding. It inspects raw message content, just like header_checks
+and body_checks. </p>
+
+<p> Note 2: this feature is disabled with "receive_override_options
+= no_header_body_checks". </p>
+
<p> Example: </p>
<pre>
\b \f \n \r \t \v \<i>ddd</i></tt> (up to three octal digits) and
<tt>\\</tt>. </p>
+<p> Note 1: this feature does not recognize text that requires MIME
+decoding. It inspects raw message content, just like header_checks
+and body_checks. </p>
+
+<p> Note 2: this feature is disabled with "receive_override_options
+= no_header_body_checks". </p>
+
<p> Example: </p>
<pre>
/*#define msg_verbose 2*/
+static void cleanup_milter_set_error(CLEANUP_STATE *, int);
+
#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
* later.
*/
if ((new_meta_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0) {
- msg_warn("%s: seek file %s: %m", myname, cleanup_path);
- state->errs |= CLEANUP_STAT_WRITE;
+ cleanup_milter_set_error(state, errno);
return;
}
if (state->filter != 0)
* value with the location of the new meta record.
*/
if (vstream_fseek(state->dst, state->append_meta_pt_offset, SEEK_SET) < 0) {
- msg_warn("%s: seek file %s: %m", myname, cleanup_path);
- state->errs |= CLEANUP_STAT_WRITE;
+ cleanup_milter_set_error(state, errno);
return;
}
cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT,
/* NAME
/* abounce 3
/* SUMMARY
-/* asynchronous bounce/defer service client
+/* asynchronous bounce/defer/trace service client
/* SYNOPSIS
/* #include <abounce.h>
/*
/* int dsn_ret;
/* void (*callback)(int status, char *context);
/* char *context;
+/*
+/* void atrace_flush(flags, queue, id, encoding, sender,
+/* dsn_envid, dsn_ret, callback, context)
+/* int flags;
+/* const char *queue;
+/* const char *id;
+/* const char *encoding;
+/* const char *sender;
+/* const char *dsn_envid;
+/* int dsn_ret;
+/* void (*callback)(int status, char *context);
+/* char *context;
/* DESCRIPTION
/* This module implements an asynchronous interface to the
-/* bounce/defer service for submitting sender notifications
+/* bounce/defer/trace service for submitting sender notifications
/* without waiting for completion of the request.
/*
/* abounce_flush() bounces the specified message to
/* the specified sender, including the defer log that was
/* built with defer_append().
/*
+/* atrace_flush() returns the specified message to the specified
+/* sender, including the message delivery record log that was
+/* built with vtrace_append().
+/*
/* Arguments:
/* .IP flags
/* The bitwise OR of zero or more of the following (specify
VSTREAM *fp; /* server I/O handle */
} ABOUNCE;
+ /*
+ * Encapsulate common code.
+ */
+#define ABOUNCE_EVENT_ENABLE(fd, callback, context, timeout) do { \
+ event_enable_read((fd), (callback), (context)); \
+ event_request_timer((callback), (context), (timeout)); \
+ } while (0)
+
+#define ABOUNCE_EVENT_DISABLE(fd, callback, context) do { \
+ event_cancel_timer((callback), (context)); \
+ event_disable_readwrite(fd); \
+ } while (0)
+
+ /*
+ * If we set the reply timeout too short, then we make the problem worse by
+ * increasing overload. With 1000s timeout mail will keep flowing, but there
+ * will be a large number of blocked bounce processes, and some resource is
+ * likely to run out.
+ */
+#define ABOUNCE_TIMEOUT 1000
+
/* abounce_done - deliver status to application and clean up pseudo thread */
static void abounce_done(ABOUNCE *ap, int status)
msg_info("%s: status=deferred (%s failed)", ap->id,
ap->command == BOUNCE_CMD_FLUSH ? "bounce" :
ap->command == BOUNCE_CMD_WARN ? "delay warning" :
+ ap->command == BOUNCE_CMD_VERP ? "verp" :
+ ap->command == BOUNCE_CMD_TRACE ? "trace" :
"whatever");
ap->callback(status, ap->context);
myfree(ap->id);
/* abounce_event - resume pseudo thread after server reply event */
-static void abounce_event(int unused_event, char *context)
+static void abounce_event(int event, char *context)
{
ABOUNCE *ap = (ABOUNCE *) context;
int status;
- event_disable_readwrite(vstream_fileno(ap->fp));
- abounce_done(ap, attr_scan(ap->fp, ATTR_FLAG_STRICT,
- ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status,
- ATTR_TYPE_END) == 1 ? status : -1);
+ ABOUNCE_EVENT_DISABLE(vstream_fileno(ap->fp), abounce_event, context);
+ abounce_done(ap, (event != EVENT_TIME
+ && attr_scan(ap->fp, ATTR_FLAG_STRICT,
+ ATTR_TYPE_INT, MAIL_ATTR_STATUS, &status,
+ ATTR_TYPE_END) == 1) ? status : -1);
}
/* abounce_request_verp - suspend pseudo thread until server reply event */
const char *sender,
const char *dsn_envid,
int dsn_ret,
- const char *verp,
+ const char *verp,
ABOUNCE_FN callback,
char *context)
{
ATTR_TYPE_STR, MAIL_ATTR_VERPDL, verp,
ATTR_TYPE_END) == 0
&& vstream_fflush(ap->fp) == 0) {
- event_enable_read(vstream_fileno(ap->fp), abounce_event, (char *) ap);
+ ABOUNCE_EVENT_ENABLE(vstream_fileno(ap->fp), abounce_event,
+ (char *) ap, ABOUNCE_TIMEOUT);
} else {
abounce_done(ap, -1);
}
{
abounce_request_verp(MAIL_CLASS_PRIVATE, var_bounce_service,
BOUNCE_CMD_VERP, flags, queue, id, encoding,
- sender, dsn_envid, dsn_ret, verp, callback, context);
+ sender, dsn_envid, dsn_ret, verp, callback, context);
}
/* adefer_flush_verp - asynchronous defer flush */
flags |= BOUNCE_FLAG_DELRCPT;
abounce_request_verp(MAIL_CLASS_PRIVATE, var_defer_service,
BOUNCE_CMD_VERP, flags, queue, id, encoding,
- sender, dsn_envid, dsn_ret, verp, callback, context);
+ sender, dsn_envid, dsn_ret, verp, callback, context);
}
/* abounce_request - suspend pseudo thread until server reply event */
ATTR_TYPE_INT, MAIL_ATTR_DSN_RET, dsn_ret,
ATTR_TYPE_END) == 0
&& vstream_fflush(ap->fp) == 0) {
- event_enable_read(vstream_fileno(ap->fp), abounce_event, (char *) ap);
+ ABOUNCE_EVENT_ENABLE(vstream_fileno(ap->fp), abounce_event,
+ (char *) ap, ABOUNCE_TIMEOUT);
} else {
abounce_done(ap, -1);
}
flags, queue, id, encoding, sender, dsn_envid, dsn_ret,
callback, context);
}
+
+/* atrace_flush - asynchronous trace flush */
+
+void atrace_flush(int flags, const char *queue, const char *id,
+ const char *encoding, const char *sender,
+ const char *dsn_envid, int dsn_ret,
+ ABOUNCE_FN callback, char *context)
+{
+ abounce_request(MAIL_CLASS_PRIVATE, var_trace_service, BOUNCE_CMD_TRACE,
+ flags, queue, id, encoding, sender, dsn_envid, dsn_ret,
+ callback, context);
+}
extern void abounce_flush(int, const char *, const char *, const char *, const char *, const char *, int, ABOUNCE_FN, char *);
extern void adefer_flush(int, const char *, const char *, const char *, const char *, const char *, int, ABOUNCE_FN, char *);
extern void adefer_warn(int, const char *, const char *, const char *, const char *, const char *, int, ABOUNCE_FN, char *);
+extern void atrace_flush(int, const char *, const char *, const char *, const char *, const char *, int, ABOUNCE_FN, char *);
extern void abounce_flush_verp(int, const char *, const char *, const char *, const char *, const char *, int, const char *, ABOUNCE_FN, char *);
extern void adefer_flush_verp(int, const char *, const char *, const char *, const char *, const char *, int, const char *, ABOUNCE_FN, char *);
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20110323"
+#define MAIL_RELEASE_DATE "20110501"
#define MAIL_VERSION_NUMBER "2.9"
#ifdef SNAPSHOT
*/
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");
+ if (vstream_wr_error(stream))
+ /* Don't report ECONNRESET (hangup), EINVAL (already shut down), etc. */
+ (void) shutdown(vstream_fileno(stream), SHUT_WR);
vstream_longjmp(stream, err);
}
bounce_workaround.o: ../../include/been_here.h
bounce_workaround.o: ../../include/bounce.h
bounce_workaround.o: ../../include/canon_addr.h
+bounce_workaround.o: ../../include/defer.h
bounce_workaround.o: ../../include/deliver_request.h
bounce_workaround.o: ../../include/delivered_hdr.h
bounce_workaround.o: ../../include/dict.h
#include <strip_addr.h>
#include <stringops.h>
#include <bounce.h>
+#include <defer.h>
#include <split_addr.h>
#include <canon_addr.h>
char *stripped_recipient;
char *owner_alias;
const char *owner_expansion;
+ int saved_dict_errno;
#define FIND_OWNER(lhs, rhs, addr) { \
lhs = concatenate("owner-", addr, (char *) 0); \
rhs = maps_find(alias_maps, lhs, DICT_FLAG_NONE); \
}
+ dict_errno = 0;
FIND_OWNER(owner_alias, owner_expansion, state.msg_attr.rcpt.address);
- if (owner_expansion == 0
+ if ((saved_dict_errno = dict_errno) == 0 && owner_expansion == 0
&& (stripped_recipient = strip_addr(state.msg_attr.rcpt.address,
(char **) 0,
*var_rcpt_delim)) != 0) {
FIND_OWNER(owner_alias, owner_expansion, stripped_recipient);
myfree(stripped_recipient);
}
- if (owner_expansion != 0) {
+ if ((saved_dict_errno = dict_errno) == 0 && owner_expansion != 0) {
canon_owner = canon_addr_internal(vstring_alloc(10),
var_exp_own_alias ?
owner_expansion : owner_alias);
SET_OWNER_ATTR(state.msg_attr, STR(canon_owner), state.level);
}
myfree(owner_alias);
+ if (saved_dict_errno != 0)
+ /* At this point, canon_owner == 0. */
+ return (defer_append(BOUNCE_FLAGS(state.request),
+ BOUNCE_ATTR(state.msg_attr)));
}
/*
transp_maps = maps_create(VAR_MBOX_TRANSP_MAPS, var_mbox_transp_maps,
DICT_FLAG_LOCK | DICT_FLAG_NO_REGSUB);
/* The -1 is a hint for the down-stream deliver_completed() function. */
+ dict_errno = 0;
if (*var_mbox_transp_maps
&& (map_transport = maps_find(transp_maps, state.msg_attr.user,
DICT_FLAG_NONE)) != 0) {
*statusp = deliver_pass(MAIL_CLASS_PRIVATE, map_transport,
state.request, &state.msg_attr.rcpt);
return (YES);
+ } else if (dict_errno != 0) {
+ /* Details in the logfile. */
+ dsb_simple(state.msg_attr.why, "4.3.0", "table lookup failure");
+ *statusp = DEL_STAT_DEFER;
+ return (YES);
}
if (*var_mailbox_transport) {
state.msg_attr.rcpt.offset = -1L;
cmd_maps = maps_create(VAR_MAILBOX_CMD_MAPS, var_mailbox_cmd_maps,
DICT_FLAG_LOCK | DICT_FLAG_PARANOID);
+ dict_errno = 0;
if (*var_mailbox_cmd_maps
&& (map_command = maps_find(cmd_maps, state.msg_attr.user,
DICT_FLAG_NONE)) != 0) {
status = deliver_command(state, usr_attr, map_command);
+ } else if (dict_errno != 0) {
+ /* Details in the logfile. */
+ dsb_simple(state.msg_attr.why, "4.3.0", "table lookup failure");
+ status = DEL_STAT_DEFER;
} else if (*var_mailbox_command) {
status = deliver_command(state, usr_attr, var_mailbox_command);
} else if (*var_home_mailbox && LAST_CHAR(var_home_mailbox) == '/') {
transp_maps = maps_create(VAR_FBCK_TRANSP_MAPS, var_fbck_transp_maps,
DICT_FLAG_LOCK | DICT_FLAG_NO_REGSUB);
/* The -1 is a hint for the down-stream deliver_completed() function. */
+ dict_errno = 0;
if (*var_fbck_transp_maps
&& (map_transport = maps_find(transp_maps, state.msg_attr.user,
DICT_FLAG_NONE)) != 0) {
state.msg_attr.rcpt.offset = -1L;
return (deliver_pass(MAIL_CLASS_PRIVATE, map_transport,
state.request, &state.msg_attr.rcpt));
+ } else if (dict_errno != 0) {
+ /* Details in the logfile. */
+ dsb_simple(state.msg_attr.why, "4.3.0", "table lookup failure");
+ return (DEL_STAT_DEFER);
}
if (*var_fallback_transport) {
state.msg_attr.rcpt.offset = -1L;
int mime_errs = 0;
MILTER_MSG_CONTEXT msg_ctx;
VSTRING *buf;
+ int saved_errno;
switch (milter->state) {
case MILTER8_STAT_ERROR:
if (msg_verbose)
msg_info("%s: message to milter %s", myname, milter->m.name);
if (vstream_fseek(qfile, data_offset, SEEK_SET) < 0) {
+ saved_errno = errno;
msg_warn("%s: vstream_fseek %s: %m", myname, VSTREAM_PATH(qfile));
- return ("450 4.3.0 Queue file write error");
+ /* XXX This should be available from cleanup_strerror.c. */
+ return (saved_errno == EFBIG ?
+ "552 5.3.4 Message file too big" :
+ "451 4.3.0 Queue file write error");
}
msg_ctx.milter = milter;
msg_ctx.eoh_macros = eoh_macros;
*/
static void qmgr_active_done_2_bounce_flush(int, char *);
static void qmgr_active_done_2_generic(QMGR_MESSAGE *);
+static void qmgr_active_done_25_trace_flush(int, char *);
+static void qmgr_active_done_25_generic(QMGR_MESSAGE *);
static void qmgr_active_done_3_defer_flush(int, char *);
static void qmgr_active_done_3_defer_warn(int, char *);
static void qmgr_active_done_3_generic(QMGR_MESSAGE *);
static void qmgr_active_done_2_generic(QMGR_MESSAGE *message)
{
- const char *myname = "qmgr_active_done_2_generic";
const char *path;
struct stat st;
- int status;
/*
* A delivery agent marks a queue file as corrupt by changing its
}
/*
- * As a temporary implementation, synchronously inform the sender of
- * trace information. This will block for 10 seconds when the qmgr FIFO
- * is full.
- *
* XXX With multi-recipient mail, some recipients may have NOTIFY=SUCCESS
* and others not. Depending on what subset of recipients are delivered,
* a trace file may or may not be created. Even when the last partial
*/
if ((message->tflags & (DEL_REQ_FLAG_USR_VRFY | DEL_REQ_FLAG_RECORD))
|| (message->rflags & QMGR_READ_FLAG_NOTIFY_SUCCESS)) {
- status = trace_flush(message->tflags,
- message->queue_name,
- message->queue_id,
- message->encoding,
- message->sender,
- message->dsn_envid,
- message->dsn_ret);
- if (status == 0 && message->tflags_offset)
- qmgr_message_kill_record(message, message->tflags_offset);
- message->flags |= status;
+ atrace_flush(message->tflags,
+ message->queue_name,
+ message->queue_id,
+ message->encoding,
+ message->sender,
+ message->dsn_envid,
+ message->dsn_ret,
+ qmgr_active_done_25_trace_flush,
+ (char *) message);
+ return;
}
+ /*
+ * Asynchronous processing does not reach this point.
+ */
+ qmgr_active_done_25_generic(message);
+}
+
+/* qmgr_active_done_25_trace_flush - continue after atrace_flush() completion */
+
+static void qmgr_active_done_25_trace_flush(int status, char *context)
+{
+ QMGR_MESSAGE *message = (QMGR_MESSAGE *) context;
+
+ /*
+ * Process atrace_flush() status and continue processing.
+ */
+ if (status == 0 && message->tflags_offset)
+ qmgr_message_kill_record(message, message->tflags_offset);
+ message->flags |= status;
+ qmgr_active_done_25_generic(message);
+}
+
+/* qmgr_active_done_25_generic - continue processing */
+
+static void qmgr_active_done_25_generic(QMGR_MESSAGE *message)
+{
+ const char *myname = "qmgr_active_done_25_generic";
+
/*
* If we get to this point we have tried all recipients for this message.
* If the message is too old, try to bounce it.
/* With Postfix version 2.8 and later, the \fB-e\fR is no
/* longer needed.
/* .IP \fB-h\fR
-/* Show parameter values only, not the "\fIname = " label
+/* Show parameter values only, not the "\fIname = \fR" label
/* that normally precedes the value.
/* .IP \fB-l\fR
/* List the names of all supported mailbox locking methods.
*
* Start the cache maintenance pseudo thread after dropping privileges.
*/
-#define PSC_DICT_OPEN_FLAGS (DICT_FLAG_DUP_REPLACE | DICT_FLAG_SYNC_UPDATE)
+#define PSC_DICT_OPEN_FLAGS (DICT_FLAG_DUP_REPLACE | DICT_FLAG_SYNC_UPDATE | \
+ DICT_FLAG_OPEN_LOCK)
if (*var_psc_cache_map)
psc_cache_map =
*/
static void qmgr_active_done_2_bounce_flush(int, char *);
static void qmgr_active_done_2_generic(QMGR_MESSAGE *);
+static void qmgr_active_done_25_trace_flush(int, char *);
+static void qmgr_active_done_25_generic(QMGR_MESSAGE *);
static void qmgr_active_done_3_defer_flush(int, char *);
static void qmgr_active_done_3_defer_warn(int, char *);
static void qmgr_active_done_3_generic(QMGR_MESSAGE *);
static void qmgr_active_done_2_generic(QMGR_MESSAGE *message)
{
- const char *myname = "qmgr_active_done_2_generic";
const char *path;
struct stat st;
- int status;
/*
* A delivery agent marks a queue file as corrupt by changing its
}
/*
- * As a temporary implementation, synchronously inform the sender of
- * trace information. This will block for 10 seconds when the qmgr FIFO
- * is full.
- *
* XXX With multi-recipient mail, some recipients may have NOTIFY=SUCCESS
* and others not. Depending on what subset of recipients are delivered,
* a trace file may or may not be created. Even when the last partial
*/
if ((message->tflags & (DEL_REQ_FLAG_USR_VRFY | DEL_REQ_FLAG_RECORD))
|| (message->rflags & QMGR_READ_FLAG_NOTIFY_SUCCESS)) {
- status = trace_flush(message->tflags,
- message->queue_name,
- message->queue_id,
- message->encoding,
- message->sender,
- message->dsn_envid,
- message->dsn_ret);
- if (status == 0 && message->tflags_offset)
- qmgr_message_kill_record(message, message->tflags_offset);
- message->flags |= status;
+ atrace_flush(message->tflags,
+ message->queue_name,
+ message->queue_id,
+ message->encoding,
+ message->sender,
+ message->dsn_envid,
+ message->dsn_ret,
+ qmgr_active_done_25_trace_flush,
+ (char *) message);
+ return;
}
+ /*
+ * Asynchronous processing does not reach this point.
+ */
+ qmgr_active_done_25_generic(message);
+}
+
+/* qmgr_active_done_25_trace_flush - continue after atrace_flush() completion */
+
+static void qmgr_active_done_25_trace_flush(int status, char *context)
+{
+ QMGR_MESSAGE *message = (QMGR_MESSAGE *) context;
+
+ /*
+ * Process atrace_flush() status and continue processing.
+ */
+ if (status == 0 && message->tflags_offset)
+ qmgr_message_kill_record(message, message->tflags_offset);
+ message->flags |= status;
+ qmgr_active_done_25_generic(message);
+}
+
+/* qmgr_active_done_25_generic - continue processing */
+
+static void qmgr_active_done_25_generic(QMGR_MESSAGE *message)
+{
+ const char *myname = "qmgr_active_done_25_generic";
+
/*
* If we get to this point we have tried all recipients for this message.
* If the message is too old, try to bounce it.
#define SMTPD_FLAG_HANGUP (1<<0) /* 421/521 disconnect */
#define SMTPD_FLAG_ILL_PIPELINING (1<<1) /* inappropriate pipelining */
+#define SMTPD_FLAG_AUTH_USED (1<<2) /* don't reuse SASL state */
#define SMTPD_MASK_MAIL_KEEP ~0 /* keep all after MAIL reset */
return (-1);
}
+ /* Don't reuse the SASL handle after authentication failure. */
+#ifndef XSASL_TYPE_CYRUS
+#define XSASL_TYPE_CYRUS "cyrus"
+#endif
+ if (state->flags & SMTPD_FLAG_AUTH_USED) {
+ smtpd_sasl_deactivate(state);
+#ifdef USE_TLS
+ if (state->tls_context != 0)
+ smtpd_sasl_activate(state, VAR_SMTPD_SASL_TLS_OPTS,
+ var_smtpd_sasl_tls_opts);
+ else
+#endif
+ smtpd_sasl_activate(state, VAR_SMTPD_SASL_OPTS,
+ var_smtpd_sasl_opts);
+ } else if (strcmp(var_smtpd_sasl_type, XSASL_TYPE_CYRUS) == 0) {
+ state->flags |= SMTPD_FLAG_AUTH_USED;
+ }
+
/*
* All authentication failures shall be logged. The 5xx reply code from
* the SASL authentication routine triggers tar-pit delays, which help to
auto_clnt.o: sys_defs.h
auto_clnt.o: vbuf.h
auto_clnt.o: vstream.h
+base32_code.o: base32_code.c
+base32_code.o: base32_code.h
+base32_code.o: msg.h
+base32_code.o: mymalloc.h
+base32_code.o: sys_defs.h
+base32_code.o: vbuf.h
+base32_code.o: vstring.h
base64_code.o: base64_code.c
base64_code.o: base64_code.h
base64_code.o: msg.h
dict_open.o: dict_unix.h
dict_open.o: htable.h
dict_open.o: msg.h
+dict_open.o: myflock.h
dict_open.o: mymalloc.h
dict_open.o: split_at.h
dict_open.o: stringops.h
mask_addr.o: sys_defs.h
match_list.o: argv.h
match_list.o: dict.h
-match_list.o: htable.h
match_list.o: match_list.c
match_list.o: match_list.h
match_list.o: match_ops.h
match_ops.o: argv.h
match_ops.o: cidr_match.h
match_ops.o: dict.h
-match_ops.o: htable.h
-match_ops.o: match_list.h
match_ops.o: match_ops.c
match_ops.o: match_ops.h
match_ops.o: msg.h
vstring_vstream.o: vstring.h
vstring_vstream.o: vstring_vstream.c
vstring_vstream.o: vstring_vstream.h
+watchdog.o: events.h
+watchdog.o: iostuff.h
watchdog.o: killme_after.h
watchdog.o: msg.h
watchdog.o: mymalloc.h
#define DICT_FLAG_FOLD_FIX (1<<14) /* case-fold key with fixed-case map */
#define DICT_FLAG_FOLD_MUL (1<<15) /* case-fold key with multi-case map */
#define DICT_FLAG_FOLD_ANY (DICT_FLAG_FOLD_FIX | DICT_FLAG_FOLD_MUL)
+#define DICT_FLAG_OPEN_LOCK (1<<16) /* open file with exclusive lock */
/* IMPORTANT: Update the dict_mask[] table when the above changes */
/* .IP DICT_FLAG_LOCK
/* With maps where this is appropriate, acquire an exclusive lock
/* before writing, and acquire a shared lock before reading.
+/* .IP DICT_FLAG_OPEN_LOCK
+/* With maps where this is appropriate, acquire an exclusive
+/* lock upon open, and report a fatal run-time error if the
+/* table is already locked.
/* .IP DICT_FLAG_FOLD_FIX
/* With databases whose lookup fields are fixed-case strings,
/* fold the search key to lower case before accessing the
#include <stringops.h>
#include <split_at.h>
#include <htable.h>
+#include <myflock.h>
/*
* lookup table for available map types.
msg_fatal("opening %s:%s %m", dict_type, dict_name);
if (msg_verbose)
msg_info("%s: %s:%s", myname, dict_type, dict_name);
+ /* XXX the choice between wait-for-lock or no-wait is hard-coded. */
+ if (dict->lock_fd >= 0 && (dict_flags & DICT_FLAG_OPEN_LOCK) != 0) {
+ if (dict_flags & DICT_FLAG_LOCK)
+ msg_panic("%s: attempt to open %s:%s with both \"open\" lock and \"access\" lock",
+ myname, dict_type, dict_name);
+ if (myflock(dict->lock_fd, INTERNAL_LOCK,
+ MYFLOCK_OP_EXCLUSIVE | MYFLOCK_OP_NOWAIT) < 0)
+ msg_fatal("%s:%s: unable to get exclusive lock: %m",
+ dict_type, dict_name);
+ }
return (dict);
}
*
* Start the cache cleanup thread after permanently dropping privileges.
*/
-#define VERIFY_DICT_OPEN_FLAGS (DICT_FLAG_DUP_REPLACE | DICT_FLAG_SYNC_UPDATE)
+#define VERIFY_DICT_OPEN_FLAGS (DICT_FLAG_DUP_REPLACE | DICT_FLAG_SYNC_UPDATE \
+ | DICT_FLAG_OPEN_LOCK)
saved_mask = umask(022);
verify_map =