summaryrefslogtreecommitdiffstats
path: root/src/trivial-rewrite/transport.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/trivial-rewrite/transport.c')
-rw-r--r--src/trivial-rewrite/transport.c438
1 files changed, 438 insertions, 0 deletions
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 <sys_defs.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <stringops.h>
+#include <mymalloc.h>
+#include <vstring.h>
+#include <split_at.h>
+#include <dict.h>
+#include <events.h>
+
+/* Global library. */
+
+#include <strip_addr.h>
+#include <mail_params.h>
+#include <mail_addr_find.h>
+#include <match_parent_style.h>
+#include <mail_proto.h>
+
+/* 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 <string.h>
+
+#include <mail_conf.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+
+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