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
91int     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