1/*++
2/* NAME
3/*	mail_addr_find 3
4/* SUMMARY
5/*	generic address-based lookup
6/* SYNOPSIS
7/*	#include <mail_addr_find.h>
8/*
9/*	const char *mail_addr_find(maps, address, extension)
10/*	MAPS	*maps;
11/*	const char *address;
12/*	char	**extension;
13/* DESCRIPTION
14/*	mail_addr_find() searches the specified maps for an entry with as
15/*	key the specified address, and derivations from that address.
16/*	It is up to the caller to specify its case sensitivity
17/*	preferences when it opens the maps.
18/*	The result is overwritten upon each call.
19/*
20/*	An address that is in the form \fIuser\fR matches itself.
21/*
22/*	Given an address of the form \fIuser@domain\fR, the following
23/*	lookups are done in the given order until one returns a result:
24/* .IP user@domain
25/*	Look up the entire address.
26/* .IP user
27/*	Look up \fIuser\fR when \fIdomain\fR is equal to $myorigin,
28/*	when \fIdomain\fR matches $mydestination, or when it matches
29/*	$inet_interfaces or $proxy_interfaces.
30/* .IP @domain
31/*	Look for an entry that matches the domain specified in \fIaddress\fR.
32/* .PP
33/*	With address extension enabled, the table lookup order is:
34/*	\fIuser+extension\fR@\fIdomain\fR, \fIuser\fR@\fIdomain\fR,
35/*	\fIuser+extension\fR, \fIuser\fR, and @\fIdomain\fR.
36/* .PP
37/*	Arguments:
38/* .IP maps
39/*	Dictionary search path (see maps(3)).
40/* .IP address
41/*	The address to be looked up.
42/* .IP extension
43/*	A null pointer, or the address of a pointer that is set to
44/*	the address of a dynamic memory copy of the address extension
45/*	that had to be chopped off in order to match the lookup tables.
46/*	The copy includes the recipient address delimiter.
47/*	The caller is expected to pass the copy to myfree().
48/* DIAGNOSTICS
49/*	The maps->error value is non-zero when the lookup
50/*	should be tried again.
51/* SEE ALSO
52/*	maps(3), multi-dictionary search
53/*	resolve_local(3), recognize local system
54/* LICENSE
55/* .ad
56/* .fi
57/*	The Secure Mailer license must be distributed with this 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 <string.h>
69
70#ifdef STRCASECMP_IN_STRINGS_H
71#include <strings.h>
72#endif
73
74/* Utility library. */
75
76#include <msg.h>
77#include <dict.h>
78#include <stringops.h>
79#include <mymalloc.h>
80#include <vstring.h>
81
82/* Global library. */
83
84#include <mail_params.h>
85#include <strip_addr.h>
86#include <mail_addr_find.h>
87#include <resolve_local.h>
88
89/* Application-specific. */
90
91#define STR	vstring_str
92
93/* mail_addr_find - map a canonical address */
94
95const char *mail_addr_find(MAPS *path, const char *address, char **extp)
96{
97    const char *myname = "mail_addr_find";
98    const char *result;
99    char   *ratsign = 0;
100    char   *full_key;
101    char   *bare_key;
102    char   *saved_ext;
103    int     rc = 0;
104
105    /*
106     * Initialize.
107     */
108    full_key = mystrdup(address);
109    if (*var_rcpt_delim == 0) {
110	bare_key = saved_ext = 0;
111    } else {
112	bare_key = strip_addr(full_key, &saved_ext, *var_rcpt_delim);
113    }
114
115    /*
116     * Try user+foo@domain and user@domain.
117     *
118     * Specify what keys are partial or full, to avoid matching partial
119     * addresses with regular expressions.
120     */
121#define FULL	0
122#define PARTIAL	DICT_FLAG_FIXED
123
124    if ((result = maps_find(path, full_key, FULL)) == 0 && path->error == 0
125      && bare_key != 0 && (result = maps_find(path, bare_key, PARTIAL)) != 0
126	&& extp != 0) {
127	*extp = saved_ext;
128	saved_ext = 0;
129    }
130
131    /*
132     * Try user+foo@$myorigin, user+foo@$mydestination or
133     * user+foo@[${proxy,inet}_interfaces]. Then try with +foo stripped off.
134     */
135    if (result == 0 && path->error == 0
136	&& (ratsign = strrchr(full_key, '@')) != 0
137	&& (strcasecmp(ratsign + 1, var_myorigin) == 0
138	    || (rc = resolve_local(ratsign + 1)) > 0)) {
139	*ratsign = 0;
140	result = maps_find(path, full_key, PARTIAL);
141	if (result == 0 && path->error == 0 && bare_key != 0) {
142	    if ((ratsign = strrchr(bare_key, '@')) == 0)
143		msg_panic("%s: bare key botch", myname);
144	    *ratsign = 0;
145	    if ((result = maps_find(path, bare_key, PARTIAL)) != 0 && extp != 0) {
146		*extp = saved_ext;
147		saved_ext = 0;
148	    }
149	}
150	*ratsign = '@';
151    } else if (rc < 0)
152	path->error = rc;
153
154    /*
155     * Try @domain.
156     */
157    if (result == 0 && path->error == 0 && ratsign)
158	result = maps_find(path, ratsign, PARTIAL);
159
160    /*
161     * Clean up.
162     */
163    if (msg_verbose)
164	msg_info("%s: %s -> %s", myname, address,
165		 result ? result :
166		 path->error ? "(try again)" :
167		 "(not found)");
168    myfree(full_key);
169    if (bare_key)
170	myfree(bare_key);
171    if (saved_ext)
172	myfree(saved_ext);
173
174    return (result);
175}
176
177#ifdef TEST
178
179 /*
180  * Proof-of-concept test program. Read an address from stdin, and spit out
181  * the lookup result.
182  */
183#include <vstream.h>
184#include <vstring_vstream.h>
185#include <mail_conf.h>
186
187int     main(int argc, char **argv)
188{
189    VSTRING *buffer = vstring_alloc(100);
190    MAPS   *path;
191    const char *result;
192    char   *extent;
193
194    /*
195     * Parse JCL.
196     */
197    if (argc != 2)
198	msg_fatal("usage: %s database", argv[0]);
199    msg_verbose = 1;
200
201    /*
202     * Initialize.
203     */
204    mail_conf_read();
205    path = maps_create(argv[0], argv[1], DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);
206    while (vstring_fgets_nonl(buffer, VSTREAM_IN)) {
207	extent = 0;
208	result = mail_addr_find(path, STR(buffer), &extent);
209	vstream_printf("%s -> %s (%s)\n", STR(buffer), result ? result :
210		       path->error ? "(try again)" :
211		       "(not found)", extent ? extent : "null extension");
212	vstream_fflush(VSTREAM_OUT);
213	if (extent)
214	    myfree(extent);
215    }
216    vstring_free(buffer);
217
218    maps_free(path);
219    return (0);
220}
221
222#endif
223