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