Rewrite/cleanup huge portion of TKL handling (16 files updated, but

src/modules/tkl.c is the main one).
Also move DB writing/reading functions to src/misc.c so they can be
removed out of channeldb and tkldb.

Important note to current tkldb users:
Unfortunately due to the major cleanup I had to remove upgrading
for previously saved tkl db files. That seemed not worth the effort
for maybe <15 current users or so. It also makes the tkldb code
a lot more cleaner. Otherwise it would be a huge mess.

Currently a FIXME item: spamfilter support in RMTKL.
This commit is contained in:
Bram Matthys 2019-08-31 15:24:52 +02:00
parent 0116c4f0d6
commit 8a6c84876e
No known key found for this signature in database
GPG key ID: BF8116B163EAAE98
16 changed files with 1391 additions and 1200 deletions

View file

@ -645,8 +645,20 @@ extern MODVAR int (*tkl_hash)(unsigned int c);
extern MODVAR char (*tkl_typetochar)(int type);
extern MODVAR int (*tkl_chartotype)(char c);
extern MODVAR char *(*tkl_type_string)(aTKline *tk);
extern MODVAR aTKline *(*tkl_add_line)(int type, char *usermask, char *hostmask, char *reason, char *setby,
time_t expire_at, time_t set_at, time_t spamf_tkl_duration, char *spamf_tkl_reason, MatchType spamf_match_type, int soft, int flags);
extern MODVAR aTKline *(*tkl_add_serverban)(int type, char *usermask, char *hostmask, char *reason, char *setby,
time_t expire_at, time_t set_at, int soft, int flags);
extern MODVAR aTKline *(*tkl_add_nameban)(int type, char *name, int hold, char *reason, char *setby,
time_t expire_at, time_t set_at, int flags);
extern MODVAR aTKline *(*tkl_add_spamfilter)(int type, unsigned short target, unsigned short action, aMatch *match, char *setby,
time_t expire_at, time_t set_at,
time_t spamf_tkl_duration, char *spamf_tkl_reason,
int flags);
extern MODVAR aTKline *(*find_tkl_serverban)(int type, char *usermask, char *hostmask, int softban);
extern MODVAR aTKline *(*find_tkl_nameban)(int type, char *name, int hold);
extern MODVAR aTKline *(*find_tkl_spamfilter)(int type, char *match_string, unsigned short action, unsigned short target);
extern MODVAR void (*sendnotice_tkl_del)(char *removed_by, aTKline *tkl);
extern MODVAR void (*sendnotice_tkl_add)(aTKline *tkl);
extern MODVAR void (*free_tkl)(aTKline *tkl);
extern MODVAR aTKline *(*tkl_del_line)(aTKline *tkl);
extern MODVAR void (*tkl_check_local_remove_shun)(aTKline *tmp);
extern MODVAR int (*find_tkline_match)(aClient *cptr, int skip_soft);
@ -861,3 +873,11 @@ extern int history_request(aClient *acptr, char *object, HistoryFilter *filter);
extern int history_del(char *object, int max_lines, long max_time);
extern int history_destroy(char *object);
extern void special_delayed_unloading(void);
extern int write_int64(FILE *fd, uint64_t t);
extern int write_int32(FILE *fd, uint32_t t);
extern int read_int64(FILE *fd, uint64_t *t);
extern int read_int32(FILE *fd, uint32_t *t);
extern int read_data(FILE *fd, void *buf, size_t len);
extern int write_data(FILE *fd, const void *buf, size_t len);
extern int write_str(FILE *fd, char *x);
extern int read_str(FILE *fd, char **x);

View file

