/*++
/* NAME
/*	postscreen 3h
/* SUMMARY
/*	postscreen internal interfaces
/* SYNOPSIS
/*	#include <postscreen.h>
/* DESCRIPTION
/* .nf

 /*
  * System library.
  */

 /*
  * Utility library.
  */
#include <dict_cache.h>
#include <vstream.h>
#include <vstring.h>
#include <events.h>
#include <htable.h>
#include <myaddrinfo.h>

 /*
  * Global library.
  */
#include <addr_match_list.h>
#include <string_list.h>
#include <maps.h>
#include <server_acl.h>

 /*
  * Preliminary stuff, to be fixed.
  */
#define PSC_READ_BUF_SIZE	1024

 /*
  * Numeric indices and symbolic names for tests whose time stamps and status
  * flags can be accessed by numeric index.
  */
#define PSC_TINDX_PREGR	0		/* pregreet */
#define PSC_TINDX_DNSBL	1		/* dnsbl */
#define PSC_TINDX_PIPEL	2		/* pipelining */
#define PSC_TINDX_NSMTP	3		/* non-smtp command */
#define PSC_TINDX_BARLF	4		/* bare newline */
#define PSC_TINDX_COUNT	5		/* number of tests */

#define PSC_TNAME_PREGR	"pregreet"
#define PSC_TNAME_DNSBL	"dnsbl"
#define PSC_TNAME_PIPEL	"pipelining"
#define PSC_TNAME_NSMTP	"non-smtp command"
#define PSC_TNAME_BARLF	"bare newline"

#define PSC_TINDX_BYTNAME(tname) (PSC_TINDX_ ## tname)

 /*
  * Per-client shared state.
  */
typedef struct {
    int     concurrency;		/* per-client */
    int     pass_new_count;		/* per-client */
    time_t  expire_time[PSC_TINDX_COUNT];	/* per-test expiration */
} PSC_CLIENT_INFO;

 /*
  * Per-session state.
  */
typedef struct {
    int     flags;			/* see below */
    /* Socket state. */
    VSTREAM *smtp_client_stream;	/* remote SMTP client */
    int     smtp_server_fd;		/* real SMTP server */
    char   *smtp_client_addr;		/* client address */
    char   *smtp_client_port;		/* client port */
    char   *smtp_server_addr;		/* server address */
    char   *smtp_server_port;		/* server port */
    const char *final_reply;		/* cause for hanging up */
    VSTRING *send_buf;			/* pending output */
    /* Test context. */
    struct timeval start_time;		/* start of current test */
    const char *test_name;		/* name of current test */
    PSC_CLIENT_INFO *client_info;	/* shared client state */
    VSTRING *dnsbl_reply;		/* dnsbl reject text */
    int     dnsbl_score;		/* saved DNSBL score */
    int     dnsbl_ttl;			/* saved DNSBL TTL */
    const char *dnsbl_name;		/* DNSBL name with largest weight */
    int     dnsbl_index;		/* dnsbl request index */
    const char *rcpt_reply;		/* how to reject recipients */
    int     command_count;		/* error + junk command count */
    const char *protocol;		/* SMTP or ESMTP */
    char   *helo_name;			/* SMTP helo/ehlo */
    char   *sender;			/* MAIL FROM */
    VSTRING *cmd_buffer;		/* command read buffer */
    int     read_state;			/* command read state machine */
    /* smtpd(8) compatibility */
    int     ehlo_discard_mask;		/* EHLO filter */
    VSTRING *expand_buf;		/* macro expansion */
    const char *where;			/* SMTP protocol state */
} PSC_STATE;

 /*
  * Special expiration time values.
  */
#define PSC_TIME_STAMP_NEW		(0)	/* test was never passed */
#define PSC_TIME_STAMP_DISABLED		(1)	/* never passed but disabled */
#define PSC_TIME_STAMP_INVALID		(-1)	/* must not be cached */

 /*
  * Status flags.
  */
