mirror of https://github.com/pissnet/pissircd.git
249 lines
6.2 KiB
C
249 lines
6.2 KiB
C
/*
|
|
* Jointhrottle (set::anti-flood::join-flood).
|
|
* (C) Copyright 2005-.. Bram Matthys (Syzop) and the UnrealIRCd team
|
|
*
|
|
* This was PREVIOUSLY channel mode +j but has been moved to the
|
|
* set::anti-flood::join-flood block instead since people rarely need
|
|
* to tweak this per-channel and it's nice to have this on by default.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 1, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include "unrealircd.h"
|
|
|
|
ModuleHeader MOD_HEADER
|
|
= {
|
|
"jointhrottle",
|
|
"5.0",
|
|
"Join flood protection (set::anti-flood::join-flood)",
|
|
"UnrealIRCd Team",
|
|
"unrealircd-6",
|
|
};
|
|
|
|
ModuleInfo *ModInfo = NULL;
|
|
|
|
ModDataInfo *jointhrottle_md; /* Module Data structure which we acquire */
|
|
|
|
typedef struct JoinFlood JoinFlood;
|
|
|
|
struct JoinFlood {
|
|
JoinFlood *prev, *next;
|
|
char name[CHANNELLEN+1];
|
|
time_t firstjoin;
|
|
unsigned short numjoins;
|
|
};
|
|
|
|
/* Forward declarations */
|
|
void jointhrottle_md_free(ModData *m);
|
|
int jointhrottle_can_join(Client *client, Channel *channel, const char *key, char **errmsg);
|
|
int jointhrottle_local_join(Client *client, Channel *channel, MessageTag *mtags);
|
|
static int isjthrottled(Client *client, Channel *channel);
|
|
static void jointhrottle_increase_usercounter(Client *client, Channel *channel);
|
|
EVENT(jointhrottle_cleanup_structs);
|
|
JoinFlood *jointhrottle_addentry(Client *client, Channel *channel);
|
|
|
|
MOD_TEST()
|
|
{
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
MOD_INIT()
|
|
{
|
|
ModDataInfo mreq;
|
|
|
|
MARK_AS_OFFICIAL_MODULE(modinfo);
|
|
ModInfo = modinfo;
|
|
|
|
memset(&mreq, 0, sizeof(mreq));
|
|
mreq.name = "jointhrottle";
|
|
mreq.free = jointhrottle_md_free;
|
|
mreq.serialize = NULL; /* not supported */
|
|
mreq.unserialize = NULL; /* not supported */
|
|
mreq.sync = 0;
|
|
mreq.type = MODDATATYPE_LOCAL_CLIENT;
|
|
jointhrottle_md = ModDataAdd(modinfo->handle, mreq);
|
|
if (!jointhrottle_md)
|
|
abort();
|
|
|
|
HookAdd(modinfo->handle, HOOKTYPE_CAN_JOIN, 0, jointhrottle_can_join);
|
|
HookAdd(modinfo->handle, HOOKTYPE_LOCAL_JOIN, 0, jointhrottle_local_join);
|
|
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
MOD_LOAD()
|
|
{
|
|
EventAdd(ModInfo->handle, "jointhrottle_cleanup_structs", jointhrottle_cleanup_structs, NULL, 60000, 0);
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
MOD_UNLOAD()
|
|
{
|
|
return MOD_FAILED;
|
|
}
|
|
|
|
static int isjthrottled(Client *client, Channel *channel)
|
|
{
|
|
JoinFlood *e;
|
|
FloodSettings *settings = get_floodsettings_for_user(client, FLD_JOIN);
|
|
|
|
if (!MyUser(client))
|
|
return 0;
|
|
|
|
/* Grab user<->chan entry.. */
|
|
for (e = moddata_local_client(client, jointhrottle_md).ptr; e; e=e->next)
|
|
if (!strcasecmp(e->name, channel->name))
|
|
break;
|
|
|
|
if (!e)
|
|
return 0; /* Not present, so cannot be throttled */
|
|
|
|
/* Ok... now the actual check:
|
|
* if ([timer valid] && [one more join would exceed num])
|
|
*/
|
|
if (((TStime() - e->firstjoin) < settings->period[FLD_JOIN]) &&
|
|
(e->numjoins >= settings->limit[FLD_JOIN]))
|
|
return 1; /* Throttled */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void jointhrottle_increase_usercounter(Client *client, Channel *channel)
|
|
{
|
|
JoinFlood *e;
|
|
|
|
if (!MyUser(client))
|
|
return;
|
|
|
|
/* Grab user<->chan entry.. */
|
|
for (e = moddata_local_client(client, jointhrottle_md).ptr; e; e=e->next)
|
|
if (!strcasecmp(e->name, channel->name))
|
|
break;
|
|
|
|
if (!e)
|
|
{
|
|
/* Allocate one */
|
|
e = jointhrottle_addentry(client, channel);
|
|
e->firstjoin = TStime();
|
|
e->numjoins = 1;
|
|
} else
|
|
if ((TStime() - e->firstjoin) < iConf.floodsettings->period[FLD_JOIN]) /* still valid? */
|
|
{
|
|
e->numjoins++;
|
|
} else {
|
|
/* reset :p */
|
|
e->firstjoin = TStime();
|
|
e->numjoins = 1;
|
|
}
|
|
}
|
|
|
|
int jointhrottle_can_join(Client *client, Channel *channel, const char *key, char **errmsg)
|
|
{
|
|
if (!ValidatePermissionsForPath("immune:join-flood",client,NULL,channel,NULL) && isjthrottled(client, channel))
|
|
{
|
|
*errmsg = STR_ERR_TOOMANYJOINS;
|
|
return ERR_TOOMANYJOINS;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int jointhrottle_local_join(Client *client, Channel *channel, MessageTag *mtags)
|
|
{
|
|
jointhrottle_increase_usercounter(client, channel);
|
|
return 0;
|
|
}
|
|
|
|
/** Adds a JoinFlood entry to user & channel and returns entry.
|
|
* NOTE: Does not check for already-existing-entry
|
|
*/
|
|
JoinFlood *jointhrottle_addentry(Client *client, Channel *channel)
|
|
{
|
|
JoinFlood *e;
|
|
|
|
#ifdef DEBUGMODE
|
|
if (!IsUser(client))
|
|
abort();
|
|
|
|
for (e=moddata_local_client(client, jointhrottle_md).ptr; e; e=e->next)
|
|
if (!strcasecmp(e->name, channel->name))
|
|
abort(); /* already exists -- should never happen */
|
|
#endif
|
|
|
|
e = safe_alloc(sizeof(JoinFlood));
|
|
strlcpy(e->name, channel->name, sizeof(e->name));
|
|
|
|
/* Insert our new entry as (new) head */
|
|
if (moddata_local_client(client, jointhrottle_md).ptr)
|
|
{
|
|
JoinFlood *current_head = moddata_local_client(client, jointhrottle_md).ptr;
|
|
current_head->prev = e;
|
|
e->next = current_head;
|
|
}
|
|
moddata_local_client(client, jointhrottle_md).ptr = e;
|
|
|
|
return e;
|
|
}
|
|
|
|
/** Regularly cleans up user/chan structs */
|
|
EVENT(jointhrottle_cleanup_structs)
|
|
{
|
|
Client *client;
|
|
JoinFlood *jf, *jf_next;
|
|
|
|
list_for_each_entry(client, &lclient_list, lclient_node)
|
|
{
|
|
if (!MyUser(client))
|
|
continue; /* only (local) persons.. */
|
|
|
|
for (jf = moddata_local_client(client, jointhrottle_md).ptr; jf; jf = jf_next)
|
|
{
|
|
jf_next = jf->next;
|
|
|
|
if (jf->firstjoin + iConf.floodsettings->period[FLD_JOIN] > TStime())
|
|
continue; /* still valid entry */
|
|
if (moddata_local_client(client, jointhrottle_md).ptr == jf)
|
|
{
|
|
/* change head */
|
|
moddata_local_client(client, jointhrottle_md).ptr = jf->next; /* could be set to NULL now */
|
|
if (jf->next)
|
|
jf->next->prev = NULL;
|
|
} else {
|
|
/* change non-head entries */
|
|
jf->prev->next = jf->next; /* could be set to NULL now */
|
|
if (jf->next)
|
|
jf->next->prev = jf->prev;
|
|
}
|
|
safe_free(jf);
|
|
}
|
|
}
|
|
}
|
|
|
|
void jointhrottle_md_free(ModData *m)
|
|
{
|
|
JoinFlood *j, *j_next;
|
|
|
|
if (!m->ptr)
|
|
return;
|
|
|
|
for (j = m->ptr; j; j = j_next)
|
|
{
|
|
j_next = j->next;
|
|
safe_free(j);
|
|
}
|
|
|
|
m->ptr = NULL;
|
|
}
|