Postfix3.3.1
mail_addr_map.c
[詳解]
1 /*++
2 /* NAME
3 /* mail_addr_map 3
4 /* SUMMARY
5 /* generic address mapping
6 /* SYNOPSIS
7 /* #include <mail_addr_map.h>
8 /*
9 /* ARGV *mail_addr_map_internal(path, address, propagate)
10 /* MAPS *path;
11 /* const char *address;
12 /* int propagate;
13 /*
14 /* ARGV *mail_addr_map_opt(path, address, propagate, in_form,
15 /* query_form, out_form)
16 /* MAPS *path;
17 /* const char *address;
18 /* int propagate;
19 /* int in_form;
20 /* int query_form;
21 /* int out_form;
22 /* DESCRIPTION
23 /* mail_addr_map_*() returns the translation for the named address,
24 /* or a null pointer if none is found.
25 /*
26 /* With mail_addr_map_internal(), the search address and results
27 /* are in internal (unquoted) form.
28 /*
29 /* mail_addr_map_opt() gives more control, at the cost of additional
30 /* conversions between internal and external forms.
31 /*
32 /* When the \fBpropagate\fR argument is non-zero,
33 /* address extensions that aren't explicitly matched in the lookup
34 /* table are propagated to the result addresses. The caller is
35 /* expected to pass the lookup result to argv_free().
36 /*
37 /* Lookups are performed by mail_addr_find_*(). When the result has the
38 /* form \fI@otherdomain\fR, the result is the original user in
39 /* \fIotherdomain\fR.
40 /*
41 /* Arguments:
42 /* .IP path
43 /* Dictionary search path (see maps(3)).
44 /* .IP address
45 /* The address to be looked up in external (quoted) form, or
46 /* in the form specified with the in_form argument.
47 /* .IP query_form
48 /* Database query address forms, either MA_FORM_INTERNAL (unquoted
49 /* form), MA_FORM_EXTERNAL (quoted form), MA_FORM_EXTERNAL_FIRST
50 /* (external, then internal if the forms differ), or
51 /* MA_FORM_INTERNAL_FIRST (internal, then external if the forms
52 /* differ).
53 /* .IP in_form
54 /* .IP out_form
55 /* Input and output address forms, either MA_FORM_INTERNAL (unquoted
56 /* form) or MA_FORM_EXTERNAL (quoted form).
57 /* DIAGNOSTICS
58 /* Warnings: map lookup returns a non-address result.
59 /*
60 /* The path->error value is non-zero when the lookup
61 /* failed with a non-permanent error.
62 /* SEE ALSO
63 /* mail_addr_find(3), mail address matching
64 /* mail_addr_crunch(3), mail address parsing and rewriting
65 /* LICENSE
66 /* .ad
67 /* .fi
68 /* The Secure Mailer license must be distributed with this software.
69 /* AUTHOR(S)
70 /* Wietse Venema
71 /* IBM T.J. Watson Research
72 /* P.O. Box 704
73 /* Yorktown Heights, NY 10598, USA
74 /*
75 /* Wietse Venema
76 /* Google, Inc.
77 /* 111 8th Avenue
78 /* New York, NY 10011, USA
79 /*--*/
80 
81 /* System library. */
82 
83 #include <sys_defs.h>
84 #include <string.h>
85 
86 /* Utility library. */
87 
88 #include <msg.h>
89 #include <vstring.h>
90 #include <dict.h>
91 #include <argv.h>
92 #include <mymalloc.h>
93 
94 /* Global library. */
95 
96 #include <quote_822_local.h>
97 #include <mail_addr_find.h>
98 #include <mail_addr_crunch.h>
99 #include <mail_addr_map.h>
100 
101 /* Application-specific. */
102 
103 #define STR vstring_str
104 #define LEN VSTRING_LEN
105 
106 /* mail_addr_map - map a canonical address */
107 
108 ARGV *mail_addr_map_opt(MAPS *path, const char *address, int propagate,
109  int in_form, int query_form, int out_form)
110 {
111  VSTRING *buffer = 0;
112  const char *myname = "mail_addr_map";
113  const char *string;
114  char *ratsign;
115  char *extension = 0;
116  ARGV *argv = 0;
117  int i;
118  VSTRING *int_address = 0;
119  VSTRING *ext_address = 0;
120  const char *int_addr;
121 
122  /*
123  * Optionally convert input from external form. We prefer internal-form
124  * input to avoid unnecessary input conversion in mail_addr_find_opt().
125  */
126  if (in_form == MA_FORM_EXTERNAL) {
127  int_address = vstring_alloc(100);
128  unquote_822_local(int_address, address);
129  int_addr = STR(int_address);
130  in_form = MA_FORM_INTERNAL;
131  } else {
132  int_addr = address;
133  }
134 
135  /*
136  * Look up the full address; if no match is found, look up the address
137  * with the extension stripped off, and remember the unmatched extension.
138  */
139  if ((string = mail_addr_find_opt(path, int_addr, &extension,
140  in_form, query_form,
142  MA_FIND_DEFAULT)) != 0) {
143 
144  /*
145  * Prepend the original user to @otherdomain, but do not propagate
146  * the unmatched address extension. Convert the address to external
147  * form just like the mail_addr_find_opt() output.
148  */
149  if (*string == '@') {
150  buffer = vstring_alloc(100);
151  if ((ratsign = strrchr(int_addr, '@')) != 0)
152  vstring_strncpy(buffer, int_addr, ratsign - int_addr);
153  else
154  vstring_strcpy(buffer, int_addr);
155  if (extension)
156  vstring_truncate(buffer, LEN(buffer) - strlen(extension));
157  vstring_strcat(buffer, string);
158  ext_address = vstring_alloc(2 * LEN(buffer));
159  quote_822_local(ext_address, STR(buffer));
160  string = STR(ext_address);
161  }
162 
163  /*
164  * Canonicalize the result, and propagate the unmatched extension to
165  * each address found.
166  */
167  argv = mail_addr_crunch_opt(string, propagate ? extension : 0,
168  MA_FORM_EXTERNAL, out_form);
169  if (buffer)
170  vstring_free(buffer);
171  if (ext_address)
172  vstring_free(ext_address);
173  if (msg_verbose)
174  for (i = 0; i < argv->argc; i++)
175  msg_info("%s: %s -> %d: %s", myname, address, i, argv->argv[i]);
176  if (argv->argc == 0) {
177  msg_warn("%s lookup of %s returns non-address result \"%s\"",
178  path->title, address, string);
179  argv = argv_free(argv);
180  path->error = DICT_ERR_RETRY;
181  }
182  }
183 
184  /*
185  * No match found.
186  */
187  else {
188  if (msg_verbose)
189  msg_info("%s: %s -> %s", myname, address,
190  path->error ? "(try again)" : "(not found)");
191  }
192 
193  /*
194  * Cleanup.
195  */
196  if (extension)
197  myfree(extension);
198  if (int_address)
199  vstring_free(int_address);
200 
201  return (argv);
202 }
203 
204 #ifdef TEST
205 
206 /*
207  * SYNOPSIS
208  * mail_addr_map pass_tests | fail_tests
209  * DESCRIPTION
210  * mail_addr_map performs the specified set of built-in
211  * unit tests. With 'pass_tests', all tests must pass, and
212  * with 'fail_tests' all tests must fail.
213  * DIAGNOSTICS
214  * When a unit test fails, the program prints details of the
215  * failed test.
216  *
217  * The program terminates with a non-zero exit status when at
218  * least one test does not pass with 'pass_tests', or when at
219  * least one test does not fail with 'fail_tests'.
220  */
221 
222 /* System library. */
223 
224 #include <sys_defs.h>
225 #include <ctype.h>
226 #include <stdlib.h>
227 #include <string.h>
228 #include <unistd.h>
229 
230 /* Utility library. */
231 
232 #include <argv.h>
233 #include <msg.h>
234 #include <mymalloc.h>
235 #include <vstring.h>
236 
237 /* Global library. */
238 
239 #include <canon_addr.h>
240 #include <mail_addr_map.h>
241 #include <mail_params.h>
242 
243 /* Application-specific. */
244 
245 #define STR vstring_str
246 
247 typedef struct {
248  const char *testname;
249  const char *database;
250  int propagate;
251  const char *delimiter;
252  int in_form;
253  int query_form;
254  int out_form;
255  const char *address;
256  const char *expect_argv[2];
257  int expect_argc;
258 } MAIL_ADDR_MAP_TEST;
259 
260 #define DONT_PROPAGATE_UNMATCHED_EXTENSION 0
261 #define DO_PROPAGATE_UNMATCHED_EXTENSION 1
262 #define NO_RECIPIENT_DELIMITER ""
263 #define PLUS_RECIPIENT_DELIMITER "+"
264 #define DOT_RECIPIENT_DELIMITER "."
265 
266  /*
267  * All these tests must pass, so that we know that mail_addr_map_opt() works
268  * as intended. mail_addr_map() has always been used for maps that expect
269  * external-form queries, so there are no tests here for internal-form
270  * queries.
271  */
272 static MAIL_ADDR_MAP_TEST pass_tests[] = {
273  {
274  "1 external -external-> external, no extension",
275  "inline:{ aa@example.com=bb@example.com }",
276  DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
278  "aa@example.com",
279  {"bb@example.com"}, 1,
280  },
281  {
282  "2 external -external-> external, extension, propagation",
283  "inline:{ aa@example.com=bb@example.com }",
284  DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
286  "aa+ext@example.com",
287  {"bb+ext@example.com"}, 1,
288  },
289  {
290  "3 external -external-> external, extension, no propagation, no match",
291  "inline:{ aa@example.com=bb@example.com }",
292  DONT_PROPAGATE_UNMATCHED_EXTENSION, NO_RECIPIENT_DELIMITER,
294  "aa+ext@example.com",
295  {0}, 0,
296  },
297  {
298  "4 external -external-> external, extension, full match",
299  "inline:{{cc+ext@example.com=dd@example.com,ee@example.com}}",
300  DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
302  "cc+ext@example.com",
303  {"dd@example.com", "ee@example.com"}, 2,
304  },
305  {
306  "5 external -external-> external, no extension, quoted",
307  "inline:{ {\"a a\"@example.com=\"b b\"@example.com} }",
308  DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
310  "\"a a\"@example.com",
311  {"\"b b\"@example.com"}, 1,
312  },
313  {
314  "6 external -external-> external, extension, propagation, quoted",
315  "inline:{ {\"a a\"@example.com=\"b b\"@example.com} }",
316  DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
318  "\"a a+ext\"@example.com",
319  {"\"b b+ext\"@example.com"}, 1,
320  },
321  {
322  "7 internal -external-> internal, no extension, propagation, embedded space",
323  "inline:{ {\"a a\"@example.com=\"b b\"@example.com} }",
324  DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
326  "a a@example.com",
327  {"b b@example.com"}, 1,
328  },
329  {
330  "8 internal -external-> internal, extension, propagation, embedded space",
331  "inline:{ {\"a a\"@example.com=\"b b\"@example.com} }",
332  DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
334  "a a+ext@example.com",
335  {"b b+ext@example.com"}, 1,
336  },
337  {
338  "9 internal -external-> internal, no extension, propagation, embedded space",
339  "inline:{ {a_a@example.com=\"b b\"@example.com} }",
340  DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
342  "a_a@example.com",
343  {"b b@example.com"}, 1,
344  },
345  {
346  "10 internal -external-> internal, extension, propagation, embedded space",
347  "inline:{ {a_a@example.com=\"b b\"@example.com} }",
348  DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
350  "a_a+ext@example.com",
351  {"b b+ext@example.com"}, 1,
352  },
353  {
354  "11 internal -external-> internal, no extension, @domain",
355  "inline:{ {@example.com=@example.net} }",
356  DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
358  "a@a@example.com",
359  {"\"a@a\"@example.net"}, 1,
360  },
361  {
362  "12 external -external-> external, extension, propagation",
363  "inline:{ aa@example.com=bb@example.com }",
364  DO_PROPAGATE_UNMATCHED_EXTENSION, DOT_RECIPIENT_DELIMITER,
366  "aa.ext@example.com",
367  {"bb.ext@example.com"}, 1,
368  },
369  0,
370 };
371 
372  /*
373  * All these tests must fail, so that we know that the tests work.
374  */
375 static MAIL_ADDR_MAP_TEST fail_tests[] = {
376  {
377  "selftest 1 external -external-> external, no extension, quoted",
378  "inline:{ {\"a a\"@example.com=\"b b\"@example.com} }",
379  DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
381  "\"a a\"@example.com",
382  {"\"bXb\"@example.com"}, 1,
383  },
384  {
385  "selftest 2 external -external-> external, no extension, quoted",
386  "inline:{ {\"a a\"@example.com=\"b b\"@example.com} }",
387  DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
389  "\"aXa\"@example.com",
390  {"\"b b\"@example.com"}, 1,
391  },
392  {
393  "selftest 3 external -external-> external, no extension, quoted",
394  "inline:{ {\"a a\"@example.com=\"b b\"@example.com} }",
395  DO_PROPAGATE_UNMATCHED_EXTENSION, PLUS_RECIPIENT_DELIMITER,
397  "\"a a\"@example.com",
398  {0}, 0,
399  },
400  0,
401 };
402 
403 /* canon_addr_external - surrogate to avoid trivial-rewrite dependency */
404 
405 VSTRING *canon_addr_external(VSTRING *result, const char *addr)
406 {
407  return (vstring_strcpy(result, addr));
408 }
409 
410 static int compare(const char *testname,
411  const char **expect_argv, int expect_argc,
412  char **result_argv, int result_argc)
413 {
414  int n;
415  int err = 0;
416 
417  if (expect_argc != 0 && result_argc != 0) {
418  for (n = 0; n < expect_argc && n < result_argc; n++) {
419  if (strcmp(expect_argv[n], result_argv[n]) != 0) {
420  msg_warn("fail test %s: expect[%d]='%s', result[%d]='%s'",
421  testname, n, expect_argv[n], n, result_argv[n]);
422  err = 1;
423  }
424  }
425  }
426  if (expect_argc != result_argc) {
427  msg_warn("fail test %s: expects %d results but there were %d",
428  testname, expect_argc, result_argc);
429  for (n = expect_argc; n < result_argc; n++)
430  msg_info("no expect to match result[%d]='%s'", n, result_argv[n]);
431  for (n = result_argc; n < expect_argc; n++)
432  msg_info("no result to match expect[%d]='%s'", n, expect_argv[n]);
433  err = 1;
434  }
435  return (err);
436 }
437 
438 static char *progname;
439 
440 static NORETURN usage(void)
441 {
442  msg_fatal("usage: %s pass_test | fail_test", progname);
443 }
444 
445 int main(int argc, char **argv)
446 {
447  MAIL_ADDR_MAP_TEST *test;
448  MAIL_ADDR_MAP_TEST *tests;
449  int errs = 0;
450 
451 #define UPDATE(dst, src) { if (dst) myfree(dst); dst = mystrdup(src); }
452 
453  /*
454  * Parse JCL.
455  */
456  progname = argv[0];
457  if (argc != 2) {
458  usage();
459  } else if (strcmp(argv[1], "pass_tests") == 0) {
460  tests = pass_tests;
461  } else if (strcmp(argv[1], "fail_tests") == 0) {
462  tests = fail_tests;
463  } else {
464  usage();
465  }
466 
467  /*
468  * Initialize.
469  */
471 
472  /*
473  * A read-eval-print loop, because specifying C strings with quotes and
474  * backslashes is painful.
475  */
476  for (test = tests; test->testname; test++) {
477  ARGV *result;
478  int fail = 0;
479 
480  if (mail_addr_form_to_string(test->in_form) == 0) {
481  msg_warn("test %s: bad in_form field: %d",
482  test->testname, test->in_form);
483  fail = 1;
484  continue;
485  }
486  if (mail_addr_form_to_string(test->query_form) == 0) {
487  msg_warn("test %s: bad query_form field: %d",
488  test->testname, test->query_form);
489  fail = 1;
490  continue;
491  }
492  if (mail_addr_form_to_string(test->out_form) == 0) {
493  msg_warn("test %s: bad out_form field: %d",
494  test->testname, test->out_form);
495  fail = 1;
496  continue;
497  }
498  MAPS *maps = maps_create("test", test->database, DICT_FLAG_LOCK
500 
501  UPDATE(var_rcpt_delim, test->delimiter);
502  result = mail_addr_map_opt(maps, test->address, test->propagate,
503  test->in_form, test->query_form, test->out_form);
504  if (compare(test->testname, test->expect_argv, test->expect_argc,
505  result ? result->argv : 0, result ? result->argc : 0) != 0) {
506  msg_info("database = %s", test->database);
507  msg_info("propagate = %d", test->propagate);
508  msg_info("delimiter = '%s'", var_rcpt_delim);
509  msg_info("in_form = %s", mail_addr_form_to_string(test->in_form));
510  msg_info("query_form = %s", mail_addr_form_to_string(test->query_form));
511  msg_info("out_form = %s", mail_addr_form_to_string(test->out_form));
512  msg_info("address = %s", test->address);
513  fail = 1;
514  }
515  maps_free(maps);
516  if (result)
517  argv_free(result);
518 
519  /*
520  * It is an error if a test does not pass or fail as intended.
521  */
522  errs += (tests == pass_tests ? fail : !fail);
523  }
524  return (errs != 0);
525 }
526 
527 #endif
int msg_verbose
Definition: msg.c:177
void mail_params_init()
Definition: mail_params.c:658
void myfree(void *ptr)
Definition: mymalloc.c:207
#define MA_FORM_INTERNAL
ARGV * argv_free(ARGV *argvp)
Definition: argv.c:136
#define NORETURN
Definition: sys_defs.h:1583
Definition: argv.h:17
int main(int argc, char **argv)
Definition: anvil.c:1010
#define DICT_ERR_RETRY
Definition: dict.h:178
#define MA_FIND_DEFAULT
Definition: maps.h:22
char ** argv
Definition: argv.h:20
VSTRING * unquote_822_local(VSTRING *dst, const char *mbox)
VSTRING * vstring_truncate(VSTRING *vp, ssize_t len)
Definition: vstring.c:415
#define DICT_FLAG_UTF8_REQUEST
Definition: dict.h:130
#define DICT_FLAG_FOLD_FIX
Definition: dict.h:124
VSTRING * vstring_strcpy(VSTRING *vp, const char *src)
Definition: vstring.c:431
char * var_rcpt_delim
Definition: mail_params.c:274
const char * mail_addr_find_opt(MAPS *path, const char *address, char **extp, int in_form, int query_form, int out_form, int strategy)
#define LEN
MAPS * maps_create(const char *title, const char *map_names, int dict_flags)
Definition: maps.c:112
VSTRING * canon_addr_external(VSTRING *result, const char *addr)
Definition: canon_addr.c:57
char * title
Definition: maps.h:23
#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
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
MAPS * maps_free(MAPS *maps)
Definition: maps.c:213
ARGV * mail_addr_crunch_opt(const char *string, const char *extension, int in_form, int out_form)
int error
Definition: maps.h:25
#define quote_822_local(dst, src)
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
#define UPDATE(ptr, new)
ssize_t argc
Definition: argv.h:19
ARGV * mail_addr_map_opt(MAPS *path, const char *address, int propagate, int in_form, int query_form, int out_form)
#define MA_FORM_EXTERNAL
VSTRING * vstring_strncpy(VSTRING *vp, const char *src, ssize_t len)
Definition: vstring.c:445
VSTRING * vstring_strcat(VSTRING *vp, const char *src)
Definition: vstring.c:459
#define STR
const char * mail_addr_form_to_string(int addr_form)
void msg_info(const char *fmt,...)
Definition: msg.c:199