/*
 * Copyright (c) 2002-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.
 *
 *	$Id: smar.h,v 1.112 2007/11/14 06:03:09 ca Exp $
 */

#ifndef SMAR_H
#define SMAR_H 1

#include "sm/generic.h"
#include "sm/net.h"
#include "sm/evthr.h"
#include "sm/rpool.h"
#include "sm/rcb.h"
#include "sm/rcbl.h"
#include "sm/rcbcomm.h"
#include "sm/queue.h"
#include "sm/bhtable.h"
#include "sm/pthread.h"
#include "sm/mta.h"
#include "sm/map.h"
#include "sm/log.h"
#include "sm/smar.h"
#include "smarstr.h"
#include "sm/sm-conf.h"
#include "sm/smarcnf.h"
#include "sm/rfc2821.h"
#include "sm/greyctl.h"

/* access map required? */
#define SMAR_LT_ACCESS (SMARA_LT_CERT_RELAY|\
			SMARA_LT_CLT_A_ACC|SMARA_LT_CLT_N_ACC|\
			SMARA_LT_RVRS_N_ACC|\
			SMARA_LT_EHLO_ACC|\
			SMARA_LT_MAIL_ACC|\
			SMARA_LT_RCPT_ACC|SMARA_LT_RCPT_PROT)

#ifndef SMAR_USE_DNS
# define SMAR_USE_DNS	1
#endif

/*
**  Limits for (currently compile time only)
**	number of MX records per domain
**	number of A records per MX record
*/

#ifndef SM_DNS_MX_MAX
# define SM_DNS_MX_MAX	50
#endif
#ifndef SM_DNS_A_PER_MX_MAX
# define SM_DNS_A_PER_MX_MAX	50
#endif

#ifndef SMAR_TEST
# define SMAR_TEST	1
#endif

#ifndef SM_ALIASES_LARGE
# define SM_ALIASES_LARGE	1
#endif

#if SMAR_USE_DNS
# include "sm/dns.h"
# include "sm/dns-int.h"
# include "sm/dnstsk.h"
#endif /* SMAR_USE_DNS */

#if SM_ALIASES_LARGE
# define DNS_MGR_HTSIZE	4993u
# define DNS_MGR_HTMAX	29989u
#else /* SM_ALIASES_LARGE */
# define DNS_MGR_HTSIZE	0u
# define DNS_MGR_HTMAX	0u
#endif /* SM_ALIASES_LARGE */

#ifndef SMAR_DEBUG
# define SMAR_DEBUG	1
#endif

#if SMAR_DEBUG
# include "sm/io.h"
uint smar_debug;
# define SMAR_DEBFP	smioerr
# define SMAR_LEV_DPRINTF(lev, x)	do			\
		{						\
			if ((lev) < smar_debug)			\
			{					\
				sm_io_fprintf x;		\
				sm_io_flush(SMAR_DEBFP);	\
			}					\
		} while (0)
# define SMAR_DPRINTF(x)	sm_io_fprintf x
#else /* SMAR_DEBUG */
# define SMAR_DPRINTF(x)
# define SMAR_LEV_DPRINTF(lev, x)
#endif /* SMAR_DEBUG */

#if SM_HEAP_CHECK
extern SM_DEBUG_T SmHeapCheck;
# define HEAP_CHECK (SmHeapCheck > 0)
#else
# define HEAP_CHECK 0
#endif

#define SMAR_RHS_SEP_C	' '	/* seperate entries in the host list */
#define SMAR_RHS_PORT_C	'^'	/* separate port from protocol */
#define SMAR_RHS_PROT_C	':'	/* separate protocol from host list */

#define SMAR_DEFAULT_TTL	3600

#ifndef SMAR_MAXRHS
# define SMAR_MAXRHS	1024
#endif

#define RHS_ERROR	"error:"
#define RHS_ERROR_LEN	6
#define RHS_ERROR_4	"error:4"
#define RHS_ERROR_4_LEN	7
#define RHS_QUICK	"quick:"
#define RHS_QUICK_LEN	6
#define RHS_DELAY	"delay:"
#define RHS_DELAY_LEN	6
/* XXX use LEN(x) (sizeof(x) - 1) ??? */

/*
**  Maximum number of SMAR clients; this should be dynamic!
**  However, the value must not exceed the number of bits in smar_clt_used.
*/

#define SMAR_MAX_CLTS	32

/* Client context type for SMAR */
struct smar_clt_ctx_S
{
	sm_magic_T      sm_magic;
#if 0
	pthread_mutex_t smac_mutex;
#endif