#define PSC_STATE_FLAG_NOFORWARD	(1<<0)	/* don't forward this session */
#define PSC_STATE_FLAG_USING_TLS	(1<<1)	/* using the TLS proxy */
#define PSC_STATE_FLAG_UNUSED2		(1<<2)	/* use me! */
#define PSC_STATE_FLAG_NEW		(1<<3)	/* some test was never passed */
#define PSC_STATE_FLAG_DNLIST_FAIL	(1<<4)	/* denylisted */
#define PSC_STATE_FLAG_HANGUP		(1<<5)	/* NOT a test failure */
#define PSC_STATE_FLAG_SMTPD_X21	(1<<6)	/* hang up after command */
#define PSC_STATE_FLAG_ALLIST_FAIL	(1<<7)	/* do not allowlist */
#define PSC_STATE_FLAG_TEST_BASE	(8)	/* start of indexable flags */

 /*
  * Tests whose flags and expiration time can be accessed by numerical index.
  * 
  * Important: every MUMBLE_TODO flag must have a MUMBLE_PASS flag, such that
  * MUMBLE_PASS == PSC_STATE_FLAGS_TODO_TO_PASS(MUMBLE_TODO).
  * 
  * MUMBLE_TODO flags must not be cleared once raised. The _TODO_TO_PASS and
  * _TODO_TO_DONE macros depend on this to decide that a group of tests is
  * passed or completed.
  * 
  * MUMBLE_DONE flags are used for "early" tests that have final results.
  * 
  * MUMBLE_SKIP flags are used for "deep" tests where the client messed up.
  * These flags look like MUMBLE_DONE but they are different. Deep tests can
  * tentatively pass, but can still fail later in a session. The "ignore"
  * action introduces an additional complication. MUMBLE_PASS indicates
  * either that a deep test passed tentatively, or that the test failed but
  * the result was ignored. MUMBLE_FAIL, on the other hand, is always final.
  * We use MUMBLE_SKIP to indicate that a decision was either "fail" or
  * forced "pass".
  * 
  * The difference between DONE and SKIP is in the beholder's eye. These flags
  * share the same bit.
  */
#define PSC_STATE_FLAGS_TODO_TO_PASS(todo_flags) ((todo_flags) >> 1)
#define PSC_STATE_FLAGS_TODO_TO_DONE(todo_flags) ((todo_flags) << 1)

#define PSC_STATE_FLAG_SHIFT_FAIL	(0)	/* failed test */
#define PSC_STATE_FLAG_SHIFT_PASS	(1)	/* passed test */
#define PSC_STATE_FLAG_SHIFT_TODO	(2)	/* expired test */
#define PSC_STATE_FLAG_SHIFT_DONE	(3)	/* decision is final */
#define PSC_STATE_FLAG_SHIFT_SKIP	(3)	/* action is already logged */
#define PSC_STATE_FLAG_SHIFT_STRIDE	(4)	/* nr of flags per test */

#define PSC_STATE_FLAG_SHIFT_BYFNAME(fname) (PSC_STATE_FLAG_SHIFT_ ## fname)

 /*
  * Indexable per-test flags. These are used for DNS allowlisting multiple
  * tests, without needing per-test ad-hoc code.
  */
#define PSC_STATE_FLAG_BYTINDX_FNAME(tindx, fname) \
	(1U << (PSC_STATE_FLAG_TEST_BASE \
	    + PSC_STATE_FLAG_SHIFT_STRIDE * (tindx) \
	    + PSC_STATE_FLAG_SHIFT_BYFNAME(fname)))

#define PSC_STATE_FLAG_BYTINDX_FAIL(tindx) \
	PSC_STATE_FLAG_BYTINDX_FNAME((tindx), FAIL)
#define PSC_STATE_FLAG_BYTINDX_PASS(tindx) \
	PSC_STATE_FLAG_BYTINDX_FNAME((tindx), PASS)
#define PSC_STATE_FLAG_BYTINDX_TODO(tindx) \
	PSC_STATE_FLAG_BYTINDX_FNAME((tindx), TODO)
#define PSC_STATE_FLAG_BYTINDX_DONE(tindx) \
	PSC_STATE_FLAG_BYTINDX_FNAME((tindx), DONE)
#define PSC_STATE_FLAG_BYTINDX_SKIP(tindx) \
	PSC_STATE_FLAG_BYTINDX_FNAME((tindx), SKIP)

 /*
  * Flags with distinct names. These are used in the per-test ad-hoc code.
  */
#define PSC_STATE_FLAG_BYTNAME_FNAME(tname, fname) \
	(1U << (PSC_STATE_FLAG_TEST_BASE \
	    + PSC_STATE_FLAG_SHIFT_STRIDE * PSC_TINDX_BYTNAME(tname) \
	    + PSC_STATE_FLAG_SHIFT_BYFNAME(fname)))

