]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.3.12 v2.3.12
authorWietse Venema <wietse@porcupine.org>
Tue, 31 Jul 2007 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <postfix-users@dukhovni.org>
Sat, 10 Feb 2018 21:08:39 +0000 (16:08 -0500)
14 files changed:
postfix/HISTORY
postfix/src/cleanup/cleanup_envelope.c
postfix/src/cleanup/cleanup_milter.c
postfix/src/global/mail_version.h
postfix/src/milter/milter.c
postfix/src/milter/milter.h
postfix/src/milter/milter8.c
postfix/src/smtp/smtp_connect.c
postfix/src/smtpd/smtpd.c
postfix/src/smtpstone/qmqp-source.c
postfix/src/smtpstone/smtp-source.c
postfix/src/util/Makefile.in
postfix/src/util/vstream.h
postfix/src/util/vstream_tweak.c [new file with mode: 0644]

index 603153856ec26fbd2acc5511e2736db0f1a4d2ce..0c6876a3c439c8844cc093f34f0ed72084e93963 100644 (file)
@@ -13045,3 +13045,30 @@ Apologies for any names omitted.
        Portability: Victor helpfully pointed out that change
        20070425 broke on non-IPv6 systems. Files: smtpd/smtpd_peer.c,
        qmqpd/qmqpd_peer.c.
+
+20070722
+
+       Cleanup: Milter client error handling, so that the (Postfix
+       SMTP server's Milter client) does not get out of sync with
+       Milter applications after the (cleanup server's Milter
+       client) encounters some non-recoverable problem.  Files:
+       milter/milter8.c, smtpd/smtpd.c.  Back-port from Postfix
+       2.4/2.5.
+
+20070729
+
+       Performance: workaround for poor TCP performance on loopback
+       (127.0.0.1) connections. Reported by Mark Martinec.  Files:
+       util/vstream_tweak.c, milter/milter8.c, smtp/smtp_connect.c,
+       smtpstone/*source.c. Back-port from Postfix 2.4/2.5.
+
+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.
+       Back-port from Postfix 2.4/2.5.
index eb4171bdfe53a06ebe8d13392d4f3dc42f5009f6..939872a777434558e73082decf999b775a2624fa 100644 (file)
@@ -147,13 +147,7 @@ static void cleanup_envelope_process(CLEANUP_STATE *state, int type,
 #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;
     }
index da4fddb1fa2c22b1ba26c02fc9405bc7d27973fa..ccb97b251d18e036857f5c18d34f9f105f832d59 100644 (file)
@@ -1339,6 +1339,8 @@ static const char *cleanup_milter_eval(const char *name, void *ptr)
 
 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,
index 2cb95c5f3e8f1d664b6fa113c5acec426e9031ab..b35f5d985f29141ad6737febee2fdae764f874da 100644 (file)
@@ -20,8 +20,8 @@
   * Patches change both the patchlevel and the release date. Snapshots have no
   * patchlevel; they change the release date only.
   */
-#define MAIL_RELEASE_DATE      "20070531"
-#define MAIL_VERSION_NUMBER    "2.3.11"
+#define MAIL_RELEASE_DATE      "20070731"
+#define MAIL_VERSION_NUMBER    "2.3.12"
 
 #ifdef SNAPSHOT
 # define MAIL_VERSION_DATE     "-" MAIL_RELEASE_DATE
index f006ffc71a595f36187282eb970bae35c9d20915..effc2612ec2c32977fd8583b2d12ffa8c00cad1e 100644 (file)
 /*     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
@@ -587,6 +594,16 @@ void    milter_free(MILTERS *milters)
 #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)
@@ -606,8 +623,6 @@ 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);
 
     /*
@@ -656,9 +671,6 @@ MILTERS *milter_receive(VSTREAM *stream, int count)
     VSTRING *eod_macros;
     VSTRING *unk_macros;
 
-    if (count == 0)
-       return (0);
-
     /*
      * Receive filter macros.
      */
index 9e480193a8d62ddeb5b10d2cd566759b83e7db62..82805285e38d4bae47b488da359a5da5ce4a8cb6 100644 (file)
@@ -99,6 +99,7 @@ extern const char *milter_unknown_event(MILTERS *, const char *);
 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 *);
