/*
* relaytest.c -- module for ZMailer's smtpserver
* By Matti Aarnio <mea@nic.funet.fi> 1997-2003
*
*/
/*
* TODO:
* - Attribute 'request' initializations for resolving
* - Addresses in form <@foo:uu@dd>, <host!user>
*/
#include "hostenv.h"
#include "mailer.h"
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include "sleepycatdb.h"
#ifdef HAVE_NDBM
#define datum Ndatum
#include <ndbm.h>
#undef datum
#endif
#ifdef HAVE_GDBM
#define datum Gdatum
#include <gdbm.h>
#undef datum
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#ifdef HAVE_NETINET_IN6_H
#include <netinet/in6.h>
#endif
#ifdef HAVE_NETINET6_IN6_H
#include <netinet6/in6.h>
#endif
#ifdef HAVE_LINUX_IN6_H
#include <linux/in6.h>
#endif
#include <arpa/inet.h>
#endif
#include "libc.h"
#include "libz.h"
#define _POLICYTEST_INTERNAL_
#include "policytest.h"
/* We are not including "smtpserver.h", thus have to do local prototype.. */
#if defined(HAVE_STDARG_H) && defined(HAVE_VPRINTF)
extern void type __((void *, int code, const char *status, const char *fmt,...));
#else
extern void type __(( /* void *SS, int code, const char *status, const char *fmt, ... */ ));
#endif
extern int debug;
extern int percent_accept;
/* This is *NOT* the official prototype for type() !!! */
extern void type __((void *, const int code, const char *status, const char *fmt,...));
static int resolveattributes __((struct policytest *, int, struct policystate *, const char *, int));
static int check_domain __((struct policytest *, struct policystate *, const char *, int));
static int check_user __((struct policytest *, struct policystate *, const char *, int));
static int checkaddr __((struct policytest *, struct policystate *, const char *));
#if defined(AF_INET6) && defined(INET6)
extern const u_char zv4mapprefix[16];
#endif
/* KK() and KA() macroes are at "policy.h" */
static char *showkey __((const char *key));
static char *showkey(key)
const char *key;
{
static char buf[256];
if (key[1] != P_K_IPv4 && key[1] != P_K_IPv6) {
if (strlen(key+2) > (sizeof(buf) - 200))
sprintf(buf,"%d/%s/'%s'", key[0], KK(key[1]), "<too long name>");
else
sprintf(buf,"%d/%s/'%s'", key[0], KK(key[1]), key+2);
} else
if (key[1] == P_K_IPv4)
sprintf(buf,"%d/%s/%u.%u.%u.%u/%d",
key[0], KK(key[1]),
key[2] & 0xff, key[3] & 0xff, key[4] & 0xff, key[5] & 0xff,
key[6] & 0xff);
else
sprintf(buf,"%d/%s/%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x/%d",
key[0], KK(key[1]),
key[2] & 0xff, key[3] & 0xff, key[4] & 0xff, key[5] & 0xff,
key[6] & 0xff, key[7] & 0xff, key[8] & 0xff, key[9] & 0xff,
key[10] & 0xff, key[11] & 0xff, key[12] & 0xff, key[13] & 0xff,
key[14] & 0xff, key[15] & 0xff, key[16] & 0xff, key[17] & 0xff,
key[18] & 0xff);
return buf;
}
static char *showattr __((const unsigned char *key));
static char *showattr(key)
const unsigned char *key;
{
static char buf[500];
sprintf(buf,"%d/%s/'%s'", key[0], KA(key[1]), key+2);
return buf;
}
static int valueeq __((const char *value, const char *str));
static int valueeq(value,str)
const char *value, *str;
{
if (!value) return 0;
return (strcmp(value,str) == 0);
}
static char *showresults __((struct policystate *state));
static char *showresults(state)
struct policystate *state;
{
static char buf[2000];
int i;
char **values=state->values;
buf[0] = '\0';
for (i = P_A_FirstAttr; i <= P_A_LastAttr; ++i) {
sprintf(buf+strlen(buf),"%s ",KA(i));
if (i == P_A_InboundSizeLimit )
sprintf(buf+strlen(buf),"%li ",state->maxinsize);
else if (i == P_A_OutboundSizeLimit )
sprintf(buf+strlen(buf),"%li ",state->maxoutsize);
else if (i == P_A_MaxSameIpSource )
sprintf(buf+strlen(buf),"%li ",state->maxsameiplimit);
else
sprintf(buf+strlen(buf),"%s ",values[i] ? values[i] : ".");
}
return buf;
}
static void printstate __((const struct policystate *state));
static void printstate (state)
const struct policystate *state;
{
int i;
type(NULL,0,NULL," always_reject=%d",state->always_reject);
type(NULL,0,NULL," always_freeze=%d",state->always_freeze);
type(NULL,0,NULL," always_accept=%d",state->always_accept);
type(NULL,0,NULL," full_trust=%d", state->full_trust);
type(NULL,0,NULL," trust_recipients=%d",state->trust_recipients);
type(NULL,0,NULL," sender_reject=%d",state->sender_reject);
type(NULL,0,NULL," sender_freeze=%d",state->sender_freeze);
type(NULL,0,NULL," sender_norelay=%d",state->sender_norelay);
type(NULL,0,NULL," relaycustnet=%d", state->relaycustnet);
type(NULL,0,NULL," rcpt_nocheck=%d", state->rcpt_nocheck);
for ( i = P_A_FirstAttr; i <= P_A_LastAttr ; ++i) {
type(NULL,0,NULL," %s: %srequested, value=%s", KA(i),
(state->origrequest & (1<<i)) ? "" : "not ",
state->values[i]?state->values[i]:".");
}
type(NULL,0,NULL," maxinsize=%li", state->maxinsize);
type(NULL,0,NULL," maxoutsize=%li", state->maxoutsize);
type(NULL,0,NULL," maxsameiplimit=%li", state->maxsameiplimit);
}
static void mask_ip_bits __((unsigned char *, int, int));
static void mask_ip_bits(ipnum, width, maxwidth)
unsigned char *ipnum;
int width, maxwidth;
{
int i, bytewidth, bytemaxwidth;
bytemaxwidth = maxwidth >> 3; /* multiple of 8 */
bytewidth = (width + 7) >> 3;
/* All full zero bytes... */
for (i = bytewidth; i < bytemaxwidth; ++i)
ipnum[i] = 0;
/* Now the remaining byte */
i = 8 - (width & 7); /* Modulo 8 */
bytewidth = width >> 3;
if (i != 8) {
/* Not exactly multiple-of-byte-width operand to be masked */
/* For 'width=31' we get now 'bytewidth=3', and 'i=1' */
/* For 'width=25' we get now 'bytewidth=3', and 'i=7' */
ipnum[bytewidth] &= (0xFF << i);
}
}
void policydefine(relp, dbtype, dbpath)
struct policytest **relp;
const char *dbtype, *dbpath;
{
struct policytest *rel = (void *) emalloc(sizeof(*rel));
*relp = rel;
memset(rel, 0, sizeof(*rel));
rel->dbtype = strdup(dbtype);
rel->dbpath = strdup(dbpath);
rel->dbt = _dbt_none;
}
/* Do the actual query - return pointer to the result record */
static void *dbquery __((struct policytest *, const void *, const int, int *));
static void *dbquery(rel, qptr, qlen, rlenp)
struct policytest *rel;
const void *qptr;
const int qlen;
int *rlenp; /* result length ptr ! */
{
char *buffer;
#ifdef HAVE_NDBM
Ndatum Nkey, Nresult;
#endif
#ifdef HAVE_GDBM
Gdatum Gkey, Gresult;
#endif
#ifdef HAVE_DB
DBT Bkey, Bresult;
int rc;
#endif
switch (rel->dbt) {
#ifdef HAVE_NDBM
case _dbt_ndbm:
Nkey.dptr = (char *) qptr;
Nkey.dsize = qlen;
Nresult = dbm_fetch(rel->ndbm, Nkey);
if (Nresult.dptr == NULL)
return NULL;
buffer = (char *) emalloc(Nresult.dsize);
memcpy(buffer, Nresult.dptr, Nresult.dsize);
*rlenp = Nresult.dsize;
return buffer;
break; /* some compilers complain, some produce bad code
without this... */
#endif
#ifdef HAVE_GDBM
case _dbt_gdbm:
Gkey.dptr = (void *) qptr;
Gkey.dsize = qlen;
Gresult = gdbm_fetch(rel->gdbm, Gkey);
/* gdbm_fetch allocates memory for return data: Gresult.dptr
Must be freed later. */
*rlenp = Gresult.dsize;
return Gresult.dptr;
break; /* some compilers complain, some produce bad code
without this... */
#endif
#ifdef HAVE_DB
case _dbt_btree:
memset(&Bkey, 0, sizeof(Bkey));
memset(&Bresult, 0, sizeof(Bresult));
Bkey.data = (void *) qptr;
Bkey.size = qlen;
#ifdef DB_INIT_TXN
rc = (rel->btree->get) (rel->btree, NULL, &Bkey, &Bresult, 0);
#else
rc = (rel->btree->get) (rel->btree, &Bkey, &Bresult, 0);
#endif
if (rc != 0)
return NULL;
buffer = (char *) emalloc(Bresult.size);
memcpy(buffer, Bresult.data, Bresult.size);
*rlenp = Bresult.size;
return buffer;
break; /* some compilers complain, some produce bad code
without this... */
case _dbt_bhash:
memset(&Bkey, 0, sizeof(Bkey));
memset(&Bresult, 0, sizeof(Bresult));
Bkey.data = (void *) qptr;
Bkey.size = qlen;
#ifdef DB_INIT_TXN
rc = (rel->bhash->get) (rel->bhash, NULL, &Bkey, &Bresult, 0);
#else
rc = (rel->bhash->get) (rel->bhash, &Bkey, &Bresult, 0);
#endif
if (rc != 0)
return NULL;
buffer = (char *) emalloc(Bresult.size);
memcpy(buffer, Bresult.data, Bresult.size);
*rlenp = Bresult.size;
return buffer;
break; /* some compilers complain, some produce bad code
without this... */
#endif
default:
break;
}
return NULL;
}
/******************************************************************************
* Function: resolveattributes()
*
* recursions - Max recursive calls for parsing aliases.
* state.request - These bit flags say which attributes are checked.
* state.values[] - Attribute values are stored here. Both are indexed
* according to the attribute constants. (P_A_...)
* key - Pointer to search record. Binary address, ascii alias or
* ascii domain name.
* init - Init flag. Give value 1. Value 0 when called recursively
* by itself.
* -------------
* Returns 0, when it has found something
*****************************************************************************/
static int resolveattributes(rel, recursions, state, key, init)
struct policytest *rel;
int recursions;
struct policystate *state;
const char *key;
int init;
{
unsigned char *str, *str_base;
int rlen, result, interest;
char *msgstr = NULL;
if (init) {
/* First call of this function. Not called recursively. */
/* Zero return value array. */
int i;
for (i = 0; i <= P_A_LastAttr; ++i) {
if (state->values[i]) free(state->values[i]);
if (state->messages[i]) free(state->messages[i]);
}
memset(state->values, 0, sizeof(state->values));
memset(state->messages, 0, sizeof(state->messages));
state->origrequest = state->request;
}
--recursions;
if (debug)
type(NULL,0,NULL," Key: %s", showkey(key));
/*
if (key[1] != P_K_IPv4 && key[1] != P_K_IPv6) {
if (debug)
type(NULL,0,NULL," Key: %d/%d/%s", key[0],key[1],key+2);
} else
if (debug)
type(NULL,0,NULL," Key: %u.%u.%u.%u", key[2] & 0xff, key[3] & 0xff,
key[4] & 0xff, key[5] & 0xff);
*/
str_base = str = (char *) dbquery(rel, &key[0], key[0], &rlen);
/* str[0] - attribute list lenght
str[1] - attribute numeric value
str[2...] - attribute flag string */
if (str == NULL) {
if (debug)
type(NULL,0,NULL," query failed");
return -1;
}
/* Scan trough attribute list. Call resolveattributes recursively
if aliases is found */
while (rlen > 3) {
if (str[0] < 3) {
if (debug)
type(NULL,0,NULL," Bad length of attrbute, under 3 bytes! %d", str[0]);
break; /* BAD ATTRIBUTE! */
}
/* Attribute */
if (debug)
type(NULL,0,NULL," Attribute: %s", showattr(str));
/* Alias */
if (str[1] == P_A_ALIAS) {
/* Do not continue if max recursions reached. */
if (recursions < 0) {
if (debug)
type(NULL,0,NULL," Max recursions reached.");
} else {
char pbuf[256];
if (debug)
type(NULL,0,NULL," Alias-recursion: %d", recursions);
strncpy(pbuf+2,str+2, sizeof(pbuf)-3);
pbuf[ sizeof(pbuf)-1 ] = 0;
strlower(pbuf+2);
pbuf[0] = strlen(str+2) + 3;
pbuf[1] = P_K_TAG;
result = resolveattributes(rel, recursions, state, pbuf, 0);
}
rlen -= str[0];
str += str[0];
continue;
}
if (str[1] == P_A_MESSAGE) {
if (msgstr) free(msgstr);
msgstr = strdup(str+2);
goto nextattr;
}
interest = 1 << str[1]; /* Convert attrib. num. value into flag bit */
if ((interest & state->request) == 0) {
/* Not interested in this attribute, skip into next. */
if (debug)
type(NULL,0,NULL," not interested, skipped...");
goto nextattr;
} else {
/* Mask it off. */
state->request &= ~interest;
}
if (P_A_FirstAttr <= str[1] && str[1] <= P_A_LastAttr) {
/* If a message was given in previous attribute, pick it! */
state->messages[0xFF & (str[1])] = msgstr;
msgstr = NULL;
}
if (str[1] == P_A_InboundSizeLimit) {
sscanf(str+2,"%li", &state->maxinsize);
goto nextattr;
} else if (str[1] == P_A_OutboundSizeLimit) {
sscanf(str+2,"%li", &state->maxoutsize);
goto nextattr;
} else if (str[1] == P_A_MaxSameIpSource) {
sscanf(str+2,"%li", &state->maxsameiplimit);
goto nextattr;
} else if ((str[2] != '+' && str[2] != '-') &&
!state->values[str[1] & 0xFF]) {
/* Supply suffix domain (set), e.g.:
RBL.MAPS.VIX.COM,DUL.MAPS.VIX.COM
whatever you want ... */
state->values[str[1] & 0xFF] = strdup(str + 2);
} else if (str[2] != '+' && str[2] != '-') {
if (debug)
type(NULL,0,NULL," Unknown flag: %s", &str[2]);
goto nextattr;
}
/* Store valid attribute.
str[1] is attributes id constant, str[2] attribute flag. */
if (P_A_FirstAttr <= str[1] && str[1] <= P_A_LastAttr) {
if (!state->values[str[1] & 0xFF])
state->values[str[1] & 0xFF] = strdup(str + 2);
if (debug)
type(NULL,0,NULL," accepted!");
} else {
if (debug)
type(NULL,0,NULL," Unknown attribute, number: %d", str[1]);
}
nextattr:
/* If this wasn't the P_A_MESSAGE, we drop possibly
existing message here.. */
if (str[1] != P_A_MESSAGE) {
if (msgstr) free(msgstr);
msgstr = NULL;
}
rlen -= str[0];
str += str[0];
/* If all requests are done, exit. */
if (!state->request) {
if (debug)
type(NULL,0,NULL," Every request found. Finishing search.");
break;
}
} /* End of while. */
/* Free memory from attribute list. Allocated in dbquery. */
if (str_base)
free(str_base);
if (msgstr) free(msgstr);
return 0;
}
/* Return 0, when found something */
static int checkaddr(rel, state, pbuf)
struct policytest *rel;
struct policystate *state;
const char *pbuf;
{
int result, count, countmax;
int maxrecursions;
maxrecursions = 5;
if (pbuf[1] == P_K_DOMAIN) {
if (debug)
type(NULL,0,NULL," checkaddr(): domain of '%s'",pbuf+2);
result = resolveattributes(rel, maxrecursions, state, pbuf, 1);
if (debug) {
type(NULL,0,NULL," Results: %s", showresults(state));
}
return (result);
}
if (pbuf[1] == P_K_USER) {
if (debug)
type(NULL,0,NULL," checkaddr(): user of '%s'",pbuf+2);
result = resolveattributes(rel, maxrecursions, state, pbuf, 1);
if (debug) {
type(NULL,0,NULL," Results: %s", showresults(state));
}
return (result);
} else if (pbuf[1] == P_K_IPv4)
countmax = 32;
else
countmax = 128;
result = 1;
count = 0;
while (result != 0 && count <= countmax) {
count++;
/* Search database */
result = resolveattributes(rel, maxrecursions, state, pbuf, 1);
if (result == 0) /* Found. */
break;
mask_ip_bits((u_char*)&pbuf[2], countmax - count, countmax);
if (pbuf[1] == P_K_IPv4)
((char*)pbuf)[6] = 32 - count; /* Width */
#if defined(AF_INET6) && defined(INET6)
else /* AF_INET6 */
((char*)pbuf)[18] = 128 - count;
#endif
}
#if defined(AF_INET6) && defined(INET6)
/* Umm.. looked for IPv6 address ? And nothing found ?
Try then to look for the wild-card [0.0.0.0]/0 entry. */
if (result != 0 && pbuf[1] == P_K_IPv6) {
memset((char*)pbuf,0,6);
((char*)pbuf)[0] = 7;
((char*)pbuf)[1] = P_K_IPv4;
((char*)pbuf)[6] = 0;
/* Search database */
result = resolveattributes(rel, maxrecursions, state, pbuf, 1);
}
#endif
if (result != 0) {
if (debug)
type(NULL,0,NULL," Address not found.");
return -1;
} else
if (debug) {
type(NULL,0,NULL," Results: %s", showresults(state));
}
return 0;
}
int policyinit(relp, state, whosonrc)
struct policytest **relp;
struct policystate *state;
int whosonrc;
{
int openok;
char *dbname;
struct policytest *rel = *relp;
if (rel == NULL)
return -1; /* Not defined! */
memset(state, 0, sizeof(*state));
#ifdef HAVE_NDBM
if (cistrcmp(rel->dbtype, "ndbm") == 0)
rel->dbt = _dbt_ndbm;
#endif
#ifdef HAVE_GDBM
if (cistrcmp(rel->dbtype, "gdbm") == 0)
rel->dbt = _dbt_gdbm;
#endif
#ifdef HAVE_DB
if (cistrcmp(rel->dbtype, "btree") == 0)
rel->dbt = _dbt_btree;
if (cistrcmp(rel->dbtype, "bhash") == 0)
rel->dbt = _dbt_bhash;
#endif
if (rel->dbt == _dbt_none) {
/* XX: ERROR! Unknown/unsupported dbtype! */
*relp = NULL;
return 1;
}
openok = 0;
#ifdef HAVE_ALLOCA
dbname = (char*)alloca(strlen(rel->dbpath) + 8);
#else
dbname = (char*)emalloc(strlen(rel->dbpath) + 8);
#endif
switch (rel->dbt) {
#ifdef HAVE_NDBM
case _dbt_ndbm:
/*
rel->ndbm = dbm_open((char*)rel->dbpath, O_RDWR|O_CREAT|O_TRUNC, 0644);
*/
strcpy(dbname, rel->dbpath);
rel->ndbm = dbm_open(dbname, O_RDONLY, 0644);
openok = (rel->ndbm != NULL);
break;
#endif
#ifdef HAVE_GDBM
case _dbt_gdbm:
/* Append '.gdbm' to the name */
sprintf(dbname, "%s.gdbm", rel->dbpath);
rel->gdbm = gdbm_open(dbname, 0, GDBM_READER, 0644, NULL);
openok = (rel->gdbm != NULL);
break;
#endif
#ifdef HAVE_DB
case _dbt_btree:
/* Append '.db' to the name */
sprintf(dbname, "%s.db", rel->dbpath);
#if defined(HAVE_DB3) || defined(HAVE_DB4)
rel->btree = NULL;
openok = db_create(&rel->btree, NULL, 0);
if (openok == 0)
openok = rel->btree->open(rel->btree,
#if (DB_VERSION_MAJOR == 4) && (DB_VERSION_MINOR == 1)
NULL, /* TXN id was added at SleepyDB 4.1 */
#endif
dbname, NULL, DB_BTREE,
DB_RDONLY, 0);
if (debug && openok)
type(NULL,0,NULL," btree->open('%s',BTREE, RDONLY) ret=%d",dbname,openok);
openok = !openok;
#else
#if defined(HAVE_DB2)
rel->btree = NULL;
#ifndef DB_RDONLY
# define DB_RDONLY O_RDONLY
#endif
openok = db_open(dbname, DB_BTREE, DB_RDONLY, 0644,
NULL, NULL, &rel->btree);
openok = !openok;
#else /* HAVE_DB1 */
rel->btree = dbopen(dbname, O_RDONLY, 0644, DB_BTREE, NULL);
openok = (rel->btree != NULL);
#endif
#endif
break;
case _dbt_bhash:
/* Append '.db' to the name */
sprintf(dbname, "%s.dbh", rel->dbpath);
#if defined(HAVE_DB3) || defined(HAVE_DB4)
rel->bhash = NULL;
openok = db_create(&rel->bhash, NULL, 0);
if (openok == 0)
openok = rel->bhash->open(rel->bhash,
#if (DB_VERSION_MAJOR == 4) && (DB_VERSION_MINOR == 1)
NULL, /* TXN id was added at SleepyDB 4.1 */
#endif
dbname, NULL, DB_HASH,
DB_RDONLY, 0);
if (debug && openok)
type(NULL,0,NULL," bhash->open('%s',BHASH, RDONLY) ret=%d",dbname,openok);
openok = !openok;
#else
#if defined(HAVE_DB2)
rel->bhash = NULL;
#ifndef DB_RDONLY
# define DB_RDONLY O_RDONLY
#endif
openok = db_open(dbname, DB_HASH, DB_RDONLY, 0644,
NULL, NULL, &rel->bhash);
openok = !openok;
#else /* HAVE_DB1 */
rel->bhash = dbopen(rel->dbpath, O_RDONLY, 0644, DB_HASH, NULL);
openok = (rel->bhash != NULL);
#endif
#endif
break;
#endif
default:
break;
}
if (!openok) {
/* ERROR! Could not open the database! */
if (debug) {
type(NULL,0,NULL," ERROR! Could not open the database file '%s'; errno=%d!",
dbname, errno);
fflush(stdout);
}
*relp = NULL;
#ifndef HAVE_ALLOCA
free(dbname);
#endif
return 2;
}
#ifndef HAVE_ALLOCA
free(dbname);
#endif
#ifdef HAVE_WHOSON_H
state->whoson_result = whosonrc;
#endif
state->maxsameiplimit = -1;
return 0;
}
static int _addrtest_ __((struct policytest *rel, struct policystate *state, const char *pbuf, int sourceaddr));
static int _addrtest_(rel, state, pbuf, sourceaddr)
struct policytest *rel;
struct policystate *state;
const char *pbuf;
int sourceaddr;
{
u_char ipaddr[16];
int ipaf = pbuf[1];
int myaddress, lcldom;
Usockaddr saddr;
/* Prepare for automatic match of the address */
memset(&saddr, 0, sizeof(saddr));
if (pbuf[1] == P_K_IPv4) {
memcpy(ipaddr, pbuf+2, 4);
memcpy(& saddr.v4.sin_addr, pbuf+2, 4);
saddr.v4.sin_family = AF_INET;
}
if (pbuf[1] == P_K_IPv6) {
memcpy(ipaddr, pbuf+2, 16);
#if defined(AF_INET6) && defined(INET6)
memcpy(& saddr.v6.sin6_addr, pbuf+2, 4);
saddr.v6.sin6_family = AF_INET6;
#endif
}
lcldom = (state->request & (1 << P_A_LocalDomain));
myaddress = matchmyaddress(&saddr);
if (debug)
type(NULL,0,NULL," policytestaddr: lcldom/myaddress=%d/%d",lcldom,myaddress);
/* state->request initialization !! */
if (sourceaddr)
state->request = ( 1 << P_A_REJECTNET |
1 << P_A_FREEZENET |
1 << P_A_RELAYCUSTNET |
1 << P_A_InboundSizeLimit |
1 << P_A_OutboundSizeLimit |
1 << P_A_FullTrustNet |
1 << P_A_TrustRecipients |
1 << P_A_TrustWhosOn |
1 << P_A_Filtering |
1 << P_A_MaxSameIpSource );
if (!myaddress)
state->request |= ( 1 << P_A_TestDnsRBL |
1 << P_A_RcptDnsRBL );
state->maxinsize = -1;
state->maxoutsize = -1;
if (checkaddr(rel, state, pbuf) != 0)
return 0; /* Nothing found */
if (myaddress && lcldom) {
if (state->values[P_A_LocalDomain]) free(state->values[P_A_LocalDomain]);
state->values[P_A_LocalDomain] = strdup("+");
if (state->values[P_A_RELAYTARGET]) free(state->values[P_A_RELAYTARGET]);
state->values[P_A_RELAYTARGET] = strdup("+");
if (state->values[P_A_TestDnsRBL]) free(state->values[P_A_TestDnsRBL]);
state->values[P_A_TestDnsRBL] = NULL;
if (state->values[P_A_RcptDnsRBL]) free(state->values[P_A_RcptDnsRBL]);
state->values[P_A_RcptDnsRBL] = NULL;
if (state->values[P_A_Filtering]) {
if (debug)
type(NULL,0,NULL," policytestaddr: 'filter %s' found",
state->values[P_A_Filtering]);
if (valueeq(state->values[P_A_Filtering], "+")) {
state->content_filter = 1;
} else {
state->content_filter = 0;
}
}
if (debug)
type(NULL,0,NULL," Results: %s", showresults(state));
return 0;
}
if (!sourceaddr)
goto just_rbl_checks;
#if 0
/* if (IP address of SMTP client has 'rejectnet +' attribute) then
any further conversation refused
[state->always_reject = 1; return -1;]
...
if (IP address of SMTP client has 'freezenet +' attribute) then
we present happy face, but always put the messages into a freezer..
[state->always_freeze = 1; return -1;]
if (IP address of SMTP client has 'relaycustnet +' attribute) then
sender accepted, recipients not checked
[state->always_accept = 1; return 0;]
...
Except that:
if (HELO-name of SMTP client has 'rejectnet +' attribute) then
any further conversation refused
[state->always_reject = 1; return -1;]
else if (sender's domain has 'rejectsource +' attribute) then
sender rejected, any further conversation refused
[state->sender_reject = 1; return -1;]
*/
#endif
if (state->message != NULL)
free(state->message);
state->message = NULL;
#define PICK_PA_MSG(attrib) \
if (state->message) free(state->message); \
state->message = state->messages[(attrib)]; \
state->messages[(attrib)] = NULL
if (valueeq(state->values[P_A_REJECTNET], "+")) {
if (debug)
type(NULL,0,NULL," policytestaddr: 'rejectnet +' found");
PICK_PA_MSG(P_A_REJECTNET);
if (state->message == NULL)
state->message = strdup("Your network address is blackholed in our static tables");
state->always_reject = 1;
return -1;
}
if (valueeq(state->values[P_A_FREEZENET], "+")) {
if (debug)
type(NULL,0,NULL," policytestaddr: 'freezenet +' found");
PICK_PA_MSG(P_A_FREEZENET);
if (state->message == NULL)
state->message = strdup("Your network address is blackholed in our static tables");
state->always_freeze = 1;
return 1;
}
if (valueeq(state->values[P_A_TrustRecipients], "+")) {
if (debug)
type(NULL,0,NULL," policytestaddr: 'trustrecipients +' found");
state->trust_recipients = 1;
PICK_PA_MSG(P_A_TrustRecipients);
}
if (valueeq(state->values[P_A_FullTrustNet], "+")) {
if (debug)
type(NULL,0,NULL," policytestaddr: 'fulltrustnet +' found");
state->full_trust = 1;
PICK_PA_MSG(P_A_FullTrustNet);
}
#ifdef HAVE_WHOSON_H
if (valueeq(state->values[P_A_TrustWhosOn], "+")) {
if (debug)
type(NULL,0,NULL," policytestaddr: 'trust-whoson +' found, accept? = %d",
(state->whoson_result == 0));
if (state->whoson_result == 0)
state->always_accept = 1;
PICK_PA_MSG(P_A_TrustWhosOn);
}
#endif
if (valueeq(state->values[P_A_RELAYCUSTNET], "+")) {
if (debug)
type(NULL,0,NULL," policytestaddr: 'relaycustnet +' found");
state->always_accept = 1;
PICK_PA_MSG(P_A_RELAYCUSTNET);
}
if (state->values[P_A_Filtering]) {
if (debug)
type(NULL,0,NULL," policytestaddr: 'filter %s' found",
state->values[P_A_Filtering]);
if (valueeq(state->values[P_A_Filtering], "+")) {
state->content_filter = 1;
} else {
state->content_filter = 0;
}
}
if (state->trust_recipients || state->full_trust || state->always_accept)
return 0;
just_rbl_checks:;
if (state->values[P_A_Filtering]) {
if (debug)
type(NULL,0,NULL," policytestaddr: 'filter %s' found",
state->values[P_A_Filtering]);
if (valueeq(state->values[P_A_Filtering], "+")) {
state->content_filter = 1;
} else {
state->content_filter = 0;
}
}
if (state->values[P_A_TestDnsRBL] &&
!valueeq(state->values[P_A_TestDnsRBL], "-")) {
int rc;
if (debug)
type(NULL,0,NULL," policytestaddr: 'test-dns-rbl %s' found;",
state->values[P_A_TestDnsRBL]);
rc = rbl_dns_test(ipaf, ipaddr, state->values[P_A_TestDnsRBL], &state->message);
if (!state->message){ PICK_PA_MSG(P_A_TestDnsRBL); }
if (debug)
type(NULL,0,NULL," rc=%d; msg='%s'",
rc, state->message ? state->message : "<nil>");
return rc;
}
/* bag = Andrey Blochintsev <bag@iptelecom.net.ua> */
/* bag + */
if (state->values[P_A_RcptDnsRBL] &&
state->values[P_A_RcptDnsRBL][0] == '_') {
int rc = 1;
if (debug)
type(NULL,0,NULL," policytestaddr: 'rcpt-dns-rbl %s' found;",
state->values[P_A_RcptDnsRBL]);
if (state->values[P_A_RcptDnsRBL][1] != '+') {
if (state->rblmsg != NULL)
free(state->rblmsg);
state->rblmsg = strdup(state->values[P_A_RcptDnsRBL] + 1);
rc = 0;
}
if (!state->message){ PICK_PA_MSG(P_A_RcptDnsRBL); }
if (debug)
type(NULL,0,NULL," rc=%d", rc);
return 0; /* We report error LATER */
}
/* bag - */
if (state->values[P_A_RcptDnsRBL] &&
!valueeq(state->values[P_A_RcptDnsRBL], "-")) {
int rc;
if (debug)
type(NULL,0,NULL," policytestaddr: 'rcpt-dns-rbl %s' found;",
state->values[P_A_RcptDnsRBL]);
rc = rbl_dns_test(ipaf, ipaddr, state->values[P_A_RcptDnsRBL], &state->rblmsg);
if (debug)
type(NULL, 0, NULL, "rcpt-dns-rbl test yields: rc=%d rblmsg='%s'", rc,
state->rblmsg ? state->rblmsg : "<none>");
if (!state->message){ PICK_PA_MSG(P_A_RcptDnsRBL); }
if (debug)
type(NULL,0,NULL," rc=%d", rc);
return 0; /* We report error LATER */
}
return 0;
}
int policytestaddr(rel, state, what, raddr)
struct policytest *rel;
struct policystate *state;
PolicyTest what;
Usockaddr *raddr;
{
char pbuf[64]; /* Not THAT much space needed.. */
int rc;
struct sockaddr_in *si4;
#if defined(AF_INET6) && defined(INET6)
struct sockaddr_in6 *si6;
#endif
if (what != POLICY_SOURCEADDR)
abort(); /* Urgle..! Code mismatch! */
if (rel == NULL)
return 0;
/* Find address match -- IPv4 mapped into IPv6 space too! */
state->message = NULL; /* This is early initial clearing */
if (raddr->v4.sin_family == 0){
state->full_trust = 1;
return 0; /* Interactive testing... */
}
if (raddr->v4.sin_family == AF_INET) {
si4 = & (raddr->v4);
pbuf[0] = 7;
pbuf[1] = P_K_IPv4;
memcpy(&pbuf[2], (char *) &si4->sin_addr.s_addr, 4);
pbuf[6] = 32; /* 32 bits */
} else
#if defined(AF_INET6) && defined(INET6)
if (raddr->v6.sin6_family == AF_INET6) {
si6 = & (raddr->v6);
if (memcmp((void *)&si6->sin6_addr, zv4mapprefix, 12) == 0) {
/* This is IPv4 address mapped into IPv6 */
pbuf[0] = 7;
pbuf[1] = P_K_IPv4;
memcpy(pbuf+2, ((char *) &si6->sin6_addr) + 12, 4);
pbuf[6] = 32; /* 32 bits */
} else {
pbuf[0] = 19;
pbuf[1] = P_K_IPv6;
memcpy(pbuf+2, ((char *) &si6->sin6_addr), 16);
pbuf[18] = 128; /* 128 bits */
}
} else
#endif
{
type(NULL,0,NULL,"Unknown address format; sa_family = %d",
raddr->v4.sin_family);
return -2;
}
state->request = 0;
state->content_filter = -1;
rc = _addrtest_(rel, state, pbuf, 1);
if (debug) fflush(stdout);
return rc;
}
static int check_domain(rel, state, input, inlen)
struct policytest *rel;
struct policystate *state;
const char *input;
int inlen;
{
char *ptr, *ptr2, pbuf[256];
int addr_len, i, plen, result;
#if 0
/* Get address after @ */
ptr = strchr(input, '@');
if (ptr == NULL) {
printf("Invalid address. @ not found!\n");
exit(0);
}
ptr++;
addr_len = inlen - (ptr - input);
#else
ptr = (char*)input;
addr_len = inlen;
#endif
/* Convert to lower case. */
if (addr_len > sizeof(pbuf)-3)
addr_len = sizeof(pbuf)-3;
strncpy(pbuf+2, ptr, addr_len);
pbuf[2+addr_len] = 0;
strlower(pbuf+2);
if (pbuf[2] == '[') {
/* IP address literal ??? */
if (strncmp(pbuf+2+1,"ipv6",4)==0) {
#if defined(AF_INET6) && defined(INET6)
char *s = strchr(pbuf+3,']');
if (s) *s = 0;
if (inet_pton(AF_INET6, pbuf+3+5, pbuf+2) < 1) {
/* XX: Duh ? Our input is syntax checked, so
this ERROR should not happen.. */
}
pbuf[0] = 19;
pbuf[1] = P_K_IPv6;
pbuf[18] = 128;
#else
/* XXX: Duh ??? IPv6 not supported, how to report errs ?? */
#endif
} else {
char *s = strchr(pbuf+3,']');
if (s) *s = 0;
if (inet_pton(AF_INET, pbuf+3, (u_char *)pbuf+2) < 1) {
/* XX: Duh ? Our input is syntax checked, so
this ERROR should not happen.. */
}
pbuf[0] = 7;
pbuf[1] = P_K_IPv4;
pbuf[6] = 32;
}
return _addrtest_(rel,state,pbuf, 0);
}
plen = addr_len;
/* '\0' not included in inlen... */
plen += 1 + 2;
pbuf[0] = plen;
pbuf[1] = P_K_DOMAIN;
result = 1;
while (result != 0) {
if (debug)
type(NULL,0,NULL," DEBUG: %s", showkey(pbuf));
result = checkaddr(rel, state, pbuf);
if (result == 0) /* Found! */
return 0;
if (pbuf[2] != '.') {
/* Put '.' in the beginning */
for (i = pbuf[0]; i >= 2; --i) {
pbuf[i + 1] = pbuf[i];
}
pbuf[2] = '.';
pbuf[0] += 1;
} else {
/* Test with shorter address. */
ptr = &pbuf[3];
while (*ptr != 0 && *ptr != '.')
ptr++;
if (*ptr == '\0') {
/* Quit the loop if everything is examined. */
if (pbuf[2] == '.' && pbuf[3] == '\0')
break;
pbuf[2] = '.';
pbuf[3] = '\0';
} else {
ptr++;
ptr2 = &pbuf[3];
while (*ptr != '\0') {
*ptr2++ = *ptr++;
}
*ptr2++ = *ptr++;
}
pbuf[0] = strlen(&pbuf[2]) + 1 + 2;
}
}
return 0; /* Nothing found */
}
static const char * find_nonqchr __((const char *, int, int));
static const char *
find_nonqchr(input, chr, inlen)
const char *input;
int chr, inlen;
{
int quote = 0;
/* Find first unquoted ``chr'' character, and return a pointer to it */
for (; inlen > 0; --inlen,++input) {
if (*input == '"')
quote = !quote;
if (*input == '\\') {
--inlen; ++input;
continue;
}
if (*input == chr && !quote)
return input;
}
return NULL;
}
/* Return 0, when found something */
static int check_user(rel, state, input, inlen)
struct policytest *rel;
struct policystate *state;
const char *input;
int inlen;
{
char pbuf[512];
const char *at;
int result;
if (inlen > (sizeof(pbuf) - 3))
inlen = sizeof(pbuf) - 3;
/* Store the MAIL FROM:<user@domain> into a temporary buffer, and
lowercasify it */
strncpy(pbuf+2, input, inlen);
pbuf[2+inlen] = 0;
strlower(pbuf + 2);
at = find_nonqchr(pbuf + 2, '@', inlen);
if (!at) return 0;
pbuf[inlen + 2] = '\0';
pbuf[0] = inlen + 1 + 2;
pbuf[1] = P_K_USER;
result = checkaddr(rel, state, pbuf);
if (result == 0) /* Found! */
return result;
/* 'user@' */
inlen = (at+1 - pbuf) - 2;
pbuf[inlen + 2] = '\0';
pbuf[0] = inlen + 1 + 2;
pbuf[1] = P_K_USER;
result = checkaddr(rel, state, pbuf);
return result;
}
static int pt_heloname __((struct policytest *, struct policystate *, const char *, const int));
static int pt_mailfrom __((struct policytest *, struct policystate *, const char *, const int));
static int pt_rcptto __((struct policytest *, struct policystate *, const char *, const int));
static int pt_rcptpostmaster __((struct policytest *, struct policystate *, const char *, const int));
static int pt_heloname(rel, state, str, len)
struct policytest *rel;
struct policystate *state;
const char *str;
const int len;
{
if (state->always_reject)
return -1;
if (state->always_freeze)
return 1;
if (state->always_accept)
return 0;
if (state->full_trust)
return 0;
/*
* This is somewhat controversial.
* This exists solely to allow simplification of
* smtpserver.conf file by having the HELO/EHLO
* strings to be rejected stored in the policy
* database.
*
* In Sep-2001 it became apparent that very least
* there is no point in analyzing '[...]' numeric
* HELO parameter contained address data.
*
* Current code will only look for the input string
* in the database (by domain lookup protocol), and
* react on it by smalling the door shut (if any).
*
*/
if (*str != '[') { /* Don't test address literals! */
/* state->request initialization !! */
state->request = ( 1 << P_A_REJECTNET |
1 << P_A_FREEZENET );
check_domain(rel, state, str, len);
/*
# if (name of SMTP client has 'rejectnet +' attribute) then
# any further conversation refused
# [state->always_reject = 1; return -1;]
*/
if (valueeq(state->values[P_A_REJECTNET], "+")) {
state->always_reject = 1;
PICK_PA_MSG(P_A_REJECTNET);
return -1;
}
if (valueeq(state->values[P_A_FREEZENET], "+")) {
state->always_freeze = 1;
PICK_PA_MSG(P_A_FREEZENET);
return 1;
}
}
return 0;
}
static int pt_sourcedomain(rel, state, str, len)
struct policytest *rel;
struct policystate *state;
const char *str;
const int len;
{
if (state->always_reject)
return -1;
if (state->always_freeze)
return 1;
if (state->always_accept)
return 0;
if (state->full_trust)
return 0;
/* state->request initialization !! */
state->request = ( 1 << P_A_REJECTNET |
1 << P_A_FREEZENET |
1 << P_A_RELAYCUSTNET |
1 << P_A_InboundSizeLimit |
1 << P_A_OutboundSizeLimit );
state->request |= ( 1 << P_A_RcptDnsRBL ); /* bag */
check_domain(rel, state, str, len);
/*
# if (name of SMTP client has 'rejectnet +' attribute) then
# any further conversation refused
# [state->always_reject = 1; return -1;]
*/
if (valueeq(state->values[P_A_REJECTNET], "+")) {
state->always_reject = 1;
PICK_PA_MSG(P_A_REJECTNET);
return -1;
}
if (valueeq(state->values[P_A_FREEZENET], "+")) {
state->always_freeze = 1;
PICK_PA_MSG(P_A_FREEZENET);
return 1;
}
if (valueeq(state->values[P_A_RELAYCUSTNET], "+")) {
if (debug)
type(NULL,0,NULL," pt_sourceaddr: 'relaycustnet +' found");
state->always_accept = 1;
PICK_PA_MSG(P_A_RELAYCUSTNET);
return 0;
}
if (valueeq(state->values[P_A_FullTrustNet], "+")) {
if (debug)
type(NULL,0,NULL," pt_sourceaddr: 'fulltrustnet +' found");
state->full_trust = 1;
PICK_PA_MSG(P_A_FullTrustNet);
return 0;
}
/* bag + */
if (state->rblmsg == NULL && /* only if no rbl_message before (from lookup by net) */
state->values[P_A_RcptDnsRBL] &&
state->values[P_A_RcptDnsRBL][0] == '_') {
if (debug)
type(NULL,0,NULL," pt_sourceaddr: 'rcpt-dns-rbl %s' found;",
state->values[P_A_RcptDnsRBL]);
if (state->values[P_A_RcptDnsRBL][1] != '+') {
state->rblmsg = strdup(state->values[P_A_RcptDnsRBL] + 1);
}
free(state->values[P_A_RcptDnsRBL]);
return 0; /* We report error LATER */
}
/* bag - */
return 0;
}
static int pt_mailfrom(rel, state, str, len)
struct policytest *rel;
struct policystate *state;
const char *str;
const int len;
{
const char *at;
int requestmask = 0;
state->rcpt_nocheck = 0;
state->sender_reject = 0;
state->sender_freeze = 0;
state->sender_norelay = 0;
if (state->always_reject)
return -1;
if (state->always_freeze)
return 1;
if (state->full_trust || state->authuser)
return 0;
if (len == 0) /* MAIL FROM:<> -- error message ? */
return 0; /* We accept it, sigh.. */
/* state->request initialization !! */
state->request = ( 1 << P_A_REJECTSOURCE |
1 << P_A_FREEZESOURCE );
/* XX: How about <@foo:user@domain> ??? */
/* XX: With IGNORING RFC-821-source-route "@foo:" we
don't have problems here */
/* Check source user */
if (check_user(rel, state, str, len) == 0) {
if (valueeq(state->values[P_A_FREEZESOURCE], "+")) {
if (debug)
type(NULL,0,NULL," mailfrom: 'freezesource +'");
state->sender_freeze = 1;
PICK_PA_MSG(P_A_FREEZESOURCE);
return 1;
}
if (state->values[P_A_FREEZESOURCE])
requestmask |= 1 << P_A_FREEZESOURCE;
if (valueeq(state->values[P_A_REJECTSOURCE], "+")) {
if (debug)
type(NULL,0,NULL," mailfrom: 'rejectsource +'");
state->sender_reject = 1;
PICK_PA_MSG(P_A_REJECTSOURCE);
return -1;
}
if (state->values[P_A_REJECTSOURCE])
requestmask |= 1 << P_A_REJECTSOURCE;
}
state->request = ( 1 << P_A_REJECTSOURCE |
1 << P_A_FREEZESOURCE |
#if 0
1 << P_A_RELAYCUSTOMER |
#endif
1 << P_A_SENDERNoRelay |
1 << P_A_SENDERokWithDNS ) & (~ requestmask);
at = find_nonqchr(str, '@', len);
if (at != NULL) {
/* @[1.2.3.4] ?? */
if (check_domain(rel, state, at+1, len - (1 + at - str)) != 0)
return -1;
} else {
/* Doh ?? Not <user@domain> ??? */
return -1;
}
if (valueeq(state->values[P_A_SENDERNoRelay], "+")) {
if (debug)
type(NULL,0,NULL," mailfrom: 'sendernorelay +'");
state->sender_norelay = 1;
PICK_PA_MSG(P_A_SENDERNoRelay);
}
if (state->values[P_A_SENDERokWithDNS] && (at[1] != '[')) {
/* Accept if found in DNS, and not an address literal! */
int rc = sender_dns_verify(state->values[P_A_SENDERokWithDNS][0],
at+1, len - (1 + at - str));
if (debug)
type(NULL,0,NULL," ... returns: %d", rc);
PICK_PA_MSG(P_A_SENDERokWithDNS);
return rc;
}
if (valueeq(state->values[P_A_REJECTSOURCE], "+")) {
if (debug)
type(NULL,0,NULL," mailfrom: 'rejectsource +'");
state->sender_reject = 1;
PICK_PA_MSG(P_A_REJECTSOURCE);
return -1;
}
if (valueeq(state->values[P_A_FREEZESOURCE], "+")) {
if (debug)
type(NULL,0,NULL," mailfrom: 'freezesource +'");
state->sender_freeze = 1;
PICK_PA_MSG(P_A_FREEZESOURCE);
return -1;
}
if (state->always_accept && at[1] != '[') {
/* Always accept, and not an address literal! */
int rc = sender_dns_verify('-', at+1, len - (1 + at - str));
if (debug)
type(NULL,0,NULL," ... returns: %d", rc);
return rc;
}
#if 0 /* Eh..., NOT! */
if (valueeq(state->values[P_A_RELAYCUSTOMER], "+")) {
if (debug)
type(NULL,0,NULL," mailfrom: 'relaycustomer +'");
state->rcpt_nocheck = 1;
PICK_PA_MSG(P_A_RELAYCUSTOMER);
return 0;
}
#endif
return 0;
}
static int pt_rcptto(rel, state, str, len)
struct policytest *rel;
struct policystate *state;
const char *str;
const int len;
{
const char *at;
int localdom, relayable = 0;
if (state->always_reject) return -1;
if (state->sender_reject) return -2;
if (state->always_freeze) return 1;
if (state->sender_freeze) return 1;
if (state->full_trust) return 0;
if (state->authuser) return 0;
if (state->trust_recipients) return 0;
/* rcptfreeze even for 'rcpt-nocheck' ? */
/* state->request initialization !! */
state->request = ( 1 << P_A_RELAYTARGET |
1 << P_A_ACCEPTbutFREEZE |
1 << P_A_TestRcptDnsRBL |
1 << P_A_LocalDomain );
/* Test first the full address */
if (check_user(rel, state, str, len) == 0) {
if (valueeq(state->values[P_A_RELAYTARGET], "+")) {
PICK_PA_MSG(P_A_RELAYTARGET);
return 0;
}
if (valueeq(state->values[P_A_RELAYTARGET], "-")) {
PICK_PA_MSG(P_A_RELAYTARGET);
return -1;
}
if (valueeq(state->values[P_A_ACCEPTbutFREEZE], "+")) {
state->sender_freeze = 1;
PICK_PA_MSG(P_A_ACCEPTbutFREEZE);
return 1;
}
if (valueeq(state->values[P_A_TestRcptDnsRBL], "+")) {
type(NULL, 0, NULL, "test-rcpt-dns-rbl test; rblmsg='%s'",
state->rblmsg ? state->rblmsg : "<none>");
if (state->rblmsg != NULL) {
/* Now this is cute... the source address had RBL entry,
and the recipient domain had a request to honour the
RBL data. */
if (state->message != NULL) free(state->message);
state->message = strdup(state->rblmsg);
if (debug)
type(NULL,0,NULL," ... TestRcptDnsRBL has a message: '%s'",
state->rblmsg);
return -1;
}
}
}
/* state->request initialization !! */
state->request = ( 1 << P_A_RELAYTARGET |
1 << P_A_ACCEPTbutFREEZE |
1 << P_A_ACCEPTifMX |
1 << P_A_ACCEPTifDNS |
1 << P_A_TestRcptDnsRBL |
1 << P_A_LocalDomain );
at = find_nonqchr(str, '@', len);
if (at != NULL) {
if (check_domain(rel, state, at+1, len - (1 + at - str)) != 0) {
type(NULL,0,NULL,"rcptto checkdomain fails; -1");
return -1;
}
} else {
if (state->rcpt_nocheck)
return 0;
/* Doh ?? Not <user@domain> ??? */
return -1;
}
/*
# else if (recipient's domain has 'relaytarget +' attribute) then
# recipient accepted
# [return 0;]
# else if (recipient's domain has 'freeze +' attribute) then
# the MESSAGE is accepted into a freezer..
# [state->sender_freeze = 1; return -1;]
# else
# this recipient refused
# [return -1;]
*/
while (((localdom = valueeq(state->values[P_A_LocalDomain], "+")) ||
(relayable = valueeq(state->values[P_A_RELAYTARGET], "+"))) &&
(percent_accept < 0)) {
/* Ok, local domain recognized, now see if it has
'%'-hack at the local-part.. */
const char *phack, *phack2;
int llen = (at - str);
/* How about '!' ??? */
phack = find_nonqchr(str, '!', llen);
if (phack != NULL && percent_accept < 0) {
/* Bang-path from left to right... */
/* ... each component from str to phack-1 */
/* state->request initialization !! */
state->request = ( 1 << P_A_RELAYTARGET |
1 << P_A_ACCEPTbutFREEZE |
1 << P_A_ACCEPTifMX |
1 << P_A_ACCEPTifDNS |
1 << P_A_TestRcptDnsRBL |
1 << P_A_LocalDomain );
llen = (phack - str);
if (check_domain(rel, state, str, llen) != 0)
return -1;
str = phack+1;
continue;
}
/* Find the LAST of unquoted '%' characters! */
phack = find_nonqchr(str, '%', llen);
phack2 = NULL;
while (phack && !phack2) {
int ll2 = at - phack -1;
phack2 = find_nonqchr(phack+1, '%', ll2);
if (phack2) {
phack = phack2;
phack2 = NULL;
} else
break; /* Not found */
}
/* Now do test of the domain in there, is it ok
for relaying to ? */
if (phack) {
/* state->request initialization !! */
state->request = ( 1 << P_A_RELAYTARGET |
1 << P_A_ACCEPTbutFREEZE |
1 << P_A_ACCEPTifMX |
1 << P_A_ACCEPTifDNS |
1 << P_A_TestRcptDnsRBL |
1 << P_A_LocalDomain );
llen = (at - phack)-1;
if (check_domain(rel, state, phack+1, llen) != 0)
return -1;
at = phack;
*((int*)&len) = (1 + at - str) + llen;
continue;
}
if (phack != NULL && percent_accept < 0) {
return -2; /* Reject the percent kludge */
}
break; /* Ok, could be ok, but RBL may say differently ... */
}
/* Do target specific rejects early */
if (valueeq(state->values[P_A_RELAYTARGET], "-")) {
PICK_PA_MSG(P_A_RELAYTARGET);
return -1;
}
if (valueeq(state->values[P_A_ACCEPTbutFREEZE], "+")) {
state->sender_freeze = 1;
PICK_PA_MSG(P_A_ACCEPTbutFREEZE);
return 1;
}
if (valueeq(state->values[P_A_TestRcptDnsRBL], "+")) {
type(NULL, 0, NULL, "test-rcpt-dns-rbl test; rblmsg='%s'",
state->rblmsg ? state->rblmsg : "<none>");
if (state->rblmsg != NULL) {
/* Now this is cute... the source address had RBL entry,
and the recipient domain had a request to honour the
RBL data. */
if (state->message != NULL) free(state->message);
state->message = strdup(state->rblmsg);
if (debug)
type(NULL,0,NULL," ... TestRcptDnsRBL has a message: '%s'",
state->rblmsg);
return -1;
}
}
if (valueeq(state->values[P_A_RELAYTARGET], "+")) {
PICK_PA_MSG(P_A_RELAYTARGET);
return 0;
}
if (state->rcpt_nocheck) {
if (debug)
type(NULL,0,NULL," ... rcpt_nocheck is on!");
return 0;
}
if (state->always_accept) {
int rc, c = '-';
if (state->values[P_A_ACCEPTifMX]) {
c = state->values[P_A_ACCEPTifMX][0];
}
rc = client_dns_verify(c, at+1, len - (1 + at - str));
/* XX: state->message setup! */
if (debug)
type(NULL,0,NULL," ... returns: %d", rc);
PICK_PA_MSG(P_A_ACCEPTifMX);
return rc;
}
if (state->values[P_A_ACCEPTifMX] || state->sender_norelay != 0) {
int c = state->values[P_A_ACCEPTifMX] ? state->values[P_A_ACCEPTifMX][0] : '.';
int rc = mx_client_verify(c, at+1, len - (1 + at - str));
/* XX: state->message setup! */
if (debug)
type(NULL,0,NULL," ...(mx_client_verify('%.*s')) returns: %d",
(int)(len - (1 + at - str)), at+1, rc);
PICK_PA_MSG(P_A_ACCEPTifMX);
return rc;
}
if (state->values[P_A_ACCEPTifDNS]) {
int rc = client_dns_verify(state->values[P_A_ACCEPTifDNS][0],
at+1, len - (1 + at - str));
/* XX: state->message setup! */
if (debug)
type(NULL,0,NULL," ... returns: %d", rc);
PICK_PA_MSG(P_A_ACCEPTifDNS);
return rc;
}
return 0;
}
static int pt_rcptpostmaster(rel, state, str, len)
struct policytest *rel;
struct policystate *state;
const char *str;
const int len;
{
/* state->request initialization !! */
state->request = ( 1 << P_A_RELAYTARGET );
if (check_user(rel, state, str, len) == 0) {
if (valueeq(state->values[P_A_RELAYTARGET], "+")) {
PICK_PA_MSG(P_A_RELAYTARGET);
return 0;
}
}
return -1;
}
int policytest(rel, state, what, str, len, authuser)
struct policytest *rel;
struct policystate *state;
PolicyTest what;
const char *str, *authuser;
const int len;
{
int rc;
if (rel == NULL)
return 0;
if (state->authuser == NULL)
state->authuser = (char*)authuser;
if (debug) {
type(NULL,0,NULL," policytest what=%d", what);
printstate(state);
}
if (state->message != NULL)
free(state->message);
state->message = NULL;
switch(what) {
case POLICY_SOURCEDOMAIN:
rc = pt_sourcedomain(rel, state, str, len);
break;
case POLICY_HELONAME:
rc = pt_heloname(rel, state, str, len);
break;
case POLICY_MAILFROM:
rc = pt_mailfrom(rel, state, str, len);
break;
case POLICY_RCPTTO:
rc = pt_rcptto(rel, state, str, len);
break;
case POLICY_RCPTPOSTMASTER:
rc = pt_rcptpostmaster(rel, state, str, len);
break;
default:
abort(); /* Code error! Bad policy ! */
return 9999; /* To silence most compilers.. */
}
if (debug) fflush(stdout);
return rc;
}
char *
policymsg(rel, state)
struct policytest *rel;
struct policystate *state;
{
return state->message;
}
long
policyinsizelimit(rel, state)
struct policytest *rel;
struct policystate *state;
{
return state->maxinsize;
}
long
policysameiplimit(rel, state)
struct policytest *rel;
struct policystate *state;
{
return state->maxsameiplimit;
}
syntax highlighted by Code2HTML, v. 0.9.1