#define PSC_STATE_FLAG_PREGR_FAIL PSC_STATE_FLAG_BYTNAME_FNAME(PREGR, FAIL)
#define PSC_STATE_FLAG_PREGR_PASS PSC_STATE_FLAG_BYTNAME_FNAME(PREGR, PASS)
#define PSC_STATE_FLAG_PREGR_TODO PSC_STATE_FLAG_BYTNAME_FNAME(PREGR, TODO)
#define PSC_STATE_FLAG_PREGR_DONE PSC_STATE_FLAG_BYTNAME_FNAME(PREGR, DONE)

#define PSC_STATE_FLAG_DNSBL_FAIL PSC_STATE_FLAG_BYTNAME_FNAME(DNSBL, FAIL)
#define PSC_STATE_FLAG_DNSBL_PASS PSC_STATE_FLAG_BYTNAME_FNAME(DNSBL, PASS)
#define PSC_STATE_FLAG_DNSBL_TODO PSC_STATE_FLAG_BYTNAME_FNAME(DNSBL, TODO)
#define PSC_STATE_FLAG_DNSBL_DONE PSC_STATE_FLAG_BYTNAME_FNAME(DNSBL, DONE)

#define PSC_STATE_FLAG_PIPEL_FAIL PSC_STATE_FLAG_BYTNAME_FNAME(PIPEL, FAIL)
#define PSC_STATE_FLAG_PIPEL_PASS PSC_STATE_FLAG_BYTNAME_FNAME(PIPEL, PASS)
#define PSC_STATE_FLAG_PIPEL_TODO PSC_STATE_FLAG_BYTNAME_FNAME(PIPEL, TODO)
#define PSC_STATE_FLAG_PIPEL_SKIP PSC_STATE_FLAG_BYTNAME_FNAME(PIPEL, SKIP)

#define PSC_STATE_FLAG_NSMTP_FAIL PSC_STATE_FLAG_BYTNAME_FNAME(NSMTP, FAIL)
#define PSC_STATE_FLAG_NSMTP_PASS PSC_STATE_FLAG_BYTNAME_FNAME(NSMTP, PASS)
#define PSC_STATE_FLAG_NSMTP_TODO PSC_STATE_FLAG_BYTNAME_FNAME(NSMTP, TODO)
#define PSC_STATE_FLAG_NSMTP_SKIP PSC_STATE_FLAG_BYTNAME_FNAME(NSMTP, SKIP)

#define PSC_STATE_FLAG_BARLF_FAIL PSC_STATE_FLAG_BYTNAME_FNAME(BARLF, FAIL)
#define PSC_STATE_FLAG_BARLF_PASS PSC_STATE_FLAG_BYTNAME_FNAME(BARLF, PASS)
#define PSC_STATE_FLAG_BARLF_TODO PSC_STATE_FLAG_BYTNAME_FNAME(BARLF, TODO)
#define PSC_STATE_FLAG_BARLF_SKIP PSC_STATE_FLAG_BYTNAME_FNAME(BARLF, SKIP)

 /*
  * Aggregates for individual tests.
  */
#define PSC_STATE_MASK_PREGR_TODO_FAIL \
	(PSC_STATE_FLAG_PREGR_TODO | PSC_STATE_FLAG_PREGR_FAIL)
#define PSC_STATE_MASK_DNSBL_TODO_FAIL \
	(PSC_STATE_FLAG_DNSBL_TODO | PSC_STATE_FLAG_DNSBL_FAIL)
#define PSC_STATE_MASK_PIPEL_TODO_FAIL \
	(PSC_STATE_FLAG_PIPEL_TODO | PSC_STATE_FLAG_PIPEL_FAIL)
#define PSC_STATE_MASK_NSMTP_TODO_FAIL \
	(PSC_STATE_FLAG_NSMTP_TODO | PSC_STATE_FLAG_NSMTP_FAIL)
#define PSC_STATE_MASK_BARLF_TODO_FAIL \
	(PSC_STATE_FLAG_BARLF_TODO | PSC_STATE_FLAG_BARLF_FAIL)

#define PSC_STATE_MASK_PREGR_TODO_DONE \
	(PSC_STATE_FLAG_PREGR_TODO | PSC_STATE_FLAG_PREGR_DONE)