	int             smac_status;   /* see below, SMAR_ST_* */
	/* info about connections? */
	uint32_t        smac_bit;      /* bit for smar_clt_used */
	uint32_t        smac_idx;      /* index (MUST be unsigned) */

	uint32_t        smac_cl_flags; /* client flags */

	smar_ctx_P      smac_ar_ctx;   /* pointer back to main ctx */
	rcbcom_ctx_T    smac_com_ctx;  /* communication context */
};

/* SMAC status */
#define SMAC_ST_NONE	0x00	/* not yet OK */
#define SMAC_ST_INIT	0x01	/* initialized */

/* Main context type for SMAR */
struct smar_ctx_S
{
	sm_magic_T	 sm_magic;
	pthread_mutex_t	 smar_mutex;
	smar_cnf_T	 smar_cnf;
	sm_str_P	 smar_hostname;
	uint		 smar_status;	/* see below, SMAR_ST_* */
	uint		 smar_flags;	/* see below, SMAR_FL_* */
	/* info about connections? */

	sm_evthr_ctx_P	 smar_ev_ctx;	/* event thread context */

#if SMAR_MAX_CLTS > 32
 ERROR: _SMAR_MAX_CLTS > 32
 value must not exceed number of bits in smar_clt_used
#endif
	uint32_t	 smar_clt_used;	/* bitmask for used elements */
	smar_clt_ctx_P	 smar_clt_ctx[SMAR_MAX_CLTS]; /* communication tasks */

	/* max number of concurrent requests from a client */
	uint32_t	 smar_clt_reqs[SMAR_MAX_CLTS];
	uint32_t	 smar_max_reqs;
	uint32_t	 smar_max_thrds_s;
	uint32_t	 smar_max_thrds_h;

	sm_log_ctx_P	 smar_lctx;
#if SMAR_USE_DNS
	uint		 smar_dns_ntsks;
	ipv4_T		 smar_nameserveripv4s[SM_DNS_MAX_TSKS];
	dns_mgr_ctx_P	 smar_dns_mgr_ctx;
#endif

	sm_map_P	 smar_mt_map;	/* "mailertable" (HACK) */
	sm_maps_P	 smar_maps;	/* map system context */
	sm_map_P	 smar_aliases;	/* alias map */
	sm_map_P	 smar_access;	/* access map */
	sm_map_P	 smar_lum;	/* map of local users */

	sm_cstr_P	 smar_strmaptype;

	uint		 smar_alias_lfl; /* alias lookup flags */
	uint		 smar_lum_lfl; /* local user map lookup flags */
	uint		 smar_mt_lfl; /* mailertable map lookup flags */

	greyctx_P	 smar_greyctx;

#if SMAR_TEST
	long		 smar_rand_err;
#endif
};

/* SMAR status */
#define SMAR_ST_NONE	0x00	/* not yet OK */
#define SMAR_ST_INIT	0x01	/* initialized */
#define SMAR_ST_CONF	0x02	/* configured */
#define SMAR_ST_START	0x04	/* started */

#define SMAR_ST_OK	0x10	/* initialized, running normal */

#define SMAR_ST_SLOW	0x20	/* slow down */

/* Notice: this must be ordered, see smar_is_stop() below */
#define SMAR_ST_SH_DOWN	0x80	/* shutting down */
#define SMAR_ST_STOPPED	0x81	/* stopped: almost terminated */

#define smar_is_stop(smar_ctx)	((smar_ctx)->smar_status >= SMAR_ST_SH_DOWN)

/* SMAR flags */
#define SMAR_FL_NONE		0x0000
#define SMAR_FL_HASACCESS	0x0001
#define SMAR_FL_HASDNSBL	0x0002
#define SMAR_FL_HASGREY		0x0004
#define SMAR_FL_HASALIAS	0x0008
#define SMAR_FL_ACCESSCOMPL	0x0010	/* complained about access */
#define SMAR_FL_DNSBLCOMPL	0x0020	/* complained about dnsbl */
#define SMAR_FL_GREYCOMPL	0x0040	/* complained about greylisting */
#define SMAR_FL_ALIASCOMPL	0x0080	/* complained about aliases */
#define SMAR_FL_REQACCESS	0x0100	/* requires access map */
#define SMAR_FL_REQALIAS	0x0800	/* requires aliases map */
/* complained about protected_rcpts misconfiguration for client_ip */
#define SMAR_FL_PROTIPCOMPL	0x1000
/* complained about protected_rcpts misconfiguration for sender */
#define SMAR_FL_PROTMAILCOMPL	0x2000

