summaryrefslogtreecommitdiffstats
path: root/src/local/bounce_workaround.c
blob: 7fe4aaa147751b16e8ed26985c4c55f9782d040a (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
150
151
152
153
154
155
156
157
158
159
/*++
/* NAME
/*	bounce_workaround 3
/* SUMMARY
/*	Send non-delivery notification with sender override
/* SYNOPSIS
/*	#include "local.h"
/*
/*	int	bounce_workaround(state)
/*	LOCAL_STATE state;
/* DESCRIPTION
/*	This module works around a limitation in the bounce daemon
/*	protocol, namely, the assumption that the envelope sender
/*	address in a queue file is the delivery status notification
/*	address for all recipients in that queue file. The assumption
/*	is not valid when the local(8) delivery agent overrides the
/*	envelope sender address by an owner- alias, for one or more
/*	recipients in the queue file.
/*
/*	Sender address override is a problem only when delivering
/*	to command or file, or when breaking a Delivered-To loop.
/*	The local(8) delivery agent saves normal recipients to a
/*	new queue file, together with the replacement envelope
/*	sender address; delivery then proceeds from that new queue
/*	file, and no workaround is needed.
/*
/*	The workaround sends one non-delivery notification for each
/*	failed delivery that has a replacement sender address.  The
/*	notifications are not aggregated, unlike notifications to
/*	non-replaced sender addresses. In practice, a local alias
/*	rarely has more than one file or command destination (if
/*	only because soft error handling is problematic).
/*
/*	Arguments:
/* .IP state
/*	The attributes that specify the message, recipient and more.
/*	Attributes describing alias, include or forward expansion.
/*	A table with the results from expanding aliases or lists.
/*	A table with delivered-to: addresses taken from the message.
/* 	The non-delivery status must be either 4.X.X or 5.X.X.
/* DIAGNOSTICS
/*	Fatal errors: out of memory. The result is non-zero when
/*	the operation should be tried again. Warnings: malformed
/*	address.
/* BUGS
/*	The proper fix is to record in the bounce logfile an error
/*	return address for each individual recipient. This would
/*	eliminate the need for VERP-specific bounce protocol code,
/*	and would move complexity from the bounce client side to
/*	the bounce server side where it more likely belongs.
/* 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 <strings.h>

/* Utility library. */

#include <msg.h>
#include <mymalloc.h>
#include <vstring.h>
#include <split_at.h>

/* Global library. */

#include <mail_params.h>
#include <strip_addr.h>
#include <stringops.h>
#include <bounce.h>
#include <defer.h>
#include <split_addr.h>
#include <canon_addr.h>

/* Application-specific. */

#include "local.h"

int     bounce_workaround(LOCAL_STATE state)
{
    const char *myname = "bounce_workaround";
    VSTRING *canon_owner = 0;
    int     rcpt_stat;

    /*
     * Look up the substitute sender address.
     */
    if (var_ownreq_special) {
	char   *stripped_recipient;
	char   *owner_alias;
	const char *owner_expansion;

#define FIND_OWNER(lhs, rhs, addr) { \
	lhs = concatenate("owner-", addr, (char *) 0); \
	(void) split_at_right(lhs, '@'); \
	rhs = maps_find(alias_maps, lhs, DICT_FLAG_NONE); \
    }

	FIND_OWNER(owner_alias, owner_expansion, state.msg_attr.rcpt.address);
	if (alias_maps->error == 0 && owner_expansion == 0
	    && (stripped_recipient = strip_addr(state.msg_attr.rcpt.address,
						(char **) 0,
						var_rcpt_delim)) != 0) {
	    myfree(owner_alias);
	    FIND_OWNER(owner_alias, owner_expansion, stripped_recipient);
	    myfree(stripped_recipient);
	}
	if (alias_maps->error == 0 && owner_expansion != 0) {
	    canon_owner = canon_addr_internal(vstring_alloc(10),
					      var_exp_own_alias ?
					      owner_expansion : owner_alias);
	    SET_OWNER_ATTR(state.msg_attr, STR(canon_owner), state.level);
	}
	myfree(owner_alias);
	if (alias_maps->error != 0) {
	    /* At this point, canon_owner == 0. */
	    dsb_simple(state.msg_attr.why, "4.3.0",
		       "alias database unavailable");
	    return (defer_append(BOUNCE_FLAGS(state.request),
				 BOUNCE_ATTR(state.msg_attr)));
	}
    }

    /*
     * Send a delivery status notification with a single recipient to the
     * substitute sender address, before completion of the delivery request.
     */
    if (canon_owner) {
	rcpt_stat =
	    (STR(state.msg_attr.why->status)[0] == '4' ?
	     defer_one : bounce_one)
	    (BOUNCE_FLAGS(state.request),
	     BOUNCE_ONE_ATTR(state.msg_attr));
	vstring_free(canon_owner);
    }

    /*
     * Send a regular delivery status notification, after completion of the
     * delivery request.
     */
    else {
	rcpt_stat =
	    (STR(state.msg_attr.why->status)[0] == '4' ?
	     defer_append : bounce_append)
	    (BOUNCE_FLAGS(state.request),
	     BOUNCE_ATTR(state.msg_attr));
    }
    return (rcpt_stat);
}