Prereq: "2.5.6" diff -cr --new-file /var/tmp/postfix-2.5.6/src/global/mail_version.h ./src/global/mail_version.h *** /var/tmp/postfix-2.5.6/src/global/mail_version.h Sat Jan 3 20:27:10 2009 --- ./src/global/mail_version.h Tue May 12 11:15:18 2009 *************** *** 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 "20090103" ! #define MAIL_VERSION_NUMBER "2.5.6" #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 "20090512" ! #define MAIL_VERSION_NUMBER "2.5.7" #ifdef SNAPSHOT # define MAIL_VERSION_DATE "-" MAIL_RELEASE_DATE diff -cr --new-file /var/tmp/postfix-2.5.6/HISTORY ./HISTORY *** /var/tmp/postfix-2.5.6/HISTORY Sat Jan 3 20:25:35 2009 --- ./HISTORY Tue May 12 10:28:07 2009 *************** *** 14469,14471 **** --- 14469,14516 ---- Documentation: log the "*" pattern as the last transport map lookup. File: proto/transport. + + 20090122 + + Bugfix: the data_directory was not automatically created! + File: conf/postfix-files. + + 20090304 + + Cleanup: skip over suspended or throttled queues while + looking for delivery requests. File: *qmgr/qmgr_transport.c. + + 20090305 + + Bugfix: in the "new queue manager", the _destination_rate_delay + code needed to postpone the job scheduler updates after + delivery completion, otherwise the scheduler could loop on + blocked jobs. Victor & Wietse. File: qmgr/qmgr_entry.c, + qmgr/qmgr_queue.c, qmgr/qmgr_job.c. + + Cleanup: report a "queue file write error", instead of + passing though bogus 2xx replies from proxy filters to SMTP + clients. File: smtpd/smtpd_proxy.c. + + 20090310 + + Bugfix: Postfix used mumble_concurrency_failed_cohort_limit + instead of mumble_destination_concurrency_failed_cohort_limit + as documented. File: global/mail_params.h. + + 20090419 + + Bugfix: don't re-enable SIGHUP if it is ignored in the + parent. This may cause random "Postfix integrity check + failed" errors at boot time (POSIX SIGHUP death), causing + Postfix not to start. We duplicate code from postdrop and + thus avoid past mistakes. File: postsuper/postsuper.c. + + Robustness: don't re-enable SIGTERM if it is ignored in the + parent. Files: postsuper/postsuper.c, postdrop/postdrop.c. + + 20090428 + + Bugfix: don't disable MIME parsing with smtp_header_checks, + smtp_mime_header_checks, smtp_nested_header_checks or with + smtp_body_checks. Bug reported by Victor. File: smtp/smtp_proto.c. diff -cr --new-file /var/tmp/postfix-2.5.6/conf/postfix-files ./conf/postfix-files *** /var/tmp/postfix-2.5.6/conf/postfix-files Wed Jan 23 19:13:49 2008 --- ./conf/postfix-files Thu Jan 22 19:47:26 2009 *************** *** 42,48 **** # permissions, so that running "make install" fixes any glitches. # $config_directory:d:root:-:755:u ! $data_directory:d:$mail_owner:-:700:u $daemon_directory:d:root:-:755:u $queue_directory:d:root:-:755:uc $sample_directory:d:root:-:755:o --- 42,48 ---- # permissions, so that running "make install" fixes any glitches. # $config_directory:d:root:-:755:u ! $data_directory:d:$mail_owner:-:700:uc $daemon_directory:d:root:-:755:u $queue_directory:d:root:-:755:uc $sample_directory:d:root:-:755:o diff -cr --new-file /var/tmp/postfix-2.5.6/html/postconf.5.html ./html/postconf.5.html *** /var/tmp/postfix-2.5.6/html/postconf.5.html Sun Jul 27 15:47:10 2008 --- ./html/postconf.5.html Tue May 12 09:50:58 2009 *************** *** 1792,1798 ****
A pseudo-cohort is the number of deliveries equal to a destination's delivery concurrency.
!Use transport_destination_concurrency_negative_feedback to specify a transport-specific override, where transport is the master.cf name of the message delivery transport.
--- 1792,1798 ----A pseudo-cohort is the number of deliveries equal to a destination's delivery concurrency.
!Use transport_destination_concurrency_negative_feedback to specify a transport-specific override, where transport is the master.cf name of the message delivery transport.
*************** *** 3718,3724 ****--- 3718,3724 ----/etc/postfix/master.cf: ! mylmtp ... lmtp -o lmtp_lhlo_name=foo.bar.com
diff -cr --new-file /var/tmp/postfix-2.5.6/mantools/postlink ./mantools/postlink *** /var/tmp/postfix-2.5.6/mantools/postlink Sun Jul 27 15:34:16 2008 --- ./mantools/postlink Tue May 12 09:47:29 2009 *************** *** 254,260 **** s;\blmtp_tls_note_starttls_offer\b;$&;g; s;\blmtp_sender_dependent_authentication\b;$&;g; s;\blmtp_sasl_path\b;$&;g; ! s;\blmtp_lhlo_name\b;$&;g; s;\blmtp_connect_timeout\b;$&;g; s;\blmtp_data_done_timeout\b;$&;g; s;\blmtp_data_init_timeout\b;$&;g; --- 254,260 ---- s;\blmtp_tls_note_starttls_offer\b;$&;g; s;\blmtp_sender_dependent_authentication\b;$&;g; s;\blmtp_sasl_path\b;$&;g; ! s;\blmtp_lhlo_name\b;$&;g; s;\blmtp_connect_timeout\b;$&;g; s;\blmtp_data_done_timeout\b;$&;g; s;\blmtp_data_init_timeout\b;$&;g; *************** *** 636,642 **** # Transport-dependent magical parameters. s;(transport)()?(_destination_concurrency_failed_cohort_limit)\b;$2$1$3;g; ! s;(transport)()?(_destination_concurrency_negative_feedback)\b;$2$1$3;g; s;(transport)()?(_destination_concurrency_positive_feedback)\b;$2$1$3;g; s;(transport)()?(_delivery_slot_cost)\b;$2$1$3;g; s;(transport)()?(_delivery_slot_discount)\b;$2$1$3;g; --- 636,642 ---- # Transport-dependent magical parameters. s;(transport)()?(_destination_concurrency_failed_cohort_limit)\b;$2$1$3;g; ! s;(transport)()?(_destination_concurrency_negative_feedback)\b;$2$1$3;g; s;(transport)()?(_destination_concurrency_positive_feedback)\b;$2$1$3;g; s;(transport)()?(_delivery_slot_cost)\b;$2$1$3;g; s;(transport)()?(_delivery_slot_discount)\b;$2$1$3;g; diff -cr --new-file /var/tmp/postfix-2.5.6/src/global/mail_params.h ./src/global/mail_params.h *** /var/tmp/postfix-2.5.6/src/global/mail_params.h Sun Jul 27 15:10:27 2008 --- ./src/global/mail_params.h Tue May 12 09:55:08 2009 *************** *** 2899,2910 **** * Scheduler concurrency feedback algorithms. */ #define VAR_CONC_POS_FDBACK "default_destination_concurrency_positive_feedback" ! #define _CONC_POS_FDBACK "_concurrency_positive_feedback" #define DEF_CONC_POS_FDBACK "1" extern char *var_conc_pos_feedback; #define VAR_CONC_NEG_FDBACK "default_destination_concurrency_negative_feedback" ! #define _CONC_NEG_FDBACK "_concurrency_negative_feedback" #define DEF_CONC_NEG_FDBACK "1" extern char *var_conc_neg_feedback; --- 2899,2910 ---- * Scheduler concurrency feedback algorithms. */ #define VAR_CONC_POS_FDBACK "default_destination_concurrency_positive_feedback" ! #define _CONC_POS_FDBACK "_destination_concurrency_positive_feedback" #define DEF_CONC_POS_FDBACK "1" extern char *var_conc_pos_feedback; #define VAR_CONC_NEG_FDBACK "default_destination_concurrency_negative_feedback" ! #define _CONC_NEG_FDBACK "_destination_concurrency_negative_feedback" #define DEF_CONC_NEG_FDBACK "1" extern char *var_conc_neg_feedback; *************** *** 2912,2918 **** #define CONC_FDBACK_NAME_SQRT_WIN "sqrt_concurrency" #define VAR_CONC_COHORT_LIM "default_destination_concurrency_failed_cohort_limit" ! #define _CONC_COHORT_LIM "_concurrency_failed_cohort_limit" #define DEF_CONC_COHORT_LIM 1 extern int var_conc_cohort_limit; --- 2912,2918 ---- #define CONC_FDBACK_NAME_SQRT_WIN "sqrt_concurrency" #define VAR_CONC_COHORT_LIM "default_destination_concurrency_failed_cohort_limit" ! #define _CONC_COHORT_LIM "_destination_concurrency_failed_cohort_limit" #define DEF_CONC_COHORT_LIM 1 extern int var_conc_cohort_limit; diff -cr --new-file /var/tmp/postfix-2.5.6/src/oqmgr/qmgr_transport.c ./src/oqmgr/qmgr_transport.c *** /var/tmp/postfix-2.5.6/src/oqmgr/qmgr_transport.c Sun Dec 2 13:13:26 2007 --- ./src/oqmgr/qmgr_transport.c Thu Mar 5 16:06:43 2009 *************** *** 286,291 **** --- 286,293 ---- continue; need = xport->pending + 1; for (queue = xport->queue_list.next; queue; queue = queue->peers.next) { + if (QMGR_QUEUE_READY(queue) == 0) + continue; if ((need -= MIN5af51743e4eef(queue->window - queue->busy_refcount, queue->todo_refcount)) <= 0) { QMGR_LIST_ROTATE(qmgr_transport_list, xport); diff -cr --new-file /var/tmp/postfix-2.5.6/src/postdrop/postdrop.c ./src/postdrop/postdrop.c *** /var/tmp/postfix-2.5.6/src/postdrop/postdrop.c Tue Jan 8 15:35:08 2008 --- ./src/postdrop/postdrop.c Tue May 12 09:40:28 2009 *************** *** 340,346 **** signal(SIGINT, postdrop_sig); signal(SIGQUIT, postdrop_sig); ! signal(SIGTERM, postdrop_sig); if (signal(SIGHUP, SIG_IGN) == SIG_DFL) signal(SIGHUP, postdrop_sig); msg_cleanup(postdrop_cleanup); --- 340,347 ---- signal(SIGINT, postdrop_sig); signal(SIGQUIT, postdrop_sig); ! if (signal(SIGTERM, SIG_IGN) == SIG_DFL) ! signal(SIGTERM, postdrop_sig); if (signal(SIGHUP, SIG_IGN) == SIG_DFL) signal(SIGHUP, postdrop_sig); msg_cleanup(postdrop_cleanup); diff -cr --new-file /var/tmp/postfix-2.5.6/src/postsuper/postsuper.c ./src/postsuper/postsuper.c *** /var/tmp/postfix-2.5.6/src/postsuper/postsuper.c Wed May 2 14:39:27 2007 --- ./src/postsuper/postsuper.c Tue May 12 09:43:20 2009 *************** *** 974,984 **** /* * This commands requires root privileges. We therefore do not worry * about hostile signals, and report problems via msg_warn(). */ ! if (signal(SIGHUP, SIG_IGN) != SIG_IGN) { ! (void) signal(SIGINT, SIG_IGN); (void) signal(SIGQUIT, SIG_IGN); (void) signal(SIGTERM, SIG_IGN); if (inode_mismatch > 0 || inode_fixed > 0 || position_mismatch > 0) msg_warn("OPERATION INCOMPLETE -- RERUN COMMAND TO FIX THE QUEUE FIRST"); if (sig) --- 974,990 ---- /* * This commands requires root privileges. We therefore do not worry * about hostile signals, and report problems via msg_warn(). + * + * We use the in-kernel SIGINT handler address as an atomic variable to + * prevent nested interrupted() calls. For this reason, main() must + * configure interrupted() as SIGINT handler before other signal handlers + * are allowed to invoke interrupted(). See also similar code in + * postdrop. */ ! if (signal(SIGINT, SIG_IGN) != SIG_IGN) { (void) signal(SIGQUIT, SIG_IGN); (void) signal(SIGTERM, SIG_IGN); + (void) signal(SIGHUP, SIG_IGN); if (inode_mismatch > 0 || inode_fixed > 0 || position_mismatch > 0) msg_warn("OPERATION INCOMPLETE -- RERUN COMMAND TO FIX THE QUEUE FIRST"); if (sig) *************** *** 1175,1185 **** * * Set up signal handlers after permanently dropping super-user privileges, * so that signal handlers will always run with the correct privileges. */ - signal(SIGHUP, interrupted); signal(SIGINT, interrupted); signal(SIGQUIT, interrupted); ! signal(SIGTERM, interrupted); msg_cleanup(fatal_warning); /* --- 1181,1200 ---- * * Set up signal handlers after permanently dropping super-user privileges, * so that signal handlers will always run with the correct privileges. + * + * XXX Don't enable SIGHUP or SIGTERM if it was ignored by the parent. + * + * interrupted() uses the in-kernel SIGINT handler address as an atomic + * variable to prevent nested interrupted() calls. For this reason, the + * SIGINT handler must be configured before other signal handlers are + * allowed to invoke interrupted(). See also similar code in postdrop. */ signal(SIGINT, interrupted); signal(SIGQUIT, interrupted); ! if (signal(SIGTERM, SIG_IGN) == SIG_DFL) ! signal(SIGTERM, interrupted); ! if (signal(SIGHUP, SIG_IGN) == SIG_DFL) ! signal(SIGHUP, interrupted); msg_cleanup(fatal_warning); /* diff -cr --new-file /var/tmp/postfix-2.5.6/src/qmgr/qmgr.h ./src/qmgr/qmgr.h *** /var/tmp/postfix-2.5.6/src/qmgr/qmgr.h Sat Dec 8 11:01:59 2007 --- ./src/qmgr/qmgr.h Thu Mar 5 16:36:32 2009 *************** *** 436,441 **** --- 436,442 ---- extern QMGR_ENTRY *qmgr_job_entry_select(QMGR_TRANSPORT *); extern QMGR_PEER *qmgr_peer_select(QMGR_JOB *); + extern void qmgr_job_blocker_update(QMGR_QUEUE *); extern QMGR_JOB *qmgr_job_obtain(QMGR_MESSAGE *, QMGR_TRANSPORT *); extern void qmgr_job_free(QMGR_JOB *); diff -cr --new-file /var/tmp/postfix-2.5.6/src/qmgr/qmgr_entry.c ./src/qmgr/qmgr_entry.c *** /var/tmp/postfix-2.5.6/src/qmgr/qmgr_entry.c Fri Dec 14 17:47:21 2007 --- ./src/qmgr/qmgr_entry.c Thu Mar 5 16:29:46 2009 *************** *** 299,327 **** } /* ! * If the queue was blocking some of the jobs on the job list, check if ! * the concurrency limit has lifted. If there are still some pending ! * deliveries, give it a try and unmark all transport blockers at once. ! * The qmgr_job_entry_select() will do the rest. In either case make sure ! * the queue is not marked as a blocker anymore, with extra handling of ! * queues which were declared dead. * ! * Note that changing the blocker status also affects the candidate cache. ! * Most of the cases would be automatically recognized by the current job ! * change, but we play safe and reset the cache explicitly below. ! * ! * Keeping the transport blocker tag odd is an easy way to make sure the tag ! * never matches jobs that are not explicitly marked as blockers. */ ! if (queue->blocker_tag == transport->blocker_tag) { ! if (queue->window > queue->busy_refcount && queue->todo.next != 0) { ! transport->blocker_tag += 2; ! transport->job_current = transport->job_list.next; ! transport->candidate_cache_current = 0; ! } ! if (queue->window > queue->busy_refcount || QMGR_QUEUE_THROTTLED(queue)) ! queue->blocker_tag = 0; } /* * When there are no more entries for this peer, discard the peer --- 299,323 ---- } /* ! * We implement a rate-limited queue by emulating a slow delivery ! * channel. We insert the artificial delays with qmgr_queue_suspend(). * ! * When a queue is suspended, we must postpone any job scheduling decisions ! * until the queue is resumed. Otherwise, we make those decisions now. ! * The job scheduling decisions are made by qmgr_job_blocker_update(). */ ! if (which == QMGR_QUEUE_BUSY && transport->rate_delay > 0) { ! if (queue->window > 1) ! msg_panic("%s: queue %s/%s: window %d > 1 on rate-limited service", ! myname, transport->name, queue->name, queue->window); ! if (QMGR_QUEUE_THROTTLED(queue)) /* XXX */ ! qmgr_queue_unthrottle(queue); ! if (QMGR_QUEUE_READY(queue)) ! qmgr_queue_suspend(queue, transport->rate_delay); } + if (!QMGR_QUEUE_SUSPENDED(queue) + && queue->blocker_tag == transport->blocker_tag) + qmgr_job_blocker_update(queue); /* * When there are no more entries for this peer, discard the peer *************** *** 336,354 **** */ if (which == QMGR_QUEUE_BUSY) queue->last_done = event_time(); - - /* - * Suspend a rate-limited queue, so that mail trickles out. - */ - if (which == QMGR_QUEUE_BUSY && transport->rate_delay > 0) { - if (queue->window > 1) - msg_panic("%s: queue %s/%s: window %d > 1 on rate-limited service", - myname, transport->name, queue->name, queue->window); - if (QMGR_QUEUE_THROTTLED(queue)) /* XXX */ - qmgr_queue_unthrottle(queue); - if (QMGR_QUEUE_READY(queue)) - qmgr_queue_suspend(queue, transport->rate_delay); - } /* * When the in-core queue for this site is empty and when this site is --- 332,337 ---- diff -cr --new-file /var/tmp/postfix-2.5.6/src/qmgr/qmgr_job.c ./src/qmgr/qmgr_job.c *** /var/tmp/postfix-2.5.6/src/qmgr/qmgr_job.c Tue Nov 7 11:34:07 2006 --- ./src/qmgr/qmgr_job.c Thu Mar 5 16:43:36 2009 *************** *** 18,23 **** --- 18,26 ---- /* /* QMGR_ENTRY *qmgr_job_entry_select(transport) /* QMGR_TRANSPORT *transport; + /* + /* void qmgr_job_blocker_update(queue) + /* QMGR_QUEUE *queue; /* DESCRIPTION /* These routines add/delete/manipulate per-transport jobs. /* Each job corresponds to a specific transport and message. *************** *** 38,43 **** --- 41,51 ---- /* If necessary, an attempt to read more recipients into core is made. /* This can result in creation of more job, queue and entry structures. /* + /* qmgr_job_blocker_update() updates the status of blocked + /* jobs after a decrease in the queue's concurrency level, + /* after the queue is throttled, or after the queue is resumed + /* from suspension. + /* /* qmgr_job_move_limits() takes care of proper distribution of the /* per-transport recipients limit among the per-transport jobs. /* Should be called whenever a job's recipient slot becomes available. *************** *** 937,939 **** --- 945,980 ---- transport->job_current = 0; return (0); } + + /* qmgr_job_blocker_update - update "blocked job" status */ + + void qmgr_job_blocker_update(QMGR_QUEUE *queue) + { + QMGR_TRANSPORT *transport = queue->transport; + + /* + * If the queue was blocking some of the jobs on the job list, check if + * the concurrency limit has lifted. If there are still some pending + * deliveries, give it a try and unmark all transport blockers at once. + * The qmgr_job_entry_select() will do the rest. In either case make sure + * the queue is not marked as a blocker anymore, with extra handling of + * queues which were declared dead. + * + * Note that changing the blocker status also affects the candidate cache. + * Most of the cases would be automatically recognized by the current job + * change, but we play safe and reset the cache explicitly below. + * + * Keeping the transport blocker tag odd is an easy way to make sure the tag + * never matches jobs that are not explicitly marked as blockers. + */ + if (queue->blocker_tag == transport->blocker_tag) { + if (queue->window > queue->busy_refcount && queue->todo.next != 0) { + transport->blocker_tag += 2; + transport->job_current = transport->job_list.next; + transport->candidate_cache_current = 0; + } + if (queue->window > queue->busy_refcount || QMGR_QUEUE_THROTTLED(queue)) + queue->blocker_tag = 0; + } + } + diff -cr --new-file /var/tmp/postfix-2.5.6/src/qmgr/qmgr_queue.c ./src/qmgr/qmgr_queue.c *** /var/tmp/postfix-2.5.6/src/qmgr/qmgr_queue.c Sat Dec 8 09:59:34 2007 --- ./src/qmgr/qmgr_queue.c Thu Mar 5 17:35:24 2009 *************** *** 66,72 **** /* "slow open" mode, and eliminates the "thundering herd" problem. /* /* qmgr_queue_suspend() suspends delivery for this destination ! /* briefly. /* DIAGNOSTICS /* Panic: consistency check failure. /* LICENSE --- 66,76 ---- /* "slow open" mode, and eliminates the "thundering herd" problem. /* /* qmgr_queue_suspend() suspends delivery for this destination ! /* briefly. This function invalidates any scheduling decisions ! /* that are based on the present queue's concurrency window. ! /* To compensate for work skipped by qmgr_entry_done(), the ! /* status of blocker jobs is re-evaluated after the queue is ! /* resumed. /* DIAGNOSTICS /* Panic: consistency check failure. /* LICENSE *************** *** 152,160 **** --- 156,175 ---- /* * Every event handler that leaves a queue in the "ready" state should * remove the queue when it is empty. + * + * XXX Do not omit the redundant test below. It is here to simplify code + * consistency checks. The check is trivially eliminated by the compiler + * optimizer. There is no need to sacrifice code clarity for the sake of + * performance. + * + * XXX Do not expose the blocker job logic here. Rate-limited queues are not + * a performance-critical feature. Here, too, there is no need to sacrifice + * code clarity for the sake of performance. */ if (QMGR_QUEUE_READY(queue) && queue->todo.next == 0 && queue->busy.next == 0) qmgr_queue_done(queue); + else + qmgr_job_blocker_update(queue); } /* qmgr_queue_suspend - briefly suspend a destination */ diff -cr --new-file /var/tmp/postfix-2.5.6/src/qmgr/qmgr_transport.c ./src/qmgr/qmgr_transport.c *** /var/tmp/postfix-2.5.6/src/qmgr/qmgr_transport.c Sun Dec 2 12:53:17 2007 --- ./src/qmgr/qmgr_transport.c Thu Mar 5 15:08:44 2009 *************** *** 291,296 **** --- 291,298 ---- continue; need = xport->pending + 1; for (queue = xport->queue_list.next; queue; queue = queue->peers.next) { + if (QMGR_QUEUE_READY(queue) == 0) + continue; if ((need -= MIN5af51743e4eef(queue->window - queue->busy_refcount, queue->todo_refcount)) <= 0) { QMGR_LIST_ROTATE(qmgr_transport_list, xport, peers); diff -cr --new-file /var/tmp/postfix-2.5.6/src/smtp/smtp_proto.c ./src/smtp/smtp_proto.c *** /var/tmp/postfix-2.5.6/src/smtp/smtp_proto.c Wed Jan 9 09:04:37 2008 --- ./src/smtp/smtp_proto.c Tue Apr 28 16:00:34 2009 *************** *** 1771,1782 **** * XXX Don't downgrade just because generic_maps is turned * on. */ ! if (downgrading || smtp_generic_maps || smtp_header_checks ! || smtp_body_checks) session->mime_state = mime_state_alloc(downgrading ? MIME_OPT_DOWNGRADE | MIME_OPT_REPORT_NESTING : ! MIME_OPT_DISABLE_MIME, smtp_generic_maps || smtp_header_checks ? smtp_header_rewrite : --- 1771,1785 ---- * XXX Don't downgrade just because generic_maps is turned * on. */ ! #define SMTP_ANY_CHECKS (smtp_header_checks || smtp_body_checks) ! ! if (downgrading || smtp_generic_maps || SMTP_ANY_CHECKS) session->mime_state = mime_state_alloc(downgrading ? MIME_OPT_DOWNGRADE | MIME_OPT_REPORT_NESTING : ! SMTP_ANY_CHECKS == 0 ? ! MIME_OPT_DISABLE_MIME : ! 0, smtp_generic_maps || smtp_header_checks ? smtp_header_rewrite : diff -cr --new-file /var/tmp/postfix-2.5.6/src/smtpd/smtpd_proxy.c ./src/smtpd/smtpd_proxy.c *** /var/tmp/postfix-2.5.6/src/smtpd/smtpd_proxy.c Tue Jan 8 16:12:41 2008 --- ./src/smtpd/smtpd_proxy.c Fri Mar 6 15:42:40 2009 *************** *** 564,574 **** --- 564,586 ---- /* * Log a warning in case the proxy does not send the expected response. * Silently accept any response when the client expressed no expectation. + * + * Don't pass through misleading 2xx replies. it confuses naive users and + * SMTP clients, and creates support problems. */ if (expect != SMTPD_PROX_WANT_ANY && expect != *STR(state->proxy_buffer)) { va_start(ap, fmt); smtpd_proxy_cmd_error(state, fmt, ap); va_end(ap); + if (*STR(state->proxy_buffer) == SMTPD_PROX_WANT_OK + || *STR(state->proxy_buffer) == SMTPD_PROX_WANT_MORE) { + state->error_mask |= MAIL_ERROR_SOFTWARE; + state->err |= CLEANUP_STAT_PROXY; + detail = cleanup_stat_detail(CLEANUP_STAT_PROXY); + vstring_sprintf(state->proxy_buffer, + "%d %s Error: %s", + detail->smtp, detail->dsn, detail->text); + } return (-1); } else { return (0);/etc/postfix/master.cf: ! mylmtp ... lmtp -o lmtp_lhlo_name=foo.bar.com