20050706
- Robustness: the SMTP client now disables caching when it
- is unable to communicate with the scache(8) connection
- caching server, instead of looping forever and not delivering
- mail. File: global/scache_clnt.c. This code is back-ported
- from the Postfix 2.3 snapshot release.
+ Robustness: the SMTP client now disables connection caching
+ when it is unable to communicate with the scache(8) server,
+ instead of looping forever and not delivering mail. File:
+ global/scache_clnt.c. This code is back-ported from the
+ Postfix 2.3 snapshot release.
Portability: after sending a socket, the scache(8) server
now waits for an ACK from the connection cache client before
modules, but that results in too much change, and is not
allowed in the stable release). Files: tls/tls_scache.c,
util/clean_env.c, util/vstring.h, smtpstone/qmqp-source.c.
+
+20050806
+
+ Workaround: accept(2) fails with EPROTO when the client
+ already disconnected (SunOS 5.5.1). File: sane_accept.c.
+
+20050815
+
+ Workaround: old Solaris compilers can't link an archive
+ without globally visible symbols. File: tls/tls_misc.c.
+
+20050922
+
+ Bugfix: the *SQL clients did not uniformly choose the
+ database host from the available pool of servers due to an
+ off-by-one error, so that the "last" available server was
+ not selected. Leandro Santi. Files: dict_mysql.c, dict_pgsql.c.
+
+20050929
+
+ Paranoia: don't ignore garbage in SMTP or LMTP server replies
+ when ESMTP command pipelining is turned on. For example,
+ after sending ".<CR><LF>QUIT<CR><LF>", Postfix could recognize
+ the server's 2XX QUIT reply as a 2XX END-OF-DATA reply after
+ garbage, causing mail to be lost. The SMTP and LMTP clients
+ now report a remote protocol error and defer delivery.
+ Files: smtp/smtp_chat.c, smtp/smtp_trouble.c, lmtp/lmtp_chat.c,
+ lmtp/lmtp_trouble.c.
+
+20051011
+
+ Bugfix: raise the "policy violation" flag when a client
+ request exceeds a concurrency or rate limit. File:
+ smtpd/smtpd.c.
+
+ Bugfix (cut-and-paste error): don't reply with 421 (too
+ many MAIL FROM or RCPT TO commands) when we aren't closing
+ the connection. File: smtpd/smtpd.c.
+
+20051013
+
+ Bugfix: don't do smtpd_end_of_data_restrictions after the
+ transaction failed due to, e.g., a write error. File:
+ smtpd/smtpd.c.
+
+ Cleanup: the SMTP server now enforces the message_size_limit
+ even when the client did not send SIZE information with the
+ MAIL FROM command. This protects before-queue content
+ filters against over-size messages. File: smtpd/smtpd.c.
+
+20051105
+
+ Workaround: the next-hop logical destination information
+ for connection caching was reset only after a good non-TLS
+ connection, so that cached connections to non-TLS backup
+ servers could suck away traffic from TLS primary servers
+ (the Postfix SMTP client cannot cache an open TLS connection).
+ Found during code review. Fixing this requires more change
+ than is allowed in a stable release. File: smtp/smtp_connect.c.
+
+20051108
+
+ Bugfix: two messages could get the same message ID due to
+ a race condition. This time window was increased when queue
+ file creation was postponed from MAIL FROM until the first
+ accepted RCPT TO. The window is closed again. Found by
+ Victor. Files: global/mail_stream.c, global/mail_queue.c,
+ cleanup/cleanup_message.c. This code is back-ported from
+ the Postfix 2.3 snapshot release.
+
+20051119
+
+ Bugfix: the queue manager did not write a per-recipient
+ defer logfile record when the delivery agent crashed after
+ the initial handshake with the queue manager, and before
+ reporting the delivery status to the queue manager. Files:
+ *qmgr/qmgr_deliver.c.
+
+20051126
+
+ Log warning when REDIRECT, FILTER, HOLD and DISCARD are
+ used in smtpd_etrn_restrictions. File: smtpd/smtpd_check.c.
+
+20051128
+
+ Bugfix: moved code around from one place to another to make
+ REDIRECT, FILTER, HOLD and DISCARD access(5) table actions
+ work in smtpd_end_of_data_restrictions. PREPEND will not
+ be fixed; it must be specified before the message content
+ is received. Files: smtpd/smtpd.c, smtpd/smtpd_check.c,
+ cleanup/cleanup_extracted.c, pickup/pickup.c.
/etc/postfix/master.cf:
:10026 inet n - n - - smtpd
- -o receive_override_options=no_address_mapping
+ -o receive_override_options=no_address_mappings
Note: do not specify whitespace around the "=" here.
/etc/postfix/master.cf:
:10026 inet n - n - - smtpd
- -o receive_override_options=no_address_mapping
+ -o receive_override_options=no_address_mappings
Note: do not specify whitespace around the "=" here.
/etc/postfix/master.cf:
:10026 inet n - n - - smtpd
- -o receive_override_options=no_address_mapping
+ -o receive_override_options=no_address_mappings
Note: do not specify whitespace around the "=" here.
/etc/postfix/master.cf:
:10026 inet n - n - - smtpd
- -o receive_override_options=no_address_mapping
+ -o receive_override_options=no_address_mappings
Note: do not specify whitespace around the "=" here.
local(8) delivery agent's mailbox_command_maps feature:
/etc/postfix/main.cf:
- mailbox_command_maps = /etc/postfix/mailbox_commands
+ mailbox_command_maps = hash:/etc/postfix/mailbox_commands
/etc/postfix/mailbox_commands:
you /path/to/maildrop -d ${USER}
issued by these CAs, append the root certificate to $smtpd_tls_CAfile or
install it in the $smtpd_tls_CApath directory. When you configure trust in a
root CA, it is not necessary to explicitly trust intermediary CAs signed by the
-root CA, unless $smtpd_tls_verify_depth is less than the number of CAs in the
-certificate chain for the clients of interest. With a verify depth of 1 you can
-only verify certificates directly signed by a trusted CA, and all trusted
+root CA, unless $smtpd_tls_ccert_verifydepth is less than the number of CAs in
+the certificate chain for the clients of interest. With a verify depth of 1 you
+can only verify certificates directly signed by a trusted CA, and all trusted
intermediary CAs need to be configured explicitly. With a verify depth of 2 you
can verify clients signed by a root CA or a direct intermediary CA (so long as
the client is correctly configured to supply its intermediate CA certificate).
jail.
When you configure Postfix to request client certificates (by setting
-$smtpd_tls_asck_ccert = yes), any certificates in $smtpd_tls_CAfile are sent to
+$smtpd_tls_ask_ccert = yes), any certificates in $smtpd_tls_CAfile are sent to
the client, in order to allow it to choose an identity signed by a CA you
trust. If no $smtpd_tls_CAfile is specified, no preferred CA list is sent, and
the client is free to choose an identity signed by any CA. Many clients use a
-# ACCESS(5) ACCESS(5)
+# ACCESS(5) ACCESS(5)
#
# NAME
# access - Postfix access table format
# Note: this action does not support multi-line mes-
# sage headers.
#
+# Note: this action must be used before the message
+# content is received; it cannot be used in
+# smtpd_end_of_data_restrictions.
+#
# This feature is available in Postfix 2.1 and later.
#
# REDIRECT user@domain
# user@ and domain constituent parts, nor is user+foo broken
# up into user and foo.
#
-# Patterns are applied in the order as specified in the
-# table, until a pattern is found that matches the search
+# Patterns are applied in the order as specified in the ta-
+# ble, until a pattern is found that matches the search
# string.
#
# Actions are the same as with indexed file lookups, with
# TCP-BASED TABLES
# This section describes how the table lookups change when
# lookups are directed to a TCP-based server. For a descrip-
-# tion of the TCP client/server lookup protocol, see
-# tcp_table(5). This feature is not available up to and
-# including Postfix version 2.2.
+# tion of the TCP client/server lookup protocol, see tcp_ta-
+# ble(5). This feature is not available up to and including
+# Postfix version 2.2.
#
# Each lookup operation uses the entire query string once.
# Depending on the application, that string is an entire
# P.O. Box 704
# Yorktown Heights, NY 10598, USA
#
-# ACCESS(5)
+# ACCESS(5)
<pre>
/etc/postfix/master.cf:
:10026 inet n - n - - smtpd
- -o <a href="postconf.5.html#receive_override_options">receive_override_options</a>=no_address_mapping
+ -o <a href="postconf.5.html#receive_override_options">receive_override_options</a>=<a href="postconf.5.html#no_address_mappings">no_address_mappings</a>
</pre>
</blockquote>
<pre>
/etc/postfix/master.cf:
:10026 inet n - n - - smtpd
- -o <a href="postconf.5.html#receive_override_options">receive_override_options</a>=no_address_mapping
+ -o <a href="postconf.5.html#receive_override_options">receive_override_options</a>=<a href="postconf.5.html#no_address_mappings">no_address_mappings</a>
</pre>
</blockquote>
<pre>
/etc/postfix/master.cf:
:10026 inet n - n - - smtpd
- -o <a href="postconf.5.html#receive_override_options">receive_override_options</a>=no_address_mapping
+ -o <a href="postconf.5.html#receive_override_options">receive_override_options</a>=<a href="postconf.5.html#no_address_mappings">no_address_mappings</a>
</pre>
</blockquote>
<pre>
/etc/postfix/master.cf:
:10026 inet n - n - - smtpd
- -o <a href="postconf.5.html#receive_override_options">receive_override_options</a>=no_address_mapping
+ -o <a href="postconf.5.html#receive_override_options">receive_override_options</a>=<a href="postconf.5.html#no_address_mappings">no_address_mappings</a>
</pre>
</blockquote>
<blockquote>
<pre>
/etc/postfix/main.cf:
- <a href="postconf.5.html#mailbox_command_maps">mailbox_command_maps</a> = /etc/postfix/mailbox_commands
+ <a href="postconf.5.html#mailbox_command_maps">mailbox_command_maps</a> = hash:/etc/postfix/mailbox_commands
/etc/postfix/mailbox_commands:
you /path/to/maildrop -d ${USER}
certificates issued by these CAs, append the root certificate to
$<a href="postconf.5.html#smtpd_tls_CAfile">smtpd_tls_CAfile</a> or install it in the $<a href="postconf.5.html#smtpd_tls_CApath">smtpd_tls_CApath</a> directory. When
you configure trust in a root CA, it is not necessary to explicitly trust
-intermediary CAs signed by the root CA, unless $smtpd_tls_verify_depth
+intermediary CAs signed by the root CA, unless $<a href="postconf.5.html#smtpd_tls_ccert_verifydepth">smtpd_tls_ccert_verifydepth</a>
is less than the number of CAs in the certificate chain for the clients
of interest. With a verify depth of 1 you can only verify certificates
directly signed by a trusted CA, and all trusted intermediary CAs need to
accessible inside the optional chroot jail. </p>
<p> When you configure Postfix to request client certificates (by
-setting $smtpd_tls_asck_ccert = yes), any certificates in
+setting $<a href="postconf.5.html#smtpd_tls_ask_ccert">smtpd_tls_ask_ccert</a> = yes), any certificates in
$<a href="postconf.5.html#smtpd_tls_CAfile">smtpd_tls_CAfile</a> are sent to the client, in order to allow it to
choose an identity signed by a CA you trust. If no $<a href="postconf.5.html#smtpd_tls_CAfile">smtpd_tls_CAfile</a>
is specified, no preferred CA list is sent, and the client is free
Note: this action does not support multi-line mes-
sage headers.
+ Note: this action must be used before the message
+ content is received; it cannot be used in
+ <b><a href="postconf.5.html#smtpd_end_of_data_restrictions">smtpd_end_of_data_restrictions</a></b>.
+
This feature is available in Postfix 2.1 and later.
<b>REDIRECT</b> <i>user@domain</i>
.sp
Note: this action does not support multi-line message headers.
.sp
+Note: this action must be used before the message content
+is received; it cannot be used in \fBsmtpd_end_of_data_restrictions\fR.
+.sp
This feature is available in Postfix 2.1 and later.
.IP "\fBREDIRECT \fIuser@domain\fR"
After the message is queued, send the message to the specified
<pre>
/etc/postfix/master.cf:
:10026 inet n - n - - smtpd
- -o receive_override_options=no_address_mapping
+ -o receive_override_options=no_address_mappings
</pre>
</blockquote>
<pre>
/etc/postfix/master.cf:
:10026 inet n - n - - smtpd
- -o receive_override_options=no_address_mapping
+ -o receive_override_options=no_address_mappings
</pre>
</blockquote>
<pre>
/etc/postfix/master.cf:
:10026 inet n - n - - smtpd
- -o receive_override_options=no_address_mapping
+ -o receive_override_options=no_address_mappings
</pre>
</blockquote>
<pre>
/etc/postfix/master.cf:
:10026 inet n - n - - smtpd
- -o receive_override_options=no_address_mapping
+ -o receive_override_options=no_address_mappings
</pre>
</blockquote>
<blockquote>
<pre>
/etc/postfix/main.cf:
- mailbox_command_maps = /etc/postfix/mailbox_commands
+ mailbox_command_maps = hash:/etc/postfix/mailbox_commands
/etc/postfix/mailbox_commands:
you /path/to/maildrop -d ${USER}
certificates issued by these CAs, append the root certificate to
$smtpd_tls_CAfile or install it in the $smtpd_tls_CApath directory. When
you configure trust in a root CA, it is not necessary to explicitly trust
-intermediary CAs signed by the root CA, unless $smtpd_tls_verify_depth
+intermediary CAs signed by the root CA, unless $smtpd_tls_ccert_verifydepth
is less than the number of CAs in the certificate chain for the clients
of interest. With a verify depth of 1 you can only verify certificates
directly signed by a trusted CA, and all trusted intermediary CAs need to
accessible inside the optional chroot jail. </p>
<p> When you configure Postfix to request client certificates (by
-setting $smtpd_tls_asck_ccert = yes), any certificates in
+setting $smtpd_tls_ask_ccert = yes), any certificates in
$smtpd_tls_CAfile are sent to the client, in order to allow it to
choose an identity signed by a CA you trust. If no $smtpd_tls_CAfile
is specified, no preferred CA list is sent, and the client is free
# .sp
# Note: this action does not support multi-line message headers.
# .sp
+# Note: this action must be used before the message content
+# is received; it cannot be used in \fBsmtpd_end_of_data_restrictions\fR.
+# .sp
# This feature is available in Postfix 2.1 and later.
# .IP "\fBREDIRECT \fIuser@domain\fR"
# After the message is queued, send the message to the specified
const char *buf, int len)
{
const char *encoding;
- const char generated_by_cleanup[] = {
- REC_TYPE_FILT, REC_TYPE_RDR, REC_TYPE_ATTR,
- REC_TYPE_RRTO, REC_TYPE_ERTO, 0,
- };
+ int extra_opts;
if (msg_verbose)
msg_info("extracted envelope %c %.*s", type, len, buf);
+ if (type == REC_TYPE_FLGS) {
+ /* Not part of queue file format. */
+ extra_opts = atol(buf);
+ if (extra_opts & ~CLEANUP_FLAG_MASK_EXTRA)
+ msg_warn("%s: ignoring bad extra flags: 0x%x",
+ state->queue_id, extra_opts);
+ else
+ state->flags |= extra_opts;
+ return;
+ }
+
if (strchr(REC_TYPE_EXTRACT, type) == 0) {
msg_warn("%s: message rejected: "
"unexpected record type %d in extracted envelope",
if (state->flags & CLEANUP_FLAG_INRCPT)
/* Tell qmgr that recipient records are mixed with other information. */
state->qmgr_opts |= QMGR_READ_FLAG_MIXED_RCPT_OTHER;
- if (strchr(generated_by_cleanup, type) != 0) {
- /* Use our own header/body info instead. */
- return;
- } else {
- /* Pass on other non-recipient record. */
- cleanup_out(state, type, buf, len);
- return;
- }
+ cleanup_out(state, type, buf, len);
+ return;
}
/* cleanup_extracted_finish - process one extracted envelope record */
char time_stamp[1024]; /* XXX locale dependent? */
struct tm *tp;
TOK822 *token;
+ time_t tv;
/*
* Add a missing (Resent-)Message-Id: header. The message ID gives the
*
* XXX It is the queue ID non-inode bits that prevent messages from getting
* the same Message-Id within the same second.
+ *
+ * XXX An arbitrary amount of time may pass between the start of the mail
+ * transaction and the creation of a queue file. Since we guarantee queue
+ * ID uniqueness only within a second, we must ensure that the time in
+ * the message ID matches the queue ID creation time, as long as we use
+ * the queue ID in the message ID.
*/
if ((state->headers_seen & (1 << (state->resent[0] ?
HDR_RESENT_MESSAGE_ID : HDR_MESSAGE_ID))) == 0) {
- tp = gmtime(&state->time);
+ tv = state->handle->ctime.tv_sec;
+ tp = gmtime(&tv);
strftime(time_stamp, sizeof(time_stamp), "%Y%m%d%H%M%S", tp);
cleanup_out_format(state, REC_TYPE_NORM, "%sMessage-Id: <%s.%s@%s>",
state->resent, time_stamp, state->queue_id, var_myhostname);
#ifdef RESOLVE_H_NEEDS_STDIO_H
#include <stdio.h>
#endif
+#ifdef RESOLVE_H_NEEDS_NAMESER8_COMPAT_H
+#include <nameser8_compat.h>
+#endif
#include <resolv.h>
/*
}
if (count) {
- /*
- * Calling myrand() can deplete the random pool.
- * Don't rely on the optimizer to weed out the call
- * when count == 1.
- */
- idx = (count > 1) ? 1 + (count - 1) * (double) myrand() / RAND_MAX : 1;
+ idx = (count > 1) ?
+ 1 + count * (double) myrand() / (1.0 + RAND_MAX) : 1;
for (i = 0; i < PLDB->len_hosts; i++) {
if (dict_mysql_check_stat(PLDB->db_hosts[i], stat, type, t) &&
}
if (count) {
- /*
- * Calling myrand() can deplete the random pool.
- * Don't rely on the optimizer to weed out the call
- * when count == 1.
- */
- idx = (count > 1) ? 1 + (count - 1) * (double) myrand() / RAND_MAX : 1;
+ idx = (count > 1) ?
+ 1 + count * (double) myrand() / (1.0 + RAND_MAX) : 1;
for (i = 0; i < PLDB->len_hosts; i++) {
if (dict_pgsql_check_stat(PLDB->db_hosts[i], stat, type, t) &&
/* SYNOPSIS
/* #include <mail_queue.h>
/*
-/* VSTREAM *mail_queue_enter(queue_name, mode)
+/* VSTREAM *mail_queue_enter(queue_name, mode, tp)
/* const char *queue_name;
/* int mode;
+/* struct timeval *tp;
/*
/* VSTREAM *mail_queue_open(queue_name, queue_id, flags, mode)
/* const char *queue_name;
/* id is the file base name, see VSTREAM_PATH(). Queue ids are
/* relatively short strings and are recycled in the course of time.
/* The only guarantee given is that on a given machine, no two queue
-/* entries will have the same queue ID at the same time.
+/* entries will have the same queue ID at the same time. The tp
+/* argument, if not a null pointer, receives the time stamp that
+/* corresponds with the queue ID.
/*
/* mail_queue_open() opens the named queue file. The \fIflags\fR
/* and \fImode\fR arguments are as with open(2). The result is a
/* mail_queue_enter - make mail queue entry with locally-unique name */
-VSTREAM *mail_queue_enter(const char *queue_name, int mode)
+VSTREAM *mail_queue_enter(const char *queue_name, int mode,
+ struct timeval * tp)
{
char *myname = "mail_queue_enter";
static VSTRING *id_buf;
path_buf = vstring_alloc(10);
temp_path = vstring_alloc(100);
}
- GETTIMEOFDAY(&tv);
+ if (tp == 0)
+ tp = &tv;
/*
* Create a file with a temporary name that does not collide. The process
* If someone is racing against us, try to win.
*/
for (;;) {
+ GETTIMEOFDAY(tp);
vstring_sprintf(temp_path, "%s/%d.%d", queue_name,
- (int) tv.tv_usec, pid);
+ (int) tp->tv_usec, pid);
if ((fd = open(STR(temp_path), O_RDWR | O_CREAT | O_EXCL, mode)) >= 0)
break;
if (errno == EEXIST || errno == EISDIR) {
- if ((int) ++tv.tv_usec < 0)
- tv.tv_usec = 0;
continue;
}
msg_warn("%s: create file %s: %m", myname, STR(temp_path));
* If someone is racing against us, try to win.
*/
file_id = get_file_id(fd);
- GETTIMEOFDAY(&tv);
/*
* XXX Some systems seem to have clocks that correlate with process
* prevents multiple messages from getting the same Message-ID value.
*/
for (count = 0;; count++) {
- vstring_sprintf(id_buf, "%05X%s", (int) tv.tv_usec, file_id);
+ GETTIMEOFDAY(tp);
+ vstring_sprintf(id_buf, "%05X%s", (int) tp->tv_usec, file_id);
mail_queue_path(path_buf, queue_name, STR(id_buf));
-#if 0
- if (access(STR(path_buf), X_OK) == 0) { /* collision. */
- if ((int) ++tv.tv_usec < 0)
- tv.tv_usec = 0;
- continue;
- }
-#endif
if (sane_rename(STR(temp_path), STR(path_buf)) == 0) /* success */
break;
if (errno == EPERM || errno == EISDIR) {/* collision. weird. */
- if ((int) ++tv.tv_usec < 0)
- tv.tv_usec = 0;
continue;
}
if (errno != ENOENT || mail_queue_mkdirs(STR(path_buf)) < 0) {
/* DESCRIPTION
/* .nf
+ /*
+ * System library.
+ */
+#include <sys/time.h>
+
/*
* Utility library.
*/
#define MAIL_QUEUE_STAT_READY (S_IRUSR | S_IWUSR | S_IXUSR)
#define MAIL_QUEUE_STAT_CORRUPT (S_IRUSR)
-extern struct VSTREAM *mail_queue_enter(const char *, int);
+extern struct VSTREAM *mail_queue_enter(const char *, int, struct timeval *);
extern struct VSTREAM *mail_queue_open(const char *, const char *, int, int);
extern int mail_queue_rename(const char *, const char *, const char *);
extern int mail_queue_remove(const char *, const char *);
/* .in +4
/* VSTREAM *stream;
/* char *id;
+/* struct timeval ctime;
/* private members...
/* .in -4
/* } MAIL_STREAM;
MAIL_STREAM *mail_stream_file(const char *queue, const char *class,
const char *service, int mode)
{
+ struct timeval tv;
MAIL_STREAM *info;
VSTREAM *stream;
- stream = mail_queue_enter(queue, 0600 | mode);
+ stream = mail_queue_enter(queue, 0600 | mode, &tv);
if (msg_verbose)
msg_info("open %s", VSTREAM_PATH(stream));
info->class = mystrdup(class);
info->service = mystrdup(service);
info->mode = mode;
+ info->ctime = tv;
return (info);
}
/* DESCRIPTION
/* .nf
+ /*
+ * System library.
+ */
+#include <sys/time.h>
+
/*
* Utility library.
*/
char *class; /* trigger class */
char *service; /* trigger service */
int mode; /* additional permissions */
+ struct timeval ctime; /* creation time */
};
extern MAIL_STREAM *mail_stream_file(const char *, const char *, const char *, int);
* Patches change the patchlevel and the release date. Snapshots change the
* release date only.
*/
-#define MAIL_RELEASE_DATE "20050719"
-#define MAIL_VERSION_NUMBER "2.2.5"
+#define MAIL_RELEASE_DATE "20051130"
+#define MAIL_VERSION_NUMBER "2.2.6"
#define VAR_MAIL_VERSION "mail_version"
#ifdef SNAPSHOT
*/
#define SMTP_ERR_EOF 1 /* unexpected client disconnect */
#define SMTP_ERR_TIME 2 /* time out */
+#define SMTP_ERR_PROTO 3 /* protocol (application) */
extern void smtp_timeout_setup(VSTREAM *, int);
extern void PRINTFLIKE(2, 3) smtp_printf(VSTREAM *, const char *,...);
if (*cp == ' ' || *cp == 0)
break;
}
+
+ /*
+ * XXX Do not ignore garbage when ESMTP command pipelining is turned
+ * on. After sending ".<CR><LF>QUIT<CR><LF>", Postfix might recognize
+ * the server's 2XX QUIT reply as a 2XX END-OF-DATA reply after
+ * garbage, causing mail to be lost. Instead, make a long jump so
+ * that all recipients of multi-recipient mail get consistent
+ * treatment.
+ */
state->error_mask |= MAIL_ERROR_PROTOCOL;
+ if (state->features & LMTP_FEATURE_PIPELINING) {
+ msg_warn("non-LMTP response from %s: %.100s",
+ session->namaddr, STR(state->buffer));
+ vstream_longjmp(session->stream, SMTP_ERR_PROTO);
+ }
}
if (three_digs != 0)
rdata.code = atoi(STR(state->buffer));
vstring_sprintf(why, "conversation with %s timed out while %s",
session->namaddr, description);
break;
+ case SMTP_ERR_PROTO:
+ vstring_sprintf(why, "remote protocol error in reply from %s while %s",
+ session->namaddr, description);
+ break;
}
/*
QMGR_MESSAGE *message = entry->message;
VSTRING *reason = vstring_alloc(1);
int status;
+ QMGR_RCPT *recipient;
+ int nrcpt;
/*
* The message transport has responded. Stop the watchdog timer.
qmgr_transport_throttle(transport, "unknown mail transport error");
msg_warn("transport %s failure -- see a previous warning/fatal/panic logfile record for the problem description",
transport->name);
+
+ /*
+ * Assume the worst and write a defer logfile record for each
+ * recipient. This omission was already present in the first queue
+ * manager implementation of 199703, and was fixed 200511.
+ *
+ * Don't move this queue entry back to the todo queue so that
+ * qmgr_defer_transport() can update the defer log. The queue entry
+ * is still hot, and making it cold would involve duplicating most
+ * but not all code at the end of this routine. That's too tricky.
+ */
+ for (nrcpt = 0; nrcpt < entry->rcpt_list.len; nrcpt++) {
+ recipient = entry->rcpt_list.info + nrcpt;
+ qmgr_defer_recipient(message, recipient, transport->reason);
+ }
qmgr_defer_transport(transport, transport->reason);
}
* No problems detected. Mark the transport and queue as alive. The queue
* itself won't go away before we dispose of the current queue entry.
*/
- if (VSTRING_LEN(reason) == 0) {
+ if (status != DELIVER_STAT_CRASH && VSTRING_LEN(reason) == 0) {
qmgr_transport_unthrottle(transport);
qmgr_queue_unthrottle(queue);
}
* XXX Workaround: REC_TYPE_FILT (used in envelopes) == REC_TYPE_CONT
* (used in message content).
*/
- if (type == REC_TYPE_FILT && *expected != REC_TYPE_CONTENT[0])
- /* Use our own content filter settings instead. */
- continue;
+ if (*expected != REC_TYPE_CONTENT[0]) {
+ if (type == REC_TYPE_FILT)
+ /* Discard FILTER record after "postsuper -r". */
+ continue;
+ if (type == REC_TYPE_RDR)
+ /* Discard REDIRECT record after "postsuper -r". */
+ continue;
+ }
+ if (*expected == REC_TYPE_EXTRACT[0]) {
+ if (type == REC_TYPE_RRTO)
+ /* Discard return-receipt record after "postsuper -r". */
+ continue;
+ if (type == REC_TYPE_ERTO)
+ /* Discard errors-to record after "postsuper -r". */
+ continue;
+ if (type == REC_TYPE_ATTR)
+ /* Discard other/header/body action after "postsuper -r". */
+ continue;
+ }
/*
* XXX Force an empty record when the queue file content begins with
QMGR_MESSAGE *message = entry->message;
VSTRING *reason = vstring_alloc(1);
int status;
+ QMGR_RCPT *recipient;
+ int nrcpt;
/*
* The message transport has responded. Stop the watchdog timer.
qmgr_transport_throttle(transport, "unknown mail transport error");
msg_warn("transport %s failure -- see a previous warning/fatal/panic logfile record for the problem description",
transport->name);
+
+ /*
+ * Assume the worst and write a defer logfile record for each
+ * recipient. This omission was already present in the first queue
+ * manager implementation of 199703, and was fixed 200511.
+ *
+ * Don't move this queue entry back to the todo queue so that
+ * qmgr_defer_transport() can update the defer log. The queue entry
+ * is still hot, and making it cold would involve duplicating most
+ * but not all code at the end of this routine. That's too tricky.
+ */
+ for (nrcpt = 0; nrcpt < entry->rcpt_list.len; nrcpt++) {
+ recipient = entry->rcpt_list.info + nrcpt;
+ qmgr_defer_recipient(message, recipient, transport->reason);
+ }
qmgr_defer_transport(transport, transport->reason);
}
* No problems detected. Mark the transport and queue as alive. The queue
* itself won't go away before we dispose of the current queue entry.
*/
- if (VSTRING_LEN(reason) == 0) {
+ if (status != DELIVER_STAT_CRASH && VSTRING_LEN(reason) == 0) {
qmgr_transport_unthrottle(transport);
qmgr_queue_unthrottle(queue);
}
if (*cp == ' ' || *cp == 0)
break;
}
+
+ /*
+ * XXX Do not ignore garbage when ESMTP command pipelining is turned
+ * on. After sending ".<CR><LF>QUIT<CR><LF>", Postfix might recognize
+ * the server's 2XX QUIT reply as a 2XX END-OF-DATA reply after
+ * garbage, causing mail to be lost. Instead, make a long jump so
+ * that all recipients of multi-recipient mail get consistent
+ * treatment.
+ */
session->error_mask |= MAIL_ERROR_PROTOCOL;
+ if (session->features & SMTP_FEATURE_PIPELINING) {
+ msg_warn("non-SMTP response from %s: %s",
+ session->namaddr, STR(session->buffer));
+ vstream_longjmp(session->stream, SMTP_ERR_PROTO);
+ }
}
if (three_digs != 0)
rdata.code = atoi(STR(session->buffer));
* XXX Should not cache TLS sessions unless we are using a single-session,
* in-process, cache. And if we did, we should passivate VSTREAM objects
* in addition to passivating SMTP_SESSION objects.
+ *
+ * XXX Workaround. If this host spoke TLS, connection caching was already
+ * turned off for this session by smtp_tls_start(). However, this alone
+ * does not distinguish between "good TLS connection" and "bad
+ * connection".
+ *
+ * In the case of "bad connection" to a primary host we want to store the
+ * first good alternate connection under the logical next-hop destination
+ * name name. In the case of a good primary TLS connection that would not
+ * make sense: the Postfix cache would prefer non-TLS secondary hosts
+ * over TLS-enabled primary hosts!
+ *
+ * The real fix is to have three-valued connection caching state: "do
+ * cache", "don't cache", and "bad connection", but that involves more
+ * change than is allowed in a stable release.
+ *
+ * To distinguish good TLS connections from bad connections we reset the
+ * logical next-hop state, so that we won't cache connections to
+ * less-preferred MX hosts under the logical next-hop destination.
*/
if (session->reuse_count > 0) {
smtp_save_session(state);
if (HAVE_NEXTHOP_STATE(state))
FREE_NEXTHOP_STATE(state);
} else {
+#ifdef USE_TLS
+ if (session->tls_context)
+ if (HAVE_NEXTHOP_STATE(state))
+ FREE_NEXTHOP_STATE(state);
+#endif
smtp_session_free(session);
}
state->session = 0;
vstring_sprintf(why, "conversation with %s timed out while %s",
session->namaddr, description);
break;
+ case SMTP_ERR_PROTO:
+ vstring_sprintf(why, "remote protocol error in reply from %s while %s",
+ session->namaddr, description);
+ break;
}
/*
&& anvil_clnt_mail(anvil_clnt, state->service, state->addr,
&rate) == ANVIL_STAT_OK
&& rate > var_smtpd_cmail_limit) {
- smtpd_chat_reply(state, "421 %s Error: too much mail from %s",
- var_myhostname, state->addr);
+ state->error_mask |= MAIL_ERROR_POLICY;
+ smtpd_chat_reply(state, "450 Error: too much mail from %s",
+ state->addr);
msg_warn("Message delivery request rate limit exceeded: %d from %s for service %s",
rate, state->namaddr, state->service);
return (-1);
&& anvil_clnt_rcpt(anvil_clnt, state->service, state->addr,
&rate) == ANVIL_STAT_OK
&& rate > var_smtpd_crcpt_limit) {
- smtpd_chat_reply(state, "421 %s Error: too many recipients from %s",
- var_myhostname, state->addr);
+ state->error_mask |= MAIL_ERROR_POLICY;
+ smtpd_chat_reply(state, "450 Error: too many recipients from %s",
+ state->addr);
msg_warn("Recipient address rate limit exceeded: %d from %s for service %s",
rate, state->namaddr, state->service);
return (-1);
/*
* Flush out any access table actions that are delegated to the cleanup
* server, and that may trigger before we accept the first valid
- * recipient.
+ * recipient. There will be more after end-of-data.
*
* Terminate the message envelope segment. Start the message content
* segment, and prepend our own Received: header. If there is only one
* recipient, list the recipient address.
*/
if (state->cleanup) {
- if (state->saved_filter)
- rec_fprintf(state->cleanup, REC_TYPE_FILT, "%s", state->saved_filter);
- if (state->saved_redirect)
- rec_fprintf(state->cleanup, REC_TYPE_RDR, "%s", state->saved_redirect);
if (state->saved_flags)
rec_fprintf(state->cleanup, REC_TYPE_FLGS, "%d", state->saved_flags);
rec_fputs(state->cleanup, REC_TYPE_MESG, "");
if (prev_rec_type != REC_TYPE_CONT && *start == '.'
&& (state->proxy == 0 ? (++start, --len) == 0 : len == 1))
break;
- state->act_size += len + 2;
- if (state->err == CLEANUP_STAT_OK
- && out_record(out_stream, curr_rec_type, start, len) < 0)
- state->err = out_error;
+ if (state->err == CLEANUP_STAT_OK) {
+ state->act_size += len + 2;
+ if (var_message_limit > 0 && state->act_size > var_message_limit)
+ state->err = CLEANUP_STAT_SIZE;
+ else if (out_record(out_stream, curr_rec_type, start, len) < 0)
+ state->err = out_error;
+ }
}
state->where = SMTPD_AFTER_DOT;
- if (SMTPD_STAND_ALONE(state) == 0 && (err = smtpd_check_eod(state)) != 0) {
+ if (state->err == CLEANUP_STAT_OK
+ && SMTPD_STAND_ALONE(state) == 0
+ && (err = smtpd_check_eod(state)) != 0) {
smtpd_chat_reply(state, "%s", err);
if (state->proxy) {
smtpd_proxy_close(state);
if (state->err == CLEANUP_STAT_OK &&
*STR(state->proxy_buffer) != '2')
state->err = CLEANUP_STAT_CONT;
- } else {
- state->error_mask |= MAIL_ERROR_SOFTWARE;
+ } else if (state->err != CLEANUP_STAT_SIZE) {
state->err |= CLEANUP_STAT_PROXY;
vstring_sprintf(state->proxy_buffer,
"451 Error: queue file write error");
}
/*
- * Send the end-of-segment markers and finish the queue file record
- * stream.
+ * Flush out access table actions that are delegated to the cleanup
+ * server. There is similar code at the beginning of the DATA command.
+ *
+ * Send the end-of-segment markers and finish the queue file record stream.
*/
else {
+ if (state->err == CLEANUP_STAT_OK) {
+ rec_fputs(state->cleanup, REC_TYPE_XTRA, "");
+ if (state->saved_filter)
+ rec_fprintf(state->cleanup, REC_TYPE_FILT, "%s",
+ state->saved_filter);
+ if (state->saved_redirect)
+ rec_fprintf(state->cleanup, REC_TYPE_RDR, "%s",
+ state->saved_redirect);
+ if (state->saved_flags)
+ rec_fprintf(state->cleanup, REC_TYPE_FLGS, "%d",
+ state->saved_flags);
+ if (vstream_ferror(state->cleanup))
+ state->err = CLEANUP_STAT_WRITE;
+ }
if (state->err == CLEANUP_STAT_OK)
- if (rec_fputs(state->cleanup, REC_TYPE_XTRA, "") < 0
- || rec_fputs(state->cleanup, REC_TYPE_END, "") < 0
+ if (rec_fputs(state->cleanup, REC_TYPE_END, "") < 0
|| vstream_fflush(state->cleanup))
state->err = CLEANUP_STAT_WRITE;
if (state->err == 0) {
&& anvil_clnt_connect(anvil_clnt, service, state->addr,
&count, &crate) == ANVIL_STAT_OK) {
if (var_smtpd_cconn_limit > 0 && count > var_smtpd_cconn_limit) {
+ state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "421 %s Error: too many connections from %s",
var_myhostname, state->addr);
msg_warn("Connection concurrency limit exceeded: %d from %s for service %s",
table, VAR_SMTPD_PROXY_FILT, action);
return (0);
}
+
+ /*
+ * ETRN does not receive mail so we can't store queue file records.
+ */
+ if (strcmp(state->where, "ETRN") == 0) {
+ msg_warn("access table %s: action %s is unavailable in %s",
+ table, action, VAR_ETRN_CHECKS);
+ return (0);
+ }
return (not_in_client_helo(state, table, action, reply_class));
}
if (not_in_client_helo(state, table, "PREPEND", reply_class) == 0)
return (SMTPD_CHECK_DUNNO);
#endif
+ if (strcmp(state->where, SMTPD_AFTER_DOT) == 0) {
+ msg_warn("access table %s: action PREPEND must be used before %s",
+ table, VAR_EOD_CHECKS);
+ return (SMTPD_CHECK_DUNNO);
+ }
if (*cmd_text == 0 || is_header(cmd_text) == 0) {
msg_warn("access map %s entry \"%s\" requires header: text",
table, datum);
return (ret);
}
+#else
+
+ /*
+ * Broken linker workaround.
+ */
+int tls_dummy_for_broken_linkers;
+
#endif
EWOULDBLOCK,
ENOBUFS, /* HPUX11 */
ECONNABORTED,
+#ifdef EPROTO
+ EPROTO, /* SunOS 5.5.1 */
+#endif
0,
};
int count;
* hosed beyond recovery. There is no point treating this as a beneficial
* error result because the program would go into a tight loop.
*
+ * XXX Solaris 2.5.1 accept() returns EPROTO when a TCP client has
+ * disconnected in the mean time. Since there is no connection, it is
+ * safe to map the error code onto EAGAIN.
+ *
* XXX LINUX < 2.1 accept() wakes up before the three-way handshake is
* complete, so it can fail with ECONNRESET and other "false alarm"
* indications.
#endif
#define GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *) 0)
#define ROOT_PATH "/bin:/usr/bin:/sbin:/usr/sbin"
-#if (defined(__NetBSD_Version__) && __NetBSD_Version__ > 200040000)
+#if (defined(__NetBSD_Version__) && __NetBSD_Version__ > 299000900)
# define USE_STATVFS
# define STATVFS_IN_SYS_STATVFS_H
#else
#define SOCKOPT_SIZE socklen_t
#endif
-#if __NetBSD_Version__ >= 200060000 /* 2.0F */
+#if __NetBSD_Version__ >= 299000900 /* 2.99.9 */
#define HAS_CLOSEFROM
#endif
#define DEF_DB_TYPE "hash"
#define ALIAS_DB_MAP "hash:/etc/aliases"
#define GETTIMEOFDAY(t) gettimeofday(t,(struct timezone *) 0)
+#define RESOLVE_H_NEEDS_NAMESER8_COMPAT_H
#define ROOT_PATH "/bin:/usr/bin:/sbin:/usr/sbin"
#define USE_STATFS
#define STATFS_IN_SYS_MOUNT_H