From: Wietse Venema
/etc/postfix/master.cf:
:10026 inet n - n - - smtpd
- -o receive_override_options=no_address_mapping
+ -o receive_override_options=no_address_mappings
@@ -702,7 +702,7 @@ Postfix version 2.1 and later.
/etc/postfix/master.cf:
:10026 inet n - n - - smtpd
- -o receive_override_options=no_address_mapping
+ -o receive_override_options=no_address_mappings
@@ -752,7 +752,7 @@ is available in Postfix version 2.1 and later.
/etc/postfix/master.cf:
:10026 inet n - n - - smtpd
- -o receive_override_options=no_address_mapping
+ -o receive_override_options=no_address_mappings
@@ -816,7 +816,7 @@ in the master.cf file. This feature is available in Postfix version
/etc/postfix/master.cf:
:10026 inet n - n - - smtpd
- -o receive_override_options=no_address_mapping
+ -o receive_override_options=no_address_mappings
diff --git a/postfix/html/MAILDROP_README.html b/postfix/html/MAILDROP_README.html
index bdf121caa..1216218c4 100644
--- a/postfix/html/MAILDROP_README.html
+++ b/postfix/html/MAILDROP_README.html
@@ -149,7 +149,7 @@ use the Postfix local(8) delivery agent's 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}
diff --git a/postfix/html/TLS_README.html b/postfix/html/TLS_README.html
index 8030a819d..cf4b4c6e2 100644
--- a/postfix/html/TLS_README.html
+++ b/postfix/html/TLS_README.html
@@ -262,7 +262,7 @@ the overhead of the TLS exchange.
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
@@ -315,7 +315,7 @@ is needed. Thus, the $smtpd_tls_CApat
accessible inside the optional chroot jail.
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 diff --git a/postfix/html/access.5.html b/postfix/html/access.5.html index e60da0971..412b9cd62 100644 --- a/postfix/html/access.5.html +++ b/postfix/html/access.5.html @@ -269,6 +269,10 @@ ACCESS(5) ACCESS(5) 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 diff --git a/postfix/man/man5/access.5 b/postfix/man/man5/access.5 index 7f9cfb821..c61a02696 100644 --- a/postfix/man/man5/access.5 +++ b/postfix/man/man5/access.5 @@ -246,6 +246,9 @@ header appears before the second etc. prepended header. .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 diff --git a/postfix/proto/ADDRESS_REWRITING_README.html b/postfix/proto/ADDRESS_REWRITING_README.html index 4955a6e8b..57f7fa2a5 100644 --- a/postfix/proto/ADDRESS_REWRITING_README.html +++ b/postfix/proto/ADDRESS_REWRITING_README.html @@ -603,7 +603,7 @@ in the master.cf file. This feature is available in Postfix version
/etc/postfix/master.cf:
:10026 inet n - n - - smtpd
- -o receive_override_options=no_address_mapping
+ -o receive_override_options=no_address_mappings
@@ -702,7 +702,7 @@ Postfix version 2.1 and later.
/etc/postfix/master.cf:
:10026 inet n - n - - smtpd
- -o receive_override_options=no_address_mapping
+ -o receive_override_options=no_address_mappings
@@ -752,7 +752,7 @@ is available in Postfix version 2.1 and later.
/etc/postfix/master.cf:
:10026 inet n - n - - smtpd
- -o receive_override_options=no_address_mapping
+ -o receive_override_options=no_address_mappings
@@ -816,7 +816,7 @@ in the master.cf file. This feature is available in Postfix version
/etc/postfix/master.cf:
:10026 inet n - n - - smtpd
- -o receive_override_options=no_address_mapping
+ -o receive_override_options=no_address_mappings
diff --git a/postfix/proto/MAILDROP_README.html b/postfix/proto/MAILDROP_README.html
index 8ce2c402f..e41e127f5 100644
--- a/postfix/proto/MAILDROP_README.html
+++ b/postfix/proto/MAILDROP_README.html
@@ -149,7 +149,7 @@ use the Postfix 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}
diff --git a/postfix/proto/TLS_README.html b/postfix/proto/TLS_README.html
index 6b2165685..04711f7cc 100644
--- a/postfix/proto/TLS_README.html
+++ b/postfix/proto/TLS_README.html
@@ -262,7 +262,7 @@ the overhead of the TLS exchange.
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
@@ -315,7 +315,7 @@ is needed. Thus, the $smtpd_tls_CApath directory needs to be
accessible inside the optional chroot jail.
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
diff --git a/postfix/proto/access b/postfix/proto/access
index 405ba1f3f..26248d58d 100644
--- a/postfix/proto/access
+++ b/postfix/proto/access
@@ -226,6 +226,9 @@
# .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
diff --git a/postfix/src/cleanup/cleanup_extracted.c b/postfix/src/cleanup/cleanup_extracted.c
index 097abcef5..614ff01f1 100644
--- a/postfix/src/cleanup/cleanup_extracted.c
+++ b/postfix/src/cleanup/cleanup_extracted.c
@@ -95,14 +95,22 @@ void cleanup_extracted_process(CLEANUP_STATE *state, int type,
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",
@@ -179,14 +187,8 @@ void cleanup_extracted_process(CLEANUP_STATE *state, int type,
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 */
diff --git a/postfix/src/cleanup/cleanup_message.c b/postfix/src/cleanup/cleanup_message.c
index 029f70688..e456f0b47 100644
--- a/postfix/src/cleanup/cleanup_message.c
+++ b/postfix/src/cleanup/cleanup_message.c
@@ -564,6 +564,7 @@ static void cleanup_header_done_callback(void *context)
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
@@ -573,10 +574,17 @@ static void cleanup_header_done_callback(void *context)
*
* 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);
diff --git a/postfix/src/dns/dns.h b/postfix/src/dns/dns.h
index 90a45b82c..c57114c6f 100644
--- a/postfix/src/dns/dns.h
+++ b/postfix/src/dns/dns.h
@@ -19,6 +19,9 @@
#ifdef RESOLVE_H_NEEDS_STDIO_H
#include
#endif
+#ifdef RESOLVE_H_NEEDS_NAMESER8_COMPAT_H
+#include
+#endif
#include
/*
diff --git a/postfix/src/global/dict_mysql.c b/postfix/src/global/dict_mysql.c
index e16d22933..3f85338fa 100644
--- a/postfix/src/global/dict_mysql.c
+++ b/postfix/src/global/dict_mysql.c
@@ -393,12 +393,8 @@ static HOST *dict_mysql_find_host(PLMYSQL *PLDB, unsigned stat, unsigned type)
}
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) &&
diff --git a/postfix/src/global/dict_pgsql.c b/postfix/src/global/dict_pgsql.c
index 025cb6b37..9fe6768bf 100644
--- a/postfix/src/global/dict_pgsql.c
+++ b/postfix/src/global/dict_pgsql.c
@@ -388,12 +388,8 @@ static HOST *dict_pgsql_find_host(PLPGSQL *PLDB, unsigned stat, unsigned type)
}
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) &&
diff --git a/postfix/src/global/mail_queue.c b/postfix/src/global/mail_queue.c
index 9068955c3..89da51b47 100644
--- a/postfix/src/global/mail_queue.c
+++ b/postfix/src/global/mail_queue.c
@@ -6,9 +6,10 @@
/* SYNOPSIS
/* #include
/*
-/* 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;
@@ -53,7 +54,9 @@
/* 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
@@ -304,7 +307,8 @@ int mail_queue_id_ok(const char *queue_id)
/* 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;
@@ -326,7 +330,8 @@ VSTREAM *mail_queue_enter(const char *queue_name, int mode)
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
@@ -338,13 +343,12 @@ VSTREAM *mail_queue_enter(const char *queue_name, int mode)
* 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));
@@ -364,7 +368,6 @@ VSTREAM *mail_queue_enter(const char *queue_name, int mode)
* 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
@@ -374,20 +377,12 @@ VSTREAM *mail_queue_enter(const char *queue_name, int mode)
* 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) {
diff --git a/postfix/src/global/mail_queue.h b/postfix/src/global/mail_queue.h
index b3d692821..e9f7655e6 100644
--- a/postfix/src/global/mail_queue.h
+++ b/postfix/src/global/mail_queue.h
@@ -11,6 +11,11 @@
/* DESCRIPTION
/* .nf
+ /*
+ * System library.
+ */
+#include
+
/*
* Utility library.
*/
@@ -37,7 +42,7 @@
#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 *);
diff --git a/postfix/src/global/mail_stream.c b/postfix/src/global/mail_stream.c
index 6edeea7ad..4930a6a5c 100644
--- a/postfix/src/global/mail_stream.c
+++ b/postfix/src/global/mail_stream.c
@@ -10,6 +10,7 @@
/* .in +4
/* VSTREAM *stream;
/* char *id;
+/* struct timeval ctime;
/* private members...
/* .in -4
/* } MAIL_STREAM;
@@ -264,10 +265,11 @@ int mail_stream_finish(MAIL_STREAM *info, VSTRING *why)
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));
@@ -280,6 +282,7 @@ MAIL_STREAM *mail_stream_file(const char *queue, const char *class,
info->class = mystrdup(class);
info->service = mystrdup(service);
info->mode = mode;
+ info->ctime = tv;
return (info);
}
diff --git a/postfix/src/global/mail_stream.h b/postfix/src/global/mail_stream.h
index a1e64f13e..f1c393b5a 100644
--- a/postfix/src/global/mail_stream.h
+++ b/postfix/src/global/mail_stream.h
@@ -11,6 +11,11 @@
/* DESCRIPTION
/* .nf
+ /*
+ * System library.
+ */
+#include
+
/*
* Utility library.
*/
@@ -34,6 +39,7 @@ struct MAIL_STREAM {
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);
diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h
index 5fcf512bc..f402600a1 100644
--- a/postfix/src/global/mail_version.h
+++ b/postfix/src/global/mail_version.h
@@ -20,8 +20,8 @@
* 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
diff --git a/postfix/src/global/smtp_stream.h b/postfix/src/global/smtp_stream.h
index cbd0f7aba..0d31fa676 100644
--- a/postfix/src/global/smtp_stream.h
+++ b/postfix/src/global/smtp_stream.h
@@ -28,6 +28,7 @@
*/
#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 *,...);
diff --git a/postfix/src/lmtp/lmtp_chat.c b/postfix/src/lmtp/lmtp_chat.c
index b06377138..7a0c31d9d 100644
--- a/postfix/src/lmtp/lmtp_chat.c
+++ b/postfix/src/lmtp/lmtp_chat.c
@@ -222,7 +222,21 @@ LMTP_RESP *lmtp_chat_resp(LMTP_STATE *state)
if (*cp == ' ' || *cp == 0)
break;
}
+
+ /*
+ * XXX Do not ignore garbage when ESMTP command pipelining is turned
+ * on. After sending ".QUIT", 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));
diff --git a/postfix/src/lmtp/lmtp_trouble.c b/postfix/src/lmtp/lmtp_trouble.c
index b9d1ce500..c58192ede 100644
--- a/postfix/src/lmtp/lmtp_trouble.c
+++ b/postfix/src/lmtp/lmtp_trouble.c
@@ -296,6 +296,10 @@ int lmtp_stream_except(LMTP_STATE *state, int code, char *description)
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;
}
/*
diff --git a/postfix/src/oqmgr/qmgr_deliver.c b/postfix/src/oqmgr/qmgr_deliver.c
index 058aa8a55..8b724b1ec 100644
--- a/postfix/src/oqmgr/qmgr_deliver.c
+++ b/postfix/src/oqmgr/qmgr_deliver.c
@@ -210,6 +210,8 @@ static void qmgr_deliver_update(int unused_event, char *context)
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.
@@ -239,6 +241,21 @@ static void qmgr_deliver_update(int unused_event, char *context)
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);
}
@@ -264,7 +281,7 @@ static void qmgr_deliver_update(int unused_event, char *context)
* 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);
}
diff --git a/postfix/src/pickup/pickup.c b/postfix/src/pickup/pickup.c
index 2272a3c74..6f73c5579 100644
--- a/postfix/src/pickup/pickup.c
+++ b/postfix/src/pickup/pickup.c
@@ -230,9 +230,25 @@ static int copy_segment(VSTREAM *qfile, VSTREAM *cleanup, PICKUP_INFO *info,
* 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
diff --git a/postfix/src/qmgr/qmgr_deliver.c b/postfix/src/qmgr/qmgr_deliver.c
index 28463a0e6..e92872b37 100644
--- a/postfix/src/qmgr/qmgr_deliver.c
+++ b/postfix/src/qmgr/qmgr_deliver.c
@@ -215,6 +215,8 @@ static void qmgr_deliver_update(int unused_event, char *context)
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.
@@ -244,6 +246,21 @@ static void qmgr_deliver_update(int unused_event, char *context)
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);
}
@@ -269,7 +286,7 @@ static void qmgr_deliver_update(int unused_event, char *context)
* 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);
}
diff --git a/postfix/src/smtp/smtp_chat.c b/postfix/src/smtp/smtp_chat.c
index 2270f7741..db694c062 100644
--- a/postfix/src/smtp/smtp_chat.c
+++ b/postfix/src/smtp/smtp_chat.c
@@ -245,7 +245,21 @@ SMTP_RESP *smtp_chat_resp(SMTP_SESSION *session)
if (*cp == ' ' || *cp == 0)
break;
}
+
+ /*
+ * XXX Do not ignore garbage when ESMTP command pipelining is turned
+ * on. After sending ".QUIT", 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));
diff --git a/postfix/src/smtp/smtp_connect.c b/postfix/src/smtp/smtp_connect.c
index 24474ca06..1fcae1f2f 100644
--- a/postfix/src/smtp/smtp_connect.c
+++ b/postfix/src/smtp/smtp_connect.c
@@ -358,12 +358,36 @@ static void smtp_cleanup_session(SMTP_STATE *state)
* 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;
diff --git a/postfix/src/smtp/smtp_trouble.c b/postfix/src/smtp/smtp_trouble.c
index 1d8ba27e3..23f49c5ca 100644
--- a/postfix/src/smtp/smtp_trouble.c
+++ b/postfix/src/smtp/smtp_trouble.c
@@ -386,6 +386,10 @@ int smtp_stream_except(SMTP_STATE *state, int code, char *description)
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;
}
/*
diff --git a/postfix/src/smtpd/smtpd.c b/postfix/src/smtpd/smtpd.c
index 756a4cd93..8d02bd3cf 100644
--- a/postfix/src/smtpd/smtpd.c
+++ b/postfix/src/smtpd/smtpd.c
@@ -1493,8 +1493,9 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
&& 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);
@@ -1702,8 +1703,9 @@ static int rcpt_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
&& 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);
@@ -1919,17 +1921,13 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
/*
* 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, "");
@@ -2042,13 +2040,18 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
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);
@@ -2074,8 +2077,7 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
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");
@@ -2083,13 +2085,28 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
}
/*
- * 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) {
@@ -2979,6 +2996,7 @@ static void smtpd_proto(SMTPD_STATE *state, const char *service)
&& 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",
diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c
index 5e59e2b9c..4d57edcc5 100644
--- a/postfix/src/smtpd/smtpd_check.c
+++ b/postfix/src/smtpd/smtpd_check.c
@@ -1748,6 +1748,15 @@ static int can_delegate_action(SMTPD_STATE *state, const char *table,
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));
}
@@ -1955,6 +1964,11 @@ static int check_table_result(SMTPD_STATE *state, const char *table,
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);
diff --git a/postfix/src/tls/tls_misc.c b/postfix/src/tls/tls_misc.c
index 03976abc7..e0a1b15d3 100644
--- a/postfix/src/tls/tls_misc.c
+++ b/postfix/src/tls/tls_misc.c
@@ -227,4 +227,11 @@ long tls_bio_dump_cb(BIO *bio, int cmd, const char *argp, int argi,
return (ret);
}
+#else
+
+ /*
+ * Broken linker workaround.
+ */
+int tls_dummy_for_broken_linkers;
+
#endif
diff --git a/postfix/src/util/sane_accept.c b/postfix/src/util/sane_accept.c
index 30a3d8533..a4560c2c5 100644
--- a/postfix/src/util/sane_accept.c
+++ b/postfix/src/util/sane_accept.c
@@ -59,6 +59,9 @@ int sane_accept(int sock, struct sockaddr * sa, SOCKADDR_SIZE *len)
EWOULDBLOCK,
ENOBUFS, /* HPUX11 */
ECONNABORTED,
+#ifdef EPROTO
+ EPROTO, /* SunOS 5.5.1 */
+#endif
0,
};
int count;
@@ -71,6 +74,10 @@ int sane_accept(int sock, struct sockaddr * sa, SOCKADDR_SIZE *len)
* 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.
diff --git a/postfix/src/util/sys_defs.h b/postfix/src/util/sys_defs.h
index c358db320..4bddae410 100644
--- a/postfix/src/util/sys_defs.h
+++ b/postfix/src/util/sys_defs.h
@@ -50,7 +50,7 @@
#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
@@ -122,7 +122,7 @@
#define SOCKOPT_SIZE socklen_t
#endif
-#if __NetBSD_Version__ >= 200060000 /* 2.0F */
+#if __NetBSD_Version__ >= 299000900 /* 2.99.9 */
#define HAS_CLOSEFROM
#endif
@@ -155,6 +155,7 @@
#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