All changes up to this point should be ready for Postfix 2.5.
- Documentation: updated nqgmr preemptive scheduler documentation
+ Documentation: updated nqmgr preemptive scheduler documentation
by Patrik Rak. File: proto/SCHEDULER_README.html.
20071211
- Bugfix: memory leak when the first write on a bi-directional
- VSTREAM fails. File: util/vstream.c.
+ Bugfix (introduced 19980315): the "write" equivalent of
+ bugfix 20030104. File: util/vstream.c.
20071212
starting with '-' at SMTP session time. To make this possible
the feature was moved from qmgr(8) to trivial-rewrite(8).
Files: *qmgr/qmgr_message.c, trivial-rewrite/resolve.c.
+
+20071213:
+
+ Cleanup: the queue manager and SMTP client now distinguish
+ between connection cache store and retrieve hints. Once the
+ queue manager enables enables connection caching (store and
+ load) hints on a per-destination queue, it keeps sending
+ connection cache retrieve hints to the delivery agent even
+ after it stops sending connection cache store hints. This
+ prevents the SMTP client from making a new connection without
+ checking the connection cache first. Victor Duchovni. Files:
+ *qmgr/qmgr_entry.c, smtp/smtp_connect.c.
+
+ Bugfix (introduced Postfix 2.3): the SMTP client never
+ marked corrupt files as corrupt. Victor Duchovni. File:
+ smtp/smtp_proto.c.
+
+ Cleanup: the SMTP client won't mark a destination as
+ unavailable when at least one SMTP session was completed
+ without connect or handshake error. Victor Duchovni. Files:
+ smtp/smtp_connect.c, smtp/smtp_session.c, smtp/smtp_proto.c,
+ smtp/smtp_trouble.c.
#define DEL_REQ_FLAG_MTA_VRFY (1<<8) /* MTA-requested address probe */
#define DEL_REQ_FLAG_USR_VRFY (1<<9) /* user-requested address probe */
#define DEL_REQ_FLAG_RECORD (1<<10) /* record and deliver */
-#define DEL_REQ_FLAG_SCACHE (1<<11) /* opportunistic caching */
+#define DEL_REQ_FLAG_SCACHE_LD (1<<11) /* Consult opportunistic cache */
+#define DEL_REQ_FLAG_SCACHE_ST (1<<12) /* Update opportunistic cache */
+
+ /*
+ * Cache Load and Store as value or mask. Use explicit names for multi-bit
+ * values.
+ */
+#define DEL_REQ_FLAG_SCACHE_MASK (DEL_REQ_FLAG_SCACHE_LD|DEL_REQ_FLAG_SCACHE_ST)
/*
* For compatibility, the old confusing names.
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20071212"
+#define MAIL_RELEASE_DATE "20071213"
#define MAIL_VERSION_NUMBER "2.5"
#ifdef SNAPSHOT
* (we need to recognize back-to-back deliveries for transports with
* concurrency 1).
*
- * XXX It would be nice if we could say "try to reuse a cached
- * connection, but don't bother saving it when you're done". As long
- * as we can't, we must not turn off session caching too early.
+ * If caching has previously been enabled, but is not now, fetch any
+ * existing entries from the cache, but don't add new ones.
*/
#define CONCURRENT_OR_BACK_TO_BACK_DELIVERY() \
(queue->busy_refcount > 1 || BACK_TO_BACK_DELIVERY())
* prevents unnecessary session caching when we have a burst of mail
* <= the initial concurrency limit.
*/
- if ((queue->dflags & DEL_REQ_FLAG_SCACHE) == 0) {
+ if ((queue->dflags & DEL_REQ_FLAG_SCACHE_ST) == 0) {
if (BACK_TO_BACK_DELIVERY()) {
if (msg_verbose)
msg_info("%s: allowing on-demand session caching for %s",
myname, queue->name);
- queue->dflags |= DEL_REQ_FLAG_SCACHE;
+ queue->dflags |= DEL_REQ_FLAG_SCACHE_MASK;
}
}
if (msg_verbose)
msg_info("%s: disallowing on-demand session caching for %s",
myname, queue->name);
- queue->dflags &= ~DEL_REQ_FLAG_SCACHE;
+ queue->dflags &= ~DEL_REQ_FLAG_SCACHE_ST;
}
}
}
* (we need to recognize back-to-back deliveries for transports with
* concurrency 1).
*
- * XXX It would be nice if we could say "try to reuse a cached
- * connection, but don't bother saving it when you're done". As long
- * as we can't, we must not turn off session caching too early.
+ * If caching has previously been enabled, but is not now, fetch any
+ * existing entries from the cache, but don't add new ones.
*/
#define CONCURRENT_OR_BACK_TO_BACK_DELIVERY() \
(queue->busy_refcount > 1 || BACK_TO_BACK_DELIVERY())
* prevents unnecessary session caching when we have a burst of mail
* <= the initial concurrency limit.
*/
- if ((queue->dflags & DEL_REQ_FLAG_SCACHE) == 0) {
+ if ((queue->dflags & DEL_REQ_FLAG_SCACHE_ST) == 0) {
if (BACK_TO_BACK_DELIVERY()) {
if (msg_verbose)
msg_info("%s: allowing on-demand session caching for %s",
myname, queue->name);
- queue->dflags |= DEL_REQ_FLAG_SCACHE;
+ queue->dflags |= DEL_REQ_FLAG_SCACHE_MASK;
}
}
if (msg_verbose)
msg_info("%s: disallowing on-demand session caching for %s",
myname, queue->name);
- queue->dflags &= ~DEL_REQ_FLAG_SCACHE;
+ queue->dflags &= ~DEL_REQ_FLAG_SCACHE_ST;
}
}
}
#define SMTP_MISC_FLAG_FIRST_NEXTHOP (1<<3)
#define SMTP_MISC_FLAG_FINAL_NEXTHOP (1<<4)
#define SMTP_MISC_FLAG_FINAL_SERVER (1<<5)
-#define SMTP_MISC_FLAG_CONN_CACHE (1<<6)
+#define SMTP_MISC_FLAG_CONN_LOAD (1<<6)
+#define SMTP_MISC_FLAG_CONN_STORE (1<<7)
+#define SMTP_MISC_FLAG_COMPLETE_SESSION (1<<8)
+
+#define SMTP_MISC_FLAG_CONN_CACHE_MASK \
+ (SMTP_MISC_FLAG_CONN_LOAD | SMTP_MISC_FLAG_CONN_STORE)
/*
* smtp.c
/* smtp_print_addr - print address list */
-static void smtp_print_addr(char *what, DNS_RR *addr_list)
+static void smtp_print_addr(const char *what, DNS_RR *addr_list)
{
DNS_RR *addr;
MAI_HOSTADDR_STR hostaddr;
/* smtp_addr_one - address lookup for one host name */
-static DNS_RR *smtp_addr_one(DNS_RR *addr_list, char *host, unsigned pref,
- DSN_BUF *why)
+static DNS_RR *smtp_addr_one(DNS_RR *addr_list, const char *host,
+ unsigned pref, DSN_BUF *why)
{
const char *myname = "smtp_addr_one";
DNS_RR *addr = 0;
/* smtp_host_addr - direct host lookup */
-DNS_RR *smtp_host_addr(char *host, int misc_flags, DSN_BUF *why)
+DNS_RR *smtp_host_addr(const char *host, int misc_flags, DSN_BUF *why)
{
DNS_RR *addr_list;
/*
* Internal interfaces.
*/
-extern DNS_RR *smtp_host_addr(char *, int, DSN_BUF *);
+extern DNS_RR *smtp_host_addr(const char *, int, DSN_BUF *);
extern DNS_RR *smtp_domain_addr(char *, int, DSN_BUF *, int *);
/* LICENSE
if (THIS_SESSION_IS_EXPIRED)
smtp_quit(state); /* also disables caching */
if (THIS_SESSION_IS_CACHED
- /* Redundant tests for safety... */
+ /* Redundant tests for safety... */
&& vstream_ferror(session->stream) == 0
&& vstream_feof(session->stream) == 0) {
smtp_save_session(state);
request->msg_stats.reuse_count = 0;
}
+static void smtp_cache_policy(SMTP_STATE *state, const char *dest)
+{
+ DELIVER_REQUEST *request = state->request;
+
+ state->misc_flags &= ~SMTP_MISC_FLAG_CONN_CACHE_MASK;
+
+ /*
+ * XXX Disable connection caching when sender-dependent authentication is
+ * enabled. We must not send someone elses mail over an authenticated
+ * connection, and we must not send mail that requires authentication
+ * over a connection that wasn't authenticated.
+ */
+ if (var_smtp_sender_auth)
+ return;
+
+ if (smtp_cache_dest && string_list_match(smtp_cache_dest, dest)) {
+ state->misc_flags |= SMTP_MISC_FLAG_CONN_CACHE_MASK;
+ } else if (var_smtp_cache_demand) {
+ if (request->flags & DEL_REQ_FLAG_SCACHE_LD)
+ state->misc_flags |= SMTP_MISC_FLAG_CONN_LOAD;
+ if (request->flags & DEL_REQ_FLAG_SCACHE_ST)
+ state->misc_flags |= SMTP_MISC_FLAG_CONN_STORE;
+ }
+}
+
/* smtp_connect_local - connect to local server */
static void smtp_connect_local(SMTP_STATE *state, const char *path)
{
const char *myname = "smtp_connect_local";
- DELIVER_REQUEST *request = state->request;
SMTP_SESSION *session;
DSN_BUF *why = state->why;
*
* Connection cache management is based on the UNIX-domain pathname, without
* the "unix:" prefix.
- *
- * XXX Disable connection caching when sender-dependent authentication is
- * enabled. We must not send someone elses mail over an authenticated
- * connection, and we must not send mail that requires authentication
- * over a connection that wasn't authenticated.
*/
-#define CAN_ENABLE_CONN_CACHE(request, dest) \
- (!var_smtp_sender_auth \
- && ((var_smtp_cache_demand && (request->flags & DEL_REQ_FLAG_SCACHE)) \
- || (smtp_cache_dest && string_list_match(smtp_cache_dest, dest))))
-
- if (CAN_ENABLE_CONN_CACHE(request, path))
- state->misc_flags |= SMTP_MISC_FLAG_CONN_CACHE;
+ smtp_cache_policy(state, path);
/*
* XXX We assume that the session->addr member refers to a copy of the
* available, "encrypt" may be a sensible policy. Otherwise, we also
* downgrade "encrypt" to "none", this time just to avoid waste.
*/
- if ((state->misc_flags & SMTP_MISC_FLAG_CONN_CACHE) == 0
+ if ((state->misc_flags & SMTP_MISC_FLAG_CONN_LOAD) == 0
|| (session = smtp_reuse_addr(state, path, NO_PORT)) == 0)
session = smtp_connect_unix(path, why, state->misc_flags);
if ((state->session = session) != 0) {
* authenticated connection, and we must not send mail that requires
* authentication over a connection that wasn't authenticated.
*/
- if (addr_list && (state->misc_flags & SMTP_MISC_FLAG_FIRST_NEXTHOP)
- && CAN_ENABLE_CONN_CACHE(request, domain)) {
- state->misc_flags |= SMTP_MISC_FLAG_CONN_CACHE;
- SET_NEXTHOP_STATE(state, lookup_mx, domain, port);
+ if (addr_list && (state->misc_flags & SMTP_MISC_FLAG_FIRST_NEXTHOP)) {
+ smtp_cache_policy(state, domain);
+ if (state->misc_flags & SMTP_MISC_FLAG_CONN_STORE)
+ SET_NEXTHOP_STATE(state, lookup_mx, domain, port);
}
/*
* fall-back destination. smtp_reuse_session() will truncate the
* address list when either limit is reached.
*/
- if (addr_list && state->misc_flags & SMTP_MISC_FLAG_CONN_CACHE) {
+ if (addr_list && (state->misc_flags & SMTP_MISC_FLAG_CONN_LOAD)) {
if (state->cache_used->used > 0)
smtp_scrub_addr_list(state->cache_used, &addr_list);
sess_count = addr_count =
next = addr->next;
if (++addr_count == var_smtp_mxaddr_limit)
next = 0;
- if ((state->misc_flags & SMTP_MISC_FLAG_CONN_CACHE) == 0
+ if ((state->misc_flags & SMTP_MISC_FLAG_CONN_LOAD) == 0
|| addr->pref == domain_best_pref
|| dns_rr_to_pa(addr, &hostaddr) == 0
|| !(session = smtp_reuse_addr(state, hostaddr.buf, port)))
fail_status = smtp_mesg_fail(state, DSN_BY_LOCAL_MTA,
SMTP_RESP_FAKE(&fake, "5.3.0"),
"unreadable mail queue entry");
- if (fail_status == 0)
+ /* Bailing out, abort stream with prejudice */
+ (void) vstream_fpurge(session->stream, VSTREAM_PURGE_BOTH);
+ DONT_USE_DEAD_SESSION;
+ /* If bounce_append() succeeded, status is still 0 */
+ if (state->status == 0)
(void) mark_corrupt(state->src);
+ /* Don't override smtp_mesg_fail() here. */
RETURN(fail_status);
}
} else {
int send_state;
int recv_state;
int send_name_addr;
+ int result;
/*
* Sanity check. Recipients should be unmarked at this point.
"message size %lu exceeds size limit %.0f of server %s",
request->data_size, (double) session->size_limit,
session->namaddr);
+ /* Redundant. We abort this delivery attempt. */
+ state->misc_flags |= SMTP_MISC_FLAG_COMPLETE_SESSION;
return (0);
}
else
recv_state = send_state = SMTP_STATE_MAIL;
- return (smtp_loop(state, send_state, recv_state));
+ /*
+ * Remember this session's "normal completion", even if the server 4xx-ed
+ * some or all recipients. Connection or handshake errors with a later MX
+ * host should not cause this destination be marked as unreachable.
+ */
+ result = smtp_loop(state, send_state, recv_state);
+
+ if (result == 0
+ /* Just in case */
+ && vstream_ferror(session->stream) == 0
+ && vstream_feof(session->stream) == 0)
+ state->misc_flags |= SMTP_MISC_FLAG_COMPLETE_SESSION;
+
+ return (result);
}
/* smtp_rset - send a lone RSET command */
/* .IP flags
/* Zero or more of the following:
/* .RS
-/* .IP SMTP_MISC_FLAG_CONN_CACHE
-/* Enable SMTP or LMTP connection caching.
+/* .IP SMTP_MISC_FLAG_CONN_LOAD
+/* Enable re-use of cached SMTP or LMTP connections.
+/* .IP SMTP_MISC_FLAG_CONN_STORE
+/* Enable saving of cached SMTP or LMTP connections.
/* .RE
+/* SMTP_MISC_FLAG_CONN_MASK corresponds with both _LOAD and _STORE.
/* .IP dest_prop
/* Destination specific session properties: the server is the
/* best MX host for the current logical destination.
session->sndbufsize = 0;
session->send_proto_helo = 0;
- if (flags & SMTP_MISC_FLAG_CONN_CACHE)
+ if (flags & SMTP_MISC_FLAG_CONN_STORE)
CACHE_THIS_SESSION_UNTIL(start + var_smtp_reuse_time);
else
DONT_CACHE_THIS_SESSION;
SMTP_RCPT_DROP(state, rcpt);
state->status |= status;
}
- if (throttle_queue && soft_error && request->hop_status == 0)
+ if ((state->misc_flags & SMTP_MISC_FLAG_COMPLETE_SESSION) == 0
+ && throttle_queue && soft_error && request->hop_status == 0)
request->hop_status = DSN_COPY(&why->dsn);
}