#define PSC_STATE_MASK_PIPEL_TODO_SKIP \
	(PSC_STATE_FLAG_PIPEL_TODO | PSC_STATE_FLAG_PIPEL_SKIP)
#define PSC_STATE_MASK_NSMTP_TODO_SKIP \
	(PSC_STATE_FLAG_NSMTP_TODO | PSC_STATE_FLAG_NSMTP_SKIP)
#define PSC_STATE_MASK_BARLF_TODO_SKIP \
	(PSC_STATE_FLAG_BARLF_TODO | PSC_STATE_FLAG_BARLF_SKIP)

#define PSC_STATE_MASK_PREGR_FAIL_DONE \
	(PSC_STATE_FLAG_PREGR_FAIL | PSC_STATE_FLAG_PREGR_DONE)

#define PSC_STATE_MASK_PIPEL_TODO_PASS_FAIL \
	(PSC_STATE_MASK_PIPEL_TODO_FAIL | PSC_STATE_FLAG_PIPEL_PASS)
#define PSC_STATE_MASK_NSMTP_TODO_PASS_FAIL \
	(PSC_STATE_MASK_NSMTP_TODO_FAIL | PSC_STATE_FLAG_NSMTP_PASS)
#define PSC_STATE_MASK_BARLF_TODO_PASS_FAIL \
	(PSC_STATE_MASK_BARLF_TODO_FAIL | PSC_STATE_FLAG_BARLF_PASS)

 /*
  * Separate aggregates for early tests and deep tests.
  */
#define PSC_STATE_MASK_EARLY_DONE \
	(PSC_STATE_FLAG_PREGR_DONE | PSC_STATE_FLAG_DNSBL_DONE)
#define PSC_STATE_MASK_EARLY_TODO \
	(PSC_STATE_FLAG_PREGR_TODO | PSC_STATE_FLAG_DNSBL_TODO)
#define PSC_STATE_MASK_EARLY_PASS \
	(PSC_STATE_FLAG_PREGR_PASS | PSC_STATE_FLAG_DNSBL_PASS)
#define PSC_STATE_MASK_EARLY_FAIL \
	(PSC_STATE_FLAG_PREGR_FAIL | PSC_STATE_FLAG_DNSBL_FAIL)

#define PSC_STATE_MASK_SMTPD_TODO \
	(PSC_STATE_FLAG_PIPEL_TODO | PSC_STATE_FLAG_NSMTP_TODO | \
	PSC_STATE_FLAG_BARLF_TODO)
#define PSC_STATE_MASK_SMTPD_PASS \
	(PSC_STATE_FLAG_PIPEL_PASS | PSC_STATE_FLAG_NSMTP_PASS | \
	PSC_STATE_FLAG_BARLF_PASS)
#define PSC_STATE_MASK_SMTPD_FAIL \
	(PSC_STATE_FLAG_PIPEL_FAIL | PSC_STATE_FLAG_NSMTP_FAIL | \
	PSC_STATE_FLAG_BARLF_FAIL)

 /*
  * Super-aggregates for all tests combined.
  */
#define PSC_STATE_MASK_ANY_FAIL \
	(PSC_STATE_FLAG_DNLIST_FAIL | \
	PSC_STATE_MASK_EARLY_FAIL | PSC_STATE_MASK_SMTPD_FAIL | \
	PSC_STATE_FLAG_ALLIST_FAIL)

#define PSC_STATE_MASK_ANY_PASS \
	(PSC_STATE_MASK_EARLY_PASS | PSC_STATE_MASK_SMTPD_PASS)

#define PSC_STATE_MASK_ANY_TODO \
	(PSC_STATE_MASK_EARLY_TODO | PSC_STATE_MASK_SMTPD_TODO)

#define PSC_STATE_MASK_ANY_TODO_FAIL \
	(PSC_STATE_MASK_ANY_TODO | PSC_STATE_MASK_ANY_FAIL)

#define PSC_STATE_MASK_ANY_UPDATE \
	(PSC_STATE_MASK_ANY_PASS)

 /*
  * Meta-commands for state->where that reflect the initial command processor
  * state and commands that aren't implemented.
  */
#define PSC_SMTPD_CMD_CONNECT		"CONNECT"
#define PSC_SMTPD_CMD_UNIMPL		"UNIMPLEMENTED"

 /*
  * See log_adhoc.c for discussion.
  */
