Postfix3.3.1
transport.c
[詳解]
1 /*++
2 /* NAME
3 /* transport 3
4 /* SUMMARY
5 /* transport mapping
6 /* SYNOPSIS
7 /* #include "transport.h"
8 /*
9 /* TRANSPORT_INFO *transport_pre_init(maps_name, maps)
10 /* const char *maps_name;
11 /* const char *maps;
12 /*
13 /* void transport_post_init(info)
14 /* TRANSPORT_INFO *info;
15 /*
16 /* int transport_lookup(info, address, rcpt_domain, channel, nexthop)
17 /* TRANSPORT_INFO *info;
18 /* const char *address;
19 /* const char *rcpt_domain;
20 /* VSTRING *channel;
21 /* VSTRING *nexthop;
22 /*
23 /* void transport_free(info);
24 /* TRANSPORT_INFO * info;
25 /* DESCRIPTION
26 /* This module implements access to the table that maps transport
27 /* user@domain addresses to (channel, nexthop) tuples.
28 /*
29 /* transport_pre_init() performs initializations that should be
30 /* done before the process enters the chroot jail, and
31 /* before calling transport_lookup().
32 /*
33 /* transport_post_init() can be invoked after entering the chroot
34 /* jail, and must be called before before calling transport_lookup().
35 /*
36 /* transport_lookup() finds the channel and nexthop for the given
37 /* domain, and returns 1 if something was found. Otherwise, 0
38 /* is returned.
39 /* DIAGNOSTICS
40 /* info->transport_path->error is non-zero when the lookup
41 /* should be tried again.
42 /* SEE ALSO
43 /* maps(3), multi-dictionary search
44 /* strip_addr(3), strip extension from address
45 /* transport(5), format of transport map
46 /* CONFIGURATION PARAMETERS
47 /* transport_maps, names of maps to be searched.
48 /* LICENSE
49 /* .ad
50 /* .fi
51 /* The Secure Mailer license must be distributed with this software.
52 /* AUTHOR(S)
53 /* Wietse Venema
54 /* IBM T.J. Watson Research
55 /* P.O. Box 704
56 /* Yorktown Heights, NY 10598, USA
57 /*
58 /* Wietse Venema
59 /* Google, Inc.
60 /* 111 8th Avenue
61 /* New York, NY 10011, USA
62 /*--*/
63 
64 /* System library. */
65 
66 #include <sys_defs.h>
67 #include <string.h>
68 
69 /* Utility library. */
70 
71 #include <msg.h>
72 #include <stringops.h>
73 #include <mymalloc.h>
74 #include <vstring.h>
75 #include <split_at.h>
76 #include <dict.h>
77 #include <events.h>
78 
79 /* Global library. */
80 
81 #include <strip_addr.h>
82 #include <mail_params.h>
83 #include <mail_addr_find.h>
84 #include <match_parent_style.h>
85 #include <mail_proto.h>
86 
87 /* Application-specific. */
88 
89 #include "transport.h"
90 
91 static int transport_match_parent_style;
92 
93 #define STR(x) vstring_str(x)
94 
95 static void transport_wildcard_init(TRANSPORT_INFO *);
96 
97 /* transport_pre_init - pre-jail initialization */
98 
99 TRANSPORT_INFO *transport_pre_init(const char *transport_maps_name,
100  const char *transport_maps)
101 {
102  TRANSPORT_INFO *tp;
103 
104  tp = (TRANSPORT_INFO *) mymalloc(sizeof(*tp));
105  tp->transport_path = maps_create(transport_maps_name, transport_maps,
109  tp->wildcard_channel = tp->wildcard_nexthop = 0;
110  tp->wildcard_errno = 0;
111  tp->expire = 0;
112  return (tp);
113 }
114 
115 /* transport_post_init - post-jail initialization */
116 
118 {
119  transport_match_parent_style = match_parent_style(VAR_TRANSPORT_MAPS);
120  transport_wildcard_init(tp);
121 }
122 
123 /* transport_free - destroy transport info */
124 
126 {
127  if (tp->transport_path)
129  if (tp->wildcard_channel)
131  if (tp->wildcard_nexthop)
133  myfree((void *) tp);
134 }
135 
136 /* update_entry - update from transport table entry */
137 
138 static void update_entry(const char *new_channel, const char *new_nexthop,
139  const char *rcpt_domain, VSTRING *channel,
140  VSTRING *nexthop)
141 {
142 
143  /*
144  * :[nexthop] means don't change the channel, and don't change the
145  * nexthop unless a non-default nexthop is specified. Thus, a right-hand
146  * side of ":" is the transport table equivalent of a NOOP.
147  */
148  if (*new_channel == 0) { /* :[nexthop] */
149  if (*new_nexthop != 0)
150  vstring_strcpy(nexthop, new_nexthop);
151  }
152 
153  /*
154  * transport[:[nexthop]] means change the channel, and reset the nexthop
155  * to the default unless a non-default nexthop is specified.
156  */
157  else {
158  vstring_strcpy(channel, new_channel);
159  if (*new_nexthop != 0)
160  vstring_strcpy(nexthop, new_nexthop);
161  else if (strcmp(STR(channel), MAIL_SERVICE_ERROR) != 0
162  && strcmp(STR(channel), MAIL_SERVICE_RETRY) != 0)
163  vstring_strcpy(nexthop, rcpt_domain);
164  else
165  vstring_strcpy(nexthop, "Address is undeliverable");
166  }
167 }
168 
169 /* parse_transport_entry - parse transport table entry */
170 
171 static void parse_transport_entry(const char *value, const char *rcpt_domain,
172  VSTRING *channel, VSTRING *nexthop)
173 {
174  char *saved_value;
175  const char *host;
176 
177 #define FOUND 1
178 #define NOTFOUND 0
179 
180  /*
181  * It would be great if we could specify a recipient address in the
182  * lookup result. Unfortunately, we cannot simply run the result through
183  * a parser that recognizes "transport:user@domain" because the lookup
184  * result can have arbitrary content (especially in the case of the error
185  * mailer).
186  */
187  saved_value = mystrdup(value);
188  host = split_at(saved_value, ':');
189  update_entry(saved_value, host ? host : "", rcpt_domain, channel, nexthop);
190  myfree(saved_value);
191 }
192 
193 /* transport_wildcard_init - (re) initialize wild-card lookup result */
194 
195 static void transport_wildcard_init(TRANSPORT_INFO *tp)
196 {
197  VSTRING *channel = vstring_alloc(10);
198  VSTRING *nexthop = vstring_alloc(10);
199  const char *value;
200 
201  /*
202  * Both channel and nexthop may be zero-length strings. Therefore we must
203  * use something else to represent "wild-card does not exist". We use
204  * null VSTRING pointers, for historical reasons.
205  */
206  if (tp->wildcard_channel)
208  if (tp->wildcard_nexthop)
210 
211  /*
212  * Technically, the wildcard lookup pattern is redundant. A static map
213  * (keys always match, result is fixed string) could achieve the same:
214  *
215  * transport_maps = hash:/etc/postfix/transport static:xxx:yyy
216  *
217  * But the user interface of such an approach would be less intuitive. We
218  * tolerate the continued existence of wildcard lookup patterns because
219  * of human interface considerations.
220  */
221 #define WILDCARD "*"
222 #define FULL 0
223 #define PARTIAL DICT_FLAG_FIXED
224 
225  if ((value = maps_find(tp->transport_path, WILDCARD, FULL)) != 0) {
226  parse_transport_entry(value, "", channel, nexthop);
227  tp->wildcard_errno = 0;
228  tp->wildcard_channel = channel;
229  tp->wildcard_nexthop = nexthop;
230  if (msg_verbose)
231  msg_info("wildcard_{chan:hop}={%s:%s}",
232  vstring_str(channel), vstring_str(nexthop));
233  } else {
235  vstring_free(channel);
236  vstring_free(nexthop);
237  tp->wildcard_channel = 0;
238  tp->wildcard_nexthop = 0;
239  }
240  tp->expire = event_time() + 30; /* XXX make configurable */
241 }
242 
243 /* transport_lookup - map a transport domain */
244 
245 int transport_lookup(TRANSPORT_INFO *tp, const char *addr,
246  const char *rcpt_domain,
247  VSTRING *channel, VSTRING *nexthop)
248 {
249  char *ratsign = 0;
250  const char *value;
251 
252 #define STREQ(x,y) (strcmp((x), (y)) == 0)
253 #define DISCARD_EXTENSION ((char **) 0)
254 
255  /*
256  * The null recipient is rewritten to the local mailer daemon address.
257  */
258  if (*addr == 0) {
259  msg_warn("transport_lookup: null address - skipping table lookup");
260  return (NOTFOUND);
261  }
262 
263  /*
264  * Look up the full and extension-stripped address, then match the domain
265  * and subdomains. Try the external form before the backwards-compatible
266  * internal form.
267  */
268 #define LOOKUP_STRATEGY \
269  (MA_FIND_FULL | MA_FIND_NOEXT | MA_FIND_DOMAIN | \
270  (transport_match_parent_style == MATCH_FLAG_PARENT ? \
271  MA_FIND_PDMS : MA_FIND_PDDMDS))
272 
273  if ((ratsign = strrchr(addr, '@')) == 0 || ratsign[1] == 0)
274  msg_panic("transport_lookup: bad address: \"%s\"", addr);
275 
276  if ((value = mail_addr_find_strategy(tp->transport_path, addr, (char **) 0,
277  LOOKUP_STRATEGY)) != 0) {
278  parse_transport_entry(value, rcpt_domain, channel, nexthop);
279  return (FOUND);
280  }
281  if (tp->transport_path->error != 0)
282  return (NOTFOUND);
283 
284  /*
285  * Fall back to the wild-card entry.
286  */
287  if (tp->wildcard_errno || event_time() > tp->expire)
288  transport_wildcard_init(tp);
289  if (tp->wildcard_errno) {
291  return (NOTFOUND);
292  } else if (tp->wildcard_channel) {
293  update_entry(STR(tp->wildcard_channel), STR(tp->wildcard_nexthop),
294  rcpt_domain, channel, nexthop);
295  return (FOUND);
296  }
297 
298  /*
299  * We really did not find it.
300  */
301  return (NOTFOUND);
302 }
303 
304 #ifdef TEST
305 
306  /*
307  * Proof-of-concept test program. Read an address from stdin, and spit out
308  * the lookup result.
309  */
310 
311 #include <string.h>
312 
313 #include <mail_conf.h>
314 #include <vstream.h>
315 #include <vstring_vstream.h>
316 
317 static NORETURN usage(const char *progname)
318 {
319  msg_fatal("usage: %s [-v] database", progname);
320 }
321 
322 int main(int argc, char **argv)
323 {
324  VSTRING *buffer = vstring_alloc(100);
325  VSTRING *channel = vstring_alloc(100);
326  VSTRING *nexthop = vstring_alloc(100);
327  TRANSPORT_INFO *tp;
328  char *bp;
329  char *addr_field;
330  char *rcpt_domain;
331  char *expect_channel;
332  char *expect_nexthop;
333  int status;
334  int ch;
335  int errs = 0;
336 
337  /*
338  * Parse JCL.
339  */
340  while ((ch = GETOPT(argc, argv, "v")) > 0) {
341  switch (ch) {
342  case 'v':
343  msg_verbose++;
344  break;
345  default:
346  usage(argv[0]);
347  }
348  }
349  if (argc != optind + 1)
350  usage(argv[0]);
351 
352  /*
353  * Initialize.
354  */
355 #define UPDATE(var, val) do { myfree(var); var = mystrdup(val); } while (0)
356 
357  mail_conf_read(); /* XXX eliminate dependency. */
358  UPDATE(var_rcpt_delim, "+");
359  UPDATE(var_mydomain, "localdomain");
360  UPDATE(var_myorigin, "localhost.localdomain");
361  UPDATE(var_mydest, "localhost.localdomain");
362 
363  tp = transport_pre_init("transport map", argv[optind]);
365 
366  while (vstring_fgets_nonl(buffer, VSTREAM_IN)) {
367  bp = STR(buffer);
368 
369  /*
370  * Parse the input and expectations. XXX We can't expect empty
371  * fields, so require '-' instead.
372  */
373  if ((addr_field = mystrtok(&bp, ":")) == 0)
374  msg_fatal("no address field");
375  if ((rcpt_domain = strrchr(addr_field, '@')) == 0)
376  msg_fatal("no recipient domain");
377  rcpt_domain += 1;
378  expect_channel = mystrtok(&bp, ":");
379  expect_nexthop = mystrtok(&bp, ":");
380  if ((expect_channel != 0) != (expect_nexthop != 0))
381  msg_fatal("specify both channel and nexthop, or specify neither");
382  if (expect_channel) {
383  if (strcmp(expect_channel, "-") == 0)
384  *expect_channel = 0;
385  if (strcmp(expect_nexthop, "-") == 0)
386  *expect_nexthop = 0;
387  vstring_strcpy(channel, "DEFAULT");
388  vstring_strcpy(nexthop, rcpt_domain);
389  }
390  if (mystrtok(&bp, ":") != 0)
391  msg_fatal("garbage after nexthop field");
392 
393  /*
394  * Lookups.
395  */
396  status = transport_lookup(tp, addr_field, rcpt_domain,
397  channel, nexthop);
398 
399  /*
400  * Enforce expectations.
401  */
402  if (expect_nexthop && status) {
403  vstream_printf("%s:%s -> %s:%s \n",
404  addr_field, rcpt_domain,
405  STR(channel), STR(nexthop));
407  if (strcmp(expect_channel, STR(channel)) != 0) {
408  msg_warn("expect channel '%s' but got '%s'",
409  expect_channel, STR(channel));
410  errs = 1;
411  }
412  if (strcmp(expect_nexthop, STR(nexthop)) != 0) {
413  msg_warn("expect nexthop '%s' but got '%s'",
414  expect_nexthop, STR(nexthop));
415  errs = 1;
416  }
417  } else if (expect_nexthop && !status) {
418  vstream_printf("%s:%s -> %s\n", addr_field, rcpt_domain,
419  tp->transport_path->error ?
420  "(try again)" : "(not found)");
422  msg_warn("expect channel '%s' but got none", expect_channel);
423  msg_warn("expect nexthop '%s' but got none", expect_nexthop);
424  errs = 1;
425  } else if (!status) {
426  vstream_printf("%s:%s -> %s\n", addr_field, rcpt_domain,
427  tp->transport_path->error ?
428  "(try again)" : "(not found)");
429  }
430  }
431  transport_free(tp);
432  vstring_free(nexthop);
433  vstring_free(channel);
434  vstring_free(buffer);
435  exit(errs != 0);
436 }
437 
438 #endif
int msg_verbose
Definition: msg.c:177
#define vstring_fgets_nonl(s, p)
VSTRING * wildcard_nexthop
Definition: transport.h:32
void myfree(void *ptr)
Definition: mymalloc.c:207
TRANSPORT_INFO * transport_pre_init(const char *transport_maps_name, const char *transport_maps)
Definition: transport.c:99
MAPS * transport_path
Definition: transport.h:30
int match_parent_style(const char *name)
char * mystrdup(const char *str)
Definition: mymalloc.c:225
#define MAIL_SERVICE_ERROR
Definition: mail_proto.h:52
#define NORETURN
Definition: sys_defs.h:1583
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
#define vstring_str(vp)
Definition: vstring.h:71
#define VSTREAM_OUT
Definition: vstream.h:67
int main(int argc, char **argv)
Definition: anvil.c:1010
char * var_mydomain
Definition: mail_params.c:224
#define DICT_FLAG_UTF8_REQUEST
Definition: dict.h:130
#define VSTREAM_IN
Definition: vstream.h:66
char * var_myorigin
Definition: mail_params.c:225
char * mystrtok(char **src, const char *sep)
Definition: mystrtok.c:54
#define FULL
#define DICT_FLAG_FOLD_FIX
Definition: dict.h:124
VSTRING * vstring_strcpy(VSTRING *vp, const char *src)
Definition: vstring.c:431
void mail_conf_read(void)
Definition: mail_conf.c:178
int wildcard_errno
Definition: transport.h:33
char * var_rcpt_delim
Definition: mail_params.c:274
void transport_post_init(TRANSPORT_INFO *tp)
Definition: transport.c:117
#define VAR_TRANSPORT_MAPS
Definition: mail_params.h:484
int transport_lookup(TRANSPORT_INFO *tp, const char *addr, const char *rcpt_domain, VSTRING *channel, VSTRING *nexthop)
Definition: transport.c:245
time_t expire
Definition: transport.h:34
MAPS * maps_create(const char *title, const char *map_names, int dict_flags)
Definition: maps.c:112
VSTREAM * vstream_printf(const char *fmt,...)
Definition: vstream.c:1335
#define DICT_FLAG_LOCK
Definition: dict.h:116
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
#define NOTFOUND
VSTRING * wildcard_channel
Definition: transport.h:31
#define STR(x)
Definition: transport.c:93
#define LOOKUP_STRATEGY
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
MAPS * maps_free(MAPS *maps)
Definition: maps.c:213
int vstream_fflush(VSTREAM *stream)
Definition: vstream.c:1257
#define MAIL_SERVICE_RETRY
Definition: mail_proto.h:53
time_t event_time(void)
Definition: events.c:647
int error
Definition: maps.h:25
#define GETOPT(argc, argv, str)
Definition: sys_defs.h:1313
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
#define DICT_FLAG_NO_REGSUB
Definition: dict.h:121
char * split_at(char *string, int delimiter)
Definition: split_at.c:53
#define UPDATE(ptr, new)
#define WILDCARD
#define mail_addr_find_strategy(maps, address, extension, strategy)
#define FOUND
const char * maps_find(MAPS *maps, const char *name, int flags)
Definition: maps.c:162
void transport_free(TRANSPORT_INFO *tp)
Definition: transport.c:125
void * mymalloc(ssize_t len)
Definition: mymalloc.c:150
void msg_info(const char *fmt,...)
Definition: msg.c:199
char * var_mydest
Definition: mail_params.c:226