and RFC 3848 ("Received ... with ESMTPS?A? ...). Currently,
support for the latter is always on. Files: smtpd/smtpd.c,
smtpd/smtpd_sasl_proto.c, smtpd/smtpd_sasl_glue.c.
+
+20070727
+
+ Workaround: the queue manager no longer logs a warning for
+ mail sent to the local double-bounce address (normally, the
+ this is used as the sender while reporting an undeliverable
+ bounce message to the local postmaster). As of 20070503
+ the local double-bounce address is the default sender for
+ sender/recipient address verification probes, and it now
+ shows up as a spam target. Files: *qmgr/qmgr_message.c.
+
+20070729
+
+ Performance: fix for poor TCP performance for loopback
+ (127.0.0.1) connections. Problem reported by Mark Martinec.
+ Files: util/vstream.c, util/vstream_tweak.c, milter/milter8.c,
+ smtp/smtp_connect.c, smtpstone/*source.c.
+
+20070730
+
+ Bugfix: when a milter replied with ACCEPT at or before the
+ first RCPT command, the cleanup server would apply the
+ non_smtpd_milters setting as if the message was a local
+ submission. Problem reported by Jukka Salmi. Also, the
+ cleanup server would get out of sync with the milter when
+ a milter replied with ACCEPT at the DATA command. Files:
+ cleanup/cleanup_envelope.c, smtpd/smtpd.c, milter/milters.c.
Major changes with Postfix snapshot 20070724
============================================
-Not really major. Support for RFC 3848 (ESMTPS, ESMTPA, ESMTPSA
-in Received: headers) and updated SASL support with reply codes and
-enhanced (DSN) status codes as per RFC 4954.
+Not really major. New support for RFC 3848 (Received: headers with
+ESMTPS, ESMTPA, or ESMTPSA); updated SASL support according to RFC
+4954, resulting in small changes to SMTP reply codes and (DSN)
+enhanced status codes.
Incompatibility with Postfix snapshot 20070614
==============================================
configuration parameters or internal protocol attributes)
should be changed from int to off_t. This also requires
checking all expressions in which var_message_limit etc.
- appears.
+ appears: qmqpd, netstring, deliver_request, ...
Add M flag (enable multi-recipient delivery) to pipe daemon.
<b>SYNOPSIS</b>
<b>smtpd</b> [generic Postfix daemon options]
+ <b>sendmail -bs</b>
+
<b>DESCRIPTION</b>
The SMTP server accepts network connection requests and
performs zero or more SMTP transactions per connection.
.na
.nf
\fBsmtpd\fR [generic Postfix daemon options]
+
+\fBsendmail -bs\fR
.SH DESCRIPTION
.ad
.fi
#endif
if (type == REC_TYPE_MILT_COUNT) {
/* Not part of queue file format. */
- if (state->milters != 0) {
- msg_warn("%s: message rejected: too many milter instances",
- state->queue_id);
- state->errs |= CLEANUP_STAT_BAD;
- return;
- }
- if ((milter_count = atoi(buf)) > 0)
+ if ((milter_count = atoi(buf)) >= 0)
cleanup_milter_receive(state, milter_count);
return;
}
void cleanup_milter_receive(CLEANUP_STATE *state, int count)
{
+ if (state->milters)
+ milter_free(state->milters);
state->milters = milter_receive(state->src, count);
milter_macro_callback(state->milters, cleanup_milter_eval, (void *) state);
milter_edit_callback(state->milters,
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20070724"
+#define MAIL_RELEASE_DATE "20070731"
#define MAIL_VERSION_NUMBER "2.5"
#ifdef SNAPSHOT
/* MILTERS *milter_receive(fp, count)
/* VSTREAM *fp;
/* int count;
+/*
+/* int milter_dummy(milters, fp)
+/* MILTERS *milters;
+/* VSTREAM *fp;
/* DESCRIPTION
/* The functions in this module manage one or more milter (mail
/* filter) clients. Currently, only the Sendmail 8 filter
/* milter_receive() receives the specified number of mail
/* filters over the specified stream. The result is a null
/* pointer when no milters were sent, or when an error happened.
+/*
+/* milter_dummy() is like milter_send(), except that it sends
+/* a dummy, but entirely valid, mail filter list.
/* SEE ALSO
/* milter8(3) Sendmail 8 Milter protocol
/* DIAGNOSTICS
#define MAIL_ATTR_MILT_EOD "eod_macros"
#define MAIL_ATTR_MILT_UNK "unk_macros"
+/* milter_dummy - send empty milter list */
+
+int milter_dummy(MILTERS *milters, VSTREAM *stream)
+{
+ MILTERS dummy = *milters;
+
+ dummy.milter_list = 0;
+ return (milter_send(&dummy, stream));
+}
+
/* milter_send - send Milter instances over stream */
int milter_send(MILTERS *milters, VSTREAM *stream)
for (m = milters->milter_list; m != 0; m = m->next)
if (m->active(m))
count++;
- if (count == 0)
- return (0);
(void) rec_fprintf(stream, REC_TYPE_MILT_COUNT, "%d", count);
/*
VSTRING *eod_macros;
VSTRING *unk_macros;
- if (count == 0)
- return (0);
-
/*
* Receive filter macros.
*/
extern const char *milter_other_event(MILTERS *);
extern void milter_abort(MILTERS *);
extern void milter_disc_event(MILTERS *);
+extern int milter_dummy(MILTERS *, VSTREAM *);
extern int milter_send(MILTERS *, VSTREAM *);
extern MILTERS *milter_receive(VSTREAM *, int);
extern void milter_free(MILTERS *);
#define MILTER_BODY_START 1 /* start message body */
#define MILTER_BODY_LINE 2 /* message body line */
#define MILTER_BODY_END 3 /* end message body */
-
+
/*
* Sendmail 8 macro names. We support forms with and without the {}.
*/
VSTREAM_CTL_DOUBLE,
VSTREAM_CTL_TIMEOUT, milter->cmd_timeout,
VSTREAM_CTL_END);
+ /* Avoid poor performance when TCP MSS > VSTREAM_BUFSIZE. */
+ if (connect_fn == inet_connect)
+ vstream_tweak_tcp(milter->fp);
/*
* Open the negotiations by sending what actions the Milter may request
#endif
} else {
#define NO_PROTOCOL ((char *) 0)
-
+
if (msg_verbose)
msg_info("%s: milter %s", myname, STR(name_buf));
msg_timeout, NO_PROTOCOL, STR(act_buf), parent);
milter->fp = vstream_fdopen(fd, O_RDWR);
vstream_control(milter->fp, VSTREAM_CTL_DOUBLE, VSTREAM_CTL_END);
+ /* Avoid poor performance when TCP MSS > VSTREAM_BUFSIZE. */
+ vstream_tweak_sock(milter->fp);
milter->version = version;
milter->rq_mask = rq_mask;
milter->ev_mask = ev_mask;
if (code == SMFIR_REPLYCODE) {
if (smfi_setreply(ctx, reply_code, reply_dsn, reply_message) == MI_FAILURE)
fprintf(stderr, "smfi_setreply failed\n");
+ printf("test_reply %s\n", reply_code);
return (reply_code[0] == '4' ? SMFIS_TEMPFAIL : SMFIS_REJECT);
} else {
+ printf("test_reply %d\n", code);
return (code);
}
}
"undeliverable postmaster notification discarded"));
if (status == 0) {
deliver_completed(message->fp, recipient->offset);
+#if 0
+ /* It's the default verification probe sender address. */
msg_warn("%s: undeliverable postmaster notification discarded",
message->queue_id);
+#endif
} else
message->flags |= status;
continue;
"undeliverable postmaster notification discarded"));
if (status == 0) {
deliver_completed(message->fp, recipient->offset);
+#if 0
+ /* It's the default verification probe sender address. */
msg_warn("%s: undeliverable postmaster notification discarded",
message->queue_id);
+#endif
} else
message->flags |= status;
continue;
conn_stat = sane_connect(sock, sa, salen);
}
if (conn_stat < 0) {
- dsb_simple(why, "4.4.1", "connect to %s[%s]: %m", name, addr);
+ if (port)
+ dsb_simple(why, "4.4.1", "connect to %s[%s]:%d: %m",
+ name, addr, ntohs(port));
+ else
+ dsb_simple(why, "4.4.1", "connect to %s[%s]: %m", name, addr);
close(sock);
return (0);
}
stream = vstream_fdopen(sock, O_RDWR);
+ /*
+ * Avoid poor performance when TCP MSS > VSTREAM_BUFSIZE.
+ */
+ if (sa->sa_family == AF_INET
+#ifdef AF_INET6
+ || sa->sa_family == AF_INET6
+#endif
+ )
+ vstream_tweak_tcp(stream);
+
/*
* Bundle up what we have into a nice SMTP_SESSION object.
*/
/* Postfix SMTP server
/* SYNOPSIS
/* \fBsmtpd\fR [generic Postfix daemon options]
+/*
+/* \fBsendmail -bs\fR
/* DESCRIPTION
/* The SMTP server accepts network connection requests
/* and performs zero or more SMTP transactions per connection.
if (SMTPD_STAND_ALONE(state) == 0) {
if (smtpd_milters != 0
&& (state->saved_flags & MILTER_SKIP_FLAGS) == 0)
- (void) milter_send(smtpd_milters, state->dest->stream);
+ /* Send place-holder smtpd_milters list. */
+ (void) milter_dummy(smtpd_milters, state->cleanup);
rec_fprintf(state->cleanup, REC_TYPE_TIME, REC_TYPE_TIME_FORMAT,
REC_TYPE_TIME_ARG(state->arrival_time));
if (*var_filter_xport)
*/
if (state->cleanup) {
if (SMTPD_STAND_ALONE(state) == 0) {
+ if (smtpd_milters != 0
+ && (state->saved_flags & MILTER_SKIP_FLAGS) == 0)
+ /* Send actual smtpd_milters list. */
+ (void) milter_send(smtpd_milters, state->cleanup);
if (state->saved_flags)
rec_fprintf(state->cleanup, REC_TYPE_FLGS, "%d",
state->saved_flags);
dequeue_connect(session);
non_blocking(fd, BLOCKING);
event_disable_readwrite(fd);
+ /* Avoid poor performance when TCP MSS > VSTREAM_BUFSIZE. */
+ if (sa->sa_family == AF_INET
+#ifdef AF_INET6
+ || sa->sa_family == AF_INET6
+#endif
+ )
+ vstream_tweak_tcp(session->stream);
send_data(session);
}
}
event_disable_readwrite(fd);
event_enable_read(fd, read_banner, (char *) session);
dequeue_connect(session);
+ /* Avoid poor performance when TCP MSS > VSTREAM_BUFSIZE. */
+ if (sa->sa_family == AF_INET
+#ifdef AF_INET6
+ || sa->sa_family == AF_INET6
+#endif
+ )
+ vstream_tweak_tcp(session->stream);
}
}
username.c valid_hostname.c vbuf.c vbuf_print.c vstream.c \
vstream_popen.c vstring.c vstring_vstream.c watchdog.c writable.c \
write_buf.c write_wait.c sane_basename.c format_tv.c allspace.c \
- allascii.c load_file.c killme_after.c
+ allascii.c load_file.c killme_after.c vstream_tweak.c
OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \
attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \
username.o valid_hostname.o vbuf.o vbuf_print.o vstream.o \
vstream_popen.o vstring.o vstring_vstream.o watchdog.o writable.o \
write_buf.o write_wait.o sane_basename.o format_tv.o allspace.o \
- allascii.o load_file.o killme_after.o
+ allascii.o load_file.o killme_after.o vstream_tweak.o
HDRS = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \
chroot_uid.h cidr_match.h clean_env.h connect.h ctable.h dict.h \
dict_cdb.h dict_cidr.h dict_db.h dict_dbm.h dict_env.h dict_ht.h \
vstream_popen.o: vbuf.h
vstream_popen.o: vstream.h
vstream_popen.o: vstream_popen.c
+vstream_tweak.o: msg.h
+vstream_tweak.o: sys_defs.h
+vstream_tweak.o: vbuf.h
+vstream_tweak.o: vstream.h
+vstream_tweak.o: vstream_tweak.c
vstring.o: msg.h
vstring.o: mymalloc.h
vstring.o: sys_defs.h
/* Enable exception handling with vstream_setjmp() and vstream_longjmp().
/* This involves allocation of additional memory that normally isn't
/* used.
+/* .IP "VSTREAM_CTL_BUFSIZE (ssize_t)"
+/* Specify a non-default write buffer size, or zero to implement
+/* a no-op. Requests to shrink an existing buffer size are
+/* ignored. Requests to change a fixed-size buffer (stdin,
+/* stdout, stderr) are not allowed.
+/*
+/* NOTE: the VSTREAM_CTL_BUFSIZE argument type is ssize_t, not
+/* int. Use an explicit cast to avoid problems on LP64
+/* environments and other environments where ssize_t is larger
+/* than int.
/* .PP
/* vstream_fileno() gives access to the file handle associated with
/* a buffered stream. With streams that have separate read/write
* Initialization of the three pre-defined streams. Pre-allocate a static
* I/O buffer for the standard error stream, so that the error handler can
* produce a diagnostic even when memory allocation fails.
+ *
+ * XXX We don't (yet) statically initialize the req_bufsize field: it is the
+ * last VSTREAM member so we don't break Postfix 2.4 binary compatibility,
+ * and Wietse doesn't know how to specify an initializer for the jmp_buf
+ * VSTREAM member (which can be a struct or an array) without collateral
+ * damage to the source code. We can fix the initialization later in the
+ * Postfix 2.5 development cycle.
*/
static unsigned char vstream_fstd_buf[VSTREAM_BUFSIZE];
/*
* Remember the direction. If this is the first PUT operation for this
- * stream, allocate a new buffer; obviously there is no data to be
- * flushed yet. Otherwise, flush the buffer if it is full.
+ * stream or if the buffer is smaller than the requested size, allocate a
+ * new buffer; obviously there is no data to be flushed yet. Otherwise,
+ * flush the buffer.
*/
- if (bp->data == 0) {
- vstream_buf_alloc(bp, VSTREAM_BUFSIZE);
- if (bp->flags & VSTREAM_FLAG_DOUBLE)
- VSTREAM_SAVE_STATE(stream, write_buf, write_fd);
+ if (stream->req_bufsize == 0)
+ stream->req_bufsize = VSTREAM_BUFSIZE; /* Postfix 2.4 binary compat. */
+ if (bp->len < stream->req_bufsize) {
+ vstream_buf_alloc(bp, stream->req_bufsize);
} else if (bp->cnt <= 0) {
if (VSTREAM_FFLUSH_SOME(stream))
return (VSTREAM_EOF);
#define VSTREAM_ROUNDUP(count, base) VSTREAM_TRUNCATE(count + base - 1, base)
if (want > bp->cnt) {
- if ((used = bp->len - bp->cnt) > VSTREAM_BUFSIZE)
- if (vstream_fflush_some(stream, VSTREAM_TRUNCATE(used, VSTREAM_BUFSIZE)))
+ if (stream->req_bufsize == 0)
+ stream->req_bufsize = VSTREAM_BUFSIZE; /* 2.4 binary compat. */
+ if ((used = bp->len - bp->cnt) > stream->req_bufsize)
+ if (vstream_fflush_some(stream, VSTREAM_TRUNCATE(used, stream->req_bufsize)))
return (VSTREAM_EOF);
if ((shortage = (want - bp->cnt)) > 0) {
- incr = VSTREAM_ROUNDUP(shortage, VSTREAM_BUFSIZE);
- vstream_buf_alloc(bp, bp->len + incr);
+ if (shortage > __MAXINT__(ssize_t) -bp->len - stream->req_bufsize) {
+ bp->flags |= VSTREAM_FLAG_ERR;
+ } else {
+ incr = VSTREAM_ROUNDUP(shortage, stream->req_bufsize);
+ vstream_buf_alloc(bp, bp->len + incr);
+ }
}
}
return (vstream_ferror(stream) ? VSTREAM_EOF : 0); /* mmap() may fail */
stream->context = 0;
stream->jbuf = 0;
stream->iotime.tv_sec = stream->iotime.tv_usec = 0;
+ stream->req_bufsize = VSTREAM_BUFSIZE;
return (stream);
}
va_list ap;
int floor;
int old_fd;
+ ssize_t req_bufsize = 0;
for (va_start(ap, name); name != VSTREAM_CTL_END; name = va_arg(ap, int)) {
switch (name) {
}
break;
#endif
+
+ /*
+ * Postpone memory (re)allocation until the space is needed.
+ */
+ case VSTREAM_CTL_BUFSIZE:
+ req_bufsize = va_arg(ap, ssize_t);
+ if (req_bufsize < 0)
+ msg_panic("VSTREAM_CTL_BUFSIZE with negative size: %ld",
+ (long) req_bufsize);
+ if (req_bufsize > stream->req_bufsize)
+ stream->req_bufsize = req_bufsize;
+ break;
default:
msg_panic("%s: bad name %d", myname, name);
}
int timeout; /* read/write timout */
jmp_buf *jbuf; /* exception handling */
struct timeval iotime; /* time of last fill/flush */
+ /* At bottom for Postfix 2.4 binary compatibility. */
+ ssize_t req_bufsize; /* write buffer size */
} VSTREAM;
extern VSTREAM vstream_fstd[]; /* pre-defined streams */
#ifdef F_DUPFD
#define VSTREAM_CTL_DUPFD 11
#endif
+#define VSTREAM_CTL_BUFSIZE 12
extern VSTREAM *PRINTFLIKE(1, 2) vstream_printf(const char *,...);
extern VSTREAM *PRINTFLIKE(2, 3) vstream_fprintf(VSTREAM *, const char *,...);
#define vstream_setjmp(stream) setjmp((stream)->jbuf[0])
#define vstream_longjmp(stream, val) longjmp((stream)->jbuf[0], (val))
+ /*
+ * Tweaks and workarounds.
+ */
+extern int vstream_tweak_sock(VSTREAM *);
+extern int vstream_tweak_tcp(VSTREAM *);
+
/* LICENSE
/* .ad
/* .fi
--- /dev/null
+/*++
+/* NAME
+/* vstream_tweak 3
+/* SUMMARY
+/* performance tweaks
+/* SYNOPSIS
+/* #include <vstream.h>
+/*
+/* VSTREAM *vstream_tweak_sock(stream)
+/* VSTREAM *stream;
+/*
+/* VSTREAM *vstream_tweak_tcp(stream)
+/* VSTREAM *stream;
+/* DESCRIPTION
+/* vstream_tweak_sock() does a best effort to boost your
+/* network performance on the specified generic stream.
+/*
+/* vstream_tweak_tcp() does a best effort to boost your
+/* Internet performance on the specified TCP stream.
+/*
+/* Arguments:
+/* .IP stream
+/* The stream being boosted.
+/* DIAGNOSTICS
+/* Panics: interface violations.
+/* LICENSE
+/* .ad
+/* .fi
+/* The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/* Wietse Venema
+/* IBM T.J. Watson Research
+/* P.O. Box 704
+/* Yorktown Heights, NY 10598, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstream.h>
+
+/* Application-specific. */
+
+#ifdef HAS_IPV6
+#define SOCKADDR_STORAGE struct sockaddr_storage
+#else
+#define SOCKADDR_STORAGE struct sockaddr
+#endif
+
+/* vstream_tweak_sock - boost your generic network performance */
+
+int vstream_tweak_sock(VSTREAM *fp)
+{
+ SOCKADDR_STORAGE ss;
+ struct sockaddr *sa = (struct sockaddr *) & ss;
+ SOCKADDR_SIZE sa_length = sizeof(ss);
+ int ret;
+
+ /*
+ * If the caller doesn't know if this socket is AF_LOCAL, AF_INET, etc.,
+ * figure it out for them.
+ */
+ if ((ret = getsockname(vstream_fileno(fp), sa, &sa_length)) >= 0) {
+ switch (sa->sa_family) {
+#ifdef AF_INET6
+ case AF_INET6:
+#endif
+ case AF_INET:
+ ret = vstream_tweak_tcp(fp);
+ break;
+ }
+ }
+ return (ret);
+}
+
+/* vstream_tweak_tcp - boost your TCP performance */
+
+int vstream_tweak_tcp(VSTREAM *fp)
+{
+ const char *myname = "vstream_tweak_tcp";
+ int mss;
+ SOCKOPT_SIZE mss_len = sizeof(mss);
+ int err;
+
+ /*
+ * Avoid Nagle delays when VSTREAM buffers are smaller than the MSS.
+ *
+ * Forcing TCP_NODELAY to be "always on" would hurt performance in the
+ * common case where VSTREAM buffers are larger than the MSS.
+ *
+ * Instead we ask the kernel what the current MSS is, and take appropriate
+ * action. Linux <= 2.2 getsockopt(TCP_MAXSEG) always returns zero (or
+ * whatever value was stored last with setsockopt()).
+ */
+ if ((err = getsockopt(vstream_fileno(fp), IPPROTO_TCP, TCP_MAXSEG,
+ (char *) &mss, &mss_len)) < 0) {
+ msg_warn("%s: getsockopt TCP_MAXSEG: %m", myname);
+ return (err);
+ }
+ if (msg_verbose)
+ msg_info("%s: TCP_MAXSEG %d", myname, mss);
+
+ /*
+ * Fix for recent Postfix versions: increase the VSTREAM buffer size if
+ * the VSTREAM buffer is smaller than the MSS. Note: the MSS may change
+ * when the route changes and IP path MTU discovery is turned on, so we
+ * choose a somewhat larger buffer.
+ */
+#ifdef VSTREAM_CTL_BUFSIZE
+ if (mss > 0) {
+ if (mss < __MAXINT__(ssize_t) /2)
+ mss *= 2;
+ vstream_control(fp,
+ VSTREAM_CTL_BUFSIZE, (ssize_t) mss,
+ VSTREAM_CTL_END);
+ }
+
+ /*
+ * Workaround for older Postfix versions: turn on TCP_NODELAY if the
+ * VSTREAM buffer size is smaller than the MSS.
+ */
+#else
+ if (mss > VSTREAM_BUFSIZE) {
+ int nodelay = 0;
+
+ if ((err = setsockopt(vstream_fileno(fp), IPPROTO_TCP, TCP_NODELAY,
+ (char *) &nodelay, sizeof(nodelay))) < 0)
+ msg_warn("%s: setsockopt TCP_NODELAY: %m", myname);
+ }
+#endif
+ return (err);
+}