summaryrefslogtreecommitdiffstats
path: root/src/trivial-rewrite/rewrite.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/trivial-rewrite/rewrite.c')
-rw-r--r--src/trivial-rewrite/rewrite.c303
1 files changed, 303 insertions, 0 deletions
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 <sys_defs.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <split_at.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_proto.h>
+#include <resolve_local.h>
+#include <tok822.h>
+#include <mail_conf.h>
+
+/* 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);
+}