This is a minimal patch to add support for NDR filtering in the Postfix SMTP client. It should work with Postfix 2.11, 2.12, and it may even work with Postfix 2.10. smtp_bounce_defer_filter (default: empty) Optional filter to rewrite the three-number enhanced status code and the explanatory text in a Postfix SMTP/LMTP client bounce/defer mes- sage. With each bounce or defer request, the user-specified filter is queried with one line of text that is structured as follows: enhanced-status-code SPACE explanatory-text The bounce_defer_filter feature may replace this only with text that has the same structure. Enhanced status codes must have a first numer- ical field of 4 (defer) or 5 (bounce), and the free text field must be non-empty. Other results will result in a warning. Example: The following example turns soft TLS errors into hard errors, by over- riding the first number in the enhanced status code. /etc/postfix/main.cf: smtp_bounce_defer_filter = pcre:/etc/postfix/smtp_ndr_filter /etc/postfix/smtp_ndr_filter: /^4(\.\d+\.\d+ TLS is required, but host \S+ refused to start TLS: .+) 5$1 /^4(\.\d+\.\d+ TLS is required, but was not offered by host .+) 5$1 # Do not change the following into hard bounces. They may # result from a local configuration problem. # 4.\d+.\d+ TLS is required, but our TLS engine is unavailable # 4.\d+.\d+ TLS is required, but unavailable # 4.\d+.\d+ Cannot start TLS: handshake failure Notes: o This feature modifies error messages that are generated by the Postfix SMTP client, and that may or may not be derived from remote SMTP server responses. In contrast, the smtp_reply_fil- ter feature modifies remote SMTP server responses that may result in email non-delivery or delivery. o This feature will NOT override the soft_bounce safety net. o This feature will apply to all bounce/defer messages from the Postfix SMTP/LMTP client. o This feature will change the enhanced status code and text that is logged to the maillog file, and that is reported tot the sender. diff --exclude=.indent.pro --exclude='lmdb*' --exclude='*TLS*' -cr --new-file /var/tmp/postfix-2.12-20140223/html/lmtp.8.html ./html/lmtp.8.html *** /var/tmp/postfix-2.12-20140223/html/lmtp.8.html Sun Feb 23 17:27:33 2014 --- ./html/lmtp.8.html Sat Mar 15 18:21:46 2014 *************** *** 152,157 **** --- 152,162 ---- smtp_never_send_ehlo (no) Never send EHLO at the start of an SMTP session. + smtp_bounce_defer_filter (empty) + Optional filter to rewrite the three-number enhanced status code + and the explanatory text in a Postfix SMTP/LMTP client + bounce/defer message. + smtp_defer_if_no_mx_address_found (no) Defer mail delivery when no MX record resolves to an IP address. diff --exclude=.indent.pro --exclude='lmdb*' --exclude='*TLS*' -cr --new-file /var/tmp/postfix-2.12-20140223/html/postconf.5.html ./html/postconf.5.html *** /var/tmp/postfix-2.12-20140223/html/postconf.5.html Fri Feb 14 09:48:42 2014 --- ./html/postconf.5.html Sat Mar 15 18:49:49 2014 *************** *** 3854,3859 **** --- 3854,3870 ---- +
lmtp_bounce_defer_filter + (default: empty)
+ +

The LMTP-specific version of the smtp_bounce_defer_filter + configuration parameter. See there for details.

+ +

This feature is available in Postfix 2.12 and later.

+ + +
+
lmtp_cache_connection (default: yes)
*************** *** 9487,9492 **** --- 9498,9576 ----
+
smtp_bounce_defer_filter + (default: empty)
+ +

Optional filter to rewrite the three-number enhanced status + code and the explanatory text in a Postfix SMTP/LMTP client + bounce/defer message.

+ +

With each bounce or defer request, the user-specified filter is + queried with one line of text that is structured as follows:

+ +
+ enhanced-status-code SPACE explanatory-text +
+ +

The bounce_defer_filter feature may replace this only with text + that has the same structure. Enhanced status codes must have a + first numerical field of 4 (defer) or 5 (bounce), and the free text + field must be non-empty. Other results will result in a warning. +

+ +

Example:

+ +

The following example turns soft TLS errors into hard errors, + by overriding the first number in the enhanced status code.

+ +
+
+ /etc/postfix/main.cf:
+     smtp_bounce_defer_filter = pcre:/etc/postfix/smtp_ndr_filter
+ 
+
+ +
+
+ /etc/postfix/smtp_ndr_filter:
+     /^4(\.\d+\.\d+ TLS is required, but host \S+ refused to start TLS: .+)
+         5$1
+     /^4(\.\d+\.\d+ TLS is required, but was not offered by host .+)
+         5$1
+     # Do not change the following into hard bounces. They may
+     # result from a local configuration problem.
+     # 4.\d+.\d+ TLS is required, but our TLS engine is unavailable
+     # 4.\d+.\d+ TLS is required, but unavailable
+     # 4.\d+.\d+ Cannot start TLS: handshake failure
+ 
+
+ +

Notes:

+ + + +

This feature is available in Postfix 2.12 and later.

