unrealircd

- supernets unrealircd source & configuration
git clone git://git.acid.vegas/unrealircd.git
Log | Files | Refs | Archive | README | LICENSE

jointhrottle.c (6397B)

      1 /*
      2  * Jointhrottle (set::anti-flood::join-flood).
      3  * (C) Copyright 2005-.. Bram Matthys (Syzop) and the UnrealIRCd team
      4  *
      5  * This was PREVIOUSLY channel mode +j but has been moved to the
      6  * set::anti-flood::join-flood block instead since people rarely need
      7  * to tweak this per-channel and it's nice to have this on by default.
      8  *
      9  * This program is free software; you can redistribute it and/or modify
     10  * it under the terms of the GNU General Public License as published by
     11  * the Free Software Foundation; either version 1, or (at your option)
     12  * any later version.
     13  *
     14  * This program is distributed in the hope that it will be useful,
     15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17  * GNU General Public License for more details.
     18  *
     19  * You should have received a copy of the GNU General Public License
     20  * along with this program; if not, write to the Free Software
     21  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     22  */
     23 
     24 #include "unrealircd.h"
     25 
     26 ModuleHeader MOD_HEADER
     27   = {
     28 	"jointhrottle",
     29 	"5.0",
     30 	"Join flood protection (set::anti-flood::join-flood)",
     31 	"UnrealIRCd Team",
     32 	"unrealircd-6",
     33     };
     34 
     35 ModuleInfo *ModInfo = NULL;
     36 
     37 ModDataInfo *jointhrottle_md; /* Module Data structure which we acquire */
     38 
     39 typedef struct JoinFlood JoinFlood;
     40 
     41 struct JoinFlood {
     42 	JoinFlood *prev, *next;
     43 	char name[CHANNELLEN+1];
     44 	time_t firstjoin;
     45 	unsigned short numjoins;
     46 };
     47 
     48 /* Forward declarations */
     49 void jointhrottle_md_free(ModData *m);
     50 int jointhrottle_can_join(Client *client, Channel *channel, const char *key, char **errmsg);
     51 int jointhrottle_local_join(Client *client, Channel *channel, MessageTag *mtags);
     52 static int isjthrottled(Client *client, Channel *channel);
     53 static void jointhrottle_increase_usercounter(Client *client, Channel *channel);
     54 EVENT(jointhrottle_cleanup_structs);
     55 JoinFlood *jointhrottle_addentry(Client *client, Channel *channel);
     56 
     57 MOD_TEST()
     58 {
     59 	return MOD_SUCCESS;
     60 }
     61 
     62 MOD_INIT()
     63 {
     64 	ModDataInfo mreq;
     65 
     66 	MARK_AS_OFFICIAL_MODULE(modinfo);
     67 	ModInfo = modinfo;
     68 
     69 	memset(&mreq, 0, sizeof(mreq));
     70 	mreq.name = "jointhrottle";
     71 	mreq.free = jointhrottle_md_free;
     72 	mreq.serialize = NULL; /* not supported */
     73 	mreq.unserialize = NULL; /* not supported */
     74 	mreq.sync = 0;
     75 	mreq.type = MODDATATYPE_LOCAL_CLIENT;
     76 	jointhrottle_md = ModDataAdd(modinfo->handle, mreq);
     77 	if (!jointhrottle_md)
     78 		abort();
     79 
     80 	HookAdd(modinfo->handle, HOOKTYPE_CAN_JOIN, 0, jointhrottle_can_join);
     81 	HookAdd(modinfo->handle, HOOKTYPE_LOCAL_JOIN, 0, jointhrottle_local_join);
     82 	
     83 	return MOD_SUCCESS;
     84 }
     85 
     86 MOD_LOAD()
     87 {
     88 	EventAdd(ModInfo->handle, "jointhrottle_cleanup_structs", jointhrottle_cleanup_structs, NULL, 60000, 0);
     89 	return MOD_SUCCESS;
     90 }
     91 
     92 MOD_UNLOAD()
     93 {
     94 	return MOD_FAILED;
     95 }
     96 
     97 static int isjthrottled(Client *client, Channel *channel)
     98 {
     99 	JoinFlood *e;
    100 	FloodSettings *settings = get_floodsettings_for_user(client, FLD_JOIN);
    101 
    102 	if (!MyUser(client))
    103 		return 0;
    104 
    105 	/* Grab user<->chan entry.. */
    106 	for (e = moddata_local_client(client, jointhrottle_md).ptr; e; e=e->next)
    107 		if (!strcasecmp(e->name, channel->name))
    108 			break;
    109 	
    110 	if (!e)
    111 		return 0; /* Not present, so cannot be throttled */
    112 
    113 	/* Ok... now the actual check:
    114 	 * if ([timer valid] && [one more join would exceed num])
    115 	 */
    116 	if (((TStime() - e->firstjoin) < settings->period[FLD_JOIN]) &&
    117 	    (e->numjoins >= settings->limit[FLD_JOIN]))
    118 		return 1; /* Throttled */
    119 
    120 	return 0;
    121 }
    122 
    123 static void jointhrottle_increase_usercounter(Client *client, Channel *channel)
    124 {
    125 	JoinFlood *e;
    126 
    127 	if (!MyUser(client))
    128 		return;
    129 		
    130 	/* Grab user<->chan entry.. */
    131 	for (e = moddata_local_client(client, jointhrottle_md).ptr; e; e=e->next)
    132 		if (!strcasecmp(e->name, channel->name))
    133 			break;
    134 	
    135 	if (!e)
    136 	{
    137 		/* Allocate one */
    138 		e = jointhrottle_addentry(client, channel);
    139 		e->firstjoin = TStime();
    140 		e->numjoins = 1;
    141 	} else
    142 	if ((TStime() - e->firstjoin) < iConf.floodsettings->period[FLD_JOIN]) /* still valid? */
    143 	{
    144 		e->numjoins++;
    145 	} else {
    146 		/* reset :p */
    147 		e->firstjoin = TStime();
    148 		e->numjoins = 1;
    149 	}
    150 }
    151 
    152 int jointhrottle_can_join(Client *client, Channel *channel, const char *key, char **errmsg)
    153 {
    154 	if (!ValidatePermissionsForPath("immune:join-flood",client,NULL,channel,NULL) && isjthrottled(client, channel))
    155 	{
    156 		*errmsg = STR_ERR_TOOMANYJOINS;
    157 		return ERR_TOOMANYJOINS;
    158 	}
    159 	return 0;
    160 }
    161 
    162 
    163 int jointhrottle_local_join(Client *client, Channel *channel, MessageTag *mtags)
    164 {
    165 	jointhrottle_increase_usercounter(client, channel);
    166 	return 0;
    167 }
    168 
    169 /** Adds a JoinFlood entry to user & channel and returns entry.
    170  * NOTE: Does not check for already-existing-entry
    171  */
    172 JoinFlood *jointhrottle_addentry(Client *client, Channel *channel)
    173 {
    174 	JoinFlood *e;
    175 
    176 #ifdef DEBUGMODE
    177 	if (!IsUser(client))
    178 		abort();
    179 
    180 	for (e=moddata_local_client(client, jointhrottle_md).ptr; e; e=e->next)
    181 		if (!strcasecmp(e->name, channel->name))
    182 			abort(); /* already exists -- should never happen */
    183 #endif
    184 
    185 	e = safe_alloc(sizeof(JoinFlood));
    186 	strlcpy(e->name, channel->name, sizeof(e->name));
    187 
    188 	/* Insert our new entry as (new) head */
    189 	if (moddata_local_client(client, jointhrottle_md).ptr)
    190 	{
    191 		JoinFlood *current_head = moddata_local_client(client, jointhrottle_md).ptr;
    192 		current_head->prev = e;
    193 		e->next = current_head;
    194 	}
    195 	moddata_local_client(client, jointhrottle_md).ptr = e;
    196 
    197 	return e;
    198 }
    199 
    200 /** Regularly cleans up user/chan structs */
    201 EVENT(jointhrottle_cleanup_structs)
    202 {
    203 	Client *client;
    204 	JoinFlood *jf, *jf_next;
    205 	
    206 	list_for_each_entry(client, &lclient_list, lclient_node)
    207 	{
    208 		if (!MyUser(client))
    209 			continue; /* only (local) persons.. */
    210 
    211 		for (jf = moddata_local_client(client, jointhrottle_md).ptr; jf; jf = jf_next)
    212 		{
    213 			jf_next = jf->next;
    214 			
    215 			if (jf->firstjoin + iConf.floodsettings->period[FLD_JOIN] > TStime())
    216 				continue; /* still valid entry */
    217 			if (moddata_local_client(client, jointhrottle_md).ptr == jf)
    218 			{
    219 				/* change head */
    220 				moddata_local_client(client, jointhrottle_md).ptr = jf->next; /* could be set to NULL now */
    221 				if (jf->next)
    222 					jf->next->prev = NULL;
    223 			} else {
    224 				/* change non-head entries */
    225 				jf->prev->next = jf->next; /* could be set to NULL now */
    226 				if (jf->next)
    227 					jf->next->prev = jf->prev;
    228 			}
    229 			safe_free(jf);
    230 		}
    231 	}
    232 }
    233 
    234 void jointhrottle_md_free(ModData *m)
    235 {
    236 	JoinFlood *j, *j_next;
    237 
    238 	if (!m->ptr)
    239 		return;
    240 
    241 	for (j = m->ptr; j; j = j_next)
    242 	{
    243 		j_next = j->next;
    244 		safe_free(j);
    245 	}	
    246 
    247 	m->ptr = NULL;
    248 }