#define SMAR_SET_FLAG(smar_ctx, fl) (smar_ctx)->smar_flags |= (fl)
#define SMAR_CLR_FLAG(smar_ctx, fl) (smar_ctx)->smar_flags &= ~(fl)
#define SMAR_IS_FLAG(smar_ctx, fl) (((smar_ctx)->smar_flags & (fl)) != 0)


struct smar_dns_S
{
	uint            ardns_ttl;     /* TTL from DNS */
	ushort          ardns_pref;    /* preference from DNS */
	sm_cstr_P       ardns_name;    /* name from DNS (RHS of MX RR) */
	int             ardns_n_A;     /* number of A records */

	/* XXX missing TTL for individual A records */

	/* pointer to list of A records [0 to ardns_n_A-1] */
	ipv4_T         *ardns_A_rrs;
};

struct smar_rcpt_S
{
	sm_magic_T	 sm_magic;
	sm_str_P	 arr_pa;	/* printable addr */
	rcpt_id_T	 arr_id;	/* rcpt id */
	rcpt_idx_T	 arr_idx;	/* (local) rcpt idx */
	sm_str_P	 arr_owner_pa;	/* owner: printable addr */
	rcpt_idx_T	 arr_owner_idx;	/* (local) owner idx */
	short		 arr_port; /* port for all addresses, 0: use default */
	sm_str_P	 arr_domain_pa;
	sm_str_P	 arr_user_pa;	/* user without +detail */
	sm_str_P	 arr_detail_pa;	/* detail */
	uchar		 arr_delim;	/* used delimiter */
	uint32_t	 arr_flags;	/* status of address resolving */
	sm_ret_T	 arr_ret;	/* result of address resolving */
	uint		 arr_timeout;	/* timeout */
	uint32_t	 arr_rqflags;	/* request flags */
	uint32_t	 arr_da;	/* DA */

	/*
	**  Number of A queries sent (= Number of MX records).
	**  For each MX record one query for the A records is sent.
	*/

	int		 arr_A_qsent;

	/* Number of responses received for A queries */
	int		 arr_A_rrcvd;

	/*
	**  Total number of A records (each A record query can return multiple
	**  A records).
	*/

	int		 arr_n_A;

	/*
	**  array of results [0 to arr_A_qsent-1]; the array can be bigger,
	**  but only arr_A_qsent entries are used.
	*/

	smar_dns_T	*arr_res;	/* array of results */
	ipv4_T		 arr_ipv4;	/* single A record */

	uint32_t	 arr_cnf_lfl;
	sm_ret_T	 arr_maprescnf;
	sm_str_P	 arr_rhs_conf;

	smar_rcpts_P	 arr_rcpts;	/* pointer back to recipient list */
	TAILQ_ENTRY(smar_rcpt_S)	 arr_next;
};

/* SMAR RCPT flags (A4: asked for) */
#define SMARR_FL_NONE	0x00000000 /* not yet OK */
#define SMARR_FL_INIT	0x00000001 /* initialized */
#define SMARR_FL_A4MT	0x00000002 /* check in mailertable */
/* XXX this needs more states if RHS in mailertable can be a hostname... */
#define SMARR_FL_GOTMT	0x00000004 /* got mailertable IP */
#define SMARR_FL_NOMT	0x00000008 /* no mailertable entry */
#define SMARR_FL_A4MX	0x00000010 /* asked for MX records */
#define SMARR_FL_GOTMX	0x00000020 /* got MX records */
#define SMARR_FL_A4A	0x00000040 /* asked for A records */
#define SMARR_FL_GOTA	0x00000080 /* got A records */
#define SMARR_FL_TEMP	0x00000100 /* temporary error */
#define SMARR_FL_EXPD	0x00000200 /* alias expansion performed */
#define SMARR_FL_ALIAS	0x00000400 /* 1-1 alias expansion performed */
#define SMARR_FL_LU	0x00000800 /* is local user */
#define SMARR_FL_FREEIT	0x00001000 /* something failed, free smar_rcpt */
#define SMARR_FL_INRLST	0x00002000 /* smar_rcpt is in smar_rcpts rcpt list */
#define SMARR_FL_INHT	0x00004000 /* smar_rcpt is in smar_rcpts hash table */
#define SMARR_FL_ORCPT	0x00008000 /* smar_rcpt is smar_rcpts->arrs_rcpt */

/* smar_rcpt_expand() to take responsibility (add to list or free it) */
#define SMARR_FL_TAKEIT	0x00010000
#define SMARR_FL_ISALIAS 0x00020000 /* found in alias map */

