int delay;
int retry_window;
int client_awl;
+ int max_age;
TCBDB *awl_db;
TCBDB *obj_db;
#define GREYLIST_INIT { .lookup_by_host = false, \
.delay = 300, \
- .retry_window = 2 * 24 * 2600, \
+ .retry_window = 2 * 24 * 3600, \
.client_awl = 5, \
+ .max_age = 35 * 3600, \
.awl_db = NULL, \
.obj_db = NULL }
if (!tcbdbopen(config->awl_db, path, BDBOWRITER | BDBOCREAT)) {
tcbdbdel(config->awl_db);
config->awl_db = NULL;
+ return false;
}
- return false;
}
snprintf(path, sizeof(path), "%s/%sgreylist.db", directory, prefix);
return cnet;
}
+static inline bool greylist_check_awlentry(const greylist_config_t *config,
+ struct awl_entry *aent, time_t now)
+{
+ return !(now - aent->last > config->max_age);
+}
+
+static inline bool greylist_check_object(const greylist_config_t *config,
+ const struct obj_entry *oent, time_t now)
+{
+ return !(now - oent->last > config->max_age
+ || (oent->last - oent->first < config->delay
+ && now - oent->last > config->retry_window));
+}
+
static bool try_greylist(const greylist_config_t *config,
const char *sender, const char *c_addr,
const char *c_name, const char *rcpt)
memcpy(&aent, res, len);
}
+ if (!greylist_check_awlentry(config, &aent, now)) {
+ aent.count = 0;
+ aent.last = 0;
+ }
+
/* Whitelist if count is enough.
*/
- if (aent.count > config->client_awl) {
+ if (aent.count >= config->client_awl) {
if (now < aent.last + 3600) {
INCR_AWL
}
/* OK.
*/
+ //syslog(LOG_INFO, "client whitelisted");
return true;
}
}
res = tcbdbget3(config->obj_db, key, klen, &len);
if (res && len == sizeof(oent)) {
memcpy(&oent, res, len);
+ greylist_check_object(config, &oent, now);
}
/* Discard stored first-seen if it is the first retrial and
- * it is beyong the retry window.
+ * it is beyong the retry window and too old entries.
*/
- if (oent.last - oent.first < config->delay
- && now - oent.first > config->retry_window) {
+ if (!greylist_check_object(config, &oent, now)) {
oent.first = now;
}
/* OK
*/
+ //syslog(LOG_INFO, "client whitelisted");
return true;
}
/* DUNNO
*/
+ //syslog(LOG_INFO, "client greylisted");
return false;
}
foreach (filter_param_t *param, filter->params) {
switch (param->type) {
- case ATK_PATH:
- path = param->value;
- break;
-
- case ATK_PREFIX:
- prefix = param->value;
- break;
-
- case ATK_LOOKUP_BY_HOST:
- config->lookup_by_host = (atoi(param->value) != 0);
- break;
-
- case ATK_RETRY_WINDOW:
- config->retry_window = atoi(param->value);
- break;
-
- case ATK_CLIENT_AWL:
- config->client_awl = atoi(param->value);
- break;
+ FILTER_PARAM_PARSE_STRING(PATH, path);
+ FILTER_PARAM_PARSE_STRING(PREFIX, prefix);
+ FILTER_PARAM_PARSE_BOOLEAN(LOOKUP_BY_HOST, config->lookup_by_host);
+ FILTER_PARAM_PARSE_INT(RETRY_WINDOW, config->retry_window);
+ FILTER_PARAM_PARSE_INT(CLIENT_AWL, config->client_awl);
+ FILTER_PARAM_PARSE_INT(DELAY, config->delay);
+ FILTER_PARAM_PARSE_INT(MAX_AGE, config->max_age);
default: break;
}
const query_t *query)
{
const greylist_config_t *config = filter->data;
+ if (query->state != SMTP_RCPT) {
+ syslog(LOG_WARNING, "greylisting only works as smtpd_recipient_restrictions");
+ return HTK_ABORT;
+ }
+
return try_greylist(config, query->sender, query->client_address,
query->client_name, query->recipient) ?
- HTK_MATCH : HTK_FAIL;
+ HTK_WHITELIST : HTK_GREYLIST;
}
static int greylist_init(void)
greylist_filter);
/* Hooks.
*/
+ (void)filter_hook_register(type, "abort");
(void)filter_hook_register(type, "error");
- (void)filter_hook_register(type, "fail");
- (void)filter_hook_register(type, "match");
+ (void)filter_hook_register(type, "greylist");
+ (void)filter_hook_register(type, "whitelist");
/* Parameters.
*/
(void)filter_param_register(type, "delay");
(void)filter_param_register(type, "retry_window");
(void)filter_param_register(type, "client_awl");
+ (void)filter_param_register(type, "max_age");
(void)filter_param_register(type, "path");
(void)filter_param_register(type, "prefix");
return 0;