Expand PMF_FN_* macros.
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / trivial-rewrite / resolve.c
blobe68d28422b7a01943aadbdf41fb5aad6fa6cabb3
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* resolve 3
6 /* SUMMARY
7 /* mail address resolver
8 /* SYNOPSIS
9 /* #include "trivial-rewrite.h"
11 /* void resolve_init(void)
13 /* void resolve_proto(context, stream)
14 /* RES_CONTEXT *context;
15 /* VSTREAM *stream;
16 /* DESCRIPTION
17 /* This module implements the trivial address resolving engine.
18 /* It distinguishes between local and remote mail, and optionally
19 /* consults one or more transport tables that map a destination
20 /* to a transport, nexthop pair.
22 /* resolve_init() initializes data structures that are private
23 /* to this module. It should be called once before using the
24 /* actual resolver routines.
26 /* resolve_proto() implements the client-server protocol:
27 /* read one address in FQDN form, reply with a (transport,
28 /* nexthop, internalized recipient) triple.
29 /* STANDARDS
30 /* DIAGNOSTICS
31 /* Problems and transactions are logged to the syslog daemon.
32 /* BUGS
33 /* SEE ALSO
34 /* LICENSE
35 /* .ad
36 /* .fi
37 /* The Secure Mailer license must be distributed with this software.
38 /* AUTHOR(S)
39 /* Wietse Venema
40 /* IBM T.J. Watson Research
41 /* P.O. Box 704
42 /* Yorktown Heights, NY 10598, USA
43 /*--*/
45 /* System library. */
47 #include <sys_defs.h>
48 #include <stdlib.h>
49 #include <string.h>
51 #ifdef STRCASECMP_IN_STRINGS_H
52 #include <strings.h>
53 #endif
55 /* Utility library. */
57 #include <msg.h>
58 #include <vstring.h>
59 #include <vstream.h>
60 #include <vstring_vstream.h>
61 #include <split_at.h>
62 #include <valid_hostname.h>
63 #include <stringops.h>
64 #include <mymalloc.h>
66 /* Global library. */
68 #include <mail_params.h>
69 #include <mail_proto.h>
70 #include <resolve_local.h>
71 #include <mail_conf.h>
72 #include <quote_822_local.h>
73 #include <tok822.h>
74 #include <domain_list.h>
75 #include <string_list.h>
76 #include <match_parent_style.h>
77 #include <maps.h>
78 #include <mail_addr_find.h>
79 #include <valid_mailhost_addr.h>
81 /* Application-specific. */
83 #include "trivial-rewrite.h"
84 #include "transport.h"
87 * The job of the address resolver is to map one recipient address to a
88 * triple of (channel, nexthop, recipient). The channel is the name of the
89 * delivery service specified in master.cf, the nexthop is (usually) a
90 * description of the next host to deliver to, and recipient is the final
91 * recipient address. The latter may differ from the input address as the
92 * result of stripping multiple layers of sender-specified routing.
94 * Addresses are resolved by their domain name. Known domain names are
95 * categorized into classes: local, virtual alias, virtual mailbox, relay,
96 * and everything else. Finding the address domain class is a matter of
97 * table lookups.
99 * Different address domain classes generally use different delivery channels,
100 * and may use class dependent ways to arrive at the corresponding nexthop
101 * information. With classes that do final delivery, the nexthop is
102 * typically the local machine hostname.
104 * The transport lookup table provides a means to override the domain class
105 * channel and/or nexhop information for specific recipients or for entire
106 * domain hierarchies.
108 * This works well in the general case. The only bug in this approach is that
109 * the structure of the nexthop information is transport dependent.
110 * Typically, the nexthop specifies a hostname, hostname + TCP Port, or the
111 * pathname of a UNIX-domain socket. However, with the error transport the
112 * nexthop field contains free text with the reason for non-delivery.
114 * Therefore, a transport map entry that overrides the channel but not the
115 * nexthop information (or vice versa) may produce surprising results. In
116 * particular, the free text nexthop information for the error transport is
117 * likely to confuse regular delivery agents; and conversely, a hostname or
118 * socket pathname is not an adequate text as reason for non-delivery.
120 * In the code below, rcpt_domain specifies the domain name that we will use
121 * when the transport table specifies a non-default channel but no nexthop
122 * information (we use a generic text when that non-default channel is the
123 * error transport).
126 #define STR vstring_str
129 * Some of the lists that define the address domain classes.
131 static DOMAIN_LIST *relay_domains;
132 static STRING_LIST *virt_alias_doms;
133 static STRING_LIST *virt_mailbox_doms;
135 static MAPS *relocated_maps;
137 /* resolve_addr - resolve address according to rule set */
139 static void resolve_addr(RES_CONTEXT *rp, char *sender, char *addr,
140 VSTRING *channel, VSTRING *nexthop,
141 VSTRING *nextrcpt, int *flags)
143 const char *myname = "resolve_addr";
144 VSTRING *addr_buf = vstring_alloc(100);
145 TOK822 *tree = 0;
146 TOK822 *saved_domain = 0;
147 TOK822 *domain = 0;
148 char *destination;
149 const char *blame = 0;
150 const char *rcpt_domain;
151 ssize_t addr_len;
152 ssize_t loop_count;
153 ssize_t loop_max;
154 char *local;
155 char *oper;
156 char *junk;
157 const char *relay;
159 *flags = 0;
160 vstring_strcpy(channel, "CHANNEL NOT UPDATED");
161 vstring_strcpy(nexthop, "NEXTHOP NOT UPDATED");
162 vstring_strcpy(nextrcpt, "NEXTRCPT NOT UPDATED");
165 * The address is in internalized (unquoted) form.
167 * In an ideal world we would parse the externalized address form as given
168 * to us by the sender.
170 * However, in the real world we have to look for routing characters like
171 * %@! in the address local-part, even when that information is quoted
172 * due to the presence of special characters or whitespace. Although
173 * technically incorrect, this is needed to stop user@domain@domain relay
174 * attempts when forwarding mail to a Sendmail MX host.
176 * This suggests that we parse the address in internalized (unquoted) form.
177 * Unfortunately, if we do that, the unparser generates incorrect white
178 * space between adjacent non-operator tokens. Example: ``first last''
179 * needs white space, but ``stuff[stuff]'' does not. This is is not a
180 * problem when unparsing the result from parsing externalized forms,
181 * because the parser/unparser were designed for valid externalized forms
182 * where ``stuff[stuff]'' does not happen.
184 * As a workaround we start with the quoted form and then dequote the
185 * local-part only where needed. This will do the right thing in most
186 * (but not all) cases.
188 addr_len = strlen(addr);
189 quote_822_local(addr_buf, addr);
190 tree = tok822_scan_addr(vstring_str(addr_buf));
193 * The optimizer will eliminate tests that always fail, and will replace
194 * multiple expansions of this macro by a GOTO to a single instance.
196 #define FREE_MEMORY_AND_RETURN { \
197 if (saved_domain) \
198 tok822_free_tree(saved_domain); \
199 if(tree) \
200 tok822_free_tree(tree); \
201 if (addr_buf) \
202 vstring_free(addr_buf); \
203 return; \
207 * Preliminary resolver: strip off all instances of the local domain.
208 * Terminate when no destination domain is left over, or when the
209 * destination domain is remote.
211 * XXX To whom it may concern. If you change the resolver loop below, or
212 * quote_822_local.c, or tok822_parse.c, be sure to re-run the tests
213 * under "make resolve_clnt_test" in the global directory.
215 #define RESOLVE_LOCAL(domain) \
216 resolve_local(STR(tok822_internalize(addr_buf, domain, TOK822_STR_DEFL)))
218 dict_errno = 0;
220 for (loop_count = 0, loop_max = addr_len + 100; /* void */ ; loop_count++) {
223 * Grr. resolve_local() table lookups may fail. It may be OK for
224 * local file lookup code to abort upon failure, but with
225 * network-based tables it is preferable to return an error
226 * indication to the requestor.
228 if (dict_errno) {
229 *flags |= RESOLVE_FLAG_FAIL;
230 FREE_MEMORY_AND_RETURN;
234 * XXX Should never happen, but if this happens with some
235 * pathological address, then that is not sufficient reason to
236 * disrupt the operation of an MTA.
238 if (loop_count > loop_max) {
239 msg_warn("resolve_addr: <%s>: giving up after %ld iterations",
240 addr, (long) loop_count);
241 break;
245 * Strip trailing dot at end of domain, but not dot-dot or at-dot.
246 * This merely makes diagnostics more accurate by leaving bogus
247 * addresses alone.
249 if (tree->tail
250 && tree->tail->type == '.'
251 && tok822_rfind_type(tree->tail, '@') != 0
252 && tree->tail->prev->type != '.'
253 && tree->tail->prev->type != '@')
254 tok822_free_tree(tok822_sub_keep_before(tree, tree->tail));
257 * Strip trailing @.
259 if (var_resolve_nulldom
260 && tree->tail
261 && tree->tail->type == '@')
262 tok822_free_tree(tok822_sub_keep_before(tree, tree->tail));
265 * Strip (and save) @domain if local.
267 if ((domain = tok822_rfind_type(tree->tail, '@')) != 0) {
268 if (domain->next && RESOLVE_LOCAL(domain->next) == 0)
269 break;
270 tok822_sub_keep_before(tree, domain);
271 if (saved_domain)
272 tok822_free_tree(saved_domain);
273 saved_domain = domain;
274 domain = 0; /* safety for future change */
278 * After stripping the local domain, if any, replace foo%bar by
279 * foo@bar, site!user by user@site, rewrite to canonical form, and
280 * retry.
282 if (tok822_rfind_type(tree->tail, '@')
283 || (var_swap_bangpath && tok822_rfind_type(tree->tail, '!'))
284 || (var_percent_hack && tok822_rfind_type(tree->tail, '%'))) {
285 rewrite_tree(&local_context, tree);
286 continue;
290 * If the local-part is a quoted string, crack it open when we're
291 * permitted to do so and look for routing operators. This is
292 * technically incorrect, but is needed to stop relaying problems.
294 * XXX Do another feeble attempt to keep local-part info quoted.
296 if (var_resolve_dequoted
297 && tree->head && tree->head == tree->tail
298 && tree->head->type == TOK822_QSTRING
299 && ((oper = strrchr(local = STR(tree->head->vstr), '@')) != 0
300 || (var_percent_hack && (oper = strrchr(local, '%')) != 0)
301 || (var_swap_bangpath && (oper = strrchr(local, '!')) != 0))) {
302 if (*oper == '%')
303 *oper = '@';
304 tok822_internalize(addr_buf, tree->head, TOK822_STR_DEFL);
305 if (*oper == '@') {
306 junk = mystrdup(STR(addr_buf));
307 quote_822_local(addr_buf, junk);
308 myfree(junk);
310 tok822_free(tree->head);
311 tree->head = tok822_scan(STR(addr_buf), &tree->tail);
312 rewrite_tree(&local_context, tree);
313 continue;
317 * An empty local-part or an empty quoted string local-part becomes
318 * the local MAILER-DAEMON, for consistency with our own From:
319 * message headers.
321 if (tree->head && tree->head == tree->tail
322 && tree->head->type == TOK822_QSTRING
323 && VSTRING_LEN(tree->head->vstr) == 0) {
324 tok822_free(tree->head);
325 tree->head = 0;
327 /* XXX must be localpart only, not user@domain form. */
328 if (tree->head == 0)
329 tree->head = tok822_scan(var_empty_addr, &tree->tail);
332 * We're done. There are no domains left to strip off the address,
333 * and all null local-part information is sanitized.
335 domain = 0;
336 break;
339 vstring_free(addr_buf);
340 addr_buf = 0;
343 * Make sure the resolved envelope recipient has the user@domain form. If
344 * no domain was specified in the address, assume the local machine. See
345 * above for what happens with an empty address.
347 if (domain == 0) {
348 if (saved_domain) {
349 tok822_sub_append(tree, saved_domain);
350 saved_domain = 0;
351 } else {
352 tok822_sub_append(tree, tok822_alloc('@', (char *) 0));
353 tok822_sub_append(tree, tok822_scan(var_myhostname, (TOK822 **) 0));
358 * Transform the recipient address back to internal form.
360 * XXX This may produce incorrect results if we cracked open a quoted
361 * local-part with routing operators; see discussion above at the top of
362 * the big loop.
364 * XXX We explicitly disallow domain names in bare network address form. A
365 * network address destination should be formatted according to RFC 2821:
366 * it should be enclosed in [], and an IPv6 address should have an IPv6:
367 * prefix.
369 tok822_internalize(nextrcpt, tree, TOK822_STR_DEFL);
370 rcpt_domain = strrchr(STR(nextrcpt), '@') + 1;
371 if (rcpt_domain == 0)
372 msg_panic("no @ in address: \"%s\"", STR(nextrcpt));
373 if (*rcpt_domain == '[') {
374 if (!valid_mailhost_literal(rcpt_domain, DONT_GRIPE))
375 *flags |= RESOLVE_FLAG_ERROR;
376 } else if (!valid_hostname(rcpt_domain, DONT_GRIPE)) {
377 if (var_resolve_num_dom && valid_hostaddr(rcpt_domain, DONT_GRIPE)) {
378 vstring_insert(nextrcpt, rcpt_domain - STR(nextrcpt), "[", 1);
379 vstring_strcat(nextrcpt, "]");
380 rcpt_domain = strrchr(STR(nextrcpt), '@') + 1;
381 if (resolve_local(rcpt_domain)) /* XXX */
382 domain = 0;
383 } else {
384 *flags |= RESOLVE_FLAG_ERROR;
387 tok822_free_tree(tree);
388 tree = 0;
391 * XXX Short-cut invalid address forms.
393 if (*flags & RESOLVE_FLAG_ERROR) {
394 *flags |= RESOLVE_CLASS_DEFAULT;
395 FREE_MEMORY_AND_RETURN;
399 * Recognize routing operators in the local-part, even when we do not
400 * recognize ! or % as valid routing operators locally. This is needed to
401 * prevent backup MX hosts from relaying third-party destinations through
402 * primary MX hosts, otherwise the backup host could end up on black
403 * lists. Ignore local swap_bangpath and percent_hack settings because we
404 * can't know how the next MX host is set up.
406 if (strcmp(STR(nextrcpt) + strcspn(STR(nextrcpt), "@!%") + 1, rcpt_domain))
407 *flags |= RESOLVE_FLAG_ROUTED;
410 * With local, virtual, relay, or other non-local destinations, give the
411 * highest precedence to transport associated nexthop information.
413 * Otherwise, with relay or other non-local destinations, the relayhost
414 * setting overrides the recipient domain name, and the sender-dependent
415 * relayhost overrides both.
417 * XXX Nag if the recipient domain is listed in multiple domain lists. The
418 * result is implementation defined, and may break when internals change.
420 * For now, we distinguish only a fixed number of address classes.
421 * Eventually this may become extensible, so that new classes can be
422 * configured with their own domain list, delivery transport, and
423 * recipient table.
425 #define STREQ(x,y) (strcmp((x), (y)) == 0)
427 dict_errno = 0;
428 if (domain != 0) {
431 * Virtual alias domain.
433 if (virt_alias_doms
434 && string_list_match(virt_alias_doms, rcpt_domain)) {
435 if (var_helpful_warnings) {
436 if (virt_mailbox_doms
437 && string_list_match(virt_mailbox_doms, rcpt_domain))
438 msg_warn("do not list domain %s in BOTH %s and %s",
439 rcpt_domain, VAR_VIRT_ALIAS_DOMS,
440 VAR_VIRT_MAILBOX_DOMS);
441 if (relay_domains
442 && domain_list_match(relay_domains, rcpt_domain))
443 msg_warn("do not list domain %s in BOTH %s and %s",
444 rcpt_domain, VAR_VIRT_ALIAS_DOMS,
445 VAR_RELAY_DOMAINS);
446 #if 0
447 if (strcasecmp(rcpt_domain, var_myorigin) == 0)
448 msg_warn("do not list $%s (%s) in %s",
449 VAR_MYORIGIN, var_myorigin, VAR_VIRT_ALIAS_DOMS);
450 #endif
452 vstring_strcpy(channel, MAIL_SERVICE_ERROR);
453 vstring_sprintf(nexthop, "User unknown%s",
454 var_show_unk_rcpt_table ?
455 " in virtual alias table" : "");
456 *flags |= RESOLVE_CLASS_ALIAS;
457 } else if (dict_errno != 0) {
458 msg_warn("%s lookup failure", VAR_VIRT_ALIAS_DOMS);
459 *flags |= RESOLVE_FLAG_FAIL;
460 FREE_MEMORY_AND_RETURN;
464 * Virtual mailbox domain.
466 else if (virt_mailbox_doms
467 && string_list_match(virt_mailbox_doms, rcpt_domain)) {
468 if (var_helpful_warnings) {
469 if (relay_domains
470 && domain_list_match(relay_domains, rcpt_domain))
471 msg_warn("do not list domain %s in BOTH %s and %s",
472 rcpt_domain, VAR_VIRT_MAILBOX_DOMS,
473 VAR_RELAY_DOMAINS);
475 vstring_strcpy(channel, RES_PARAM_VALUE(rp->virt_transport));
476 vstring_strcpy(nexthop, rcpt_domain);
477 blame = rp->virt_transport_name;
478 *flags |= RESOLVE_CLASS_VIRTUAL;
479 } else if (dict_errno != 0) {
480 msg_warn("%s lookup failure", VAR_VIRT_MAILBOX_DOMS);
481 *flags |= RESOLVE_FLAG_FAIL;
482 FREE_MEMORY_AND_RETURN;
483 } else {
486 * Off-host relay destination.
488 if (relay_domains
489 && domain_list_match(relay_domains, rcpt_domain)) {
490 vstring_strcpy(channel, RES_PARAM_VALUE(rp->relay_transport));
491 blame = rp->relay_transport_name;
492 *flags |= RESOLVE_CLASS_RELAY;
493 } else if (dict_errno != 0) {
494 msg_warn("%s lookup failure", VAR_RELAY_DOMAINS);
495 *flags |= RESOLVE_FLAG_FAIL;
496 FREE_MEMORY_AND_RETURN;
500 * Other off-host destination.
502 else {
503 vstring_strcpy(channel, RES_PARAM_VALUE(rp->def_transport));
504 blame = rp->def_transport_name;
505 *flags |= RESOLVE_CLASS_DEFAULT;
509 * With off-host delivery, sender-dependent or global relayhost
510 * override the recipient domain.
512 if (rp->snd_relay_info
513 && (relay = mail_addr_find(rp->snd_relay_info, *sender ?
514 sender : var_null_relay_maps_key,
515 (char **) 0)) != 0)
516 vstring_strcpy(nexthop, strcasecmp(relay, "DUNNO") == 0 ?
517 rcpt_domain : relay);
518 else if (*RES_PARAM_VALUE(rp->relayhost))
519 vstring_strcpy(nexthop, RES_PARAM_VALUE(rp->relayhost));
520 else
521 vstring_strcpy(nexthop, rcpt_domain);
526 * Local delivery.
528 * XXX Nag if the domain is listed in multiple domain lists. The effect is
529 * implementation defined, and may break when internals change.
531 else {
532 if (var_helpful_warnings) {
533 if (virt_alias_doms
534 && string_list_match(virt_alias_doms, rcpt_domain))
535 msg_warn("do not list domain %s in BOTH %s and %s",
536 rcpt_domain, VAR_MYDEST, VAR_VIRT_ALIAS_DOMS);
537 if (virt_mailbox_doms
538 && string_list_match(virt_mailbox_doms, rcpt_domain))
539 msg_warn("do not list domain %s in BOTH %s and %s",
540 rcpt_domain, VAR_MYDEST, VAR_VIRT_MAILBOX_DOMS);
542 vstring_strcpy(channel, RES_PARAM_VALUE(rp->local_transport));
543 vstring_strcpy(nexthop, rcpt_domain);
544 blame = rp->local_transport_name;
545 *flags |= RESOLVE_CLASS_LOCAL;
549 * An explicit main.cf transport:nexthop setting overrides the nexthop.
551 * XXX We depend on this mechanism to enforce per-recipient concurrencies
552 * for local recipients. With "local_transport = local:$myhostname" we
553 * force mail for any domain in $mydestination/${proxy,inet}_interfaces
554 * to share the same queue.
556 if ((destination = split_at(STR(channel), ':')) != 0 && *destination)
557 vstring_strcpy(nexthop, destination);
560 * Sanity checks.
562 if (*STR(channel) == 0) {
563 if (blame == 0)
564 msg_panic("%s: null blame", myname);
565 msg_warn("file %s/%s: parameter %s: null transport is not allowed",
566 var_config_dir, MAIN_CONF_FILE, blame);
567 *flags |= RESOLVE_FLAG_FAIL;
568 FREE_MEMORY_AND_RETURN;
570 if (*STR(nexthop) == 0)
571 msg_panic("%s: null nexthop", myname);
574 * The transport map can selectively override any transport and/or
575 * nexthop host info that is set up above. Unfortunately, the syntax for
576 * nexthop information is transport specific. We therefore need sane and
577 * intuitive semantics for transport map entries that specify a channel
578 * but no nexthop.
580 * With non-error transports, the initial nexthop information is the
581 * recipient domain. However, specific main.cf transport definitions may
582 * specify a transport-specific destination, such as a host + TCP socket,
583 * or the pathname of a UNIX-domain socket. With less precedence than
584 * main.cf transport definitions, a main.cf relayhost definition may also
585 * override nexthop information for off-host deliveries.
587 * With the error transport, the nexthop information is free text that
588 * specifies the reason for non-delivery.
590 * Because nexthop syntax is transport specific we reset the nexthop
591 * information to the recipient domain when the transport table specifies
592 * a transport without also specifying the nexthop information.
594 * Subtle note: reset nexthop even when the transport table does not change
595 * the transport. Otherwise it is hard to get rid of main.cf specified
596 * nexthop information.
598 * XXX Don't override the virtual alias class (error:User unknown) result.
600 if (rp->transport_info && !(*flags & RESOLVE_CLASS_ALIAS)) {
601 if (transport_lookup(rp->transport_info, STR(nextrcpt),
602 rcpt_domain, channel, nexthop) == 0
603 && dict_errno != 0) {
604 msg_warn("%s lookup failure", rp->transport_maps_name);
605 *flags |= RESOLVE_FLAG_FAIL;
606 FREE_MEMORY_AND_RETURN;
611 * Bounce recipients that have moved, regardless of domain address class.
612 * We do this last, in anticipation of transport maps that can override
613 * the recipient address.
615 * The downside of not doing this in delivery agents is that this table has
616 * no effect on local alias expansion results. Such mail will have to
617 * make almost an entire iteration through the mail system.
619 #define IGNORE_ADDR_EXTENSION ((char **) 0)
621 if (relocated_maps != 0) {
622 const char *newloc;
624 if ((newloc = mail_addr_find(relocated_maps, STR(nextrcpt),
625 IGNORE_ADDR_EXTENSION)) != 0) {
626 vstring_strcpy(channel, MAIL_SERVICE_ERROR);
627 /* 5.1.6 is the closest match, but not perfect. */
628 vstring_sprintf(nexthop, "5.1.6 User has moved to %s", newloc);
629 } else if (dict_errno != 0) {
630 msg_warn("%s lookup failure", VAR_RELOCATED_MAPS);
631 *flags |= RESOLVE_FLAG_FAIL;
632 FREE_MEMORY_AND_RETURN;
637 * Bounce recipient addresses that start with `-'. External commands may
638 * misinterpret such addresses as command-line options.
640 * In theory I could say people should always carefully set up their
641 * master.cf pipe mailer entries with `--' before the first non-option
642 * argument, but mistakes will happen regardless.
644 * Therefore the protection is put in place here, where it cannot be
645 * bypassed.
647 if (var_allow_min_user == 0 && STR(nextrcpt)[0] == '-') {
648 *flags |= RESOLVE_FLAG_ERROR;
649 FREE_MEMORY_AND_RETURN;
653 * Clean up.
655 FREE_MEMORY_AND_RETURN;
658 /* Static, so they can be used by the network protocol interface only. */
660 static VSTRING *channel;
661 static VSTRING *nexthop;
662 static VSTRING *nextrcpt;
663 static VSTRING *query;
664 static VSTRING *sender;
666 /* resolve_proto - read request and send reply */
668 int resolve_proto(RES_CONTEXT *context, VSTREAM *stream)
670 int flags;
672 if (attr_scan(stream, ATTR_FLAG_STRICT,
673 ATTR_TYPE_STR, MAIL_ATTR_SENDER, sender,
674 ATTR_TYPE_STR, MAIL_ATTR_ADDR, query,
675 ATTR_TYPE_END) != 2)
676 return (-1);
678 resolve_addr(context, STR(sender), STR(query),
679 channel, nexthop, nextrcpt, &flags);
681 if (msg_verbose)
682 msg_info("`%s' -> `%s' -> (`%s' `%s' `%s' `%d')",
683 STR(sender), STR(query), STR(channel),
684 STR(nexthop), STR(nextrcpt), flags);
686 attr_print(stream, ATTR_FLAG_NONE,
687 ATTR_TYPE_INT, MAIL_ATTR_FLAGS, server_flags,
688 ATTR_TYPE_STR, MAIL_ATTR_TRANSPORT, STR(channel),
689 ATTR_TYPE_STR, MAIL_ATTR_NEXTHOP, STR(nexthop),
690 ATTR_TYPE_STR, MAIL_ATTR_RECIP, STR(nextrcpt),
691 ATTR_TYPE_INT, MAIL_ATTR_FLAGS, flags,
692 ATTR_TYPE_END);
694 if (vstream_fflush(stream) != 0) {
695 msg_warn("write resolver reply: %m");
696 return (-1);
698 return (0);
701 /* resolve_init - module initializations */
703 void resolve_init(void)
705 sender = vstring_alloc(100);
706 query = vstring_alloc(100);
707 channel = vstring_alloc(100);
708 nexthop = vstring_alloc(100);
709 nextrcpt = vstring_alloc(100);
711 if (*var_virt_alias_doms)
712 virt_alias_doms =
713 string_list_init(MATCH_FLAG_NONE, var_virt_alias_doms);
715 if (*var_virt_mailbox_doms)
716 virt_mailbox_doms =
717 string_list_init(MATCH_FLAG_NONE, var_virt_mailbox_doms);
719 if (*var_relay_domains)
720 relay_domains =
721 domain_list_init(match_parent_style(VAR_RELAY_DOMAINS),
722 var_relay_domains);
724 if (*var_relocated_maps)
725 relocated_maps =
726 maps_create(VAR_RELOCATED_MAPS, var_relocated_maps,
727 DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX);