1/*++
2/* NAME
3/*	smtpd_dsn_fix 3
4/* SUMMARY
5/*	fix DSN status
6/* SYNOPSIS
7/*	#include <smtpd_dsn_fix.h>
8/*
9/*	const char *smtpd_dsn_fix(status, reply_class)
10/*	const char *status;
11/*	const char *reply_class;
12/* DESCRIPTION
13/*	smtpd_dsn_fix() transforms DSN status codes according to the
14/*	status information that is actually being reported. The
15/*	following transformations are implemented:
16/* .IP \(bu
17/*	Transform a recipient address DSN into a sender address DSN
18/*	when reporting sender address status information, and vice
19/*	versa. This transformation may be needed because some Postfix
20/*	access control features don't know whether the address being
21/*	rejected is a sender or recipient. Examples are smtpd access
22/*	tables, rbl reply templates, and the error mailer.
23/* .IP \(bu
24/*	Transform a sender or recipient address DSN into a non-address
25/*	DSN when reporting non-address status information. For
26/*	example, if something rejects HELO with DSN status 4.1.1
27/*	(unknown recipient address), then we send the more neutral
28/*	4.0.0 DSN instead. This transformation is needed when the
29/*	same smtpd access map entry or rbl reply template is used
30/*	for both address and non-address information.
31/* .PP
32/*	A non-address DSN is not transformed
33/*	when reporting sender or recipient address status information,
34/*	as there are many legitimate instances of such usage.
35/*
36/*	It is left up to the caller to update the initial DSN digit
37/*	appropriately; in Postfix this is done as late as possible,
38/*	because hard rejects may be changed into soft rejects for
39/*	all kinds of reasons.
40/*
41/*	Arguments:
42/* .IP status
43/*	A DSN status as per RFC 3463.
44/* .IP reply_class
45/*	SMTPD_NAME_SENDER, SMTPD_NAME_RECIPIENT or some other
46/*	null-terminated string.
47/* LICENSE
48/* .ad
49/* .fi
50/*	The Secure Mailer license must be distributed with this software.
51/* AUTHOR(S)
52/*	Wietse Venema
53/*	IBM T.J. Watson Research
54/*	P.O. Box 704
55/*	Yorktown Heights, NY 10598, USA
56/*--*/
57/* System library. */
58
59#include <sys_defs.h>
60#include <ctype.h>
61#include <string.h>
62
63/* Utility library. */
64
65#include <msg.h>
66
67/* Global library. */
68
69/* Application-specific. */
70
71#include <smtpd_dsn_fix.h>
72
73struct dsn_map {
74    const char *micro_code;		/* Final digits in mailbox D.S.N. */
75    const char *sender_dsn;		/* Replacement sender D.S.N. */
76    const char *rcpt_dsn;		/* Replacement recipient D.S.N. */
77};
78
79static struct dsn_map dsn_map[] = {
80    /* - Sender - Recipient */
81    "1", SND_DSN, "4.1.1",		/* 4.1.1: Bad dest mbox addr */
82    "2", "4.1.8", "4.1.2",		/* 4.1.2: Bad dest system addr */
83    "3", "4.1.7", "4.1.3",		/* 4.1.3: Bad dest mbox addr syntax */
84    "4", SND_DSN, "4.1.4",		/* 4.1.4: Dest mbox addr ambiguous */
85    "5", "4.1.0", "4.1.5",		/* 4.1.5: Dest mbox addr valid */
86    "6", SND_DSN, "4.1.6",		/* 4.1.6: Mailbox has moved */
87    "7", "4.1.7", "4.1.3",		/* 4.1.7: Bad sender mbox addr syntax */
88    "8", "4.1.8", "4.1.2",		/* 4.1.8: Bad sender system addr */
89    0, "4.1.0", "4.1.0",		/* Default mapping */
90};
91
92/* smtpd_dsn_fix - fix DSN status */
93
94const char *smtpd_dsn_fix(const char *status, const char *reply_class)
95{
96    struct dsn_map *dp;
97    const char *result = status;
98
99    /*
100     * Update an address-specific DSN according to what is being rejected.
101     */
102    if (ISDIGIT(status[0]) && strncmp(status + 1, ".1.", 3) == 0) {
103
104	/*
105	 * Fix recipient address DSN while rejecting a sender address. Don't
106	 * let future recipient-specific DSN codes slip past us.
107	 */
108	if (strcmp(reply_class, SMTPD_NAME_SENDER) == 0) {
109	    for (dp = dsn_map; dp->micro_code != 0; dp++)
110		if (strcmp(status + 4, dp->micro_code) == 0)
111		    break;
112	    result = dp->sender_dsn;
113	}
114
115	/*
116	 * Fix sender address DSN while rejecting a recipient address. Don't
117	 * let future sender-specific DSN codes slip past us.
118	 */
119	else if (strcmp(reply_class, SMTPD_NAME_RECIPIENT) == 0) {
120	    for (dp = dsn_map; dp->micro_code != 0; dp++)
121		if (strcmp(status + 4, dp->micro_code) == 0)
122		    break;
123	    result = dp->rcpt_dsn;
124	}
125
126	/*
127	 * Fix address-specific DSN while rejecting a non-address.
128	 */
129	else {
130	    result = "4.0.0";
131	}
132
133	/*
134	 * Give them a clue of what is going on.
135	 */
136	if (strcmp(status + 2, result + 2) != 0)
137	    msg_info("mapping DSN status %s into %s status %c%s",
138		     status, reply_class, status[0], result + 1);
139	return (result);
140    }
141
142    /*
143     * Don't update a non-address DSN. There are many legitimate uses for
144     * these while rejecting address or non-address information.
145     */
146    else {
147	return (status);
148    }
149}
150