index 96f637b49c5c6116735f03e771a294112115efbf..ee5fadeac1f04dfa79ebba8de5b9f33e1704973b 100644 (file)
 #include <string.h>
 #include <stdarg.h>
 
+#ifndef SHUT_RDWR
+#define SHUT_RDWR      2
+#endif
+
 /* Sendmail 8 Milter protocol. */
 
 #ifdef USE_LIBMILTER_INCLUDES
@@ -436,7 +440,16 @@ static int milter8_conf_error(MILTER8 *milter)
 {
     const char *reply;
 
+    /*
+     * XXX When the cleanup server closes its end of the Milter socket while
+     * editing a queue file, the SMTP server is left out of sync with the
+     * Milter. Sending an ABORT to the Milters will not restore
+     * synchronization, because there may be any number of Milter replies
+     * already in flight. Workaround: poison the socket and force the SMTP
+     * server to abandon it.
+     */
     if (milter->fp != 0) {
+       (void) shutdown(vstream_fileno(milter->fp), SHUT_RDWR);
        (void) vstream_fclose(milter->fp);
        milter->fp = 0;
     }
@@ -455,7 +468,16 @@ static int milter8_comm_error(MILTER8 *milter)
 {
     const char *reply;
 
+    /*
+     * XXX When the cleanup server closes its end of the Milter socket while
+     * editing a queue file, the SMTP server is left out of sync with the
+     * Milter. Sending an ABORT to the Milters will not restore
+     * synchronization, because there may be any number of Milter replies
+     * already in flight. Workaround: poison the socket and force the SMTP
+     * server to abandon it.
+     */
     if (milter->fp != 0) {
+       (void) shutdown(vstream_fileno(milter->fp), SHUT_RDWR);
        (void) vstream_fclose(milter->fp);
        milter->fp = 0;
     }
@@ -868,7 +890,9 @@ static const char *milter8_event(MILTER8 *milter, int event,
     const char *smfir_name;
     MILTERS *parent;
     UINT32_TYPE index;
-    const char *edit_resp;
+    const char *edit_resp = 0;
+    const char *retval = 0;
+    int     done = 0;
 
 #define DONT_SKIP_REPLY        0
 
@@ -970,17 +994,39 @@ static const char *milter8_event(MILTER8 *milter, int event,
     /*
      * Receive the reply or replies.
      * 
+     * Intercept all loop exits so that we can do post-(queue file edit)
+     * processing.
+     * 
      * XXX Bound the loop iteration count.
+     * 
+     * In the end-of-body stage, the Milter may reply with one or more queue
+     * file edit requests before it replies with its final decision: accept,
+     * reject, etc. After a local queue file edit error (file too big, media
+     * write error), do not close the Milter socket in the cleanup server.
+     * Instead skip all further Milter replies until the final decision. This
+     * way the Postfix SMTP server stays in sync with the Milter, and Postfix
+     * doesn't have to lose the ability to handle multiple deliveries within
+     * the same SMTP session.
      */
 #define IN_CONNECT_EVENT(e) ((e) == SMFIC_CONNECT || (e) == SMFIC_HELO)
 
-    for (;;) {
+    /*
+     * XXX Don't evaluate this macro's argument multiple times. Since we use
+     * "continue" the macro can't be enclosed in do .. while (0).
+     */
+#define MILTER8_EVENT_BREAK(s) { \
+       retval = (s); \
+       done = 1; \
+       continue; \
+    }
+
+    while (done == 0) {
        char   *cp;
        char   *rp;
        char    ch;
 
        if (milter8_read_resp(milter, event, &cmd, &data_size) != 0)
-           return (milter->def_reply);
+           MILTER8_EVENT_BREAK(milter->def_reply);
        if (msg_verbose)
            msg_info("reply: %s data %ld bytes",
                     (smfir_name = str_name_code(smfir_table, cmd)) != 0 ?
@@ -1001,7 +1047,7 @@ static const char *milter8_event(MILTER8 *milter, int event,
        case SMFIR_CONTINUE:
            if (data_size != 0)
                break;
-           return (milter->def_reply);
+           MILTER8_EVENT_BREAK(milter->def_reply);
 
            /*
             * Decision: accept this message, or accept all further commands
@@ -1021,7 +1067,7 @@ static const char *milter8_event(MILTER8 *milter, int event,
                /* No more events for this message. */
                milter->state = MILTER8_STAT_ACCEPT_MSG;
            }
-           return (milter->def_reply);
+           MILTER8_EVENT_BREAK(milter->def_reply);
 
            /*
             * Decision: accept and silently discard this message. According
@@ -1035,12 +1081,11 @@ static const char *milter8_event(MILTER8 *milter, int event,
            if (IN_CONNECT_EVENT(event)) {
                msg_warn("milter %s: DISCARD action is not allowed "
                         "for connect or helo", milter->m.name);
-               milter8_conf_error(milter);
-               return (milter->def_reply);
+               MILTER8_EVENT_BREAK(milter->def_reply);
            } else {
                /* No more events for this message. */
                milter->state = MILTER8_STAT_ACCEPT_MSG;
-               return ("D");
+               MILTER8_EVENT_BREAK("D");
            }
 
            /*
@@ -1055,9 +1100,9 @@ static const char *milter8_event(MILTER8 *milter, int event,
                milter8_close_stream(milter);
 #endif
                milter->state = MILTER8_STAT_REJECT_CON;
-               return (milter8_def_reply(milter, "550 5.7.1 Command rejected"));
+               MILTER8_EVENT_BREAK(milter8_def_reply(milter, "550 5.7.1 Command rejected"));
            } else {
-               return ("550 5.7.1 Command rejected");
+               MILTER8_EVENT_BREAK("550 5.7.1 Command rejected");
            }
 
            /*
@@ -1072,10 +1117,10 @@ static const char *milter8_event(MILTER8 *milter, int event,
                milter8_close_stream(milter);
 #endif
                milter->state = MILTER8_STAT_REJECT_CON;
-               return (milter8_def_reply(milter,
+               MILTER8_EVENT_BREAK(milter8_def_reply(milter,
                        "451 4.7.1 Service unavailable - try again later"));
            } else {
-               return ("451 4.7.1 Service unavailable - try again later");
+               MILTER8_EVENT_BREAK("451 4.7.1 Service unavailable - try again later");
            }
 
            /*
@@ -1090,7 +1135,7 @@ static const char *milter8_event(MILTER8 *milter, int event,
            milter8_close_stream(milter);
 #endif
            milter->state = MILTER8_STAT_REJECT_CON;
-           return (milter8_def_reply(milter, "S"));
+           MILTER8_EVENT_BREAK(milter8_def_reply(milter, "S"));
 #endif
 
            /*
@@ -1107,7 +1152,7 @@ static const char *milter8_event(MILTER8 *milter, int event,
            if (milter8_read_data(milter, data_size,
                                  MILTER8_DATA_BUFFER, milter->buf,
                                  MILTER8_DATA_END) != 0)
-               return (milter->def_reply);
+               MILTER8_EVENT_BREAK(milter->def_reply);
            if ((STR(milter->buf)[0] != '4' && STR(milter->buf)[0] != '5')
                || !ISDIGIT(STR(milter->buf)[1])
                || !ISDIGIT(STR(milter->buf)[2])
@@ -1116,7 +1161,7 @@ static const char *milter8_event(MILTER8 *milter, int event,
                msg_warn("milter %s: malformed reply: %s",
                         milter->m.name, STR(milter->buf));
                milter8_conf_error(milter);
-               return (milter->def_reply);
+               MILTER8_EVENT_BREAK(milter->def_reply);
            }
            if ((rp = cp = strchr(STR(milter->buf), '%')) != 0) {
                for (;;) {
@@ -1132,9 +1177,9 @@ static const char *milter8_event(MILTER8 *milter, int event,
                milter8_close_stream(milter);
 #endif
                milter->state = MILTER8_STAT_REJECT_CON;
-               return (milter8_def_reply(milter, STR(milter->buf)));
+               MILTER8_EVENT_BREAK(milter8_def_reply(milter, STR(milter->buf)));
            } else {
-               return (STR(milter->buf));
+               MILTER8_EVENT_BREAK(STR(milter->buf));
            }
 
            /*
@@ -1148,8 +1193,8 @@ static const char *milter8_event(MILTER8 *milter, int event,
            if (milter8_read_data(milter, data_size,
                                  MILTER8_DATA_BUFFER, milter->buf,
                                  MILTER8_DATA_END) != 0)
-               return (milter->def_reply);
-           return ("H");
+               MILTER8_EVENT_BREAK(milter->def_reply);
+           MILTER8_EVENT_BREAK("H");
 #endif
 
            /*
@@ -1170,7 +1215,10 @@ static const char *milter8_event(MILTER8 *milter, int event,
                                          MILTER8_DATA_STRING, milter->buf,
                                          MILTER8_DATA_STRING, milter->body,
                                          MILTER8_DATA_END) != 0)
-                       return (milter->def_reply);
+                       MILTER8_EVENT_BREAK(milter->def_reply);
+                   /* Skip to the next request after previous edit error. */
+                   if (edit_resp)
+                       continue;
                    parent = milter->m.parent;
                    /* XXX Sendmail 8 compatibility. */
                    if (index == 0)
@@ -1179,13 +1227,13 @@ static const char *milter8_event(MILTER8 *milter, int event,
                        msg_warn("milter %s: bad change header index: %ld",
                                 milter->m.name, (long) index);
                        milter8_conf_error(milter);
-                       return (milter->def_reply);
+                       MILTER8_EVENT_BREAK(milter->def_reply);
                    }
                    if (LEN(milter->buf) == 0) {
                        msg_warn("milter %s: null change header name",
                                 milter->m.name);
                        milter8_conf_error(milter);
-                       return (milter->def_reply);
+                       MILTER8_EVENT_BREAK(milter->def_reply);
                    }
                    if (STR(milter->body)[0])
                        edit_resp = parent->upd_header(parent->chg_context,
@@ -1196,8 +1244,6 @@ static const char *milter8_event(MILTER8 *milter, int event,
                        edit_resp = parent->del_header(parent->chg_context,
                                                       (ssize_t) index,
                                                       STR(milter->buf));
-                   if (edit_resp)
-                       return (edit_resp);
                    continue;
 #endif
 
@@ -1209,13 +1255,14 @@ static const char *milter8_event(MILTER8 *milter, int event,
                                          MILTER8_DATA_STRING, milter->buf,
                                          MILTER8_DATA_STRING, milter->body,
                                          MILTER8_DATA_END) != 0)
-                       return (milter->def_reply);
+                       MILTER8_EVENT_BREAK(milter->def_reply);
+                   /* Skip to the next request after previous edit error. */
+                   if (edit_resp)
+                       continue;
                    parent = milter->m.parent;
                    edit_resp = parent->add_header(parent->chg_context,
                                                   STR(milter->buf),
                                                   STR(milter->body));
-                   if (edit_resp)
-                       return (edit_resp);
                    continue;
 
                    /*
@@ -1231,20 +1278,21 @@ static const char *milter8_event(MILTER8 *milter, int event,
                                          MILTER8_DATA_STRING, milter->buf,
                                          MILTER8_DATA_STRING, milter->body,
                                          MILTER8_DATA_END) != 0)
-                       return (milter->def_reply);
+                       MILTER8_EVENT_BREAK(milter->def_reply);
+                   /* Skip to the next request after previous edit error. */
+                   if (edit_resp)
+                       continue;
                    if ((ssize_t) index + 1 < 1) {
                        msg_warn("milter %s: bad insert header index: %ld",
                                 milter->m.name, (long) index);
                        milter8_conf_error(milter);
-                       return (milter->def_reply);
+                       MILTER8_EVENT_BREAK(milter->def_reply);
                    }
                    parent = milter->m.parent;
                    edit_resp = parent->ins_header(parent->chg_context,
                                                   (ssize_t) index + 1,
                                                   STR(milter->buf),
                                                   STR(milter->body));
-                   if (edit_resp)
-                       return (edit_resp);
                    continue;
 #endif
 
@@ -1255,12 +1303,13 @@ static const char *milter8_event(MILTER8 *milter, int event,
                    if (milter8_read_data(milter, data_size,
                                          MILTER8_DATA_STRING, milter->buf,
                                          MILTER8_DATA_END) != 0)
-                       return (milter->def_reply);
+                       MILTER8_EVENT_BREAK(milter->def_reply);
+                   /* Skip to the next request after previous edit error. */
+                   if (edit_resp)
+                       continue;
                    parent = milter->m.parent;
                    edit_resp = parent->add_rcpt(parent->chg_context,
                                                 STR(milter->buf));
-                   if (edit_resp)
-                       return (edit_resp);
                    continue;
 
                    /*
@@ -1270,12 +1319,13 @@ static const char *milter8_event(MILTER8 *milter, int event,
                    if (milter8_read_data(milter, data_size,
                                          MILTER8_DATA_STRING, milter->buf,
                                          MILTER8_DATA_END) != 0)
-                       return (milter->def_reply);
+                       MILTER8_EVENT_BREAK(milter->def_reply);
+                   /* Skip to the next request after previous edit error. */
+                   if (edit_resp)
+                       continue;
                    parent = milter->m.parent;
                    edit_resp = parent->del_rcpt(parent->chg_context,
                                                 STR(milter->buf));
-                   if (edit_resp)
-                       return (edit_resp);
                    continue;
 
                    /*
@@ -1287,12 +1337,13 @@ static const char *milter8_event(MILTER8 *milter, int event,
                    if (milter8_read_data(milter, data_size,
                                          MILTER8_DATA_BUFFER, milter->body,
                                          MILTER8_DATA_END) != 0)
-                       return (milter->def_reply);
+                       MILTER8_EVENT_BREAK(milter->def_reply);
+                   /* Skip to the next request after previous edit error. */
+                   if (edit_resp)
+                       continue;
                    parent = milter->m.parent;
                    edit_resp = parent->repl_body(parent->chg_context,
                                                  milter->body);
-                   if (edit_resp)
-                       return (edit_resp);
                    continue;
 #endif
                }
@@ -1304,7 +1355,7 @@ static const char *milter8_event(MILTER8 *milter, int event,
                     (smfic_name = str_name_code(smfic_table, event)) != 0 ?
                     smfic_name : "(unknown MTA event)");
            milter8_comm_error(milter);
-           return (milter->def_reply);
+           MILTER8_EVENT_BREAK(milter->def_reply);
        }
 
        /*
@@ -1315,8 +1366,19 @@ static const char *milter8_event(MILTER8 *milter, int event,
        milter->m.name, (smfir_name = str_name_code(smfir_table, cmd)) != 0 ?
                 smfir_name : "unknown", (long) data_len);
        milter8_comm_error(milter);
-       return (milter->def_reply);
+       MILTER8_EVENT_BREAK(milter->def_reply);
     }
+
+    /*
+     * XXX Some cleanup clients ask the cleanup server to bounce mail for
+     * them. In that case we must override a hard reject retval result after
+     * queue file update failure. This is not a big problem; the odds are
+     * small that a Milter application sends a hard reject after replacing
+     * the message body.
+     */
+    if (edit_resp && (retval == 0 || strchr("DS4", retval[0]) == 0))
+       retval = edit_resp;
+    return (retval);
 }
 
 /* milter8_connect - connect to filter */
@@ -1471,6 +1533,9 @@ static void milter8_connect(MILTER8 *milter)
                    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
@@ -2372,6 +2437,8 @@ MILTER *milter8_receive(VSTREAM *stream, MILTERS *parent)
                            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;
index 19b5267f4300c9221fa5e2db2f4eba6bbaa4cce2..650432dc75fcfc4a389fa60752c2ba34abae831b 100644 (file)
@@ -303,6 +303,16 @@ static SMTP_SESSION *smtp_connect_sock(int sock, struct sockaddr * sa,
     }
     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.
      */
index 20f462397d0397643c37a9bb3607bc0059beaa0d..7713fcac5b27f25a82fbf47361825e5bc5b6093c 100644 (file)
@@ -1618,7 +1618,8 @@ static int mail_open_stream(SMTPD_STATE *state)
        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)
@@ -2516,6 +2517,10 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
      */
     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);
@@ -2731,6 +2736,25 @@ static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
        state->cleanup = 0;
     }
 
+    /*
+     * XXX If we lose the cleanup server while it is editing a queue file,
+     * the Postfix SMTP server will be out of sync with Milter applications.
+     * Sending an ABORT to the Milters is not sufficient to restore
+     * synchronization, because there may be any number of Milter replies
+     * already in flight. Destroying and recreating the Milters (and faking
+     * the connect and ehlo events) is too much trouble for testing and
+     * maintenance. Workaround: force the Postfix SMTP server to hang up with
+     * a 421 response in the rare case that the cleanup server breaks AND
+     * that the remote SMTP client continues the session after end-of-data.
+     * 
+     * XXX Should use something other than CLEANUP_STAT_WRITE when we lose
+     * contact with the cleanup server. This requires changes to the
+     * mail_stream module and its users (smtpd, qmqpd, perhaps sendmail).
+     * That is too much change for a stable release.
+     */
+    if (smtpd_milters != 0 && (state->err & CLEANUP_STAT_WRITE) != 0)
+       state->access_denied = mystrdup("421 4.3.0 Mail system error");
+
     /*
      * Handle any errors. One message may suffer from multiple errors, so
      * complain only about the most severe error. Forgive any previous client
index d06054215edbf2539c000fd02fd540116f69ae74..f12da47fdd1857774d8834a028f78833ed4a84f9 100644 (file)
@@ -352,6 +352,13 @@ static void connect_done(int unused_event, char *context)
        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);
     }
 }
index 5f84e43320189ea553129ceb33661519f1af98ad..5c386661b45b9f5b4d88a5c334f53aa7ba72f047 100644 (file)
@@ -467,6 +467,13 @@ static void connect_done(int unused_event, char *context)
        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);
     }
 }
 
index aadfe11c9aeb101c9e8bfff9aa14e3db4163677b..12a82e665e3d83c7a1409d183f2412ac310556d8 100644 (file)
@@ -30,7 +30,7 @@ SRCS  = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \
        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
+       allascii.c load_file.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 \
@@ -62,7 +62,7 @@ OBJS  = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.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
+       allascii.o load_file.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 \
@@ -1597,6 +1597,11 @@ vstream_popen.o: sys_defs.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
index 37c9efba9535f9532f33bf1fade70de51b98bb12..0f5eb8b7aa7a6306e7e586888d5dd025d679230e 100644 (file)
@@ -146,6 +146,12 @@ extern ssize_t vstream_peek(VSTREAM *);
 #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
diff --git a/postfix/src/util/vstream_tweak.c b/postfix/src/util/vstream_tweak.c
new file mode 100644 (file)
index 0000000..d1afac8
--- /dev/null
@@ -0,0 +1,139 @@
+/*++
+/* 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 = 1;
+
+       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);
+}