aboutsummaryrefslogtreecommitdiff
path: root/mfd
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2009-05-02 23:50:08 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2009-05-02 23:50:08 +0300
commite728b9defbf5712e2b99ae03d5974246a3f4937b (patch)
tree94cbd561e941d3bf3bb29b7d887d645fca89463c /mfd
parentd54b57cef00367b94adfeb575475e3c3731cbcc1 (diff)
downloadmailfromd-e728b9defbf5712e2b99ae03d5974246a3f4937b.tar.gz
mailfromd-e728b9defbf5712e2b99ae03d5974246a3f4937b.tar.bz2
Add an alternative implementation of the greylist function.
* mfd/bi_db.m4: Add a new implementation of the greylist function, proposed by Con Tassios. In this implementation, the database keeps the time the greylisting period is set to expire, rather than the time the greylisting was activated. This allows to implement is_greylisted function. The implementation to use is selected by #pragma greylist. * tests/greylist-ct.at: New file. Testcase for Con Tassios style greylist. * tests/Makefile.am (TESTSUITE_AT): Add greylist-ct.at. * tests/greylist.at: Update. * tests/testsuite.at: Include greylist-ct.at. * tests/etc/greylist-ct.rc: New file. * tests/etc/Makefile.am (RCFILES): Add greylist-ct.rc. * THANKS: Fix alphabetical ordering.
Diffstat (limited to 'mfd')
-rw-r--r--mfd/bi_db.m4219
1 files changed, 211 insertions, 8 deletions
diff --git a/mfd/bi_db.m4 b/mfd/bi_db.m4
index 468a994b..f2e1a673 100644
--- a/mfd/bi_db.m4
+++ b/mfd/bi_db.m4
@@ -430,6 +430,31 @@ MF_DEFUN(dbvalue, STRING, NUMBER dn)
END
+enum greylist_semantics
+{
+ greylist_traditional,
+ greylist_ct
+};
+
+static enum greylist_semantics greylist_semantics = greylist_traditional;
+
+/* #pragma greylist {traditional|gray|ct|con-tassios}*/
+MF_PRAGMA(greylist, 2, 2)
+{
+ if (strcmp(argv[1], "traditional") == 0
+ || strcmp(argv[1], "gray") == 0)
+ greylist_semantics = greylist_traditional;
+ else if (strcmp(argv[1], "ct") == 0
+ || strcmp(argv[1], "con-tassios") == 0)
+ greylist_semantics = greylist_ct;
+ else
+ /* TRANSLATORS: Do not translate keywords:
+ traditional, gray, ct, con-tassios */
+ parse_error(_("unknow semantics; allowed values are: "
+ "traditional (or gray) and "
+ "ct (or con-tassios)"));
+}
+
static void
greylist_print_item(const char *key, size_t size, const void *content)
{
@@ -462,12 +487,11 @@ struct db_format *greylist_format = &greylist_format_struct;
MF_VAR(greylist_seconds_left, NUMBER);
-/* greylist(key, interval)
-
- Returns true if the key is greylisted, false if it's OK to
- deliver mail.
+/* The traditional (aka gray's) greylist implementation: the greylist
+ database keeps the time the greylisting was activated.
*/
-MF_DEFUN(greylist, NUMBER, STRING email, NUMBER interval)
+static int
+do_greylist_traditional(eval_environ_t env, char *email, long interval)
{
int rc;
DBM_FILE db;
@@ -497,7 +521,7 @@ MF_DEFUN(greylist, NUMBER, STRING email, NUMBER interval)
timestamp = *(time_t*) MU_DATUM_PTR(contents);
diff = now - timestamp;
-
+
__DBG(20) {
char timebuf[32];
debug_log("%s entered greylist database on %s, "
@@ -520,8 +544,8 @@ MF_DEFUN(greylist, NUMBER, STRING email, NUMBER interval)
} else if (diff > greylist_format->expire_interval) {
debug1(20, "greylist record for %s expired",
email);
+ MF_VAR_REF(greylist_seconds_left, interval);
if (!readonly) {
- MF_VAR_REF(greylist_seconds_left, interval);
memcpy(MU_DATUM_PTR(contents),
&now, sizeof now);
if (mu_dbm_insert(&db, key, contents, 1))
@@ -558,7 +582,186 @@ MF_DEFUN(greylist, NUMBER, STRING email, NUMBER interval)
mu_dbm_close(&db);
- MF_RETURN(rc);
+ return rc;
+}
+
+/* Implementation of the is_greylisted predicate has no sense for
+ traditional greylist databases, because greylisting interval is
+ not known beforehand.
+ FIXME: update the docs and place a reference to the right spot
+ in the message below.
+ */
+static int
+is_greylisted_traditional(eval_environ_t env, char *email)
+{
+ MF_THROW(mfe_failure,
+ _("is_greylisted is not implemented for traditional greylist databases;"
+ "see documentation, chapter FIXME for more info"));
+ return 0;
+}
+
+/* New greylist implementation (by Con Tassios): the database keeps
+ the time the greylisting period is set to expire (`interval' seconds
+ from now)
+*/
+static int
+do_greylist_ct(eval_environ_t env, char *email, long interval)
+{
+ int rc;
+ DBM_FILE db;
+ DBM_DATUM key;
+ DBM_DATUM contents;
+ int readonly;
+ time_t now;
+
+ rc = mu_dbm_open(greylist_format->dbname, &db, MU_STREAM_RDWR, 0600,
+ &readonly);
+ MF_ASSERT(rc == 0, mfe_dbfailure, _("mu_dbm_open(%s) failed: %s"),
+ greylist_format->dbname, mu_dbm_strerror());
+
+ memset(&key, 0, sizeof key);
+ memset(&contents, 0, sizeof contents);
+ MU_DATUM_PTR(key) = email;
+ MU_DATUM_SIZE(key) = strlen(email)+1;
+
+ time(&now);
+ if (mu_dbm_fetch(&db, key, &contents) == 0) {
+ time_t timestamp;
+
+ MF_ASSERT(MU_DATUM_SIZE(contents) == sizeof timestamp,
+ mfe_dbfailure,
+ _("Greylist database %s has wrong data size"),
+ greylist_format->dbname);
+
+ timestamp = *(time_t*) MU_DATUM_PTR(contents);
+
+ if (now < timestamp) {
+ time_t diff = timestamp - now;
+ MF_VAR_REF(greylist_seconds_left, diff);
+
+ debug2(20, "%s still greylisted (for %lu sec.)",
+ email,
+ (unsigned long) diff);
+ rc = 1;
+ } else if (now - timestamp >
+ greylist_format->expire_interval) {
+ debug1(20, "greylist record for %s expired", email);
+ MF_VAR_REF(greylist_seconds_left, interval);
+ if (!readonly) {
+ now += interval;
+ memcpy(MU_DATUM_PTR(contents),
+ &now, sizeof now);
+ if (mu_dbm_insert(&db, key, contents, 1))
+ mu_error(_("Cannot insert datum "
+ "`%-.*s' in greylist "
+ "database %s: %s"),
+ MU_DATUM_SIZE(key),
+ (char*)MU_DATUM_PTR(key),
+ greylist_format->dbname,
+ mu_dbm_strerror());
+ } else
+ debug(20, "database opened in readonly mode: "
+ "not updating");
+ rc = 1;
+ } else {
+ debug1(20, "%s finished greylisting period", email);
+ rc = 0;
+ }
+ mu_dbm_datum_free(&contents);
+ } else if (!readonly) {
+ debug1(20, "greylisting %s", email);
+ MF_VAR_REF(greylist_seconds_left, interval);
+ now += interval;
+ MU_DATUM_PTR(contents) = (void*)&now;
+ MU_DATUM_SIZE(contents) = sizeof now;
+ if (mu_dbm_insert(&db, key, contents, 1))
+ mu_error(_("Cannot insert datum `%-.*s' in greylist "
+ "database %s: %s"),
+ MU_DATUM_SIZE(key), (char*)MU_DATUM_PTR(key),
+ greylist_format->dbname,
+ mu_dbm_strerror());
+ rc = 1;
+ } else
+ rc = 0;
+
+ mu_dbm_close(&db);
+
+ return rc;
+}
+
+/* The `is_greylisted' predicate for new databases */
+static int
+is_greylisted_ct(eval_environ_t env, char *email)
+{
+ int rc;
+ DBM_FILE db;
+ DBM_DATUM key;
+ DBM_DATUM contents;
+ int readonly;
+ time_t now;
+
+ rc = mu_dbm_open(greylist_format->dbname, &db, MU_STREAM_RDWR, 0600,
+ &readonly);
+ MF_ASSERT(rc == 0, mfe_dbfailure, _("mu_dbm_open(%s) failed: %s"),
+ greylist_format->dbname, mu_dbm_strerror());
+
+ memset(&key, 0, sizeof key);
+ memset(&contents, 0, sizeof contents);
+ MU_DATUM_PTR(key) = email;
+ MU_DATUM_SIZE(key) = strlen(email) + 1;
+
+ time(&now);
+ if (mu_dbm_fetch(&db, key, &contents) == 0) {
+ time_t timestamp;
+
+ MF_ASSERT(MU_DATUM_SIZE(contents) == sizeof timestamp,
+ mfe_dbfailure,
+ _("Greylist database %s has wrong data size"),
+ greylist_format->dbname);
+
+ timestamp = *(time_t*) MU_DATUM_PTR(contents);
+
+ rc = timestamp > now;
+
+ mu_dbm_datum_free(&contents);
+ } else
+ rc = 0;
+
+ mu_dbm_close(&db);
+
+ return rc;
+}
+
+struct greylist_class {
+ int (*gl_fun)(eval_environ_t, char *, long);
+ int (*gl_pred)(eval_environ_t, char *);
+};
+
+struct greylist_class greylist_class[] = {
+ { do_greylist_traditional, is_greylisted_traditional },
+ { do_greylist_ct, is_greylisted_ct }
+};
+
+/* greylist(key, interval)
+
+ Returns true if the key is greylisted, false if it's OK to
+ deliver mail.
+ */
+MF_DEFUN(greylist, NUMBER, STRING email, NUMBER interval)
+{
+ MF_RETURN(greylist_class[greylist_semantics].gl_fun(env, email,
+ interval));
+}
+END
+
+/* is_greylisted(key)
+
+ Returns true if the key is greylisted, otherwise false
+
+*/
+MF_DEFUN(is_greylisted, NUMBER, STRING email)
+{
+ MF_RETURN(greylist_class[greylist_semantics].gl_pred(env, email));
}
END

Return to:

Send suggestions and report system problems to the System administrator.