typedef struct {
    int     dt_sec;			/* make sure it's signed */
    int     dt_usec;			/* make sure it's signed */
} DELTA_TIME;

#define PSC_CALC_DELTA(x, y, z) \
    do { \
	(x).dt_sec = (y).tv_sec - (z).tv_sec; \
	(x).dt_usec = (y).tv_usec - (z).tv_usec; \
	while ((x).dt_usec < 0) { \
	    (x).dt_usec += 1000000; \
	    (x).dt_sec -= 1; \
	} \
	while ((x).dt_usec >= 1000000) { \
	    (x).dt_usec -= 1000000; \
	    (x).dt_sec += 1; \
	} \
	if ((x).dt_sec < 0) \
	    (x).dt_sec = (x).dt_usec = 0; \
    } while (0)

#define SIG_DIGS        2

 /*
  * Event management.
  */

/* PSC_READ_EVENT_REQUEST - prepare for transition to next state */

#define PSC_READ_EVENT_REQUEST(fd, action, context, timeout) do { \
	if (msg_verbose > 1) \
	    msg_info("%s: read-request fd=%d", myname, (fd)); \
	event_enable_read((fd), (action), (context)); \
	event_request_timer((action), (context), (timeout)); \
    } while (0)

#define PSC_READ_EVENT_REQUEST2(fd, read_act, time_act, context, timeout) do { \
	if (msg_verbose > 1) \
	    msg_info("%s: read-request fd=%d", myname, (fd)); \
	event_enable_read((fd), (read_act), (context)); \
	event_request_timer((time_act), (context), (timeout)); \
    } while (0)

/* PSC_CLEAR_EVENT_REQUEST - complete state transition */

#define PSC_CLEAR_EVENT_REQUEST(fd, time_act, context) do { \
	if (msg_verbose > 1) \
	    msg_info("%s: clear-request fd=%d", myname, (fd)); \
	event_disable_readwrite(fd); \
	event_cancel_timer((time_act), (context)); \
    } while (0)

 /*
  * Failure enforcement policies.
  */
#define PSC_NAME_ACT_DROP	"drop"
#define PSC_NAME_ACT_ENFORCE	"enforce"
#define PSC_NAME_ACT_IGNORE	"ignore"
#define PSC_NAME_ACT_CONT	"continue"

#define PSC_ACT_DROP		1
#define PSC_ACT_ENFORCE		2
#define PSC_ACT_IGNORE		3

 /*
  * Global variables.
  */
extern int psc_check_queue_length;	/* connections being checked */
extern int psc_post_queue_length;	/* being sent to real SMTPD */
extern DICT_CACHE *psc_cache_map;	/* cache table handle */
extern VSTRING *psc_temp;		/* scratchpad */
extern char *psc_smtpd_service_name;	/* path to real SMTPD */
extern int psc_pregr_action;		/* PSC_ACT_DROP etc. */
extern int psc_dnsbl_action;		/* PSC_ACT_DROP etc. */
extern int psc_pipel_action;		/* PSC_ACT_DROP etc. */
extern int psc_nsmtp_action;		/* PSC_ACT_DROP etc. */
extern int psc_barlf_action;		/* PSC_ACT_DROP etc. */
extern int psc_min_ttl;			/* Update with new tests! */
extern STRING_LIST *psc_forbid_cmds;	/* CONNECT GET POST */
extern int psc_stress_greet_wait;	/* stressed greet wait */
extern int psc_normal_greet_wait;	/* stressed greet wait */
extern int psc_stress_cmd_time_limit;	/* stressed command limit */
extern int psc_normal_cmd_time_limit;	/* normal command time limit */
extern int psc_stress;			/* stress level */
extern int psc_lowat_check_queue_length;/* stress low-water mark */
extern int psc_hiwat_check_queue_length;/* stress high-water mark */
extern DICT *psc_dnsbl_reply;		/* DNSBL name mapper */
extern HTABLE *psc_client_concurrency;	/* per-client concurrency */

#define PSC_EFF_GREET_WAIT \
	(psc_stress ? psc_stress_greet_wait : psc_normal_greet_wait)
#define PSC_EFF_CMD_TIME_LIMIT \
	(psc_stress ? psc_stress_cmd_time_limit : psc_normal_cmd_time_limit)

 /*
  * String plumbing macros.
  */
