NetBSD/gnu/dist/postfix/trivial-rewrite/resolve.c

273 lines
7.3 KiB
C

/*++
/* NAME
/* resolve 3
/* SUMMARY
/* mail address resolver
/* SYNOPSIS
/* #include "trivial-rewrite.h"
/*
/* void resolve_init(void)
/*
/* void resolve_proto(stream)
/* VSTREAM *stream;
/*
/* void resolve_addr(rule, addr, result)
/* char *rule;
/* char *addr;
/* VSTRING *result;
/* 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_proto() implements the client-server protocol:
/* read one address in FQDN form, reply with a (transport,
/* nexthop, internalized recipient) triple.
/*
/* resolve_addr() gives direct access to the address resolving
/* engine. It resolves an internalized address to a (transport,
/* nexthop, internalized recipient) triple.
/* STANDARDS
/* DIAGNOSTICS
/* Problems and transactions are logged to the syslog daemon.
/* 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>
/* 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 <mail_addr.h>
#include <rewrite_clnt.h>
#include <resolve_local.h>
#include <mail_conf.h>
#include <quote_822_local.h>
#include <tok822.h>
/* Application-specific. */
#include "trivial-rewrite.h"
#include "transport.h"
#define STR vstring_str
/* resolve_addr - resolve address according to rule set */
void resolve_addr(char *addr, VSTRING *channel, VSTRING *nexthop,
VSTRING *nextrcpt, int *flags)
{
VSTRING *addr_buf = vstring_alloc(100);
TOK822 *tree;
TOK822 *saved_domain = 0;
TOK822 *domain = 0;
*flags = 0;
/*
* The address is in internalized (unquoted) form, so we must externalize
* it first before we can parse it.
*/
quote_822_local(addr_buf, addr);
tree = tok822_scan_addr(vstring_str(addr_buf));
/*
* 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.
*/
#define RESOLVE_LOCAL(domain) \
resolve_local(STR(tok822_internalize(addr_buf, domain, TOK822_STR_DEFL)))
while (tree->head) {
/*
* Strip trailing dot or @.
*/
if (tree->tail->type == '.' || tree->tail->type == '@') {
tok822_free_tree(tok822_sub_keep_before(tree, tree->tail));
continue;
}
/*
* A lone empty string becomes the postmaster.
*/
if (tree->head == tree->tail && tree->head->type == TOK822_QSTRING
&& VSTRING_LEN(tree->head->vstr) == 0) {
tok822_free(tree->head);
tree->head = tok822_scan(MAIL_ADDR_POSTMASTER, &tree->tail);
rewrite_tree(REWRITE_CANON, tree);
}
/*
* Strip (and save) @domain if local.
*/
if ((domain = tok822_rfind_type(tree->tail, '@')) != 0) {
if (RESOLVE_LOCAL(domain->next) == 0)
break;
tok822_sub_keep_before(tree, domain);
if (saved_domain)
tok822_free_tree(saved_domain);
saved_domain = domain;
}
/*
* After stripping the local domain, if any, replace foo%bar by
* foo@bar, site!user by user@site, rewrite to canonical form, and
* retry.
*
* Otherwise we're done.
*/
if (tok822_rfind_type(tree->tail, '@')
|| (var_swap_bangpath && tok822_rfind_type(tree->tail, '!'))
|| (var_percent_hack && tok822_rfind_type(tree->tail, '%'))) {
rewrite_tree(REWRITE_CANON, tree);
} else {
domain = 0;
break;
}
}
/*
* If the destination is non-local, recognize routing operators in the
* address localpart. 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
* primary MX host is set up.
*/
if (domain && domain->prev)
if (tok822_rfind_type(domain->prev, '@') != 0
|| tok822_rfind_type(domain->prev, '!') != 0
|| tok822_rfind_type(domain->prev, '%') != 0)
*flags |= RESOLVE_FLAG_ROUTED;
/*
* 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));
}
}
tok822_internalize(nextrcpt, tree, TOK822_STR_DEFL);
/*
* The transport map overrides any transport and next-hop host info that
* is set up below. For a long time, it was not possible to override
* routing of mail that resolves locally, because Postfix used a
* zero-length next-hop hostname result to indicate local delivery, and
* transport maps cannot return zero-length hostnames.
*/
if (*var_transport_maps
&& transport_lookup(strrchr(STR(nextrcpt), '@') + 1, channel, nexthop)) {
/* void */ ;
}
/*
* Non-local delivery, presumably. Set up the default remote transport
* specified with var_def_transport. Use the destination's mail exchanger
* unless a default mail relay is specified with var_relayhost.
*/
else if (domain != 0) {
vstring_strcpy(channel, var_def_transport);
if (*var_relayhost)
vstring_strcpy(nexthop, var_relayhost);
else
tok822_internalize(nexthop, domain->next, TOK822_STR_DEFL);
}
/*
* Local delivery. Set up the default local transport and the default
* next-hop hostname (myself).
*/
else {
vstring_strcpy(channel, var_local_transport);
vstring_strcpy(nexthop, var_myhostname);
}
/*
* Clean up.
*/
if (saved_domain)
tok822_free_tree(saved_domain);
tok822_free_tree(tree);
vstring_free(addr_buf);
}
/* Static, so they can be used by the network protocol interface only. */
static VSTRING *channel;
static VSTRING *nexthop;
static VSTRING *nextrcpt;
static VSTRING *query;
/* resolve_proto - read request and send reply */
int resolve_proto(VSTREAM *stream)
{
int flags;
if (mail_scan(stream, "%s", query) != 1)
return (-1);
resolve_addr(STR(query), channel, nexthop, nextrcpt, &flags);
if (msg_verbose)
msg_info("%s -> (`%s' `%s' `%s' `%d')", STR(query), STR(channel),
STR(nexthop), STR(nextrcpt), flags);
mail_print(stream, "%s %s %s %d",
STR(channel), STR(nexthop), STR(nextrcpt), flags);
if (vstream_fflush(stream) != 0) {
msg_warn("write resolver reply: %m");
return (-1);
}
return (0);
}
/* resolve_init - module initializations */
void resolve_init(void)
{
query = vstring_alloc(100);
channel = vstring_alloc(100);
nexthop = vstring_alloc(100);
nextrcpt = vstring_alloc(100);
}