Logo Search packages:      
Sourcecode: zmailer version File versions  Download package

policytest.c

/*
 *  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;
}

Generated by  Doxygen 1.6.0   Back to index