#define PSC_STRING_UPDATE(str, text) do { \
	if (str) myfree(str); \
	(str) = ((text) ? mystrdup(text) : 0); \
    } while (0)

#define PSC_STRING_RESET(str) do { \
	if (str) { \
	    myfree(str); \
	    (str) = 0; \
	} \
    } while (0)

 /*
  * SLMs.
  */
#define STR(x)  vstring_str(x)
#define LEN(x)  VSTRING_LEN(x)

 /*
  * postscreen_state.c
  */
#define PSC_CLIENT_ADDR_PORT(state) \
	(state)->smtp_client_addr, (state)->smtp_client_port

#define PSC_PASS_SESSION_STATE(state, what, bits) do { \
	if (msg_verbose) \
	    msg_info("PASS %s [%s]:%s", (what), PSC_CLIENT_ADDR_PORT(state)); \
	(state)->flags |= (bits); \
    } while (0)
#define PSC_FAIL_SESSION_STATE(state, bits) do { \
	if (msg_verbose) \
	    msg_info("FAIL [%s]:%s", PSC_CLIENT_ADDR_PORT(state)); \
	(state)->flags |= (bits); \
    } while (0)
#define PSC_SKIP_SESSION_STATE(state, what, bits) do { \
	if (msg_verbose) \
	    msg_info("SKIP %s [%s]:%s", (what), PSC_CLIENT_ADDR_PORT(state)); \
	(state)->flags |= (bits); \
    } while (0)
#define PSC_DROP_SESSION_STATE(state, reply) do { \
	if (msg_verbose) \
	    msg_info("DROP [%s]:%s", PSC_CLIENT_ADDR_PORT(state)); \
	(state)->flags |= PSC_STATE_FLAG_NOFORWARD; \
	(state)->final_reply = (reply); \
	psc_conclude(state); \
    } while (0)
#define PSC_ENFORCE_SESSION_STATE(state, reply) do { \
	if (msg_verbose) \
	    msg_info("ENFORCE [%s]:%s", PSC_CLIENT_ADDR_PORT(state)); \
	(state)->rcpt_reply = (reply); \
	(state)->flags |= PSC_STATE_FLAG_NOFORWARD; \
    } while (0)
#define PSC_UNPASS_SESSION_STATE(state, bits) do { \
	if (msg_verbose) \
	    msg_info("UNPASS [%s]:%s", PSC_CLIENT_ADDR_PORT(state)); \
	(state)->flags &= ~(bits); \
    } while (0)
#define PSC_UNFAIL_SESSION_STATE(state, bits) do { \
	if (msg_verbose) \
	    msg_info("UNFAIL [%s]:%s", PSC_CLIENT_ADDR_PORT(state)); \
	(state)->flags &= ~(bits); \
    } while (0)
#define PSC_ADD_SERVER_STATE(state, fd) do { \
	(state)->smtp_server_fd = (fd); \
	psc_post_queue_length++; \
    } while (0)
#define PSC_DEL_SERVER_STATE(state) do { \
	close((state)->smtp_server_fd); \
	(state)->smtp_server_fd = (-1); \
	psc_post_queue_length--; \
    } while (0)
#define PSC_DEL_CLIENT_STATE(state) do { \
	event_server_disconnect((state)->smtp_client_stream); \
	(state)->smtp_client_stream = 0; \
	psc_check_queue_length--; \
    } while (0)
extern PSC_STATE *psc_new_session_state(VSTREAM *, const char *, const char *, const char *, const char *);
extern void psc_free_session_state(PSC_STATE *);
extern const char *psc_print_state_flags(int, const char *);

 /*
  * postscreen_dict.c
  */
extern int psc_addr_match_list_match(ADDR_MATCH_LIST *, const char *);
extern const char *psc_cache_lookup(DICT_CACHE *, const char *);
extern void psc_cache_update(DICT_CACHE *, const char *, const char *);
const char *psc_dict_get(DICT *, const char *);
const char *psc_maps_find(MAPS *, const char *, int);

 /*
  * postscreen_dnsbl.c
  */
extern void psc_dnsbl_init(void);
extern int psc_dnsbl_retrieve(const char *, const char **, int, int *);
extern int psc_dnsbl_request(const char *, void (*) (int, void *), void *);

 /*
  * postscreen_tests.c
  */
