From b5896ba9f6047e7031e2bdee0622d543e11a6734 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 6 May 2024 03:46:30 +0200 Subject: Adding upstream version 3.4.23. Signed-off-by: Daniel Baumann --- src/trivial-rewrite/.indent.pro | 1 + src/trivial-rewrite/.printfck | 25 + src/trivial-rewrite/Makefile.in | 199 ++++++++ src/trivial-rewrite/resolve.c | 828 ++++++++++++++++++++++++++++++++++ src/trivial-rewrite/rewrite.c | 303 +++++++++++++ src/trivial-rewrite/transport.c | 438 ++++++++++++++++++ src/trivial-rewrite/transport.h | 51 +++ src/trivial-rewrite/transport.in | 45 ++ src/trivial-rewrite/transport.ref | 22 + src/trivial-rewrite/trivial-rewrite.c | 652 ++++++++++++++++++++++++++ src/trivial-rewrite/trivial-rewrite.h | 87 ++++ 11 files changed, 2651 insertions(+) create mode 120000 src/trivial-rewrite/.indent.pro create mode 100644 src/trivial-rewrite/.printfck create mode 100644 src/trivial-rewrite/Makefile.in create mode 100644 src/trivial-rewrite/resolve.c create mode 100644 src/trivial-rewrite/rewrite.c create mode 100644 src/trivial-rewrite/transport.c create mode 100644 src/trivial-rewrite/transport.h create mode 100644 src/trivial-rewrite/transport.in create mode 100644 src/trivial-rewrite/transport.ref create mode 100644 src/trivial-rewrite/trivial-rewrite.c create mode 100644 src/trivial-rewrite/trivial-rewrite.h (limited to 'src/trivial-rewrite') diff --git a/src/trivial-rewrite/.indent.pro b/src/trivial-rewrite/.indent.pro new file mode 120000 index 0000000..5c837ec --- /dev/null +++ b/src/trivial-rewrite/.indent.pro @@ -0,0 +1 @@ +../../.indent.pro \ No newline at end of file diff --git a/src/trivial-rewrite/.printfck b/src/trivial-rewrite/.printfck new file mode 100644 index 0000000..66016ed --- /dev/null +++ b/src/trivial-rewrite/.printfck @@ -0,0 +1,25 @@ +been_here_xt 2 0 +bounce_append 5 0 +cleanup_out_format 1 0 +defer_append 5 0 +mail_command 1 0 +mail_print 1 0 +msg_error 0 0 +msg_fatal 0 0 +msg_info 0 0 +msg_panic 0 0 +msg_warn 0 0 +opened 4 0 +post_mail_fprintf 1 0 +qmgr_message_bounce 2 0 +rec_fprintf 2 0 +sent 4 0 +smtp_cmd 1 0 +smtp_mesg_fail 2 0 +smtp_printf 1 0 +smtp_rcpt_fail 3 0 +smtp_site_fail 2 0 +udp_syslog 1 0 +vstream_fprintf 1 0 +vstream_printf 0 0 +vstring_sprintf 1 0 diff --git a/src/trivial-rewrite/Makefile.in b/src/trivial-rewrite/Makefile.in new file mode 100644 index 0000000..92fa666 --- /dev/null +++ b/src/trivial-rewrite/Makefile.in @@ -0,0 +1,199 @@ +SHELL = /bin/sh +SRCS = trivial-rewrite.c rewrite.c resolve.c transport.c +OBJS = trivial-rewrite.o rewrite.o resolve.o transport.o +HDRS = +TESTSRC = +DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) +CFLAGS = $(DEBUG) $(OPT) $(DEFS) +LIB = +TESTPROG= transport +PROG = trivial-rewrite +LIBS = ../../lib/lib$(LIB_PREFIX)master$(LIB_SUFFIX) \ + ../../lib/lib$(LIB_PREFIX)global$(LIB_SUFFIX) \ + ../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX) +LIB_DIR = ../../lib +INC_DIR = ../../include +BIN_DIR = ../../libexec + +.c.o:; $(CC) $(CFLAGS) -c $*.c + +all: $(PROG) $(LIB) + +$(PROG): $(OBJS) $(LIBS) + $(CC) $(CFLAGS) $(SHLIB_RPATH) -o $@ $(OBJS) $(LIBS) $(SYSLIBS) + +$(OBJS): ../../conf/makedefs.out + +Makefile: Makefile.in + cat ../../conf/makedefs.out $? >$@ + +test: $(TESTPROG) + +tests: transport_test + +root_tests: + +$(BIN_DIR)/$(PROG): $(PROG) + cp $(PROG) $@ + +update: $(BIN_DIR)/$(PROG) + +transport: transport.c $(LIB) $(LIBS) + -mv $@.o junk + $(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS) + mv junk $@.o + +transport_test: transport transport.in transport.ref + $(SHLIB_ENV) sh transport.in >transport.tmp 2>&1 + diff transport.ref transport.tmp + rm -f transport.tmp + +printfck: $(OBJS) $(PROG) + rm -rf printfck + mkdir printfck + cp *.h printfck + sed '1,/^# do not edit/!d' Makefile >printfck/Makefile + set -e; for i in *.c; do printfck -f .printfck $$i >printfck/$$i; done + cd printfck; make "INC_DIR=../../../include" `cd ..; ls *.o` + +lint: + lint $(DEFS) $(SRCS) $(LINTFIX) + +clean: + rm -f *.o *core trivial-rewrite $(TESTPROG) junk $(LIB) + rm -rf printfck + +tidy: clean + +depend: $(MAKES) + (sed '1,/^# do not edit/!d' Makefile.in; \ + set -e; for i in [a-z][a-z0-9]*.c; do \ + $(CC) -E $(DEFS) $(INCL) $$i | grep -v '[<>]' | sed -n -e '/^# *1 *"\([^"]*\)".*/{' \ + -e 's//'`echo $$i|sed 's/c$$/o/'`': \1/' \ + -e 's/o: \.\//o: /' -e p -e '}' ; \ + done | LANG=C sort -u) | grep -v '[.][o][:][ ][/]' >$$$$ && mv $$$$ Makefile.in + @$(EXPORT) make -f Makefile.in Makefile 1>&2 + +# do not edit below this line - it is generated by 'make depend' +resolve.o: ../../include/argv.h +resolve.o: ../../include/attr.h +resolve.o: ../../include/check_arg.h +resolve.o: ../../include/dict.h +resolve.o: ../../include/domain_list.h +resolve.o: ../../include/htable.h +resolve.o: ../../include/iostuff.h +resolve.o: ../../include/mail_addr_find.h +resolve.o: ../../include/mail_addr_form.h +resolve.o: ../../include/mail_conf.h +resolve.o: ../../include/mail_params.h +resolve.o: ../../include/mail_proto.h +resolve.o: ../../include/maps.h +resolve.o: ../../include/match_list.h +resolve.o: ../../include/match_parent_style.h +resolve.o: ../../include/msg.h +resolve.o: ../../include/myflock.h +resolve.o: ../../include/mymalloc.h +resolve.o: ../../include/nvtable.h +resolve.o: ../../include/quote_822_local.h +resolve.o: ../../include/quote_flags.h +resolve.o: ../../include/resolve_clnt.h +resolve.o: ../../include/resolve_local.h +resolve.o: ../../include/split_at.h +resolve.o: ../../include/string_list.h +resolve.o: ../../include/stringops.h +resolve.o: ../../include/sys_defs.h +resolve.o: ../../include/tok822.h +resolve.o: ../../include/valid_hostname.h +resolve.o: ../../include/valid_mailhost_addr.h +resolve.o: ../../include/valid_utf8_hostname.h +resolve.o: ../../include/vbuf.h +resolve.o: ../../include/vstream.h +resolve.o: ../../include/vstring.h +resolve.o: ../../include/vstring_vstream.h +resolve.o: resolve.c +resolve.o: transport.h +resolve.o: trivial-rewrite.h +rewrite.o: ../../include/argv.h +rewrite.o: ../../include/attr.h +rewrite.o: ../../include/check_arg.h +rewrite.o: ../../include/dict.h +rewrite.o: ../../include/htable.h +rewrite.o: ../../include/iostuff.h +rewrite.o: ../../include/mail_conf.h +rewrite.o: ../../include/mail_params.h +rewrite.o: ../../include/mail_proto.h +rewrite.o: ../../include/maps.h +rewrite.o: ../../include/msg.h +rewrite.o: ../../include/myflock.h +rewrite.o: ../../include/mymalloc.h +rewrite.o: ../../include/nvtable.h +rewrite.o: ../../include/resolve_clnt.h +rewrite.o: ../../include/resolve_local.h +rewrite.o: ../../include/split_at.h +rewrite.o: ../../include/sys_defs.h +rewrite.o: ../../include/tok822.h +rewrite.o: ../../include/vbuf.h +rewrite.o: ../../include/vstream.h +rewrite.o: ../../include/vstring.h +rewrite.o: ../../include/vstring_vstream.h +rewrite.o: rewrite.c +rewrite.o: trivial-rewrite.h +transport.o: ../../include/argv.h +transport.o: ../../include/attr.h +transport.o: ../../include/check_arg.h +transport.o: ../../include/dict.h +transport.o: ../../include/events.h +transport.o: ../../include/htable.h +transport.o: ../../include/iostuff.h +transport.o: ../../include/mail_addr_find.h +transport.o: ../../include/mail_addr_form.h +transport.o: ../../include/mail_params.h +transport.o: ../../include/mail_proto.h +transport.o: ../../include/maps.h +transport.o: ../../include/match_list.h +transport.o: ../../include/match_parent_style.h +transport.o: ../../include/msg.h +transport.o: ../../include/myflock.h +transport.o: ../../include/mymalloc.h +transport.o: ../../include/nvtable.h +transport.o: ../../include/split_at.h +transport.o: ../../include/stringops.h +transport.o: ../../include/strip_addr.h +transport.o: ../../include/sys_defs.h +transport.o: ../../include/vbuf.h +transport.o: ../../include/vstream.h +transport.o: ../../include/vstring.h +transport.o: transport.c +transport.o: transport.h +trivial-rewrite.o: ../../include/argv.h +trivial-rewrite.o: ../../include/attr.h +trivial-rewrite.o: ../../include/check_arg.h +trivial-rewrite.o: ../../include/dict.h +trivial-rewrite.o: ../../include/events.h +trivial-rewrite.o: ../../include/htable.h +trivial-rewrite.o: ../../include/iostuff.h +trivial-rewrite.o: ../../include/mail_addr.h +trivial-rewrite.o: ../../include/mail_conf.h +trivial-rewrite.o: ../../include/mail_params.h +trivial-rewrite.o: ../../include/mail_proto.h +trivial-rewrite.o: ../../include/mail_server.h +trivial-rewrite.o: ../../include/mail_version.h +trivial-rewrite.o: ../../include/maps.h +trivial-rewrite.o: ../../include/msg.h +trivial-rewrite.o: ../../include/myflock.h +trivial-rewrite.o: ../../include/mymalloc.h +trivial-rewrite.o: ../../include/nvtable.h +trivial-rewrite.o: ../../include/resolve_clnt.h +trivial-rewrite.o: ../../include/resolve_local.h +trivial-rewrite.o: ../../include/rewrite_clnt.h +trivial-rewrite.o: ../../include/split_at.h +trivial-rewrite.o: ../../include/stringops.h +trivial-rewrite.o: ../../include/sys_defs.h +trivial-rewrite.o: ../../include/tok822.h +trivial-rewrite.o: ../../include/vbuf.h +trivial-rewrite.o: ../../include/vstream.h +trivial-rewrite.o: ../../include/vstring.h +trivial-rewrite.o: ../../include/vstring_vstream.h +trivial-rewrite.o: transport.h +trivial-rewrite.o: trivial-rewrite.c +trivial-rewrite.o: trivial-rewrite.h diff --git a/src/trivial-rewrite/resolve.c b/src/trivial-rewrite/resolve.c new file mode 100644 index 0000000..4e9ea2a --- /dev/null +++ b/src/trivial-rewrite/resolve.c @@ -0,0 +1,828 @@ +/*++ +/* NAME +/* resolve 3 +/* SUMMARY +/* mail address resolver +/* SYNOPSIS +/* #include "trivial-rewrite.h" +/* +/* void resolve_init(void) +/* +/* int resolve_class(domain) +/* const char *domain; +/* +/* void resolve_proto(context, stream) +/* RES_CONTEXT *context; +/* VSTREAM *stream; +/* DESCRIPTION +/* This module implements the trivial address resolving engine. +/* It distinguishes between local and remote mail, and optionally +/* consults one or more transport tables that map a destination +/* to a transport, nexthop pair. +/* +/* resolve_init() initializes data structures that are private +/* to this module. It should be called once before using the +/* actual resolver routines. +/* +/* resolve_class() returns the address class for the specified +/* domain, or -1 in case of error. +/* +/* resolve_proto() implements the client-server protocol: +/* read one address in FQDN form, reply with a (transport, +/* nexthop, internalized recipient) triple. +/* STANDARDS +/* DIAGNOSTICS +/* Problems and transactions are logged to \fBsyslogd\fR(8) +/* or \fBpostlogd\fR(8). +/* BUGS +/* SEE ALSO +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +/* System library. */ + +#include +#include +#include + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "trivial-rewrite.h" +#include "transport.h" + + /* + * The job of the address resolver is to map one recipient address to a + * triple of (channel, nexthop, recipient). The channel is the name of the + * delivery service specified in master.cf, the nexthop is (usually) a + * description of the next host to deliver to, and recipient is the final + * recipient address. The latter may differ from the input address as the + * result of stripping multiple layers of sender-specified routing. + * + * Addresses are resolved by their domain name. Known domain names are + * categorized into classes: local, virtual alias, virtual mailbox, relay, + * and everything else. Finding the address domain class is a matter of + * table lookups. + * + * Different address domain classes generally use different delivery channels, + * and may use class dependent ways to arrive at the corresponding nexthop + * information. With classes that do final delivery, the nexthop is + * typically the local machine hostname. + * + * The transport lookup table provides a means to override the domain class + * channel and/or nexhop information for specific recipients or for entire + * domain hierarchies. + * + * This works well in the general case. The only bug in this approach is that + * the structure of the nexthop information is transport dependent. + * Typically, the nexthop specifies a hostname, hostname + TCP Port, or the + * pathname of a UNIX-domain socket. However, with the error transport the + * nexthop field contains free text with the reason for non-delivery. + * + * Therefore, a transport map entry that overrides the channel but not the + * nexthop information (or vice versa) may produce surprising results. In + * particular, the free text nexthop information for the error transport is + * likely to confuse regular delivery agents; and conversely, a hostname or + * socket pathname is not an adequate text as reason for non-delivery. + * + * In the code below, rcpt_domain specifies the domain name that we will use + * when the transport table specifies a non-default channel but no nexthop + * information (we use a generic text when that non-default channel is the + * error transport). + */ + +#define STR vstring_str +#define LEN VSTRING_LEN + + /* + * Some of the lists that define the address domain classes. + */ +static DOMAIN_LIST *relay_domains; +static STRING_LIST *virt_alias_doms; +static STRING_LIST *virt_mailbox_doms; + +static MAPS *relocated_maps; + +/* resolve_class - determine domain address class */ + +int resolve_class(const char *domain) +{ + int ret; + + /* + * Same order as in resolve_addr(). + */ + if ((ret = resolve_local(domain)) != 0) + return (ret > 0 ? RESOLVE_CLASS_LOCAL : -1); + if (virt_alias_doms) { + if (string_list_match(virt_alias_doms, domain)) + return (RESOLVE_CLASS_ALIAS); + if (virt_alias_doms->error) + return (-1); + } + if (virt_mailbox_doms) { + if (string_list_match(virt_mailbox_doms, domain)) + return (RESOLVE_CLASS_VIRTUAL); + if (virt_mailbox_doms->error) + return (-1); + } + if (relay_domains) { + if (string_list_match(relay_domains, domain)) + return (RESOLVE_CLASS_RELAY); + if (relay_domains->error) + return (-1); + } + return (RESOLVE_CLASS_DEFAULT); +} + +/* resolve_addr - resolve address according to rule set */ + +static void resolve_addr(RES_CONTEXT *rp, char *sender, char *addr, + VSTRING *channel, VSTRING *nexthop, + VSTRING *nextrcpt, int *flags) +{ + const char *myname = "resolve_addr"; + VSTRING *addr_buf = vstring_alloc(100); + TOK822 *tree = 0; + TOK822 *saved_domain = 0; + TOK822 *domain = 0; + char *destination; + const char *blame = 0; + const char *rcpt_domain; + ssize_t addr_len; + ssize_t loop_count; + ssize_t loop_max; + char *local; + char *oper; + char *junk; + const char *relay; + const char *xport; + const char *sender_key; + int rc; + + *flags = 0; + vstring_strcpy(channel, "CHANNEL NOT UPDATED"); + vstring_strcpy(nexthop, "NEXTHOP NOT UPDATED"); + vstring_strcpy(nextrcpt, "NEXTRCPT NOT UPDATED"); + + /* + * The address is in internalized (unquoted) form. + * + * In an ideal world we would parse the externalized address form as given + * to us by the sender. + * + * However, in the real world we have to look for routing characters like + * %@! in the address local-part, even when that information is quoted + * due to the presence of special characters or whitespace. Although + * technically incorrect, this is needed to stop user@domain@domain relay + * attempts when forwarding mail to a Sendmail MX host. + * + * This suggests that we parse the address in internalized (unquoted) form. + * Unfortunately, if we do that, the unparser generates incorrect white + * space between adjacent non-operator tokens. Example: ``first last'' + * needs white space, but ``stuff[stuff]'' does not. This is is not a + * problem when unparsing the result from parsing externalized forms, + * because the parser/unparser were designed for valid externalized forms + * where ``stuff[stuff]'' does not happen. + * + * As a workaround we start with the quoted form and then dequote the + * local-part only where needed. This will do the right thing in most + * (but not all) cases. + */ + addr_len = strlen(addr); + quote_822_local(addr_buf, addr); + tree = tok822_scan_addr(vstring_str(addr_buf)); + + /* + * The optimizer will eliminate tests that always fail, and will replace + * multiple expansions of this macro by a GOTO to a single instance. + */ +#define FREE_MEMORY_AND_RETURN { \ + if (saved_domain) \ + tok822_free_tree(saved_domain); \ + if(tree) \ + tok822_free_tree(tree); \ + if (addr_buf) \ + vstring_free(addr_buf); \ + return; \ + } + + /* + * Preliminary resolver: strip off all instances of the local domain. + * Terminate when no destination domain is left over, or when the + * destination domain is remote. + * + * XXX To whom it may concern. If you change the resolver loop below, or + * quote_822_local.c, or tok822_parse.c, be sure to re-run the tests + * under "make resolve_clnt_test" in the global directory. + */ +#define RESOLVE_LOCAL(domain) \ + resolve_local(STR(tok822_internalize(addr_buf, domain, TOK822_STR_DEFL))) + + for (loop_count = 0, loop_max = addr_len + 100; /* void */ ; loop_count++) { + + /* + * XXX Should never happen, but if this happens with some + * pathological address, then that is not sufficient reason to + * disrupt the operation of an MTA. + */ + if (loop_count > loop_max) { + msg_warn("resolve_addr: <%s>: giving up after %ld iterations", + addr, (long) loop_count); + *flags |= RESOLVE_FLAG_FAIL; + FREE_MEMORY_AND_RETURN; + break; + } + + /* + * Strip trailing dot at end of domain, but not dot-dot or at-dot. + * This merely makes diagnostics more accurate by leaving bogus + * addresses alone. + */ + if (tree->tail + && tree->tail->type == '.' + && tok822_rfind_type(tree->tail, '@') != 0 + && tree->tail->prev->type != '.' + && tree->tail->prev->type != '@') + tok822_free_tree(tok822_sub_keep_before(tree, tree->tail)); + + /* + * Strip trailing @. + */ + if (var_resolve_nulldom + && tree->tail + && tree->tail->type == '@') + tok822_free_tree(tok822_sub_keep_before(tree, tree->tail)); + + /* + * Strip (and save) @domain if local. + * + * Grr. resolve_local() table lookups may fail. It may be OK for local + * file lookup code to abort upon failure, but with network-based + * tables it is preferable to return an error indication to the + * requestor. + */ + if ((domain = tok822_rfind_type(tree->tail, '@')) != 0) { + if (domain->next && (rc = RESOLVE_LOCAL(domain->next)) <= 0) { + if (rc < 0) { + *flags |= RESOLVE_FLAG_FAIL; + FREE_MEMORY_AND_RETURN; + } + break; + } + tok822_sub_keep_before(tree, domain); + if (saved_domain) + tok822_free_tree(saved_domain); + saved_domain = domain; + domain = 0; /* safety for future change */ + } + + /* + * After stripping the local domain, if any, replace foo%bar by + * foo@bar, site!user by user@site, rewrite to canonical form, and + * retry. + */ + if (tok822_rfind_type(tree->tail, '@') + || (var_swap_bangpath && tok822_rfind_type(tree->tail, '!')) + || (var_percent_hack && tok822_rfind_type(tree->tail, '%'))) { + rewrite_tree(&local_context, tree); + continue; + } + + /* + * If the local-part is a quoted string, crack it open when we're + * permitted to do so and look for routing operators. This is + * technically incorrect, but is needed to stop relaying problems. + * + * XXX Do another feeble attempt to keep local-part info quoted. + */ + if (var_resolve_dequoted + && tree->head && tree->head == tree->tail + && tree->head->type == TOK822_QSTRING + && ((oper = strrchr(local = STR(tree->head->vstr), '@')) != 0 + || (var_percent_hack && (oper = strrchr(local, '%')) != 0) + || (var_swap_bangpath && (oper = strrchr(local, '!')) != 0))) { + if (*oper == '%') + *oper = '@'; + tok822_internalize(addr_buf, tree->head, TOK822_STR_DEFL); + if (*oper == '@') { + junk = mystrdup(STR(addr_buf)); + quote_822_local(addr_buf, junk); + myfree(junk); + } + tok822_free(tree->head); + tree->head = tok822_scan(STR(addr_buf), &tree->tail); + rewrite_tree(&local_context, tree); + continue; + } + + /* + * An empty local-part or an empty quoted string local-part becomes + * the local MAILER-DAEMON, for consistency with our own From: + * message headers. + */ + if (tree->head && tree->head == tree->tail + && tree->head->type == TOK822_QSTRING + && VSTRING_LEN(tree->head->vstr) == 0) { + tok822_free(tree->head); + tree->head = 0; + } + /* XXX Re-resolve the surrogate, in case already in user@domain form. */ + if (tree->head == 0) { + tree->head = tok822_scan(var_empty_addr, &tree->tail); + continue; + } + /* XXX Re-resolve with @$myhostname for backwards compatibility. */ + if (domain == 0 && saved_domain == 0) { + tok822_sub_append(tree, tok822_alloc('@', (char *) 0)); + tok822_sub_append(tree, tok822_scan(var_myhostname, (TOK822 **) 0)); + continue; + } + + /* + * We're done. There are no domains left to strip off the address, + * and all null local-part information is sanitized. + */ + domain = 0; + break; + } + + vstring_free(addr_buf); + addr_buf = 0; + + /* + * Make sure the resolved envelope recipient has the user@domain form. If + * no domain was specified in the address, assume the local machine. See + * above for what happens with an empty address. + */ + if (domain == 0) { + if (saved_domain) { + tok822_sub_append(tree, saved_domain); + saved_domain = 0; + } else { + tok822_sub_append(tree, tok822_alloc('@', (char *) 0)); + tok822_sub_append(tree, tok822_scan(var_myhostname, (TOK822 **) 0)); + } + } + + /* + * Transform the recipient address back to internal form. + * + * XXX This may produce incorrect results if we cracked open a quoted + * local-part with routing operators; see discussion above at the top of + * the big loop. + * + * XXX We explicitly disallow domain names in bare network address form. A + * network address destination should be formatted according to RFC 2821: + * it should be enclosed in [], and an IPv6 address should have an IPv6: + * prefix. + */ + tok822_internalize(nextrcpt, tree, TOK822_STR_DEFL); + rcpt_domain = strrchr(STR(nextrcpt), '@') + 1; + if (rcpt_domain == (char *) 1) + msg_panic("no @ in address: \"%s\"", STR(nextrcpt)); + if (*rcpt_domain == '[') { + if (!valid_mailhost_literal(rcpt_domain, DONT_GRIPE)) + *flags |= RESOLVE_FLAG_ERROR; + } else if (var_smtputf8_enable + && valid_utf8_string(STR(nextrcpt), LEN(nextrcpt)) == 0) { + *flags |= RESOLVE_FLAG_ERROR; + } else if (!valid_utf8_hostname(var_smtputf8_enable, rcpt_domain, + DONT_GRIPE)) { + if (var_resolve_num_dom && valid_hostaddr(rcpt_domain, DONT_GRIPE)) { + vstring_insert(nextrcpt, rcpt_domain - STR(nextrcpt), "[", 1); + vstring_strcat(nextrcpt, "]"); + rcpt_domain = strrchr(STR(nextrcpt), '@') + 1; + if ((rc = resolve_local(rcpt_domain)) > 0) /* XXX */ + domain = 0; + else if (rc < 0) { + *flags |= RESOLVE_FLAG_FAIL; + FREE_MEMORY_AND_RETURN; + } + } else { + *flags |= RESOLVE_FLAG_ERROR; + } + } + tok822_free_tree(tree); + tree = 0; + + /* + * XXX Short-cut invalid address forms. + */ + if (*flags & RESOLVE_FLAG_ERROR) { + *flags |= RESOLVE_CLASS_DEFAULT; + FREE_MEMORY_AND_RETURN; + } + + /* + * Recognize routing operators in the local-part, even when we do not + * recognize ! or % as valid routing operators locally. This is needed to + * prevent backup MX hosts from relaying third-party destinations through + * primary MX hosts, otherwise the backup host could end up on black + * lists. Ignore local swap_bangpath and percent_hack settings because we + * can't know how the next MX host is set up. + */ + if (strcmp(STR(nextrcpt) + strcspn(STR(nextrcpt), "@!%") + 1, rcpt_domain)) + *flags |= RESOLVE_FLAG_ROUTED; + + /* + * With local, virtual, relay, or other non-local destinations, give the + * highest precedence to transport associated nexthop information. + * + * Otherwise, with relay or other non-local destinations, the relayhost + * setting overrides the recipient domain name, and the sender-dependent + * relayhost overrides both. + * + * XXX Nag if the recipient domain is listed in multiple domain lists. The + * result is implementation defined, and may break when internals change. + * + * For now, we distinguish only a fixed number of address classes. + * Eventually this may become extensible, so that new classes can be + * configured with their own domain list, delivery transport, and + * recipient table. + */ +#define STREQ(x,y) (strcmp((x), (y)) == 0) + + if (domain != 0) { + + /* + * Virtual alias domain. + */ + if (virt_alias_doms + && string_list_match(virt_alias_doms, rcpt_domain)) { + if (var_helpful_warnings) { + if (virt_mailbox_doms + && string_list_match(virt_mailbox_doms, rcpt_domain)) + msg_warn("do not list domain %s in BOTH %s and %s", + rcpt_domain, VAR_VIRT_ALIAS_DOMS, + VAR_VIRT_MAILBOX_DOMS); + if (relay_domains + && domain_list_match(relay_domains, rcpt_domain)) + msg_warn("do not list domain %s in BOTH %s and %s", + rcpt_domain, VAR_VIRT_ALIAS_DOMS, + VAR_RELAY_DOMAINS); +#if 0 + if (strcasecmp_utf8(rcpt_domain, var_myorigin) == 0) + msg_warn("do not list $%s (%s) in %s", + VAR_MYORIGIN, var_myorigin, VAR_VIRT_ALIAS_DOMS); +#endif + } + vstring_strcpy(channel, MAIL_SERVICE_ERROR); + vstring_sprintf(nexthop, "5.1.1 User unknown%s", + var_show_unk_rcpt_table ? + " in virtual alias table" : ""); + *flags |= RESOLVE_CLASS_ALIAS; + } else if (virt_alias_doms && virt_alias_doms->error != 0) { + msg_warn("%s lookup failure", VAR_VIRT_ALIAS_DOMS); + *flags |= RESOLVE_FLAG_FAIL; + FREE_MEMORY_AND_RETURN; + } + + /* + * Virtual mailbox domain. + */ + else if (virt_mailbox_doms + && string_list_match(virt_mailbox_doms, rcpt_domain)) { + if (var_helpful_warnings) { + if (relay_domains + && domain_list_match(relay_domains, rcpt_domain)) + msg_warn("do not list domain %s in BOTH %s and %s", + rcpt_domain, VAR_VIRT_MAILBOX_DOMS, + VAR_RELAY_DOMAINS); + } + vstring_strcpy(channel, RES_PARAM_VALUE(rp->virt_transport)); + vstring_strcpy(nexthop, rcpt_domain); + blame = rp->virt_transport_name; + *flags |= RESOLVE_CLASS_VIRTUAL; + } else if (virt_mailbox_doms && virt_mailbox_doms->error != 0) { + msg_warn("%s lookup failure", VAR_VIRT_MAILBOX_DOMS); + *flags |= RESOLVE_FLAG_FAIL; + FREE_MEMORY_AND_RETURN; + } else { + + /* + * Off-host relay destination. + */ + if (relay_domains + && domain_list_match(relay_domains, rcpt_domain)) { + vstring_strcpy(channel, RES_PARAM_VALUE(rp->relay_transport)); + blame = rp->relay_transport_name; + *flags |= RESOLVE_CLASS_RELAY; + } else if (relay_domains && relay_domains->error != 0) { + msg_warn("%s lookup failure", VAR_RELAY_DOMAINS); + *flags |= RESOLVE_FLAG_FAIL; + FREE_MEMORY_AND_RETURN; + } + + /* + * Other off-host destination. + */ + else { + if (rp->snd_def_xp_info + && (xport = mail_addr_find(rp->snd_def_xp_info, + sender_key = (*sender ? sender : + var_null_def_xport_maps_key), + (char **) 0)) != 0) { + if (*xport == 0) { + msg_warn("%s: ignoring null lookup result for %s", + rp->snd_def_xp_maps_name, sender_key); + xport = "DUNNO"; + } + vstring_strcpy(channel, strcasecmp(xport, "DUNNO") == 0 ? + RES_PARAM_VALUE(rp->def_transport) : xport); + blame = rp->snd_def_xp_maps_name; + } else if (rp->snd_def_xp_info + && rp->snd_def_xp_info->error != 0) { + msg_warn("%s lookup failure", rp->snd_def_xp_maps_name); + *flags |= RESOLVE_FLAG_FAIL; + FREE_MEMORY_AND_RETURN; + } else { + vstring_strcpy(channel, RES_PARAM_VALUE(rp->def_transport)); + blame = rp->def_transport_name; + } + *flags |= RESOLVE_CLASS_DEFAULT; + } + + /* + * With off-host delivery, sender-dependent or global relayhost + * override the recipient domain. + */ + if (rp->snd_relay_info + && (relay = mail_addr_find(rp->snd_relay_info, + sender_key = (*sender ? sender : + var_null_relay_maps_key), + (char **) 0)) != 0) { + if (*relay == 0) { + msg_warn("%s: ignoring null lookup result for %s", + rp->snd_relay_maps_name, sender_key); + relay = 0; + } else if (strcasecmp_utf8(relay, "DUNNO") == 0) + relay = 0; + } else if (rp->snd_relay_info + && rp->snd_relay_info->error != 0) { + msg_warn("%s lookup failure", rp->snd_relay_maps_name); + *flags |= RESOLVE_FLAG_FAIL; + FREE_MEMORY_AND_RETURN; + } else { + relay = 0; + } + /* Enforce all the relayhost precedences in one place. */ + if (relay != 0) { + vstring_strcpy(nexthop, relay); + } else if (*RES_PARAM_VALUE(rp->relayhost)) + vstring_strcpy(nexthop, RES_PARAM_VALUE(rp->relayhost)); + else + vstring_strcpy(nexthop, rcpt_domain); + } + } + + /* + * Local delivery. + * + * XXX Nag if the domain is listed in multiple domain lists. The effect is + * implementation defined, and may break when internals change. + */ + else { + if (var_helpful_warnings) { + if (virt_alias_doms + && string_list_match(virt_alias_doms, rcpt_domain)) + msg_warn("do not list domain %s in BOTH %s and %s", + rcpt_domain, VAR_MYDEST, VAR_VIRT_ALIAS_DOMS); + if (virt_mailbox_doms + && string_list_match(virt_mailbox_doms, rcpt_domain)) + msg_warn("do not list domain %s in BOTH %s and %s", + rcpt_domain, VAR_MYDEST, VAR_VIRT_MAILBOX_DOMS); + } + vstring_strcpy(channel, RES_PARAM_VALUE(rp->local_transport)); + vstring_strcpy(nexthop, rcpt_domain); + blame = rp->local_transport_name; + *flags |= RESOLVE_CLASS_LOCAL; + } + + /* + * An explicit main.cf transport:nexthop setting overrides the nexthop. + * + * XXX We depend on this mechanism to enforce per-recipient concurrencies + * for local recipients. With "local_transport = local:$myhostname" we + * force mail for any domain in $mydestination/${proxy,inet}_interfaces + * to share the same queue. + */ + if ((destination = split_at(STR(channel), ':')) != 0 && *destination) + vstring_strcpy(nexthop, destination); + + /* + * Sanity checks. + */ + if (*STR(channel) == 0) { + if (blame == 0) + msg_panic("%s: null blame", myname); + msg_warn("file %s/%s: parameter %s: null transport is not allowed", + var_config_dir, MAIN_CONF_FILE, blame); + *flags |= RESOLVE_FLAG_FAIL; + FREE_MEMORY_AND_RETURN; + } + if (*STR(nexthop) == 0) + msg_panic("%s: null nexthop", myname); + + /* + * The transport map can selectively override any transport and/or + * nexthop host info that is set up above. Unfortunately, the syntax for + * nexthop information is transport specific. We therefore need sane and + * intuitive semantics for transport map entries that specify a channel + * but no nexthop. + * + * With non-error transports, the initial nexthop information is the + * recipient domain. However, specific main.cf transport definitions may + * specify a transport-specific destination, such as a host + TCP socket, + * or the pathname of a UNIX-domain socket. With less precedence than + * main.cf transport definitions, a main.cf relayhost definition may also + * override nexthop information for off-host deliveries. + * + * With the error transport, the nexthop information is free text that + * specifies the reason for non-delivery. + * + * Because nexthop syntax is transport specific we reset the nexthop + * information to the recipient domain when the transport table specifies + * a transport without also specifying the nexthop information. + * + * Subtle note: reset nexthop even when the transport table does not change + * the transport. Otherwise it is hard to get rid of main.cf specified + * nexthop information. + * + * XXX Don't override the virtual alias class (error:User unknown) result. + */ + if (rp->transport_info && !(*flags & RESOLVE_CLASS_ALIAS)) { + if (transport_lookup(rp->transport_info, STR(nextrcpt), + rcpt_domain, channel, nexthop) == 0 + && rp->transport_info->transport_path->error != 0) { + msg_warn("%s lookup failure", rp->transport_maps_name); + *flags |= RESOLVE_FLAG_FAIL; + FREE_MEMORY_AND_RETURN; + } + } + + /* + * Bounce recipients that have moved, regardless of domain address class. + * We do this last, in anticipation of transport maps that can override + * the recipient address. + * + * The downside of not doing this in delivery agents is that this table has + * no effect on local alias expansion results. Such mail will have to + * make almost an entire iteration through the mail system. + */ +#define IGNORE_ADDR_EXTENSION ((char **) 0) + + if (relocated_maps != 0) { + const char *newloc; + + if ((newloc = mail_addr_find(relocated_maps, STR(nextrcpt), + IGNORE_ADDR_EXTENSION)) != 0) { + vstring_strcpy(channel, MAIL_SERVICE_ERROR); + /* 5.1.6 is the closest match, but not perfect. */ + vstring_sprintf(nexthop, "5.1.6 User has moved to %s", newloc); + } else if (relocated_maps->error != 0) { + msg_warn("%s lookup failure", VAR_RELOCATED_MAPS); + *flags |= RESOLVE_FLAG_FAIL; + FREE_MEMORY_AND_RETURN; + } + } + + /* + * Bounce recipient addresses that start with `-'. External commands may + * misinterpret such addresses as command-line options. + * + * In theory I could say people should always carefully set up their + * master.cf pipe mailer entries with `--' before the first non-option + * argument, but mistakes will happen regardless. + * + * Therefore the protection is put in place here, where it cannot be + * bypassed. + */ + if (var_allow_min_user == 0 && STR(nextrcpt)[0] == '-') { + *flags |= RESOLVE_FLAG_ERROR; + FREE_MEMORY_AND_RETURN; + } + + /* + * Clean up. + */ + FREE_MEMORY_AND_RETURN; +} + +/* Static, so they can be used by the network protocol interface only. */ + +static VSTRING *channel; +static VSTRING *nexthop; +static VSTRING *nextrcpt; +static VSTRING *query; +static VSTRING *sender; + +/* resolve_proto - read request and send reply */ + +int resolve_proto(RES_CONTEXT *context, VSTREAM *stream) +{ + int flags; + + if (attr_scan(stream, ATTR_FLAG_STRICT, + RECV_ATTR_STR(MAIL_ATTR_SENDER, sender), + RECV_ATTR_STR(MAIL_ATTR_ADDR, query), + ATTR_TYPE_END) != 2) + return (-1); + + resolve_addr(context, STR(sender), STR(query), + channel, nexthop, nextrcpt, &flags); + + if (msg_verbose) + msg_info("`%s' -> `%s' -> (`%s' `%s' `%s' `%d')", + STR(sender), STR(query), STR(channel), + STR(nexthop), STR(nextrcpt), flags); + + attr_print(stream, ATTR_FLAG_NONE, + SEND_ATTR_INT(MAIL_ATTR_FLAGS, server_flags), + SEND_ATTR_STR(MAIL_ATTR_TRANSPORT, STR(channel)), + SEND_ATTR_STR(MAIL_ATTR_NEXTHOP, STR(nexthop)), + SEND_ATTR_STR(MAIL_ATTR_RECIP, STR(nextrcpt)), + SEND_ATTR_INT(MAIL_ATTR_FLAGS, flags), + ATTR_TYPE_END); + + if (vstream_fflush(stream) != 0) { + msg_warn("write resolver reply: %m"); + return (-1); + } + return (0); +} + +/* resolve_init - module initializations */ + +void resolve_init(void) +{ + sender = vstring_alloc(100); + query = vstring_alloc(100); + channel = vstring_alloc(100); + nexthop = vstring_alloc(100); + nextrcpt = vstring_alloc(100); + + if (*var_virt_alias_doms) + virt_alias_doms = + string_list_init(VAR_VIRT_ALIAS_DOMS, MATCH_FLAG_RETURN, + var_virt_alias_doms); + + if (*var_virt_mailbox_doms) + virt_mailbox_doms = + string_list_init(VAR_VIRT_MAILBOX_DOMS, MATCH_FLAG_RETURN, + var_virt_mailbox_doms); + + if (*var_relay_domains) + relay_domains = + domain_list_init(VAR_RELAY_DOMAINS, MATCH_FLAG_RETURN + | match_parent_style(VAR_RELAY_DOMAINS), + var_relay_domains); + + if (*var_relocated_maps) + relocated_maps = + maps_create(VAR_RELOCATED_MAPS, var_relocated_maps, + DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX + | DICT_FLAG_UTF8_REQUEST); +} diff --git a/src/trivial-rewrite/rewrite.c b/src/trivial-rewrite/rewrite.c new file mode 100644 index 0000000..483463c --- /dev/null +++ b/src/trivial-rewrite/rewrite.c @@ -0,0 +1,303 @@ +/*++ +/* NAME +/* rewrite 3 +/* SUMMARY +/* mail address rewriter +/* SYNOPSIS +/* #include "trivial-rewrite.h" +/* +/* void rewrite_init(void) +/* +/* void rewrite_proto(stream) +/* VSTREAM *stream; +/* +/* void rewrite_addr(context, addr, result) +/* RWR_CONTEXT *context; +/* char *addr; +/* VSTRING *result; +/* +/* void rewrite_tree(context, tree) +/* RWR_CONTEXT *context; +/* TOK822 *tree; +/* +/* RWR_CONTEXT local_context; +/* RWR_CONTEXT remote_context; +/* DESCRIPTION +/* This module implements the trivial address rewriting engine. +/* +/* rewrite_init() initializes data structures that are private +/* to this module. It should be called once before using the +/* actual rewriting routines. +/* +/* rewrite_proto() implements the client-server protocol: read +/* one rule set name and one address in external (quoted) form, +/* reply with the rewritten address in external form. +/* +/* rewrite_addr() rewrites an address string to another string. +/* Both input and output are in external (quoted) form. +/* +/* rewrite_tree() rewrites a parse tree with a single address to +/* another tree. A tree is a dummy node on top of a token list. +/* +/* local_context and remote_context provide domain names for +/* completing incomplete address forms. +/* STANDARDS +/* DIAGNOSTICS +/* Problems and transactions are logged to \fBsyslogd\fR(8) +/* or \fBpostlogd\fR(8). +/* BUGS +/* SEE ALSO +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include +#include +#include + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "trivial-rewrite.h" + +RWR_CONTEXT local_context = { + VAR_MYORIGIN, &var_myorigin, + VAR_MYDOMAIN, &var_mydomain, +}; + +RWR_CONTEXT remote_context = { + VAR_REM_RWR_DOMAIN, &var_remote_rwr_domain, + VAR_REM_RWR_DOMAIN, &var_remote_rwr_domain, +}; + +static VSTRING *ruleset; +static VSTRING *address; +static VSTRING *result; + +/* rewrite_tree - rewrite address according to rule set */ + +void rewrite_tree(RWR_CONTEXT *context, TOK822 *tree) +{ + TOK822 *colon; + TOK822 *domain; + TOK822 *bang; + TOK822 *local; + VSTRING *vstringval; + + /* + * XXX If you change this module, quote_822_local.c, or tok822_parse.c, + * be sure to re-run the tests under "make rewrite_clnt_test" and "make + * resolve_clnt_test" in the global directory. + */ + + /* + * Sanity check. + */ + if (tree->head == 0) + msg_panic("rewrite_tree: empty tree"); + + /* + * An empty address is a special case. + */ + if (tree->head == tree->tail + && tree->tail->type == TOK822_QSTRING + && VSTRING_LEN(tree->tail->vstr) == 0) + return; + + /* + * Treat a lone @ as if it were an empty address. + */ + if (tree->head == tree->tail + && tree->tail->type == '@') { + tok822_free_tree(tok822_sub_keep_before(tree, tree->tail)); + tok822_sub_append(tree, tok822_alloc(TOK822_QSTRING, "")); + return; + } + + /* + * Strip source route. + */ + if (tree->head->type == '@' + && (colon = tok822_find_type(tree->head, ':')) != 0 + && colon != tree->tail) + tok822_free_tree(tok822_sub_keep_after(tree, colon)); + + /* + * Optionally, transform address forms without @. + */ + if ((domain = tok822_rfind_type(tree->tail, '@')) == 0) { + + /* + * Swap domain!user to user@domain. + */ + if (var_swap_bangpath != 0 + && (bang = tok822_find_type(tree->head, '!')) != 0) { + tok822_sub_keep_before(tree, bang); + local = tok822_cut_after(bang); + tok822_free(bang); + tok822_sub_prepend(tree, tok822_alloc('@', (char *) 0)); + if (local) + tok822_sub_prepend(tree, local); + } + + /* + * Promote user%domain to user@domain. + */ + else if (var_percent_hack != 0 + && (domain = tok822_rfind_type(tree->tail, '%')) != 0) { + domain->type = '@'; + } + + /* + * Append missing @origin + */ + else if (var_append_at_myorigin != 0 + && REW_PARAM_VALUE(context->origin) != 0 + && REW_PARAM_VALUE(context->origin)[0] != 0) { + domain = tok822_sub_append(tree, tok822_alloc('@', (char *) 0)); + tok822_sub_append(tree, tok822_scan(REW_PARAM_VALUE(context->origin), + (TOK822 **) 0)); + } + } + + /* + * Append missing .domain, but leave broken forms ending in @ alone. This + * merely makes diagnostics more accurate by leaving bogus addresses + * alone. + * + * Backwards-compatibility warning: warn for "user@localhost" when there is + * no "localhost" in mydestination or in any other address class with an + * explicit domain list. + */ + if (var_append_dot_mydomain != 0 + && REW_PARAM_VALUE(context->domain) != 0 + && REW_PARAM_VALUE(context->domain)[0] != 0 + && (domain = tok822_rfind_type(tree->tail, '@')) != 0 + && domain != tree->tail + && tok822_find_type(domain, TOK822_DOMLIT) == 0 + && tok822_find_type(domain, '.') == 0) { + if (warn_compat_break_app_dot_mydomain + && (vstringval = domain->next->vstr) != 0) { + if (strcasecmp(vstring_str(vstringval), "localhost") != 0) { + msg_info("using backwards-compatible default setting " + VAR_APP_DOT_MYDOMAIN "=yes to rewrite \"%s\" to " + "\"%s.%s\"", vstring_str(vstringval), + vstring_str(vstringval), var_mydomain); + } else if (resolve_class("localhost") == RESOLVE_CLASS_DEFAULT) { + msg_info("using backwards-compatible default setting " + VAR_APP_DOT_MYDOMAIN "=yes to rewrite \"%s\" to " + "\"%s.%s\"; please add \"localhost\" to " + "mydestination or other address class", + vstring_str(vstringval), vstring_str(vstringval), + var_mydomain); + } + } + tok822_sub_append(tree, tok822_alloc('.', (char *) 0)); + tok822_sub_append(tree, tok822_scan(REW_PARAM_VALUE(context->domain), + (TOK822 **) 0)); + } + + /* + * Strip trailing dot at end of domain, but not dot-dot or @-dot. This + * merely makes diagnostics more accurate by leaving bogus addresses + * alone. + */ + if (tree->tail->type == '.' + && tree->tail->prev + && tree->tail->prev->type != '.' + && tree->tail->prev->type != '@') + tok822_free_tree(tok822_sub_keep_before(tree, tree->tail)); +} + +/* rewrite_proto - read request and send reply */ + +int rewrite_proto(VSTREAM *stream) +{ + RWR_CONTEXT *context; + TOK822 *tree; + + if (attr_scan(stream, ATTR_FLAG_STRICT, + RECV_ATTR_STR(MAIL_ATTR_RULE, ruleset), + RECV_ATTR_STR(MAIL_ATTR_ADDR, address), + ATTR_TYPE_END) != 2) + return (-1); + + if (strcmp(vstring_str(ruleset), MAIL_ATTR_RWR_LOCAL) == 0) + context = &local_context; + else if (strcmp(vstring_str(ruleset), MAIL_ATTR_RWR_REMOTE) == 0) + context = &remote_context; + else { + msg_warn("unknown context: %s", vstring_str(ruleset)); + return (-1); + } + + /* + * Sanity check. An address is supposed to be in externalized form. + */ + if (*vstring_str(address) == 0) { + msg_warn("rewrite_addr: null address"); + vstring_strcpy(result, vstring_str(address)); + } + + /* + * Convert the address from externalized (quoted) form to token list, + * rewrite it, and convert back. + */ + else { + tree = tok822_scan_addr(vstring_str(address)); + rewrite_tree(context, tree); + tok822_externalize(result, tree, TOK822_STR_DEFL); + tok822_free_tree(tree); + } + if (msg_verbose) + msg_info("`%s' `%s' -> `%s'", vstring_str(ruleset), + vstring_str(address), vstring_str(result)); + + attr_print(stream, ATTR_FLAG_NONE, + SEND_ATTR_INT(MAIL_ATTR_FLAGS, server_flags), + SEND_ATTR_STR(MAIL_ATTR_ADDR, vstring_str(result)), + ATTR_TYPE_END); + + if (vstream_fflush(stream) != 0) { + msg_warn("write rewrite reply: %m"); + return (-1); + } + return (0); +} + +/* rewrite_init - module initializations */ + +void rewrite_init(void) +{ + ruleset = vstring_alloc(100); + address = vstring_alloc(100); + result = vstring_alloc(100); +} diff --git a/src/trivial-rewrite/transport.c b/src/trivial-rewrite/transport.c new file mode 100644 index 0000000..cd62d67 --- /dev/null +++ b/src/trivial-rewrite/transport.c @@ -0,0 +1,438 @@ +/*++ +/* NAME +/* transport 3 +/* SUMMARY +/* transport mapping +/* SYNOPSIS +/* #include "transport.h" +/* +/* TRANSPORT_INFO *transport_pre_init(maps_name, maps) +/* const char *maps_name; +/* const char *maps; +/* +/* void transport_post_init(info) +/* TRANSPORT_INFO *info; +/* +/* int transport_lookup(info, address, rcpt_domain, channel, nexthop) +/* TRANSPORT_INFO *info; +/* const char *address; +/* const char *rcpt_domain; +/* VSTRING *channel; +/* VSTRING *nexthop; +/* +/* void transport_free(info); +/* TRANSPORT_INFO * info; +/* DESCRIPTION +/* This module implements access to the table that maps transport +/* user@domain addresses to (channel, nexthop) tuples. +/* +/* transport_pre_init() performs initializations that should be +/* done before the process enters the chroot jail, and +/* before calling transport_lookup(). +/* +/* transport_post_init() can be invoked after entering the chroot +/* jail, and must be called before before calling transport_lookup(). +/* +/* transport_lookup() finds the channel and nexthop for the given +/* domain, and returns 1 if something was found. Otherwise, 0 +/* is returned. +/* DIAGNOSTICS +/* info->transport_path->error is non-zero when the lookup +/* should be tried again. +/* SEE ALSO +/* maps(3), multi-dictionary search +/* strip_addr(3), strip extension from address +/* transport(5), format of transport map +/* CONFIGURATION PARAMETERS +/* transport_maps, names of maps to be searched. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +/* System library. */ + +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include "transport.h" + +static int transport_match_parent_style; + +#define STR(x) vstring_str(x) + +static void transport_wildcard_init(TRANSPORT_INFO *); + +/* transport_pre_init - pre-jail initialization */ + +TRANSPORT_INFO *transport_pre_init(const char *transport_maps_name, + const char *transport_maps) +{ + TRANSPORT_INFO *tp; + + tp = (TRANSPORT_INFO *) mymalloc(sizeof(*tp)); + tp->transport_path = maps_create(transport_maps_name, transport_maps, + DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX + | DICT_FLAG_NO_REGSUB + | DICT_FLAG_UTF8_REQUEST); + tp->wildcard_channel = tp->wildcard_nexthop = 0; + tp->wildcard_errno = 0; + tp->expire = 0; + return (tp); +} + +/* transport_post_init - post-jail initialization */ + +void transport_post_init(TRANSPORT_INFO *tp) +{ + transport_match_parent_style = match_parent_style(VAR_TRANSPORT_MAPS); + transport_wildcard_init(tp); +} + +/* transport_free - destroy transport info */ + +void transport_free(TRANSPORT_INFO *tp) +{ + if (tp->transport_path) + maps_free(tp->transport_path); + if (tp->wildcard_channel) + vstring_free(tp->wildcard_channel); + if (tp->wildcard_nexthop) + vstring_free(tp->wildcard_nexthop); + myfree((void *) tp); +} + +/* update_entry - update from transport table entry */ + +static void update_entry(const char *new_channel, const char *new_nexthop, + const char *rcpt_domain, VSTRING *channel, + VSTRING *nexthop) +{ + + /* + * :[nexthop] means don't change the channel, and don't change the + * nexthop unless a non-default nexthop is specified. Thus, a right-hand + * side of ":" is the transport table equivalent of a NOOP. + */ + if (*new_channel == 0) { /* :[nexthop] */ + if (*new_nexthop != 0) + vstring_strcpy(nexthop, new_nexthop); + } + + /* + * transport[:[nexthop]] means change the channel, and reset the nexthop + * to the default unless a non-default nexthop is specified. + */ + else { + vstring_strcpy(channel, new_channel); + if (*new_nexthop != 0) + vstring_strcpy(nexthop, new_nexthop); + else if (strcmp(STR(channel), MAIL_SERVICE_ERROR) != 0 + && strcmp(STR(channel), MAIL_SERVICE_RETRY) != 0) + vstring_strcpy(nexthop, rcpt_domain); + else + vstring_strcpy(nexthop, "Address is undeliverable"); + } +} + +/* parse_transport_entry - parse transport table entry */ + +static void parse_transport_entry(const char *value, const char *rcpt_domain, + VSTRING *channel, VSTRING *nexthop) +{ + char *saved_value; + const char *host; + +#define FOUND 1 +#define NOTFOUND 0 + + /* + * It would be great if we could specify a recipient address in the + * lookup result. Unfortunately, we cannot simply run the result through + * a parser that recognizes "transport:user@domain" because the lookup + * result can have arbitrary content (especially in the case of the error + * mailer). + */ + saved_value = mystrdup(value); + host = split_at(saved_value, ':'); + update_entry(saved_value, host ? host : "", rcpt_domain, channel, nexthop); + myfree(saved_value); +} + +/* transport_wildcard_init - (re) initialize wild-card lookup result */ + +static void transport_wildcard_init(TRANSPORT_INFO *tp) +{ + VSTRING *channel = vstring_alloc(10); + VSTRING *nexthop = vstring_alloc(10); + const char *value; + + /* + * Both channel and nexthop may be zero-length strings. Therefore we must + * use something else to represent "wild-card does not exist". We use + * null VSTRING pointers, for historical reasons. + */ + if (tp->wildcard_channel) + vstring_free(tp->wildcard_channel); + if (tp->wildcard_nexthop) + vstring_free(tp->wildcard_nexthop); + + /* + * Technically, the wildcard lookup pattern is redundant. A static map + * (keys always match, result is fixed string) could achieve the same: + * + * transport_maps = hash:/etc/postfix/transport static:xxx:yyy + * + * But the user interface of such an approach would be less intuitive. We + * tolerate the continued existence of wildcard lookup patterns because + * of human interface considerations. + */ +#define WILDCARD "*" +#define FULL 0 +#define PARTIAL DICT_FLAG_FIXED + + if ((value = maps_find(tp->transport_path, WILDCARD, FULL)) != 0) { + parse_transport_entry(value, "", channel, nexthop); + tp->wildcard_errno = 0; + tp->wildcard_channel = channel; + tp->wildcard_nexthop = nexthop; + if (msg_verbose) + msg_info("wildcard_{chan:hop}={%s:%s}", + vstring_str(channel), vstring_str(nexthop)); + } else { + tp->wildcard_errno = tp->transport_path->error; + vstring_free(channel); + vstring_free(nexthop); + tp->wildcard_channel = 0; + tp->wildcard_nexthop = 0; + } + tp->expire = event_time() + 30; /* XXX make configurable */ +} + +/* transport_lookup - map a transport domain */ + +int transport_lookup(TRANSPORT_INFO *tp, const char *addr, + const char *rcpt_domain, + VSTRING *channel, VSTRING *nexthop) +{ + char *ratsign = 0; + const char *value; + +#define STREQ(x,y) (strcmp((x), (y)) == 0) +#define DISCARD_EXTENSION ((char **) 0) + + /* + * The null recipient is rewritten to the local mailer daemon address. + */ + if (*addr == 0) { + msg_warn("transport_lookup: null address - skipping table lookup"); + return (NOTFOUND); + } + + /* + * Look up the full and extension-stripped address, then match the domain + * and subdomains. Try the external form before the backwards-compatible + * internal form. + */ +#define LOOKUP_STRATEGY \ + (MA_FIND_FULL | MA_FIND_NOEXT | MA_FIND_DOMAIN | \ + (transport_match_parent_style == MATCH_FLAG_PARENT ? \ + MA_FIND_PDMS : MA_FIND_PDDMDS)) + + if ((ratsign = strrchr(addr, '@')) == 0 || ratsign[1] == 0) + msg_panic("transport_lookup: bad address: \"%s\"", addr); + + if ((value = mail_addr_find_strategy(tp->transport_path, addr, (char **) 0, + LOOKUP_STRATEGY)) != 0) { + parse_transport_entry(value, rcpt_domain, channel, nexthop); + return (FOUND); + } + if (tp->transport_path->error != 0) + return (NOTFOUND); + + /* + * Fall back to the wild-card entry. + */ + if (tp->wildcard_errno || event_time() > tp->expire) + transport_wildcard_init(tp); + if (tp->wildcard_errno) { + tp->transport_path->error = tp->wildcard_errno; + return (NOTFOUND); + } else if (tp->wildcard_channel) { + update_entry(STR(tp->wildcard_channel), STR(tp->wildcard_nexthop), + rcpt_domain, channel, nexthop); + return (FOUND); + } + + /* + * We really did not find it. + */ + return (NOTFOUND); +} + +#ifdef TEST + + /* + * Proof-of-concept test program. Read an address from stdin, and spit out + * the lookup result. + */ + +#include + +#include +#include +#include + +static NORETURN usage(const char *progname) +{ + msg_fatal("usage: %s [-v] database", progname); +} + +int main(int argc, char **argv) +{ + VSTRING *buffer = vstring_alloc(100); + VSTRING *channel = vstring_alloc(100); + VSTRING *nexthop = vstring_alloc(100); + TRANSPORT_INFO *tp; + char *bp; + char *addr_field; + char *rcpt_domain; + char *expect_channel; + char *expect_nexthop; + int status; + int ch; + int errs = 0; + + /* + * Parse JCL. + */ + while ((ch = GETOPT(argc, argv, "v")) > 0) { + switch (ch) { + case 'v': + msg_verbose++; + break; + default: + usage(argv[0]); + } + } + if (argc != optind + 1) + usage(argv[0]); + + /* + * Initialize. + */ +#define UPDATE(var, val) do { myfree(var); var = mystrdup(val); } while (0) + + mail_conf_read(); /* XXX eliminate dependency. */ + UPDATE(var_rcpt_delim, "+"); + UPDATE(var_mydomain, "localdomain"); + UPDATE(var_myorigin, "localhost.localdomain"); + UPDATE(var_mydest, "localhost.localdomain"); + + tp = transport_pre_init("transport map", argv[optind]); + transport_post_init(tp); + + while (vstring_fgets_nonl(buffer, VSTREAM_IN)) { + bp = STR(buffer); + + /* + * Parse the input and expectations. XXX We can't expect empty + * fields, so require '-' instead. + */ + if ((addr_field = mystrtok(&bp, ":")) == 0) + msg_fatal("no address field"); + if ((rcpt_domain = strrchr(addr_field, '@')) == 0) + msg_fatal("no recipient domain"); + rcpt_domain += 1; + expect_channel = mystrtok(&bp, ":"); + expect_nexthop = mystrtok(&bp, ":"); + if ((expect_channel != 0) != (expect_nexthop != 0)) + msg_fatal("specify both channel and nexthop, or specify neither"); + if (expect_channel) { + if (strcmp(expect_channel, "-") == 0) + *expect_channel = 0; + if (strcmp(expect_nexthop, "-") == 0) + *expect_nexthop = 0; + vstring_strcpy(channel, "DEFAULT"); + vstring_strcpy(nexthop, rcpt_domain); + } + if (mystrtok(&bp, ":") != 0) + msg_fatal("garbage after nexthop field"); + + /* + * Lookups. + */ + status = transport_lookup(tp, addr_field, rcpt_domain, + channel, nexthop); + + /* + * Enforce expectations. + */ + if (expect_nexthop && status) { + vstream_printf("%s:%s -> %s:%s \n", + addr_field, rcpt_domain, + STR(channel), STR(nexthop)); + vstream_fflush(VSTREAM_OUT); + if (strcmp(expect_channel, STR(channel)) != 0) { + msg_warn("expect channel '%s' but got '%s'", + expect_channel, STR(channel)); + errs = 1; + } + if (strcmp(expect_nexthop, STR(nexthop)) != 0) { + msg_warn("expect nexthop '%s' but got '%s'", + expect_nexthop, STR(nexthop)); + errs = 1; + } + } else if (expect_nexthop && !status) { + vstream_printf("%s:%s -> %s\n", addr_field, rcpt_domain, + tp->transport_path->error ? + "(try again)" : "(not found)"); + vstream_fflush(VSTREAM_OUT); + msg_warn("expect channel '%s' but got none", expect_channel); + msg_warn("expect nexthop '%s' but got none", expect_nexthop); + errs = 1; + } else if (!status) { + vstream_printf("%s:%s -> %s\n", addr_field, rcpt_domain, + tp->transport_path->error ? + "(try again)" : "(not found)"); + } + } + transport_free(tp); + vstring_free(nexthop); + vstring_free(channel); + vstring_free(buffer); + exit(errs != 0); +} + +#endif diff --git a/src/trivial-rewrite/transport.h b/src/trivial-rewrite/transport.h new file mode 100644 index 0000000..7db0b50 --- /dev/null +++ b/src/trivial-rewrite/transport.h @@ -0,0 +1,51 @@ +/*++ +/* NAME +/* transport 3h +/* SUMMARY +/* transport mapping +/* SYNOPSIS +/* #include "transport.h" +/* DESCRIPTION +/* .nf + + /* + * System library. + */ +#include + + /* + * Utility library. + */ +#include + + /* + * Global library. + */ +#include + + /* + * External interface. + */ +typedef struct TRANSPORT_INFO { + MAPS *transport_path; + VSTRING *wildcard_channel; + VSTRING *wildcard_nexthop; + int wildcard_errno; + time_t expire; +} TRANSPORT_INFO; + +extern TRANSPORT_INFO *transport_pre_init(const char *, const char *); +extern void transport_post_init(TRANSPORT_INFO *); +extern int transport_lookup(TRANSPORT_INFO *, const char *, const char *, VSTRING *, VSTRING *); +extern void transport_free(TRANSPORT_INFO *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ diff --git a/src/trivial-rewrite/transport.in b/src/trivial-rewrite/transport.in new file mode 100644 index 0000000..1ed3a53 --- /dev/null +++ b/src/trivial-rewrite/transport.in @@ -0,0 +1,45 @@ +#!/bin/sh + +# Format: address:expected_channel:expected_nexthop +# No expectation means no match is expected. +# Specify "-" to expect an empty string. + +echo ==== no wildcard +${VALGRIND} ./transport 'inline:{rcpt1@example1.com=channel1:nexthop1, rcpt2@example2=channel2:, example3=channel3}' <<'EOF' +rcpt1@example1.com:channel1:nexthop1 +rcpt1+ext1@example1.com:channel1:nexthop1 +rcpt2@example2:channel2:example2 +rcpt@example3:channel3:example3 +EOF + +echo ==== with wildcard channel and nexthop +${VALGRIND} ./transport 'inline:{*=channel0:nexthop0, rcpt1@example1.com=channel1:nexthop1}' <<'EOF' +rcpt1@example1.com:channel1:nexthop1 +rcpt2@example2:channel0:nexthop0 +EOF + +echo ==== with wildcard channel only +${VALGRIND} ./transport 'inline:{*=channel0, rcpt1@example1.com=channel1:nexthop1}' <<'EOF' +rcpt1@example1.com:channel1:nexthop1 +rcpt2@example2:channel0:example2 +EOF + +echo ==== with wildcard nexthop only +${VALGRIND} ./transport 'inline:{*=:nexthop0, rcpt1@example1.com=channel1:nexthop1}' <<'EOF' +rcpt1@example1.com:channel1:nexthop1 +rcpt2@example2:DEFAULT:nexthop0 +EOF + +echo ==== with wildcard empty fields. +${VALGRIND} ./transport 'inline:{*=:, rcpt1@example1.com=channel1:nexthop1}' <<'EOF' +rcpt1@example1.com:channel1:nexthop1 +rcpt2@example2:DEFAULT:example2 +EOF + +echo === subdomain test +${VALGRIND} ./transport 'inline:{example=:example-result,.example=:dot-example-result}' <<'EOF' +plain1+ext@other-example: +foo@example:DEFAULT:example-result +foo@sub.example:DEFAULT:dot-example-result +foo@sub.sub.example:DEFAULT:dot-example-result +EOF diff --git a/src/trivial-rewrite/transport.ref b/src/trivial-rewrite/transport.ref new file mode 100644 index 0000000..47ab07b --- /dev/null +++ b/src/trivial-rewrite/transport.ref @@ -0,0 +1,22 @@ +==== no wildcard +rcpt1@example1.com:example1.com -> channel1:nexthop1 +rcpt1+ext1@example1.com:example1.com -> channel1:nexthop1 +rcpt2@example2:example2 -> channel2:example2 +rcpt@example3:example3 -> channel3:example3 +==== with wildcard channel and nexthop +rcpt1@example1.com:example1.com -> channel1:nexthop1 +rcpt2@example2:example2 -> channel0:nexthop0 +==== with wildcard channel only +rcpt1@example1.com:example1.com -> channel1:nexthop1 +rcpt2@example2:example2 -> channel0:example2 +==== with wildcard nexthop only +rcpt1@example1.com:example1.com -> channel1:nexthop1 +rcpt2@example2:example2 -> DEFAULT:nexthop0 +==== with wildcard empty fields. +rcpt1@example1.com:example1.com -> channel1:nexthop1 +rcpt2@example2:example2 -> DEFAULT:example2 +=== subdomain test +plain1+ext@other-example:other-example -> (not found) +foo@example:example -> DEFAULT:example-result +foo@sub.example:sub.example -> DEFAULT:dot-example-result +foo@sub.sub.example:sub.sub.example -> DEFAULT:dot-example-result diff --git a/src/trivial-rewrite/trivial-rewrite.c b/src/trivial-rewrite/trivial-rewrite.c new file mode 100644 index 0000000..29c55c9 --- /dev/null +++ b/src/trivial-rewrite/trivial-rewrite.c @@ -0,0 +1,652 @@ +/*++ +/* NAME +/* trivial-rewrite 8 +/* SUMMARY +/* Postfix address rewriting and resolving daemon +/* SYNOPSIS +/* \fBtrivial-rewrite\fR [generic Postfix daemon options] +/* DESCRIPTION +/* The \fBtrivial-rewrite\fR(8) daemon processes three types of client +/* service requests: +/* .IP "\fBrewrite \fIcontext address\fR" +/* Rewrite an address to standard form, according to the +/* address rewriting context: +/* .RS +/* .IP \fBlocal\fR +/* Append the domain names specified with \fB$myorigin\fR or +/* \fB$mydomain\fR to incomplete addresses; do \fBswap_bangpath\fR +/* and \fBallow_percent_hack\fR processing as described below, and +/* strip source routed addresses (\fI@site,@site:user@domain\fR) +/* to \fIuser@domain\fR form. +/* .IP \fBremote\fR +/* Append the domain name specified with +/* \fB$remote_header_rewrite_domain\fR to incomplete +/* addresses. Otherwise the result is identical to that of +/* the \fBlocal\fR address rewriting context. This prevents +/* Postfix from appending the local domain to spam from poorly +/* written remote clients. +/* .RE +/* .IP "\fBresolve \fIsender\fR \fIaddress\fR" +/* Resolve the address to a (\fItransport\fR, \fInexthop\fR, +/* \fIrecipient\fR, \fIflags\fR) quadruple. The meaning of +/* the results is as follows: +/* .RS +/* .IP \fItransport\fR +/* The delivery agent to use. This is the first field of an entry +/* in the \fBmaster.cf\fR file. +/* .IP \fInexthop\fR +/* The host to send to and optional delivery method information. +/* .IP \fIrecipient\fR +/* The envelope recipient address that is passed on to \fInexthop\fR. +/* .IP \fIflags\fR +/* The address class, whether the address requires relaying, +/* whether the address has problems, and whether the request failed. +/* .RE +/* .IP "\fBverify \fIsender\fR \fIaddress\fR" +/* Resolve the address for address verification purposes. +/* SERVER PROCESS MANAGEMENT +/* .ad +/* .fi +/* The \fBtrivial-rewrite\fR(8) servers run under control by +/* the Postfix master +/* server. Each server can handle multiple simultaneous connections. +/* When all servers are busy while a client connects, the master +/* creates a new server process, provided that the trivial-rewrite +/* server process limit is not exceeded. +/* Each trivial-rewrite server terminates after +/* serving at least \fB$max_use\fR clients of after \fB$max_idle\fR +/* seconds of idle time. +/* STANDARDS +/* .ad +/* .fi +/* None. The command does not interact with the outside world. +/* SECURITY +/* .ad +/* .fi +/* The \fBtrivial-rewrite\fR(8) daemon is not security sensitive. +/* By default, this daemon does not talk to remote or local users. +/* It can run at a fixed low privilege in a chrooted environment. +/* DIAGNOSTICS +/* Problems and transactions are logged to \fBsyslogd\fR(8) +/* or \fBpostlogd\fR(8). +/* CONFIGURATION PARAMETERS +/* .ad +/* .fi +/* On busy mail systems a long time may pass before a \fBmain.cf\fR +/* change affecting \fBtrivial-rewrite\fR(8) is picked up. Use the command +/* "\fBpostfix reload\fR" to speed up a change. +/* +/* The text below provides only a parameter summary. See +/* \fBpostconf\fR(5) for more details including examples. +/* COMPATIBILITY CONTROLS +/* .ad +/* .fi +/* .IP "\fBresolve_dequoted_address (yes)\fR" +/* Resolve a recipient address safely instead of correctly, by +/* looking inside quotes. +/* .PP +/* Available with Postfix version 2.1 and later: +/* .IP "\fBresolve_null_domain (no)\fR" +/* Resolve an address that ends in the "@" null domain as if the +/* local hostname were specified, instead of rejecting the address as +/* invalid. +/* .PP +/* Available with Postfix version 2.3 and later: +/* .IP "\fBresolve_numeric_domain (no)\fR" +/* Resolve "user@ipaddress" as "user@[ipaddress]", instead of +/* rejecting the address as invalid. +/* .PP +/* Available with Postfix version 2.5 and later: +/* .IP "\fBallow_min_user (no)\fR" +/* Allow a sender or recipient address to have `-' as the first +/* character. +/* ADDRESS REWRITING CONTROLS +/* .ad +/* .fi +/* .IP "\fBmyorigin ($myhostname)\fR" +/* The domain name that locally-posted mail appears to come +/* from, and that locally posted mail is delivered to. +/* .IP "\fBallow_percent_hack (yes)\fR" +/* Enable the rewriting of the form "user%domain" to "user@domain". +/* .IP "\fBappend_at_myorigin (yes)\fR" +/* With locally submitted mail, append the string "@$myorigin" to mail +/* addresses without domain information. +/* .IP "\fBappend_dot_mydomain (Postfix >= 3.0: no, Postfix < 3.0: yes)\fR" +/* With locally submitted mail, append the string ".$mydomain" to +/* addresses that have no ".domain" information. +/* .IP "\fBrecipient_delimiter (empty)\fR" +/* The set of characters that can separate a user name from its +/* extension (example: user+foo), or a .forward file name from its +/* extension (example: .forward+foo). +/* .IP "\fBswap_bangpath (yes)\fR" +/* Enable the rewriting of "site!user" into "user@site". +/* .PP +/* Available in Postfix 2.2 and later: +/* .IP "\fBremote_header_rewrite_domain (empty)\fR" +/* Don't rewrite message headers from remote clients at all when +/* this parameter is empty; otherwise, rewrite message headers and +/* append the specified domain name to incomplete addresses. +/* ROUTING CONTROLS +/* .ad +/* .fi +/* The following is applicable to Postfix version 2.0 and later. +/* Earlier versions do not have support for: virtual_transport, +/* relay_transport, virtual_alias_domains, virtual_mailbox_domains +/* or proxy_interfaces. +/* .IP "\fBlocal_transport (local:$myhostname)\fR" +/* The default mail delivery transport and next-hop destination +/* for final delivery to domains listed with mydestination, and for +/* [ipaddress] destinations that match $inet_interfaces or $proxy_interfaces. +/* .IP "\fBvirtual_transport (virtual)\fR" +/* The default mail delivery transport and next-hop destination for +/* final delivery to domains listed with $virtual_mailbox_domains. +/* .IP "\fBrelay_transport (relay)\fR" +/* The default mail delivery transport and next-hop destination for +/* remote delivery to domains listed with $relay_domains. +/* .IP "\fBdefault_transport (smtp)\fR" +/* The default mail delivery transport and next-hop destination for +/* destinations that do not match $mydestination, $inet_interfaces, +/* $proxy_interfaces, $virtual_alias_domains, $virtual_mailbox_domains, +/* or $relay_domains. +/* .IP "\fBparent_domain_matches_subdomains (see 'postconf -d' output)\fR" +/* A list of Postfix features where the pattern "example.com" also +/* matches subdomains of example.com, +/* instead of requiring an explicit ".example.com" pattern. +/* .IP "\fBrelayhost (empty)\fR" +/* The next-hop destination of non-local mail; overrides non-local +/* domains in recipient addresses. +/* .IP "\fBtransport_maps (empty)\fR" +/* Optional lookup tables with mappings from recipient address to +/* (message delivery transport, next-hop destination). +/* .PP +/* Available in Postfix version 2.3 and later: +/* .IP "\fBsender_dependent_relayhost_maps (empty)\fR" +/* A sender-dependent override for the global relayhost parameter +/* setting. +/* .PP +/* Available in Postfix version 2.5 and later: +/* .IP "\fBempty_address_relayhost_maps_lookup_key (<>)\fR" +/* The sender_dependent_relayhost_maps search string that will be +/* used instead of the null sender address. +/* .PP +/* Available in Postfix version 2.7 and later: +/* .IP "\fBempty_address_default_transport_maps_lookup_key (<>)\fR" +/* The sender_dependent_default_transport_maps search string that +/* will be used instead of the null sender address. +/* .IP "\fBsender_dependent_default_transport_maps (empty)\fR" +/* A sender-dependent override for the global default_transport +/* parameter setting. +/* ADDRESS VERIFICATION CONTROLS +/* .ad +/* .fi +/* Postfix version 2.1 introduces sender and recipient address verification. +/* This feature is implemented by sending probe email messages that +/* are not actually delivered. +/* By default, address verification probes use the same route +/* as regular mail. To override specific aspects of message +/* routing for address verification probes, specify one or more +/* of the following: +/* .IP "\fBaddress_verify_local_transport ($local_transport)\fR" +/* Overrides the local_transport parameter setting for address +/* verification probes. +/* .IP "\fBaddress_verify_virtual_transport ($virtual_transport)\fR" +/* Overrides the virtual_transport parameter setting for address +/* verification probes. +/* .IP "\fBaddress_verify_relay_transport ($relay_transport)\fR" +/* Overrides the relay_transport parameter setting for address +/* verification probes. +/* .IP "\fBaddress_verify_default_transport ($default_transport)\fR" +/* Overrides the default_transport parameter setting for address +/* verification probes. +/* .IP "\fBaddress_verify_relayhost ($relayhost)\fR" +/* Overrides the relayhost parameter setting for address verification +/* probes. +/* .IP "\fBaddress_verify_transport_maps ($transport_maps)\fR" +/* Overrides the transport_maps parameter setting for address verification +/* probes. +/* .PP +/* Available in Postfix version 2.3 and later: +/* .IP "\fBaddress_verify_sender_dependent_relayhost_maps ($sender_dependent_relayhost_maps)\fR" +/* Overrides the sender_dependent_relayhost_maps parameter setting for address +/* verification probes. +/* .PP +/* Available in Postfix version 2.7 and later: +/* .IP "\fBaddress_verify_sender_dependent_default_transport_maps ($sender_dependent_default_transport_maps)\fR" +/* Overrides the sender_dependent_default_transport_maps parameter +/* setting for address verification probes. +/* MISCELLANEOUS CONTROLS +/* .ad +/* .fi +/* .IP "\fBconfig_directory (see 'postconf -d' output)\fR" +/* The default location of the Postfix main.cf and master.cf +/* configuration files. +/* .IP "\fBdaemon_timeout (18000s)\fR" +/* How much time a Postfix daemon process may take to handle a +/* request before it is terminated by a built-in watchdog timer. +/* .IP "\fBempty_address_recipient (MAILER-DAEMON)\fR" +/* The recipient of mail addressed to the null address. +/* .IP "\fBipc_timeout (3600s)\fR" +/* The time limit for sending or receiving information over an internal +/* communication channel. +/* .IP "\fBmax_idle (100s)\fR" +/* The maximum amount of time that an idle Postfix daemon process waits +/* for an incoming connection before terminating voluntarily. +/* .IP "\fBmax_use (100)\fR" +/* The maximal number of incoming connections that a Postfix daemon +/* process will service before terminating voluntarily. +/* .IP "\fBrelocated_maps (empty)\fR" +/* Optional lookup tables with new contact information for users or +/* domains that no longer exist. +/* .IP "\fBprocess_id (read-only)\fR" +/* The process ID of a Postfix command or daemon process. +/* .IP "\fBprocess_name (read-only)\fR" +/* The process name of a Postfix command or daemon process. +/* .IP "\fBqueue_directory (see 'postconf -d' output)\fR" +/* The location of the Postfix top-level queue directory. +/* .IP "\fBshow_user_unknown_table_name (yes)\fR" +/* Display the name of the recipient table in the "User unknown" +/* responses. +/* .IP "\fBsyslog_facility (mail)\fR" +/* The syslog facility of Postfix logging. +/* .IP "\fBsyslog_name (see 'postconf -d' output)\fR" +/* A prefix that is prepended to the process name in syslog +/* records, so that, for example, "smtpd" becomes "prefix/smtpd". +/* .PP +/* Available in Postfix version 2.0 and later: +/* .IP "\fBhelpful_warnings (yes)\fR" +/* Log warnings about problematic configuration settings, and provide +/* helpful suggestions. +/* .PP +/* Available in Postfix 3.3 and later: +/* .IP "\fBservice_name (read-only)\fR" +/* The master.cf service name of a Postfix daemon process. +/* SEE ALSO +/* postconf(5), configuration parameters +/* transport(5), transport table format +/* relocated(5), format of the "user has moved" table +/* master(8), process manager +/* postlogd(8), Postfix logging +/* syslogd(8), system logging +/* README FILES +/* .ad +/* .fi +/* Use "\fBpostconf readme_directory\fR" or +/* "\fBpostconf html_directory\fR" to locate this information. +/* .na +/* .nf +/* ADDRESS_CLASS_README, Postfix address classes howto +/* ADDRESS_VERIFICATION_README, Postfix address verification +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +/* System library. */ + +#include +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Multi server skeleton. */ + +#include + +/* Application-specific. */ + +#include +#include + +static VSTRING *command; + + /* + * Tunable parameters. + */ +char *var_transport_maps; +bool var_swap_bangpath; +bool var_append_dot_mydomain; +bool var_append_at_myorigin; +bool var_percent_hack; +char *var_local_transport; +char *var_virt_transport; +char *var_relay_transport; +int var_resolve_dequoted; +char *var_virt_alias_maps; /* XXX virtual_alias_domains */ +char *var_virt_mailbox_maps; /* XXX virtual_mailbox_domains */ +char *var_virt_alias_doms; +char *var_virt_mailbox_doms; +char *var_relocated_maps; +char *var_def_transport; +char *var_snd_def_xport_maps; +char *var_empty_addr; +int var_show_unk_rcpt_table; +int var_resolve_nulldom; +char *var_remote_rwr_domain; +char *var_snd_relay_maps; +char *var_null_relay_maps_key; +char *var_null_def_xport_maps_key; +int var_resolve_num_dom; +bool var_allow_min_user; + + /* + * Shadow personality for address verification. + */ +char *var_vrfy_xport_maps; +char *var_vrfy_local_xport; +char *var_vrfy_virt_xport; +char *var_vrfy_relay_xport; +char *var_vrfy_def_xport; +char *var_vrfy_snd_def_xport_maps; +char *var_vrfy_relayhost; +char *var_vrfy_relay_maps; + + /* + * Different resolver personalities depending on the kind of request. + */ +RES_CONTEXT resolve_regular = { + VAR_LOCAL_TRANSPORT, &var_local_transport, + VAR_VIRT_TRANSPORT, &var_virt_transport, + VAR_RELAY_TRANSPORT, &var_relay_transport, + VAR_DEF_TRANSPORT, &var_def_transport, + VAR_SND_DEF_XPORT_MAPS, &var_snd_def_xport_maps, 0, + VAR_RELAYHOST, &var_relayhost, + VAR_SND_RELAY_MAPS, &var_snd_relay_maps, 0, + VAR_TRANSPORT_MAPS, &var_transport_maps, 0 +}; + +RES_CONTEXT resolve_verify = { + VAR_VRFY_LOCAL_XPORT, &var_vrfy_local_xport, + VAR_VRFY_VIRT_XPORT, &var_vrfy_virt_xport, + VAR_VRFY_RELAY_XPORT, &var_vrfy_relay_xport, + VAR_VRFY_DEF_XPORT, &var_vrfy_def_xport, + VAR_VRFY_SND_DEF_XPORT_MAPS, &var_vrfy_snd_def_xport_maps, 0, + VAR_VRFY_RELAYHOST, &var_vrfy_relayhost, + VAR_VRFY_RELAY_MAPS, &var_vrfy_relay_maps, 0, + VAR_VRFY_XPORT_MAPS, &var_vrfy_xport_maps, 0 +}; + + /* + * Connection management. When file-based lookup tables change we should + * restart at our convenience, but avoid client read errors. We restart + * rather than reopen, because the process may be chrooted (and if it isn't + * we still need code that handles the chrooted case anyway). + * + * Three variants are implemented. Only one should be used. + * + * ifdef DETACH_AND_ASK_CLIENTS_TO_RECONNECT + * + * This code detaches the trivial-rewrite process from the master, stops + * accepting new clients, and handles established clients in the background, + * asking them to reconnect the next time they send a request. The master + * creates a new process that accepts connections. This is reasonably safe + * because the number of trivial-rewrite server processes is small compared + * to the number of trivial-rewrite client processes. The few extra + * background processes should not make a difference in Postfix's footprint. + * However, once a daemon detaches from the master, its exit status will be + * lost, and abnormal termination may remain undetected. Timely restart is + * achieved by checking the table changed status every 10 seconds or so + * before responding to a client request. + * + * ifdef CHECK_TABLE_STATS_PERIODICALLY + * + * This code runs every 10 seconds and terminates the process when lookup + * tables have changed. This is subject to race conditions when established + * clients send a request while the server exits; those clients may read EOF + * instead of a server reply. If the experience with the oldest option + * (below) is anything to go by, however, then this is unlikely to be a + * problem during real deployment. + * + * ifdef CHECK_TABLE_STATS_BEFORE_ACCEPT + * + * This is the old code. It checks the table changed status when a new client + * connects (i.e. before the server calls accept()), and terminates + * immediately. This is invisible for the connecting client, but is subject + * to race conditions when established clients send a request while the + * server exits; those clients may read EOF instead of a server reply. This + * has, however, not been a problem in real deployment. With the old code, + * timely restart is achieved by setting the ipc_ttl parameter to 60 + * seconds, so that the table change status is checked several times a + * minute. + */ +int server_flags; + + /* + * Define exactly one of these. + */ +/* #define DETACH_AND_ASK_CLIENTS_TO_RECONNECT /* correct and complex */ +#define CHECK_TABLE_STATS_PERIODICALLY /* quick */ +/* #define CHECK_TABLE_STATS_BEFORE_ACCEPT /* slow */ + +/* rewrite_service - read request and send reply */ + +static void rewrite_service(VSTREAM *stream, char *unused_service, char **argv) +{ + int status = -1; + +#ifdef DETACH_AND_ASK_CLIENTS_TO_RECONNECT + static time_t last; + time_t now; + const char *table; + +#endif + + /* + * Sanity check. This service takes no command-line arguments. + */ + if (argv[0]) + msg_fatal("unexpected command-line argument: %s", argv[0]); + + /* + * Client connections are long-lived. Be sure to refesh timely. + */ +#ifdef DETACH_AND_ASK_CLIENTS_TO_RECONNECT + if (server_flags == 0 && (now = event_time()) - last > 10) { + if ((table = dict_changed_name()) != 0) { + msg_info("table %s has changed -- restarting", table); + if (multi_server_drain() == 0) + server_flags = 1; + } + last = now; + } +#endif + + /* + * This routine runs whenever a client connects to the UNIX-domain socket + * dedicated to address rewriting. All connection-management stuff is + * handled by the common code in multi_server.c. + */ + if (attr_scan(stream, ATTR_FLAG_STRICT | ATTR_FLAG_MORE, + RECV_ATTR_STR(MAIL_ATTR_REQ, command), + ATTR_TYPE_END) == 1) { + if (strcmp(vstring_str(command), REWRITE_ADDR) == 0) { + status = rewrite_proto(stream); + } else if (strcmp(vstring_str(command), RESOLVE_REGULAR) == 0) { + status = resolve_proto(&resolve_regular, stream); + } else if (strcmp(vstring_str(command), RESOLVE_VERIFY) == 0) { + status = resolve_proto(&resolve_verify, stream); + } else { + msg_warn("bad command %.30s", printable(vstring_str(command), '?')); + } + } + if (status < 0) + multi_server_disconnect(stream); +} + +/* pre_accept - see if tables have changed */ + +#ifdef CHECK_TABLE_STATS_BEFORE_ACCEPT + +static void pre_accept(char *unused_name, char **unused_argv) +{ + const char *table; + + if ((table = dict_changed_name()) != 0) { + msg_info("table %s has changed -- restarting", table); + exit(0); + } +} + +#endif + +static void check_table_stats(int unused_event, void *unused_context) +{ + const char *table; + + if ((table = dict_changed_name()) != 0) { + msg_info("table %s has changed -- restarting", table); + exit(0); + } + event_request_timer(check_table_stats, (void *) 0, 10); +} + +/* pre_jail_init - initialize before entering chroot jail */ + +static void pre_jail_init(char *unused_name, char **unused_argv) +{ + command = vstring_alloc(100); + rewrite_init(); + resolve_init(); + if (*RES_PARAM_VALUE(resolve_regular.transport_maps)) + resolve_regular.transport_info = + transport_pre_init(resolve_regular.transport_maps_name, + RES_PARAM_VALUE(resolve_regular.transport_maps)); + if (*RES_PARAM_VALUE(resolve_verify.transport_maps)) + resolve_verify.transport_info = + transport_pre_init(resolve_verify.transport_maps_name, + RES_PARAM_VALUE(resolve_verify.transport_maps)); + if (*RES_PARAM_VALUE(resolve_regular.snd_relay_maps)) + resolve_regular.snd_relay_info = + maps_create(resolve_regular.snd_relay_maps_name, + RES_PARAM_VALUE(resolve_regular.snd_relay_maps), + DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX + | DICT_FLAG_NO_REGSUB | DICT_FLAG_UTF8_REQUEST); + if (*RES_PARAM_VALUE(resolve_verify.snd_relay_maps)) + resolve_verify.snd_relay_info = + maps_create(resolve_verify.snd_relay_maps_name, + RES_PARAM_VALUE(resolve_verify.snd_relay_maps), + DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX + | DICT_FLAG_NO_REGSUB | DICT_FLAG_UTF8_REQUEST); + if (*RES_PARAM_VALUE(resolve_regular.snd_def_xp_maps)) + resolve_regular.snd_def_xp_info = + maps_create(resolve_regular.snd_def_xp_maps_name, + RES_PARAM_VALUE(resolve_regular.snd_def_xp_maps), + DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX + | DICT_FLAG_NO_REGSUB | DICT_FLAG_UTF8_REQUEST); + if (*RES_PARAM_VALUE(resolve_verify.snd_def_xp_maps)) + resolve_verify.snd_def_xp_info = + maps_create(resolve_verify.snd_def_xp_maps_name, + RES_PARAM_VALUE(resolve_verify.snd_def_xp_maps), + DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX + | DICT_FLAG_NO_REGSUB | DICT_FLAG_UTF8_REQUEST); +} + +/* post_jail_init - initialize after entering chroot jail */ + +static void post_jail_init(char *unused_name, char **unused_argv) +{ + if (resolve_regular.transport_info) + transport_post_init(resolve_regular.transport_info); + if (resolve_verify.transport_info) + transport_post_init(resolve_verify.transport_info); + check_table_stats(0, (void *) 0); +} + +MAIL_VERSION_STAMP_DECLARE; + +/* main - pass control to the multi-threaded skeleton code */ + +int main(int argc, char **argv) +{ + static const CONFIG_STR_TABLE str_table[] = { + VAR_TRANSPORT_MAPS, DEF_TRANSPORT_MAPS, &var_transport_maps, 0, 0, + VAR_LOCAL_TRANSPORT, DEF_LOCAL_TRANSPORT, &var_local_transport, 1, 0, + VAR_VIRT_TRANSPORT, DEF_VIRT_TRANSPORT, &var_virt_transport, 1, 0, + VAR_RELAY_TRANSPORT, DEF_RELAY_TRANSPORT, &var_relay_transport, 1, 0, + VAR_DEF_TRANSPORT, DEF_DEF_TRANSPORT, &var_def_transport, 1, 0, + VAR_VIRT_ALIAS_MAPS, DEF_VIRT_ALIAS_MAPS, &var_virt_alias_maps, 0, 0, + VAR_VIRT_ALIAS_DOMS, DEF_VIRT_ALIAS_DOMS, &var_virt_alias_doms, 0, 0, + VAR_VIRT_MAILBOX_MAPS, DEF_VIRT_MAILBOX_MAPS, &var_virt_mailbox_maps, 0, 0, + VAR_VIRT_MAILBOX_DOMS, DEF_VIRT_MAILBOX_DOMS, &var_virt_mailbox_doms, 0, 0, + VAR_RELOCATED_MAPS, DEF_RELOCATED_MAPS, &var_relocated_maps, 0, 0, + VAR_EMPTY_ADDR, DEF_EMPTY_ADDR, &var_empty_addr, 1, 0, + VAR_VRFY_XPORT_MAPS, DEF_VRFY_XPORT_MAPS, &var_vrfy_xport_maps, 0, 0, + VAR_VRFY_LOCAL_XPORT, DEF_VRFY_LOCAL_XPORT, &var_vrfy_local_xport, 1, 0, + VAR_VRFY_VIRT_XPORT, DEF_VRFY_VIRT_XPORT, &var_vrfy_virt_xport, 1, 0, + VAR_VRFY_RELAY_XPORT, DEF_VRFY_RELAY_XPORT, &var_vrfy_relay_xport, 1, 0, + VAR_VRFY_DEF_XPORT, DEF_VRFY_DEF_XPORT, &var_vrfy_def_xport, 1, 0, + VAR_VRFY_RELAYHOST, DEF_VRFY_RELAYHOST, &var_vrfy_relayhost, 0, 0, + VAR_REM_RWR_DOMAIN, DEF_REM_RWR_DOMAIN, &var_remote_rwr_domain, 0, 0, + VAR_SND_RELAY_MAPS, DEF_SND_RELAY_MAPS, &var_snd_relay_maps, 0, 0, + VAR_NULL_RELAY_MAPS_KEY, DEF_NULL_RELAY_MAPS_KEY, &var_null_relay_maps_key, 1, 0, + VAR_VRFY_RELAY_MAPS, DEF_VRFY_RELAY_MAPS, &var_vrfy_relay_maps, 0, 0, + VAR_SND_DEF_XPORT_MAPS, DEF_SND_DEF_XPORT_MAPS, &var_snd_def_xport_maps, 0, 0, + VAR_NULL_DEF_XPORT_MAPS_KEY, DEF_NULL_DEF_XPORT_MAPS_KEY, &var_null_def_xport_maps_key, 1, 0, + VAR_VRFY_SND_DEF_XPORT_MAPS, DEF_VRFY_SND_DEF_XPORT_MAPS, &var_vrfy_snd_def_xport_maps, 0, 0, + 0, + }; + static const CONFIG_BOOL_TABLE bool_table[] = { + VAR_SWAP_BANGPATH, DEF_SWAP_BANGPATH, &var_swap_bangpath, + VAR_APP_AT_MYORIGIN, DEF_APP_AT_MYORIGIN, &var_append_at_myorigin, + VAR_PERCENT_HACK, DEF_PERCENT_HACK, &var_percent_hack, + VAR_RESOLVE_DEQUOTED, DEF_RESOLVE_DEQUOTED, &var_resolve_dequoted, + VAR_SHOW_UNK_RCPT_TABLE, DEF_SHOW_UNK_RCPT_TABLE, &var_show_unk_rcpt_table, + VAR_RESOLVE_NULLDOM, DEF_RESOLVE_NULLDOM, &var_resolve_nulldom, + VAR_RESOLVE_NUM_DOM, DEF_RESOLVE_NUM_DOM, &var_resolve_num_dom, + VAR_ALLOW_MIN_USER, DEF_ALLOW_MIN_USER, &var_allow_min_user, + 0, + }; + static const CONFIG_NBOOL_TABLE nbool_table[] = { + VAR_APP_DOT_MYDOMAIN, DEF_APP_DOT_MYDOMAIN, &var_append_dot_mydomain, + 0, + }; + + /* + * Fingerprint executables and core dumps. + */ + MAIL_VERSION_STAMP_ALLOCATE; + + multi_server_main(argc, argv, rewrite_service, + CA_MAIL_SERVER_STR_TABLE(str_table), + CA_MAIL_SERVER_BOOL_TABLE(bool_table), + CA_MAIL_SERVER_NBOOL_TABLE(nbool_table), + CA_MAIL_SERVER_PRE_INIT(pre_jail_init), + CA_MAIL_SERVER_POST_INIT(post_jail_init), +#ifdef CHECK_TABLE_STATS_BEFORE_ACCEPT + CA_MAIL_SERVER_PRE_ACCEPT(pre_accept), +#endif + 0); +} diff --git a/src/trivial-rewrite/trivial-rewrite.h b/src/trivial-rewrite/trivial-rewrite.h new file mode 100644 index 0000000..bf0dd4a --- /dev/null +++ b/src/trivial-rewrite/trivial-rewrite.h @@ -0,0 +1,87 @@ +/*++ +/* NAME +/* trivial-rewrite 3h +/* SUMMARY +/* mail address rewriter and resolver +/* SYNOPSIS +/* #include "trivial-rewrite.h" +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include +#include + + /* + * Global library. + */ +#include +#include + + /* + * Connection management. + */ +int server_flags; + + /* + * rewrite.c + */ +typedef struct { + const char *origin_name; /* name of variable */ + char **origin; /* default origin */ + const char *domain_name; /* name of variable */ + char **domain; /* default domain */ +} RWR_CONTEXT; + +#define REW_PARAM_VALUE(x) (*(x)) /* make it easy to do it right */ + +extern void rewrite_init(void); +extern int rewrite_proto(VSTREAM *); +extern void rewrite_addr(RWR_CONTEXT *, char *, VSTRING *); +extern void rewrite_tree(RWR_CONTEXT *, TOK822 *); +extern RWR_CONTEXT local_context; +extern RWR_CONTEXT inval_context; + + /* + * resolve.c + */ +typedef struct { + const char *local_transport_name; /* name of variable */ + char **local_transport; /* local transport:nexthop */ + const char *virt_transport_name; /* name of variable */ + char **virt_transport; /* virtual mailbox transport:nexthop */ + const char *relay_transport_name; /* name of variable */ + char **relay_transport; /* relay transport:nexthop */ + const char *def_transport_name; /* name of variable */ + char **def_transport; /* default transport:nexthop */ + const char *snd_def_xp_maps_name; /* name of variable */ + char **snd_def_xp_maps; /* maptype:mapname */ + MAPS *snd_def_xp_info; /* handle */ + const char *relayhost_name; /* name of variable */ + char **relayhost; /* for relay and default transport */ + const char *snd_relay_maps_name; /* name of variable */ + char **snd_relay_maps; /* maptype:mapname */ + MAPS *snd_relay_info; /* handle */ + const char *transport_maps_name; /* name of variable */ + char **transport_maps; /* maptype:mapname */ + struct TRANSPORT_INFO *transport_info; /* handle */ +} RES_CONTEXT; + +#define RES_PARAM_VALUE(x) (*(x)) /* make it easy to do it right */ + +extern void resolve_init(void); +extern int resolve_proto(RES_CONTEXT *, VSTREAM *); +extern int resolve_class(const char *); + +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ -- cgit v1.2.3