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 }