/* * ================================================================= * Filename: jointhrottle.c * Description: Channel mode +j: join flood protection. * Author: AngryWolf * Documentation: jointhrottle.txt (comes with the package) * ================================================================= */ #include "config.h" #include "struct.h" #include "common.h" #include "sys.h" #include "numeric.h" #include "msg.h" #include "channel.h" #include #include #include #include #include #ifdef _WIN32 #include #endif #include #include "h.h" #ifdef STRIPBADWORDS #include "badwords.h" #endif #ifdef _WIN32 #include "version.h" #endif #ifndef EXTCMODE #error "This module requires extended channel modes to be enabled." #error "See the EXTCMODE macro in include/config.h for more information." #error "Compilation aborted." #endif extern void sendto_one(aClient *to, char *pattern, ...); #define FLAG_JOINTHROTTLE 'j' #define ERR_TOOMUCHJOINS 496 #define HasJoinLimit(x) ((x)->mode.extmode & MODE_JOINTHROTTLE) #define ClearMem(x) memset(&(x), 0, sizeof (x)) #define DelCmode(x) if (x) CmodeDel(x); x = NULL #define DelHook(x) if (x) HookDel(x); x = NULL /* Backward compatibility */ #ifndef EX_DENY #define EX_DENY 0 #define EX_ALLOW 1 #define EX_ALWAYS_DENY 0 #endif #ifndef HOOK_CONTINUE #define HOOK_CONTINUE 0 #define HOOK_ALLOW -1 #define HOOK_DENY 1 #endif typedef struct { EXTCM_PAR_HEADER int joins; long seconds; } aModeJTEntry; typedef struct _joinflood JoinFlood; struct _joinflood { JoinFlood *prev, *next; aChannel *chptr; aClient *sptr; int joins; TS last; }; static Cmode *AddCmode(Module *module, CmodeInfo *req, Cmode_t *mode); static int ModeJT_is_ok(aClient *, aChannel *, char *, int, int); static CmodeParam *ModeJT_put_param(CmodeParam *, char *); static char *ModeJT_get_param(CmodeParam *); static char *ModeJT_conv_param(char *); static void ModeJT_free_param(CmodeParam *); static CmodeParam *ModeJT_dup_struct(CmodeParam *); static int ModeJT_sjoin_check(aChannel *, CmodeParam *, CmodeParam *); static int cb_channel_destroy(aChannel *); static int cb_join(aClient *, aChannel *, char *[]); static int cb_quit(aClient *, char *); static void InitConf(); static void FreeConf(); Cmode_t MODE_JOINTHROTTLE = 0L; Cmode *ModeJoinThrottle = NULL; Hook *HookJoin, *HookChannelDestroy; Hook *HookQuit, *HookRemoteQuit; JoinFlood *JFlist; ModuleHeader MOD_HEADER(jointhrottle) = { "jointhrottle", "$Id: jointhrottle.c,v 2.4 2004/04/17 18:29:32 angrywolf Exp $", "channel mode +j (join throttle)", "3.2-b8-1", NULL }; DLLFUNC int MOD_TEST(jointhrottle)(ModuleInfo *modinfo) { CmodeInfo ModeJT; ClearMem(ModeJT); ModeJT.flag = FLAG_JOINTHROTTLE; ModeJT.paracount = 1; ModeJT.is_ok = ModeJT_is_ok; ModeJT.put_param = ModeJT_put_param; ModeJT.get_param = ModeJT_get_param; ModeJT.conv_param = ModeJT_conv_param; ModeJT.free_param = ModeJT_free_param; ModeJT.sjoin_check = ModeJT_sjoin_check; ModeJT.dup_struct = ModeJT_dup_struct; ModeJoinThrottle = AddCmode(modinfo->handle, &ModeJT, &MODE_JOINTHROTTLE); return MOD_SUCCESS; } DLLFUNC int MOD_INIT(jointhrottle)(ModuleInfo *modinfo) { #ifndef STATIC_LINKING ModuleSetOptions(modinfo->handle, MOD_OPT_PERM); #endif InitConf(); HookJoin = HookAddEx(modinfo->handle, HOOKTYPE_PRE_LOCAL_JOIN, cb_join); HookChannelDestroy = HookAddEx(modinfo->handle, HOOKTYPE_CHANNEL_DESTROY, cb_channel_destroy); HookQuit = HookAddEx(modinfo->handle, HOOKTYPE_LOCAL_QUIT, cb_quit); HookRemoteQuit = HookAddEx(modinfo->handle, HOOKTYPE_REMOTE_QUIT, cb_quit); return MOD_SUCCESS; } DLLFUNC int MOD_LOAD(jointhrottle)(int module_load) { return MOD_SUCCESS; } DLLFUNC int MOD_UNLOAD(jointhrottle)(int module_unload) { FreeConf(); DelCmode(ModeJoinThrottle); DelHook(HookJoin); DelHook(HookChannelDestroy); DelHook(HookQuit); DelHook(HookRemoteQuit); return MOD_FAILED; } static void InitConf() { JFlist = NULL; } static void FreeConf() { JoinFlood *j; ListStruct *next; for (j = JFlist; j; j = (JoinFlood *) next) { next = (ListStruct *) j->next; DelListItem(j, JFlist); MyFree(j); } } static Cmode *AddCmode(Module *module, CmodeInfo *req, Cmode_t *mode) { Cmode *cmode; *mode = 0; cmode = CmodeAdd(module, *req, mode); #ifndef STATIC_LINKING if (ModuleGetError(module) != MODERR_NOERROR || !cmode) #else if (!cmode) #endif { #ifndef STATIC_LINKING config_error("Error adding channel mode +%c when loading module %s: %s", req->flag, MOD_HEADER(jointhrottle).name, ModuleGetErrorStr(module)); #else config_error("Error adding channel mode +%c when loading module %s", req->flag, MOD_HEADER(jointhrottle).name); #endif return NULL; } return cmode; } static int cb_channel_destroy(aChannel *chptr) { JoinFlood *j; ListStruct *next; for (j = JFlist; j; j = (JoinFlood *) next) { next = (ListStruct *) j->next; if (j->chptr == chptr) { DelListItem(j, JFlist); MyFree(j); } } return 0; } static int cb_quit(aClient *sptr, char *comment) { JoinFlood *j; ListStruct *next; for (j = JFlist; j; j = (JoinFlood *) next) { next = (ListStruct *) j->next; if (j->sptr == sptr) { DelListItem(j, JFlist); MyFree(j); } } return 0; } static int cb_join(aClient *sptr, aChannel *chptr, char *parv[]) { JoinFlood *j; aModeJTEntry *p; TS now = TStime(); if (!HasJoinLimit(chptr)) return HOOK_CONTINUE; for (j = JFlist; j; j = j->next) if ((j->chptr == chptr && j->sptr == sptr)) break; if (!j) { j = (JoinFlood *) MyMallocEx(sizeof(JoinFlood)); j->sptr = sptr; j->chptr = chptr; j->last = now; j->joins = 1; AddListItem(j, JFlist); } else { p = (aModeJTEntry *) extcmode_get_struct(chptr->mode.extmodeparam, FLAG_JOINTHROTTLE); if (now - j->last >= p->seconds) { j->last = now; j->joins = 1; } else { j->joins++; if (j->joins > p->joins && now - j->last < p->seconds) { #ifndef NO_OPEROVERRIDE if (IsOper(sptr) && OPCanOverride(sptr)) { sendto_snomask(SNO_EYES, "*** OperOverride -- %s (%s@%s) joined %s (overriding +%c).", sptr->name, sptr->user->username, sptr->user->realhost, chptr->chname, FLAG_JOINTHROTTLE); return HOOK_CONTINUE; } #endif sendto_one(sptr, ":%s %d %s %s :Too many join requests. " "Wait %ld seconds before trying again.", me.name, ERR_TOOMUCHJOINS, parv[0], chptr->chname, j->last + p->seconds - now); return HOOK_DENY; } } } return HOOK_CONTINUE; } static int ModeJT_is_ok(aClient *sptr, aChannel *chptr, char *param, int type, int what) { char *p; int num; long sec; u_int ok; if (type == EXCHK_ACCESS || type == EXCHK_ACCESS_ERR) { if (!IsPerson(sptr) || IsULine(sptr)) return EX_ALLOW; if (!is_chan_op(sptr, chptr)) { if (type == EXCHK_ACCESS_ERR) sendto_one(sptr, err_str(ERR_CHANOPRIVSNEEDED), me.name, sptr->name, chptr->chname); return EX_DENY; } return EX_ALLOW; } else if (type == EXCHK_PARAM) { ok = 1; p = strchr(param, ':'); if (!p) { ok = 0; sendto_one(sptr, ":%s NOTICE %s :Channel mode +%c requires a parameter in form :", me.name, sptr->name, FLAG_JOINTHROTTLE); } else { *p = '\0'; num = atoi(param); sec = config_checkval(p+1, CFG_TIME); if (num < 1 || sec < 1 || num > 10000 || sec > 60*60*24) { ok = 0; sendto_one(sptr, ":%s NOTICE %s :Parameter for channel mode +%c is out of range", me.name, sptr->name, FLAG_JOINTHROTTLE); } *p = ':'; } return ok; } return 0; } static CmodeParam *ModeJT_put_param(CmodeParam *param, char *text) { aModeJTEntry *m; char *p; m = (aModeJTEntry *) param; if (!m) { m = (aModeJTEntry *) MyMallocEx(sizeof(aModeJTEntry)); m->flag = FLAG_JOINTHROTTLE; } p = strchr(text, ':'); *p = '\0'; m->joins = atoi(text); m->seconds = config_checkval(p+1, CFG_TIME); *p = ':'; return (CmodeParam *) m; } static char *ModeJT_get_param(CmodeParam *param) { aModeJTEntry *m; static char text[50]; m = (aModeJTEntry *) param; if (!m) return NULL; ClearMem(text); sprintf(text, "%d:%ld", m->joins, m->seconds); return text; } static char *ModeJT_conv_param(char *in) { int joins; long seconds; static char text[50]; char *p; p = strchr(in, ':'); *p = '\0'; joins = atoi(in); seconds = config_checkval(p+1, CFG_TIME); *p = ':'; ClearMem(text); sprintf(text, "%d:%ld", joins, seconds); return text; } static void ModeJT_free_param(CmodeParam *param) { aModeJTEntry *m = (aModeJTEntry *) param; MyFree(m); } static CmodeParam *ModeJT_dup_struct(CmodeParam *in) { aModeJTEntry *m; m = (aModeJTEntry *) MyMalloc(sizeof(aModeJTEntry)); memcpy(m, in, sizeof(aModeJTEntry)); m->prev = m->next = NULL; return (CmodeParam *) m; } static int ModeJT_sjoin_check(aChannel *chptr, CmodeParam *ours, CmodeParam *theirs) { aModeJTEntry *o, *t; o = (aModeJTEntry *) ours; t = (aModeJTEntry *) theirs; // sendto_realops("ModeJT_sjoin_check(): (OURS) o->joins=%d, o->seconds=%ld", o->joins, o->seconds); // sendto_realops("ModeJT_sjoin_check(): (THEIRS) t->joins=%d, t->seconds=%ld", t->joins, t->seconds); if (o->joins == t->joins && o->seconds == t->seconds) return EXSJ_SAME; if (o->seconds < t->seconds) return EXSJ_THEYWON; else if (o->joins < t->joins) return EXSJ_THEYWON; else return EXSJ_WEWON; }