summaryrefslogtreecommitdiffstats
path: root/src/smtpd/smtpd_dsn_fix.c
blob: d436967282bb19ebb98a3b9d5884e9110fc10712 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/*++
/* NAME
/*	smtpd_dsn_fix 3
/* SUMMARY
/*	fix DSN status
/* SYNOPSIS
/*	#include <smtpd_dsn_fix.h>
/*
/*	const char *smtpd_dsn_fix(status, reply_class)
/*	const char *status;
/*	const char *reply_class;
/* DESCRIPTION
/*	smtpd_dsn_fix() transforms DSN status codes according to the
/*	status information that is actually being reported. The
/*	following transformations are implemented:
/* .IP \(bu
/*	Transform a recipient address DSN into a sender address DSN
/*	when reporting sender address status information, and vice
/*	versa. This transformation may be needed because some Postfix
/*	access control features don't know whether the address being
/*	rejected is a sender or recipient. Examples are smtpd access
/*	tables, rbl reply templates, and the error mailer.
/* .IP \(bu
/*	Transform a sender or recipient address DSN into a non-address
/*	DSN when reporting non-address status information. For
/*	example, if something rejects HELO with DSN status 4.1.1
/*	(unknown recipient address), then we send the more neutral
/*	4.0.0 DSN instead. This transformation is needed when the
/*	same smtpd access map entry or rbl reply template is used
/*	for both address and non-address information.
/* .PP
/*	A non-address DSN is not transformed
/*	when reporting sender or recipient address status information,
/*	as there are many legitimate instances of such usage.
/*
/*	It is left up to the caller to update the initial DSN digit
/*	appropriately; in Postfix this is done as late as possible,
/*	because hard rejects may be changed into soft rejects for
/*	all kinds of reasons.
/*
/*	Arguments:
/* .IP status
/*	A DSN status as per RFC 3463.
/* .IP reply_class
/*	SMTPD_NAME_SENDER, SMTPD_NAME_RECIPIENT or some other
/*	null-terminated string.
/* 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 library. */

#include <sys_defs.h>
#include <ctype.h>
#include <string.h>

/* Utility library. */

#include <msg.h>

/* Global library. */

/* Application-specific. */

#include <smtpd_dsn_fix.h>

struct dsn_map {
    const char *micro_code;		/* Final digits in mailbox D.S.N. */
    const char *sender_dsn;		/* Replacement sender D.S.N. */
    const char *rcpt_dsn;		/* Replacement recipient D.S.N. */
};

static struct dsn_map dsn_map[] = {
    /* - Sender - Recipient */
    "1", SND_DSN, "4.1.1",		/* 4.1.1: Bad dest mbox addr */
    "2", "4.1.8", "4.1.2",		/* 4.1.2: Bad dest system addr */
    "3", "4.1.7", "4.1.3",		/* 4.1.3: Bad dest mbox addr syntax */
    "4", SND_DSN, "4.1.4",		/* 4.1.4: Dest mbox addr ambiguous */
    "5", "4.1.0", "4.1.5",		/* 4.1.5: Dest mbox addr valid */
    "6", SND_DSN, "4.1.6",		/* 4.1.6: Mailbox has moved */
    "7", "4.1.7", "4.1.3",		/* 4.1.7: Bad sender mbox addr syntax */
    "8", "4.1.8", "4.1.2",		/* 4.1.8: Bad sender system addr */
    0, "4.1.0", "4.1.0",		/* Default mapping */
};

/* smtpd_dsn_fix - fix DSN status */

const char *smtpd_dsn_fix(const char *status, const char *reply_class)
{
    struct dsn_map *dp;
    const char *result = status;

    /*
     * Update an address-specific DSN according to what is being rejected.
     */
    if (ISDIGIT(status[0]) && strncmp(status + 1, ".1.", 3) == 0) {

	/*
	 * Fix recipient address DSN while rejecting a sender address. Don't
	 * let future recipient-specific DSN codes slip past us.
	 */
	if (strcmp(reply_class, SMTPD_NAME_SENDER) == 0) {
	    for (dp = dsn_map; dp->micro_code != 0; dp++)
		if (strcmp(status + 4, dp->micro_code) == 0)
		    break;
	    result = dp->sender_dsn;
	}

	/*
	 * Fix sender address DSN while rejecting a recipient address. Don't
	 * let future sender-specific DSN codes slip past us.
	 */
	else if (strcmp(reply_class, SMTPD_NAME_RECIPIENT) == 0) {
	    for (dp = dsn_map; dp->micro_code != 0; dp++)
		if (strcmp(status + 4, dp->micro_code) == 0)
		    break;
	    result = dp->rcpt_dsn;
	}

	/*
	 * Fix address-specific DSN while rejecting a non-address.
	 */
	else {
	    result = "4.0.0";
	}

	/*
	 * Give them a clue of what is going on.
	 */
	if (strcmp(status + 2, result + 2) != 0)
	    msg_info("mapping DSN status %s into %s status %c%s",
		     status, reply_class, status[0], result + 1);
	return (result);
    }

    /*
     * Don't update a non-address DSN. There are many legitimate uses for
     * these while rejecting address or non-address information.
     */
    else {
	return (status);
    }
}