This is a minimal patch to add support for delivery status filtering
in the Postfix 2.11 SMTP client, originally developed to change some
soft TLS-related into hard errors.
smtp_delivery_status_filter (default: empty)
Optional filter for the smtp(8) delivery agent to change the delivery
status code or explanatory text of successful or unsuccessful deliver-
ies.
Specify zero or more "type:table" lookup table names, separated by
comma or whitespace. For each successful or unsuccessful delivery to a
recipient, the tables are queried in the specified order with one line
of text that is structured as follows:
enhanced-status-code SPACE explanatory-text
The first table match wins. The lookup result must have the same struc-
ture as the query, a successful status code (2.X.X) must be replaced
with a successful status code, an unsuccessful status code (4.X.X or
5.X.X) must be replaced with an unsuccessful status code, and the
explanatory text field must be non-empty. Other results will result in
a warning.
Example: convert specific soft TLS errors into hard errors, by overrid-
ing the first number in the enhanced status code.
/etc/postfix/main.cf:
smtp_delivery_status_filter = pcre:/etc/postfix/smtp_dsn_filter
/etc/postfix/smtp_dsn_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 will NOT override the soft_bounce safety net.
o This feature will change the enhanced status code and text that
is logged to the maillog file, and that is reported to the
sender in delivery confirmation or non-delivery notifications.
o NOTE: This feature modifies Postfix SMTP client error or non-
error messages that may or may not be derived from remote SMTP
server responses. In contrast, the smtp_reply_filter feature
modifies remote SMTP server responses only.
diff -cr --new-file ../postfix-2.11.0/html/lmtp.8.html ./html/lmtp.8.html
*** ../postfix-2.11.0/html/lmtp.8.html Fri Dec 20 19:37:50 2013
--- ./html/lmtp.8.html Sat Mar 22 19:32:09 2014
***************
*** 289,294 ****
--- 289,301 ----
smtp_dns_support_level (empty)
Level of DNS support in the Postfix SMTP client.
+ Imported from Postfix version 2.12:
+
+ smtp_delivery_status_filter (empty)
+ Optional filter for the smtp(8) delivery agent to change the
+ delivery status code or explanatory text of successful or unsuc-
+ cessful deliveries.
+
MIME PROCESSING CONTROLS
Available in Postfix version 2.0 and later:
diff -cr --new-file ../postfix-2.11.0/html/postconf.5.html ./html/postconf.5.html
*** ../postfix-2.11.0/html/postconf.5.html Sun Jan 12 13:01:05 2014
--- ./html/postconf.5.html Sat Mar 22 19:33:33 2014
***************
*** 9751,9756 ****
--- 9751,9827 ----
+
diff -cr --new-file ../postfix-2.11.0/html/smtp.8.html ./html/smtp.8.html
*** ../postfix-2.11.0/html/smtp.8.html Fri Dec 20 19:37:50 2013
--- ./html/smtp.8.html Sat Mar 22 19:32:09 2014
***************
*** 289,294 ****
--- 289,301 ----
smtp_dns_support_level (empty)
Level of DNS support in the Postfix SMTP client.
+ Imported from Postfix version 2.12:
+
+ smtp_delivery_status_filter (empty)
+ Optional filter for the smtp(8) delivery agent to change the
+ delivery status code or explanatory text of successful or unsuc-
+ cessful deliveries.
+
MIME PROCESSING CONTROLS
Available in Postfix version 2.0 and later:
diff -cr --new-file ../postfix-2.11.0/man/man5/postconf.5 ./man/man5/postconf.5
*** ../postfix-2.11.0/man/man5/postconf.5 Sun Jan 12 13:01:05 2014
--- ./man/man5/postconf.5 Sat Mar 22 19:33:33 2014
***************
*** 5935,5940 ****
--- 5935,6009 ----
than the local MTA itself.
.PP
This feature is available in Postfix 2.1 and later.
+ .SH smtp_delivery_status_filter (default: empty)
+ Optional filter for the \fBsmtp\fR(8) delivery agent to change the
+ delivery status code or explanatory text of successful or unsuccessful
+ deliveries.
+ .PP
+ Specify zero or more "type:table" lookup table names, separated
+ by comma or whitespace. For each successful or unsuccessful delivery
+ to a recipient, the tables are queried in the specified order with
+ one line of text that is structured as follows:
+ .sp
+ .in +4
+ enhanced-status-code SPACE explanatory-text
+ .in -4
+ .PP
+ The first table match wins. The lookup result must have the
+ same structure as the query, a successful status code (2.X.X) must
+ be replaced with a successful status code, an unsuccessful status
+ code (4.X.X or 5.X.X) must be replaced with an unsuccessful status
+ code, and the explanatory text field must be non-empty. Other results
+ will result in a warning.
+ .PP
+ Example: convert specific 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_delivery_status_filter = pcre:/etc/postfix/smtp_dsn_filter
+ .fi
+ .ad
+ .ft R
+ .in -4
+ .sp
+ .in +4
+ .nf
+ .na
+ .ft C
+ /etc/postfix/smtp_dsn_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 will NOT override the soft_bounce safety net.
+ .IP \(bu
+ This feature will change the enhanced status code and text
+ that is logged to the maillog file, and that is reported to the
+ sender in delivery confirmation or non-delivery notifications.
+ .IP \(bu
+ NOTE: This feature modifies Postfix SMTP client error or
+ non-error messages that may or may not be derived from remote SMTP
+ server responses. In contrast, the smtp_reply_filter feature
+ modifies remote SMTP server responses only.
+ .br
+ .PP
+ This feature is imported from Postfix 2.12.
.SH smtp_destination_concurrency_limit (default: $default_destination_concurrency_limit)
The maximal number of parallel deliveries to the same destination
via the smtp message delivery transport. This limit is enforced by
diff -cr --new-file ../postfix-2.11.0/man/man8/smtp.8 ./man/man8/smtp.8
*** ../postfix-2.11.0/man/man8/smtp.8 Tue Nov 26 17:59:16 2013
--- ./man/man8/smtp.8 Sat Mar 22 19:31:57 2014
***************
*** 273,278 ****
--- 273,284 ----
Available in Postfix version 2.11 and later:
.IP "\fBsmtp_dns_support_level (empty)\fR"
Level of DNS support in the Postfix SMTP client.
+ .PP
+ Imported from Postfix version 2.12:
+ .IP "\fBsmtp_delivery_status_filter (empty)\fR"
+ Optional filter for the \fBsmtp\fR(8) delivery agent to change the
+ delivery status code or explanatory text of successful or unsuccessful
+ deliveries.
.SH "MIME PROCESSING CONTROLS"
.na
.nf
diff -cr --new-file ../postfix-2.11.0/mantools/postlink ./mantools/postlink
*** ../postfix-2.11.0/mantools/postlink Fri Dec 20 17:20:01 2013
--- ./mantools/postlink Sat Mar 22 18:20:56 2014
***************
*** 451,456 ****
--- 451,457 ----
s;\bsmtp_connection_cache_time_limit\b;$&;g;
s;\bsmtp_connection_cache_destinations\b;$&;g;
+ s;\bsmtp_delivery_status_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 -cr --new-file ../postfix-2.11.0/proto/postconf.proto ./proto/postconf.proto
*** ../postfix-2.11.0/proto/postconf.proto Sun Jan 12 13:00:56 2014
--- ./proto/postconf.proto Sat Mar 22 19:33:28 2014
***************
*** 15092,15097 ****
--- 15092,15164 ----
This feature is available in Postfix 2.9 and later.
+ %PARAM smtp_delivery_status_filter
+
+ Optional filter for the smtp(8) delivery agent to change the
+ delivery status code or explanatory text of successful or unsuccessful
+ deliveries.
+
+ Specify zero or more "type:table" lookup table names, separated
+ by comma or whitespace. For each successful or unsuccessful delivery
+ to a recipient, the tables are queried in the specified order with
+ one line of text that is structured as follows:
+
+
+ enhanced-status-code SPACE explanatory-text
+
+
+ The first table match wins. The lookup result must have the
+ same structure as the query, a successful status code (2.X.X) must
+ be replaced with a successful status code, an unsuccessful status
+ code (4.X.X or 5.X.X) must be replaced with an unsuccessful status
+ code, and the explanatory text field must be non-empty. Other results
+ will result in a warning.
+
+ Example: convert specific soft TLS errors into hard errors,
+ by overriding the first number in the enhanced status code.
+
+
+
+ /etc/postfix/main.cf:
+ smtp_delivery_status_filter = pcre:/etc/postfix/smtp_dsn_filter
+
+
+
+
+
+ /etc/postfix/smtp_dsn_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 will NOT override the soft_bounce safety net.
+
+ -
This feature will change the enhanced status code and text
+ that is logged to the maillog file, and that is reported to the
+ sender in delivery confirmation or non-delivery notifications.
+
+
+ -
NOTE: This feature modifies Postfix SMTP client error or
+ non-error messages that may or may not be derived from remote SMTP
+ server responses. In contrast, the smtp_reply_filter feature
+ modifies remote SMTP server responses only.
+
+
+
+ This feature is imported from Postfix 2.12.
+
%PARAM smtp_send_dummy_mail_auth no
Whether or not to append the "AUTH=<>" option to the MAIL
diff -cr --new-file ../postfix-2.11.0/src/global/Makefile.in ./src/global/Makefile.in
*** ../postfix-2.11.0/src/global/Makefile.in Mon Dec 9 15:13:50 2013
--- ./src/global/Makefile.in Sat Mar 22 18:19:34 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 dsn_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 dsn_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 dsn_filter.h
TESTSRC = rec2stream.c stream2rec.c recdump.c
DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE)
CFLAGS = $(DEBUG) $(OPT) $(DEFS)
***************
*** 660,665 ****
--- 660,666 ----
bounce.o: deliver_request.h
bounce.o: dsn.h
bounce.o: dsn_buf.h
+ bounce.o: dsn_filter.h
bounce.o: dsn_print.h
bounce.o: dsn_util.h
bounce.o: log_adhoc.h
***************
*** 803,808 ****
--- 804,810 ----
defer.o: deliver_request.h
defer.o: dsn.h
defer.o: dsn_buf.h
+ defer.o: dsn_filter.h
defer.o: dsn_print.h
defer.o: dsn_util.h
defer.o: flush_clnt.h
***************
*** 1012,1017 ****
--- 1014,1033 ----
dsn_buf.o: dsn.h
dsn_buf.o: dsn_buf.c
dsn_buf.o: dsn_buf.h
+ dsn_filter.o: ../../include/argv.h
+ dsn_filter.o: ../../include/dict.h
+ dsn_filter.o: ../../include/msg.h
+ dsn_filter.o: ../../include/myflock.h
+ dsn_filter.o: ../../include/mymalloc.h
+ dsn_filter.o: ../../include/sys_defs.h
+ dsn_filter.o: ../../include/vbuf.h
+ dsn_filter.o: ../../include/vstream.h
+ dsn_filter.o: ../../include/vstring.h
+ dsn_filter.o: dsn.h
+ dsn_filter.o: dsn_filter.c
+ dsn_filter.o: dsn_filter.h
+ dsn_filter.o: dsn_util.h
+ dsn_filter.o: maps.h
dsn_mask.o: ../../include/msg.h
dsn_mask.o: ../../include/name_code.h
dsn_mask.o: ../../include/name_mask.h
***************
*** 2035,2040 ****
--- 2051,2057 ----
sent.o: deliver_request.h
sent.o: dsn.h
sent.o: dsn_buf.h
+ sent.o: dsn_filter.h
sent.o: dsn_mask.h
sent.o: dsn_util.h
sent.o: log_adhoc.h
diff -cr --new-file ../postfix-2.11.0/src/global/bounce.c ./src/global/bounce.c
*** ../postfix-2.11.0/src/global/bounce.c Tue May 15 16:13:00 2007
--- ./src/global/bounce.c Sat Mar 22 19:00:59 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
+ /* DSN_FILTER *delivery_status_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,97 ----
/* return address in a manner that depends on the recipient
/* address.
/*
+ /* bounce_client_init() initializes an optional DSN filter.
+ /*
+ /* bounce_append_intern() is for use after the DSN filter.
+ /*
/* Arguments:
/* .IP flags
/* The bitwise OR of zero or more of the following (specify
***************
*** 131,136 ****
--- 148,158 ----
/* zero value. Otherwise, the functions return a non-zero result,
/* and when BOUNCE_FLAG_CLEAN is disabled, log that message
/* delivery is deferred.
+ /* .IP title
+ /* The origin of the optional DSN filter lookup table names.
+ /* .IP maps
+ /* The optional "type:table" DSN filter lookup table names,
+ /* separated by comma or whitespace.
/* BUGS
/* Should be replaced by routines with an attribute-value based
/* interface instead of an interface that uses a rigid argument list.
***************
*** 158,163 ****
--- 180,186 ----
/* Global library. */
+ #define DSN_INTERN
#include
#include
#include
***************
*** 169,174 ****
--- 192,201 ----
#include
#include
+ /* Shared internally, between bounce and defer clients. */
+
+ DSN_FILTER *delivery_status_filter;
+
/* bounce_append - append dsn_text to per-message bounce log */
int bounce_append(int flags, const char *id, MSG_STATS *stats,
***************
*** 176,182 ****
DSN *dsn)
{
DSN my_dsn = *dsn;
! int status;
/*
* Sanity check. If we're really confident, change this into msg_panic
--- 203,209 ----
DSN *dsn)
{
DSN my_dsn = *dsn;
! DSN *dsn_res;
/*
* Sanity check. If we're really confident, change this into msg_panic
***************
*** 188,193 ****
--- 215,241 ----
}
/*
+ * DSN filter (Postfix 2.12).
+ */
+ if (delivery_status_filter != 0
+ && (dsn_res = dsn_filter_lookup(delivery_status_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;
--- 307,313 ----
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 ****
--- 476,490 ----
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 (delivery_status_filter != 0)
+ msg_panic("%s: duplicate initialization", myname);
+ if (*maps)
+ delivery_status_filter = dsn_filter_create(title, maps);
+ }
diff -cr --new-file ../postfix-2.11.0/src/global/bounce.h ./src/global/bounce.h
*** ../postfix-2.11.0/src/global/bounce.h Sat Jan 7 15:49:53 2006
--- ./src/global/bounce.h Sat Mar 22 18:10:01 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,86 ----
*/
#define BOUNCE_FLAG_KEEP BOUNCE_FLAG_NONE
+ /*
+ * Start of private API.
+ */
+
+ #ifdef DSN_INTERN
+
+ #include
+
+ extern DSN_FILTER *delivery_status_filter;
+
+ extern int bounce_append_intern(int, const char *, MSG_STATS *, RECIPIENT *,
+ const char *, DSN *);
+
+ #endif
+
/* LICENSE
/* .ad
/* .fi
diff -cr --new-file ../postfix-2.11.0/src/global/defer.c ./src/global/defer.c
*** ../postfix-2.11.0/src/global/defer.c Tue May 15 16:13:13 2007
--- ./src/global/defer.c Sat Mar 22 18:36:25 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 DSN_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 (delivery_status_filter != 0
+ && (dsn_res = dsn_filter_lookup(delivery_status_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 -cr --new-file ../postfix-2.11.0/src/global/defer.h ./src/global/defer.h
*** ../postfix-2.11.0/src/global/defer.h Tue Nov 1 15:35:05 2005
--- ./src/global/defer.h Sat Mar 22 18:37:20 2014
***************
*** 26,31 ****
--- 26,41 ----
extern int defer_warn(int, const char *, const char *, const char *,
const char *, int);
+ /*
+ * Start of private API.
+ */
+ #ifdef DSN_INTERN
+
+ extern int defer_append_intern(int, const char *, MSG_STATS *, RECIPIENT *,
+ const char *, DSN *);
+
+ #endif
+
/* LICENSE
/* .ad
/* .fi
diff -cr --new-file ../postfix-2.11.0/src/global/dsn_filter.c ./src/global/dsn_filter.c
*** ../postfix-2.11.0/src/global/dsn_filter.c Wed Dec 31 19:00:00 1969
--- ./src/global/dsn_filter.c Sat Mar 22 18:56:46 2014
***************
*** 0 ****
--- 1,193 ----
+ /*++
+ /* NAME
+ /* dsn_filter 3
+ /* SUMMARY
+ /* filter delivery status code or text
+ /* SYNOPSIS
+ /* #include
+ /*
+ /* DSN_FILTER *dsn_filter_create(
+ /* const char *title,
+ /* const char *map_names)
+ /*
+ /* DSN *dsn_filter_lookup(
+ /* DSN_FILTER *fp,
+ /* DSN *dsn)
+ /*
+ /* void dsn_free(
+ /* DSN_FILTER *fp)
+ /* DESCRIPTION
+ /* This module maps (bounce or defer non-delivery status code
+ /* and text) into replacement (bounce or defer non-delivery
+ /* status code and text), or maps (success status code and
+ /* text) into replacement (success status code and text). Other
+ /* DSN attributes are passed through without modification.
+ /*
+ /* dsn_filter_create() instantiates a delivery status filter.
+ /*
+ /* dsn_filter_lookup() queries the specified filter. The input
+ /* DSN must be a success, bounce or defer DSN. If a match is
+ /* found a non-delivery status must map to a non-delivery
+ /* status, a success status must map to a success status, and
+ /* the text must be non-empty. The result is a null pointer
+ /* when no valid match is found. Otherwise, the result is
+ /* overwritten upon each call. This function must not be
+ /* called with the result from a dsn_filter_lookup() call.
+ /*
+ /* dsn_free() destroys the specified delivery status 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 dsn_filter_create()
+ /* .IP dsn
+ /* A success, bounce or defer DSN data structure. The
+ /* dsn_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 DSN_FILTER {
+ MAPS *maps; /* Replacement (status, text) */
+ VSTRING *buffer; /* Status code and text */
+ DSN_SPLIT dp; /* Parsing aid */
+ DSN dsn; /* Shallow copy */
+ };
+
+ /*
+ * SLMs.
+ */
+ #define STR(x) vstring_str(x)
+
+ /* dsn_filter_create - create delivery status filter */
+
+ DSN_FILTER *dsn_filter_create(const char *title, const char *map_names)
+ {
+ const char myname[] = "dsn_filter_create";
+ DSN_FILTER *fp;
+
+ if (msg_verbose)
+ msg_info("%s: %s %s", myname, title, map_names);
+
+ fp = (DSN_FILTER *) mymalloc(sizeof(*fp));
+ fp->buffer = vstring_alloc(100);
+ fp->maps = maps_create(title, map_names, DICT_FLAG_LOCK);
+ return (fp);
+ }
+
+ /* dsn_filter_lookup - apply delivery status filter */
+
+ DSN *dsn_filter_lookup(DSN_FILTER *fp, DSN *dsn)
+ {
+ const char myname[] = "dsn_filter_lookup";
+ const char *result;
+ int ndr_dsn = 0;
+
+ if (msg_verbose)
+ msg_info("%s: %s %s", myname, dsn->status, dsn->reason);
+
+ /*
+ * XXX Instead of hard-coded '4' etc., use some form of encapsulation
+ * when reading or updating the status class field.
+ */
+ #define IS_SUCCESS_DSN(s) (dsn_valid(s) && (s)[0] == '2')
+ #define IS_NDR_DSN(s) (dsn_valid(s) && ((s)[0] == '4' || (s)[0] == '5'))
+
+ /*
+ * Sanity check. We filter only success/bounce/defer DSNs.
+ */
+ if (IS_SUCCESS_DSN(dsn->status))
+ ndr_dsn = 0;
+ else if (IS_NDR_DSN(dsn->status))
+ ndr_dsn = 1;
+ else
+ msg_panic("%s: dsn argument with bad status code: %s",
+ myname, dsn->status);
+
+ /*
+ * Sanity check. A delivery status 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. Do not allow success<=>error mappings. */
+ if ((ndr_dsn == 0 && !IS_SUCCESS_DSN(result))
+ || (ndr_dsn != 0 && !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" :
+ result[0] == '5' ? "failed" :
+ dsn->action),
+ fp->dp.text, dsn->dtype, dsn->dtext,
+ dsn->mtype, dsn->mname);
+ return (&fp->dsn);
+ }
+ }
+ return (0);
+ }
+
+ /* dsn_filter_free - destroy delivery status filter */
+
+ void dsn_filter_free(DSN_FILTER *fp)
+ {
+ const char myname[] = "dsn_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 -cr --new-file ../postfix-2.11.0/src/global/dsn_filter.h ./src/global/dsn_filter.h
*** ../postfix-2.11.0/src/global/dsn_filter.h Wed Dec 31 19:00:00 1969
--- ./src/global/dsn_filter.h Sat Mar 22 18:53:27 2014
***************
*** 0 ****
--- 1,34 ----
+ #ifndef _DSN_FILTER_H_INCLUDED_
+ #define _DSN_FILTER_H_INCLUDED_
+
+ /*++
+ /* NAME
+ /* dsn_filter 3h
+ /* SUMMARY
+ /* delivery status filter
+ /* SYNOPSIS
+ /* #include
+ /* DESCRIPTION
+ /* .nf
+
+ /*
+ * External interface.
+ */
+ typedef struct DSN_FILTER DSN_FILTER;
+
+ extern DSN_FILTER *dsn_filter_create(const char *, const char *);
+ extern DSN *dsn_filter_lookup(DSN_FILTER *, DSN *);
+ extern void dsn_filter_free(DSN_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 -cr --new-file ../postfix-2.11.0/src/global/mail_params.h ./src/global/mail_params.h
*** ../postfix-2.11.0/src/global/mail_params.h Sat Dec 28 20:27:16 2013
--- ./src/global/mail_params.h Sat Mar 22 18:27:16 2014
***************
*** 3744,3749 ****
--- 3744,3758 ----
#define DEF_DAEMON_OPEN_FATAL 0
extern bool var_daemon_open_fatal;
+ /*
+ * Optional delivery status filter.
+ */
+ #define VAR_SMTP_DSN_FILTER "smtp_delivery_status_filter"
+ #define DEF_SMTP_DSN_FILTER ""
+ #define VAR_LMTP_DSN_FILTER "lmtp_delivery_status_filter"
+ #define DEF_LMTP_DSN_FILTER ""
+ extern char *var_smtp_dsn_filter;
+
/* LICENSE
/* .ad
/* .fi
diff -cr --new-file ../postfix-2.11.0/src/global/sent.c ./src/global/sent.c
*** ../postfix-2.11.0/src/global/sent.c Sat Dec 17 17:30:01 2005
--- ./src/global/sent.c Sat Mar 22 18:59:38 2014
***************
*** 79,84 ****
--- 79,85 ----
/* Global library. */
+ #define DSN_INTERN
#include
#include
#include
***************
*** 97,102 ****
--- 98,104 ----
DSN *dsn)
{
DSN my_dsn = *dsn;
+ DSN *dsn_res;
int status;
/*
***************
*** 108,113 ****
--- 110,122 ----
}
/*
+ * DSN filter (Postfix 2.12).
+ */
+ if (delivery_status_filter != 0
+ && (dsn_res = dsn_filter_lookup(delivery_status_filter, &my_dsn)) != 0)
+ my_dsn = *dsn_res;
+
+ /*
* MTA-requested address verification information is stored in the verify
* service database.
*/
diff -cr --new-file ../postfix-2.11.0/src/master/mail_server.h ./src/master/mail_server.h
*** ../postfix-2.11.0/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 -cr --new-file ../postfix-2.11.0/src/master/single_server.c ./src/master/single_server.c
*** ../postfix-2.11.0/src/master/single_server.c Thu Jun 21 09:15:04 2012
--- ./src/master/single_server.c Thu Mar 20 19:09:45 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 *dsn_filter_title;
+ const char **dsn_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:
+ dsn_filter_title = va_arg(ap, const char *);
+ dsn_filter_maps = va_arg(ap, const char **);
+ bounce_client_init(dsn_filter_title, *dsn_filter_maps);
+ break;
default:
msg_panic("%s: unknown argument type: %d", myname, key);
}
diff -cr --new-file ../postfix-2.11.0/src/smtp/lmtp_params.c ./src/smtp/lmtp_params.c
*** ../postfix-2.11.0/src/smtp/lmtp_params.c Sun Aug 25 15:49:34 2013
--- ./src/smtp/lmtp_params.c Sat Mar 22 18:41:25 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_DSN_FILTER, DEF_LMTP_DSN_FILTER, &var_smtp_dsn_filter, 0, 0,
0,
};
static const CONFIG_TIME_TABLE lmtp_time_table[] = {
diff -cr --new-file ../postfix-2.11.0/src/smtp/smtp.c ./src/smtp/smtp.c
*** ../postfix-2.11.0/src/smtp/smtp.c Tue Nov 26 17:58:51 2013
--- ./src/smtp/smtp.c Sat Mar 22 19:29:10 2014
***************
*** 251,256 ****
--- 251,262 ----
/* Available in Postfix version 2.11 and later:
/* .IP "\fBsmtp_dns_support_level (empty)\fR"
/* Level of DNS support in the Postfix SMTP client.
+ /* .PP
+ /* Imported from Postfix version 2.12:
+ /* .IP "\fBsmtp_delivery_status_filter (empty)\fR"
+ /* Optional filter for the \fBsmtp\fR(8) delivery agent to change the
+ /* delivery status code or explanatory text of successful or unsuccessful
+ /* deliveries.
/* MIME PROCESSING CONTROLS
/* .ad
/* .fi
***************
*** 873,878 ****
--- 879,885 ----
char *var_smtp_dns_support;
bool var_smtp_rec_deadline;
bool var_smtp_dummy_mail_auth;
+ char *var_smtp_dsn_filter;
/* Special handling of 535 AUTH errors. */
char *var_smtp_sasl_auth_cache_name;
***************
*** 1268,1272 ****
--- 1275,1281 ----
MAIL_SERVER_PRE_INIT, pre_init,
MAIL_SERVER_POST_INIT, post_init,
MAIL_SERVER_PRE_ACCEPT, pre_accept,
+ MAIL_SERVER_BOUNCE_INIT, VAR_SMTP_DSN_FILTER,
+ &var_smtp_dsn_filter,
0);
}
diff -cr --new-file ../postfix-2.11.0/src/smtp/smtp_params.c ./src/smtp/smtp_params.c
*** ../postfix-2.11.0/src/smtp/smtp_params.c Sun Aug 25 15:49:34 2013
--- ./src/smtp/smtp_params.c Sat Mar 22 18:41:25 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_DSN_FILTER, DEF_SMTP_DSN_FILTER, &var_smtp_dsn_filter, 0, 0,
0,
};
static const CONFIG_TIME_TABLE smtp_time_table[] = {
diff -cr --new-file ../postfix-2.11.0/src/smtp/smtp_trouble.c ./src/smtp/smtp_trouble.c
*** ../postfix-2.11.0/src/smtp/smtp_trouble.c Mon Jun 11 11:25:02 2012
--- ./src/smtp/smtp_trouble.c Sat Mar 22 17:58:08 2014
***************
*** 190,195 ****
--- 190,196 ----
DSN_BUF *why = state->why;
RECIPIENT *rcpt;
int status;
+ int aggregate_status;
int soft_error = (STR(why->status)[0] == '4');
int soft_bounce_error = (STR(why->status)[0] == '5' && var_soft_bounce);
int nrcpt;
***************
*** 234,239 ****
--- 235,241 ----
GETTIMEOFDAY(&request->msg_stats.deliver_done);
(void) DSN_FROM_DSN_BUF(why);
+ aggregate_status = 0;
for (nrcpt = 0; nrcpt < SMTP_RCPT_LEFT(state); nrcpt++) {
rcpt = request->rcpt_list.info + nrcpt;
if (SMTP_RCPT_ISMARKED(rcpt))
***************
*** 245,254 ****
if (status == 0)
deliver_completed(state->src, rcpt->offset);
SMTP_RCPT_DROP(state, rcpt);
! state->status |= status;
}
if ((state->misc_flags & SMTP_MISC_FLAG_COMPLETE_SESSION) == 0
! && throttle_queue && (soft_error || soft_bounce_error)
&& request->hop_status == 0)
request->hop_status = DSN_COPY(&why->dsn);
}
--- 247,257 ----
if (status == 0)
deliver_completed(state->src, rcpt->offset);
SMTP_RCPT_DROP(state, rcpt);
! aggregate_status |= status;
}
+ state->status |= aggregate_status;
if ((state->misc_flags & SMTP_MISC_FLAG_COMPLETE_SESSION) == 0
! && throttle_queue && aggregate_status
&& request->hop_status == 0)
request->hop_status = DSN_COPY(&why->dsn);
}