Current File : //root/postfix-3.2.0/src/postscreen/postscreen_tests.c |
/*++
/* NAME
/* postscreen_tests 3
/* SUMMARY
/* postscreen tests timestamp/flag bulk support
/* SYNOPSIS
/* #include <postscreen.h>
/*
/* void PSC_INIT_TESTS(state)
/* PSC_STATE *state;
/*
/* void psc_new_tests(state)
/* PSC_STATE *state;
/*
/* void psc_parse_tests(state, stamp_text, time_value)
/* PSC_STATE *state;
/* const char *stamp_text;
/* time_t time_value;
/*
/* void psc_todo_tests(state, time_value)
/* PSC_STATE *state;
/* const char *stamp_text;
/* time_t time_value;
/*
/* char *psc_print_tests(buffer, state)
/* VSTRING *buffer;
/* PSC_STATE *state;
/*
/* char *psc_print_grey_key(buffer, client, helo, sender, rcpt)
/* VSTRING *buffer;
/* const char *client;
/* const char *helo;
/* const char *sender;
/* const char *rcpt;
/*
/* const char *psc_test_name(tindx)
/* int tindx;
/* DESCRIPTION
/* The functions in this module overwrite the per-test expiration
/* time stamps and all flags bits. Some functions are implemented
/* as unsafe macros, meaning they evaluate one or more arguments
/* multiple times.
/*
/* PSC_INIT_TESTS() is an unsafe macro that sets the per-test
/* expiration time stamps to PSC_TIME_STAMP_INVALID, and that
/* zeroes all the flags bits. These values are not meant to
/* be stored into the postscreen(8) cache.
/*
/* PSC_INIT_TEST_FLAGS_ONLY() zeroes all the flag bits. It
/* should be used when the time stamps are already initialized.
/*
/* psc_new_tests() sets all test expiration time stamps to
/* PSC_TIME_STAMP_NEW, and invokes psc_todo_tests().
/*
/* psc_parse_tests() parses a cache file record and invokes
/* psc_todo_tests().
/*
/* psc_todo_tests() overwrites all per-session flag bits, and
/* populates the flags based on test expiration time stamp
/* information. Tests are considered "expired" when they
/* would be expired at the specified time value. Only enabled
/* tests are flagged as "expired"; the object is flagged as
/* "new" if some enabled tests have "new" time stamps.
/*
/* psc_print_tests() creates a cache file record for the
/* specified flags and per-test expiration time stamps.
/* This may modify the time stamps for disabled tests.
/*
/* psc_print_grey_key() prints a greylist lookup key.
/*
/* psc_test_name() returns the name for the specified text
/* index.
/* 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
/*--*/
/* System library. */
#include <sys_defs.h>
#include <stdio.h> /* sscanf */
#include <stdlib.h> /* strtoul */
/* Utility library. */
#include <msg.h>
#include <name_code.h>
/* Global library. */
#include <mail_params.h>
/* Application-specific. */
#include <postscreen.h>
/*
* Kludge to detect if some test is enabled.
*/
#define PSC_PREGR_TEST_ENABLE() (*var_psc_pregr_banner != 0)
#define PSC_DNSBL_TEST_ENABLE() (*var_psc_dnsbl_sites != 0)
/*
* Format of a persistent cache entry (which is almost but not quite the
* same as the in-memory representation).
*
* Each cache entry has one time stamp for each test.
*
* - A time stamp of PSC_TIME_STAMP_INVALID must never appear in the cache. It
* is reserved for in-memory objects that are still being initialized.
*
* - A time stamp of PSC_TIME_STAMP_NEW indicates that the test never passed.
* Postscreen will log the client with "pass new" when it passes the final
* test.
*
* - A time stamp of PSC_TIME_STAMP_DISABLED indicates that the test never
* passed, and that the test was disabled when the cache entry was written.
*
* - Otherwise, the test was passed, and the time stamp indicates when that
* test result expires.
*
* A cache entry is expired when the time stamps of all passed tests are
* expired.
*/
/* psc_new_tests - initialize new test results from scratch */
void psc_new_tests(PSC_STATE *state)
{
time_t *expire_time = state->client_info->expire_time;
/*
* Give all tests a PSC_TIME_STAMP_NEW time stamp, so that we can later
* recognize cache entries that haven't passed all enabled tests. When we
* write a cache entry to the database, any new-but-disabled tests will
* get a PSC_TIME_STAMP_DISABLED time stamp.
*/
expire_time[PSC_TINDX_PREGR] = PSC_TIME_STAMP_NEW;
expire_time[PSC_TINDX_DNSBL] = PSC_TIME_STAMP_NEW;
expire_time[PSC_TINDX_PIPEL] = PSC_TIME_STAMP_NEW;
expire_time[PSC_TINDX_NSMTP] = PSC_TIME_STAMP_NEW;
expire_time[PSC_TINDX_BARLF] = PSC_TIME_STAMP_NEW;
/*
* Determine what tests need to be completed.
*/
psc_todo_tests(state, PSC_TIME_STAMP_NEW + 1);
}
/* psc_parse_tests - parse test results from cache */
void psc_parse_tests(PSC_STATE *state,
const char *stamp_str,
time_t time_value)
{
const char *start = stamp_str;
char *cp;
time_t *time_stamps = state->client_info->expire_time;
time_t *sp;
/*
* Parse the cache entry, and allow for older postscreen versions that
* implemented fewer tests. We pretend that the newer tests were disabled
* at the time that the cache entry was written.
*/
for (sp = time_stamps; sp < time_stamps + PSC_TINDX_COUNT; sp++) {
*sp = strtoul(start, &cp, 10);
if (*start == 0 || (*cp != '\0' && *cp != ';') || errno == ERANGE)
*sp = PSC_TIME_STAMP_DISABLED;
if (msg_verbose)
msg_info("%s -> %lu", start, (unsigned long) *sp);
if (*cp == ';')
start = cp + 1;
else
start = cp;
}
/*
* Determine what tests need to be completed.
*/
psc_todo_tests(state, time_value);
}
/* psc_todo_tests - determine what tests to perform */
void psc_todo_tests(PSC_STATE *state, time_t time_value)
{
time_t *expire_time = state->client_info->expire_time;
time_t *sp;
/*
* Reset all per-session flags.
*/
state->flags = 0;
/*
* Flag the tests as "new" when the cache entry has fields for all
* enabled tests, but the remote SMTP client has not yet passed all those
* tests.
*/
for (sp = expire_time; sp < expire_time + PSC_TINDX_COUNT; sp++) {
if (*sp == PSC_TIME_STAMP_NEW)
state->flags |= PSC_STATE_FLAG_NEW;
}
/*
* Don't flag disabled tests as "todo", because there would be no way to
* make those bits go away.
*/
if (PSC_PREGR_TEST_ENABLE() && time_value > expire_time[PSC_TINDX_PREGR])
state->flags |= PSC_STATE_FLAG_PREGR_TODO;
if (PSC_DNSBL_TEST_ENABLE() && time_value > expire_time[PSC_TINDX_DNSBL])
state->flags |= PSC_STATE_FLAG_DNSBL_TODO;
if (var_psc_pipel_enable && time_value > expire_time[PSC_TINDX_PIPEL])
state->flags |= PSC_STATE_FLAG_PIPEL_TODO;
if (var_psc_nsmtp_enable && time_value > expire_time[PSC_TINDX_NSMTP])
state->flags |= PSC_STATE_FLAG_NSMTP_TODO;
if (var_psc_barlf_enable && time_value > expire_time[PSC_TINDX_BARLF])
state->flags |= PSC_STATE_FLAG_BARLF_TODO;
/*
* If any test has expired, proactively refresh tests that will expire
* soon. This can increase the occurrence of client-visible delays, but
* avoids questions about why a client can pass some test and then fail
* within seconds. The proactive refresh time is really a surrogate for
* the user's curiosity level, and therefore hard to choose optimally.
*/
#ifdef VAR_PSC_REFRESH_TIME
if ((state->flags & PSC_STATE_MASK_ANY_TODO) != 0
&& var_psc_refresh_time > 0) {
time_t refresh_time = time_value + var_psc_refresh_time;
if (PSC_PREGR_TEST_ENABLE() && refresh_time > expire_time[PSC_TINDX_PREGR])
state->flags |= PSC_STATE_FLAG_PREGR_TODO;
if (PSC_DNSBL_TEST_ENABLE() && refresh_time > expire_time[PSC_TINDX_DNSBL])
state->flags |= PSC_STATE_FLAG_DNSBL_TODO;
if (var_psc_pipel_enable && refresh_time > expire_time[PSC_TINDX_PIPEL])
state->flags |= PSC_STATE_FLAG_PIPEL_TODO;
if (var_psc_nsmtp_enable && refresh_time > expire_time[PSC_TINDX_NSMTP])
state->flags |= PSC_STATE_FLAG_NSMTP_TODO;
if (var_psc_barlf_enable && refresh_time > expire_time[PSC_TINDX_BARLF])
state->flags |= PSC_STATE_FLAG_BARLF_TODO;
}
#endif
/*
* Gratuitously make postscreen logging more useful by turning on all
* enabled pre-handshake tests when any pre-handshake test is turned on.
*
* XXX Don't enable PREGREET gratuitously before the test expires. With a
* short TTL for DNSBL whitelisting, turning on PREGREET would force a
* full postscreen_greet_wait too frequently.
*/
#if 0
if (state->flags & PSC_STATE_MASK_EARLY_TODO) {
if (PSC_PREGR_TEST_ENABLE())
state->flags |= PSC_STATE_FLAG_PREGR_TODO;
if (PSC_DNSBL_TEST_ENABLE())
state->flags |= PSC_STATE_FLAG_DNSBL_TODO;
}
#endif
}
/* psc_print_tests - print postscreen cache record */
char *psc_print_tests(VSTRING *buf, PSC_STATE *state)
{
const char *myname = "psc_print_tests";
time_t *expire_time = state->client_info->expire_time;
/*
* Sanity check.
*/
if ((state->flags & PSC_STATE_MASK_ANY_UPDATE) == 0)
msg_panic("%s: attempt to save a no-update record", myname);
/*
* Give disabled tests a dummy time stamp so that we don't log a client
* with "pass new" when some disabled test becomes enabled at some later
* time.
*/
if (PSC_PREGR_TEST_ENABLE() == 0 && expire_time[PSC_TINDX_PREGR] == PSC_TIME_STAMP_NEW)
expire_time[PSC_TINDX_PREGR] = PSC_TIME_STAMP_DISABLED;
if (PSC_DNSBL_TEST_ENABLE() == 0 && expire_time[PSC_TINDX_DNSBL] == PSC_TIME_STAMP_NEW)
expire_time[PSC_TINDX_DNSBL] = PSC_TIME_STAMP_DISABLED;
if (var_psc_pipel_enable == 0 && expire_time[PSC_TINDX_PIPEL] == PSC_TIME_STAMP_NEW)
expire_time[PSC_TINDX_PIPEL] = PSC_TIME_STAMP_DISABLED;
if (var_psc_nsmtp_enable == 0 && expire_time[PSC_TINDX_NSMTP] == PSC_TIME_STAMP_NEW)
expire_time[PSC_TINDX_NSMTP] = PSC_TIME_STAMP_DISABLED;
if (var_psc_barlf_enable == 0 && expire_time[PSC_TINDX_BARLF] == PSC_TIME_STAMP_NEW)
expire_time[PSC_TINDX_BARLF] = PSC_TIME_STAMP_DISABLED;
vstring_sprintf(buf, "%lu;%lu;%lu;%lu;%lu",
(unsigned long) expire_time[PSC_TINDX_PREGR],
(unsigned long) expire_time[PSC_TINDX_DNSBL],
(unsigned long) expire_time[PSC_TINDX_PIPEL],
(unsigned long) expire_time[PSC_TINDX_NSMTP],
(unsigned long) expire_time[PSC_TINDX_BARLF]);
return (STR(buf));
}
/* psc_print_grey_key - print postscreen cache record */
char *psc_print_grey_key(VSTRING *buf, const char *client,
const char *helo, const char *sender,
const char *rcpt)
{
return (STR(vstring_sprintf(buf, "%s/%s/%s/%s",
client, helo, sender, rcpt)));
}
/* psc_test_name - map test index to symbolic name */
const char *psc_test_name(int tindx)
{
const char *myname = "psc_test_name";
const NAME_CODE test_name_map[] = {
PSC_TNAME_PREGR, PSC_TINDX_PREGR,
PSC_TNAME_DNSBL, PSC_TINDX_DNSBL,
PSC_TNAME_PIPEL, PSC_TINDX_PIPEL,
PSC_TNAME_NSMTP, PSC_TINDX_NSMTP,
PSC_TNAME_BARLF, PSC_TINDX_BARLF,
0, -1,
};
const char *result;
if ((result = str_name_code(test_name_map, tindx)) == 0)
msg_panic("%s: bad index %d", myname, tindx);
return (result);
}