#define SMARR_FL_C_MX	0x00100000	/* got a CNAME record for MX */
#define SMARR_FL_C_A	0x00200000	/* got a CNAME record for A */
/* XXX maybe use counters instead of flags? */
#define SMARR_FL_C_MX_L 0x00400000	/* CNAME loop for MX */
#define SMARR_FL_C_A_L	0x00800000	/* CNAME loop for A */
#define SMARR_FL_ISOWN	0x01000000 /* address is owner- alias */
#define SMARR_FL_INOWNLST 0x02000000 /* smar_rcpt is in smar_rcpts owner list */

#define SMARR_FL_ISVERP	0x04000000 /* address is verp- alias */
#define SMARR_FL_HASVERP 0x08000000 /* address has verp- alias */
#define SMARR_FL_HASDET 0x10000000 /* address has +detail */

#define SMARR_SET_FLAG(smar_rcpt, fl) (smar_rcpt)->arr_flags |= (fl)
#define SMARR_CLR_FLAG(smar_rcpt, fl) (smar_rcpt)->arr_flags &= ~(fl)
#define SMARR_IS_FLAG(smar_rcpt, fl) (((smar_rcpt)->arr_flags & (fl)) != 0)

#define SMARRQ_SET_FLAG(smar_rcpt, fl) (smar_rcpt)->arr_rqflags |= (fl)
#define SMARRQ_CLR_FLAG(smar_rcpt, fl) (smar_rcpt)->arr_rqflags &= ~(fl)
#define SMARRQ_IS_FLAG(smar_rcpt, fl) (((smar_rcpt)->arr_rqflags & (fl)) != 0)

/* return codes from SMAR modules that decode/handle requests */
#define SMAR_R_WAITQ	SM_SUCCESS	/* default: put back in waitq */
#define SMAR_R_ASYNC	1	/* do nothing, task has been put in waitq */

typedef TAILQ_HEAD(smar_rcptshd_S, smar_rcpt_S)	smar_rcptshd_T, *smar_rcptshd_P;

/* callback function to return result */
typedef sm_ret_T (smar_rcpts_cb_F)(smar_rcpts_P, void *);


struct smar_rcpts_S
{
	sm_magic_T	 sm_magic;

#if 0
	/*
	**  It seems this mutex isn't needed because the callback function
	**  (which can be called asynchronously) is protected using
	**  smar_ctx->smar_mutex
	*/

	pthread_mutex_t	 arrs_mutex;
#endif /* 0 */
	smar_rcpt_P	 arrs_rcpt;	/* original recipient */
	rcpt_id_T	 arrs_rcpt_id;	/* original rcpt id */
	uint32_t	 arrs_flags;	/* status of address resolving */
	sm_ret_T	 arrs_ret;	/* result of address resolving */
	rcpt_idx_T	 arrs_idx;	/* running counter for recipients */

	bht_P		 arrs_rcpts;	/* hash table of recipients */
	smar_rcptshd_T	 arrs_rcpthd;	/* list of recipients */
	smar_rcptshd_T	 arrs_ownerhd;	/* list of owner addresses */
	uint		 arrs_ht_n;	/* number of entries (in hash table) */
	uint		 arrs_lst_n;	/* number of entries (in list) */
	rcpt_idx_T	 arrs_owners_n;	/* number of owner aliases */
	uint		 arrs_resolved; /* resolved entries */

	sm_rcbe_P	 arrs_rcbe;	/* RCB to write back result */
	smar_ctx_P	 arrs_smar_ctx;	/* pointer back to SMAR context */
	smar_clt_ctx_P	 arrs_smar_clt_ctx; /* pointer back to client context */

	sm_str_P	 arrs_pa;	/* address to check against elements */
	smar_addr_P	 arrs_addr;

	smar_rcpts_cb_F	*arrs_cbf; /* callback fct to invoke to return result */
	void		*arrs_cb_ctx;	/* context for callback function */
};

#define SMARRS_FL_NONE	0x0000	/* not yet OK */
#define SMARRS_FL_ERCB	0x0001	/* writing to RCB failed, return an error */
#define SMARRS_FL_TEMP	0x0002	/* temporary error (ENOMEM), return error */
#define SMARRS_FL_FAIL	0x0004	/* last attempt to write result failed */
#define SMARRS_FL_INITRE	0x0008	/* initialized reply (RCB) */
#define SMARRS_FL_NOSEND	0x0010	/* do not send data */
#define SMARRS_FL_NOFREE	0x0020	/* do not free context */

#define SMARRS_FL_NOREC	0x0100	/* no recursion */
#define SMARRS_FL_NOADD	0x0200	/* do not add elements to hash table */
#define SMARRS_FL_COMP	0x0400	/* compare elements against address arrs_addr */

#define SMARRS_FL_FOUND	0x1000	/* found arrs_pa */

