|
|
|
@@ -99,6 +99,11 @@ int _take_action(Client *client, BanAction *action, const char *reason, long dur
|
|
|
|
|
int _match_spamfilter(Client *client, const char *str_in, int type, const char *cmd, const char *target, int flags, ClientContext *clictx, TKL **rettk);
|
|
|
|
|
int _match_spamfilter_mtags(Client *client, MessageTag *mtags, const char *cmd);
|
|
|
|
|
int check_special_spamfilters_present(void);
|
|
|
|
|
char *tkl_hits_key(TKL *tkl, char *buf, size_t len);
|
|
|
|
|
void config_tkl_hits_free(ModData *m);
|
|
|
|
|
void config_tkl_hits_snapshot(TKL *tkl);
|
|
|
|
|
void _config_tkl_hits_restore(void);
|
|
|
|
|
void _remove_config_tkls(int flag);
|
|
|
|
|
int _join_viruschan(Client *client, TKL *tk, int type);
|
|
|
|
|
void _spamfilter_build_user_string(char *buf, const char *nick, Client *client);
|
|
|
|
|
int _match_user(const char *rmask, Client *client, int options);
|
|
|
|
@@ -132,6 +137,7 @@ struct TKLTypeTable
|
|
|
|
|
unsigned tkltype:1; /**< Is a type available in cmd_tkl() and friends */
|
|
|
|
|
unsigned exceptiontype:1; /**< Is a type available for exceptions */
|
|
|
|
|
unsigned needip:1; /**< When using this exempt option, only IP addresses are permitted (processed before DNS/ident lookups etc) */
|
|
|
|
|
char *id_prefix; /**< Prefix for generated TKL ids, eg "G", "K", "SPAM". NULL for exempt-only options. Note that shun has "H" here while its type.letter is 's'. */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** This table which defines all TKL types and TKL exception types.
|
|
|
|
@@ -144,26 +150,26 @@ struct TKLTypeTable
|
|
|
|
|
* - more?
|
|
|
|
|
*/
|
|
|
|
|
TKLTypeTable tkl_types[] = {
|
|
|
|
|
/* <config name> <letter> <TKL_xxx type> <logging name> <tkl option?> <exempt option?> <ip address only?> */
|
|
|
|
|
{ "gline", 'G', TKL_KILL | TKL_GLOBAL, "G-Line", 1, 1, 0 },
|
|
|
|
|
{ "kline", 'k', TKL_KILL, "K-Line", 1, 1, 0 },
|
|
|
|
|
{ "gzline", 'Z', TKL_ZAP | TKL_GLOBAL, "Global Z-Line", 1, 1, 1 },
|
|
|
|
|
{ "zline", 'z', TKL_ZAP, "Z-Line", 1, 1, 1 },
|
|
|
|
|
{ "spamfilter", 'F', TKL_SPAMF | TKL_GLOBAL, "Spamfilter", 1, 1, 0 },
|
|
|
|
|
{ "qline", 'Q', TKL_NAME | TKL_GLOBAL, "Q-Line", 1, 1, 0 },
|
|
|
|
|
{ "except", 'E', TKL_EXCEPTION | TKL_GLOBAL, "Exception", 1, 0, 0 },
|
|
|
|
|
{ "shun", 's', TKL_SHUN | TKL_GLOBAL, "Shun", 1, 1, 0 },
|
|
|
|
|
{ "local-qline", 'q', TKL_NAME, "Local Q-Line", 1, 0, 0 },
|
|
|
|
|
{ "local-exception", 'e', TKL_EXCEPTION, "Local Exception", 1, 0, 0 },
|
|
|
|
|
{ "local-spamfilter", 'f', TKL_SPAMF, "Local Spamfilter", 1, 0, 0 },
|
|
|
|
|
{ "blacklist", 'b', TKL_BLACKLIST, "Blacklist", 0, 1, 1 },
|
|
|
|
|
{ "connect-flood", 'c', TKL_CONNECT_FLOOD, "Connect flood", 0, 1, 0 },
|
|
|
|
|
{ "maxperip", 'm', TKL_MAXPERIP, "Max-per-IP", 0, 1, 0 },
|
|
|
|
|
{ "handshake-data-flood", 'd', TKL_HANDSHAKE_DATA_FLOOD, "Handshake data flood", 0, 1, 1 },
|
|
|
|
|
{ "antirandom", 'r', TKL_ANTIRANDOM, "Antirandom", 0, 1, 0 },
|
|
|
|
|
{ "antimixedutf8", '8', TKL_ANTIMIXEDUTF8, "Antimixedutf8", 0, 1, 0 },
|
|
|
|
|
{ "ban-version", 'v', TKL_BAN_VERSION, "Ban Version", 0, 1, 0 },
|
|
|
|
|
{ NULL, '\0', 0, NULL, 0, 0, 0 },
|
|
|
|
|
/* <config name> <letter> <TKL_xxx type> <logging name> <tkl option?> <exempt option?> <ip address only?> <TKLID Prefix> */
|
|
|
|
|
{ "gline", 'G', TKL_KILL | TKL_GLOBAL, "G-Line", 1, 1, 0, "G" },
|
|
|
|
|
{ "kline", 'k', TKL_KILL, "K-Line", 1, 1, 0, "K" },
|
|
|
|
|
{ "gzline", 'Z', TKL_ZAP | TKL_GLOBAL, "Global Z-Line", 1, 1, 1, "Z" },
|
|
|
|
|
{ "zline", 'z', TKL_ZAP, "Z-Line", 1, 1, 1, "Z" },
|
|
|
|
|
{ "spamfilter", 'F', TKL_SPAMF | TKL_GLOBAL, "Spamfilter", 1, 1, 0, "SPAM" },
|
|
|
|
|
{ "qline", 'Q', TKL_NAME | TKL_GLOBAL, "Q-Line", 1, 1, 0, "Q" },
|
|
|
|
|
{ "except", 'E', TKL_EXCEPTION | TKL_GLOBAL, "Exception", 1, 0, 0, "E" },
|
|
|
|
|
{ "shun", 's', TKL_SHUN | TKL_GLOBAL, "Shun", 1, 1, 0, "H" },
|
|
|
|
|
{ "local-qline", 'q', TKL_NAME, "Local Q-Line", 1, 0, 0, "Q" },
|
|
|
|
|
{ "local-exception", 'e', TKL_EXCEPTION, "Local Exception", 1, 0, 0, "E" },
|
|
|
|
|
{ "local-spamfilter", 'f', TKL_SPAMF, "Local Spamfilter", 1, 0, 0, "SPAM" },
|
|
|
|
|
{ "blacklist", 'b', TKL_BLACKLIST, "Blacklist", 0, 1, 1, NULL },
|
|
|
|
|
{ "connect-flood", 'c', TKL_CONNECT_FLOOD, "Connect flood", 0, 1, 0, NULL },
|
|
|
|
|
{ "maxperip", 'm', TKL_MAXPERIP, "Max-per-IP", 0, 1, 0, NULL },
|
|
|
|
|
{ "handshake-data-flood", 'd', TKL_HANDSHAKE_DATA_FLOOD, "Handshake data flood", 0, 1, 1, NULL },
|
|
|
|
|
{ "antirandom", 'r', TKL_ANTIRANDOM, "Antirandom", 0, 1, 0, NULL },
|
|
|
|
|
{ "antimixedutf8", '8', TKL_ANTIMIXEDUTF8, "Antimixedutf8", 0, 1, 0, NULL },
|
|
|
|
|
{ "ban-version", 'v', TKL_BAN_VERSION, "Ban Version", 0, 1, 0, NULL },
|
|
|
|
|
{ NULL, '\0', 0, NULL, 0, 0, 0, NULL },
|
|
|
|
|
};
|
|
|
|
|
#define ALL_VALID_EXCEPTION_TYPES "kline, gline, zline, gzline, spamfilter, shun, qline, blacklist, connect-flood, handshake-data-flood, antirandom, antimixedutf8, ban-version"
|
|
|
|
|
|
|
|
|
@@ -176,6 +182,27 @@ int confusables_spamfilters_present = 0; /**< Are any spamfilters with input-con
|
|
|
|
|
long previous_spamfilter_utf8 = 0;
|
|
|
|
|
static int firstboot = 0;
|
|
|
|
|
|
|
|
|
|
/* Config-file (and central) TKLs are freed and re-created on every /REHASH (and
|
|
|
|
|
* central spamfilters on every feed refresh) since they live in the config, not
|
|
|
|
|
* in the tkldb, which would otherwise reset their hit counters. To keep the
|
|
|
|
|
* counters we stash them here right before the old entry is freed, keyed by a
|
|
|
|
|
* stable per-TKL identity (see tkl_hits_key), and copy them back onto the freshly
|
|
|
|
|
* re-added entry with the same key. The list is carried across the rehash
|
|
|
|
|
* module-unload by Save/LoadPersistentPointer (same trick connthrottle uses).
|
|
|
|
|
* This is rehash/refresh-only; on a full restart the counters still reset.
|
|
|
|
|
* Covers config server bans, name bans (qlines) and spamfilters; not exceptions.
|
|
|
|
|
*/
|
|
|
|
|
typedef struct ConfigTKLHits ConfigTKLHits;
|
|
|
|
|
struct ConfigTKLHits {
|
|
|
|
|
ConfigTKLHits *prev, *next;
|
|
|
|
|
char key[256];
|
|
|
|
|
long long hits;
|
|
|
|
|
time_t lasthit;
|
|
|
|
|
long long hits_except; /* spamfilter only */
|
|
|
|
|
time_t lasthit_except; /* spamfilter only */
|
|
|
|
|
};
|
|
|
|
|
ConfigTKLHits *config_tkl_hits = NULL;
|
|
|
|
|
|
|
|
|
|
/* s2s-tkl/<field>: per-TKL fields carried over S2S as @s2s-tkl/<field>=<value> on the
|
|
|
|
|
* TKL command (modeled on s2s-md/). Old servers ignore unknown tags. To add a field,
|
|
|
|
|
* add a row here plus its serialize/unserialize pair (defined further down).
|
|
|
|
@@ -224,6 +251,8 @@ MOD_TEST()
|
|
|
|
|
EfunctionAddVoid(modinfo->handle, EFUNC_TKL_CHECK_LOCAL_REMOVE_SHUN, _tkl_check_local_remove_shun);
|
|
|
|
|
EfunctionAdd(modinfo->handle, EFUNC_FIND_TKLINE_MATCH, _find_tkline_match);
|
|
|
|
|
EfunctionAddVoid(modinfo->handle, EFUNC_TKL_HIT, _tkl_hit);
|
|
|
|
|
EfunctionAddVoid(modinfo->handle, EFUNC_REMOVE_CONFIG_TKLS, _remove_config_tkls);
|
|
|
|
|
EfunctionAddVoid(modinfo->handle, EFUNC_CONFIG_TKL_HITS_RESTORE, _config_tkl_hits_restore);
|
|
|
|
|
EfunctionAdd(modinfo->handle, EFUNC_FIND_SHUN, _find_shun);
|
|
|
|
|
EfunctionAdd(modinfo->handle, EFUNC_FIND_SPAMFILTER_USER, _find_spamfilter_user);
|
|
|
|
|
EfunctionAddPVoid(modinfo->handle, EFUNC_FIND_QLINE, TO_PVOIDFUNC(_find_qline));
|
|
|
|
@@ -260,6 +289,7 @@ MOD_INIT()
|
|
|
|
|
if (loop.booted == 0)
|
|
|
|
|
firstboot = 1;
|
|
|
|
|
LoadPersistentLong(modinfo, previous_spamfilter_utf8);
|
|
|
|
|
LoadPersistentPointer(modinfo, config_tkl_hits, config_tkl_hits_free);
|
|
|
|
|
HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, tkl_config_run_spamfilter);
|
|
|
|
|
HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, tkl_config_run_ban);
|
|
|
|
|
HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, tkl_config_run_except);
|
|
|
|
@@ -285,13 +315,21 @@ MOD_LOAD()
|
|
|
|
|
{
|
|
|
|
|
check_special_spamfilters_present();
|
|
|
|
|
check_set_spamfilter_utf8_setting_changed();
|
|
|
|
|
_config_tkl_hits_restore();
|
|
|
|
|
EventAdd(modinfo->handle, "tklexpire", tkl_check_expire, NULL, 5000, 0);
|
|
|
|
|
return MOD_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MOD_UNLOAD()
|
|
|
|
|
{
|
|
|
|
|
/* Free our config TKLs here (rather than from config_rehash) so the same pass
|
|
|
|
|
* can snapshot the spamfilter hit counters. Call the local impl: the snapshot
|
|
|
|
|
* store is a per-instance global, and we must write to ours (saved just below),
|
|
|
|
|
* not the new instance the efunc pointer now points to.
|
|
|
|
|
*/
|
|
|
|
|
_remove_config_tkls(TKL_FLAG_CONFIG);
|
|
|
|
|
SavePersistentLong(modinfo, previous_spamfilter_utf8);
|
|
|
|
|
SavePersistentPointer(modinfo, config_tkl_hits);
|
|
|
|
|
return MOD_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -1294,20 +1332,34 @@ void check_set_spamfilter_utf8_setting_changed(void)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* === TKL unique IDs ===
|
|
|
|
|
* Every TKL has a unique id (struct TKL.id): either assigned (a random id generated
|
|
|
|
|
* by the server that originates the entry, or an id supplied by an external setter
|
|
|
|
|
* such as services), or an empty string if none is known. A native (assigned-here)
|
|
|
|
|
* id is <prefix><10 x base32>, eg "G7K2MP9WQX3". It is carried over S2S in the
|
|
|
|
|
* s2s-tkl/id message tag and persisted by tkldb; there is no derivation.
|
|
|
|
|
* Every TKL typically has a unique id (tkl->id), such as "G7K2MP9WQX3" for a gline.
|
|
|
|
|
* See the comment below about TKL_GENERATED_ID_LEN for more info.
|
|
|
|
|
* These TKLIDs are communicated in S2S via @s2s-tkl/id mtag, and they
|
|
|
|
|
* also persist via tkldb.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/** Single-letter prefix for a native TKL id, derived from the TKL type.
|
|
|
|
|
* Uppercase, with global/local collapsed (eg gzline and zline both 'Z', spamfilter
|
|
|
|
|
* and local-spamfilter both 'F'). gline stays 'G' and kline 'K' as their letters differ.
|
|
|
|
|
/* The generated TKLID (not to be confused with the maximum allowed length of TKLID):
|
|
|
|
|
* This consists of the prefix + the random/hashed base32 chars. Obviously, the idea
|
|
|
|
|
* is that this will never clash, so we have to look at birthday attack collision rates:
|
|
|
|
|
* - Spamfilter: "SPAM" prefix + 7 chars = 35 bits = ~0.0015% for 1000 spamfilters.
|
|
|
|
|
* - All the rest: 1 letter prefix + 10 chars = 50 bits = ~0.0018% for 200k entries
|
|
|
|
|
* And obviously both the 1000 spamfilters and 200k GLINE/whatever are a rather
|
|
|
|
|
* absurd number of entries, but possible in odd/attack scenarios.
|
|
|
|
|
*/
|
|
|
|
|
static char tkl_id_prefix(int type)
|
|
|
|
|
#define TKL_GENERATED_ID_LEN 11
|
|
|
|
|
|
|
|
|
|
/** The TKL ID prefix string. Eg. "G" for a gline or "SPAM" for a spamfilter. */
|
|
|
|
|
static const char *tkl_id_prefix(int type)
|
|
|
|
|
{
|
|
|
|
|
return toupper(_tkl_typetochar(type));
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; tkl_types[i].config_name; i++)
|
|
|
|
|
if ((tkl_types[i].type == type) && tkl_types[i].id_prefix)
|
|
|
|
|
return tkl_types[i].id_prefix;
|
|
|
|
|
#ifdef DEBUGMODE
|
|
|
|
|
abort(); /* this should never happen */
|
|
|
|
|
#endif
|
|
|
|
|
return "?";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Find a TKL by its exact id (case-insensitive). Returns NULL on no match or empty id. */
|
|
|
|
@@ -1340,16 +1392,18 @@ static void tkl_generate_id(TKL *tkl)
|
|
|
|
|
{
|
|
|
|
|
/* Crockford base32 (no I/L/O/U) so the id survives being read aloud */
|
|
|
|
|
static const char b32[] = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
|
|
|
char prefix = tkl_id_prefix(tkl->type);
|
|
|
|
|
const char *prefix = tkl_id_prefix(tkl->type);
|
|
|
|
|
int prefixlen = strlen(prefix);
|
|
|
|
|
int nrandom = TKL_GENERATED_ID_LEN - prefixlen;
|
|
|
|
|
TKL *other;
|
|
|
|
|
int attempt, i;
|
|
|
|
|
|
|
|
|
|
for (attempt = 0; attempt < 16; attempt++)
|
|
|
|
|
{
|
|
|
|
|
tkl->id[0] = prefix;
|
|
|
|
|
for (i = 1; i <= 10; i++)
|
|
|
|
|
tkl->id[i] = b32[getrandom8() % 32];
|
|
|
|
|
tkl->id[i] = '\0';
|
|
|
|
|
strlcpy(tkl->id, prefix, sizeof(tkl->id));
|
|
|
|
|
for (i = 0; i < nrandom; i++)
|
|
|
|
|
tkl->id[prefixlen + i] = b32[getrandom8() % 32];
|
|
|
|
|
tkl->id[prefixlen + nrandom] = '\0';
|
|
|
|
|
other = find_tkl_by_id(tkl->id);
|
|
|
|
|
if (!other || (other == tkl))
|
|
|
|
|
return; /* unique (or only matches ourselves, if already linked) */
|
|
|
|
@@ -1474,6 +1528,8 @@ static const char *spamfilter_fallback_id(TKL *tkl)
|
|
|
|
|
* key just makes the hash deterministic and identical across servers. */
|
|
|
|
|
static const char key[16] = "UnrealIRCd rocks";
|
|
|
|
|
static char buf[16];
|
|
|
|
|
const char *prefix;
|
|
|
|
|
int prefixlen;
|
|
|
|
|
char content[8192];
|
|
|
|
|
char actbuf[2];
|
|
|
|
|
uint64_t h;
|
|
|
|
@@ -1489,13 +1545,15 @@ static const char *spamfilter_fallback_id(TKL *tkl)
|
|
|
|
|
|
|
|
|
|
h = siphash(content, key);
|
|
|
|
|
|
|
|
|
|
buf[0] = tkl_id_prefix(tkl->type);
|
|
|
|
|
for (i = 1; i <= 10; i++)
|
|
|
|
|
prefix = tkl_id_prefix(tkl->type);
|
|
|
|
|
prefixlen = strlen(prefix);
|
|
|
|
|
strlcpy(buf, prefix, sizeof(buf));
|
|
|
|
|
for (i = 0; i < TKL_GENERATED_ID_LEN - prefixlen; i++)
|
|
|
|
|
{
|
|
|
|
|
buf[i] = b32[h & 31];
|
|
|
|
|
buf[prefixlen + i] = b32[h & 31];
|
|
|
|
|
h >>= 5;
|
|
|
|
|
}
|
|
|
|
|
buf[i] = '\0';
|
|
|
|
|
buf[prefixlen + i] = '\0';
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -3502,6 +3560,176 @@ void _free_tkl(TKL *tkl)
|
|
|
|
|
safe_free(tkl);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Build a stable match key for a hit-bearing config/central TKL (server ban,
|
|
|
|
|
* name ban or spamfilter), used to pair an old entry with its rebuilt self across
|
|
|
|
|
* a rehash / feed refresh. Returns buf, or NULL for types we don't preserve
|
|
|
|
|
* (exceptions) or with no usable key. Format: "<typechar> <id|mask|name>", joined
|
|
|
|
|
* by a single space (these fields never contain spaces).
|
|
|
|
|
*/
|
|
|
|
|
char *tkl_hits_key(TKL *tkl, char *buf, size_t len)
|
|
|
|
|
{
|
|
|
|
|
char tmp[BUFSIZE];
|
|
|
|
|
char t = tkl_typetochar(tkl->type);
|
|
|
|
|
|
|
|
|
|
if (TKLIsSpamfilter(tkl) && tkl->id[0])
|
|
|
|
|
snprintf(buf, len, "%c %s", t, tkl->id);
|
|
|
|
|
else if (TKLIsServerBan(tkl))
|
|
|
|
|
snprintf(buf, len, "%c %s", t, tkl_uhost(tkl, tmp, sizeof(tmp), 0));
|
|
|
|
|
else if (TKLIsNameBan(tkl))
|
|
|
|
|
snprintf(buf, len, "%c %s", t, tkl->ptr.nameban->name);
|
|
|
|
|
else
|
|
|
|
|
return NULL;
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Stash a config/central TKL's hit counters before it is freed (from the removal
|
|
|
|
|
* loop in _remove_config_tkls), so they can be copied back onto the same entry
|
|
|
|
|
* after a rehash / feed refresh re-adds it. Covers server bans, name bans and
|
|
|
|
|
* spamfilters (whatever tkl_hits_key recognises); skips entries that never hit.
|
|
|
|
|
*/
|
|
|
|
|
void config_tkl_hits_snapshot(TKL *tkl)
|
|
|
|
|
{
|
|
|
|
|
ConfigTKLHits *e;
|
|
|
|
|
char key[256];
|
|
|
|
|
|
|
|
|
|
if (!(tkl->flags & (TKL_FLAG_CONFIG | TKL_FLAG_CENTRAL_SPAMFILTER)))
|
|
|
|
|
return;
|
|
|
|
|
if (!tkl_hits_key(tkl, key, sizeof(key)))
|
|
|
|
|
return; /* not a hit-bearing config type */
|
|
|
|
|
if (!tkl->hits && !tkl->lasthit &&
|
|
|
|
|
(!TKLIsSpamfilter(tkl) ||
|
|
|
|
|
(!tkl->ptr.spamfilter->hits_except && !tkl->ptr.spamfilter->lasthit_except)))
|
|
|
|
|
return; /* never hit, nothing to keep */
|
|
|
|
|
|
|
|
|
|
e = safe_alloc(sizeof(ConfigTKLHits));
|
|
|
|
|
strlcpy(e->key, key, sizeof(e->key));
|
|
|
|
|
e->hits = tkl->hits;
|
|
|
|
|
e->lasthit = tkl->lasthit;
|
|
|
|
|
if (TKLIsSpamfilter(tkl))
|
|
|
|
|
{
|
|
|
|
|
e->hits_except = tkl->ptr.spamfilter->hits_except;
|
|
|
|
|
e->lasthit_except = tkl->ptr.spamfilter->lasthit_except;
|
|
|
|
|
}
|
|
|
|
|
AddListItem(e, config_tkl_hits);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Free the whole snapshot list and forget it. */
|
|
|
|
|
void config_tkl_hits_free_all(void)
|
|
|
|
|
{
|
|
|
|
|
ConfigTKLHits *e, *e_next;
|
|
|
|
|
|
|
|
|
|
for (e = config_tkl_hits; e; e = e_next)
|
|
|
|
|
{
|
|
|
|
|
e_next = e->next;
|
|
|
|
|
safe_free(e);
|
|
|
|
|
}
|
|
|
|
|
config_tkl_hits = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** PersistentPointer free callback: used only if the snapshot is never reloaded
|
|
|
|
|
* (e.g. final module unload), otherwise MOD_LOAD consumes and frees it.
|
|
|
|
|
*/
|
|
|
|
|
void config_tkl_hits_free(ModData *m)
|
|
|
|
|
{
|
|
|
|
|
config_tkl_hits_free_all();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Copy stashed counters back onto one re-added TKL, matched by key. */
|
|
|
|
|
static void config_tkl_hits_restore_one(TKL *tkl)
|
|
|
|
|
{
|
|
|
|
|
ConfigTKLHits *e;
|
|
|
|
|
char key[256];
|
|
|
|
|
|
|
|
|
|
if (!(tkl->flags & (TKL_FLAG_CONFIG | TKL_FLAG_CENTRAL_SPAMFILTER)))
|
|
|
|
|
return;
|
|
|
|
|
if (!tkl_hits_key(tkl, key, sizeof(key)))
|
|
|
|
|
return;
|
|
|
|
|
for (e = config_tkl_hits; e; e = e->next)
|
|
|
|
|
{
|
|
|
|
|
if (!strcmp(e->key, key))
|
|
|
|
|
{
|
|
|
|
|
tkl->hits = e->hits;
|
|
|
|
|
tkl->lasthit = e->lasthit;
|
|
|
|
|
if (TKLIsSpamfilter(tkl))
|
|
|
|
|
{
|
|
|
|
|
tkl->ptr.spamfilter->hits_except = e->hits_except;
|
|
|
|
|
tkl->ptr.spamfilter->lasthit_except = e->lasthit_except;
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** After config/central TKLs have been re-added, copy the stashed hit counters
|
|
|
|
|
* back onto the ones with a matching key, then drop the whole stash. Called from
|
|
|
|
|
* MOD_LOAD (config rehash) and from the central feed refresh; a no-op on a normal
|
|
|
|
|
* boot (the stash is empty).
|
|
|
|
|
*/
|
|
|
|
|
void _config_tkl_hits_restore(void)
|
|
|
|
|
{
|
|
|
|
|
TKL *tkl;
|
|
|
|
|
int index, index2;
|
|
|
|
|
|
|
|
|
|
if (!config_tkl_hits)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
/* IP-hashed list (zlines, klines, glines) */
|
|
|
|
|
for (index = 0; index < TKLIPHASHLEN1; index++)
|
|
|
|
|
for (index2 = 0; index2 < TKLIPHASHLEN2; index2++)
|
|
|
|
|
for (tkl = tklines_ip_hash[index][index2]; tkl; tkl = tkl->next)
|
|
|
|
|
config_tkl_hits_restore_one(tkl);
|
|
|
|
|
|
|
|
|
|
/* Generic list (name bans, spamfilters, non-ip-hashed server bans) */
|
|
|
|
|
for (index = 0; index < TKLISTLEN; index++)
|
|
|
|
|
for (tkl = tklines[index]; tkl; tkl = tkl->next)
|
|
|
|
|
config_tkl_hits_restore_one(tkl);
|
|
|
|
|
|
|
|
|
|
/* Matched entries are applied; entries no longer in the config are discarded. */
|
|
|
|
|
config_tkl_hits_free_all();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Remove all TKLs with the given flag (TKL_FLAG_CONFIG or
|
|
|
|
|
* TKL_FLAG_CENTRAL_SPAMFILTER). For config/central spamfilters the hit counters
|
|
|
|
|
* are snapshotted first, so a later re-add can restore them. Lives here (rather
|
|
|
|
|
* than in conf.c) so the snapshot sits right next to the removal it rides on.
|
|
|
|
|
*/
|
|
|
|
|
void _remove_config_tkls(int flag)
|
|
|
|
|
{
|
|
|
|
|
TKL *tk, *tk_next;
|
|
|
|
|
int index, index2;
|
|
|
|
|
|
|
|
|
|
/* IP hashed TKL list */
|
|
|
|
|
for (index = 0; index < TKLIPHASHLEN1; index++)
|
|
|
|
|
{
|
|
|
|
|
for (index2 = 0; index2 < TKLIPHASHLEN2; index2++)
|
|
|
|
|
{
|
|
|
|
|
for (tk = tklines_ip_hash[index][index2]; tk; tk = tk_next)
|
|
|
|
|
{
|
|
|
|
|
tk_next = tk->next;
|
|
|
|
|
if (tk->flags & flag)
|
|
|
|
|
{
|
|
|
|
|
config_tkl_hits_snapshot(tk);
|
|
|
|
|
tkl_del_line(tk);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Generic TKL list */
|
|
|
|
|
for (index = 0; index < TKLISTLEN; index++)
|
|
|
|
|
{
|
|
|
|
|
for (tk = tklines[index]; tk; tk = tk_next)
|
|
|
|
|
{
|
|
|
|
|
tk_next = tk->next;
|
|
|
|
|
if (tk->flags & flag)
|
|
|
|
|
{
|
|
|
|
|
config_tkl_hits_snapshot(tk);
|
|
|
|
|
tkl_del_line(tk);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Delete a TKL entry from the list and free it.
|
|
|
|
|
* @param tkl The TKL entry.
|
|
|
|
|
*/
|
|
|
|
|