/*
* Copyright (c) 2003-2006 Sendmail, Inc. and its suppliers.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*/
#include "sm/generic.h"
SM_RCSID("@(#)$Id: access.c,v 1.145 2007/11/14 06:03:08 ca Exp $")
#include "sm/error.h"
#include "sm/assert.h"
#include "sm/memops.h"
#include "sm/io.h"
#include "sm/net.h"
#include "sm/rcb.h"
#include "sm/map.h"
#include "sm/mta.h"
#include "sm/ctype.h"
#include "sm/rfc2821.h"
#include "sm/greyctl.h"
#include "smar.h"
#include "reverse.h"
#include "dnsbl.h"
#include "log.h"
#include "sm/reccom.h"
/* see sm/error.h! */
#define SM_DONE 3 /* caller should use "goto done;" */
#define SM_BREAK 4 /* caller should use "break;" */
#define SM_AGAIN 5 /* caller should invoke this function again */
#if SM_NOTDONE >= SM_DONE
ERROR: _SM_NOTDONE MUST BE LESS THAN _SM_DONE: SM_NOTDONE >= SM_DONE
#endif
typedef sm_ret_T (smar_chk_F)(smar_ctx_P, smar_addr_P, sm_ret_T *);
struct smar_chk_ctx_S
{
const char *arcc_name;
smar_chk_F *arcc_fct;
/* anything else? preconditions? flags? */
};
typedef struct smar_chk_ctx_S smar_chk_ctx_T, *smar_chk_ctx_P;
/*
** SMAR_DNS_GETRET -- get result of dns resolution query (wait for it)
**
** Parameters:
** smar_addr -- address context
**
** Returns:
** usual sm_error code
**
** Locking: locks smar_addr
**
** Last code review: 2004-03-11 17:10:43; see below
** Last code change: 2004-04-18 23:28:23
*/
#if SMAR_DEBUG
/* HACK: count number of waiting tasks; doesn't use locking... */
static int waits = 0;
#endif
static sm_ret_T
smar_dns_getret(smar_addr_P smar_addr)
{
int r;
sm_evthr_ctx_P evthr_ctx;
SM_IS_SMAR_ADDR(smar_addr);
r = pthread_mutex_lock(&smar_addr->ara_mutex);
SM_LOCK_OK(r);
if (r != 0) {
sm_log_write(smar_addr->ara_smar_clt_ctx->smac_ar_ctx->smar_lctx,
AR_LCAT_RESOLVER, AR_LMOD_RESOLVER,
SM_LOG_CRIT, 4,
"sev=CRIT, func=smar_dns_getret, lock=%d", r);
return sm_error_perm(SM_EM_AR_WAIT, r);
}
if (smar_addr->ara_res_cnt > 0) {
SMARA_SET_FLAG(smar_addr, SMARA_FL_AFWAIT);
while (smar_addr->ara_res_cnt > 0) {
evthr_ctx = smar_addr->ara_smar_clt_ctx->smac_ar_ctx->smar_ev_ctx;
# if SMAR_DEBUG
++waits;
SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "sev=DBG, func=smar_dns_getret, smar_addr=%p, taid=%s, where=before-cond_wait, waits=%d, act=%u, cur=%u, max_s=%u, max_h=%u\n", smar_addr, smar_addr->ara_taid, waits
, evthr_ctx->evthr_c_act
, evthr_ctx->evthr_c_cur
, evthr_ctx->evthr_c_max_s
, evthr_ctx->evthr_c_max_h
));
# endif
(void) evthr_before_block(evthr_ctx);
r = pthread_cond_wait(&smar_addr->ara_cond, &smar_addr->ara_mutex);
# if SMAR_DEBUG
--waits;
SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "sev=DBG, func=smar_dns_getret, smar_addr=%p, taid=%s, where=after-cond_wait, r=%d, waits=%d, act=%u, cur=%u, max_s=%u, max_h=%u\n", smar_addr, smar_addr->ara_taid, r, waits
, evthr_ctx->evthr_c_act
, evthr_ctx->evthr_c_cur
, evthr_ctx->evthr_c_max_s
, evthr_ctx->evthr_c_max_h
));
# endif
(void) evthr_after_block(evthr_ctx);
if (EINVAL == r) {
sm_log_write(smar_addr->ara_smar_clt_ctx->smac_ar_ctx->smar_lctx,
AR_LCAT_RESOLVER, AR_LMOD_RESOLVER,
SM_LOG_ERR, 4,
"sev=ERROR, func=smar_dns_getret, cond_wait=%m",
sm_err_perm(r));
break;
}
}
if (0 == r) {
r = pthread_mutex_unlock(&smar_addr->ara_mutex);
SM_ASSERT(0 == r);
}
}
else {
r = pthread_mutex_unlock(&smar_addr->ara_mutex);
SM_ASSERT(0 == r);
}
if (0 == r) {
if (SMARA_IS_FLAG(smar_addr, SMARA_FL_A4RVRS))
SMARA_SET_FLAG(smar_addr, SMARA_FL_GOTRVRS);
if (SMARA_IS_FLAG(smar_addr, SMARA_FL_A4RCPT))
SMARA_SET_FLAG(smar_addr, SMARA_FL_GOTRCPT);
if (SMARA_IS_FLAG(smar_addr, SMARA_FL_A4DNSBL))
SMARA_SET_FLAG(smar_addr, SMARA_FL_GOTDNSBL);
return SM_SUCCESS;
}
SMARA_SET_FLAG(smar_addr, SMARA_FL_FAILDNS);
return sm_error_perm(SM_EM_AR_WAIT, r); /* error value/type */
}
/*
** SMAR_RVRS_NOTIFY -- callback for a reverse resolution query
** This function will be called when the reverse resolution is done
** (to "send back" the result to the caller, i.e., usually it would be
** a RCB routine).
**
** Parameters:
** smar_rvrs -- reverse resolution context
** ctx -- smar_addr
**
** Returns:
** usual sm_error code
**
** Locking: locks smar_addr
**
** Last code review: 2004-03-11 17:14:30; see below
** Last code change:
*/
static sm_ret_T
smar_rvrs_notify(smar_rvrs_P smar_rvrs, void *ctx)
{
sm_ret_T ret;
int r;
smar_addr_P smar_addr;
SM_REQUIRE(smar_rvrs != NULL);
SM_REQUIRE(ctx != NULL);
smar_addr = ctx;
ret = SM_SUCCESS;
r = pthread_mutex_lock(&smar_addr->ara_mutex);
SM_LOCK_OK(r);
if (r != 0) {
sm_log_write(smar_addr->ara_smar_clt_ctx->smac_ar_ctx->smar_lctx,
AR_LCAT_RESOLVER, AR_LMOD_RESOLVER,
SM_LOG_CRIT, 4,
"sev=CRIT, func=smar_rvrs_notify, lock=%d", r);
return sm_error_perm(SM_EM_AR, r);
}
if (smar_rvrs->arv_hostname != NULL)
smar_addr->ara_rvrs_hostname = SM_CSTR_DUP(smar_rvrs->arv_hostname);
SM_REQUIRE(smar_rvrs->arv_ret != 0);
smar_addr->ara_dns_ret = smar_rvrs->arv_ret;
SM_ASSERT(smar_addr->ara_res_cnt > 0);
--smar_addr->ara_res_cnt;
/*
** Notify original thread that the data is now there.
** How? Can this be done via evthreads?
** Otherwise:
** pipe() + select()
** pthread_cond_timedwait()
** Write a generic function?
** Blocking here isn't a good idea because it's not under
** control of evthreads, i.e., it will consume a task;
** in the worst case this could lead to a deadlock, however,
** libevthr solves this by allowing more "potentially blocking"
** threads.
*/
if (smar_addr->ara_res_cnt == 0 && SMARA_IS_FLAG(smar_addr, SMARA_FL_AFWAIT))
{
r = pthread_cond_signal(&smar_addr->ara_cond);
if (r != 0) {
sm_log_write(smar_addr->ara_smar_clt_ctx->smac_ar_ctx->smar_lctx,
AR_LCAT_RESOLVER, AR_LMOD_RESOLVER,
SM_LOG_ERR, 1,
"sev=ERROR, func=smar_rvrs_notify, cond_signal=%m",
sm_err_perm(r));
}
else
SMARA_CLR_FLAG(smar_addr, SMARA_FL_AFWAIT);
}
r = pthread_mutex_unlock(&smar_addr->ara_mutex);
SM_ASSERT(0 == r);
if (r != 0 && sm_is_success(ret))
ret = sm_error_perm(SM_EM_AR, r);
SMAR_LEV_DPRINTF(8, (SMAR_DEBFP, "func=smar_rvrs_notify, ret=%m\n", smar_addr->ara_dns_ret));
return ret;
}
/*
** SMAR_RCPT_NOTIFY -- callback for a recipient resolution query
** This function will be called when the recipient resolution is done
** (to "send back" the result to the caller, i.e., usually it would be
** a RCB routine).
**
** Parameters:
** smar_rcpts -- recipient list context
** ctx -- smar_addr
**
** Returns:
** usual sm_error code
**
** Locking: locks smar_addr
**
** Last code review:
** Last code change:
**
** Note: this is almost the same as smar_rvrs_notify(), except
** for the assignment of smar_addr->ara_rvrs_hostname
*/
static sm_ret_T
smar_rcpt_notify(smar_rcpts_P smar_rcpts, void *ctx)
{
sm_ret_T ret;
int r;
smar_addr_P smar_addr;
SM_IS_SMAR_RCPTS(smar_rcpts);
SM_REQUIRE(ctx != NULL);
smar_addr = ctx;
ret = SM_SUCCESS;
r = pthread_mutex_lock(&smar_addr->ara_mutex);
SM_LOCK_OK(r);
if (r != 0) {
sm_log_write(smar_addr->ara_smar_clt_ctx->smac_ar_ctx->smar_lctx,
AR_LCAT_RESOLVER, AR_LMOD_RESOLVER,
SM_LOG_CRIT, 4,
"sev=CRIT, func=smar_rcpt_notify, lock=%d", r);
return sm_error_perm(SM_EM_AR, r);
}
smar_addr->ara_dns_ret = smar_rcpts->arrs_ret;
SM_ASSERT(smar_addr->ara_res_cnt > 0);
--smar_addr->ara_res_cnt;
/*
** Notify original thread that the data is now there. (see above)
*/
if (smar_addr->ara_res_cnt == 0 && SMARA_IS_FLAG(smar_addr, SMARA_FL_AFWAIT))
{
r = pthread_cond_signal(&smar_addr->ara_cond);
if (r != 0) {
sm_log_write(smar_addr->ara_smar_clt_ctx->smac_ar_ctx->smar_lctx,
AR_LCAT_RESOLVER, AR_LMOD_RESOLVER,
SM_LOG_ERR, 4,
"sev=ERROR, func=smar_rcpt_notify, cond_signal=%m",
sm_err_perm(r));
}
else
SMARA_CLR_FLAG(smar_addr, SMARA_FL_AFWAIT);
}
r = pthread_mutex_unlock(&smar_addr->ara_mutex);
SM_ASSERT(0 == r);
if (r != 0 && sm_is_success(ret))
ret = sm_error_perm(SM_EM_AR, r);
SMAR_LEV_DPRINTF(8, (SMAR_DEBFP, "func=smar_rcpt_notify, ret=%m\n", smar_addr->ara_dns_ret));
return ret;
}
/*
** SMAR_DNSBL_NOTIFY -- callback for a DNS BL query
**
** Parameters:
** smar_dnsbl -- DNS BL context
** ctx -- smar_addr
**
** Returns:
** usual sm_error code
**
** Locking: locks smar_addr
**
** Last code review:
** Last code change:
*/
static sm_ret_T
smar_dnsbl_notify(smar_dnsbl_P smar_dnsbl, void *ctx)
{
sm_ret_T ret;
int r;
smar_addr_P smar_addr;
SM_REQUIRE(smar_dnsbl != NULL);
SM_REQUIRE(ctx != NULL);
smar_addr = ctx;
SM_IS_SMAR_ADDR(smar_addr);
ret = SM_SUCCESS;
r = pthread_mutex_lock(&smar_addr->ara_mutex);
SM_LOCK_OK(r);
if (r != 0) {
sm_log_write(smar_addr->ara_smar_clt_ctx->smac_ar_ctx->smar_lctx,
AR_LCAT_RESOLVER, AR_LMOD_RESOLVER,
SM_LOG_CRIT, 4,
"sev=CRIT, func=smar_dnsbl_notify, lock=%d",
r);
return sm_error_perm(SM_EM_AR, r);
}
SM_ASSERT(smar_addr->ara_res_cnt > 0);
--smar_addr->ara_res_cnt;
/*
** Notify original thread that the data is now there. (see above)
*/
if (smar_addr->ara_res_cnt == 0 && SMARA_IS_FLAG(smar_addr, SMARA_FL_AFWAIT))
{
r = pthread_cond_signal(&smar_addr->ara_cond);
if (r != 0) {
/* XXX Complain/Log */
sm_log_write(smar_addr->ara_smar_clt_ctx->smac_ar_ctx->smar_lctx,
AR_LCAT_RESOLVER, AR_LMOD_RESOLVER,
SM_LOG_ERR, 4,
"sev=ERROR, func=smar_dnsbl_notify, cond_signal=%m",
sm_err_perm(r));
}
else
SMARA_CLR_FLAG(smar_addr, SMARA_FL_AFWAIT);
}
r = pthread_mutex_unlock(&smar_addr->ara_mutex);
SM_ASSERT(0 == r);
if (r != 0 && sm_is_success(ret))
ret = sm_error_perm(SM_EM_AR, r);
SMAR_LEV_DPRINTF(8, (SMAR_DEBFP, "func=smar_dnsbl_notify, ret=%m\n", smar_addr->ara_dns_ret));
return ret;
}
/*
** SMAR_ACCESS_RE -- Send reply for an address check
** The status is stored in smar_addr: smar_addr->ara_status;
** if smar_addr->ara_rhs isn't NULL it is sent as error text
** (no real checking is done here).
** NOTE: ara_status is only sent iff
** (SM_SUCCESS == ret && SM_ACC_FOUND == ara_mapres)
**
** Parameters:
** smar_addr -- address context
** which_status -- which status record type is returned
** off -- possible offset in return string
**
** Returns:
** usual sm_error code
**
** Side Effects:
** reply is stored in smar_addr->ara_rcbe->rcbe_rcb
** which must be taken care of (sent) by the caller.
**
** Locking: smar_addr must be under control of the caller ("locked")
**
** Last code review: 2004-03-11
** Last code change:
*/
sm_ret_T
smar_access_re(smar_addr_P smar_addr, uint32_t which_status, uint off)
{
sm_rcb_P rcb;
sm_ret_T ret;
SM_IS_SMAR_ADDR(smar_addr);
rcb = &smar_addr->ara_rcbe->rcbe_rcb;
ret = sm_rcb_putv(rcb, RCB_PUTV_FIRST,
SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
SM_RCBV_INT, RT_A2S_ID, smar_addr->ara_id,
SM_RCBV_BUF, RT_A2S_TAID, smar_addr->ara_taid, SMTP_STID_SIZE,
SM_RCBV_INT, RT_A2S_MAP_RES, smar_addr->ara_mapres,
SM_RCBV_END);
if (SM_SUCCESS == ret && SM_ACC_FOUND == smar_addr->ara_mapres) {
ret = sm_rcb_putv(rcb, RCB_PUTV_NONE,
SM_RCBV_INT, which_status, smar_addr->ara_status|smar_addr->ara_rflags,
SM_RCBV_END);
}
if (SM_SUCCESS == ret && smar_addr->ara_rhs != NULL
&& sm_str_getlen(smar_addr->ara_rhs) > 3)
{
if (off > 0) {
SM_ASSERT(sm_str_getlen(smar_addr->ara_rhs) > off);
ret = sm_rcb_putv(rcb, RCB_PUTV_NONE,
SM_RCBV_BUF, RT_A2S_STATT,
sm_str_getdata(smar_addr->ara_rhs) + off,
sm_str_getlen(smar_addr->ara_rhs) - off,
SM_RCBV_END);
}
else {
ret = sm_rcb_putv(rcb, RCB_PUTV_NONE,
SM_RCBV_STR, RT_A2S_STATT, smar_addr->ara_rhs,
SM_RCBV_END);
}
}
if (SM_SUCCESS == ret && SMARA_IS_LFLAG(smar_addr, SMARA_LFL_RVRS4)) {
ret = sm_rcb_putv(rcb, RCB_PUTV_NONE,
SM_RCBV_INT, RT_A2S_RVRS_ST, smar_addr->ara_dns_ret,
SM_RCBV_END);
SMAR_LEV_DPRINTF(7, (SMAR_DEBFP, "func=smar_access_re, rvrs_ret=%r, putv=%r, hostname=%C\n", smar_addr->ara_dns_ret, ret, smar_addr->ara_rvrs_hostname));
if (SM_SUCCESS == ret && smar_addr->ara_rvrs_hostname != NULL) {
ret = sm_rcb_putv(rcb, RCB_PUTV_NONE,
SM_RCBV_CSTR, RT_A2S_RVRS_NAME,
smar_addr->ara_rvrs_hostname,
SM_RCBV_END);
}
}
if (SM_SUCCESS == ret && SMARA_IS_FLAG(smar_addr, SMARA_FL_GOTSSSECONF)) {
ret = sm_rcb_putv(rcb, RCB_PUTV_NONE,
SM_RCBV_INT, RT_A2S_MAP_RES_CNF, smar_addr->ara_maprescnf,
SM_RCBV_STR, RT_A2S_RHS_CNF, smar_addr->ara_rhs_conf,
SM_RCBV_END);
}
return ret;
}
/*
** SMAR_DNS2RET -- Convert DNS status code to address status
**
** Parameters:
** smar_ctx -- SMAR context
** smar_addr -- address context
**
** Returns:
** usual sm_error code
**
** Side Effects:
** creates error string in ara_rhs
**
** Locking: smar_addr must be under control of the caller.
**
** Last code review:
** Last code change:
**
** Note: enh.status code X.1.8 is only for sender, which is currently
** correct. However, it might be useful to check the lookup type, e.g.,
** SMARA_LT_MAIL_ROUTE.
*/
static sm_ret_T
smar_dns2ret(smar_ctx_P smar_ctx, smar_addr_P smar_addr)
{
sm_ret_T ret;
#define SM_ARA_RHS_LEN 64
ret = SM_SUCCESS;
switch (smar_addr->ara_dns_ret)
{
case 0:
break;
case DNSR_TEMP:
case DNSR_TIMEOUT:
smar_addr->ara_status = SMTP_R_TEMP;
smar_addr->ara_rhs = sm_str_scpy(NULL,
"451 4.1.8 Sender address does not resolve",
SM_ARA_RHS_LEN);
break;
case DNSR_REFUSED:
sm_log_write(smar_ctx->smar_lctx,
AR_LCAT_RESOLVER, AR_LMOD_RVRS_CB,
SM_LOG_CRIT, 6,
"sev=CRIT, func=smar_dns2ret, pa=%#S, error=%m",
smar_addr->ara_pa, smar_addr->ara_dns_ret);
case DNSR_NOTFOUND:
case DNSR_PERM:
case DNSR_NO_DATA:
smar_addr->ara_status = SMTP_R_REJECT;
smar_addr->ara_rhs = sm_str_scpy(NULL,
"553 5.1.8 Sender address does not exist",
SM_ARA_RHS_LEN);
break;
case sm_error_perm(SM_EM_AR, SM_E_ALIAS_REC):
smar_addr->ara_status = SMTP_R_TEMP;
smar_addr->ara_rhs = sm_str_scpy(NULL,
"451 4.1.8 Alias loop", SM_ARA_RHS_LEN);
break;
case sm_error_perm(SM_EM_AR, ELOOP):
smar_addr->ara_status = SMTP_R_TEMP;
smar_addr->ara_rhs = sm_str_scpy(NULL,
"451 4.1.8 CNAME loop", SM_ARA_RHS_LEN);
break;
case sm_error_perm(SM_EM_DNS, EINVAL):
smar_addr->ara_status = SMTP_R_REJECT;
smar_addr->ara_rhs = sm_str_scpy(NULL,
"551 5.1.8 Invalid DNS entry", SM_ARA_RHS_LEN);
break;
case DNSR_MXINVALID:
smar_addr->ara_status = SMTP_R_REJECT;
smar_addr->ara_rhs = sm_str_scpy(NULL,
"551 5.1.8 Invalid MX entry", SM_ARA_RHS_LEN);
break;
case DNSR_PTRINVALID:
smar_addr->ara_status = SMTP_R_REJECT;
smar_addr->ara_rhs = sm_str_scpy(NULL,
"551 5.1.8 Invalid PTR entry", SM_ARA_RHS_LEN);
break;
case DNSR_CNINVALID:
smar_addr->ara_status = SMTP_R_REJECT;
smar_addr->ara_rhs = sm_str_scpy(NULL,
"551 5.1.8 Invalid CNAME entry", SM_ARA_RHS_LEN);
break;
default:
smar_addr->ara_status = SMTP_R_TEMP;
smar_addr->ara_rhs = sm_str_scpy(NULL,
"451 4.1.8 oops", SM_ARA_RHS_LEN);
/* don't return an error to caller? */
ret = smar_addr->ara_dns_ret;
}
return ret;
}
#if 0
///*
//** SMAR_IP2STR -- app
//**
//** Parameters:
//** smar_rcpt -- SMAR RCPT context
//**
//** Returns:
//** usual sm_error code
//**
//** Locking:
//**
//** Last code review:
//** Last code change:
//*/
//
//static sm_ret_T
//smar_ip2str(smar_rcpt_P smar_rcpt, uint i, uint j, sm_str_P str)
//{
// sm_ret_T ret;
// smar_dns_P smar_dns;
//
// SM_IS_SMAR_RCPT(smar_rcpt);
// for (i = 0; i < smar_rcpt->arr_A_qsent; i++)
// {
// smar_dns = &(smar_rcpt->arr_res[i]);
//SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "i=%d, smar_dns=%p\n", i, smar_dns));
//
// /* and send all A records with the MX data */
// for (j = 0; j < smar_dns->ardns_n_A; j++)
// {
//SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "i=%d, j=%d, a=%A\n", i, j, smar_dns->ardns_A_rrs[j]));
// /* HACK: ignore addresses with value 0 */
// if (smar_dns->ardns_A_rrs[j] == 0)
// continue;
//
// smar_dns->ardns_A_rrs[j],
// }
// }
//}
#endif /* 0 */
/*
** SMAR_MAP2ACC -- convert map result code to access lookup code
**
** Parameters:
** rv -- result of map lookup
**
** Returns:
** access lookup code
*/
sm_ret_T
smar_map2acc(sm_ret_T rv)
{
if (SM_MAP_TEMPMAP == rv)
return SM_ACC_TEMPFAIL;
else if (SM_MAP_PERMMAP == rv)
return SM_ACC_PERMFAIL;
else if (SM_MAP_NOTFOUND == rv)
return SM_ACC_NOTFOUND;
else
return sm_error_perm(SM_EM_AR, EINVAL);
}
/*
** SMAR_DNSBL -- start DNS BL queries
**
** Parameters:
** smar_ctx -- SMAR context
** smar_addr -- address context
**
** Returns:
** usual sm_error code
**
** Locking: should smar_addr be locked?
**
** Side Effects:
** sets SMARA_FL_A4DNSBL if successful
*/
static sm_ret_T
smar_startdnsbl(smar_ctx_P smar_ctx, smar_addr_P smar_addr)
{
sm_ret_T ret;
int r;
ipv4_T ipv4;
uint u;
smar_dnsbl_P smar_dnsbl;
SM_IS_SMAR_ADDR(smar_addr);
SM_IS_SMAR_CTX(smar_ctx);
r = pthread_mutex_lock(&smar_addr->ara_mutex);
SM_LOCK_OK(r);
if (r != 0) {
sm_log_write(smar_addr->ara_smar_clt_ctx->smac_ar_ctx->smar_lctx,
AR_LCAT_RESOLVER, AR_LMOD_RESOLVER, SM_LOG_CRIT, 4,
"sev=CRIT, func=smar_startdnsbl, lock=%d", r);
return sm_error_perm(SM_EM_AR, r);
}
ret = SM_SUCCESS;
for (u = 0;
u < SM_MAX_DNSBL &&
smar_ctx->smar_cnf.smar_cnf_dnsbl[u].scdb_name != NULL;
u++)
{
ret = smar_dnsbl_new(smar_addr, &(smar_addr->ara_dnsblres[u]),
&(smar_ctx->smar_cnf.smar_cnf_dnsbl[u]),
smar_dnsbl_notify, smar_addr, &smar_dnsbl);
if (sm_is_err(ret))
goto unlock; /* XXX set some error code, log? */
++smar_addr->ara_res_cnt;
ret = sm_inet_a2ipv4((const char *)sm_str_getdata(smar_addr->ara_pa),
NULL, &ipv4);
/*
** This is an asynchronous function!
** Note: smar_dnsbl is now under control of smar_dnsbl_rslv().
*/
ret = smar_dnsbl_rslv(smar_ctx, smar_dnsbl, ipv4, 0u);
if (sm_is_success(ret))
SMARA_SET_FLAG(smar_addr, SMARA_FL_A4DNSBL);
else {
--smar_addr->ara_res_cnt;
ret = SM_SUCCESS; /* don't return error to caller */
}
SMAR_LEV_DPRINTF(7, (SMAR_DEBFP, "sev=DBG, func=smar_startdnsbl, smar_dnsbl_rslv=%r, ipv4=%A, scdb_name=%s\n", ret, ipv4, smar_ctx->smar_cnf.smar_cnf_dnsbl[u].scdb_name));
}
/* ignore error, ara_status is initialized */
unlock:
r = pthread_mutex_unlock(&smar_addr->ara_mutex);
SM_ASSERT(0 == r);
if (r != 0 && sm_is_success(ret))
ret = sm_error_perm(SM_EM_AR, r);
return ret;
}
/*
** SMAR_INIT_PA2 -- Initialize address data structure *2 components
**
** Parameters:
** smar_ctx -- SMAR context
** smar_addr -- address context
**
** Returns:
** usual sm_error code
**
** Locking: smar_addr must be under control of the caller.
*/
static sm_ret_T
smar_init_pa2(smar_ctx_P smar_ctx, smar_addr_P smar_addr)
{
sm_ret_T ret;
sm_a2821_T a_addr;
sm_a2821_P paddr;
SM_REQUIRE(smar_ctx != NULL);
SM_REQUIRE(smar_addr != NULL);
if (NULL == smar_addr->ara_pa2)
return SM_SUCCESS;
paddr = &a_addr; /* paddr is just pointer to a_addr */
A2821_INIT_RP(paddr, smar_addr->ara_rpool);
ret = t2821_scan((sm_rdstr_P) smar_addr->ara_pa2, paddr, 0);
if (sm_is_success(ret)) {
ret = t2821_parse(paddr, (R2821_CORRECT|R2821_EMPTY) & ~R2821_AT);
if (sm_is_err(ret))
goto done;
if (sm_str_getlen(smar_addr->ara_pa2) == 2
&& sm_str_rd_elem(smar_addr->ara_pa2, 0) == '<'
&& sm_str_rd_elem(smar_addr->ara_pa2, 1) == '>')
{
SMARA_SET_FLAG(smar_addr, SMARA_FL_PA2EMPTY);
}
}
smar_addr->ara_rhs2 = sm_str_new(smar_addr->ara_rpool, MAXADDRLEN,
SMAR_MAXRHS);
if (NULL == smar_addr->ara_rhs2) {
ret = sm_err_temp(ENOMEM);
goto done;
}
if (!SMARA_IS_FLAG(smar_addr, SMARA_FL_PA2EMPTY)) {
smar_addr->ara_user2 = sm_str_new(smar_addr->ara_rpool,
MAXADDRLEN >> 1, MAXADDRLEN);
smar_addr->ara_detail2 = sm_str_new(smar_addr->ara_rpool,
MAXADDRLEN >> 1, MAXADDRLEN);
smar_addr->ara_domain_pa2 = sm_str_new(smar_addr->ara_rpool,
MAXADDRLEN >> 1, MAXADDRLEN);
if (smar_addr->ara_user2 == NULL
|| smar_addr->ara_detail2 == NULL
|| NULL == smar_addr->ara_domain_pa2)
{
ret = sm_err_temp(ENOMEM);
goto done;
}
ret = t2821_parts(paddr,
smar_ctx->smar_cnf.smar_cnf_addr_delim,
true, smar_addr->ara_user2, smar_addr->ara_detail2,
smar_addr->ara_domain_pa2, &smar_addr->ara_delim2);
if (0 == ret) {
/* no delimiter: detail must be set to NULL! */
SM_STR_FREE(smar_addr->ara_detail2);
}
}
a2821_free(paddr);
paddr = NULL;
if (sm_is_err(ret))
goto done;
return ret;
done:
if (paddr != NULL) {
a2821_free(paddr);
paddr = NULL;
}
return ret;
}
/*
** SMAR_ADDR_INIT -- Initialize address data structure for further operations
**
** Parameters:
** smar_ctx -- SMAR context
** smar_addr -- address context
** rv -- return value (output)
** this is used by the caller to decide how to continue
**
** Returns:
** usual sm_error code
**
** Locking: smar_addr must be under control of the caller.
**
** Last code review:
** Last code change:
**
** Called by: smar_access_chk()
**
** XXX switch ret and rv?
*/
static sm_ret_T
smar_addr_init(smar_ctx_P smar_ctx, smar_addr_P smar_addr, sm_ret_T *rv)
{
sm_ret_T ret;
smar_clt_ctx_P smar_clt_ctx;
SM_IS_SMAR_ADDR(smar_addr);
SM_IS_SMAR_CTX(smar_ctx);
SM_REQUIRE(rv != NULL);
smar_clt_ctx = smar_addr->ara_smar_clt_ctx;
ret = SM_SUCCESS;
*rv = SM_SUCCESS;
if (SMARA_IS_FLAG(smar_addr, SMARA_FL_INIT))
return SM_SUCCESS;
/* SM_IS_SMAR_CLT_CTX(smar_clt_ctx); not really necessary here */
smar_addr->ara_c_MX = smar_addr->ara_c_A = 0;
smar_addr->ara_c_dnsbl = 0;
/* default status: "continue" aka "don't know" */
smar_addr->ara_status = SMTP_R_CONT;
smar_addr->ara_mapres = SM_ACC_NOTFOUND;
smar_addr->ara_str = smar_addr->ara_tag = smar_addr->ara_detail = NULL;
smar_addr->ara_domain_pa = NULL;
smar_addr->ara_rvrs = NULL;
smar_addr->ara_rcpt = NULL;
smar_addr->ara_rcpts = NULL;
smar_addr->ara_rhstagoff = 0;
/* which lookup types use RFC2821 addresses? */
if (SM_IS_FLAG(smar_addr->ara_ltype,
SMARA_LT_MAIL_ACC|SMARA_LT_MAIL_ROUTE|
SMARA_LT_RCPT_ACC|SMARA_LT_RCPT_PROT))
SMARA_SET_FLAG(smar_addr, SMARA_FL_RFC2821);
/* XXX is this needed? it can be done by smar_access_re() */
if (SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_CLT_A_ACC))
smar_addr->ara_which_status = RT_A2S_CLT_A_ST;
else if (SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_CLT_N_ACC))
smar_addr->ara_which_status = RT_A2S_CLT_N_ST;
else if (SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_MAIL_ACC|
SMARA_LT_MAIL_ROUTE|SMARA_LT_MAIL_LOCAL))
smar_addr->ara_which_status = RT_A2S_MAIL_ST;
else if (SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_RCPT_ACC|
SMARA_LT_RCPT_PROT))
smar_addr->ara_which_status = RT_A2S_RCPT_ST;
else if (SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_CERT_RELAY))
smar_addr->ara_which_status = RT_A2S_CERT_ST;
else if (SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_EHLO_ACC))
smar_addr->ara_which_status = RT_A2S_EHLO_ST;
else {
/* XXX BOGUS */
smar_addr->ara_which_status = RT_A2S_CLT_N_ST;
goto done;
}
if (SMARA_IS_FLAG(smar_addr, SMARA_FL_RFC2821)) {
int flags;
A2821_INIT_RP(&smar_addr->ara_a_rcpt, smar_addr->ara_rpool);
ret = t2821_scan((sm_rdstr_P) smar_addr->ara_pa,
&smar_addr->ara_a_rcpt, 0);
if (sm_is_err(ret))
goto done;
flags = R2821_CORRECT & ~R2821_AT;
if (SM_IS_FLAG(smar_addr->ara_ltype,
SMARA_LT_MAIL_ACC|SMARA_LT_MAIL_ROUTE))
flags |= R2821_EMPTY;
ret = t2821_parse(&smar_addr->ara_a_rcpt, flags);
if (sm_is_err(ret))
goto done;
smar_addr->ara_detail = sm_str_new(smar_addr->ara_rpool,
MAXADDRLEN >> 1, MAXADDRLEN);
if (NULL == smar_addr->ara_detail)
goto done;
smar_addr->ara_domain_pa = sm_str_new(smar_addr->ara_rpool,
MAXADDRLEN >> 1, MAXADDRLEN);
if (NULL == smar_addr->ara_domain_pa)
goto done;
}
smar_addr->ara_str = sm_str_new(smar_addr->ara_rpool, MAXADDRLEN,
MAXADDRLEN + 2);
if (NULL == smar_addr->ara_str)
goto done;
smar_addr->ara_rhs = sm_str_new(smar_addr->ara_rpool, MAXADDRLEN,
SMAR_MAXRHS);
if (NULL == smar_addr->ara_rhs)
goto done;
smar_addr->ara_tag = sm_str_new(smar_addr->ara_rpool, MAXADDRLEN,
MAXADDRLEN + 2);
if (NULL == smar_addr->ara_tag)
goto done;
/* setup mutex+cond for asynchronous calls */
if (SMARA_IS_LFLAG(smar_addr, SMARA_LFL_RVRS4) ||
SMARA_IS_LFLAG(smar_addr, SMARA_LFL_DNSBL) ||
SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_MAIL_ROUTE))
{
int r;
r = pthread_mutex_init(&smar_addr->ara_mutex, SM_PTHREAD_MUTEXATTR);
if (r != 0)
goto done; /* XXX set some error code, log? */
SMARA_SET_FLAG(smar_addr, SMARA_FL_HASMUT);
r = pthread_cond_init(&smar_addr->ara_cond, NULL);
if (r != 0)
goto done; /* XXX set some error code, log? */
SMARA_SET_FLAG(smar_addr, SMARA_FL_HASCOND);
}
if (SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_MAIL_ROUTE)) {
smar_addr->ara_which_status = RT_A2S_MAIL_ST;
if (sm_is_err(smar_rcpts_new(smar_ctx, smar_clt_ctx, &smar_addr->ara_rcpts)) ||
sm_is_err(smar_rcpt_new(&smar_addr->ara_rcpt)))
goto done;
/* create rcpt id, idx = 0 (bogus, but irrelevant) */
smar_addr->ara_rcpt_idx = 0;
sm_snprintf(smar_addr->ara_rcpt->arr_id,
sizeof(smar_addr->ara_rcpt->arr_id),
SMTP_RCPTID_FORMAT, smar_addr->ara_taid, 0);
RCPT_ID_COPY(smar_addr->ara_rcpts->arrs_rcpt_id,
smar_addr->ara_rcpt->arr_id);
smar_addr->ara_rcpt->arr_pa = sm_str_dup(NULL, smar_addr->ara_pa);
if (NULL == smar_addr->ara_rcpt->arr_pa)
goto done;
smar_addr->ara_rcpts->arrs_rcpt = smar_addr->ara_rcpt;
SMARR_SET_FLAG(smar_addr->ara_rcpt, SMARR_FL_ORCPT);
SMARRS_SET_FLAG(smar_addr->ara_rcpts, SMARRS_FL_NOSEND|SMARRS_FL_NOFREE);
/* XXX setup more data? */
smar_addr->ara_rcpt->arr_rqflags = SMARRQ_FL_NOEXP|SMARRQ_FL_NOMXCUT;
smar_addr->ara_rcpt->arr_timeout = DNS_TIMEOUT;
smar_addr->ara_rcpts->arrs_cbf = smar_rcpt_notify;
smar_addr->ara_rcpts->arrs_cb_ctx = smar_addr;
++smar_addr->ara_res_cnt;
/* This is an asynchronous function! */
ret = smar_rcpt_rslv(smar_ctx, smar_addr->ara_rcpts);
if (sm_is_success(ret))
SMARA_SET_FLAG(smar_addr, SMARA_FL_A4RCPT);
else
--smar_addr->ara_res_cnt;
/* ignore error, ara_status is initialized */
ret = SM_SUCCESS; /* better safe than sorry */
}
if (SMARA_IS_LFLAG(smar_addr, SMARA_LFL_RVRS4)) {
ret = smar_rvrs_new(smar_ctx, smar_clt_ctx, &smar_addr->ara_rvrs);
if (sm_is_err(ret))
goto done; /* XXX set some error code, log? */
SMARV_SET_FLAG(smar_addr->ara_rvrs, SMARV_FL_NOSEND|SMARV_FL_NOFREE);
SESSTA_COPY(smar_addr->ara_rvrs->arv_seid, smar_addr->ara_taid);
smar_addr->ara_rvrs->arv_ltype = smar_addr->ara_ltype;
smar_addr->ara_rvrs->arv_lflags = smar_addr->ara_lflags;
sm_str_cat(smar_addr->ara_str, smar_addr->ara_pa);
ret = sm_inet_a2ipv4((const char *)sm_str_getdata(smar_addr->ara_str),
NULL, &smar_addr->ara_rvrs->arv_ipv4);
smar_addr->ara_rvrs->arv_cbf = smar_rvrs_notify;
smar_addr->ara_rvrs->arv_cb_ctx = smar_addr;
sm_str_clr(smar_addr->ara_str);
++smar_addr->ara_res_cnt;
/* This is an asynchronous function! */
ret = smar_rvrs_rslv(smar_ctx, smar_addr->ara_rvrs, 0u);
SMAR_LEV_DPRINTF(5, (SMAR_DEBFP, "sev=DBG, func=smar_addr_init, smar_rvrs_rslv=%r\n", ret));
if (sm_is_success(ret))
SMARA_SET_FLAG(smar_addr, SMARA_FL_A4RVRS);
else
--smar_addr->ara_res_cnt;
/* ignore error, ara_status is initialized */
ret = SM_SUCCESS; /* better safe than sorry */
}
/*
** don't do this here if clt access is checked because it may
** make this superfluous?
*/
if (SMARA_IS_LFLAG(smar_addr, SMARA_LFL_DNSBL) &&
!SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_CLT_A_ACC))
{
ret = smar_startdnsbl(smar_ctx, smar_addr);
if (sm_is_err(ret))
goto done; /* XXX set some error code, log? */
}
/* check flags for what to do... */
if (SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_CLT_A_ACC))
ret = sm_str_scat(smar_addr->ara_tag, CLT_A_TAG);
else if (SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_CLT_N_ACC))
ret = sm_str_scat(smar_addr->ara_tag, CLT_N_TAG);
else if (SM_IS_FLAG(smar_addr->ara_ltype,
SMARA_LT_MAIL_ACC|SMARA_LT_MAIL_ROUTE))
ret = sm_str_scat(smar_addr->ara_tag, MAIL_TAG);
else if (SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_RCPT_ACC))
ret = sm_str_scat(smar_addr->ara_tag, RCPT_TAG);
else if (SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_RCPT_PROT)) {
ret = sm_str_scat(smar_addr->ara_tag, PROT_RCPT_TAG);
SMARA_SET_FLAG(smar_addr, SMARA_FL_GOTRCPTPROT|SMARA_FL_RP_ANALYSE);
/* note: this is too early, but the check is done "real soon" */
}
else if (SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_EHLO_ACC))
ret = sm_str_scat(smar_addr->ara_tag, EHLO_TAG);
else if (!SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_CERT_RELAY))
goto done;
if (sm_is_err(ret))
goto done;
if (SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_RCPT_PROT)
&& SMARA_IS_LFLAG(smar_addr, SMARA_LFL_PROTMAP)
&& smar_addr->ara_pa2 != NULL)
{
ret = smar_init_pa2(smar_ctx, smar_addr);
if (sm_is_err(ret))
goto done;
}
else if (SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_RCPT_PROT)
&& SMARA_IS_LFLAG(smar_addr, SMARA_LFL_PROTMAP))
{
smar_addr->ara_rhs2 = sm_str_new(smar_addr->ara_rpool, MAXADDRLEN,
SMAR_MAXRHS);
if (NULL == smar_addr->ara_rhs2)
goto done;
}
if (SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_SS_SE_CONF))
{
smar_addr->ara_rhs_conf = sm_str_new(smar_addr->ara_rpool, MAXADDRLEN,
SMAR_MAXRHS);
if (NULL == smar_addr->ara_rhs_conf)
goto done;
}
if (SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_RCPT_PROT)
&& SMARA_IS_LFLAG(smar_addr, SMARA_LFL_PROTMAP))
{
ret = sm_map_open(smar_ctx->smar_maps,
smar_ctx->smar_strmaptype /* HACK!!! should be NAME */,
smar_ctx->smar_strmaptype, 0, "", SMAP_MODE_RDONLY,
&smar_addr->ara_str_map, SMPO_END);
if (sm_is_err(ret)) {
SMAR_LEV_DPRINTF(5, (SMAR_DEBFP, "sev=ERROR, func=smar_addr_init, map=%C, sm_map_open=%r\n", ret, smar_ctx->smar_strmaptype));
goto done;
}
}
if (SMARA_IS_LFLAG(smar_addr, SMARA_LFL_2821)) {
SM_ASSERT(SMARA_IS_FLAG(smar_addr, SMARA_FL_RFC2821));
sm_str_clr(smar_addr->ara_str);
ret = t2821_parts(&smar_addr->ara_a_rcpt,
smar_ctx->smar_cnf.smar_cnf_addr_delim,
true, smar_addr->ara_str, smar_addr->ara_detail,
smar_addr->ara_domain_pa, &smar_addr->ara_delim);
if (sm_is_err(ret)) {
SMAR_LEV_DPRINTF(5, (SMAR_DEBFP, "sev=ERROR, func=smar_addr_init, t2821_parts=%r\n", ret));
goto done;
}
if (0 == ret) {
/* no delimiter: detail must be set to NULL! */
SM_STR_FREE(smar_addr->ara_detail);
}
}
else if (SMARA_IS_LFLAG(smar_addr, SMARA_LFL_IPV4|SMARA_LFL_STR)) {
sm_str_clr(smar_addr->ara_str);
ret = sm_str_cat(smar_addr->ara_str, smar_addr->ara_pa);
if (sm_is_err(ret))
goto done;
}
/* ... other cases?? ... */
SMARA_SET_FLAG(smar_addr, SMARA_FL_INIT);
return ret;
done:
SMARA_SET_FLAG(smar_addr, SMARA_FL_INIT);
*rv = SM_DONE;
return ret;
}
/*
** SMAR_ACC_ANALYSE -- Analyse result of current check/map lookup
**
** Parameters:
** smar_ctx -- SMAR context
** smar_addr -- address context
** ret -- return value of map lookup
**
** Returns:
** usual sm_error code
** if ok: smar_addr->ara_status should have result of lookup(s)
**
** Locking: smar_addr must be under control of the caller.
**
** Last code review:
** Last code change: 2005-08-24 05:24:02
*/
static sm_ret_T
smar_acc_analyse(smar_ctx_P smar_ctx, smar_addr_P smar_addr, sm_ret_T ret)
{
sm_ret_T res;
/* note: ara_str is not necessarily the item that was looked up! */
sm_log_write(smar_ctx->smar_lctx,
AR_LCAT_RESOLVER, AR_LMOD_RESOLVER,
SM_LOG_INFO, 14,
"sev=INFO, func=smar_acc_analyse, tag=%S, str=%S, ret=%r, rhs=%.256S",
smar_addr->ara_tag, smar_addr->ara_str, ret, smar_addr->ara_rhs);
if (ret != SM_SUCCESS) {
/* XXX check for other temporary errors? */
res = smar_map2acc(ret);
if (res == sm_error_perm(SM_EM_AR, EINVAL)) {
sm_log_write(smar_ctx->smar_lctx,
AR_LCAT_RESOLVER, AR_LMOD_RESOLVER,
SM_LOG_ERR, 4,
"sev=ERROR, func=smar_acc_analyse, str=%S, ret=%m, rhs=%.256S",
smar_addr->ara_str, ret,
smar_addr->ara_rhs);
}
smar_addr->ara_mapres = res;
smar_addr->ara_status = SMTP_R_CONT;
sm_str_clr(smar_addr->ara_rhs);
}
else if (sm_str_getlen(smar_addr->ara_rhs) > 0 &&
SMARA_IS_FLAG(smar_addr, SMARA_FL_RP_ANALYSE) &&
SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_RCPT_PROT))
{
res = smar_prot_rhs(smar_ctx, smar_addr);
}
else if (sm_str_getlen(smar_addr->ara_rhs) > 0) {
char *rhsc;
uint mod_len;
mod_len = 0;
rhsc = (char *) sm_str_getdata(smar_addr->ara_rhs);
if (NULL == rhsc) {
/* tempfail... */
ret = sm_error_temp(SM_EM_AR, ENOMEM);
goto error;
}
if (sm_str_getlen(smar_addr->ara_rhs) > RHS_QUICK_LEN
&& strncasecmp(rhsc, RHS_QUICK, RHS_QUICK_LEN) == 0)
{
mod_len = RHS_QUICK_LEN;
rhsc += RHS_QUICK_LEN;
SMAR_SET_RFL(smar_addr, SMAR_R_QUICK);
}
else if (sm_str_getlen(smar_addr->ara_rhs) > RHS_DELAY_LEN
&& strncasecmp(rhsc, RHS_DELAY, RHS_DELAY_LEN) == 0)
{
mod_len = RHS_DELAY_LEN;
rhsc += RHS_DELAY_LEN;
SMAR_SET_RFL(smar_addr, SMAR_R_DELAY);
}
if (sm_strcaseeq(rhsc, "OK")) {
sm_str_clr(smar_addr->ara_rhs);
smar_addr->ara_mapres = SM_ACC_FOUND;
smar_addr->ara_status = SMTP_R_OK;
/* stop further lookups?? */
/* see design docs about sequence of lookups! */
SMARA_SET_FLAG(smar_addr, SMARA_FL_STOP);
}
else if (sm_strcaseeq(rhsc, "RELAY")) {
sm_str_clr(smar_addr->ara_rhs);
smar_addr->ara_mapres = SM_ACC_FOUND;
smar_addr->ara_status = SMTP_R_RELAY;
/* stop further lookups?? */
/* see design docs about sequence of lookups! */
SMARA_SET_FLAG(smar_addr, SMARA_FL_STOP);
}
else if (sm_strcaseeq(rhsc, "CONT")
/* || sm_strcaseeq(rhsc, "DUNNO") */ )
{
sm_str_clr(smar_addr->ara_rhs);
smar_addr->ara_mapres = SM_ACC_FOUND;
smar_addr->ara_status = SMTP_R_CONT;
}
else if (sm_strcaseeq(rhsc, "REJECT")) {
sm_str_clr(smar_addr->ara_rhs);
smar_addr->ara_mapres = SM_ACC_FOUND;
smar_addr->ara_status = SMTP_R_REJECT;
}
else if (sm_strcaseeq(rhsc, "DISCARD")) {
sm_str_clr(smar_addr->ara_rhs);
smar_addr->ara_mapres = SM_ACC_FOUND;
smar_addr->ara_status = SMTP_R_DISCARD;
/* stop further lookups?? */
/* see design docs about sequence of lookups! */
SMARA_SET_FLAG(smar_addr, SMARA_FL_STOP);
}
else if (strncasecmp(rhsc, RHS_ERROR, RHS_ERROR_LEN) == 0) {
smar_addr->ara_rhstagoff = RHS_ERROR_LEN + mod_len;
smar_addr->ara_mapres = SM_ACC_FOUND;
if (IS_SMTP_REPLY_STR(smar_addr->ara_rhs, smar_addr->ara_rhstagoff))
{
smar_addr->ara_status = SMTP_REPLY_STR2VAL(smar_addr->ara_rhs,
smar_addr->ara_rhstagoff);
}
}
}
return SM_SUCCESS;
error:
return ret;
}
/*
** SMAR_GREY -- Perform greylisting check
**
** Parameters:
** smar_ctx -- SMAR context
** smar_addr -- address context
** perr -- (pointer to) error code (output)
**
** Returns:
** usual sm_error code
** if ok: smar_addr->ara_rhs has result, smar_acc_analyse()
** should be called to "analyse" it.
**
** Locking: smar_addr must be under control of the caller.
**
** Last code review:
** Last code change:
*/
static sm_ret_T
smar_grey(smar_ctx_P smar_ctx, smar_addr_P smar_addr, sm_ret_T *perr)
{
sm_ret_T ret;
ipv4_T ipv4;
SM_IS_SMAR_CTX(smar_ctx);
SM_IS_SMAR_ADDR(smar_addr);
SM_REQUIRE(perr != NULL);
*perr = SM_SUCCESS;
if (!SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_GREY)
|| SMARA_IS_FLAG(smar_addr, SMARA_FL_GOTGREY)
|| !SMAR_IS_FLAG(smar_ctx, SMAR_FL_HASGREY))
return SM_NOTDONE;
SMARA_SET_FLAG(smar_addr, SMARA_FL_GOTGREY);
if (SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_RCPT_ACC)) {
ipv4 = smar_addr->ara_ipv4;
ret = SM_SUCCESS;
}
else {
ret = sm_inet_a2ipv4((const char *)sm_str_getdata(smar_addr->ara_pa),
NULL, &ipv4);
}
if (sm_is_success(ret) && ipv4 != INADDR_ANY &&
smar_addr->ara_conn_time != 0)
{
ret = sm_greyctl(smar_ctx->smar_greyctx, (uchar *)&ipv4, sizeof(ipv4),
smar_addr->ara_conn_time);
SMAR_LEV_DPRINTF(1, (SMAR_DEBFP, "sev=DBG, func=smar_grey, grey=%r, ip=%A, now=%ld\n", ret, ipv4, (long) smar_addr->ara_conn_time));
/*
** "convert" ret into "map lookup result"
** suitable for smar_acc_analyse()
*/
if (SM_GREY_AGAIN == ret || SM_GREY_FIRST == ret || SM_GREY_WAIT == ret)
{
ret = SM_SUCCESS;
sm_str_clr(smar_addr->ara_rhs);
(void) sm_str_scat(smar_addr->ara_rhs,
"error:421 4.7.0 Come back later.");
smar_addr->ara_mapres = SM_ACC_FOUND;
SMAR_SET_RFL(smar_addr, SMAR_R_GREY);
}
else
ret = SM_MAP_NOTFOUND;
SMARA_SET_FLAG(smar_addr, SMARA_FL_DIDLOOKUP);
SMAR_SET_RFL(smar_addr, SMAR_R_GREYCHKD);
}
SMARA_SET_FLAG(smar_addr, SMARA_FL_GOTGREY);
return ret;
}
/*
** SMAR_CLTRESOLVE -- Perform cltresolve check
**
** Parameters:
** smar_ctx -- SMAR context
** smar_addr -- address context
** perr -- (pointer to) error code (output)
**
** Returns:
** usual sm_error code
**
** Locking: smar_addr must be under control of the caller.
**
** Last code review:
** Last code change:
*/
static sm_ret_T
smar_cltresolve(smar_ctx_P smar_ctx, smar_addr_P smar_addr, sm_ret_T *perr)
{
sm_ret_T ret;
#define CLT_RVRS_TAG "cltresolve:"
SM_REQUIRE(perr != NULL);
*perr = SM_SUCCESS;
if (SMARA_IS_FLAG(smar_addr, SMARA_FL_GOTRVRSA)
|| !SMARA_IS_LFLAG(smar_addr, SMARA_LFL_RVACC))
return SM_NOTDONE;
ret = SM_SUCCESS;
sm_str_clr(smar_addr->ara_str);
/* XXX can block */
if (smar_dns_getret(smar_addr) == SM_SUCCESS
&& sm_str_scat(smar_addr->ara_str, CLT_RVRS_TAG) == SM_SUCCESS
&& sm_str_scat(smar_addr->ara_str,
sm_rslv2txt(smar_addr->ara_rvrs->arv_ret)) == SM_SUCCESS)
{
sm_str_clr(smar_addr->ara_rhs);
ret = sm_map_lookup(smar_ctx->smar_access, SMMAP_FL_LWR_KEY,
smar_addr->ara_str, smar_addr->ara_rhs);
SMARA_SET_FLAG(smar_addr, SMARA_FL_DIDLOOKUP);
}
SMARA_SET_FLAG(smar_addr, SMARA_FL_GOTRVRSA);
return ret;
}
/*
** SMAR_CLTNAME -- Perform clientname check
**
** Parameters:
** smar_ctx -- SMAR context
** smar_addr -- address context
** perr -- (pointer to) error code (output)
**
** Returns:
** usual sm_error code
**
** Locking: smar_addr must be under control of the caller.
**
** Last code review:
** Last code change:
*/
static sm_ret_T
smar_cltname(smar_ctx_P smar_ctx, smar_addr_P smar_addr, sm_ret_T *perr)
{
sm_ret_T ret;
SM_REQUIRE(perr != NULL);
*perr = SM_SUCCESS;
/* Note: SMARA_LT_RVRS_N_ACC requires SMARA_LFL_RVACC! */
if (SMARA_IS_FLAG(smar_addr, SMARA_FL_GOTRVRSN)
|| !SMARA_IS_FLAG(smar_addr, SMARA_FL_GOTRVRSA)
|| !SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_RVRS_N_ACC)
|| NULL == smar_addr->ara_rvrs_hostname)
return SM_NOTDONE;
ret = SM_SUCCESS;
sm_str_clr(smar_addr->ara_tag);
ret = sm_str_scat(smar_addr->ara_tag, CLT_N_TAG);
ret = sm_map_lookup_domain(smar_ctx->smar_access,
(sm_rdstr_P) smar_addr->ara_rvrs_hostname,
smar_addr->ara_tag, 0, smar_addr->ara_rhs);
SMARA_SET_FLAG(smar_addr, SMARA_FL_DIDLOOKUP|SMARA_FL_GOTRVRSN);
return ret;
}
/*
** SMAR_DNSBL -- check DNSBL results (look up in access map)
**
** Parameters:
** smar_ctx -- SMAR context
** smar_addr -- address context
** perr -- (pointer to) error code (output)
**
** Returns:
** usual sm_error code
**
** Locking: smar_addr must be under control of the caller.
**
** Last code review:
** Last code change: 2007-06-21 05:07:49
*/
static sm_ret_T
smar_chk_dnsbl(smar_ctx_P smar_ctx, smar_addr_P smar_addr, sm_ret_T *perr)
{
sm_ret_T ret;
char *dnsbltag;
SM_REQUIRE(perr != NULL);
*perr = SM_SUCCESS;
if (SMARA_IS_LFLAG(smar_addr, SMARA_LFL_DNSBL) &&
!SMARA_IS_FLAG(smar_addr, SMARA_FL_A4DNSBL))
{
/* This is an asynchronous function! */
ret = smar_startdnsbl(smar_ctx, smar_addr);
if (sm_is_err(ret)) {
*perr = ret;
return ret;
}
}
/* get DNS BL results? */
if (SMARA_IS_LFLAG(smar_addr, SMARA_LFL_DNSBL)
&& !SMARA_IS_FLAG(smar_addr, SMARA_FL_GOTDNSBL)
&& !SMARA_IS_FLAG(smar_addr, SMARA_FL_FAILDNS))
{
/* XXX can block */
if (smar_dns_getret(smar_addr) != SM_SUCCESS)
SMARA_SET_FLAG(smar_addr, SMARA_FL_FAILDNS);
}
if (!SMARA_IS_LFLAG(smar_addr, SMARA_LFL_DNSBL)
|| !SMARA_IS_FLAG(smar_addr, SMARA_FL_GOTDNSBL)
|| smar_addr->ara_c_dnsbl >= SM_MAX_DNSBL
|| smar_ctx->smar_cnf.smar_cnf_dnsbl[smar_addr->ara_c_dnsbl].scdb_name
== NULL)
return SM_NOTDONE;
ret = SM_SUCCESS;
sm_str_clr(smar_addr->ara_str);
dnsbltag = smar_ctx->smar_cnf.smar_cnf_dnsbl[smar_addr->ara_c_dnsbl].scdb_tag;
if (NULL == dnsbltag)
dnsbltag = "dnsbl";
if (smar_addr->ara_dnsblres[smar_addr->ara_c_dnsbl].sbdr_res == SM_DNSBL_RES_OK
&& sm_strprintf(smar_addr->ara_str, "%s:%A", dnsbltag,
smar_addr->ara_dnsblres[smar_addr->ara_c_dnsbl].sdbr_ipv4) > 0)
{
/* should this use _lookup_ip? */
sm_str_clr(smar_addr->ara_rhs);
ret = sm_map_lookup(smar_ctx->smar_access,
SMMAP_FL_LWR_KEY, smar_addr->ara_str, smar_addr->ara_rhs);
SMARA_SET_FLAG(smar_addr, SMARA_FL_DIDLOOKUP);
*perr = SM_AGAIN;
SMAR_LEV_DPRINTF(5, (SMAR_DEBFP, "dnsbl=%S, ret=%r\n", smar_addr->ara_str, ret));
/*
* check the rest... but how to get the result back?
* add a counter:
* smar_addr->ara_dnsblres[smar_addr->ara_c_dnsbl].sbdr_i
* and check it against the limit in the statement above similar to
* smar_addr->ara_c_dnsbl >= SM_MAX_DNSBL
*
* however, then we still have the problem of selecting the "right"
* result, e.g., perm over temp error.
*
* call something like "analyze" to extract result?
*/
/* hack for now: try to find the first match, this is random! */
if (smar_addr->ara_dnsblres[smar_addr->ara_c_dnsbl].sdbr_n > 0 &&
smar_addr->ara_dnsblres[smar_addr->ara_c_dnsbl].sdbr_ipv4s != NULL
&& ret != SM_SUCCESS
)
{
uint ui;
ipv4_T *ipv4s;
for (ui = 0,
ipv4s = smar_addr->ara_dnsblres[smar_addr->ara_c_dnsbl].sdbr_ipv4s;
ui < smar_addr->ara_dnsblres[smar_addr->ara_c_dnsbl].sdbr_n &&
ret != SM_SUCCESS;
++ui)
{
sm_str_clr(smar_addr->ara_str);
if (sm_strprintf(smar_addr->ara_str, "%s:%A", dnsbltag,
ipv4s[ui]) > 0)
{
sm_str_clr(smar_addr->ara_rhs);
ret = sm_map_lookup(smar_ctx->smar_access,
SMMAP_FL_LWR_KEY, smar_addr->ara_str,
smar_addr->ara_rhs);
SMAR_LEV_DPRINTF(5, (SMAR_DEBFP, "dnsbl=%S, ret=%r\n", smar_addr->ara_str, ret));
}
}
}
}
else if (smar_addr->ara_dnsblres[smar_addr->ara_c_dnsbl].sbdr_res == DNSR_TEMP
&& sm_str_scatv(smar_addr->ara_str, 2, dnsbltag, ":temp") == SM_SUCCESS)
{
sm_str_clr(smar_addr->ara_rhs);
ret = sm_map_lookup(smar_ctx->smar_access,
SMMAP_FL_LWR_KEY, smar_addr->ara_str, smar_addr->ara_rhs);
SMARA_SET_FLAG(smar_addr, SMARA_FL_DIDLOOKUP);
*perr = SM_AGAIN;
SMAR_LEV_DPRINTF(5, (SMAR_DEBFP, "dnsbl=%S, ret=%r\n", smar_addr->ara_str, ret));
}
else if (smar_addr->ara_dnsblres[smar_addr->ara_c_dnsbl].sbdr_res == DNSR_PERM
&& sm_str_scatv(smar_addr->ara_str, 2, dnsbltag, ":perm") == SM_SUCCESS)
{
sm_str_clr(smar_addr->ara_rhs);
ret = sm_map_lookup(smar_ctx->smar_access,
SMMAP_FL_LWR_KEY, smar_addr->ara_str, smar_addr->ara_rhs);
SMARA_SET_FLAG(smar_addr, SMARA_FL_DIDLOOKUP);
*perr = SM_AGAIN;
SMAR_LEV_DPRINTF(5, (SMAR_DEBFP, "dnsbl=%S, ret=%r\n", smar_addr->ara_str, ret));
}
++smar_addr->ara_c_dnsbl;
return ret;
}
/*
** SMAR_MXBADIP -- Perform MXBADIP check
**
** Parameters:
** smar_ctx -- SMAR context
** smar_addr -- address context
** perr -- (pointer to) error code (output)
**
** Returns:
** usual sm_error code
**
** Locking: smar_addr must be under control of the caller.
**
** Last code review:
** Last code change:
*/
static sm_ret_T
smar_mxbadip(smar_ctx_P smar_ctx, smar_addr_P smar_addr, sm_ret_T *perr)
{
sm_ret_T ret;
smar_dns_P smar_dns;
SM_REQUIRE(perr != NULL);
*perr = SM_SUCCESS;
/*
** XXX Need consistency check!
** ara_rcpts is only initialized of
** SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_MAIL_ROUTE)
** but this test is supposed to be done
** SMARA_IS_LFLAG(smar_addr, SMARA_LFL_MXACC)
** -> confusion with lookup type and flags!
*/
if (smar_addr->ara_mapres != SM_ACC_NOTFOUND
|| !SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_MAIL_ROUTE)
|| !SMARA_IS_LFLAG(smar_addr, SMARA_LFL_MXACC))
return SM_NOTDONE;
ret = SM_SUCCESS;
SMAR_LEV_DPRINTF(5, (SMAR_DEBFP, "func=smar_mxbadip, call=smar_dns_getret, where=mxbadip\n"));
if (!SMARA_IS_FLAG(smar_addr, SMARA_FL_GOTRCPT)) {
/* XXX can block */
ret = smar_dns_getret(smar_addr);
if (sm_is_err(ret))
goto error;
}
SMAR_LEV_DPRINTF(5, (SMAR_DEBFP, "func=smar_mxbadip, ara_dns_ret=%r\n", smar_addr->ara_dns_ret));
if (smar_addr->ara_dns_ret != SM_SUCCESS) {
ret = smar_dns2ret(smar_ctx, smar_addr);
SMAR_LEV_DPRINTF(5, (SMAR_DEBFP, "func=smar_mxbadip, ara_dns_ret=%r, ret=%r\n", smar_addr->ara_dns_ret, ret));
if (sm_is_err(ret))
goto error;
/* required for smar_access_re() */
smar_addr->ara_mapres = SM_ACC_FOUND;
SMARA_CLR_FLAG(smar_addr, SMARA_FL_DIDLOOKUP);
*perr = SM_BREAK;
return SM_SUCCESS;
}
#define MAIL_MX_TAG "mxbadip:"
sm_str_clr(smar_addr->ara_str);
SM_IS_SMAR_RCPTS(smar_addr->ara_rcpts);
SM_IS_SMAR_RCPT(smar_addr->ara_rcpt);
SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "n_mx=%d, ipv4=%A\n", smar_addr->ara_rcpt->arr_A_qsent, smar_addr->ara_rcpt->arr_ipv4));
sm_str_scat(smar_addr->ara_str, MAIL_MX_TAG);
/*
** This would be a nested loop:
**
** for (c_MX = 0; c_MX < smar_addr->ara_rcpt->arr_A_qsent; c_MX++)
** smar_dns = &(smar_addr->ara_rcpt->arr_res[c_MX]);
** for (c_A = 0; c_A < smar_dns->ardns_n_Al c_A++)
** do lookup
**
** except that we are in a do { } while () loop already
** which we want to reuse for checking
** the results of each lookup.
** hence we "wrap" the loop "inside out":
**
** c_MX = c_A = 0;
** do {
** next_MX:
** if (c_MX < smar_addr->ara_rcpt->arr_A_qsent) {
** smar_dns = &(smar_addr->ara_rcpt->arr_res[c_MX]);
** * check whether c_A is OK *
** if (c_A >= smar_dns->ardns_n_A) {
** * try next c_MX *
** ++c_MX; c_A = 0;
** goto next_MX;
** }
** do lookup
** }
** } while ();
**
** let's remove the goto:
**
** c_MX = c_A = 0;
** do {
** if (c_MX < smar_addr->ara_rcpt->arr_A_qsent) {
** smar_dns = &(smar_addr->ara_rcpt->arr_res[c_MX]);
** * check whether c_A is OK *
** while (c_A >= smar_dns->ardns_n_A) {
** c_A = 0; ++c_MX;
** if (c_MX < smar_addr->ara_rcpt->arr_A_qsent)
** smar_dns = &(smar_addr->ara_rcpt->arr_res[c_MX]);
** else
** break;
** }
** * check whether end has not been reached *
** if (c_MX < smar_addr->ara_rcpt->arr_A_qsent &&
** c_A < smar_dns->ardns_n_A) {
** do lookup
** }
** }
** } while ();
**
*/
if (0 == smar_addr->ara_rcpt->arr_A_qsent) {
sm_inet_ipv4str(smar_addr->ara_rcpt->arr_ipv4, smar_addr->ara_str);
SMARA_SET_FLAG(smar_addr, SMARA_FL_DIDLOOKUP);
SMARA_CLR_LFLAG(smar_addr, SMARA_LFL_MXACC);
}
else if (smar_addr->ara_c_MX < smar_addr->ara_rcpt->arr_A_qsent) {
smar_dns = &(smar_addr->ara_rcpt->arr_res[smar_addr->ara_c_MX]);
SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "smar_addr->ara_c_MX=%d, smar_dns=%p\n", smar_addr->ara_c_MX, smar_dns));
while (smar_addr->ara_c_A >= smar_dns->ardns_n_A) {
smar_addr->ara_c_A = 0;
++smar_addr->ara_c_MX;
if (smar_addr->ara_c_MX < smar_addr->ara_rcpt->arr_A_qsent)
smar_dns = &(smar_addr->ara_rcpt->arr_res[smar_addr->ara_c_MX]);
else
break;
}
if (smar_addr->ara_c_MX < smar_addr->ara_rcpt->arr_A_qsent &&
smar_addr->ara_c_A < smar_dns->ardns_n_A)
{
/* XXX HACK: ignore addresses with value 0 */
if (smar_dns->ardns_A_rrs[smar_addr->ara_c_A] != 0)
sm_inet_ipv4str(smar_dns->ardns_A_rrs[smar_addr->ara_c_A],
smar_addr->ara_str);
SMAR_LEV_DPRINTF(4, (SMAR_DEBFP, "smar_addr->ara_c_MX=%d, smar_addr->ara_c_A=%d, a=%A, str=%S\n", smar_addr->ara_c_MX, smar_addr->ara_c_A, smar_dns->ardns_A_rrs[smar_addr->ara_c_A], smar_addr->ara_str));
++smar_addr->ara_c_A;
SMARA_SET_FLAG(smar_addr, SMARA_FL_DIDLOOKUP);
}
}
else
SMARA_CLR_LFLAG(smar_addr, SMARA_LFL_MXACC);
if (SMARA_IS_FLAG(smar_addr, SMARA_FL_DIDLOOKUP)) {
sm_str_clr(smar_addr->ara_rhs);
ret = sm_map_lookup(smar_ctx->smar_access, SMMAP_FL_LWR_KEY,
smar_addr->ara_str, smar_addr->ara_rhs);
}
/* XXX use a different flag to indicate that the test has been done? */
return ret;
error:
*perr = ret;
return ret;
}
/*
** SMAR_CERTSUB -- Perform CERT subject check
**
** Parameters:
** smar_ctx -- SMAR context
** smar_addr -- address context
** perr -- (pointer to) error code (output)
**
** Returns:
** usual sm_error code
**
** Locking: smar_addr must be under control of the caller.
**
** Last code review:
** Last code change:
*/
static sm_ret_T
smar_certsub(smar_ctx_P smar_ctx, smar_addr_P smar_addr, sm_ret_T *perr)
{
sm_ret_T ret;
SM_REQUIRE(perr != NULL);
*perr = SM_SUCCESS;
if (!SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_CERT_RELAY)
|| !SMARA_IS_FLAG(smar_addr, SMARA_FL_GOTCERTISS)
|| SMARA_IS_FLAG(smar_addr, SMARA_FL_GOTCERTSUB))
return SM_NOTDONE;
ret = SM_SUCCESS;
SMARA_SET_FLAG(smar_addr, SMARA_FL_GOTCERTSUB);
sm_str_clr(smar_addr->ara_str);
if (sm_str_scat(smar_addr->ara_str, CERT_SUBJECT_TAG) == SM_SUCCESS
&& sm_str_cat(smar_addr->ara_str, smar_addr->ara_pa2) == SM_SUCCESS)
{
sm_str_clr(smar_addr->ara_rhs);
ret = sm_map_lookup(smar_ctx->smar_access, SMMAP_FL_LWR_KEY,
smar_addr->ara_str, smar_addr->ara_rhs);
SMARA_SET_FLAG(smar_addr, SMARA_FL_DIDLOOKUP);
}
return ret;
}
/*
** SMAR_PROTRCPT -- Perform PROTRCPT check
**
** Parameters:
** smar_ctx -- SMAR context
** smar_addr -- address context
** perr -- (pointer to) error code (output)
**
** Returns:
** usual sm_error code
**
** Locking: smar_addr must be under control of the caller.
**
** Last code review:
** Last code change:
*/
static sm_ret_T
smar_protrcpt(smar_ctx_P smar_ctx, smar_addr_P smar_addr, sm_ret_T *perr)
{
sm_ret_T ret;
SM_REQUIRE(perr != NULL);
*perr = SM_SUCCESS;
if (!SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_RCPT_PROT)
|| SMARA_IS_FLAG(smar_addr, SMARA_FL_GOTRCPTPROT))
return SM_NOTDONE;
ret = SM_SUCCESS;
sm_str_clr(smar_addr->ara_tag);
ret = sm_str_scat(smar_addr->ara_tag, PROT_RCPT_TAG);
SMARA_SET_FLAG(smar_addr, SMARA_FL_GOTRCPTPROT|SMARA_FL_RP_ANALYSE);
/* flags (SMMAP_LFL_*) to be set by caller of smar?? */
ret = sm_map_lookup_addr(smar_ctx->smar_access,
smar_addr->ara_str, smar_addr->ara_detail,
smar_addr->ara_domain_pa, smar_addr->ara_tag,
smar_addr->ara_delim,
SMMAP_LFL_ALL|SMMAP_LFL_NOAT|
(SMARA_IS_LFLAG(smar_addr, SMARA_LFL_ACCIMPLDET)
? SMMAP_LFL_IMPLDET : 0),
smar_addr->ara_rhs);
SMARA_SET_FLAG(smar_addr, SMARA_FL_DIDLOOKUP);
return ret;
}
/*
** SMAR_CHK_IPV4 -- perform IPv4 access map lookup.
**
** Parameters:
** smar_ctx -- SMAR context
** smar_addr -- address context
** perr -- (pointer to) error code (output)
**
** Returns:
** usual sm_error code
**
** Locking: smar_addr must be under control of the caller.
**
** Last code review:
** Last code change:
*/
static sm_ret_T
smar_chk_ipv4(smar_ctx_P smar_ctx, smar_addr_P smar_addr, sm_ret_T *perr)
{
sm_ret_T ret;
SM_IS_SMAR_ADDR(smar_addr);
SM_IS_SMAR_CTX(smar_ctx);
SM_REQUIRE(perr != NULL);
*perr = SM_SUCCESS;
if (!SMARA_IS_LFLAG(smar_addr, SMARA_LFL_IPV4)
|| !SMARA_IS_LFLAG(smar_addr, SMARA_LFL_SUB)
|| SMARA_IS_FLAG(smar_addr, SMARA_FL_GOTIPV4ACC))
return SM_NOTDONE;
sm_str_clr(smar_addr->ara_tag);
ret = sm_str_scat(smar_addr->ara_tag, CLT_A_TAG);
ret = sm_map_lookup_ip(smar_ctx->smar_access,
smar_addr->ara_str, smar_addr->ara_tag,
SMMAP_LFL_SUBNETS|SMMAP_LFL_TAG, smar_addr->ara_rhs);
SMARA_SET_FLAG(smar_addr, SMARA_FL_DIDLOOKUP|SMARA_FL_GOTIPV4ACC);
SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "sev=DBG, func=smar_chk_ipv4, ip=%S, tag=%S, ret=%r\n", smar_addr->ara_str, smar_addr->ara_tag, ret));
return ret;
}
/*
** SMAR_CHK_2821 -- perform RFC 2821 address access map lookup.
**
** Parameters:
** smar_ctx -- SMAR context
** smar_addr -- address context
** perr -- (pointer to) error code (output)
**
** Returns:
** usual sm_error code
**
** Locking: smar_addr must be under control of the caller.
**
** Last code review:
** Last code change:
*/
static sm_ret_T
smar_chk_2821(smar_ctx_P smar_ctx, smar_addr_P smar_addr, sm_ret_T *perr)
{
sm_ret_T ret;
SM_IS_SMAR_ADDR(smar_addr);
SM_IS_SMAR_CTX(smar_ctx);
SM_REQUIRE(perr != NULL);
*perr = SM_SUCCESS;
if (!SMARA_IS_LFLAG(smar_addr, SMARA_LFL_2821)
|| SMARA_IS_FLAG(smar_addr, SMARA_FL_GOT2821ACC))
return SM_NOTDONE;
/* fixme: flags (SMMAP_LFL_*) to be set by caller of smar?? */
ret = sm_map_lookup_addr(smar_ctx->smar_access,
smar_addr->ara_str, smar_addr->ara_detail,
smar_addr->ara_domain_pa, smar_addr->ara_tag,
smar_addr->ara_delim,
SMMAP_LFL_ALL|SMMAP_LFL_NOAT|
(SMARA_IS_LFLAG(smar_addr, SMARA_LFL_ACCIMPLDET)
? SMMAP_LFL_IMPLDET : 0),
smar_addr->ara_rhs);
SMARA_SET_FLAG(smar_addr, SMARA_FL_DIDLOOKUP|SMARA_FL_GOT2821ACC);
SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "sev=DBG, func=smar_chk_2821, str=%S, detail=%S, domain=%S, tag=%S, ltype=%#x, lflags=%#x, flags=%#x, ret=%r\n", smar_addr->ara_str, smar_addr->ara_detail, smar_addr->ara_domain_pa, smar_addr->ara_tag, smar_addr->ara_ltype, smar_addr->ara_lflags, smar_addr->ara_flags, ret));
return ret;
}
/*
** SMAR_CHK_CERTISS -- perform CERT issuer access map lookup.
**
** Parameters:
** smar_ctx -- SMAR context
** smar_addr -- address context
** perr -- (pointer to) error code (output)
**
** Returns:
** usual sm_error code
**
** Locking: smar_addr must be under control of the caller.
**
** Last code review:
** Last code change:
*/
static sm_ret_T
smar_chk_certiss(smar_ctx_P smar_ctx, smar_addr_P smar_addr, sm_ret_T *perr)
{
sm_ret_T ret;
SM_IS_SMAR_ADDR(smar_addr);
SM_IS_SMAR_CTX(smar_ctx);
SM_REQUIRE(perr != NULL);
*perr = SM_SUCCESS;
if (!SMARA_IS_LFLAG(smar_addr, SMARA_LFL_CERT)
|| SMARA_IS_FLAG(smar_addr, SMARA_FL_GOTCERTISS))
return SM_NOTDONE;
ret = SM_SUCCESS;
SMARA_SET_FLAG(smar_addr, SMARA_FL_GOTCERTISS);
sm_str_clr(smar_addr->ara_str);
if (sm_str_scat(smar_addr->ara_str, CERT_ISSUER_TAG) == SM_SUCCESS &&
sm_str_cat(smar_addr->ara_str, smar_addr->ara_pa) == SM_SUCCESS)
{
sm_str_clr(smar_addr->ara_rhs);
ret = sm_map_lookup(smar_ctx->smar_access,
SMMAP_FL_LWR_KEY|SMMAP_LFL_TAG,
smar_addr->ara_str, smar_addr->ara_rhs);
SMARA_SET_FLAG(smar_addr, SMARA_FL_DIDLOOKUP);
}
return ret;
}
/*
** SMAR_CHK_GEN -- perform "generic" access map lookup.
**
** Parameters:
** smar_ctx -- SMAR context
** smar_addr -- address context
** perr -- (pointer to) error code (output)
**
** Returns:
** usual sm_error code
**
** Locking: smar_addr must be under control of the caller.
**
** Last code review:
** Last code change:
*/
static sm_ret_T
smar_chk_gen(smar_ctx_P smar_ctx, smar_addr_P smar_addr, sm_ret_T *perr)
{
sm_ret_T ret;
sm_str_P exchg;
SM_IS_SMAR_ADDR(smar_addr);
SM_IS_SMAR_CTX(smar_ctx);
SM_REQUIRE(perr != NULL);
*perr = SM_SUCCESS;
/* Which flag to check here for SM_NOTDONE??? */
if (!SM_IS_FLAG(smar_addr->ara_ltype, SMAR_LT_ACCESS)
|| (SMARA_IS_LFLAG(smar_addr, SMARA_LFL_IPV4)
&& SMARA_IS_LFLAG(smar_addr, SMARA_LFL_SUB))
|| SMARA_IS_LFLAG(smar_addr, SMARA_LFL_2821)
|| SMARA_IS_LFLAG(smar_addr, SMARA_LFL_CERT)
)
return SM_NOTDONE;
/*
** HACK: sm_map_lookup() doesn't support tags, hence:
** append ara_str to ara_tag and use that as key (by exchanging
** ara_tag and ara_str).
*/
ret = sm_str_cat(smar_addr->ara_tag, smar_addr->ara_str);
if (sm_is_err(ret)) {
/* tempfail? */
*perr = SM_DONE;
}
else {
exchg = smar_addr->ara_str;
smar_addr->ara_str = smar_addr->ara_tag;
smar_addr->ara_tag = exchg;
ret = sm_map_lookup(smar_ctx->smar_access,
SMMAP_FL_LWR_KEY|SMMAP_FL_HAS_TAG,
smar_addr->ara_str, smar_addr->ara_rhs);
SMARA_SET_FLAG(smar_addr, SMARA_FL_DIDLOOKUP);
SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "sev=DBG, func=smar_chk_gen, str=%S, ltype=%#x, lflags=%#x, flags=%#x, ret=%r\n", smar_addr->ara_str, smar_addr->ara_ltype, smar_addr->ara_lflags, smar_addr->ara_flags, ret));
}
return ret;
}
/*
** SMAR_SS_SE_CONF -- look up smtps session configuration in access map
**
** Parameters:
** smar_ctx -- SMAR context
** smar_addr -- address context
** perr -- (pointer to) error code (output)
**
** Returns:
** usual sm_error code
**
** Locking: smar_addr must be under control of the caller.
**
** Last code review:
** Last code change:
*/
static sm_ret_T
smar_ss_se_conf(smar_ctx_P smar_ctx, smar_addr_P smar_addr, sm_ret_T *perr)
{
SM_IS_SMAR_ADDR(smar_addr);
SM_IS_SMAR_CTX(smar_ctx);
SM_REQUIRE(perr != NULL);
*perr = SM_SUCCESS;
/* Which flag to check here for SM_NOTDONE??? */
if (!SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_SS_SE_CONF)
|| SMARA_IS_FLAG(smar_addr, SMARA_FL_GOTSSSECONF))
return SM_NOTDONE;
sm_str_clr(smar_addr->ara_tag);
sm_str_clr(smar_addr->ara_rhs_conf);
(void) sm_str_scat(smar_addr->ara_tag, SS_SE_CONF_A_TAG);
smar_addr->ara_maprescnf = sm_map_lookup_ip(smar_ctx->smar_access,
smar_addr->ara_str, smar_addr->ara_tag,
SMMAP_LFL_SUBNETS|SMMAP_LFL_TAG, smar_addr->ara_rhs_conf);
/* do not set SMARA_FL_DIDLOOKUP: there is nothing to analyze */
SMARA_SET_FLAG(smar_addr, SMARA_FL_GOTSSSECONF);
SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "sev=DBG, func=smar_ss_se_conf, ip=%S, tag=%S, ret=%r, rhs=%S\n", smar_addr->ara_str, smar_addr->ara_tag, smar_addr->ara_maprescnf, smar_addr->ara_rhs_conf));
return SM_SUCCESS;
}
/*
** SMAR_ACCESS_CHK -- Check address against access map and perform other
** checks as requested (e.g., DNS)
**
** Parameters:
** smar_ctx -- SMAR context
** smar_addr -- address context
**
** Returns:
** usual sm_error code
** if ok: smar_addr->ara_status should have result of lookup(s)
**
** Locking: smar_addr must be under control of the caller.
**
** Last code review:
** Last code change:
**
** split this into smaller functions that can be called individually?
*/
const smar_chk_ctx_T ar_chks[] = {
{ "smar_ss_se_conf", smar_ss_se_conf },
{ "smar_chk_ipv4", smar_chk_ipv4 },
{ "smar_chk_2821", smar_chk_2821 },
{ "smar_chk_certiss", smar_chk_certiss },
{ "smar_chk_gen", smar_chk_gen },
/*
{ "smar_access_first", smar_access_first },
*/
{ "smar_cltresolve", smar_cltresolve },
{ "smar_cltname", smar_cltname },
{ "smar_chk_dnsbl", smar_chk_dnsbl },
{ "smar_mxbadip", smar_mxbadip },
{ "smar_certsub", smar_certsub },
{ "smar_protrcpt", smar_protrcpt },
{ "smar_grey", smar_grey },
{ NULL, (smar_chk_F*)NULL},
};
sm_ret_T
smar_access_chk(smar_ctx_P smar_ctx, smar_addr_P smar_addr)
{
sm_ret_T ret, res;
uint fct_idx;
smar_chk_F *smar_cc_fct;
SM_IS_SMAR_ADDR(smar_addr);
SM_IS_SMAR_CTX(smar_ctx);
/* move to smar_access_check()??? only if the cleanup code is moved too */
ret = smar_addr_init(smar_ctx, smar_addr, &res);
if (sm_is_err(res))
goto error;
if (SM_DONE == res)
goto done;
/*
** lookup loop; note: ret is the result of the last lookup, it is
** "analyzed" at the begin of the loop.
*/
fct_idx = 0;
smar_cc_fct = ar_chks[fct_idx].arcc_fct;
for (;;) {
/*
** Perform lookups as long as requested.
*/
SMAR_LEV_DPRINTF(6, (SMAR_DEBFP, "func=smar_access_chk, where=before_while, ara_status=%d, flags=%#x\n", smar_addr->ara_status, smar_addr->ara_rflags));
while (smar_addr->ara_status == SMTP_R_CONT
&& !SMARA_IS_FLAG(smar_addr, SMARA_FL_STOP)
&& !SMARA_IS_FLAG(smar_addr, SMARA_FL_DIDLOOKUP)
&& fct_idx < SM_ARRAY_SIZE(ar_chks)
&& (smar_cc_fct = ar_chks[fct_idx].arcc_fct) != NULL)
{
/*
** ret is used by smar_acc_analyse()
** res is used "locally" to make decision about the control flow,
** e.g., whether to "error out", break, etc.
*/
ret = (*smar_cc_fct)(smar_ctx, smar_addr, &res);
SMAR_LEV_DPRINTF(5, (SMAR_DEBFP, "func=smar_access_chk, chk_fct=%s, ret=%r, res=%r\n", ar_chks[fct_idx].arcc_name, ret, res));
if (sm_is_err(res))
goto error;
if (SM_BREAK == res)
break;
if (SM_DONE == res)
goto done;
/* hack... how should this be done properly? */
if (res != SM_AGAIN)
++fct_idx;
}
if (!SMARA_IS_FLAG(smar_addr, SMARA_FL_DIDLOOKUP))
break;
ret = smar_acc_analyse(smar_ctx, smar_addr, ret);
if (SMARA_IS_FLAG(smar_addr, SMARA_FL_RP_ANALYSE))
SMARA_CLR_FLAG(smar_addr, SMARA_FL_RP_ANALYSE);
if (sm_is_err(ret))
goto error;
SMARA_CLR_FLAG(smar_addr, SMARA_FL_DIDLOOKUP);
}
done:
SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "func=smar_access_chk, where=done, ara_status=%d\n", smar_addr->ara_status));
if (smar_addr->ara_status == SMTP_R_CONT
&& SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_MAIL_ROUTE)
&& !SMARA_IS_FLAG(smar_addr, SMARA_FL_GOTRCPT))
{
SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "func=smar_access_chk, calling=smar_dns_getret\n"));
/* XXX can block */
ret = smar_dns_getret(smar_addr);
if (SM_SUCCESS == ret) {
ret = smar_dns2ret(smar_ctx, smar_addr);
if (sm_is_err(ret))
goto error;
/* required for smar_access_re() */
smar_addr->ara_mapres = SM_ACC_FOUND;
/* XXX more? e.g., error text */
SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "func=smar_access_chk, smar_addr->ara_dns_ret=%d\n", smar_addr->ara_dns_ret));
}
else {
SMAR_LEV_DPRINTF(0, (SMAR_DEBFP, "sev=ERROR, func=smar_access_chk, smar_dns_getret=%r\n", ret));
}
}
/* when to perform greylisting??? */
if (SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_RCPT_ACC)
&& SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_GREY)
&& !SMARA_IS_FLAG(smar_addr, SMARA_FL_GOTGREY)
&& SMAR_IS_FLAG(smar_ctx, SMAR_FL_HASGREY)
&& (SMTP_R_OK == smar_addr->ara_status ||
SMTP_R_RELAY == smar_addr->ara_status)
&& !SMAR_IS_RFL(smar_addr, SMAR_R_QUICK)
)
{
ret = smar_grey(smar_ctx, smar_addr, &res);
if (SMAR_IS_RFL(smar_addr, SMAR_R_GREY)) {
ret = smar_acc_analyse(smar_ctx, smar_addr, ret);
if (SMARA_IS_FLAG(smar_addr, SMARA_FL_RP_ANALYSE))
SMARA_CLR_FLAG(smar_addr, SMARA_FL_RP_ANALYSE);
}
SMARA_CLR_FLAG(smar_addr, SMARA_FL_DIDLOOKUP);
}
/* claim that everything is ok */
ret = SM_SUCCESS;
/* fallthrough for cleanup */
error:
if (smar_addr->ara_rhs != NULL && sm_str_getlen(smar_addr->ara_rhs) > 0) {
ret = sm_str_scat(smar_addr->ara_rhs, "\r\n");
SM_ASSERT(SM_SUCCESS == ret);
}
if (SMARA_IS_FLAG(smar_addr, SMARA_FL_RFC2821))
a2821_free(&smar_addr->ara_a_rcpt);
SM_STR_FREE(smar_addr->ara_str);
SM_STR_FREE(smar_addr->ara_tag);
SM_STR_FREE(smar_addr->ara_detail);
SM_STR_FREE(smar_addr->ara_domain_pa);
/* XXX should we wait for result here? smar_addr->ara_rvrs needs to be free()d */
if ((SMARA_IS_FLAG(smar_addr, SMARA_FL_A4RVRS)
&& !SMARA_IS_FLAG(smar_addr, SMARA_FL_GOTRVRS)) ||
(SMARA_IS_FLAG(smar_addr, SMARA_FL_A4RCPT)
&& !SMARA_IS_FLAG(smar_addr, SMARA_FL_GOTRCPT)) ||
(SMARA_IS_FLAG(smar_addr, SMARA_FL_A4DNSBL)
&& !SMARA_IS_FLAG(smar_addr, SMARA_FL_GOTDNSBL)))
{
/*
** XXX really overwrite ret?
** what if ret already contains an error?
*/
/* XXX can block */
ret = smar_dns_getret(smar_addr);
}
if (smar_addr->ara_rvrs != NULL) {
(void) smar_rvrs_free(smar_addr->ara_rvrs);
smar_addr->ara_rvrs = NULL;
}
(void) smar_rcpts_free(smar_addr->ara_rcpts);
return ret;
}
/*
** SMAR_ACCESS_CHECK -- Check address against access map and perform other
** tests as requested; send answer
**
** Parameters:
** smar_ctx -- SMAR context
** smar_addr -- address context
**
** Returns:
** usual sm_error code
**
** Called by: smar_react()
**
** Locking: smar_addr has been created by smar_react(),
** this is the only function that has been given access
** (locking is only required for reverse resolution)
**
** Last code review: 2004-03-11 17:57:57; see comments below
** Last code change: 2004-04-11 18:20:34
*/
sm_ret_T
smar_access_check(smar_ctx_P smar_ctx, smar_addr_P smar_addr)
{
sm_ret_T ret;
smar_clt_ctx_P smar_clt_ctx;
SM_IS_SMAR_ADDR(smar_addr);
SM_IS_SMAR_CTX(smar_ctx);
smar_clt_ctx = smar_addr->ara_smar_clt_ctx;
SM_IS_SMAR_CLT_CTX(smar_clt_ctx);
ret = smar_access_chk(smar_ctx, smar_addr);
if (sm_is_err(ret)) {
sm_log_write(smar_ctx->smar_lctx,
AR_LCAT_RESOLVER, AR_LMOD_RESOLVER,
SM_LOG_ERROR, 1,
"sev=ERROR, func=smar_access_check, smar_access_chk=%m"
, ret);
goto error;
}
SMAR_LEV_DPRINTF(5, (SMAR_DEBFP, "sev=DBG, func=smar_access_check, map_res=%r, status=%r, ltype=%#x\n", smar_addr->ara_mapres, smar_addr->ara_status, smar_addr->ara_ltype));
if (SM_IS_FLAG(smar_addr->ara_ltype, SMARA_LT_MAIL_LOCAL) &&
(SM_ACC_NOTFOUND == smar_addr->ara_mapres ||
(SM_ACC_FOUND == smar_addr->ara_mapres &&
SMTP_R_CONT == smar_addr->ara_status))
)
{
ret = smar_addr_chk(smar_ctx, smar_addr);
SMAR_LEV_DPRINTF(3, (SMAR_DEBFP, "sev=DBG, func=smar_access_check, map_res=%r, ltype=%#x, smar_addr_chk=%r, status=%r\n", smar_addr->ara_mapres, smar_addr->ara_ltype, ret, smar_addr->ara_status));
if (sm_is_err(ret)) {
sm_log_write(smar_ctx->smar_lctx,
AR_LCAT_RESOLVER, AR_LMOD_RESOLVER,
SM_LOG_ERROR, 1,
"sev=ERROR, func=smar_access_check, smar_addr_chk=%m"
, ret);
goto error;
}
/* this is necessary to send back the result */
if (smar_addr->ara_status != SMTP_R_CONT) {
smar_addr->ara_mapres = SM_ACC_FOUND;
sm_str_clr(smar_addr->ara_rhs);
(void) sm_str_scat(smar_addr->ara_rhs,
"550 5.1.8 Bogus sender\r\n");
SMAR_SET_RFL(smar_addr, SMAR_R_QUICK);
}
}
ret = smar_access_re(smar_addr, smar_addr->ara_which_status,
smar_addr->ara_rhstagoff);
if (sm_is_err(ret)) {
sm_log_write(smar_ctx->smar_lctx,
AR_LCAT_RESOLVER, AR_LMOD_RESOLVER,
SM_LOG_ERROR, 1,
"sev=ERROR, func=smar_access_check, smar_access_re=%m"
, ret);
goto error;
}
ret = sm_rcbcom_endrep(&smar_clt_ctx->smac_com_ctx,
smar_clt_ctx->smac_com_ctx.rcbcom_tsk, false, &smar_addr->ara_rcbe);
if (sm_is_err(ret)) {
sm_log_write(smar_ctx->smar_lctx,
AR_LCAT_RESOLVER, AR_LMOD_RESOLVER,
SM_LOG_ERROR, 1,
"sev=ERROR, func=smar_access_check, sm_rcbcom_endrep=%m"
, ret);
goto error;
}
(void) smar_addr_free(smar_addr);
return ret;
error:
(void) smar_addr_free(smar_addr);
sm_log_write(smar_ctx->smar_lctx,
AR_LCAT_RESOLVER, AR_LMOD_RESOLVER,
SM_LOG_ERROR, 0,
"sev=ERROR, func=smar_access_check, ret=%m", ret);
return ret;
}
syntax highlighted by Code2HTML, v. 0.9.1