#define SMARRS_SET_FLAG(smar_rcpts, fl) (smar_rcpts)->arrs_flags |= (fl)
#define SMARRS_CLR_FLAG(smar_rcpts, fl) (smar_rcpts)->arrs_flags &= ~(fl)
#define SMARRS_IS_FLAG(smar_rcpts, fl) (((smar_rcpts)->arrs_flags & (fl)) != 0)

/* prototypes */
sm_ret_T smar_rcpts_new(smar_ctx_P _smar_ctx, smar_clt_ctx_P _smar_clt_ctx, smar_rcpts_P *_psmar_rcpts);
sm_ret_T smar_rcpts_free(smar_rcpts_P _smar_rcpts);

sm_ret_T smar_rcpt_expand(smar_rcpts_P _smar_rcpts, smar_rcpt_P _smar_rcpt, rcpt_idx_T _owner_idx, uint _flags, uint _level);
sm_ret_T smar_rcpts_t2l(smar_rcpts_P _smar_rcpts);

#define SM_IS_SMAR_RCPTS(smar_rcpts) SM_REQUIRE_ISA((smar_rcpts), SM_SMAR_RCPTS_MAGIC)

#define RCPTS_INIT(smar_rcpts)	TAILQ_INIT(&((smar_rcpts)->arrs_rcpthd))
#define RCPTS_FIRST(smar_rcpts)	TAILQ_FIRST(&((smar_rcpts)->arrs_rcpthd))
#define RCPTS_END(smar_rcpts)	TAILQ_END(&((smar_rcpts)->arrs_rcpthd))
#define RCPTS_EMPTY(smar_rcpts)	TAILQ_EMPTY(&((smar_rcpts)->arrs_rcpthd))
#define RCPTS_NEXT(smar_rcpt)	TAILQ_NEXT(smar_rcpt, arr_next)
#define RCPTS_APP(smar_rcpts, smar_rcpt)	TAILQ_INSERT_TAIL(&((smar_rcpts)->arrs_rcpthd), smar_rcpt, arr_next)
#define RCPTS_REMOVE(smar_rcpts, smar_rcpt)	TAILQ_REMOVE(&((smar_rcpts)->arrs_rcpthd), smar_rcpt, arr_next)

#define OWNER_INIT(smar_rcpts)	TAILQ_INIT(&((smar_rcpts)->arrs_ownerhd))
#define OWNER_FIRST(smar_rcpts)	TAILQ_FIRST(&((smar_rcpts)->arrs_ownerhd))
#define OWNER_END(smar_rcpts)	TAILQ_END(&((smar_rcpts)->arrs_ownerhd))
#define OWNER_EMPTY(smar_rcpts)	TAILQ_EMPTY(&((smar_rcpts)->arrs_ownerhd))
#define OWNER_NEXT(smar_rcpt)	TAILQ_NEXT(smar_rcpt, arr_next)
#define OWNER_APP(smar_rcpts, smar_rcpt)	TAILQ_INSERT_TAIL(&((smar_rcpts)->arrs_ownerhd), smar_rcpt, arr_next)
#define OWNER_REMOVE(smar_rcpts, smar_rcpt)	TAILQ_REMOVE(&((smar_rcpts)->arrs_ownerhd), smar_rcpt, arr_next)

/* DNS BL result */
struct smar_dnsblres_S
{
	ipv4_T		 sdbr_ipv4;	/* IPv4 address returned */
	sm_ret_T	 sbdr_res;	/* result of lookup */
	ipv4_T		*sdbr_ipv4s;	/* IPv4 addresses returned */
	uint		 sdbr_n;	/* size of array */
};

/* result values (+ usual error code) */
#define SM_DNSBL_RES_INIT	0	/* initialized */
#define SM_DNSBL_RES_WAIT	1	/* waiting for result */
#define SM_DNSBL_RES_OK		2	/* result is OK (found A record) */

/*
**  Recipient/Sender address context for map lookups or other checks.
**
**  Should this allow for multiple tests per invocation?
**  If yes, it would be necessary to send back multiple results.
**  For now, it can be multiple tests, but there is only one result, i.e.,
**  some order is fixed in the algorithm.
*/

struct smar_addr_S
{
	sm_magic_T	 sm_magic;
	sessta_id_T	 ara_taid;	/* transaction id */
	int		 ara_id;	/* SMTPS id */
	smar_clt_ctx_P	 ara_smar_clt_ctx; /* pointer back to client context */
	sm_str_P	 ara_pa;	/* printable addr */
	sm_str_P	 ara_pa2;	/* another printable addr */
	sm_str_P	 ara_rhs;	/* rewritten address/result of lookup */
	sm_str_P	 ara_str;	/* various purposes */
	sm_str_P	 ara_tag;	/* tag */
	sm_str_P	 ara_detail;
	sm_str_P	 ara_domain_pa;	/* domain part */
	uchar		 ara_delim;	/* used delimiter */
	rcpt_idx_T	 ara_rcpt_idx;	/* only valid for recipients */

