Prereq: "2.3.11" diff -cr --new-file /var/tmp/postfix-2.3.11/src/global/mail_version.h ./src/global/mail_version.h *** /var/tmp/postfix-2.3.11/src/global/mail_version.h Thu May 31 14:20:02 2007 --- ./src/global/mail_version.h Tue Jul 31 15:04:02 2007 *************** *** 20,27 **** * 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" #ifdef SNAPSHOT # define MAIL_VERSION_DATE "-" MAIL_RELEASE_DATE --- 20,27 ---- * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ ! #define MAIL_RELEASE_DATE "20070731" ! #define MAIL_VERSION_NUMBER "2.3.12" #ifdef SNAPSHOT # define MAIL_VERSION_DATE "-" MAIL_RELEASE_DATE diff -cr --new-file /var/tmp/postfix-2.3.11/HISTORY ./HISTORY *** /var/tmp/postfix-2.3.11/HISTORY Thu May 31 11:22:44 2007 --- ./HISTORY Tue Jul 31 17:05:43 2007 *************** *** 13045,13047 **** --- 13045,13074 ---- 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. diff -cr --new-file /var/tmp/postfix-2.3.11/src/cleanup/cleanup_envelope.c ./src/cleanup/cleanup_envelope.c *** /var/tmp/postfix-2.3.11/src/cleanup/cleanup_envelope.c Tue Jun 13 17:59:07 2006 --- ./src/cleanup/cleanup_envelope.c Tue Jul 31 15:15:43 2007 *************** *** 147,159 **** #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) cleanup_milter_receive(state, milter_count); return; } --- 147,153 ---- #endif if (type == REC_TYPE_MILT_COUNT) { /* Not part of queue file format. */ ! if ((milter_count = atoi(buf)) >= 0) cleanup_milter_receive(state, milter_count); return; } diff -cr --new-file /var/tmp/postfix-2.3.11/src/cleanup/cleanup_milter.c ./src/cleanup/cleanup_milter.c *** /var/tmp/postfix-2.3.11/src/cleanup/cleanup_milter.c Wed Dec 20 19:44:08 2006 --- ./src/cleanup/cleanup_milter.c Tue Jul 31 15:12:03 2007 *************** *** 1339,1344 **** --- 1339,1346 ---- 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, diff -cr --new-file /var/tmp/postfix-2.3.11/src/milter/milter.c ./src/milter/milter.c *** /var/tmp/postfix-2.3.11/src/milter/milter.c Thu Jul 27 12:42:41 2006 --- ./src/milter/milter.c Tue Jul 31 15:33:36 2007 *************** *** 97,102 **** --- 97,106 ---- /* 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 *************** *** 192,197 **** --- 196,204 ---- /* 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,592 **** --- 594,609 ---- #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,613 **** 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); /* --- 623,628 ---- *************** *** 655,663 **** VSTRING *data_macros; VSTRING *eod_macros; VSTRING *unk_macros; - - if (count == 0) - return (0); /* * Receive filter macros. --- 670,675 ---- diff -cr --new-file /var/tmp/postfix-2.3.11/src/milter/milter.h ./src/milter/milter.h *** /var/tmp/postfix-2.3.11/src/milter/milter.h Sun Jul 2 17:54:20 2006 --- ./src/milter/milter.h Tue Jul 31 15:29:29 2007 *************** *** 99,104 **** --- 99,105 ---- 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 *); diff -cr --new-file /var/tmp/postfix-2.3.11/src/milter/milter8.c ./src/milter/milter8.c *** /var/tmp/postfix-2.3.11/src/milter/milter8.c Sun Dec 24 16:23:47 2006 --- ./src/milter/milter8.c Tue Jul 31 15:38:33 2007 *************** *** 64,69 **** --- 64,73 ---- #include #include + #ifndef SHUT_RDWR + #define SHUT_RDWR 2 + #endif + /* Sendmail 8 Milter protocol. */ #ifdef USE_LIBMILTER_INCLUDES *************** *** 436,442 **** --- 440,455 ---- { 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,461 **** --- 468,483 ---- { 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,874 **** const char *smfir_name; MILTERS *parent; UINT32_TYPE index; ! const char *edit_resp; #define DONT_SKIP_REPLY 0 --- 890,898 ---- const char *smfir_name; MILTERS *parent; UINT32_TYPE index; ! const char *edit_resp = 0; ! const char *retval = 0; ! int done = 0; #define DONT_SKIP_REPLY 0 *************** *** 970,986 **** /* * Receive the reply or replies. * * XXX Bound the loop iteration count. */ #define IN_CONNECT_EVENT(e) ((e) == SMFIC_CONNECT || (e) == SMFIC_HELO) ! for (;;) { char *cp; char *rp; char ch; if (milter8_read_resp(milter, event, &cmd, &data_size) != 0) ! return (milter->def_reply); if (msg_verbose) msg_info("reply: %s data %ld bytes", (smfir_name = str_name_code(smfir_table, cmd)) != 0 ? --- 994,1032 ---- /* * 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) ! /* ! * 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) ! 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,1007 **** case SMFIR_CONTINUE: if (data_size != 0) break; ! return (milter->def_reply); /* * Decision: accept this message, or accept all further commands --- 1047,1053 ---- case SMFIR_CONTINUE: if (data_size != 0) break; ! MILTER8_EVENT_BREAK(milter->def_reply); /* * Decision: accept this message, or accept all further commands *************** *** 1021,1027 **** /* No more events for this message. */ milter->state = MILTER8_STAT_ACCEPT_MSG; } ! return (milter->def_reply); /* * Decision: accept and silently discard this message. According --- 1067,1073 ---- /* No more events for this message. */ milter->state = MILTER8_STAT_ACCEPT_MSG; } ! MILTER8_EVENT_BREAK(milter->def_reply); /* * Decision: accept and silently discard this message. According *************** *** 1035,1046 **** 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); } else { /* No more events for this message. */ milter->state = MILTER8_STAT_ACCEPT_MSG; ! return ("D"); } /* --- 1081,1091 ---- if (IN_CONNECT_EVENT(event)) { msg_warn("milter %s: DISCARD action is not allowed " "for connect or helo", milter->m.name); ! MILTER8_EVENT_BREAK(milter->def_reply); } else { /* No more events for this message. */ milter->state = MILTER8_STAT_ACCEPT_MSG; ! MILTER8_EVENT_BREAK("D"); } /* *************** *** 1055,1063 **** milter8_close_stream(milter); #endif milter->state = MILTER8_STAT_REJECT_CON; ! return (milter8_def_reply(milter, "550 5.7.1 Command rejected")); } else { ! return ("550 5.7.1 Command rejected"); } /* --- 1100,1108 ---- milter8_close_stream(milter); #endif milter->state = MILTER8_STAT_REJECT_CON; ! MILTER8_EVENT_BREAK(milter8_def_reply(milter, "550 5.7.1 Command rejected")); } else { ! MILTER8_EVENT_BREAK("550 5.7.1 Command rejected"); } /* *************** *** 1072,1081 **** milter8_close_stream(milter); #endif milter->state = MILTER8_STAT_REJECT_CON; ! return (milter8_def_reply(milter, "451 4.7.1 Service unavailable - try again later")); } else { ! return ("451 4.7.1 Service unavailable - try again later"); } /* --- 1117,1126 ---- milter8_close_stream(milter); #endif milter->state = MILTER8_STAT_REJECT_CON; ! MILTER8_EVENT_BREAK(milter8_def_reply(milter, "451 4.7.1 Service unavailable - try again later")); } else { ! MILTER8_EVENT_BREAK("451 4.7.1 Service unavailable - try again later"); } /* *************** *** 1090,1096 **** milter8_close_stream(milter); #endif milter->state = MILTER8_STAT_REJECT_CON; ! return (milter8_def_reply(milter, "S")); #endif /* --- 1135,1141 ---- milter8_close_stream(milter); #endif milter->state = MILTER8_STAT_REJECT_CON; ! MILTER8_EVENT_BREAK(milter8_def_reply(milter, "S")); #endif /* *************** *** 1107,1113 **** if (milter8_read_data(milter, data_size, MILTER8_DATA_BUFFER, milter->buf, MILTER8_DATA_END) != 0) ! return (milter->def_reply); if ((STR(milter->buf)[0] != '4' && STR(milter->buf)[0] != '5') || !ISDIGIT(STR(milter->buf)[1]) || !ISDIGIT(STR(milter->buf)[2]) --- 1152,1158 ---- if (milter8_read_data(milter, data_size, MILTER8_DATA_BUFFER, milter->buf, MILTER8_DATA_END) != 0) ! 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,1122 **** msg_warn("milter %s: malformed reply: %s", milter->m.name, STR(milter->buf)); milter8_conf_error(milter); ! return (milter->def_reply); } if ((rp = cp = strchr(STR(milter->buf), '%')) != 0) { for (;;) { --- 1161,1167 ---- msg_warn("milter %s: malformed reply: %s", milter->m.name, STR(milter->buf)); milter8_conf_error(milter); ! MILTER8_EVENT_BREAK(milter->def_reply); } if ((rp = cp = strchr(STR(milter->buf), '%')) != 0) { for (;;) { *************** *** 1132,1140 **** milter8_close_stream(milter); #endif milter->state = MILTER8_STAT_REJECT_CON; ! return (milter8_def_reply(milter, STR(milter->buf))); } else { ! return (STR(milter->buf)); } /* --- 1177,1185 ---- milter8_close_stream(milter); #endif milter->state = MILTER8_STAT_REJECT_CON; ! MILTER8_EVENT_BREAK(milter8_def_reply(milter, STR(milter->buf))); } else { ! MILTER8_EVENT_BREAK(STR(milter->buf)); } /* *************** *** 1148,1155 **** if (milter8_read_data(milter, data_size, MILTER8_DATA_BUFFER, milter->buf, MILTER8_DATA_END) != 0) ! return (milter->def_reply); ! return ("H"); #endif /* --- 1193,1200 ---- if (milter8_read_data(milter, data_size, MILTER8_DATA_BUFFER, milter->buf, MILTER8_DATA_END) != 0) ! MILTER8_EVENT_BREAK(milter->def_reply); ! MILTER8_EVENT_BREAK("H"); #endif /* *************** *** 1170,1176 **** MILTER8_DATA_STRING, milter->buf, MILTER8_DATA_STRING, milter->body, MILTER8_DATA_END) != 0) ! return (milter->def_reply); parent = milter->m.parent; /* XXX Sendmail 8 compatibility. */ if (index == 0) --- 1215,1224 ---- MILTER8_DATA_STRING, milter->buf, MILTER8_DATA_STRING, milter->body, MILTER8_DATA_END) != 0) ! 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,1191 **** msg_warn("milter %s: bad change header index: %ld", milter->m.name, (long) index); milter8_conf_error(milter); ! return (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); } if (STR(milter->body)[0]) edit_resp = parent->upd_header(parent->chg_context, --- 1227,1239 ---- msg_warn("milter %s: bad change header index: %ld", milter->m.name, (long) index); milter8_conf_error(milter); ! 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); ! MILTER8_EVENT_BREAK(milter->def_reply); } if (STR(milter->body)[0]) edit_resp = parent->upd_header(parent->chg_context, *************** *** 1196,1203 **** edit_resp = parent->del_header(parent->chg_context, (ssize_t) index, STR(milter->buf)); - if (edit_resp) - return (edit_resp); continue; #endif --- 1244,1249 ---- *************** *** 1209,1221 **** MILTER8_DATA_STRING, milter->buf, MILTER8_DATA_STRING, milter->body, MILTER8_DATA_END) != 0) ! return (milter->def_reply); 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; /* --- 1255,1268 ---- MILTER8_DATA_STRING, milter->buf, MILTER8_DATA_STRING, milter->body, MILTER8_DATA_END) != 0) ! 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)); continue; /* *************** *** 1231,1250 **** MILTER8_DATA_STRING, milter->buf, MILTER8_DATA_STRING, milter->body, MILTER8_DATA_END) != 0) ! return (milter->def_reply); 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); } 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 --- 1278,1298 ---- MILTER8_DATA_STRING, milter->buf, MILTER8_DATA_STRING, milter->body, MILTER8_DATA_END) != 0) ! 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); ! 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)); continue; #endif *************** *** 1255,1266 **** if (milter8_read_data(milter, data_size, MILTER8_DATA_STRING, milter->buf, MILTER8_DATA_END) != 0) ! return (milter->def_reply); parent = milter->m.parent; edit_resp = parent->add_rcpt(parent->chg_context, STR(milter->buf)); - if (edit_resp) - return (edit_resp); continue; /* --- 1303,1315 ---- if (milter8_read_data(milter, data_size, MILTER8_DATA_STRING, milter->buf, MILTER8_DATA_END) != 0) ! 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)); continue; /* *************** *** 1270,1281 **** if (milter8_read_data(milter, data_size, MILTER8_DATA_STRING, milter->buf, MILTER8_DATA_END) != 0) ! return (milter->def_reply); parent = milter->m.parent; edit_resp = parent->del_rcpt(parent->chg_context, STR(milter->buf)); - if (edit_resp) - return (edit_resp); continue; /* --- 1319,1331 ---- if (milter8_read_data(milter, data_size, MILTER8_DATA_STRING, milter->buf, MILTER8_DATA_END) != 0) ! 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)); continue; /* *************** *** 1287,1298 **** if (milter8_read_data(milter, data_size, MILTER8_DATA_BUFFER, milter->body, MILTER8_DATA_END) != 0) ! return (milter->def_reply); parent = milter->m.parent; edit_resp = parent->repl_body(parent->chg_context, milter->body); - if (edit_resp) - return (edit_resp); continue; #endif } --- 1337,1349 ---- if (milter8_read_data(milter, data_size, MILTER8_DATA_BUFFER, milter->body, MILTER8_DATA_END) != 0) ! 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); continue; #endif } *************** *** 1304,1310 **** (smfic_name = str_name_code(smfic_table, event)) != 0 ? smfic_name : "(unknown MTA event)"); milter8_comm_error(milter); ! return (milter->def_reply); } /* --- 1355,1361 ---- (smfic_name = str_name_code(smfic_table, event)) != 0 ? smfic_name : "(unknown MTA event)"); milter8_comm_error(milter); ! MILTER8_EVENT_BREAK(milter->def_reply); } /* *************** *** 1315,1322 **** 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_connect - connect to filter */ --- 1366,1384 ---- milter->m.name, (smfir_name = str_name_code(smfir_table, cmd)) != 0 ? smfir_name : "unknown", (long) data_len); milter8_comm_error(milter); ! 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,1476 **** --- 1533,1541 ---- 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,2377 **** --- 2437,2444 ---- 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; diff -cr --new-file /var/tmp/postfix-2.3.11/src/smtp/smtp_connect.c ./src/smtp/smtp_connect.c *** /var/tmp/postfix-2.3.11/src/smtp/smtp_connect.c Sun Dec 3 17:05:40 2006 --- ./src/smtp/smtp_connect.c Tue Jul 31 15:40:41 2007 *************** *** 304,309 **** --- 304,319 ---- 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. */ return (smtp_session_alloc(stream, destination, name, addr, diff -cr --new-file /var/tmp/postfix-2.3.11/src/smtpd/smtpd.c ./src/smtpd/smtpd.c *** /var/tmp/postfix-2.3.11/src/smtpd/smtpd.c Sun Feb 25 09:26:59 2007 --- ./src/smtpd/smtpd.c Tue Jul 31 15:48:51 2007 *************** *** 1618,1624 **** 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); rec_fprintf(state->cleanup, REC_TYPE_TIME, REC_TYPE_TIME_FORMAT, REC_TYPE_TIME_ARG(state->arrival_time)); if (*var_filter_xport) --- 1618,1625 ---- if (SMTPD_STAND_ALONE(state) == 0) { if (smtpd_milters != 0 && (state->saved_flags & MILTER_SKIP_FLAGS) == 0) ! /* 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,2521 **** --- 2517,2526 ---- */ 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); *************** *** 2730,2735 **** --- 2735,2759 ---- state->dest = 0; 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 diff -cr --new-file /var/tmp/postfix-2.3.11/src/smtpstone/qmqp-source.c ./src/smtpstone/qmqp-source.c *** /var/tmp/postfix-2.3.11/src/smtpstone/qmqp-source.c Fri Dec 9 20:30:35 2005 --- ./src/smtpstone/qmqp-source.c Tue Jul 31 15:55:33 2007 *************** *** 352,357 **** --- 352,364 ---- 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); } } diff -cr --new-file /var/tmp/postfix-2.3.11/src/smtpstone/smtp-source.c ./src/smtpstone/smtp-source.c *** /var/tmp/postfix-2.3.11/src/smtpstone/smtp-source.c Thu Jun 15 14:07:16 2006 --- ./src/smtpstone/smtp-source.c Tue Jul 31 15:56:08 2007 *************** *** 467,472 **** --- 467,479 ---- 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); } } diff -cr --new-file /var/tmp/postfix-2.3.11/src/util/Makefile.in ./src/util/Makefile.in *** /var/tmp/postfix-2.3.11/src/util/Makefile.in Sun Jul 9 13:45:23 2006 --- ./src/util/Makefile.in Mon Jul 30 19:18:35 2007 *************** *** 30,36 **** 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 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 \ --- 30,36 ---- 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 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,68 **** 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 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 \ --- 62,68 ---- 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 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,1602 **** --- 1597,1607 ---- 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 diff -cr --new-file /var/tmp/postfix-2.3.11/src/util/vstream.h ./src/util/vstream.h *** /var/tmp/postfix-2.3.11/src/util/vstream.h Tue Apr 18 09:47:53 2006 --- ./src/util/vstream.h Mon Jul 30 19:17:17 2007 *************** *** 146,151 **** --- 146,157 ---- #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 -cr --new-file /var/tmp/postfix-2.3.11/src/util/vstream_tweak.c ./src/util/vstream_tweak.c *** /var/tmp/postfix-2.3.11/src/util/vstream_tweak.c Wed Dec 31 19:00:00 1969 --- ./src/util/vstream_tweak.c Tue Jul 31 17:14:02 2007 *************** *** 0 **** --- 1,139 ---- + /*++ + /* NAME + /* vstream_tweak 3 + /* SUMMARY + /* performance tweaks + /* SYNOPSIS + /* #include + /* + /* 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 + #include + #include + #include + + /* Utility library. */ + + #include + #include + + /* 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); + }