1/*	$NetBSD: unknown.c,v 1.8 2022/10/08 16:12:46 christos Exp $	*/
2
3/*++
4/* NAME
5/*	unknown 3
6/* SUMMARY
7/*	delivery of unknown recipients
8/* SYNOPSIS
9/*	#include "local.h"
10/*
11/*	int	deliver_unknown(state, usr_attr)
12/*	LOCAL_STATE state;
13/*	USER_ATTR usr_attr;
14/* DESCRIPTION
15/*	deliver_unknown() delivers a message for unknown recipients.
16/* .IP \(bu
17/*	If an alternative message transport is specified via the
18/*	fallback_transport parameter, delivery is delegated to the
19/*	named transport.
20/* .IP \(bu
21/*	If an alternative address is specified via the luser_relay
22/*	configuration parameter, mail is forwarded to that address.
23/* .IP \(bu
24/*	Otherwise the recipient is bounced.
25/* .PP
26/*	The luser_relay parameter is subjected to $name expansion of
27/*	the standard message attributes: $user, $home, $shell, $domain,
28/*	$recipient, $mailbox, $extension, $recipient_delimiter, not
29/*	all of which actually make sense.
30/*
31/*	Arguments:
32/* .IP state
33/*	Message delivery attributes (sender, recipient etc.).
34/*	Attributes describing alias, include or forward expansion.
35/*	A table with the results from expanding aliases or lists.
36/*	A table with delivered-to: addresses taken from the message.
37/* .IP usr_attr
38/*	Attributes describing user rights and environment.
39/* DIAGNOSTICS
40/*	The result status is non-zero when delivery should be tried again.
41/* LICENSE
42/* .ad
43/* .fi
44/*	The Secure Mailer license must be distributed with this software.
45/* AUTHOR(S)
46/*	Wietse Venema
47/*	IBM T.J. Watson Research
48/*	P.O. Box 704
49/*	Yorktown Heights, NY 10598, USA
50/*
51/*	Wietse Venema
52/*	Google, Inc.
53/*	111 8th Avenue
54/*	New York, NY 10011, USA
55/*--*/
56
57/* System library. */
58
59#include <sys_defs.h>
60#include <string.h>
61
62#ifdef STRCASECMP_IN_STRINGS_H
63#include <strings.h>
64#endif
65
66/* Utility library. */
67
68#include <msg.h>
69#include <stringops.h>
70#include <mymalloc.h>
71#include <vstring.h>
72
73/* Global library. */
74
75#include <been_here.h>
76#include <mail_params.h>
77#include <mail_proto.h>
78#include <bounce.h>
79#include <mail_addr.h>
80#include <sent.h>
81#include <deliver_pass.h>
82#include <defer.h>
83#include <canon_addr.h>
84
85/* Application-specific. */
86
87#include "local.h"
88
89#define STREQ(x,y) (strcasecmp((x),(y)) == 0)
90
91/* deliver_unknown - delivery for unknown recipients */
92
93int     deliver_unknown(LOCAL_STATE state, USER_ATTR usr_attr)
94{
95    const char *myname = "deliver_unknown";
96    int     status;
97    VSTRING *expand_luser;
98    VSTRING *canon_luser;
99    static MAPS *transp_maps;
100    const char *map_transport;
101
102    /*
103     * Make verbose logging easier to understand.
104     */
105    state.level++;
106    if (msg_verbose)
107	MSG_LOG_STATE(myname, state);
108
109    /*
110     * DUPLICATE/LOOP ELIMINATION
111     *
112     * Don't deliver the same user twice.
113     */
114    if (been_here(state.dup_filter, "%s %s", myname, state.msg_attr.local))
115	return (0);
116
117    /*
118     * The fall-back transport specifies a delivery mechanism that handles
119     * users not found in the aliases or UNIX passwd databases.
120     */
121    if (*var_fbck_transp_maps && transp_maps == 0)
122	transp_maps = maps_create(VAR_FBCK_TRANSP_MAPS, var_fbck_transp_maps,
123				  DICT_FLAG_LOCK | DICT_FLAG_NO_REGSUB
124				  | DICT_FLAG_UTF8_REQUEST);
125    /* The -1 is a hint for the down-stream deliver_completed() function. */
126    if (transp_maps
127	&& (map_transport = maps_find(transp_maps, state.msg_attr.user,
128				      DICT_FLAG_NONE)) != 0) {
129	state.msg_attr.rcpt.offset = -1L;
130	return (deliver_pass(MAIL_CLASS_PRIVATE, map_transport,
131			     state.request, &state.msg_attr.rcpt));
132    } else if (transp_maps && transp_maps->error != 0) {
133	/* Details in the logfile. */
134	dsb_simple(state.msg_attr.why, "4.3.0", "table lookup failure");
135	return (defer_append(BOUNCE_FLAGS(state.request),
136			     BOUNCE_ATTR(state.msg_attr)));
137    }
138    if (*var_fallback_transport) {
139	state.msg_attr.rcpt.offset = -1L;
140	return (deliver_pass(MAIL_CLASS_PRIVATE, var_fallback_transport,
141			     state.request, &state.msg_attr.rcpt));
142    }
143
144    /*
145     * Subject the luser_relay address to $name expansion, disable
146     * propagation of unmatched address extension, and re-inject the address
147     * into the delivery machinery. Do not give special treatment to "|stuff"
148     * or /stuff.
149     */
150    if (*var_luser_relay) {
151	state.msg_attr.unmatched = 0;
152	expand_luser = vstring_alloc(100);
153	canon_luser = vstring_alloc(100);
154	local_expand(expand_luser, var_luser_relay, &state, &usr_attr, (void *) 0);
155	/* In case luser_relay specifies a domain-less address. */
156	canon_addr_external(canon_luser, vstring_str(expand_luser));
157	/* Assumes that the address resolver won't change the address. */
158	if (STREQ(vstring_str(canon_luser), state.msg_attr.rcpt.address)) {
159	    dsb_simple(state.msg_attr.why, "5.1.1",
160		       "unknown user: \"%s\"", state.msg_attr.user);
161	    status = bounce_append(BOUNCE_FLAGS(state.request),
162				   BOUNCE_ATTR(state.msg_attr));
163	} else {
164	    status = deliver_resolve_addr(state, usr_attr, STR(expand_luser));
165	}
166	vstring_free(canon_luser);
167	vstring_free(expand_luser);
168	return (status);
169    }
170
171    /*
172     * If no alias was found for a required reserved name, toss the message
173     * into the bit bucket, and issue a warning instead.
174     */
175    if (STREQ(state.msg_attr.user, MAIL_ADDR_MAIL_DAEMON)
176	|| STREQ(state.msg_attr.user, MAIL_ADDR_POSTMASTER)) {
177	msg_warn("required alias not found: %s", state.msg_attr.user);
178	dsb_simple(state.msg_attr.why, "2.0.0", "discarded");
179	return (sent(BOUNCE_FLAGS(state.request), SENT_ATTR(state.msg_attr)));
180    }
181
182    /*
183     * Bounce the message when no luser relay is specified.
184     */
185    dsb_simple(state.msg_attr.why, "5.1.1",
186	       "unknown user: \"%s\"", state.msg_attr.user);
187    return (bounce_append(BOUNCE_FLAGS(state.request),
188			  BOUNCE_ATTR(state.msg_attr)));
189}
190