	ipv4_T		 ara_ipv4;	/* IPv4 address */
	sm_str_P	 ara_ipv4_str;	/* IPv4 address in ASCII */
	sm_map_P	 ara_str_map;	/* str map */

	time_t		 ara_conn_time;	/* connection time (greylisting) */

	/*
	**  Parts of ara_pa2 (if necessary).
	**  Note: ara_rhs2 is used for protected_rcpts
	**  and in smar_rcpt_expand()
	*/

	sm_str_P	 ara_user2;
	sm_str_P	 ara_detail2;
	sm_str_P	 ara_domain_pa2;
	sm_str_P	 ara_rhs2;
	uchar		 ara_delim2;	/* used delimiter */

	/*
	**  Note: it might be useful to have a "cache" of these instead of
	**  of opening and closing them all the time.
	*/

	sm_a2821_T	 ara_a_rcpt;

	/*
	**  What is the relation between lookup flags and lookup types?
	**  E.g., RCPT_LOCAL and RCPT_ACC: should that really be two
	**  lookup types? Why not RCPT with lookup flags: LOCAL, ACC?
	**  Should it be:
	**  type: This is an MAIL/RCPT address, a hostname, an IP address
	**  flags: perform the following tests:
	**	access map (tag determined by type)
	**		with "subflags": lookup exactly, lookup subdomains, ...
	**	check whether RCPT is local
	**	check whether MAIL is deliverable
	**  Note: the last two apply to exactly one type, so it doesn't
	**	seem to make much sense to have them as generic flags.
	*/

	uint32_t	 ara_lflags;	/* lookup flags */
	uint32_t	 ara_ltype;	/* lookup type */
	uint32_t	 ara_flags;	/* various flags */

	/* return value of map lookup function */
	sm_ret_T	 ara_mapres;

	/* status of lookup, e.g., (interpreted) RHS if lookup succeeded */
	uint32_t	 ara_status;
	uint32_t	 ara_rflags;	/* result flags */
	/* these values will be combined into one return value */

	/* record type for status return, i.e., what does ara_status refer to */
	uint32_t	 ara_which_status;
	uint		 ara_rhstagoff;	/* offset in RHS to return text */

	/* 2nd return value of map lookup function, result in ara_rhs2 */
	sm_ret_T	 ara_mapres2;

	int		 ara_c_MX;
	int		 ara_c_A;
	int		 ara_c_dnsbl;

	smar_rcpt_P	 ara_rcpt;
	smar_rcpts_P	 ara_rcpts;

	/* result of dns (reverse/recipient) lookup */
	sm_ret_T	 ara_dns_ret;
	sm_cstr_P	 ara_rvrs_hostname; /* name from DNS (reverse lookup) */
	int		 ara_res_cnt;	/* result counter */
	smar_rvrs_P	 ara_rvrs;

	smar_dnsblres_T	 ara_dnsblres[SM_MAX_DNSBL];

	/*
	**  ara_mutex protects access to ara_dns_ret, ara_rvrs_hostname,
	**  ara_dnsblres, and ara_res_cnt. Currently it does not protect
	**  ara_flags even though some are modified in the callback/notify
	**  functions (see algorithm descriptions; it might be better to
	**  use "inline" signaling via the result variables because those
	**  are protected by the mutex).
	**  ara_cond is used to signal that those variables have valid content.
	*/

	pthread_mutex_t	 ara_mutex;
	pthread_cond_t	 ara_cond;

	sm_rcbe_P	 ara_rcbe;	/* RCB to write back result */
	sm_rpool_P	 ara_rpool;	/* for this context? */

	sm_ret_T	 ara_maprescnf;
	sm_str_P	 ara_rhs_conf;
};

#define SM_IS_SMAR_ADDR(smar_addr) SM_REQUIRE_ISA((smar_addr), SM_SMAR_ADDR_MAGIC)

/*
**  values for ara_dns_ret to signal between caller and callee
**  Algorithm:
**  Caller:
**	status = init;			* initialize status *
**	...				* do something *
**	lock				* acquire mutex for v etc *
**	if (status == init) {		* is it still the initial value? *
**		status = wait;		* indicate that caller is waiting *
**		while (status == wait)
**			cond_wait
**	}
**	unlock
**
**  Callee:
**	lock				* acquire mutex *
**	notify = (status == wait);	* is caller waiting? *
**	v = v_new			* set new value *
**	status = sent;			* result is available *
**	if (notify)
**		cond_signal		* notify caller if it is waiting *
**	unlock				* done *
**
**  Explanation: see docs
*/