+ + +
+
smtp_cname_overrides_servername (default: version dependent)
diff --exclude=.indent.pro --exclude='lmdb*' --exclude='*TLS*' -cr --new-file /var/tmp/postfix-2.12-20140223/html/smtp.8.html ./html/smtp.8.html *** /var/tmp/postfix-2.12-20140223/html/smtp.8.html Sun Feb 23 17:27:33 2014 --- ./html/smtp.8.html Sat Mar 15 18:21:46 2014 *************** *** 152,157 **** --- 152,162 ---- smtp_never_send_ehlo (no) Never send EHLO at the start of an SMTP session. + smtp_bounce_defer_filter (empty) + Optional filter to rewrite the three-number enhanced status code + and the explanatory text in a Postfix SMTP/LMTP client + bounce/defer message. + smtp_defer_if_no_mx_address_found (no) Defer mail delivery when no MX record resolves to an IP address. diff --exclude=.indent.pro --exclude='lmdb*' --exclude='*TLS*' -cr --new-file /var/tmp/postfix-2.12-20140223/man/man5/postconf.5 ./man/man5/postconf.5 *** /var/tmp/postfix-2.12-20140223/man/man5/postconf.5 Fri Feb 14 09:48:42 2014 --- ./man/man5/postconf.5 Sat Mar 15 18:49:49 2014 *************** *** 2275,2280 **** --- 2275,2285 ---- parameter. See there for details. .PP This feature is available in Postfix 2.5 and later. + .SH lmtp_bounce_defer_filter (default: empty) + The LMTP-specific version of the smtp_bounce_defer_filter + configuration parameter. See there for details. + .PP + This feature is available in Postfix 2.12 and later. .SH lmtp_cache_connection (default: yes) Keep Postfix LMTP client connections open for up to $max_idle seconds. When the LMTP client receives a request for the same *************** *** 5770,5775 **** --- 5775,5851 ---- that change the delivery time or destination are not available. .PP This feature is available in Postfix 2.5 and later. + .SH smtp_bounce_defer_filter (default: empty) + Optional filter to rewrite the three-number enhanced status + code and the explanatory text in a Postfix SMTP/LMTP client + bounce/defer message. + .PP + With each bounce or defer request, the user-specified filter is + queried with one line of text that is structured as follows: + .sp + .in +4 + enhanced-status-code SPACE explanatory-text + .in -4 + .PP + The bounce_defer_filter feature may replace this only with text + that has the same structure. Enhanced status codes must have a + first numerical field of 4 (defer) or 5 (bounce), and the free text + field must be non-empty. Other results will result in a warning. + .PP + Example: + .PP + The following example turns soft TLS errors into hard errors, + by overriding the first number in the enhanced status code. + .sp + .in +4 + .nf + .na + .ft C + /etc/postfix/main.cf: + smtp_bounce_defer_filter = pcre:/etc/postfix/smtp_ndr_filter + .fi + .ad + .ft R + .in -4 + .sp + .in +4 + .nf + .na + .ft C + /etc/postfix/smtp_ndr_filter: + /^4(\e.\ed+\e.\ed+ TLS is required, but host \eS+ refused to start TLS: .+) + 5$1 + /^4(\e.\ed+\e.\ed+ TLS is required, but was not offered by host .+) + 5$1 + # Do not change the following into hard bounces. They may + # result from a local configuration problem. + # 4.\ed+.\ed+ TLS is required, but our TLS engine is unavailable + # 4.\ed+.\ed+ TLS is required, but unavailable + # 4.\ed+.\ed+ Cannot start TLS: handshake failure + .fi + .ad + .ft R + .in -4 + .PP + Notes: + .IP \(bu + This feature modifies error messages that are generated + by the Postfix SMTP client, and that may or may not be derived from + remote SMTP server responses. In contrast, the smtp_reply_filter + feature modifies remote SMTP server responses that may result in + email non-delivery or delivery. + .IP \(bu + This feature will NOT override the soft_bounce safety net. + .IP \(bu + This feature will apply to all bounce/defer messages from + the Postfix SMTP/LMTP client. + .IP \(bu + This feature will change the enhanced status code and text + that is logged to the maillog file, and that is reported tot the + sender. + .br + .PP + This feature is available in Postfix 2.12 and later. .SH smtp_cname_overrides_servername (default: version dependent) When the remote SMTP servername is a DNS CNAME, replace the servername with the result from CNAME expansion for the purpose of diff --exclude=.indent.pro --exclude='lmdb*' --exclude='*TLS*' -cr --new-file /var/tmp/postfix-2.12-20140223/man/man8/smtp.8 ./man/man8/smtp.8 *** /var/tmp/postfix-2.12-20140223/man/man8/smtp.8 Sun Feb 23 17:27:33 2014 --- ./man/man8/smtp.8 Sat Mar 15 18:21:46 2014 *************** *** 163,168 **** --- 163,172 ---- Always send EHLO at the start of an SMTP session. .IP "\fBsmtp_never_send_ehlo (no)\fR" Never send EHLO at the start of an SMTP session. + .IP "\fBsmtp_bounce_defer_filter (empty)\fR" + Optional filter to rewrite the three-number enhanced status + code and the explanatory text in a Postfix SMTP/LMTP client + bounce/defer message. .IP "\fBsmtp_defer_if_no_mx_address_found (no)\fR" Defer mail delivery when no MX record resolves to an IP address. .IP "\fBsmtp_line_length_limit (998)\fR" diff --exclude=.indent.pro --exclude='lmdb*' --exclude='*TLS*' -cr --new-file /var/tmp/postfix-2.12-20140223/mantools/postlink ./mantools/postlink *** /var/tmp/postfix-2.12-20140223/mantools/postlink Fri Dec 20 17:20:01 2013 --- ./mantools/postlink Sat Mar 15 18:34:17 2014 *************** *** 212,217 **** --- 212,218 ---- s;\blmtp_address_preference\b;$&;g; s;\blmtp_body_checks\b;$&;g; s;\blmtp_cname_overrides_servername\b;$&;g; + s;\blmtp_bounce_defer_filter\b;$&;g; s;\blmtp_dns_resolver_options\b;$&;g; s;\blmtp_dns_support_level\b;$&;g; s;\blmtp_header_checks\b;$&;g; *************** *** 451,456 **** --- 452,458 ---- s;\bsmtp_connection_cache_time_limit\b;$&;g; s;\bsmtp_connection_cache_destinations\b;$&;g; + s;\bsmtp_bounce_defer_filter\b;$&;g; s;\bsmtp_data_done_timeout\b;$&;g; s;\bsmtp_data_init_timeout\b;$&;g; s;\bsmtp_data_xfer_timeout\b;$&;g; diff --exclude=.indent.pro --exclude='lmdb*' --exclude='*TLS*' -cr --new-file /var/tmp/postfix-2.12-20140223/proto/postconf.proto ./proto/postconf.proto *** /var/tmp/postfix-2.12-20140223/proto/postconf.proto Fri Feb 14 09:47:30 2014 --- ./proto/postconf.proto Sat Mar 15 18:49:44 2014 *************** *** 15615,15617 **** --- 15615,15693 ---- anchor assertion) TLSA records.

