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