/* SMAR addr flags */
#define SMARA_FL_NONE		0x00000000
#define SMARA_FL_HASMUT		0x00000001	/* mutex is initialized */
#define SMARA_FL_HASCOND	0x00000002	/* condition is initialized */
#define SMARA_FL_A4RVRS		0x00000004	/* asked for reverse lookup */
#define SMARA_FL_A4RCPT		0x00000008	/* asked for recipient lookup */
#define SMARA_FL_A4DNSBL	0x00000010	/* asked for DNS BL lookup */
#define SMARA_FL_GOTRVRS	0x00000020	/* got reverse DNS lookup result */
#define SMARA_FL_GOTDNSBL	0x00000040	/* got DNS BL result */
#define SMARA_FL_FAILDNS	0x00000080	/* failed to get DNS result */
#define SMARA_FL_GOTRCPT	0x00000100	/* got recipient DNS lookup result */
#define SMARA_FL_2ND		0x00000200	/* second lookup result is valid */
#define SMARA_FL_STOP		0x00000400	/* don't perform more lookups */
#define SMARA_FL_DIDLOOKUP	0x00000800	/* did lookup: check result */
#define SMARA_FL_RFC2821	0x00001000	/* RFC 2821 address */
#define SMARA_FL_GOTCERTISS	0x00002000	/* checked cert issuer */
#define SMARA_FL_GOTCERTSUB	0x00004000	/* checked cert subject */
#define SMARA_FL_GOTRCPTPROT	0x00008000	/* checked protected rcpt */
#define SMARA_FL_RP_ANALYSE	0x00010000
/* need to "analyse" protected rcpt lookup result */
#define SMARA_FL_INIT		0x00020000	/* all data initialized */

#define SMARA_FL_GOTGREY	0x00040000	/* greylisting check done */
#define SMARA_FL_GOTRVRSA	0x00080000	/* got reverse lookup result */
#define SMARA_FL_GOTRVRSN	0x00100000	/* resolved hostname checked */
#define SMARA_FL_RCVDIPV4	0x00200000	/* ara_ipv4 is valid */
#define SMARA_FL_PA2EMPTY	0x00400000	/* ara_pa2 is <> */
#define SMARA_FL_GOT2821ACC	0x00800000 /*did RFC2821 address access map lookup*/
#define SMARA_FL_GOTIPV4ACC	0x01000000	/* did IPv4 access map lookup */

/* flags for algorithm described above (AF: Asynchronous Function) */
#define SMARA_FL_AFWAIT		0x02000000	/* status==wait */
#define SMARA_FL_GOTSSSECONF 0x08000000	/* got smtps session conf */

#define SMARA_SET_FLAG(smar_addr, fl) (smar_addr)->ara_flags |= (fl)
#define SMARA_CLR_FLAG(smar_addr, fl) (smar_addr)->ara_flags &= ~(fl)
#define SMARA_IS_FLAG(smar_addr, fl) (((smar_addr)->ara_flags & (fl)) != 0)

#define SMARA_SET_LFLAG(smar_addr, fl) (smar_addr)->ara_lflags |= (fl)
#define SMARA_CLR_LFLAG(smar_addr, fl) (smar_addr)->ara_lflags &= ~(fl)
#define SMARA_IS_LFLAG(smar_addr, fl) (((smar_addr)->ara_lflags & (fl)) != 0)

/* see sm/smar.h: SMAR_R_* */
#define SMAR_SET_RFL(smar_addr, flags)	(smar_addr)->ara_rflags |= (flags)
#define SMAR_CLR_RFL(smar_addr, flags)	(smar_addr)->ara_rflags &= ~(flags)
#define SMAR_IS_RFL(smar_addr, flags)	(((smar_addr)->ara_rflags & (flags)) != 0)

/* assertions */
#define SM_IS_SMAR_CTX(smar_ctx) SM_REQUIRE_ISA((smar_ctx), SM_SMAR_CTX_MAGIC)
#define SM_IS_SMAR_CLT_CTX(smac_ctx) SM_REQUIRE_ISA((smac_ctx), SM_SMAC_CTX_MAGIC)

#define SM_IS_SMAR_RCPT(smar_rcpt) SM_REQUIRE_ISA((smar_rcpt), SM_SMAR_RCPT_MAGIC)