This feature is available in Postfix 2.11 and later.

+ + %PARAM smtp_bounce_defer_filter + +

Optional filter to rewrite the three-number enhanced status + code and the explanatory text in a Postfix SMTP/LMTP client + bounce/defer message.

+ +

With each bounce or defer request, the user-specified filter is + queried with one line of text that is structured as follows:

+ +
+ enhanced-status-code SPACE explanatory-text +
+ +

The bounce_defer_filter feature may replace this only with text + that has the same structure. Enhanced status codes must have a + first numerical field of 4 (defer) or 5 (bounce), and the free text + field must be non-empty. Other results will result in a warning. +

+ +

Example:

+ +

The following example turns soft TLS errors into hard errors, + by overriding the first number in the enhanced status code.

+ +
+
+ /etc/postfix/main.cf:
+     smtp_bounce_defer_filter = pcre:/etc/postfix/smtp_ndr_filter
+ 
+
+ +
+
+ /etc/postfix/smtp_ndr_filter:
+     /^4(\.\d+\.\d+ TLS is required, but host \S+ refused to start TLS: .+)
+         5$1
+     /^4(\.\d+\.\d+ TLS is required, but was not offered by host .+)
+         5$1
+     # Do not change the following into hard bounces. They may
+     # result from a local configuration problem.
+     # 4.\d+.\d+ TLS is required, but our TLS engine is unavailable
+     # 4.\d+.\d+ TLS is required, but unavailable
+     # 4.\d+.\d+ Cannot start TLS: handshake failure
+ 
+
+ +

Notes:

+ + + +

This feature is available in Postfix 2.12 and later.

+ + %PARAM lmtp_bounce_defer_filter + +

The LMTP-specific version of the smtp_bounce_defer_filter + configuration parameter. See there for details.

+ +

This feature is available in Postfix 2.12 and later.

