Sync usage with man page.
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / trivial-rewrite / transport.c
blob7051893deb4027082d79a09374e9473e850ab773
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* transport 3
6 /* SUMMARY
7 /* transport mapping
8 /* SYNOPSIS
9 /* #include "transport.h"
11 /* TRANSPORT_INFO *transport_pre_init(maps_name, maps)
12 /* const char *maps_name;
13 /* const char *maps;
15 /* void transport_post_init(info)
16 /* TRANSPORT_INFO *info;
18 /* int transport_lookup(info, address, rcpt_domain, channel, nexthop)
19 /* TRANSPORT_INFO *info;
20 /* const char *address;
21 /* const char *rcpt_domain;
22 /* VSTRING *channel;
23 /* VSTRING *nexthop;
25 /* void transport_free(info);
26 /* TRANSPORT_INFO * info;
27 /* DESCRIPTION
28 /* This module implements access to the table that maps transport
29 /* user@domain addresses to (channel, nexthop) tuples.
31 /* transport_pre_init() performs initializations that should be
32 /* done before the process enters the chroot jail, and
33 /* before calling transport_lookup().
35 /* transport_post_init() can be invoked after entering the chroot
36 /* jail, and must be called before before calling transport_lookup().
38 /* transport_lookup() finds the channel and nexthop for the given
39 /* domain, and returns 1 if something was found. Otherwise, 0
40 /* is returned.
41 /* DIAGNOSTICS
42 /* The global \fIdict_errno\fR is non-zero when the lookup
43 /* should be tried again.
44 /* SEE ALSO
45 /* maps(3), multi-dictionary search
46 /* strip_addr(3), strip extension from address
47 /* transport(5), format of transport map
48 /* CONFIGURATION PARAMETERS
49 /* transport_maps, names of maps to be searched.
50 /* LICENSE
51 /* .ad
52 /* .fi
53 /* The Secure Mailer license must be distributed with this software.
54 /* AUTHOR(S)
55 /* Wietse Venema
56 /* IBM T.J. Watson Research
57 /* P.O. Box 704
58 /* Yorktown Heights, NY 10598, USA
59 /*--*/
61 /* System library. */
63 #include <sys_defs.h>
64 #include <string.h>
66 /* Utility library. */
68 #include <msg.h>
69 #include <stringops.h>
70 #include <mymalloc.h>
71 #include <vstring.h>
72 #include <split_at.h>
73 #include <dict.h>
74 #include <events.h>
76 /* Global library. */
78 #include <strip_addr.h>
79 #include <mail_params.h>
80 #include <maps.h>
81 #include <match_parent_style.h>
82 #include <mail_proto.h>
84 /* Application-specific. */
86 #include "transport.h"
88 static int transport_match_parent_style;
90 #define STR(x) vstring_str(x)
92 static void transport_wildcard_init(TRANSPORT_INFO *);
94 /* transport_pre_init - pre-jail initialization */
96 TRANSPORT_INFO *transport_pre_init(const char *transport_maps_name,
97 const char *transport_maps)
99 TRANSPORT_INFO *tp;
101 tp = (TRANSPORT_INFO *) mymalloc(sizeof(*tp));
102 tp->transport_path = maps_create(transport_maps_name, transport_maps,
103 DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
104 | DICT_FLAG_NO_REGSUB);
105 tp->wildcard_channel = tp->wildcard_nexthop = 0;
106 tp->transport_errno = 0;
107 tp->expire = 0;
108 return (tp);
111 /* transport_post_init - post-jail initialization */
113 void transport_post_init(TRANSPORT_INFO *tp)
115 transport_match_parent_style = match_parent_style(VAR_TRANSPORT_MAPS);
116 transport_wildcard_init(tp);
119 /* transport_free - destroy transport info */
121 void transport_free(TRANSPORT_INFO *tp)
123 if (tp->transport_path)
124 maps_free(tp->transport_path);
125 if (tp->wildcard_channel)
126 vstring_free(tp->wildcard_channel);
127 if (tp->wildcard_nexthop)
128 vstring_free(tp->wildcard_nexthop);
129 myfree((char *) tp);
132 /* update_entry - update from transport table entry */
134 static void update_entry(const char *new_channel, const char *new_nexthop,
135 const char *rcpt_domain, VSTRING *channel,
136 VSTRING *nexthop)
140 * :[nexthop] means don't change the channel, and don't change the
141 * nexthop unless a non-default nexthop is specified. Thus, a right-hand
142 * side of ":" is the transport table equivalent of a NOOP.
144 if (*new_channel == 0) { /* :[nexthop] */
145 if (*new_nexthop != 0)
146 vstring_strcpy(nexthop, new_nexthop);
150 * transport[:[nexthop]] means change the channel, and reset the nexthop
151 * to the default unless a non-default nexthop is specified.
153 else {
154 vstring_strcpy(channel, new_channel);
155 if (*new_nexthop != 0)
156 vstring_strcpy(nexthop, new_nexthop);
157 else if (strcmp(STR(channel), MAIL_SERVICE_ERROR) != 0)
158 vstring_strcpy(nexthop, rcpt_domain);
159 else
160 vstring_strcpy(nexthop, "Address is undeliverable");
164 /* find_transport_entry - look up and parse transport table entry */
166 static int find_transport_entry(TRANSPORT_INFO *tp, const char *key,
167 const char *rcpt_domain, int flags,
168 VSTRING *channel, VSTRING *nexthop)
170 char *saved_value;
171 const char *host;
172 const char *value;
175 * Reset previous error history.
177 dict_errno = 0;
179 #define FOUND 1
180 #define NOTFOUND 0
183 * Look up an entry with extreme prejudice.
185 * XXX Should report lookup failure status to caller instead of aborting.
187 if ((value = maps_find(tp->transport_path, key, flags)) == 0)
188 return (NOTFOUND);
191 * It would be great if we could specify a recipient address in the
192 * lookup result. Unfortunately, we cannot simply run the result through
193 * a parser that recognizes "transport:user@domain" because the lookup
194 * result can have arbitrary content (especially in the case of the error
195 * mailer).
197 else {
198 saved_value = mystrdup(value);
199 host = split_at(saved_value, ':');
200 update_entry(saved_value, host ? host : "", rcpt_domain,
201 channel, nexthop);
202 myfree(saved_value);
203 return (FOUND);
207 /* transport_wildcard_init - (re) initialize wild-card lookup result */
209 static void transport_wildcard_init(TRANSPORT_INFO *tp)
211 VSTRING *channel = vstring_alloc(10);
212 VSTRING *nexthop = vstring_alloc(10);
215 * Both channel and nexthop may be zero-length strings. Therefore we must
216 * use something else to represent "wild-card does not exist". We use
217 * null VSTRING pointers, for historical reasons.
219 if (tp->wildcard_channel)
220 vstring_free(tp->wildcard_channel);
221 if (tp->wildcard_nexthop)
222 vstring_free(tp->wildcard_nexthop);
225 * Technically, the wildcard lookup pattern is redundant. A static map
226 * (keys always match, result is fixed string) could achieve the same:
228 * transport_maps = hash:/etc/postfix/transport static:xxx:yyy
230 * But the user interface of such an approach would be less intuitive. We
231 * tolerate the continued existence of wildcard lookup patterns because
232 * of human interface considerations.
234 #define WILDCARD "*"
235 #define FULL 0
236 #define PARTIAL DICT_FLAG_FIXED
238 if (find_transport_entry(tp, WILDCARD, "", FULL, channel, nexthop)) {
239 tp->transport_errno = 0;
240 tp->wildcard_channel = channel;
241 tp->wildcard_nexthop = nexthop;
242 if (msg_verbose)
243 msg_info("wildcard_{chan:hop}={%s:%s}",
244 vstring_str(channel), vstring_str(nexthop));
245 } else {
246 tp->transport_errno = dict_errno;
247 vstring_free(channel);
248 vstring_free(nexthop);
249 tp->wildcard_channel = 0;
250 tp->wildcard_nexthop = 0;
252 tp->expire = event_time() + 30; /* XXX make configurable */
255 /* transport_lookup - map a transport domain */
257 int transport_lookup(TRANSPORT_INFO *tp, const char *addr,
258 const char *rcpt_domain,
259 VSTRING *channel, VSTRING *nexthop)
261 char *stripped_addr;
262 char *ratsign = 0;
263 const char *name;
264 const char *next;
265 int found;
267 #define STREQ(x,y) (strcmp((x), (y)) == 0)
268 #define DISCARD_EXTENSION ((char **) 0)
271 * The null recipient is rewritten to the local mailer daemon address.
273 if (*addr == 0) {
274 msg_warn("transport_lookup: null address - skipping table lookup");
275 return (NOTFOUND);
279 * Look up the full address with the FULL flag to include regexp maps in
280 * the query.
282 if ((ratsign = strrchr(addr, '@')) == 0 || ratsign[1] == 0)
283 msg_panic("transport_lookup: bad address: \"%s\"", addr);
285 if (find_transport_entry(tp, addr, rcpt_domain, FULL, channel, nexthop))
286 return (FOUND);
287 if (dict_errno != 0)
288 return (NOTFOUND);
291 * If the full address did not match, and there is an address extension,
292 * look up the stripped address with the PARTIAL flag to avoid matching
293 * partial lookup keys with regular expressions.
295 if ((stripped_addr = strip_addr(addr, DISCARD_EXTENSION,
296 *var_rcpt_delim)) != 0) {
297 found = find_transport_entry(tp, stripped_addr, rcpt_domain, PARTIAL,
298 channel, nexthop);
300 myfree(stripped_addr);
301 if (found)
302 return (FOUND);
303 if (dict_errno != 0)
304 return (NOTFOUND);
308 * If the full and stripped address lookup fails, try domain name lookup.
310 * Keep stripping domain components until nothing is left or until a
311 * matching entry is found.
313 * After checking the full domain name, check for .upper.domain, to
314 * distinguish between the parent domain and it's decendants, a la
315 * sendmail and tcp wrappers.
317 * Before changing the DB lookup result, make a copy first, in order to
318 * avoid DB cache corruption.
320 * Specify that the lookup key is partial, to avoid matching partial keys
321 * with regular expressions.
323 for (name = ratsign + 1; *name != 0; name = next) {
324 if (find_transport_entry(tp, name, rcpt_domain, PARTIAL, channel, nexthop))
325 return (FOUND);
326 if (dict_errno != 0)
327 return (NOTFOUND);
328 if ((next = strchr(name + 1, '.')) == 0)
329 break;
330 if (transport_match_parent_style == MATCH_FLAG_PARENT)
331 next++;
335 * Fall back to the wild-card entry.
337 if (tp->transport_errno || event_time() > tp->expire)
338 transport_wildcard_init(tp);
339 if (tp->transport_errno) {
340 dict_errno = tp->transport_errno;
341 return (NOTFOUND);
342 } else if (tp->wildcard_channel) {
343 update_entry(STR(tp->wildcard_channel), STR(tp->wildcard_nexthop),
344 rcpt_domain, channel, nexthop);
345 return (FOUND);
349 * We really did not find it.
351 return (NOTFOUND);