Postfix3.3.1
resolve.c
[詳解]
1 /*++
2 /* NAME
3 /* resolve 3
4 /* SUMMARY
5 /* mail address resolver
6 /* SYNOPSIS
7 /* #include "trivial-rewrite.h"
8 /*
9 /* void resolve_init(void)
10 /*
11 /* int resolve_class(domain)
12 /* const char *domain;
13 /*
14 /* void resolve_proto(context, stream)
15 /* RES_CONTEXT *context;
16 /* VSTREAM *stream;
17 /* DESCRIPTION
18 /* This module implements the trivial address resolving engine.
19 /* It distinguishes between local and remote mail, and optionally
20 /* consults one or more transport tables that map a destination
21 /* to a transport, nexthop pair.
22 /*
23 /* resolve_init() initializes data structures that are private
24 /* to this module. It should be called once before using the
25 /* actual resolver routines.
26 /*
27 /* resolve_class() returns the address class for the specified
28 /* domain, or -1 in case of error.
29 /*
30 /* resolve_proto() implements the client-server protocol:
31 /* read one address in FQDN form, reply with a (transport,
32 /* nexthop, internalized recipient) triple.
33 /* STANDARDS
34 /* DIAGNOSTICS
35 /* Problems and transactions are logged to the syslog daemon.
36 /* BUGS
37 /* SEE ALSO
38 /* LICENSE
39 /* .ad
40 /* .fi
41 /* The Secure Mailer license must be distributed with this software.
42 /* AUTHOR(S)
43 /* Wietse Venema
44 /* IBM T.J. Watson Research
45 /* P.O. Box 704
46 /* Yorktown Heights, NY 10598, USA
47 /*
48 /* Wietse Venema
49 /* Google, Inc.
50 /* 111 8th Avenue
51 /* New York, NY 10011, USA
52 /*--*/
53 
54 /* System library. */
55 
56 #include <sys_defs.h>
57 #include <stdlib.h>
58 #include <string.h>
59 
60 #ifdef STRCASECMP_IN_STRINGS_H
61 #include <strings.h>
62 #endif
63 
64 /* Utility library. */
65 
66 #include <msg.h>
67 #include <vstring.h>
68 #include <vstream.h>
69 #include <vstring_vstream.h>
70 #include <split_at.h>
71 #include <valid_utf8_hostname.h>
72 #include <stringops.h>
73 #include <mymalloc.h>
74 
75 /* Global library. */
76 
77 #include <mail_params.h>
78 #include <mail_proto.h>
79 #include <resolve_local.h>
80 #include <mail_conf.h>
81 #include <quote_822_local.h>
82 #include <tok822.h>
83 #include <domain_list.h>
84 #include <string_list.h>
85 #include <match_parent_style.h>
86 #include <maps.h>
87 #include <mail_addr_find.h>
88 #include <valid_mailhost_addr.h>
89 
90 /* Application-specific. */
91 
92 #include "trivial-rewrite.h"
93 #include "transport.h"
94 
95  /*
96  * The job of the address resolver is to map one recipient address to a
97  * triple of (channel, nexthop, recipient). The channel is the name of the
98  * delivery service specified in master.cf, the nexthop is (usually) a
99  * description of the next host to deliver to, and recipient is the final
100  * recipient address. The latter may differ from the input address as the
101  * result of stripping multiple layers of sender-specified routing.
102  *
103  * Addresses are resolved by their domain name. Known domain names are
104  * categorized into classes: local, virtual alias, virtual mailbox, relay,
105  * and everything else. Finding the address domain class is a matter of
106  * table lookups.
107  *
108  * Different address domain classes generally use different delivery channels,
109  * and may use class dependent ways to arrive at the corresponding nexthop
110  * information. With classes that do final delivery, the nexthop is
111  * typically the local machine hostname.
112  *
113  * The transport lookup table provides a means to override the domain class
114  * channel and/or nexhop information for specific recipients or for entire
115  * domain hierarchies.
116  *
117  * This works well in the general case. The only bug in this approach is that
118  * the structure of the nexthop information is transport dependent.
119  * Typically, the nexthop specifies a hostname, hostname + TCP Port, or the
120  * pathname of a UNIX-domain socket. However, with the error transport the
121  * nexthop field contains free text with the reason for non-delivery.
122  *
123  * Therefore, a transport map entry that overrides the channel but not the
124  * nexthop information (or vice versa) may produce surprising results. In
125  * particular, the free text nexthop information for the error transport is
126  * likely to confuse regular delivery agents; and conversely, a hostname or
127  * socket pathname is not an adequate text as reason for non-delivery.
128  *
129  * In the code below, rcpt_domain specifies the domain name that we will use
130  * when the transport table specifies a non-default channel but no nexthop
131  * information (we use a generic text when that non-default channel is the
132  * error transport).
133  */
134 
135 #define STR vstring_str
136 #define LEN VSTRING_LEN
137 
138  /*
139  * Some of the lists that define the address domain classes.
140  */
141 static DOMAIN_LIST *relay_domains;
142 static STRING_LIST *virt_alias_doms;
143 static STRING_LIST *virt_mailbox_doms;
144 
145 static MAPS *relocated_maps;
146 
147 /* resolve_class - determine domain address class */
148 
149 int resolve_class(const char *domain)
150 {
151  int ret;
152 
153  /*
154  * Same order as in resolve_addr().
155  */
156  if ((ret = resolve_local(domain)) != 0)
157  return (ret > 0 ? RESOLVE_CLASS_LOCAL : -1);
158  if (virt_alias_doms) {
159  if (string_list_match(virt_alias_doms, domain))
160  return (RESOLVE_CLASS_ALIAS);
161  if (virt_alias_doms->error)
162  return (-1);
163  }
164  if (virt_mailbox_doms) {
165  if (string_list_match(virt_mailbox_doms, domain))
166  return (RESOLVE_CLASS_VIRTUAL);
167  if (virt_mailbox_doms->error)
168  return (-1);
169  }
170  if (relay_domains) {
171  if (string_list_match(relay_domains, domain))
172  return (RESOLVE_CLASS_RELAY);
173  if (relay_domains->error)
174  return (-1);
175  }
176  return (RESOLVE_CLASS_DEFAULT);
177 }
178 
179 /* resolve_addr - resolve address according to rule set */
180 
181 static void resolve_addr(RES_CONTEXT *rp, char *sender, char *addr,
182  VSTRING *channel, VSTRING *nexthop,
183  VSTRING *nextrcpt, int *flags)
184 {
185  const char *myname = "resolve_addr";
186  VSTRING *addr_buf = vstring_alloc(100);
187  TOK822 *tree = 0;
188  TOK822 *saved_domain = 0;
189  TOK822 *domain = 0;
190  char *destination;
191  const char *blame = 0;
192  const char *rcpt_domain;
193  ssize_t addr_len;
194  ssize_t loop_count;
195  ssize_t loop_max;
196  char *local;
197  char *oper;
198  char *junk;
199  const char *relay;
200  const char *xport;
201  const char *sender_key;
202  int rc;
203 
204  *flags = 0;
205  vstring_strcpy(channel, "CHANNEL NOT UPDATED");
206  vstring_strcpy(nexthop, "NEXTHOP NOT UPDATED");
207  vstring_strcpy(nextrcpt, "NEXTRCPT NOT UPDATED");
208 
209  /*
210  * The address is in internalized (unquoted) form.
211  *
212  * In an ideal world we would parse the externalized address form as given
213  * to us by the sender.
214  *
215  * However, in the real world we have to look for routing characters like
216  * %@! in the address local-part, even when that information is quoted
217  * due to the presence of special characters or whitespace. Although
218  * technically incorrect, this is needed to stop user@domain@domain relay
219  * attempts when forwarding mail to a Sendmail MX host.
220  *
221  * This suggests that we parse the address in internalized (unquoted) form.
222  * Unfortunately, if we do that, the unparser generates incorrect white
223  * space between adjacent non-operator tokens. Example: ``first last''
224  * needs white space, but ``stuff[stuff]'' does not. This is is not a
225  * problem when unparsing the result from parsing externalized forms,
226  * because the parser/unparser were designed for valid externalized forms
227  * where ``stuff[stuff]'' does not happen.
228  *
229  * As a workaround we start with the quoted form and then dequote the
230  * local-part only where needed. This will do the right thing in most
231  * (but not all) cases.
232  */
233  addr_len = strlen(addr);
234  quote_822_local(addr_buf, addr);
235  tree = tok822_scan_addr(vstring_str(addr_buf));
236 
237  /*
238  * The optimizer will eliminate tests that always fail, and will replace
239  * multiple expansions of this macro by a GOTO to a single instance.
240  */
241 #define FREE_MEMORY_AND_RETURN { \
242  if (saved_domain) \
243  tok822_free_tree(saved_domain); \
244  if(tree) \
245  tok822_free_tree(tree); \
246  if (addr_buf) \
247  vstring_free(addr_buf); \
248  return; \
249  }
250 
251  /*
252  * Preliminary resolver: strip off all instances of the local domain.
253  * Terminate when no destination domain is left over, or when the
254  * destination domain is remote.
255  *
256  * XXX To whom it may concern. If you change the resolver loop below, or
257  * quote_822_local.c, or tok822_parse.c, be sure to re-run the tests
258  * under "make resolve_clnt_test" in the global directory.
259  */
260 #define RESOLVE_LOCAL(domain) \
261  resolve_local(STR(tok822_internalize(addr_buf, domain, TOK822_STR_DEFL)))
262 
263  for (loop_count = 0, loop_max = addr_len + 100; /* void */ ; loop_count++) {
264 
265  /*
266  * XXX Should never happen, but if this happens with some
267  * pathological address, then that is not sufficient reason to
268  * disrupt the operation of an MTA.
269  */
270  if (loop_count > loop_max) {
271  msg_warn("resolve_addr: <%s>: giving up after %ld iterations",
272  addr, (long) loop_count);
273  *flags |= RESOLVE_FLAG_FAIL;
275  break;
276  }
277 
278  /*
279  * Strip trailing dot at end of domain, but not dot-dot or at-dot.
280  * This merely makes diagnostics more accurate by leaving bogus
281  * addresses alone.
282  */
283  if (tree->tail
284  && tree->tail->type == '.'
285  && tok822_rfind_type(tree->tail, '@') != 0
286  && tree->tail->prev->type != '.'
287  && tree->tail->prev->type != '@')
289 
290  /*
291  * Strip trailing @.
292  */
294  && tree->tail
295  && tree->tail->type == '@')
297 
298  /*
299  * Strip (and save) @domain if local.
300  *
301  * Grr. resolve_local() table lookups may fail. It may be OK for local
302  * file lookup code to abort upon failure, but with network-based
303  * tables it is preferable to return an error indication to the
304  * requestor.
305  */
306  if ((domain = tok822_rfind_type(tree->tail, '@')) != 0) {
307  if (domain->next && (rc = RESOLVE_LOCAL(domain->next)) <= 0) {
308  if (rc < 0) {
309  *flags |= RESOLVE_FLAG_FAIL;
311  }
312  break;
313  }
314  tok822_sub_keep_before(tree, domain);
315  if (saved_domain)
316  tok822_free_tree(saved_domain);
317  saved_domain = domain;
318  domain = 0; /* safety for future change */
319  }
320 
321  /*
322  * After stripping the local domain, if any, replace foo%bar by
323  * foo@bar, site!user by user@site, rewrite to canonical form, and
324  * retry.
325  */
326  if (tok822_rfind_type(tree->tail, '@')
327  || (var_swap_bangpath && tok822_rfind_type(tree->tail, '!'))
328  || (var_percent_hack && tok822_rfind_type(tree->tail, '%'))) {
329  rewrite_tree(&local_context, tree);
330  continue;
331  }
332 
333  /*
334  * If the local-part is a quoted string, crack it open when we're
335  * permitted to do so and look for routing operators. This is
336  * technically incorrect, but is needed to stop relaying problems.
337  *
338  * XXX Do another feeble attempt to keep local-part info quoted.
339  */
341  && tree->head && tree->head == tree->tail
342  && tree->head->type == TOK822_QSTRING
343  && ((oper = strrchr(local = STR(tree->head->vstr), '@')) != 0
344  || (var_percent_hack && (oper = strrchr(local, '%')) != 0)
345  || (var_swap_bangpath && (oper = strrchr(local, '!')) != 0))) {
346  if (*oper == '%')
347  *oper = '@';
348  tok822_internalize(addr_buf, tree->head, TOK822_STR_DEFL);
349  if (*oper == '@') {
350  junk = mystrdup(STR(addr_buf));
351  quote_822_local(addr_buf, junk);
352  myfree(junk);
353  }
354  tok822_free(tree->head);
355  tree->head = tok822_scan(STR(addr_buf), &tree->tail);
356  rewrite_tree(&local_context, tree);
357  continue;
358  }
359 
360  /*
361  * An empty local-part or an empty quoted string local-part becomes
362  * the local MAILER-DAEMON, for consistency with our own From:
363  * message headers.
364  */
365  if (tree->head && tree->head == tree->tail
366  && tree->head->type == TOK822_QSTRING
367  && VSTRING_LEN(tree->head->vstr) == 0) {
368  tok822_free(tree->head);
369  tree->head = 0;
370  }
371  /* XXX Re-resolve the surrogate, in case already in user@domain form. */
372  if (tree->head == 0) {
373  tree->head = tok822_scan(var_empty_addr, &tree->tail);
374  continue;
375  }
376  /* XXX Re-resolve with @$myhostname for backwards compatibility. */
377  if (domain == 0 && saved_domain == 0) {
378  tok822_sub_append(tree, tok822_alloc('@', (char *) 0));
380  continue;
381  }
382 
383  /*
384  * We're done. There are no domains left to strip off the address,
385  * and all null local-part information is sanitized.
386  */
387  domain = 0;
388  break;
389  }
390 
391  vstring_free(addr_buf);
392  addr_buf = 0;
393 
394  /*
395  * Make sure the resolved envelope recipient has the user@domain form. If
396  * no domain was specified in the address, assume the local machine. See
397  * above for what happens with an empty address.
398  */
399  if (domain == 0) {
400  if (saved_domain) {
401  tok822_sub_append(tree, saved_domain);
402  saved_domain = 0;
403  } else {
404  tok822_sub_append(tree, tok822_alloc('@', (char *) 0));
406  }
407  }
408 
409  /*
410  * Transform the recipient address back to internal form.
411  *
412  * XXX This may produce incorrect results if we cracked open a quoted
413  * local-part with routing operators; see discussion above at the top of
414  * the big loop.
415  *
416  * XXX We explicitly disallow domain names in bare network address form. A
417  * network address destination should be formatted according to RFC 2821:
418  * it should be enclosed in [], and an IPv6 address should have an IPv6:
419  * prefix.
420  */
421  tok822_internalize(nextrcpt, tree, TOK822_STR_DEFL);
422  rcpt_domain = strrchr(STR(nextrcpt), '@') + 1;
423  if (rcpt_domain == (char *) 1)
424  msg_panic("no @ in address: \"%s\"", STR(nextrcpt));
425  if (*rcpt_domain == '[') {
426  if (!valid_mailhost_literal(rcpt_domain, DONT_GRIPE))
427  *flags |= RESOLVE_FLAG_ERROR;
428  } else if (var_smtputf8_enable
429  && valid_utf8_string(STR(nextrcpt), LEN(nextrcpt)) == 0) {
430  *flags |= RESOLVE_FLAG_ERROR;
431  } else if (!valid_utf8_hostname(var_smtputf8_enable, rcpt_domain,
432  DONT_GRIPE)) {
433  if (var_resolve_num_dom && valid_hostaddr(rcpt_domain, DONT_GRIPE)) {
434  vstring_insert(nextrcpt, rcpt_domain - STR(nextrcpt), "[", 1);
435  vstring_strcat(nextrcpt, "]");
436  rcpt_domain = strrchr(STR(nextrcpt), '@') + 1;
437  if ((rc = resolve_local(rcpt_domain)) > 0) /* XXX */
438  domain = 0;
439  else if (rc < 0) {
440  *flags |= RESOLVE_FLAG_FAIL;
442  }
443  } else {
444  *flags |= RESOLVE_FLAG_ERROR;
445  }
446  }
447  tok822_free_tree(tree);
448  tree = 0;
449 
450  /*
451  * XXX Short-cut invalid address forms.
452  */
453  if (*flags & RESOLVE_FLAG_ERROR) {
454  *flags |= RESOLVE_CLASS_DEFAULT;
456  }
457 
458  /*
459  * Recognize routing operators in the local-part, even when we do not
460  * recognize ! or % as valid routing operators locally. This is needed to
461  * prevent backup MX hosts from relaying third-party destinations through
462  * primary MX hosts, otherwise the backup host could end up on black
463  * lists. Ignore local swap_bangpath and percent_hack settings because we
464  * can't know how the next MX host is set up.
465  */
466  if (strcmp(STR(nextrcpt) + strcspn(STR(nextrcpt), "@!%") + 1, rcpt_domain))
467  *flags |= RESOLVE_FLAG_ROUTED;
468 
469  /*
470  * With local, virtual, relay, or other non-local destinations, give the
471  * highest precedence to transport associated nexthop information.
472  *
473  * Otherwise, with relay or other non-local destinations, the relayhost
474  * setting overrides the recipient domain name, and the sender-dependent
475  * relayhost overrides both.
476  *
477  * XXX Nag if the recipient domain is listed in multiple domain lists. The
478  * result is implementation defined, and may break when internals change.
479  *
480  * For now, we distinguish only a fixed number of address classes.
481  * Eventually this may become extensible, so that new classes can be
482  * configured with their own domain list, delivery transport, and
483  * recipient table.
484  */
485 #define STREQ(x,y) (strcmp((x), (y)) == 0)
486 
487  if (domain != 0) {
488 
489  /*
490  * Virtual alias domain.
491  */
492  if (virt_alias_doms
493  && string_list_match(virt_alias_doms, rcpt_domain)) {
494  if (var_helpful_warnings) {
495  if (virt_mailbox_doms
496  && string_list_match(virt_mailbox_doms, rcpt_domain))
497  msg_warn("do not list domain %s in BOTH %s and %s",
498  rcpt_domain, VAR_VIRT_ALIAS_DOMS,
500  if (relay_domains
501  && domain_list_match(relay_domains, rcpt_domain))
502  msg_warn("do not list domain %s in BOTH %s and %s",
503  rcpt_domain, VAR_VIRT_ALIAS_DOMS,
505 #if 0
506  if (strcasecmp_utf8(rcpt_domain, var_myorigin) == 0)
507  msg_warn("do not list $%s (%s) in %s",
509 #endif
510  }
512  vstring_sprintf(nexthop, "5.1.1 User unknown%s",
514  " in virtual alias table" : "");
515  *flags |= RESOLVE_CLASS_ALIAS;
516  } else if (virt_alias_doms && virt_alias_doms->error != 0) {
517  msg_warn("%s lookup failure", VAR_VIRT_ALIAS_DOMS);
518  *flags |= RESOLVE_FLAG_FAIL;
520  }
521 
522  /*
523  * Virtual mailbox domain.
524  */
525  else if (virt_mailbox_doms
526  && string_list_match(virt_mailbox_doms, rcpt_domain)) {
527  if (var_helpful_warnings) {
528  if (relay_domains
529  && domain_list_match(relay_domains, rcpt_domain))
530  msg_warn("do not list domain %s in BOTH %s and %s",
531  rcpt_domain, VAR_VIRT_MAILBOX_DOMS,
533  }
535  vstring_strcpy(nexthop, rcpt_domain);
536  blame = rp->virt_transport_name;
537  *flags |= RESOLVE_CLASS_VIRTUAL;
538  } else if (virt_mailbox_doms && virt_mailbox_doms->error != 0) {
539  msg_warn("%s lookup failure", VAR_VIRT_MAILBOX_DOMS);
540  *flags |= RESOLVE_FLAG_FAIL;
542  } else {
543 
544  /*
545  * Off-host relay destination.
546  */
547  if (relay_domains
548  && domain_list_match(relay_domains, rcpt_domain)) {
550  blame = rp->relay_transport_name;
551  *flags |= RESOLVE_CLASS_RELAY;
552  } else if (relay_domains && relay_domains->error != 0) {
553  msg_warn("%s lookup failure", VAR_RELAY_DOMAINS);
554  *flags |= RESOLVE_FLAG_FAIL;
556  }
557 
558  /*
559  * Other off-host destination.
560  */
561  else {
562  if (rp->snd_def_xp_info
563  && (xport = mail_addr_find(rp->snd_def_xp_info,
564  sender_key = (*sender ? sender :
566  (char **) 0)) != 0) {
567  if (*xport == 0) {
568  msg_warn("%s: ignoring null lookup result for %s",
569  rp->snd_def_xp_maps_name, sender_key);
570  xport = "DUNNO";
571  }
572  vstring_strcpy(channel, strcasecmp(xport, "DUNNO") == 0 ?
573  RES_PARAM_VALUE(rp->def_transport) : xport);
574  blame = rp->snd_def_xp_maps_name;
575  } else if (rp->snd_def_xp_info
576  && rp->snd_def_xp_info->error != 0) {
577  msg_warn("%s lookup failure", rp->snd_def_xp_maps_name);
578  *flags |= RESOLVE_FLAG_FAIL;
580  } else {
582  blame = rp->def_transport_name;
583  }
584  *flags |= RESOLVE_CLASS_DEFAULT;
585  }
586 
587  /*
588  * With off-host delivery, sender-dependent or global relayhost
589  * override the recipient domain.
590  */
591  if (rp->snd_relay_info
592  && (relay = mail_addr_find(rp->snd_relay_info,
593  sender_key = (*sender ? sender :
595  (char **) 0)) != 0) {
596  if (*relay == 0) {
597  msg_warn("%s: ignoring null lookup result for %s",
598  rp->snd_relay_maps_name, sender_key);
599  relay = 0;
600  } else if (strcasecmp_utf8(relay, "DUNNO") == 0)
601  relay = 0;
602  } else if (rp->snd_relay_info
603  && rp->snd_relay_info->error != 0) {
604  msg_warn("%s lookup failure", rp->snd_relay_maps_name);
605  *flags |= RESOLVE_FLAG_FAIL;
607  } else {
608  relay = 0;
609  }
610  /* Enforce all the relayhost precedences in one place. */
611  if (relay != 0) {
612  vstring_strcpy(nexthop, relay);
613  } else if (*RES_PARAM_VALUE(rp->relayhost))
615  else
616  vstring_strcpy(nexthop, rcpt_domain);
617  }
618  }
619 
620  /*
621  * Local delivery.
622  *
623  * XXX Nag if the domain is listed in multiple domain lists. The effect is
624  * implementation defined, and may break when internals change.
625  */
626  else {
627  if (var_helpful_warnings) {
628  if (virt_alias_doms
629  && string_list_match(virt_alias_doms, rcpt_domain))
630  msg_warn("do not list domain %s in BOTH %s and %s",
631  rcpt_domain, VAR_MYDEST, VAR_VIRT_ALIAS_DOMS);
632  if (virt_mailbox_doms
633  && string_list_match(virt_mailbox_doms, rcpt_domain))
634  msg_warn("do not list domain %s in BOTH %s and %s",
635  rcpt_domain, VAR_MYDEST, VAR_VIRT_MAILBOX_DOMS);
636  }
638  vstring_strcpy(nexthop, rcpt_domain);
639  blame = rp->local_transport_name;
640  *flags |= RESOLVE_CLASS_LOCAL;
641  }
642 
643  /*
644  * An explicit main.cf transport:nexthop setting overrides the nexthop.
645  *
646  * XXX We depend on this mechanism to enforce per-recipient concurrencies
647  * for local recipients. With "local_transport = local:$myhostname" we
648  * force mail for any domain in $mydestination/${proxy,inet}_interfaces
649  * to share the same queue.
650  */
651  if ((destination = split_at(STR(channel), ':')) != 0 && *destination)
652  vstring_strcpy(nexthop, destination);
653 
654  /*
655  * Sanity checks.
656  */
657  if (*STR(channel) == 0) {
658  if (blame == 0)
659  msg_panic("%s: null blame", myname);
660  msg_warn("file %s/%s: parameter %s: null transport is not allowed",
662  *flags |= RESOLVE_FLAG_FAIL;
664  }
665  if (*STR(nexthop) == 0)
666  msg_panic("%s: null nexthop", myname);
667 
668  /*
669  * The transport map can selectively override any transport and/or
670  * nexthop host info that is set up above. Unfortunately, the syntax for
671  * nexthop information is transport specific. We therefore need sane and
672  * intuitive semantics for transport map entries that specify a channel
673  * but no nexthop.
674  *
675  * With non-error transports, the initial nexthop information is the
676  * recipient domain. However, specific main.cf transport definitions may
677  * specify a transport-specific destination, such as a host + TCP socket,
678  * or the pathname of a UNIX-domain socket. With less precedence than
679  * main.cf transport definitions, a main.cf relayhost definition may also
680  * override nexthop information for off-host deliveries.
681  *
682  * With the error transport, the nexthop information is free text that
683  * specifies the reason for non-delivery.
684  *
685  * Because nexthop syntax is transport specific we reset the nexthop
686  * information to the recipient domain when the transport table specifies
687  * a transport without also specifying the nexthop information.
688  *
689  * Subtle note: reset nexthop even when the transport table does not change
690  * the transport. Otherwise it is hard to get rid of main.cf specified
691  * nexthop information.
692  *
693  * XXX Don't override the virtual alias class (error:User unknown) result.
694  */
695  if (rp->transport_info && !(*flags & RESOLVE_CLASS_ALIAS)) {
696  if (transport_lookup(rp->transport_info, STR(nextrcpt),
697  rcpt_domain, channel, nexthop) == 0
698  && rp->transport_info->transport_path->error != 0) {
699  msg_warn("%s lookup failure", rp->transport_maps_name);
700  *flags |= RESOLVE_FLAG_FAIL;
702  }
703  }
704 
705  /*
706  * Bounce recipients that have moved, regardless of domain address class.
707  * We do this last, in anticipation of transport maps that can override
708  * the recipient address.
709  *
710  * The downside of not doing this in delivery agents is that this table has
711  * no effect on local alias expansion results. Such mail will have to
712  * make almost an entire iteration through the mail system.
713  */
714 #define IGNORE_ADDR_EXTENSION ((char **) 0)
715 
716  if (relocated_maps != 0) {
717  const char *newloc;
718 
719  if ((newloc = mail_addr_find(relocated_maps, STR(nextrcpt),
720  IGNORE_ADDR_EXTENSION)) != 0) {
722  /* 5.1.6 is the closest match, but not perfect. */
723  vstring_sprintf(nexthop, "5.1.6 User has moved to %s", newloc);
724  } else if (relocated_maps->error != 0) {
725  msg_warn("%s lookup failure", VAR_RELOCATED_MAPS);
726  *flags |= RESOLVE_FLAG_FAIL;
728  }
729  }
730 
731  /*
732  * Bounce recipient addresses that start with `-'. External commands may
733  * misinterpret such addresses as command-line options.
734  *
735  * In theory I could say people should always carefully set up their
736  * master.cf pipe mailer entries with `--' before the first non-option
737  * argument, but mistakes will happen regardless.
738  *
739  * Therefore the protection is put in place here, where it cannot be
740  * bypassed.
741  */
742  if (var_allow_min_user == 0 && STR(nextrcpt)[0] == '-') {
743  *flags |= RESOLVE_FLAG_ERROR;
745  }
746 
747  /*
748  * Clean up.
749  */
751 }
752 
753 /* Static, so they can be used by the network protocol interface only. */
754 
755 static VSTRING *channel;
756 static VSTRING *nexthop;
757 static VSTRING *nextrcpt;
758 static VSTRING *query;
759 static VSTRING *sender;
760 
761 /* resolve_proto - read request and send reply */
762 
763 int resolve_proto(RES_CONTEXT *context, VSTREAM *stream)
764 {
765  int flags;
766 
767  if (attr_scan(stream, ATTR_FLAG_STRICT,
770  ATTR_TYPE_END) != 2)
771  return (-1);
772 
773  resolve_addr(context, STR(sender), STR(query),
774  channel, nexthop, nextrcpt, &flags);
775 
776  if (msg_verbose)
777  msg_info("`%s' -> `%s' -> (`%s' `%s' `%s' `%d')",
778  STR(sender), STR(query), STR(channel),
779  STR(nexthop), STR(nextrcpt), flags);
780 
781  attr_print(stream, ATTR_FLAG_NONE,
785  SEND_ATTR_STR(MAIL_ATTR_RECIP, STR(nextrcpt)),
787  ATTR_TYPE_END);
788 
789  if (vstream_fflush(stream) != 0) {
790  msg_warn("write resolver reply: %m");
791  return (-1);
792  }
793  return (0);
794 }
795 
796 /* resolve_init - module initializations */
797 
798 void resolve_init(void)
799 {
800  sender = vstring_alloc(100);
801  query = vstring_alloc(100);
802  channel = vstring_alloc(100);
803  nexthop = vstring_alloc(100);
804  nextrcpt = vstring_alloc(100);
805 
806  if (*var_virt_alias_doms)
807  virt_alias_doms =
810 
812  virt_mailbox_doms =
815 
816  if (*var_relay_domains)
817  relay_domains =
821 
822  if (*var_relocated_maps)
823  relocated_maps =
827 }
int msg_verbose
Definition: msg.c:177
int valid_hostaddr(const char *addr, int gripe)
#define RESOLVE_FLAG_FAIL
Definition: resolve_clnt.h:28
void rewrite_tree(RWR_CONTEXT *context, TOK822 *tree)
Definition: rewrite.c:106
#define ATTR_FLAG_NONE
Definition: attr.h:98
void myfree(void *ptr)
Definition: mymalloc.c:207
#define MATCH_FLAG_RETURN
Definition: match_list.h:40
TOK822 * tok822_sub_keep_before(TOK822 *, TOK822 *)
Definition: tok822_tree.c:240
MAPS * transport_path
Definition: transport.h:30
int match_parent_style(const char *name)
bool var_percent_hack
char * mystrdup(const char *str)
Definition: mymalloc.c:225
char * var_null_def_xport_maps_key
#define FREE_MEMORY_AND_RETURN
#define MAIL_SERVICE_ERROR
Definition: mail_proto.h:52
char * var_relocated_maps
Definition: proxymap.c:265
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
TOK822 * tok822_rfind_type(TOK822 *, int)
Definition: tok822_find.c:62
#define VAR_RELAY_DOMAINS
Definition: mail_params.h:2048
int resolve_proto(RES_CONTEXT *context, VSTREAM *stream)
Definition: resolve.c:763
#define vstring_str(vp)
Definition: vstring.h:71
#define tok822_scan(cp, ptr)
Definition: tok822.h:83
#define STRING_LIST
Definition: string_list.h:22
#define domain_list_match
Definition: domain_list.h:26
int valid_utf8_string(const char *, ssize_t)
char ** virt_transport
char ** def_transport
Definition: tok822.h:27
Definition: maps.h:22
char * var_relay_domains
Definition: mail_params.c:286
int resolve_class(const char *domain)
Definition: resolve.c:149
const char * local_transport_name
bool var_resolve_nulldom
int resolve_local(const char *addr)
Definition: resolve_local.c:78
#define ATTR_TYPE_END
Definition: attr.h:39
#define RESOLVE_LOCAL(domain)
char * var_config_dir
Definition: mail_params.c:241
#define DICT_FLAG_UTF8_REQUEST
Definition: dict.h:130
int var_smtputf8_enable
Definition: mail_params.c:343
char * var_virt_alias_doms
Definition: proxymap.c:257
#define strcasecmp_utf8(s1, s2)
Definition: stringops.h:75
#define VAR_RELOCATED_MAPS
Definition: mail_params.h:732
char * var_myorigin
Definition: mail_params.c:225
bool var_swap_bangpath
MAPS * snd_relay_info
#define VSTRING_LEN(vp)
Definition: vstring.h:72
#define DOMAIN_LIST
Definition: domain_list.h:22
#define MAIL_ATTR_ADDR
Definition: mail_proto.h:146
#define DICT_FLAG_FOLD_FIX
Definition: dict.h:124
#define RESOLVE_FLAG_ROUTED
Definition: resolve_clnt.h:26
#define RESOLVE_CLASS_VIRTUAL
Definition: resolve_clnt.h:32
TOK822 * tok822_sub_append(TOK822 *, TOK822 *)
Definition: tok822_tree.c:206
#define MAIN_CONF_FILE
Definition: mail_params.h:334
#define VAR_VIRT_MAILBOX_DOMS
Definition: mail_params.h:2525
VSTRING * vstring_strcpy(VSTRING *vp, const char *src)
Definition: vstring.c:431
TOK822 * tok822_free_tree(TOK822 *)
Definition: tok822_tree.c:262
#define VAR_VIRT_ALIAS_DOMS
Definition: mail_params.h:434
#define MAIL_ATTR_TRANSPORT
Definition: mail_proto.h:147
bool var_resolve_dequoted
#define string_list_init(o, f, p)
Definition: string_list.h:24
struct TRANSPORT_INFO * transport_info
#define RESOLVE_CLASS_LOCAL
Definition: resolve_clnt.h:30
#define string_list_match
Definition: string_list.h:26
VSTRING * vstring_insert(VSTRING *vp, ssize_t start, const char *buf, ssize_t len)
Definition: vstring.c:518
const char * snd_def_xp_maps_name
#define attr_print
Definition: attr.h:109
const char * relay_transport_name
#define RESOLVE_FLAG_ERROR
Definition: resolve_clnt.h:27
int valid_mailhost_literal(const char *addr, int gripe)
char * var_empty_addr
Definition: cleanup_init.c:137
int valid_utf8_hostname(int enable_utf8, const char *name, int gripe)
VSTRING * vstr
Definition: tok822.h:29
struct TOK822 * head
Definition: tok822.h:32
int type
Definition: tok822.h:28
int transport_lookup(TRANSPORT_INFO *tp, const char *addr, const char *rcpt_domain, VSTRING *channel, VSTRING *nexthop)
Definition: transport.c:245
MAPS * maps_create(const char *title, const char *map_names, int dict_flags)
Definition: maps.c:112
bool var_show_unk_rcpt_table
Definition: smtpd.c:1305
char ** relay_transport
RWR_CONTEXT local_context
Definition: rewrite.c:90
#define mail_addr_find(maps, address, extension)
#define DICT_FLAG_LOCK
Definition: dict.h:116
void msg_warn(const char *fmt,...)
Definition: msg.c:215
#define STR
Definition: resolve.c:135
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
#define VAR_MYORIGIN
Definition: mail_params.h:125
#define IGNORE_ADDR_EXTENSION
VSTRING * vstring_sprintf(VSTRING *vp, const char *format,...)
Definition: vstring.c:602
VSTRING * tok822_internalize(VSTRING *, TOK822 *, int)
Definition: tok822_parse.c:199
#define SEND_ATTR_INT(name, val)
Definition: attr.h:63
int var_helpful_warnings
Definition: mail_params.c:231
#define LEN
Definition: resolve.c:136
#define domain_list_init(o, f, p)
Definition: domain_list.h:24
#define MAIL_ATTR_NEXTHOP
Definition: mail_proto.h:148
int vstream_fflush(VSTREAM *stream)
Definition: vstream.c:1257
char * var_virt_mailbox_doms
Definition: proxymap.c:259
void resolve_init(void)
Definition: resolve.c:798
int error
Definition: maps.h:25
MAPS * snd_def_xp_info
TOK822 * tok822_alloc(int, const char *)
Definition: tok822_node.c:55
TOK822 * tok822_free(TOK822 *)
Definition: tok822_node.c:73
#define TOK822_STR_DEFL
Definition: tok822.h:91
bool var_allow_min_user
#define quote_822_local(dst, src)
bool var_resolve_num_dom
const char * transport_maps_name
#define RESOLVE_CLASS_RELAY
Definition: resolve_clnt.h:33
#define TOK822_QSTRING
Definition: tok822.h:43
int strcasecmp(const char *s1, const char *s2)
Definition: strcasecmp.c:41
char ** local_transport
#define RESOLVE_CLASS_ALIAS
Definition: resolve_clnt.h:31
#define MAIL_ATTR_RECIP
Definition: mail_proto.h:134
TOK822 * tok822_scan_addr(const char *)
Definition: tok822_parse.c:648
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
#define MAIL_ATTR_SENDER
Definition: mail_proto.h:131
char * split_at(char *string, int delimiter)
Definition: split_at.c:53
char * var_null_relay_maps_key
#define RESOLVE_CLASS_DEFAULT
Definition: resolve_clnt.h:34
int server_flags
char * var_myhostname
Definition: mail_params.c:223
const char * def_transport_name
#define VAR_MYDEST
Definition: mail_params.h:133
#define attr_scan
Definition: attr.h:111
char ** relayhost
const char * virt_transport_name
struct TOK822 * prev
Definition: tok822.h:30
#define SEND_ATTR_STR(name, val)
Definition: attr.h:64
#define MAIL_ATTR_FLAGS
Definition: mail_proto.h:128
#define RES_PARAM_VALUE(x)
VSTRING * vstring_strcat(VSTRING *vp, const char *src)
Definition: vstring.c:459
struct TOK822 * next
Definition: tok822.h:31
const char * snd_relay_maps_name
#define RECV_ATTR_STR(name, val)
Definition: attr.h:72
#define DONT_GRIPE
Definition: haproxy_srvr.h:31
#define ATTR_FLAG_STRICT
Definition: attr.h:103
void msg_info(const char *fmt,...)
Definition: msg.c:199
struct TOK822 * tail
Definition: tok822.h:33