/* prototypes */
sm_ret_T smar_init0(smar_ctx_P _smar_ctx);
sm_ret_T smar_init1(smar_ctx_P _smar_ctx);
sm_ret_T smar_init_map_lfl(smar_ctx_P _smar_ctx, uint _lfl_conf, uint *_plflags);
sm_ret_T smar_rdcf(smar_ctx_P _smar_ctx, int _argc, char *_argv[]);
sm_ret_T smar_read_cnf(smar_ctx_P _smar_ctx, const char *_fn, sm_conf_T **_psmc);
sm_ret_T smar_mt_init(smar_ctx_P _smar_ctx);
sm_ret_T smar_start(smar_ctx_P _smar_ctx);
sm_ret_T smar_stop(smar_ctx_P _smar_ctx);

sm_ret_T smar_clt_new(smar_ctx_P _smar_ctx, smar_clt_ctx_P *_psmar_clt_ctx);
sm_ret_T smar_clt_free(smar_clt_ctx_P _smar_clt_ctx);
sm_ret_T smar_li(sm_evthr_task_P _tsk);

sm_ret_T smar_clt(sm_evthr_task_P _tsk);
sm_ret_T smar_rcpt_rslv(smar_ctx_P _smar_ctx, smar_rcpts_P _smar_rcpts);
sm_ret_T smar_rcpt_new(smar_rcpt_P *_psmar_rcpt);
sm_ret_T smar_rcpt_free(smar_rcpt_P _smar_rcpt, smar_rcpts_P _smar_rcpts);

sm_ret_T smar_addr_new(smar_clt_ctx_P _smar_clt_ctx, smar_addr_P *_psmar_addr);
sm_ret_T smar_addr_free(smar_addr_P _smar_addr);
sm_ret_T smar_addr_check(smar_ctx_P _smar_ctx, smar_addr_P _smar_addr);
sm_ret_T smar_addr_chk(smar_ctx_P _smar_ctx, smar_addr_P _smar_addr);
sm_ret_T smar_addr_lu(smar_ctx_P _smar_ctx, sm_str_P _user, uchar _delim, sm_str_P _detail, sm_str_P _domain, sm_str_P _rhs, uint32_t *_pstatus);

sm_ret_T smar_addr_re(smar_addr_P _smar_addr, uint _offset);

#define SM_CONST_LEN(str) (sizeof(str) - 1)
#define CLT_A_TAG		"cltaddr:"
#define CLT_A_TAG_LEN	SM_CONST_LEN(CLT_A_TAG)
#define CLT_N_TAG		"cltname:"
#define CLT_N_TAG_LEN	SM_CONST_LEN(CLT_N_TAG)
#define MAIL_TAG		"from:"
#define MAIL_TAG_LEN	SM_CONST_LEN(MAIL_TAG)
#define RCPT_TAG		"to:"
#define RCPT_TAG_LEN	SM_CONST_LEN(RCPT_TAG)
#define CERT_ISSUER_TAG		"certissuer:"
#define CERT_ISSUER_TAG_LEN	SM_CONST_LEN(CERT_ISSUER_TAG)
#define CERT_SUBJECT_TAG	"certsubject:"
#define CERT_SUBJECT_TAG_LEN	SM_CONST_LEN(CERT_SUBJECT_TAG)
#define PROT_RCPT_TAG		"protectedrcpt:"
#define PROT_RCPT_TAG_LEN	SM_CONST_LEN(PROT_RCPT_TAG)
#define RHS_LIST_TAG		"list:"
#define RHS_LIST_TAG_LEN	SM_CONST_LEN(RHS_LIST_TAG)
#define EHLO_TAG		"ehlo:"
#define SS_SE_CONF_A_TAG	"smtps_session_conf:"
#define SC_SE_CONF_TAG		"smtpc_session_conf:"
#define SC_RCPT_CONF_TAG	"smtpc_rcpt_conf:"

sm_ret_T smar_map2acc(sm_ret_T _rv);
sm_ret_T smar_access_re(smar_addr_P _smar_addr, uint32_t _which_status, uint _off);

sm_ret_T smar_access_check(smar_ctx_P _smar_ctx, smar_addr_P _smar_addr);
sm_ret_T smar_access_chk(smar_ctx_P _smar_ctx, smar_addr_P _smar_addr);

sm_ret_T smar_prot_chk(smar_ctx_P _smar_ctx, smar_addr_P _smar_addr);
sm_ret_T smar_prot_rhs(smar_ctx_P _smar_ctx, smar_addr_P _smar_addr);

/* should mailertable use the "full" address for lookups? */
#define SMAR_MT_FULL_LOOKUP(smar_ctx) (SMMAP_LFL_MT != (smar_ctx)->smar_mt_lfl)

#endif /* SMAR_H */


syntax highlighted by Code2HTML, v. 0.9.1