#define PSC_INIT_TESTS(dst) do { \
	time_t *_it_stamp_p; \
	(dst)->flags = 0; \
	for (_it_stamp_p = (dst)->client_info->expire_time; \
	    _it_stamp_p < (dst)->client_info->expire_time + PSC_TINDX_COUNT; \
	    _it_stamp_p++) \
	    *_it_stamp_p = PSC_TIME_STAMP_INVALID; \
    } while (0)
#define PSC_INIT_TEST_FLAGS_ONLY(dst) do { \
	(dst)->flags = 0; \
    } while (0)
#define PSC_BEGIN_TESTS(state, name) do { \
	(state)->test_name = (name); \
	GETTIMEOFDAY(&(state)->start_time); \
    } while (0)
extern void psc_new_tests(PSC_STATE *);
extern void psc_parse_tests(PSC_STATE *, const char *, time_t);
extern void psc_todo_tests(PSC_STATE *, time_t);
extern char *psc_print_tests(VSTRING *, PSC_STATE *);
extern char *psc_print_grey_key(VSTRING *, const char *, const char *,
				        const char *, const char *);
extern const char *psc_test_name(int);

#define PSC_MIN(x, y) ((x) < (y) ? (x) : (y))
#define PSC_MAX(x, y) ((x) > (y) ? (x) : (y))

 /*
  * postscreen_early.c
  */
extern void psc_early_tests(PSC_STATE *);
extern void psc_early_init(void);

 /*
  * postscreen_smtpd.c
  */
extern void psc_smtpd_tests(PSC_STATE *);
extern void psc_smtpd_init(void);
extern void psc_smtpd_pre_jail_init(void);

#define PSC_SMTPD_X21(state, reply) do { \
	(state)->flags |= PSC_STATE_FLAG_SMTPD_X21; \
	(state)->final_reply = (reply); \
	psc_smtpd_tests(state); \
    } while (0)

 /*
  * postscreen_misc.c
  */
extern char *psc_format_delta_time(VSTRING *, struct timeval, DELTA_TIME *);
extern void psc_conclude(PSC_STATE *);
extern void psc_hangup_event(PSC_STATE *);

 /*
  * postscreen_send.c
  */
#define PSC_SEND_REPLY psc_send_reply	/* legacy macro */
extern void pcs_send_pre_jail_init(void);
extern int psc_send_reply(PSC_STATE *, const char *);
extern void psc_send_socket(PSC_STATE *);

 /*
  * postscreen_starttls.c
  */
extern void psc_starttls_open(PSC_STATE *, EVENT_NOTIFY_FN);

 /*
  * postscreen_expand.c
  */
extern VSTRING *psc_expand_filter;
extern void psc_expand_init(void);
extern const char *psc_expand_lookup(const char *, int, void *);

 /*
  * postscreen_endpt.c
  */
typedef void (*PSC_ENDPT_LOOKUP_FN) (int, VSTREAM *,
			             MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *,
			            MAI_HOSTADDR_STR *, MAI_SERVPORT_STR *);
extern void psc_endpt_lookup(VSTREAM *, PSC_ENDPT_LOOKUP_FN);
extern void psc_endpt_local_lookup(VSTREAM *, PSC_ENDPT_LOOKUP_FN);

 /*
  * postscreen_access emulation.
  */
#define PSC_ACL_ACT_ALLOWLIST	SERVER_ACL_ACT_PERMIT
#define PSC_ACL_ACT_DUNNO	SERVER_ACL_ACT_DUNNO
#define PSC_ACL_ACT_DENYLIST	SERVER_ACL_ACT_REJECT
#define PSC_ACL_ACT_ERROR	SERVER_ACL_ACT_ERROR

#define psc_acl_pre_jail_init	server_acl_pre_jail_init
#define psc_acl_parse		server_acl_parse
#define psc_acl_eval(s,a,p)	server_acl_eval((s)->smtp_client_addr, (a), (p))

/* LICENSE
/* .ad
/* .fi
/*	The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/*	Wietse Venema
/*	IBM T.J. Watson Research
/*	P.O. Box 704
/*	Yorktown Heights, NY 10598, USA
/*
/*	Wietse Venema
/*	Google, Inc.
/*	111 8th Avenue
/*	New York, NY 10011, USA
/*--*/
