#include <errno.h>
#include <string.h>
#include "smtpd.h"
#define MATCH_RESULT(r, neg) ((r) == -1 ? -1 : ((neg) < 0 ? !(r) : (r)))
static int
ruleset_match_tag(struct rule *r, const struct envelope *evp)
{
int ret;
struct table *table;
enum table_service service = K_STRING;
if (!r->flag_tag)
return 1;
if (r->flag_tag_regex)
service = K_REGEX;
table = table_find(env, r->table_tag);
ret = table_match(table, service, evp->tag);
return MATCH_RESULT(ret, r->flag_tag);
}
static int
ruleset_match_from(struct rule *r, const struct envelope *evp)
{
int ret;
int has_rdns;
const char *key;
struct table *table;
enum table_service service = K_NETADDR;
if (!r->flag_from)
return 1;
if (evp->flags & EF_INTERNAL) {
if (r->table_from == NULL)
return 0;
key = "local";
}
else if (r->flag_from_rdns) {
has_rdns = strcmp(evp->hostname, "<unknown>") != 0;
if (r->table_from == NULL)
return MATCH_RESULT(has_rdns, r->flag_from);
if (!has_rdns)
return 0;
key = evp->hostname;
}
else {
key = ss_to_text(&evp->ss);
if (r->flag_from_socket) {
if (strcmp(key, "local") == 0)
return MATCH_RESULT(1, r->flag_from);
else
return r->flag_from < 0 ? 1 : 0;
}
}
if (r->flag_from_regex)
service = K_REGEX;
table = table_find(env, r->table_from);
ret = table_match(table, service, key);
return MATCH_RESULT(ret, r->flag_from);
}
static int
ruleset_match_to(struct rule *r, const struct envelope *evp)
{
int ret;
struct table *table;
enum table_service service = K_DOMAIN;
if (!r->flag_for)
return 1;
if (r->flag_for_regex)
service = K_REGEX;
table = table_find(env, r->table_for);
ret = table_match(table, service, evp->dest.domain);
return MATCH_RESULT(ret, r->flag_for);
}
static int
ruleset_match_smtp_helo(struct rule *r, const struct envelope *evp)
{
int ret;
struct table *table;
enum table_service service = K_DOMAIN;
if (!r->flag_smtp_helo)
return 1;
if (r->flag_smtp_helo_regex)
service = K_REGEX;
table = table_find(env, r->table_smtp_helo);
ret = table_match(table, service, evp->helo);
return MATCH_RESULT(ret, r->flag_smtp_helo);
}
static int
ruleset_match_smtp_starttls(struct rule *r, const struct envelope *evp)
{
if (!r->flag_smtp_starttls)
return 1;
return -1;
}
static int
ruleset_match_smtp_auth(struct rule *r, const struct envelope *evp)
{
int ret;
struct table *table;
enum table_service service;
if (!r->flag_smtp_auth)
return 1;
if (!(evp->flags & EF_AUTHENTICATED))
ret = 0;
else if (r->table_smtp_auth) {
if (r->flag_smtp_auth_regex)
service = K_REGEX;
else
service = strchr(evp->username, '@') ?
K_MAILADDR : K_STRING;
table = table_find(env, r->table_smtp_auth);
ret = table_match(table, service, evp->username);
}
else
ret = 1;
return MATCH_RESULT(ret, r->flag_smtp_auth);
}
static int
ruleset_match_smtp_mail_from(struct rule *r, const struct envelope *evp)
{
int ret;
const char *key;
struct table *table;
enum table_service service = K_MAILADDR;
if (!r->flag_smtp_mail_from)
return 1;
if (r->flag_smtp_mail_from_regex)
service = K_REGEX;
if ((key = mailaddr_to_text(&evp->sender)) == NULL)
return -1;
table = table_find(env, r->table_smtp_mail_from);
ret = table_match(table, service, key);
return MATCH_RESULT(ret, r->flag_smtp_mail_from);
}
static int
ruleset_match_smtp_rcpt_to(struct rule *r, const struct envelope *evp)
{
int ret;
const char *key;
struct table *table;
enum table_service service = K_MAILADDR;
if (!r->flag_smtp_rcpt_to)
return 1;
if (r->flag_smtp_rcpt_to_regex)
service = K_REGEX;
if ((key = mailaddr_to_text(&evp->dest)) == NULL)
return -1;
table = table_find(env, r->table_smtp_rcpt_to);
ret = table_match(table, service, key);
return MATCH_RESULT(ret, r->flag_smtp_rcpt_to);
}
struct rule *
ruleset_match(const struct envelope *evp)
{
struct rule *r;
int i = 0;
#define MATCH_EVAL(x) \
switch ((x)) { \
case -1: goto tempfail; \
case 0: continue; \
default: break; \
}
TAILQ_FOREACH(r, env->sc_rules, r_entry) {
++i;
MATCH_EVAL(ruleset_match_tag(r, evp));
MATCH_EVAL(ruleset_match_from(r, evp));
MATCH_EVAL(ruleset_match_to(r, evp));
MATCH_EVAL(ruleset_match_smtp_helo(r, evp));
MATCH_EVAL(ruleset_match_smtp_auth(r, evp));
MATCH_EVAL(ruleset_match_smtp_starttls(r, evp));
MATCH_EVAL(ruleset_match_smtp_mail_from(r, evp));
MATCH_EVAL(ruleset_match_smtp_rcpt_to(r, evp));
goto matched;
}
#undef MATCH_EVAL
errno = 0;
log_trace(TRACE_RULES, "no rule matched");
return (NULL);
tempfail:
errno = EAGAIN;
log_trace(TRACE_RULES, "temporary failure in processing of a rule");
return (NULL);
matched:
log_trace(TRACE_RULES, "rule #%d matched: %s", i, rule_to_text(r));
return r;
}