1 /* $NetBSD: bounce_workaround.c,v 1.2 2017/02/14 01:16:45 christos Exp $ */
2
3 /*++
4 /* NAME
5 /* bounce_workaround 3
6 /* SUMMARY
7 /* Send non-delivery notification with sender override
8 /* SYNOPSIS
9 /* #include "local.h"
10 /*
11 /* int bounce_workaround(state)
12 /* LOCAL_STATE state;
13 /* DESCRIPTION
14 /* This module works around a limitation in the bounce daemon
15 /* protocol, namely, the assumption that the envelope sender
16 /* address in a queue file is the delivery status notification
17 /* address for all recipients in that queue file. The assumption
18 /* is not valid when the local(8) delivery agent overrides the
19 /* envelope sender address by an owner- alias, for one or more
20 /* recipients in the queue file.
21 /*
22 /* Sender address override is a problem only when delivering
23 /* to command or file, or when breaking a Delivered-To loop.
24 /* The local(8) delivery agent saves normal recipients to a
25 /* new queue file, together with the replacement envelope
26 /* sender address; delivery then proceeds from that new queue
27 /* file, and no workaround is needed.
28 /*
29 /* The workaround sends one non-delivery notification for each
30 /* failed delivery that has a replacement sender address. The
31 /* notifications are not aggregated, unlike notifications to
32 /* non-replaced sender addresses. In practice, a local alias
33 /* rarely has more than one file or command destination (if
34 /* only because soft error handling is problematic).
35 /*
36 /* Arguments:
37 /* .IP state
38 /* The attributes that specify the message, recipient and more.
39 /* Attributes describing alias, include or forward expansion.
40 /* A table with the results from expanding aliases or lists.
41 /* A table with delivered-to: addresses taken from the message.
42 /* The non-delivery status must be either 4.X.X or 5.X.X.
43 /* DIAGNOSTICS
44 /* Fatal errors: out of memory. The result is non-zero when
45 /* the operation should be tried again. Warnings: malformed
46 /* address.
47 /* BUGS
48 /* The proper fix is to record in the bounce logfile an error
49 /* return address for each individual recipient. This would
50 /* eliminate the need for VERP-specific bounce protocol code,
51 /* and would move complexity from the bounce client side to
52 /* the bounce server side where it more likely belongs.
53 /* LICENSE
54 /* .ad
55 /* .fi
56 /* The Secure Mailer license must be distributed with this
57 /* software.
58 /* AUTHOR(S)
59 /* Wietse Venema
60 /* IBM T.J. Watson Research
61 /* P.O. Box 704
62 /* Yorktown Heights, NY 10598, USA
63 /*--*/
64
65 /* System library. */
66
67 #include <sys_defs.h>
68 #include <strings.h>
69
70 /* Utility library. */
71
72 #include <msg.h>
73 #include <mymalloc.h>
74 #include <vstring.h>
75 #include <split_at.h>
76
77 /* Global library. */
78
79 #include <mail_params.h>
80 #include <strip_addr.h>
81 #include <stringops.h>
82 #include <bounce.h>
83 #include <defer.h>
84 #include <split_addr.h>
85 #include <canon_addr.h>
86
87 /* Application-specific. */
88
89 #include "local.h"
90
bounce_workaround(LOCAL_STATE state)91 int bounce_workaround(LOCAL_STATE state)
92 {
93 const char *myname = "bounce_workaround";
94 VSTRING *canon_owner = 0;
95 int rcpt_stat;
96
97 /*
98 * Look up the substitute sender address.
99 */
100 if (var_ownreq_special) {
101 char *stripped_recipient;
102 char *owner_alias;
103 const char *owner_expansion;
104
105 #define FIND_OWNER(lhs, rhs, addr) { \
106 lhs = concatenate("owner-", addr, (char *) 0); \
107 (void) split_at_right(lhs, '@'); \
108 rhs = maps_find(alias_maps, lhs, DICT_FLAG_NONE); \
109 }
110
111 FIND_OWNER(owner_alias, owner_expansion, state.msg_attr.rcpt.address);
112 if (alias_maps->error == 0 && owner_expansion == 0
113 && (stripped_recipient = strip_addr(state.msg_attr.rcpt.address,
114 (char **) 0,
115 var_rcpt_delim)) != 0) {
116 myfree(owner_alias);
117 FIND_OWNER(owner_alias, owner_expansion, stripped_recipient);
118 myfree(stripped_recipient);
119 }
120 if (alias_maps->error == 0 && owner_expansion != 0) {
121 canon_owner = canon_addr_internal(vstring_alloc(10),
122 var_exp_own_alias ?
123 owner_expansion : owner_alias);
124 SET_OWNER_ATTR(state.msg_attr, STR(canon_owner), state.level);
125 }
126 myfree(owner_alias);
127 if (alias_maps->error != 0) {
128 /* At this point, canon_owner == 0. */
129 dsb_simple(state.msg_attr.why, "4.3.0",
130 "alias database unavailable");
131 return (defer_append(BOUNCE_FLAGS(state.request),
132 BOUNCE_ATTR(state.msg_attr)));
133 }
134 }
135
136 /*
137 * Send a delivery status notification with a single recipient to the
138 * substitute sender address, before completion of the delivery request.
139 */
140 if (canon_owner) {
141 rcpt_stat =
142 (STR(state.msg_attr.why->status)[0] == '4' ?
143 defer_one : bounce_one)
144 (BOUNCE_FLAGS(state.request),
145 BOUNCE_ONE_ATTR(state.msg_attr));
146 vstring_free(canon_owner);
147 }
148
149 /*
150 * Send a regular delivery status notification, after completion of the
151 * delivery request.
152 */
153 else {
154 rcpt_stat =
155 (STR(state.msg_attr.why->status)[0] == '4' ?
156 defer_append : bounce_append)
157 (BOUNCE_FLAGS(state.request),
158 BOUNCE_ATTR(state.msg_attr));
159 }
160 return (rcpt_stat);
161 }
162