@ -1170,7 +1170,7 @@ _UNREAL_ERROR(_hook_error_incompatible, "Incompatible hook function. Check argum
#define EFUNC_REGISTER_USER 7
#define EFUNC_TKL_HASH 8
#define EFUNC_TKL_TYPETOCHAR 9
#define EFUNC_TKL_ADD_LINE 10
#define EFUNC_TKL_ADD_SERVERBAN 10
#define EFUNC_TKL_DEL_LINE 11
#define EFUNC_TKL_CHECK_LOCAL_REMOVE_SHUN 12
#define EFUNC_TKL_EXPIRE 13
@ -1227,6 +1227,14 @@ _UNREAL_ERROR(_hook_error_incompatible, "Incompatible hook function. Check argum
#define EFUNC_BROADCAST_MD_GLOBALVAR_CMD 67
#define EFUNC_TKL_IP_HASH 68
#define EFUNC_TKL_IP_HASH_TYPE 69
#define EFUNC_TKL_ADD_NAMEBAN 70
#define EFUNC_TKL_ADD_SPAMFILTER 71
#define EFUNC_SENDNOTICE_TKL_ADD 72
#define EFUNC_SENDNOTICE_TKL_DEL 73
#define EFUNC_FREE_TKL 74
#define EFUNC_FIND_TKL_SERVERBAN 75
#define EFUNC_FIND_TKL_NAMEBAN 76
#define EFUNC_FIND_TKL_SPAMFILTER 77
/* Module flags */
#define MODFLAG_NONE 0x0000

View file

@ -67,8 +67,9 @@ typedef struct aloopStruct LoopStruct;
typedef struct ConfItem aConfItem;
typedef struct t_kline aTKline;
typedef struct _spamfilter Spamfilter;
typedef struct _serverban ServerBan;
typedef struct _nameban NameBan;
typedef struct _spamexcept SpamExcept;
/* New Config Stuff */
typedef struct _conditionalconfig ConditionalConfig;
typedef struct _configentry ConfigEntry;
typedef struct _configfile ConfigFile;
@ -711,11 +712,17 @@ struct Server {
#define TKL_ZAP 0x0002
#define TKL_GLOBAL 0x0004
#define TKL_SHUN 0x0008
#define TKL_QUIET 0x0010
#define TKL_SPAMF 0x0020
#define TKL_NICK 0x0040
#define TKL_NAME 0x0040
#define TKL_EXCEPT 0x0080
#define TKLIsServerBan(tkl) ((tkl)->type & (TKL_KILL|TKL_ZAP|TKL_SHUN))
#define TKLIsServerBanType(tpe) ((tpe) & (TKL_KILL|TKL_ZAP|TKL_SHUN))
#define TKLIsSpamfilter(tkl) ((tkl)->type & TKL_SPAMF)
#define TKLIsSpamfilterType(tpe) ((tpe) & TKL_SPAMF)
#define TKLIsNameBan(tkl) ((tkl)->type & TKL_NAME)
#define TKLIsNameBanType(tpe) ((tpe) & TKL_NAME)
#define SPAMF_CHANMSG 0x0001 /* c */
#define SPAMF_USERMSG 0x0002 /* p */
#define SPAMF_USERNOTICE 0x0004 /* n */
@ -730,14 +737,30 @@ struct Server {
/* Other flags only for function calls: */
#define SPAMFLAG_NOWARN 0x0001
/** Spamfilter sub-struct of TKL entry */
/** Spamfilter sub-struct of TKL entry (Spamfilter) */
struct _spamfilter {
unsigned short target;
unsigned short action; /**< Ban action, see BAN_ACT* */
aMatch *expr; /**< Spamfilter matcher */
aMatch *match; /**< Spamfilter matcher */
char *tkl_reason; /**< Reason to use for bans placed by this spamfilter, escaped by unreal_encodespace(). */
time_t tkl_duration; /**< Duration of bans placed by this spamfilter */
};
/** Server ban sub-struct of TKL entry (KLINE/GLINE/ZLINE/GZLINE/SHUN) */
struct _serverban {
char *usermask; /**< User mask */
char *hostmask; /**< Host mask */
unsigned short subtype; /**< See TKL_SUBTYPE_* */
char *reason; /**< Reason */
};
/* Name ban sub-struct of TKL entry (QLINE) */
struct _nameban {
char hold; /**< nickname hold is used by services */
char *name; /**< the nick or channel that is banned */
char *reason; /**< Reason */
};
#define TKL_SUBTYPE_NONE 0x0000
#define TKL_SUBTYPE_SOFT 0x0001 /* (require SASL) */
@ -747,17 +770,15 @@ struct _spamfilter {
struct t_kline {
aTKline *prev, *next;
unsigned int type; /**< TKL type. One of TKL_*, such as TKL_KILL|TKL_GLOBAL for gline */
unsigned short subtype; /* TKL subtype. For spamfilter one of SPAMF_*, otherwise TKL_SUBTYPE_* */
unsigned short flags; /**< One of TKL_FLAG_*, such as TKL_FLAG_CONFIG */
union {
Spamfilter *spamf;
} ptr;
char usermask[USERLEN + 3]; /**< User mask [different for spamfilter] */
char *hostmask; /**< Host mask [different for spamfilter] */
char *reason; /**< Reason [different for spamfilter] */
char *setby; /**< By who was this entry added */
time_t expire_at; /**< When this entry will expire */
char *set_by; /**< By who was this entry added */
time_t set_at; /**< When this entry was added */
time_t expire_at; /**< When this entry will expire */
union {
Spamfilter *spamfilter;
ServerBan *serverban;
NameBan *nameban;
} ptr;
};
/** A spamfilter except entry */

View file

@ -174,9 +174,9 @@ static NameValue _LogFlags[] = {
static NameValue ExceptTklFlags[] = {
{ 0, "all" },
{ TKL_GLOBAL|TKL_KILL, "gline" },
{ TKL_GLOBAL|TKL_NICK, "gqline" },
{ TKL_GLOBAL|TKL_NAME, "gqline" },
{ TKL_GLOBAL|TKL_ZAP, "gzline" },
{ TKL_NICK, "qline" },
{ TKL_NAME, "qline" },
{ TKL_GLOBAL|TKL_SHUN, "shun" }
};
@ -1644,14 +1644,14 @@ void postconf_defaults(void)
// which is more meaningful.
for (tk = tklines[tkl_hash('q')]; tk; tk = tk->next)
{
if (tk->type != TKL_NICK)
if (tk->type != TKL_NAME)
continue;
if (!tk->setby)
if (!tk->set_by)
{
if (me.name[0] != '\0')
tk->setby = strdup(me.name);
tk->set_by = strdup(me.name);
else
tk->setby = strdup(conf_me->name ? conf_me->name : "~server~");
tk->set_by = strdup(conf_me->name ? conf_me->name : "~server~");
}
}
@ -1659,19 +1659,19 @@ void postconf_defaults(void)
{
if (tk->type != TKL_SPAMF)
continue; /* global entry or something else.. */
if (!strcmp(tk->ptr.spamf->tkl_reason, "<internally added by ircd>"))
if (!strcmp(tk->ptr.spamfilter->tkl_reason, "<internally added by ircd>"))
{
MyFree(tk->ptr.spamf->tkl_reason);
tk->ptr.spamf->tkl_reason = strdup(encoded);
tk->ptr.spamf->tkl_duration = SPAMFILTER_BAN_TIME;
MyFree(tk->ptr.spamfilter->tkl_reason);
tk->ptr.spamfilter->tkl_reason = strdup(encoded);
tk->ptr.spamfilter->tkl_duration = SPAMFILTER_BAN_TIME;
}
/* This one is even more ugly, but our config crap is VERY confusing :[ */
if (!tk->setby)
if (!tk->set_by)
{
if (me.name[0] != '\0')
tk->setby = strdup(me.name);
tk->set_by = strdup(me.name);
else
tk->setby = strdup(conf_me->name ? conf_me->name : "~server~");
tk->set_by = strdup(conf_me->name ? conf_me->name : "~server~");
}
}
@ -6882,7 +6882,7 @@ int _conf_require(ConfigFile *conf, ConfigEntry *ce)
if (!reason)
reason = strdup("-");
tkl_add_line(TKL_KILL, usermask, hostmask, reason, "-config-", 0, TStime(), 0, NULL, 0, 1, TKL_FLAG_CONFIG);
tkl_add_serverban(TKL_KILL, usermask, hostmask, reason, "-config-", 0, TStime(), 1, TKL_FLAG_CONFIG);
safefree(usermask);
safefree(hostmask);
safefree(reason);

View file

@ -1418,3 +1418,162 @@ time_t server_time_to_unix_time(const char *tbuf)
ret = my_timegm(&tm);
return ret;
}
/** Write a 64 bit integer.
* @param fd File descriptor
* @param t The value to write
* @example write_int64(fd, 1234);
* @returns 1 on success, 0 on failure.
*/
int write_int64(FILE *fd, uint64_t t)
{
if (fwrite(&t, 1, sizeof(t), fd) < sizeof(t))
return 0;
return 1;
}
/** Write a 32 bit integer.
* @param fd File descriptor
* @param t The value to write
* @example write_int32(fd, 1234);
* @returns 1 on success, 0 on failure.
*/
int write_int32(FILE *fd, uint32_t t)
{
if (fwrite(&t, 1, sizeof(t), fd) < sizeof(t))
return 0;
return 1;
}
/** Read a 64 bit integer.
* @param fd File descriptor
* @param t The value to write
* @example read_int64(fd, &var);
* @returns 1 on success, 0 on failure.
*/
int read_int64(FILE *fd, uint64_t *t)
{
if (fread(t, 1, sizeof(uint64_t), fd) < sizeof(uint64_t))
return 0;
return 1;
}
/** Read a 64 bit integer.
* @param fd File descriptor
* @param t The value to write
* @example read_int64(fd, &var);
* @returns 1 on success, 0 on failure.
*/
int read_int32(FILE *fd, uint32_t *t)
{
if (fread(t, 1, sizeof(uint32_t), fd) < sizeof(uint32_t))
return 0;
return 1;
}
/** Read binary data.
* @param fd File descriptor
* @param buf Pointer to buffer
* @param len Size of buffer
* @example read_data(fd, buf, sizeof(buf));
* @notes This function is not used much, in most cases
* you should use read_str(), read_int32() or
* read_int64() instead.
* @returns 1 on success, 0 on failure.
*/
int read_data(FILE *fd, void *buf, size_t len)
{
if (fread(buf, 1, len, fd) < len)
return 0;
return 1;
}
/** Write binary data.
* @param fd File descriptor
* @param buf Pointer to buffer
* @param len Size of buffer
* @example write_data(fd, buf, sizeof(buf));
* @notes This function is not used much, in most cases
* you should use write_str(), write_int32() or
* write_int64() instead.
* @returns 1 on success, 0 on failure.
*/
int write_data(FILE *fd, const void *buf, size_t len)
{
if (fwrite(buf, 1, len, fd) < len)
return 0;
return 1;
}
/** Write a string.
* @param fd File descriptor
* @param x Pointer to string
* @example write_str(fd, "hello there!");
* @notes This function can write a string up to 65534
* characters, which should be plenty for usage
* in UnrealIRCd.
* Note that 'x' can safely be NULL.
* @returns 1 on success, 0 on failure.
*/
int write_str(FILE *fd, char *x)
{
uint16_t len;
len = x ? strlen(x) : 0xffff;
if (!write_data(fd, &len, sizeof(len)))
return 0;
if ((len > 0) && (len < 0xffff))
{
if (!write_data(fd, x, len))
return 0;
}
return 1;
}
/** Read a string.
* @param fd File descriptor
* @param x Pointer to string pointer
* @example write_str(fd, &str);
* @notes This function will allocate memory for the data
* and set the string pointer to this value.
* If a NULL pointer was written via write_str()
* then read_str() may also return a NULL pointer.
* @returns 1 on success, 0 on failure.
*/
int read_str(FILE *fd, char **x)
{
uint16_t len;
size_t size;
*x = NULL;
if (!read_data(fd, &len, sizeof(len)))
return 0;
if (len == 0xffff)
{
/* Magic value meaning NULL */
*x = NULL;
return 1;
}
if (len == 0)
{
/* 0 means empty string */
*x = strdup("");
return 1;
}
if (len > 10000)
return 0;
size = len;
*x = MyMallocEx(size + 1);
if (!read_data(fd, *x, size))
{
safefree(*x);
return 0;
}
(*x)[len] = 0;
return 1;
}

View file

@ -73,8 +73,14 @@ int (*tkl_hash)(unsigned int c);
char (*tkl_typetochar)(int type);
int (*tkl_chartotype)(char c);
char *(*tkl_type_string)(aTKline *tk);
aTKline *(*tkl_add_line)(int type, char *usermask, char *hostmask, char *reason, char *setby,
time_t expire_at, time_t set_at, time_t spamf_tkl_duration, char *spamf_tkl_reason, MatchType spamf_match_type, int soft, int flags);
aTKline *(*tkl_add_serverban)(int type, char *usermask, char *hostmask, char *reason, char *setby,
time_t expire_at, time_t set_at, int soft, int flags);
aTKline *(*tkl_add_nameban)(int type, char *name, int hold, char *reason, char *setby,
time_t expire_at, time_t set_at, int flags);
aTKline *(*tkl_add_spamfilter)(int type, unsigned short target, unsigned short action, aMatch *match, char *setby,
time_t expire_at, time_t set_at,
time_t spamf_tkl_duration, char *spamf_tkl_reason,
int flags);
aTKline *(*tkl_del_line)(aTKline *tkl);
void (*tkl_check_local_remove_shun)(aTKline *tmp);
int (*find_tkline_match)(aClient *cptr, int skip_soft);
@ -128,6 +134,12 @@ void (*broadcast_md_globalvar)(ModDataInfo *mdi, ModData *md);
void (*broadcast_md_globalvar_cmd)(aClient *except, aClient *sender, char *varname, char *value);
int (*tkl_ip_hash)(char *ip);
int (*tkl_ip_hash_type)(int type);
void (*sendnotice_tkl_del)(char *removed_by, aTKline *tkl);
void (*sendnotice_tkl_add)(aTKline *tkl);
void (*free_tkl)(aTKline *tkl);
aTKline *(*find_tkl_serverban)(int type, char *usermask, char *hostmask, int softban);
aTKline *(*find_tkl_nameban)(int type, char *name, int hold);
aTKline *(*find_tkl_spamfilter)(int type, char *match_string, unsigned short action, unsigned short target);
static const EfunctionsList efunction_table[MAXEFUNCTIONS] = {
/* 00 */ {NULL, NULL, NULL},
@ -140,7 +152,7 @@ static const EfunctionsList efunction_table[MAXEFUNCTIONS] = {
/* 07 */ {"register_user", (void *)&register_user, NULL},
/* 08 */ {"tkl_hash", (void *)&tkl_hash, NULL},
/* 09 */ {"tkl_typetochar", (void *)&tkl_typetochar, NULL},
/* 10 */ {"tkl_add_line", (void *)&tkl_add_line, NULL},
/* 10 */ {"tkl_add_serverban", (void *)&tkl_add_serverban, NULL},
/* 11 */ {"tkl_del_line", (void *)&tkl_del_line, NULL},
/* 12 */ {"tkl_check_local_remove_shun", (void *)&tkl_check_local_remove_shun, NULL},
/* 13 */ {NULL, NULL, NULL},
@ -200,7 +212,15 @@ static const EfunctionsList efunction_table[MAXEFUNCTIONS] = {
/* 67 */ {"broadcast_md_globalvar_cmd", (void *)&broadcast_md_globalvar_cmd, NULL},
/* 68 */ {"tkl_ip_hash", (void *)&tkl_ip_hash, NULL},
/* 69 */ {"tkl_ip_hash_type", (void *)&tkl_ip_hash_type, NULL},
/* 70 */ {NULL, NULL, NULL},
/* 70 */ {"tkl_add_nameban", (void *)&tkl_add_nameban, NULL},
/* 71 */ {"tkl_add_spamfilter", (void *)&tkl_add_spamfilter, NULL},
/* 72 */ {"sendnotice_tkl_add", (void *)&sendnotice_tkl_add, NULL},
/* 73 */ {"sendnotice_tkl_del", (void *)&sendnotice_tkl_del, NULL},
/* 74 */ {"free_tkl", (void *)&free_tkl, NULL},
/* 75 */ {"find_tkl_serverban", (void *)&find_tkl_serverban, NULL},
/* 76 */ {"find_tkl_nameban", (void *)&find_tkl_nameban, NULL},
/* 77 */ {"find_tkl_spamfilter", (void *)&find_tkl_spamfilter, NULL},
/* 78 */ {NULL, NULL, NULL},
};
#ifdef UNDERSCORE

View file

@ -580,21 +580,6 @@ MOD_UNLOAD(antirandom)
return MOD_SUCCESS;
}
/* Sends a message to all (local) opers AND logs to the ircdlog (as LOG_ERROR) */
static void multi_log(char *fmt, ...)
{
va_list vl;
static char buf[2048];
va_start(vl, fmt);
vsnprintf(buf, sizeof(buf), fmt, vl);
va_end(vl);
sendto_realops("%s", buf);
ircd_log(LOG_ERROR, "%s", buf);
}
static void free_config(void)
{
safefree(cfg.ban_reason);
@ -845,21 +830,21 @@ static int internal_getscore(char *str)
{
score += 5 + (digits - 5);
#ifdef DEBUGMODE
multi_log("score@'%s': MATCH for digits check", str);
sendto_ops_and_log("score@'%s': MATCH for digits check", str);
#endif
}
if (vowels >= 4)
{
score += 4 + (vowels - 4);
#ifdef DEBUGMODE
multi_log("score@'%s': MATCH for vowels check", str);
sendto_ops_and_log("score@'%s': MATCH for vowels check", str);
#endif
}
if (consonants >= 4)
{
score += 4 + (consonants - 4);
#ifdef DEBUGMODE
multi_log("score@'%s': MATCH for consonants check", str);
sendto_ops_and_log("score@'%s': MATCH for consonants check", str);
#endif
}
@ -870,7 +855,7 @@ static int internal_getscore(char *str)
{
score++; /* OK */
#ifdef DEBUGMODE
multi_log("score@'%s': MATCH for '%s[%s]' %c/%c/%c", str, t->two, t->rest,
sendto_ops_and_log("score@'%s': MATCH for '%s[%s]' %c/%c/%c", str, t->two, t->rest,
s[0], s[1], s[2]);
#endif
}
@ -934,7 +919,7 @@ static int get_spam_score(aClient *sptr)
((tv_beta.tv_sec - tv_alpha.tv_sec) * 1000000) + (tv_beta.tv_usec - tv_alpha.tv_usec));
#endif
#ifdef DEBUGMODE
multi_log("got score: %d/%d/%d = %d",
sendto_ops_and_log("got score: %d/%d/%d = %d",
nscore, uscore, gscore, score);
#endif
@ -979,12 +964,12 @@ int antirandom_preconnect(aClient *sptr)
{
if (cfg.ban_action == BAN_ACT_WARN)
{
multi_log("[antirandom] would have denied access to user with score %d: %s!%s@%s:%s",
sendto_ops_and_log("[antirandom] would have denied access to user with score %d: %s!%s@%s:%s",
score, sptr->name, sptr->user->username, sptr->user->realhost, sptr->info);
return 0;
}
if (cfg.show_failedconnects)
multi_log("[antirandom] denied access to user with score %d: %s!%s@%s:%s",
sendto_ops_and_log("[antirandom] denied access to user with score %d: %s!%s@%s:%s",
score, sptr->name, sptr->user->username, sptr->user->realhost, sptr->info);
return place_host_ban(sptr, cfg.ban_action, cfg.ban_reason, cfg.ban_time);
}

View file

@ -448,16 +448,19 @@ int authprompt_place_host_ban(aClient *sptr, int action, char *reason, long dura
}
/** Called upon "check for KLINE/GLINE" */
int authprompt_find_tkline_match(aClient *sptr, aTKline *tk)
int authprompt_find_tkline_match(aClient *sptr, aTKline *tkl)
{
/* If it's a soft-xx action and the user is not logged in
* and the user is not yet online, then we will handle this user.
*/
if ((tk->subtype & TKL_SUBTYPE_SOFT) && !IsLoggedIn(sptr) && !IsPerson(sptr))
if (TKLIsServerBan(tkl) &&
(tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT) &&
!IsLoggedIn(sptr) &&
!IsPerson(sptr))
{
/* Send ban reason */
if (tk->reason)
sendnotice(sptr, "%s", tk->reason);
if (tkl->ptr.serverban->reason)
sendnotice(sptr, "%s", tkl->ptr.serverban->reason);
/* And tag the user */
authprompt_tag_as_auth_required(sptr);

View file

@ -51,12 +51,6 @@ EVENT(write_channeldb_evt);
int write_channeldb(void);
int write_channel_entry(FILE *fd, const char *tmpfname, aChannel *chptr);
int read_channeldb(void);
static inline int read_data(FILE *fd, void *buf, size_t len);
static inline int write_data(FILE *fd, void *buf, size_t len);
static inline int write_int64(FILE *fd, uint64_t t);
static inline int write_int32(FILE *fd, uint32_t t);
static int write_str(FILE *fd, char *x);
static int read_str(FILE *fd, char **x);
static void set_channel_mode(aChannel *chptr, char *modes, char *parameters);
// Globals
@ -479,74 +473,6 @@ int read_channeldb(void)
#undef FreeChannelEntry
#undef R_SAFE
static inline int read_data(FILE *fd, void *buf, size_t len)
{
if (fread(buf, 1, len, fd) < len)
return 0;
return 1;
}
static inline int write_data(FILE *fd, void *buf, size_t len)
{
if (fwrite(buf, 1, len, fd) < len)
return 0;
return 1;
}
static inline int write_int64(FILE *fd, uint64_t t)
{
if (fwrite(&t, 1, sizeof(t), fd) < sizeof(t))
return 0;
return 1;
}
static inline int write_int32(FILE *fd, uint32_t t)
{
if (fwrite(&t, 1, sizeof(t), fd) < sizeof(t))
return 0;
return 1;
}
static int write_str(FILE *fd, char *x)
{
uint16_t len = (x ? strlen(x) : 0);
if (!write_data(fd, &len, sizeof(len)))
return 0;
if (len)
{
if (!write_data(fd, x, len))
return 0;
}
return 1;
}
static int read_str(FILE *fd, char **x)
{
uint16_t len;
size_t size;
*x = NULL;
if (!read_data(fd, &len, sizeof(len)))
return 0;
if (!len)
{
*x = strdup("");
return 1;
}
size = len;
*x = MyMallocEx(size + 1);
if (!read_data(fd, *x, size))
{
MyFree(*x);
*x = NULL;
return 0;
}
(*x)[len] = 0;
return 1;
}
static void set_channel_mode(aChannel *chptr, char *modes, char *parameters)
{
char buf[512];

View file

@ -530,7 +530,7 @@ int _do_join(aClient *cptr, aClient *sptr, int parc, char *parv[])
}
if (ValidatePermissionsForPath("immune:server-ban:deny-channel",sptr,NULL,NULL,NULL) && (tklban = find_qline(sptr, name, &ishold)))
{
sendnumeric(sptr, ERR_FORBIDDENCHANNEL, name, tklban->reason);
sendnumeric(sptr, ERR_FORBIDDENCHANNEL, name, tklban->ptr.nameban->reason);
continue;
}
/* ugly set::spamfilter::virus-help-channel-deny hack.. */

View file

@ -743,13 +743,13 @@ CMD_FUNC(m_nick)
{
if (ishold)
{
sendnumeric(sptr, ERR_ERRONEUSNICKNAME, nick, tklban->reason);
sendnumeric(sptr, ERR_ERRONEUSNICKNAME, nick, tklban->ptr.nameban->reason);
return 0;
}
if (!ValidatePermissionsForPath("immune:server-ban:ban-nick",sptr,NULL,NULL,nick))
{
sptr->local->since += 4; /* lag them up */
sendnumeric(sptr, ERR_ERRONEUSNICKNAME, nick, tklban->reason);
sendnumeric(sptr, ERR_ERRONEUSNICKNAME, nick, tklban->ptr.nameban->reason);
sendto_snomask(SNO_QLINE, "Forbidding Q-lined nick %s from %s.",
nick, get_client_name(cptr, FALSE));
return 0; /* NICK message ignored */

View file

@ -73,7 +73,7 @@ int _check_banned(aClient *cptr, int exitflags)
if ((tk = find_tkline_match_zap(cptr)))
{
return banned_client(cptr, "Z-Lined", tk->reason, (tk->type & TKL_GLOBAL)?1:0, exitflags);
return banned_client(cptr, "Z-Lined", tk->ptr.serverban->reason, (tk->type & TKL_GLOBAL)?1:0, exitflags);
}
else
{

View file

@ -49,7 +49,7 @@ TKLType tkl_types[] = {
{ TKL_KILL | TKL_GLOBAL, 'G', "G-Line", "server-ban:gline:remove" },
{ TKL_ZAP | TKL_GLOBAL, 'Z', "Global Z-Line", "server-ban:zline:global:remove" },
{ TKL_SHUN | TKL_GLOBAL, 's', "Shun", "server-ban:shun:remove" },
{ TKL_SPAMF | TKL_GLOBAL, 'F', "Global Spamfilter", "server-ban:spamfilter:remove" },
// { TKL_SPAMF | TKL_GLOBAL, 'F', "Global Spamfilter", "server-ban:spamfilter:remove" }, TODO: re-add spamfilter support
{ 0, 0, "Unknown *-Line", 0 },
};
@ -71,8 +71,8 @@ static char *rmtkl_help[] = {
" [remove \037all\037 supported TKLs except spamfilters]",
" - \002/rmtkl *@*.mx GZ\002 * -skipperm",
" [remove all Mexican G/Z-Lines while skipping over permanent ones]",
" - \002/rmtkl * * *Zombie*\002",
" [remove all non-spamfilter bans having \037Zombie\037 in the reason field]",
/* " - \002/rmtkl * * *Zombie*\002",
" [remove all non-spamfilter bans having \037Zombie\037 in the reason field]", TODO: re-add spamfilter support */
"*** \002End of help\002 ***",
NULL
};
@ -143,66 +143,37 @@ int rmtkl_tryremove(aClient *sptr, aClient *cptr, TKLType *tkltype, aTKline *tkl
return 0;
// Let's not touch Q-Lines
if (tkl->type & TKL_NICK)
if (tkl->type & TKL_NAME)
return 0;
if (tkl->type & TKL_SPAMF)
{
// Skip default spamfilters and ones added through configs, 'f' is the proper flag for that
if (tkltype->flag == 'f')
return 0;
/* Don't touch TKL's that were added through config */
if (tkl->flags & TKL_FLAG_CONFIG)
return 0;
if (TKLIsSpamfilter(tkl))
{
#if 0
//FIXME: re-add spamfilter support
// Is a spamfilter added through IRC, we can remove this if the "user" mask matches the reason
if (!match_simple(uhmask, tkl->reason))
return 0;
#endif
} else
if (TKLIsServerBan(tkl))
{
if (!match_simple(uhmask, make_user_host(tkl->usermask, tkl->hostmask)))
if (!match_simple(uhmask, make_user_host(tkl->ptr.serverban->usermask, tkl->ptr.serverban->hostmask)))
return 0;
if (commentmask && !match_simple(commentmask, tkl->reason))
if (commentmask && !match_simple(commentmask, tkl->ptr.serverban->reason))
return 0;
}
} else
return 0;
if (skipperm && tkl->expire_at == 0)
return 0;
// FIXME: use sendnotice_tkl_del() once it exists !!
// Convert "set at" timestamp to human readable time, we'll try to remove the TKL even if this fails (might be a bogus entry in that case)
if (tkl->set_at)
{
if ((timeret = short_date(tkl->set_at, NULL)))
strlcpy(gmt, timeret, sizeof(gmt));
else
strlcpy(gmt, "<INVALID TIMESTAMP>", sizeof(gmt));
}
// Spamfilters have a slightly different format
if (tkl->type & TKL_SPAMF)
{
if (!silent)
{
sendto_snomask(SNO_TKL, "%s removed %s [%s] %s (set at %s " "- reason: %s)",
sptr->name, tkltype->txt, banact_valtostring(tkl->ptr.spamf->action),
tkl->reason, gmt, tkl->ptr.spamf->tkl_reason);
}
ircd_log(LOG_TKL, "%s removed %s [%s] %s (set at %s " "- reason: %s)",
sptr->name, tkltype->txt, banact_valtostring(tkl->ptr.spamf->action),
tkl->reason, gmt, tkl->ptr.spamf->tkl_reason);
} else
{
if (!silent)
{
sendto_snomask(SNO_TKL, "%s removed %s %s@%s (set at %s - reason: %s)",
sptr->name, tkltype->txt, tkl->usermask, tkl->hostmask,
gmt, tkl->reason);
}
ircd_log(LOG_TKL, "%s removed %s %s@%s (set at %s - reason: %s)",
sptr->name, tkltype->txt, tkl->usermask, tkl->hostmask,
gmt, tkl->reason);
}
if (!silent)
sendnotice_tkl_del(sptr->name, tkl);
RunHook5(HOOKTYPE_TKL_DEL, cptr, sptr, tkl, NULL, NULL);

View file

@ -587,7 +587,7 @@ int stats_port(aClient *sptr, char *para)
int stats_bannick(aClient *sptr, char *para)
{
tkl_stats(sptr, TKL_NICK, para);
tkl_stats(sptr, TKL_NAME, para);
return 0;
}
@ -1000,7 +1000,7 @@ int stats_banrealname(aClient *sptr, char *para)
int stats_sqline(aClient *sptr, char *para)
{
tkl_stats(sptr, TKL_NICK|TKL_GLOBAL, para);
tkl_stats(sptr, TKL_NAME|TKL_GLOBAL, para);
return 0;
}

File diff suppressed because it is too large Load diff

View file

@ -27,7 +27,8 @@ ModuleHeader MOD_HEADER(tkldb) = {
"unrealircd-5",
};
#define TKL_DB_VERSION 1100
#define TKL_DB_MAGIC 0x10101010
#define TKL_DB_VERSION 4999
#define TKL_DB_SAVE_EVERY 299
#ifdef DEBUGMODE
@ -45,15 +46,8 @@ ModuleHeader MOD_HEADER(tkldb) = {
#define FreeTKLRead() \
do { \
/* Some of these might be NULL */ \
safefree(tkltype); \
safefree(usermask); \
safefree(hostmask); \
safefree(reason); \
safefree(setby); \
safefree(spamf_check); \
safefree(spamf_expr); \
safefree(spamf_matchtype); \
safefree(spamf_tkl_reason); \
if (tkl) \
free_tkl(tkl); \
} while(0)
#define R_SAFE(x) \
@ -93,20 +87,12 @@ EVENT(write_tkldb_evt);
int write_tkldb(void);
int write_tkline(FILE *fd, const char *tmpfname, aTKline *tkl);
int read_tkldb(void);
static inline int read_data(FILE *fd, void *buf, size_t len);
static inline int write_data(FILE *fd, void *buf, size_t len);
static int write_str(FILE *fd, char *x);
static int read_str(FILE *fd, char **x);
// Globals
static ModDataInfo *tkldb_md;
static uint32_t tkl_db_version = TKL_DB_VERSION;
const uint32_t tkl_db_version = TKL_DB_VERSION;
struct cfgstruct {
char *database;
// Stored .db might still work with flags instead of actual values (will be corrected on next write)
// This backport stuff will eventually be removed ;]
unsigned int backport_tkl1000;
};
static struct cfgstruct cfg;
@ -177,7 +163,6 @@ void setcfg(void)
// Default: data/tkl.db
cfg.database = strdup("tkl.db");
convert_to_absolute_path(&cfg.database, PERMDATADIR);
cfg.backport_tkl1000 = 0;
}
void freecfg(void)
@ -262,6 +247,7 @@ int write_tkldb(void)
return 0;
}
W_SAFE(write_int32(fd, TKL_DB_MAGIC));
W_SAFE(write_data(fd, &tkl_db_version, sizeof(tkl_db_version)));
// Count the *-Lines
@ -337,83 +323,68 @@ int write_tkldb(void)
return 1;
}
/** Write a TKL entry */
int write_tkline(FILE *fd, const char *tmpfname, aTKline *tkl)
{
// Since we can't just write 'tkl' in its entirety, we have to get the relevant variables instead
// These will be used to reconstruct the proper internal m_tkl() call ;]
char tkltype;
char usermask_subtype[256]; // Might need to prefix something to usermask
char spamf_action; // Storing the action not as unsigned short, but as char is more reliable for the future
uint64_t expire_at, set_at, spamf_tkl_duration; // To prevent 32 vs 64 bit incompatibilies regarding the TS data type(def)
char buf[256];
/* First, write the common attributes */
tkltype = tkl_typetochar(tkl->type);
W_SAFE(write_data(fd, &tkltype, sizeof(tkltype))); // TKL char
// Might be a softban
if (!tkl->ptr.spamf && (tkl->subtype & TKL_SUBTYPE_SOFT))
W_SAFE(write_str(fd, tkl->set_by));
W_SAFE(write_int64(fd, tkl->set_at));
W_SAFE(write_int64(fd, tkl->expire_at));
if (TKLIsServerBan(tkl))
{
snprintf(usermask_subtype, sizeof(usermask_subtype), "%%%s", tkl->usermask);
W_SAFE(write_str(fd, usermask_subtype)); // User mask (targets for spamfilter, like 'cpnNPqdatu'), includes % for softbans
char *usermask = tkl->ptr.serverban->usermask;
if (tkl->ptr.serverban->subtype & TKL_SUBTYPE_SOFT)
{
snprintf(buf, sizeof(buf), "%%%s", tkl->ptr.serverban->usermask);
usermask = buf;
}
W_SAFE(write_str(fd, usermask));
W_SAFE(write_str(fd, tkl->ptr.serverban->hostmask));
W_SAFE(write_str(fd, tkl->ptr.serverban->reason));
} else
if (TKLIsNameBan(tkl))
{
W_SAFE(write_str(fd, tkl->usermask));
char *hold = tkl->ptr.nameban->hold ? "H" : "*";
W_SAFE(write_str(fd, hold));
W_SAFE(write_str(fd, tkl->ptr.nameban->name));
W_SAFE(write_str(fd, tkl->ptr.nameban->reason));
} else
if (TKLIsSpamfilter(tkl))
{
char *match_type = unreal_match_method_valtostr(tkl->ptr.spamfilter->match->type);
char *target = spamfilter_target_inttostring(tkl->ptr.spamfilter->target);
char action = banact_valtochar(tkl->ptr.spamfilter->action);
W_SAFE(write_str(fd, match_type));
W_SAFE(write_str(fd, tkl->ptr.spamfilter->match->str));
W_SAFE(write_str(fd, target));
W_SAFE(write_data(fd, &action, sizeof(action)));
W_SAFE(write_str(fd, tkl->ptr.spamfilter->tkl_reason));
W_SAFE(write_int64(fd, tkl->ptr.spamfilter->tkl_duration));
}
W_SAFE(write_str(fd, tkl->hostmask)); // Host mask (action for spamfilter, like 'block')
W_SAFE(write_str(fd, tkl->reason)); // Ban reason (TKL time for spamfilters, in case of *-Line actions)
W_SAFE(write_str(fd, tkl->setby));
expire_at = tkl->expire_at;
set_at = tkl->set_at;
W_SAFE(write_data(fd, &expire_at, sizeof(expire_at)));
W_SAFE(write_data(fd, &set_at, sizeof(set_at)));
if (tkl->ptr.spamf)
{
W_SAFE(write_str(fd, "SPAMF")); // Write a string so we know to expect more when reading the DB
spamf_action = banact_valtochar(tkl->ptr.spamf->action); // Block, GZ-Line, etc; also refer to BAN_ACT_*
W_SAFE(write_data(fd, &spamf_action, sizeof(spamf_action)));
W_SAFE(write_str(fd, tkl->ptr.spamf->tkl_reason));
spamf_tkl_duration = tkl->ptr.spamf->tkl_duration;
W_SAFE(write_data(fd, &spamf_tkl_duration, sizeof(spamf_tkl_duration)));
W_SAFE(write_str(fd, tkl->ptr.spamf->expr->str)); // Actual expression/regex/etc
// Expression type (simple/PCRE), see also enum MatchType
if (tkl->ptr.spamf->expr->type == MATCH_PCRE_REGEX)
W_SAFE(write_str(fd, "regex"));
else
W_SAFE(write_str(fd, "simple"));
} else
{
W_SAFE(write_str(fd, "NOSPAMF"));
}
return 1;
}
/** Read all entries from the TKL db */
int read_tkldb(void)
{
FILE *fd;
uint64_t i;
aTKline *tkl = NULL;
uint32_t magic = 0;
uint64_t cnt;
uint64_t tklcount = 0;
size_t tklcount_tkl1000 = 0;
uint32_t version;
int added = 0;
int expired = 0;
int added_cnt = 0;
char c;
// Variables for all TKL types
// Some of them need to be declared and NULL initialised early due to the macro FreeTKLRead() being used by R_SAFE() on error
char *tkltype = NULL;
char *usermask = NULL;
char *hostmask = NULL;
char *reason = NULL;
char *setby = NULL;
// Some stuff related to spamfilters
char *spamf_check = NULL;
char *spamf_expr = NULL;
char *spamf_matchtype = NULL;
char *spamf_tkl_reason = NULL;
char *str;
#ifdef BENCHMARK
struct timeval tv_alpha, tv_beta;
@ -421,9 +392,6 @@ int read_tkldb(void)
gettimeofday(&tv_alpha, NULL);
#endif
ircd_log(LOG_ERROR, "[tkldb] Reading stored *-Lines from '%s'", cfg.database);
sendto_realops("[tkldb] Reading stored *-Lines from '%s'", cfg.database); // Probably won't be seen ever, but just in case ;]
fd = fopen(cfg.database, "rb");
if (!fd)
{
@ -438,240 +406,224 @@ int read_tkldb(void)
}
}
R_SAFE(read_data(fd, &version, sizeof(version)));
if (version > tkl_db_version)
/* The database starts with a "magic value" - unless it's some old version or corrupt */
R_SAFE(read_data(fd, &magic, sizeof(magic)));
if (magic != TKL_DB_MAGIC)
{
// Older DBs should still work with newer versions of this module
config_warn("[tkldb] Database '%s' has a wrong version: expected it to be <= %u but got %u instead", cfg.database, tkl_db_version, version);
if (fclose(fd) != 0)
config_warn("[tkldb] Got an error when trying to close database file '%s' (possible corruption occurred): %s", cfg.database, strerror(errno));
config_error("[tkldb] Database '%s' uses an old and unsupported format OR is corrupt", cfg.database);
config_status("If you are upgrading from UnrealIRCd 4 (or 5.0.0-alpha1) then we suggest you to "
"delete the existing database. Just keep at least 1 server linked during the upgrade "
"process to preserve your global *LINES and Spamfilters.");
fclose(fd);
return 0;
}
cfg.backport_tkl1000 = (version <= 1000 ? 1 : 0);
if (cfg.backport_tkl1000)
/* Now do a version check */
R_SAFE(read_data(fd, &version, sizeof(version)));
if (version < 4999)
{
R_SAFE(read_data(fd, &tklcount_tkl1000, sizeof(tklcount_tkl1000)));
tklcount = tklcount_tkl1000;
} else
config_error("[tkldb] Database '%s' uses an unsupport - possibly old - format (%ld).", cfg.database, (long)version);
fclose(fd);
return 0;
}
if (version > tkl_db_version)
{
R_SAFE(read_data(fd, &tklcount, sizeof(tklcount)));
config_warn("[tkldb] Database '%s' has version %lu while we only support %lu. Did you just downgrade UnrealIRCd? Sorry this is not suported",
cfg.database, (unsigned long)tkl_db_version, (unsigned long)version);
fclose(fd);
return 0;
}
for (i = 1; i <= tklcount; i++)
R_SAFE(read_data(fd, &tklcount, sizeof(tklcount)));
for (cnt = 0; cnt < tklcount; cnt++)
{
int type;
unsigned short subtype;
int parc = 0;
int do_not_add = 0;
// Variables for all TKL types
usermask = NULL;
hostmask = NULL;
reason = NULL;
setby = NULL;
char tklflag;
tkltype = NULL;
uint64_t expire_at, set_at;
time_t expire_at_tkl1000, set_at_tkl1000;
char setTime[100], expTime[100], spamfTime[100];
tkl = MyMallocEx(sizeof(aTKline));
// Some stuff related to spamfilters
spamf_check = NULL;
int spamf = 0;
char spamf_action;
unsigned short spamf_actionval;
spamf_tkl_reason = NULL;
time_t spamf_tkl_duration_tkl1000;
uint64_t spamf_tkl_duration;
spamf_expr = NULL;
MatchType matchtype;
spamf_matchtype = NULL;
int doadd = 1;
aTKline *tkl;
char *tkllayer[13] = { // Args for m_tkl()
me.name, // 0: Server name
"+", // 1: Direction
NULL, // 2: Type, like G
NULL, // 3: User mask (targets for spamfilter)
NULL, // 4: Host mask (action for spamfilter)
NULL, // 5: Set by who
NULL, // 6: Expiration time
NULL, // 7: Set-at time
NULL, // 8: Reason (TKL time for spamfilters, in case of *-Line actions)
NULL, // 9: Spamfilter only: TKL reason (w/ underscores and all)
NULL, // 10: Spamfilter only: Match type (simple/regex)
NULL, // 11: Spamfilter only: Match string/regex
NULL, // 12: Some functions rely on the post-last entry being NULL =]
};
if (cfg.backport_tkl1000)
{
R_SAFE(read_data(fd, &type, sizeof(type)));
tklflag = tkl_typetochar(type);
tkltype = MyMallocEx(2);
tkltype[0] = tklflag;
tkltype[1] = '\0';
R_SAFE(read_data(fd, &subtype, sizeof(subtype))); // Subtype is kinda redundant so we're not using it past v1000 anymore
}
else {
// No need for tkl_typetochar() on read anymore
R_SAFE(read_data(fd, &tklflag, sizeof(tklflag)));
tkltype = MyMallocEx(2);
tkltype[0] = tklflag;
tkltype[1] = '\0';
}
R_SAFE(read_str(fd, &usermask));
R_SAFE(read_str(fd, &hostmask));
R_SAFE(read_str(fd, &reason));
R_SAFE(read_str(fd, &setby));
if (cfg.backport_tkl1000)
{
R_SAFE(read_data(fd, &expire_at_tkl1000, sizeof(expire_at_tkl1000)));
R_SAFE(read_data(fd, &set_at_tkl1000, sizeof(set_at_tkl1000)));
expire_at = expire_at_tkl1000;
set_at = set_at_tkl1000;
}
else {
R_SAFE(read_data(fd, &expire_at, sizeof(expire_at)));
R_SAFE(read_data(fd, &set_at, sizeof(set_at)));
}
R_SAFE(read_str(fd, &spamf_check));
if (!strcmp(spamf_check, "SPAMF"))
{
spamf = 1;
if (cfg.backport_tkl1000)
{
R_SAFE(read_data(fd, &spamf_actionval, sizeof(spamf_actionval)));
// FIXME: BUG: spamf_action is not set
} else {
R_SAFE(read_data(fd, &spamf_action, sizeof(spamf_action)));
spamf_actionval = banact_chartoval(spamf_action);
}
R_SAFE(read_str(fd, &spamf_tkl_reason));
if (cfg.backport_tkl1000)
{
R_SAFE(read_data(fd, &spamf_tkl_duration_tkl1000, sizeof(spamf_tkl_duration_tkl1000)));
spamf_tkl_duration = spamf_tkl_duration_tkl1000;
} else {
R_SAFE(read_data(fd, &spamf_tkl_duration, sizeof(spamf_tkl_duration)));
}
R_SAFE(read_str(fd, &spamf_expr));
if (cfg.backport_tkl1000)
{
R_SAFE(read_data(fd, &matchtype, sizeof(matchtype)));
if (matchtype == MATCH_PCRE_REGEX)
spamf_matchtype = strdup("regex");
else
spamf_matchtype = strdup("simple");
}
else {
// We have better compatibility by just using the strings, since its MATCH_* counterpart might just change in value someday
R_SAFE(read_str(fd, &spamf_matchtype));
if (!strcmp(spamf_matchtype, "regex"))
matchtype = MATCH_PCRE_REGEX;
else
matchtype = MATCH_SIMPLE;
}
}
// v1000 still stored local Q-Lines and spamfilters, but those are either added through a .conf or already built-in
if (strchr("qf", tklflag))
/* First, fetch the TKL type.. */
R_SAFE(read_data(fd, &c, sizeof(c)));
tkl->type = tkl_chartotype(c);
if (!tkl->type)
{
/* We can't continue reading the DB if we don't know the TKL type,
* since we don't know how long the entry will be, we can't skip it.
* This is "impossible" anyway, unless we some day remove a TKL type
* in core UnrealIRCd. In which case we should add some skipping code
* here to gracefully handle that situation ;)
*/
config_error("[tkldb] Invalid type '%c' encountered - STOPPED READING DATABASE!", tkl->type);
FreeTKLRead();
continue;
break; /* we MUST stop reading */
}
// Don't add the TKL if it's expired
if (expire_at != 0 && expire_at <= TStime())
/* Read the common types (same for all TKLs) */
R_SAFE(read_str(fd, &tkl->set_by));
R_SAFE(read_int64(fd, &tkl->set_at));
R_SAFE(read_int64(fd, &tkl->expire_at));
/* Save some CPU... if it's already expired then don't bother adding */
if (tkl->expire_at != 0 && tkl->expire_at <= TStime())
do_not_add = 1;
/* Now handle all the specific types */
if (TKLIsServerBan(tkl))
{
#ifdef DEBUGMODE
ircd_log(LOG_ERROR, "[tkldb] Not re-adding expired %c:-Line '%s@%s' [%s]", tklflag, usermask, hostmask, reason);
sendto_realops("[tkldb] Not re-adding expired %c-Line '%s@%s' [%s]", tklflag, usermask, hostmask, reason); // Probably won't be seen ever, but just in case ;]
#endif
expired++;
int softban = 0;
tkl->ptr.serverban = MyMallocEx(sizeof(ServerBan));
/* Usermask - but taking into account that the
* %-prefix means a soft ban.
*/
R_SAFE(read_str(fd, &str));
if (*str == '%')
{
softban = 1;
str++;
}
tkl->ptr.serverban->usermask = strdup(str);
safefree(str);
/* And the other 2 fields.. */
R_SAFE(read_str(fd, &tkl->ptr.serverban->hostmask));
R_SAFE(read_str(fd, &tkl->ptr.serverban->reason));
if (find_tkl_serverban(tkl->type, tkl->ptr.serverban->usermask,
tkl->ptr.serverban->hostmask, softban))
{
do_not_add = 1;
}
if (!do_not_add)
{
tkl_add_serverban(tkl->type, tkl->ptr.serverban->usermask,
tkl->ptr.serverban->hostmask,
tkl->ptr.serverban->reason,
tkl->set_by, tkl->expire_at,
tkl->set_at, softban, 0);
}
} else
if (TKLIsNameBan(tkl))
{
tkl->ptr.nameban = MyMallocEx(sizeof(NameBan));
R_SAFE(read_str(fd, &str));
if (*str == 'H')
tkl->ptr.nameban->hold = 1;
safefree(str);
R_SAFE(read_str(fd, &tkl->ptr.nameban->name));
R_SAFE(read_str(fd, &tkl->ptr.nameban->reason));
if (find_tkl_nameban(tkl->type, tkl->ptr.nameban->name,
tkl->ptr.nameban->hold))
{
do_not_add = 1;
}
if (!do_not_add)
{
tkl_add_nameban(tkl->type, tkl->ptr.nameban->name,
tkl->ptr.nameban->hold,
tkl->ptr.nameban->reason,
tkl->set_by, tkl->expire_at,
tkl->set_at, 0);
}
} else
if (TKLIsSpamfilter(tkl))
{
int match_method;
char *err = NULL;
tkl->ptr.spamfilter = MyMallocEx(sizeof(Spamfilter));
/* Match method */
R_SAFE(read_str(fd, &str));
match_method = unreal_match_method_strtoval(str);
if (!match_method)
{
config_warn("[tkldb] Unhandled spamfilter match method '%s' -- spamfilter entry not added", str);
do_not_add = 1;
}
safefree(str);
/* Match string (eg: regex) */
R_SAFE(read_str(fd, &str));
tkl->ptr.spamfilter->match = unreal_create_match(match_method, str, &err);
if (!tkl->ptr.spamfilter->match)
{
config_warn("[tkldb] Spamfilter '%s' does not compile: %s -- spamfilter entry not added", str, err);
do_not_add = 1;
}
safefree(str);
/* Target (eg: cpn) */
R_SAFE(read_str(fd, &str));
tkl->ptr.spamfilter->target = spamfilter_gettargets(str, NULL);
if (!tkl->ptr.spamfilter->target)
{
config_warn("[tkldb] Spamfilter '%s' without any valid targets (%s) -- spamfilter entry not added",
tkl->ptr.spamfilter->match->str, str);
do_not_add = 1;
}
safefree(str);
/* Action */
R_SAFE(read_data(fd, &c, sizeof(c)));
tkl->ptr.spamfilter->action = banact_chartoval(c);
if (!tkl->ptr.spamfilter->action)
{
config_warn("[tkldb] Spamfilter '%s' without valid action (%c) -- spamfilter entry not added",
tkl->ptr.spamfilter->match->str, c);
do_not_add = 1;
}
R_SAFE(read_str(fd, &tkl->ptr.spamfilter->tkl_reason));
R_SAFE(read_int64(fd, &tkl->ptr.spamfilter->tkl_duration));
if (find_tkl_spamfilter(tkl->type, tkl->ptr.spamfilter->match->str,
tkl->ptr.spamfilter->action,
tkl->ptr.spamfilter->target))
{
do_not_add = 1;
}
if (!do_not_add)
{
tkl_add_spamfilter(tkl->type, tkl->ptr.spamfilter->target,
tkl->ptr.spamfilter->action,
tkl->ptr.spamfilter->match,
tkl->set_by, tkl->expire_at, tkl->set_at,
tkl->ptr.spamfilter->tkl_duration,
tkl->ptr.spamfilter->tkl_reason,
0);
/* tkl_add_spamfilter() does not copy the match but assign it.
* so set to NULL here to avoid a read-after-free later on.
*/
tkl->ptr.spamfilter->match = NULL;
}
} else
{
config_error("[tkldb] Unhandled type!! TKLDB is missing support for type %ld -- STOPPED reading db entries!", (long)tkl->type);
FreeTKLRead();
continue;
break; /* we MUST stop reading */
}
ircsnprintf(setTime, sizeof(setTime), "%lld", (long long)set_at);
ircsnprintf(expTime, sizeof(expTime), "%lld", (long long)expire_at);
if (!do_not_add)
added_cnt++;
// Build TKL args
// All of these except [8] are the same for all (only odd one is spamfilter)
parc = 9;
tkllayer[2] = tkltype;
tkllayer[3] = usermask;
tkllayer[4] = hostmask;
tkllayer[5] = setby;
tkllayer[6] = expTime;
tkllayer[7] = setTime;
tkllayer[8] = reason;
if (spamf)
{
parc = 12;
// Make sure this particular *-Line isn't already active somehow
for (tkl = tklines[tkl_hash(tklflag)]; tkl; tkl = tkl->next)
{
// We can assume it's the same spamfilter if all of the following match: spamfilter expression, targets, TKL reason, action, matchtype and TKL duration
if (!strcmp(tkl->ptr.spamf->expr->str, spamf_expr) && !strcmp(tkl->usermask, usermask) && !strcmp(tkl->ptr.spamf->tkl_reason, spamf_tkl_reason) &&
tkl->ptr.spamf->action == spamf_action && tkl->ptr.spamf->expr->type == matchtype && tkl->ptr.spamf->tkl_duration == spamf_tkl_duration)
{
doadd = 0;
break;
}
}
if (doadd)
{
ircsnprintf(spamfTime, sizeof(spamfTime), "%lld", (long long)spamf_tkl_duration);
tkllayer[8] = spamfTime;
tkllayer[9] = spamf_tkl_reason;
tkllayer[10] = spamf_matchtype;
tkllayer[11] = spamf_expr;
}
}
else {
for (tkl = tklines[tkl_hash(tklflag)]; tkl; tkl = tkl->next)
{
if (!strcmp(tkl->usermask, usermask) && !strcmp(tkl->hostmask, hostmask) && !strcmp(tkl->reason, reason) && tkl->expire_at == expire_at)
{
doadd = 0;
break;
}
}
}
if (doadd)
{
m_tkl(&me, &me, NULL, parc, tkllayer);
added++;
}
FreeTKLRead();
}
/* If everything went fine, then reading a single byte should cause an EOF error */
if (fread(&c, 1, 1, fd) == 1)
{
ircd_log(LOG_ERROR, "[warning] [tkldb] Database possibly corrupt. Extra data found at end of DB file.");
sendto_realops("[warning] [tkldb] Database possibly corrupt. Extra data found at end of DB file.");
}
config_warn("[tkldb] Database invalid. Extra data found at end of DB file.");
fclose(fd);
if (added || expired)
{
ircd_log(LOG_ERROR, "[tkldb] Re-added %d *-Lines (skipped %d expired)", added, expired);
sendto_realops("[tkldb] Re-added %d *-Lines (skipped %d expired)", added, expired); // Probably won't be seen ever, but just in case ;]
}
if (added_cnt)
config_status("[tkldb] Re-added %d *-Lines", added_cnt);
#ifdef BENCHMARK
gettimeofday(&tv_beta, NULL);
ircd_log(LOG_ERROR, "[tkldb] Benchmark: LOAD DB: %lld microseconds",
@ -680,69 +632,3 @@ int read_tkldb(void)
return 1;
}
static inline int read_data(FILE *fd, void *buf, size_t len)
{
if (fread(buf, 1, len, fd) < len)
return 0;
return 1;
}
static inline int write_data(FILE *fd, void *buf, size_t len)
{
if (fwrite(buf, 1, len, fd) < len)
return 0;
return 1;
}
static int write_str(FILE *fd, char *x)
{
uint16_t len = (x ? strlen(x) : 0);
if (!write_data(fd, &len, sizeof(len)))
return 0;
if (len)
{
if (!write_data(fd, x, len))
return 0;
}
return 1;
}
static int read_str(FILE *fd, char **x)
{
uint16_t len;
size_t len_tkl1000; // len used to be of type size_t, but this has portability problems when writing to/reading from binary files
size_t size;
*x = NULL;
if (cfg.backport_tkl1000)
{
if (!read_data(fd, &len_tkl1000, sizeof(len_tkl1000)))
return 0;
len = len_tkl1000;
} else
{
if (!read_data(fd, &len, sizeof(len)))
return 0;
}
if (!len)
{
*x = strdup(""); // It's safer for m_tkl to work with empty strings instead of NULLs
return 1;
}
if (len > 10000)
return 0;
size = len;
*x = MyMallocEx(size + 1);
if (!read_data(fd, *x, size))
{
MyFree(*x);
*x = NULL;
return 0;
}
(*x)[len] = 0;
return 1;
}