#include <sys_defs.h>
#include <stdlib.h>
#include <string.h>
#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
#endif
#include <msg.h>
#include <vstring.h>
#include <vstream.h>
#include <vstring_vstream.h>
#include <split_at.h>
#include <valid_utf8_hostname.h>
#include <stringops.h>
#include <mymalloc.h>
#include <mail_params.h>
#include <mail_proto.h>
#include <resolve_local.h>
#include <mail_conf.h>
#include <quote_822_local.h>
#include <tok822.h>
#include <domain_list.h>
#include <string_list.h>
#include <match_parent_style.h>
#include <maps.h>
#include <mail_addr_find.h>
#include <valid_mailhost_addr.h>
#include "trivial-rewrite.h"
#include "transport.h"
#define STR vstring_str
#define LEN VSTRING_LEN
static DOMAIN_LIST *relay_domains;
static STRING_LIST *virt_alias_doms;
static STRING_LIST *virt_mailbox_doms;
static MAPS *relocated_maps;
int resolve_class(const char *domain)
{
int ret;
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);
}
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");
addr_len = strlen(addr);
quote_822_local(addr_buf, addr);
tree = tok822_scan_addr(vstring_str(addr_buf));
#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; \
}
#define RESOLVE_LOCAL(domain) \
resolve_local(STR(tok822_internalize(addr_buf, domain, TOK822_STR_DEFL)))
for (loop_count = 0, loop_max = addr_len + 100; ; loop_count++) {
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;
}
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));
if (var_resolve_nulldom
&& tree->tail
&& tree->tail->type == '@')
tok822_free_tree(tok822_sub_keep_before(tree, tree->tail));
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;
}
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 (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;
}
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;
}
if (tree->head == 0) {
tree->head = tok822_scan(var_empty_addr, &tree->tail);
continue;
}
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;
}
domain = 0;
break;
}
vstring_free(addr_buf);
addr_buf = 0;
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);
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)
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;
if (*flags & RESOLVE_FLAG_ERROR) {
*flags |= RESOLVE_CLASS_DEFAULT;
FREE_MEMORY_AND_RETURN;
}
if (strcmp(STR(nextrcpt) + strcspn(STR(nextrcpt), "@!%") + 1, rcpt_domain))
*flags |= RESOLVE_FLAG_ROUTED;
#define STREQ(x,y) (strcmp((x), (y)) == 0)
if (domain != 0) {
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;
}
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 {
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;
}
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;
}
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;
}
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);
}
}
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;
}
if ((destination = split_at(STR(channel), ':')) != 0 && *destination)
vstring_strcpy(nexthop, destination);
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);
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;
}
}
#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);
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;
}
}
if (var_allow_min_user == 0 && STR(nextrcpt)[0] == '-') {
*flags |= RESOLVE_FLAG_ERROR;
FREE_MEMORY_AND_RETURN;
}
FREE_MEMORY_AND_RETURN;
}
static VSTRING *channel;
static VSTRING *nexthop;
static VSTRING *nextrcpt;
static VSTRING *query;
static VSTRING *sender;
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);
}
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);
}