diff --exclude=.indent.pro --exclude='lmdb*' --exclude='*TLS*' -cr --new-file /var/tmp/postfix-2.12-20140223/src/global/Makefile.in ./src/global/Makefile.in *** /var/tmp/postfix-2.12-20140223/src/global/Makefile.in Mon Dec 9 15:13:50 2013 --- ./src/global/Makefile.in Sat Mar 15 19:02:42 2014 *************** *** 32,38 **** match_service.c mail_conf_nint.c addr_match_list.c mail_conf_nbool.c \ smtp_reply_footer.c safe_ultostr.c verify_sender_addr.c \ dict_memcache.c mail_version.c memcache_proto.c server_acl.c \ ! mkmap_fail.c haproxy_srvr.c OBJS = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \ canon_addr.o cfg_parser.o cleanup_strerror.o cleanup_strflags.o \ clnt_stream.o conv_time.o db_common.o debug_peer.o debug_process.o \ --- 32,38 ---- match_service.c mail_conf_nint.c addr_match_list.c mail_conf_nbool.c \ smtp_reply_footer.c safe_ultostr.c verify_sender_addr.c \ dict_memcache.c mail_version.c memcache_proto.c server_acl.c \ ! mkmap_fail.c haproxy_srvr.c ndr_filter.c OBJS = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \ canon_addr.o cfg_parser.o cleanup_strerror.o cleanup_strflags.o \ clnt_stream.o conv_time.o db_common.o debug_peer.o debug_process.o \ *************** *** 66,72 **** match_service.o mail_conf_nint.o addr_match_list.o mail_conf_nbool.o \ smtp_reply_footer.o safe_ultostr.o verify_sender_addr.o \ dict_memcache.o mail_version.o memcache_proto.o server_acl.o \ ! mkmap_fail.o haproxy_srvr.o HDRS = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \ canon_addr.h cfg_parser.h cleanup_user.h clnt_stream.h config.h \ conv_time.h db_common.h debug_peer.h debug_process.h defer.h \ --- 66,72 ---- match_service.o mail_conf_nint.o addr_match_list.o mail_conf_nbool.o \ smtp_reply_footer.o safe_ultostr.o verify_sender_addr.o \ dict_memcache.o mail_version.o memcache_proto.o server_acl.o \ ! mkmap_fail.o haproxy_srvr.o ndr_filter.o HDRS = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \ canon_addr.h cfg_parser.h cleanup_user.h clnt_stream.h config.h \ conv_time.h db_common.h debug_peer.h debug_process.h defer.h \ *************** *** 93,99 **** fold_addr.h header_body_checks.h data_redirect.h match_service.h \ addr_match_list.h smtp_reply_footer.h safe_ultostr.h \ verify_sender_addr.h dict_memcache.h memcache_proto.h server_acl.h \ ! haproxy_srvr.h TESTSRC = rec2stream.c stream2rec.c recdump.c DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) CFLAGS = $(DEBUG) $(OPT) $(DEFS) --- 93,99 ---- fold_addr.h header_body_checks.h data_redirect.h match_service.h \ addr_match_list.h smtp_reply_footer.h safe_ultostr.h \ verify_sender_addr.h dict_memcache.h memcache_proto.h server_acl.h \ ! haproxy_srvr.h ndr_filter.h TESTSRC = rec2stream.c stream2rec.c recdump.c DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) CFLAGS = $(DEBUG) $(OPT) $(DEFS) *************** *** 666,671 **** --- 666,672 ---- bounce.o: mail_params.h bounce.o: mail_proto.h bounce.o: msg_stats.h + bounce.o: ndr_filter.h bounce.o: rcpt_print.h bounce.o: recipient_list.h bounce.o: trace.h *************** *** 811,816 **** --- 812,818 ---- defer.o: mail_proto.h defer.o: mail_queue.h defer.o: msg_stats.h + defer.o: ndr_filter.h defer.o: rcpt_print.h defer.o: recipient_list.h defer.o: trace.h *************** *** 1768,1773 **** --- 1806,1825 ---- namadr_list.o: ../../include/sys_defs.h namadr_list.o: namadr_list.c namadr_list.o: namadr_list.h + ndr_filter.o: ../../include/argv.h + ndr_filter.o: ../../include/dict.h + ndr_filter.o: ../../include/msg.h + ndr_filter.o: ../../include/myflock.h + ndr_filter.o: ../../include/mymalloc.h + ndr_filter.o: ../../include/sys_defs.h + ndr_filter.o: ../../include/vbuf.h + ndr_filter.o: ../../include/vstream.h + ndr_filter.o: ../../include/vstring.h + ndr_filter.o: dsn.h + ndr_filter.o: dsn_util.h + ndr_filter.o: maps.h + ndr_filter.o: ndr_filter.c + ndr_filter.o: ndr_filter.h off_cvt.o: ../../include/msg.h off_cvt.o: ../../include/sys_defs.h off_cvt.o: ../../include/vbuf.h diff --exclude=.indent.pro --exclude='lmdb*' --exclude='*TLS*' -cr --new-file /var/tmp/postfix-2.12-20140223/src/global/bounce.c ./src/global/bounce.c *** /var/tmp/postfix-2.12-20140223/src/global/bounce.c Tue May 15 16:13:00 2007 --- ./src/global/bounce.c Sat Mar 15 19:46:29 2014 *************** *** 48,53 **** --- 48,66 ---- /* RECIPIENT *rcpt; /* const char *relay; /* DSN *dsn; + /* + /* void bounce_client_init(title, maps) + /* const char *title; + /* const char *maps; + /* INTERNAL API + /* NDR_FILTER *bounce_defer_filter; + /* + /* int bounce_append_intern(flags, id, stats, recipient, relay, dsn) + /* int flags; + /* const char *id; + /* MSG_STATS *stats; + /* RECIPIENT *rcpt; + /* const char *relay; /* DESCRIPTION /* This module implements the client interface to the message /* bounce service, which maintains a per-message log of status *************** *** 75,80 **** --- 88,96 ---- /* return address in a manner that depends on the recipient /* address. /* + /* bounce_append_intern() is for use after the DSN filter. DSN + /* filtering is not yet supported for bounce_one(). + /* /* Arguments: /* .IP flags /* The bitwise OR of zero or more of the following (specify *************** *** 158,163 **** --- 174,180 ---- /* Global library. */ + #define BOUNCE_DEFER_INTERN #include #include #include *************** *** 169,182 **** #include #include ! /* bounce_append - append dsn_text to per-message bounce log */ int bounce_append(int flags, const char *id, MSG_STATS *stats, RECIPIENT *rcpt, const char *relay, DSN *dsn) { DSN my_dsn = *dsn; ! int status; /* * Sanity check. If we're really confident, change this into msg_panic --- 186,203 ---- #include #include ! /* Shared internally, between bounce and defer clients. */ ! ! NDR_FILTER *bounce_defer_filter; ! ! /* bounce_append - append delivery status to per-message bounce log */ int bounce_append(int flags, const char *id, MSG_STATS *stats, RECIPIENT *rcpt, const char *relay, DSN *dsn) { DSN my_dsn = *dsn; ! DSN *dsn_res; /* * Sanity check. If we're really confident, change this into msg_panic *************** *** 188,193 **** --- 209,235 ---- } /* + * DSN filter (Postfix 2.12). + */ + if (bounce_defer_filter != 0 + && (dsn_res = ndr_filter_lookup(bounce_defer_filter, &my_dsn)) != 0) { + if (dsn_res->status[0] == '4') + return (defer_append_intern(flags, id, stats, rcpt, relay, dsn_res)); + my_dsn = *dsn_res; + } + return (bounce_append_intern(flags, id, stats, rcpt, relay, &my_dsn)); + } + + /* bounce_append_intern - append delivery status to per-message bounce log */ + + int bounce_append_intern(int flags, const char *id, MSG_STATS *stats, + RECIPIENT *rcpt, const char *relay, + DSN *dsn) + { + DSN my_dsn = *dsn; + int status; + + /* * MTA-requested address verification information is stored in the verify * service database. */ *************** *** 259,265 **** vstring_sprintf(junk, "%s or %s service failure", var_bounce_service, var_trace_service); my_dsn.reason = vstring_str(junk); ! status = defer_append(flags, id, stats, rcpt, relay, &my_dsn); vstring_free(junk); } else { status = -1; --- 301,307 ---- vstring_sprintf(junk, "%s or %s service failure", var_bounce_service, var_trace_service); my_dsn.reason = vstring_str(junk); ! status = defer_append_intern(flags, id, stats, rcpt, relay, &my_dsn); vstring_free(junk); } else { status = -1; *************** *** 428,430 **** --- 470,484 ---- return (status); } } + + /* bounce_client_init - initialize bounce/defer DSN filter */ + + void bounce_client_init(const char *title, const char *maps) + { + const char myname[] = "bounce_client_init"; + + if (bounce_defer_filter != 0) + msg_panic("%s: duplicate initialization", myname); + if (*maps) + bounce_defer_filter = ndr_filter_create(title, maps); + } diff --exclude=.indent.pro --exclude='lmdb*' --exclude='*TLS*' -cr --new-file /var/tmp/postfix-2.12-20140223/src/global/bounce.h ./src/global/bounce.h *** /var/tmp/postfix-2.12-20140223/src/global/bounce.h Sat Jan 7 15:49:53 2006 --- ./src/global/bounce.h Sat Mar 15 18:28:59 2014 *************** *** 35,40 **** --- 35,41 ---- const char *, const char *, int, MSG_STATS *, RECIPIENT *, const char *, DSN *); + extern void bounce_client_init(const char *, const char *); /* * Bounce/defer protocol commands. *************** *** 65,70 **** --- 66,90 ---- */ #define BOUNCE_FLAG_KEEP BOUNCE_FLAG_NONE + /* + * Start of private API. + */ + + #ifdef BOUNCE_DEFER_INTERN + + #include + + extern NDR_FILTER *bounce_defer_filter; + + extern int bounce_append_intern(int, const char *, MSG_STATS *, RECIPIENT *, + const char *, DSN *); + extern int bounce_one_intern(int, const char *, const char *, const char *, + const char *, const char *, + int, MSG_STATS *, RECIPIENT *, + const char *, DSN *); + + #endif + /* LICENSE /* .ad /* .fi diff --exclude=.indent.pro --exclude='lmdb*' --exclude='*TLS*' -cr --new-file /var/tmp/postfix-2.12-20140223/src/global/defer.c ./src/global/defer.c *** /var/tmp/postfix-2.12-20140223/src/global/defer.c Tue May 15 16:13:13 2007 --- ./src/global/defer.c Sat Mar 15 19:46:41 2014 *************** *** 31,36 **** --- 31,43 ---- /* const char *sender; /* const char *dsn_envid; /* int dsn_ret; + /* INTERNAL API + /* int defer_append_intern(flags, id, stats, rcpt, relay, dsn) + /* int flags; + /* const char *id; + /* MSG_STATS *stats; + /* RECIPIENT *rcpt; + /* const char *relay; /* DESCRIPTION /* This module implements a client interface to the defer service, /* which maintains a per-message logfile with status records for *************** *** 56,61 **** --- 63,70 ---- /* question has been deferred. The defer log is not deleted, /* and no recipients are deleted from the original queue file. /* + /* defer_append_intern() is for use after the DSN filter. + /* /* Arguments: /* .IP flags /* The bit-wise OR of zero or more of the following (specify *************** *** 134,139 **** --- 143,149 ---- /* Global library. */ + #define BOUNCE_DEFER_INTERN #include #include #include *************** *** 154,162 **** RECIPIENT *rcpt, const char *relay, DSN *dsn) { - const char *rcpt_domain; DSN my_dsn = *dsn; ! int status; /* * Sanity check. --- 164,171 ---- RECIPIENT *rcpt, const char *relay, DSN *dsn) { DSN my_dsn = *dsn; ! DSN *dsn_res; /* * Sanity check. *************** *** 167,172 **** --- 176,203 ---- } /* + * DSN filter (Postfix 2.12). + */ + if (bounce_defer_filter != 0 + && (dsn_res = ndr_filter_lookup(bounce_defer_filter, &my_dsn)) != 0) { + if (dsn_res->status[0] == '5') + return (bounce_append_intern(flags, id, stats, rcpt, relay, dsn_res)); + my_dsn = *dsn_res; + } + return (defer_append_intern(flags, id, stats, rcpt, relay, &my_dsn)); + } + + /* defer_append_intern - defer message delivery */ + + int defer_append_intern(int flags, const char *id, MSG_STATS *stats, + RECIPIENT *rcpt, const char *relay, + DSN *dsn) + { + const char *rcpt_domain; + DSN my_dsn = *dsn; + int status; + + /* * MTA-requested address verification information is stored in the verify * service database. */ diff --exclude=.indent.pro --exclude='lmdb*' --exclude='*TLS*' -cr --new-file /var/tmp/postfix-2.12-20140223/src/global/defer.h ./src/global/defer.h *** /var/tmp/postfix-2.12-20140223/src/global/defer.h Tue Nov 1 15:35:05 2005 --- ./src/global/defer.h Sat Mar 15 18:53:06 2014 *************** *** 26,31 **** --- 26,41 ---- extern int defer_warn(int, const char *, const char *, const char *, const char *, int); + /* + * Start of private API. + */ + #ifdef BOUNCE_DEFER_INTERN + + extern int defer_append_intern(int, const char *, MSG_STATS *, RECIPIENT *, + const char *, DSN *); + + #endif + /* LICENSE /* .ad /* .fi diff --exclude=.indent.pro --exclude='lmdb*' --exclude='*TLS*' -cr --new-file /var/tmp/postfix-2.12-20140223/src/global/mail_params.h ./src/global/mail_params.h *** /var/tmp/postfix-2.12-20140223/src/global/mail_params.h Sat Dec 28 20:27:16 2013 --- ./src/global/mail_params.h Sat Mar 15 18:30:36 2014 *************** *** 3744,3749 **** --- 3744,3758 ---- #define DEF_DAEMON_OPEN_FATAL 0 extern bool var_daemon_open_fatal; + /* + * Optional DSN bounce/defer filter. + */ + #define VAR_SMTP_NDR_FILTER "smtp_bounce_defer_filter" + #define DEF_SMTP_NDR_FILTER "" + #define VAR_LMTP_NDR_FILTER "lmtp_bounce_defer_filter" + #define DEF_LMTP_NDR_FILTER "" + extern char *var_ndr_filter; + /* LICENSE /* .ad /* .fi diff --exclude=.indent.pro --exclude='lmdb*' --exclude='*TLS*' -cr --new-file /var/tmp/postfix-2.12-20140223/src/global/ndr_filter.c ./src/global/ndr_filter.c *** /var/tmp/postfix-2.12-20140223/src/global/ndr_filter.c Wed Dec 31 19:00:00 1969 --- ./src/global/ndr_filter.c Sat Mar 15 20:14:11 2014 *************** *** 0 **** --- 1,178 ---- + /*++ + /* NAME + /* ndr_filter 3 + /* SUMMARY + /* bounce or defer NDR filter + /* SYNOPSIS + /* #include + /* + /* NDR_FILTER *ndr_filter_create( + /* const char *title, + /* const char *map_names) + /* + /* DSN *ndr_filter_lookup( + /* NDR_FILTER *fp, + /* DSN dsn) + /* + /* void dsn_free( + /* NDR_FILTER *fp) + /* DESCRIPTION + /* This module maps a bounce or defer non-delivery status code + /* and text into a bounce or defer non-delivery status code + /* and text. The other DSN attributes are passed through without + /* modification. + /* + /* ndr_filter_create() instantiates a bounce or defer NDR filter. + /* + /* ndr_filter_lookup() queries the specified filter. The DSN + /* must be a bounce or defer DSN. If a match is found and the + /* result is properly formatted, the result value must specify + /* a bounce or defer DSN. The result is in part overwritten + /* upon each call, and is in part a shallow copy of the dsn + /* argument. The result is a null pointer when no valid match + /* is found. This function must not be called with the result + /* from a ndr_filter_lookup() call. + /* + /* dsn_free() destroys the specified NDR filter. + /* + /* Arguments: + /* .IP title + /* Origin of the mapnames argument, typically a configuration + /* parameter name. This is reported in diagnostics. + /* .IP mapnames + /* List of lookup tables, separated by whitespace or comma. + /* .IP fp + /* filter created with ndr_filter_create() + /* .IP dsn + /* A bounce or defer DSN data structure. The ndr_filter_lookup() + /* result value is in part a shallow copy of this argument. + /* SEE ALSO + /* maps(3) multi-table search + /* DIAGNOSTICS + /* Panic: invalid dsn argument; recursive call. Fatal error: + /* memory allocation problem. Warning: invalid DSN lookup + /* result. + /* 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 libraries. + */ + #include + + /* + * Utility library. + */ + #include + #include + #include + + /* + * Global library. + */ + #include + #include + #include + #include + #include + + /* + * Private data structure. + */ + struct NDR_FILTER { + MAPS *maps; + VSTRING *buffer; /* status code and text */ + DSN_SPLIT dp; + DSN dsn; /* Shallow copy */ + }; + + /* + * SLMs. + */ + #define STR(x) vstring_str(x) + + /* ndr_filter_create - create bounce/defer NDR filter */ + + NDR_FILTER *ndr_filter_create(const char *title, const char *map_names) + { + const char myname[] = "ndr_filter_create"; + NDR_FILTER *fp; + + if (msg_verbose) + msg_info("%s: %s %s", myname, title, map_names); + + fp = (NDR_FILTER *) mymalloc(sizeof(*fp)); + fp->buffer = vstring_alloc(100); + fp->maps = maps_create(title, map_names, DICT_FLAG_LOCK); + return (fp); + } + + /* ndr_filter_lookup - apply bounce/defer NDR filter */ + + DSN *ndr_filter_lookup(NDR_FILTER *fp, DSN *dsn) + { + const char myname[] = "ndr_filter_lookup"; + const char *result; + + if (msg_verbose) + msg_info("%s: %s %s", myname, dsn->status, dsn->reason); + + #define IS_NDR_DSN(s) \ + (dsn_valid(s) && (s)[1] == '.' && ((s)[0] == '4' || (s)[0] == '5')) + + /* + * Sanity check. We filter only bounce/defer DSNs. + */ + if (!IS_NDR_DSN(dsn->status)) + msg_panic("%s: dsn argument with bad status code: %s", + myname, dsn->status); + + /* + * Sanity check. An NDR filter must not be invoked with its own result. + */ + if (dsn->reason == fp->dsn.reason) + msg_panic("%s: recursive call is not allowed", myname); + + /* + * Look up replacement status and text. + */ + vstring_sprintf(fp->buffer, "%s %s", dsn->status, dsn->reason); + if ((result = maps_find(fp->maps, STR(fp->buffer), 0)) != 0) { + /* Sanity check. We accept only bounce/defer DSNs. */ + if (!IS_NDR_DSN(result)) { + msg_warn("%s: bad status code: %s", fp->maps->title, result); + return (0); + } else { + vstring_strcpy(fp->buffer, result); + dsn_split(&fp->dp, "can't happen", STR(fp->buffer)); + (void) DSN_ASSIGN(&fp->dsn, DSN_STATUS(fp->dp.dsn), + (result[0] == '4' ? "delayed" : "failed"), + fp->dp.text, dsn->dtype, dsn->dtext, + dsn->mtype, dsn->mname); + return (&fp->dsn); + } + } + return (0); + } + + /* ndr_filter_free - destroy bounce/defer NDR filter */ + + void ndr_filter_free(NDR_FILTER *fp) + { + const char myname[] = "ndr_filter_free"; + + if (msg_verbose) + msg_info("%s: %s", myname, fp->maps->title); + + maps_free(fp->maps); + vstring_free(fp->buffer); + myfree((char *) fp); + } diff --exclude=.indent.pro --exclude='lmdb*' --exclude='*TLS*' -cr --new-file /var/tmp/postfix-2.12-20140223/src/global/ndr_filter.h ./src/global/ndr_filter.h *** /var/tmp/postfix-2.12-20140223/src/global/ndr_filter.h Wed Dec 31 19:00:00 1969 --- ./src/global/ndr_filter.h Sat Mar 15 17:27:29 2014 *************** *** 0 **** --- 1,34 ---- + #ifndef _NDR_FILTER_H_INCLUDED_ + #define _NDR_FILTER_H_INCLUDED_ + + /*++ + /* NAME + /* ndr_filter 3h + /* SUMMARY + /* bounce/defer DSN filter + /* SYNOPSIS + /* #include + /* DESCRIPTION + /* .nf + + /* + * External interface. + */ + typedef struct NDR_FILTER NDR_FILTER; + + extern NDR_FILTER *ndr_filter_create(const char *, const char *); + extern DSN *ndr_filter_lookup(NDR_FILTER *, DSN *); + extern void ndr_filter_free(NDR_FILTER *); + + /* 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 + /*--*/ + + #endif diff --exclude=.indent.pro --exclude='lmdb*' --exclude='*TLS*' -cr --new-file /var/tmp/postfix-2.12-20140223/src/master/mail_server.h ./src/master/mail_server.h *** /var/tmp/postfix-2.12-20140223/src/master/mail_server.h Thu Jun 14 15:43:35 2012 --- ./src/master/mail_server.h Sat Mar 15 08:38:01 2014 *************** *** 38,43 **** --- 38,44 ---- #define MAIL_SERVER_IN_FLOW_DELAY 20 #define MAIL_SERVER_SLOW_EXIT 21 + #define MAIL_SERVER_BOUNCE_INIT 22 typedef void (*MAIL_SERVER_INIT_FN) (char *, char **); typedef int (*MAIL_SERVER_LOOP_FN) (char *, char **); diff --exclude=.indent.pro --exclude='lmdb*' --exclude='*TLS*' -cr --new-file /var/tmp/postfix-2.12-20140223/src/master/single_server.c ./src/master/single_server.c *** /var/tmp/postfix-2.12-20140223/src/master/single_server.c Thu Jun 21 09:15:04 2012 --- ./src/master/single_server.c Sat Mar 15 17:19:04 2014 *************** *** 118,123 **** --- 118,126 ---- /* This service must be configured with process limit of 0. /* .IP MAIL_SERVER_PRIVILEGED /* This service must be configured as privileged. + /* .IP "MAIL_SERVER_BOUNCE_INIT (const char *, const char **)" + /* Initialize the DSN filter for the bounce/defer service + /* clients with the specified map source and map names. /* .PP /* The var_use_limit variable limits the number of clients that /* a server can service before it commits suicide. *************** *** 194,199 **** --- 197,203 ---- #include #include #include + #include /* Process manager. */ *************** *** 430,435 **** --- 434,441 ---- char *generation; int msg_vstream_needed = 0; int redo_syslog_init = 0; + const char *ndr_filter_title; + const char **ndr_filter_maps; /* * Process environment options as early as we can. *************** *** 631,636 **** --- 637,647 ---- msg_fatal("service %s requires privileged operation", service_name); break; + case MAIL_SERVER_BOUNCE_INIT: + ndr_filter_title = va_arg(ap, const char *); + ndr_filter_maps = va_arg(ap, const char **); + bounce_client_init(ndr_filter_title, *ndr_filter_maps); + break; default: msg_panic("%s: unknown argument type: %d", myname, key); } diff --exclude=.indent.pro --exclude='lmdb*' --exclude='*TLS*' -cr --new-file /var/tmp/postfix-2.12-20140223/src/smtp/lmtp_params.c ./src/smtp/lmtp_params.c *** /var/tmp/postfix-2.12-20140223/src/smtp/lmtp_params.c Thu Feb 20 19:54:26 2014 --- ./src/smtp/lmtp_params.c Sat Mar 15 18:02:56 2014 *************** *** 57,62 **** --- 57,63 ---- VAR_LMTP_RESP_FILTER, DEF_LMTP_RESP_FILTER, &var_smtp_resp_filter, 0, 0, VAR_LMTP_ADDR_PREF, DEF_LMTP_ADDR_PREF, &var_smtp_addr_pref, 1, 0, VAR_LMTP_DNS_RES_OPT, DEF_LMTP_DNS_RES_OPT, &var_smtp_dns_res_opt, 0, 0, + VAR_LMTP_NDR_FILTER, DEF_LMTP_NDR_FILTER, &var_smtp_ndr_filter, 0, 0, 0, }; static const CONFIG_TIME_TABLE lmtp_time_table[] = { diff --exclude=.indent.pro --exclude='lmdb*' --exclude='*TLS*' -cr --new-file /var/tmp/postfix-2.12-20140223/src/smtp/smtp.c ./src/smtp/smtp.c *** /var/tmp/postfix-2.12-20140223/src/smtp/smtp.c Sun Feb 23 17:27:33 2014 --- ./src/smtp/smtp.c Sat Mar 15 18:21:41 2014 *************** *** 141,146 **** --- 141,150 ---- /* Always send EHLO at the start of an SMTP session. /* .IP "\fBsmtp_never_send_ehlo (no)\fR" /* Never send EHLO at the start of an SMTP session. + /* .IP "\fBsmtp_bounce_defer_filter (empty)\fR" + /* Optional filter to rewrite the three-number enhanced status + /* code and the explanatory text in a Postfix SMTP/LMTP client + /* bounce/defer message. /* .IP "\fBsmtp_defer_if_no_mx_address_found (no)\fR" /* Defer mail delivery when no MX record resolves to an IP address. /* .IP "\fBsmtp_line_length_limit (998)\fR" *************** *** 876,881 **** --- 880,886 ---- char *var_smtp_dns_support; bool var_smtp_rec_deadline; bool var_smtp_dummy_mail_auth; + char *var_smtp_ndr_filter; /* Special handling of 535 AUTH errors. */ char *var_smtp_sasl_auth_cache_name; *************** *** 1271,1275 **** --- 1276,1282 ---- MAIL_SERVER_PRE_INIT, pre_init, MAIL_SERVER_POST_INIT, post_init, MAIL_SERVER_PRE_ACCEPT, pre_accept, + MAIL_SERVER_BOUNCE_INIT, VAR_SMTP_NDR_FILTER, + &var_smtp_ndr_filter, 0); } diff --exclude=.indent.pro --exclude='lmdb*' --exclude='*TLS*' -cr --new-file /var/tmp/postfix-2.12-20140223/src/smtp/smtp_params.c ./src/smtp/smtp_params.c *** /var/tmp/postfix-2.12-20140223/src/smtp/smtp_params.c Thu Feb 20 19:53:48 2014 --- ./src/smtp/smtp_params.c Sat Mar 15 18:02:46 2014 *************** *** 58,63 **** --- 58,64 ---- VAR_SMTP_RESP_FILTER, DEF_SMTP_RESP_FILTER, &var_smtp_resp_filter, 0, 0, VAR_SMTP_ADDR_PREF, DEF_SMTP_ADDR_PREF, &var_smtp_addr_pref, 1, 0, VAR_SMTP_DNS_RES_OPT, DEF_SMTP_DNS_RES_OPT, &var_smtp_dns_res_opt, 0, 0, + VAR_SMTP_NDR_FILTER, DEF_SMTP_NDR_FILTER, &var_smtp_ndr_filter, 0, 0, 0, }; static const CONFIG_TIME_TABLE smtp_time_table[] = {