1
0
mirror of https://github.com/unrealircd/unrealircd.git synced 2026-07-09 11:53:13 +02:00

- Added channelmode +j (jointhrottle), syntax: /mode #chan +j X:Y, and then it will

throttle the number of joins per-user to X in Y seconds. Idea from Angrywolf (who
  wrote a module that did this before). This might need some more testing :).
  It's enabled by default but can be #undef'ed in include/config.h (line 449).
This commit is contained in:
Bram Matthys
2005-02-12 00:05:08 +00:00
parent 0441305a3a
commit d3a0bb92f6
15 changed files with 508 additions and 2 deletions
+4
View File
@@ -589,3 +589,7 @@
- Fixed a bug with channel alias{}'s where using the format syntax caused a crash (#0002323)
reported by Snake.
- Made channel mode +S strip RGB color codes.
- Added channelmode +j (jointhrottle), syntax: /mode #chan +j X:Y, and then it will
throttle the number of joins per-user to X in Y seconds. Idea from Angrywolf (who
wrote a module that did this before). This might need some more testing :).
It's enabled by default but can be #undef'ed in include/config.h (line 449).
+4
View File
@@ -2375,6 +2375,10 @@ set {
<td>Makes channel G rated. Checks for words listed in the Badword Blocks,
and replaces them with the words specified</td>
</tr>
<tr>
<td><div align="center">j &lt;joins:seconds&gt;</div></td>
<td>Throttles joins per-user to <i>joins</i> per <i>seconds</i> seconds</td>
</tr>
<tr>
<td><div align="center">M</div></td>
<td>A registered nickname (+r) is required to talk</td>
+1
View File
@@ -165,6 +165,7 @@ help Chmodes {
" I <nick!ident@host> = Overrides +i for matching users [h]";
" f <floodparams> = Flood protection (for more info see /HELPOP CHMODEF) [o]";
" i = A user must be invited to join the channel [h]";
" j <joins:sec> = Throttle joins per-user to 'joins' per 'sec' seconds";
" k <key> = Users must specify <key> to join [h]";
" l <number of max users> = Channel may hold at most <number> of users [o]";
" m = Moderated channel (only +vhoaq users may speak) [h]";
+8
View File
@@ -431,6 +431,7 @@
/*
* Extended channel modes. This extends the channel modes with yet another
* 32 possible modes which can also be used in modules.
* This is now pretty much required.
*/
#define EXTCMODE
@@ -440,6 +441,13 @@
*/
#define NEWCHFLOODPROT
/* JoinThrottle (chanmode +j): +j x:y throttles users to X joins per Y seconds (per-user).
* In peak situations (eg: just after a server restart with thousand clients joining
* hundreds of channels) it can use like ~200k, but in normal circumstances you should
* count on just ~10-50k.
*/
#define JOINTHROTTLE
/* ------------------------- END CONFIGURATION SECTION -------------------- */
#define MOTD MPATH
#define RULES RPATH
+7
View File
@@ -467,6 +467,7 @@ extern MODVAR Cmode_t EXTMODE_NONOTICE;
#ifdef STRIPBADWORDS
extern MODVAR Cmode_t EXTMODE_STRIPBADWORDS;
#endif
extern MODVAR Cmode_t EXTMODE_JOINTHROTTLE;
#endif
#ifndef HAVE_STRLCPY
@@ -739,3 +740,9 @@ extern char *clean_ban_mask(char *, int, aClient *);
extern void chanfloodtimer_stopchantimers(aChannel *chptr);
extern int find_invex(aChannel *chptr, aClient *sptr);
extern void DoMD5(unsigned char *mdout, unsigned char *src, unsigned long n);
#ifdef JOINTHROTTLE
aJFlood *cmodej_addentry(aClient *cptr, aChannel *chptr);
void cmodej_delentry(aJFlood *e);
void cmodej_deluserentries(aClient *cptr);
void cmodej_delchannelentries(aChannel *chptr);
#endif
+1
View File
@@ -129,6 +129,7 @@
#define ERR_CHANOWNPRIVNEEDED 499
#define ERR_TOOMANYJOINS 500
#define ERR_UMODEUNKNOWNFLAG 501
#define ERR_USERSDONTMATCH 502
+49
View File
@@ -143,6 +143,7 @@ typedef struct _cmdoverride Cmdoverride;
typedef struct SMember Member;
typedef struct SMembership Membership;
typedef struct SMembershipL MembershipL;
typedef struct JFlood aJFlood;
#ifdef ZIP_LINKS
typedef struct Zdata aZdata;
@@ -754,6 +755,9 @@ struct User {
unsigned char away_c; /* number of times away has been set */
#endif
} flood;
#ifdef JOINTHROTTLE
aJFlood *jflood;
#endif
};
struct Server {
@@ -873,6 +877,12 @@ extern Cmode *CmodeAdd(Module *reserved, CmodeInfo req, Cmode_t *mode);
extern void CmodeDel(Cmode *cmode);
#endif
typedef struct {
EXTCM_PAR_HEADER
unsigned short num;
unsigned short t;
} aModejEntry;
#define LISTENER_NORMAL 0x000001
#define LISTENER_CLIENTSONLY 0x000002
#define LISTENER_SERVERSONLY 0x000004
@@ -1486,6 +1496,9 @@ struct Channel {
Ban *banlist;
Ban *exlist; /* exceptions */
Ban *invexlist; /* invite list */
#ifdef JOINTHROTTLE
aJFlood *jflood;
#endif
char chname[1];
};
@@ -1735,6 +1748,42 @@ typedef struct {
unsigned parameters : 1;
} aCtab;
#ifdef JOINTHROTTLE
/** A jointhrottle item, this is a double linked list.
* prev_u Previous entry of user
* next_u Next entry of user
* prev_c Previous entry of channel
* next_c Next entry of channel
* chptr The channel this entry applies to
* cptr The user this entry applies to
* firstjoin Timestamp of "first join" (since last timer reset)
* numjoin Number of joins since that period
* CLARIFICATION:
* Why a double linked list? Well, the following operations need to be performed:
* - if user quits, entry must be removed
* - if channel is destroyed, entry must be removed
* (and of course, more, but these are the most important ones affecting this decision)
* While it would be possible to have a linked list only by user (for example),
* that would mean that upon channel destroy ALL entries would have to be searched
* trough, which might mean for example 800*8=6400 entries in a peak situation
* (such as after a server restart and hundreds of clients connecting&joining).
* For obvious reasons, that would be a very bad idea :).
* So this costs us 2 pointers (8b on ia32) per entry, but in case of channel destroy
* it means we only have for example 20 entries to scan trough rather than 2000.
* Worth the extra memory :). -- Syzop
* Note that in normal situations it won't be that bad since we will try to
* regulary free up some entries.
*/
struct JFlood {
aJFlood *prev_u, *next_u;
aJFlood *prev_c, *next_c;
aChannel *chptr;
aClient *cptr;
time_t firstjoin;
unsigned short numjoins;
};
#endif
void init_throttling_hash();
int hash_throttling(struct IN_ADDR *in);
struct ThrottlingBucket *find_throttling_bucket(struct IN_ADDR *in);
+3
View File
@@ -1523,6 +1523,9 @@ void sub1_from_channel(aChannel *chptr)
chanfloodtimer_stopchantimers(chptr);
if (chptr->mode.floodprot)
MyFree(chptr->mode.floodprot);
#endif
#ifdef JOINTHROTTLE
cmodej_delchannelentries(chptr);
#endif
if (chptr->topic)
MyFree(chptr->topic);
+7
View File
@@ -45,6 +45,10 @@ ID_Copyright("(C) Carsten Munk 2001");
MODVAR Event *events = NULL;
#ifdef JOINTHROTTLE
extern EVENT(cmodej_cleanup_structs);
#endif
void LockEventSystem(void)
{
}
@@ -213,6 +217,9 @@ void SetupEvents(void)
EventAddEx(NULL, "loop", 0, 0, loop_event, NULL);
#ifndef NO_FDLIST
EventAddEx(NULL, "fdlistcheck", 1, 0, e_check_fdlists, NULL);
#endif
#ifdef JOINTHROTTLE
EventAddEx(NULL, "cmodej_cleanup_structs", 60, 0, cmodej_cleanup_structs, NULL);
#endif
UnlockEventSystem();
}
+175
View File
@@ -58,6 +58,18 @@ Cmode_t EXTMODE_NONOTICE = 0L;
Cmode_t EXTMODE_STRIPBADWORDS = 0L;
#endif
#ifdef JOINTHROTTLE
/* cmode j stuff... */
Cmode_t EXTMODE_JOINTHROTTLE = 0L;
int cmodej_is_ok(aClient *sptr, aChannel *chptr, char *para, int type, int what);
CmodeParam *cmodej_put_param(CmodeParam *r_in, char *param);
char *cmodej_get_param(CmodeParam *r_in);
char *cmodej_conv_param(char *param_in);
void cmodej_free_param(CmodeParam *r);
CmodeParam *cmodej_dup_struct(CmodeParam *r_in);
int cmodej_sjoin_check(aChannel *chptr, CmodeParam *ourx, CmodeParam *theirx);
#endif
void make_extcmodestr()
{
char *p;
@@ -99,6 +111,21 @@ static void load_extendedchanmodes(void)
req.flag = 'G';
CmodeAdd(NULL, req, &EXTMODE_STRIPBADWORDS);
#endif
#ifdef JOINTHROTTLE
/* +j */
memset(&req, 0, sizeof(req));
req.paracount = 1;
req.is_ok = cmodej_is_ok;
req.flag = 'j';
req.put_param = cmodej_put_param;
req.get_param = cmodej_get_param;
req.conv_param = cmodej_conv_param;
req.free_param = cmodej_free_param;
req.dup_struct = cmodej_dup_struct;
req.sjoin_check = cmodej_sjoin_check;
CmodeAdd(NULL, req, &EXTMODE_JOINTHROTTLE);
#endif
}
void extcmode_init(void)
@@ -268,4 +295,152 @@ int extcmode_default_requirehalfop(aClient *cptr, aChannel *chptr, char *para, i
return EX_DENY;
}
#ifdef JOINTHROTTLE
/*** CHANNEL MODE +j STUFF ******/
int cmodej_is_ok(aClient *sptr, aChannel *chptr, char *para, int type, int what)
{
if ((type == EXCHK_ACCESS) || (type == EXCHK_ACCESS_ERR))
{
if (IsPerson(sptr) && is_chan_op(sptr, chptr))
return EX_ALLOW;
return EX_DENY;
} else
if (type == EXCHK_PARAM)
{
/* Check parameter.. syntax should be X:Y, X should be 1-255, Y should be 1-999 */
char buf[32], *p;
int num, t, fail = 0;
strlcpy(buf, para, sizeof(buf));
p = strchr(buf, ':');
if (!p)
{
fail = 1;
} else {
*p++ = '\0';
num = atoi(buf);
t = atoi(p);
if ((num < 1) || (num > 255) || (t < 1) || (t > 999))
fail = 1;
}
if (fail)
{
sendnotice(sptr, "Error in setting +j, syntax: +j <num>:<seconds>, where <num> must be 1-255, and <seconds> 1-999");
return EX_DENY;
}
return EX_ALLOW;
}
/* falltrough -- should not be used */
return EX_DENY;
}
CmodeParam *cmodej_put_param(CmodeParam *r_in, char *param)
{
aModejEntry *r = (aModejEntry *)r_in;
char buf[32], *p;
int num, t;
if (!r)
{
/* Need to create one */
r = (aModejEntry *)malloc(sizeof(aModejEntry));
memset(r, 0, sizeof(aModejEntry));
r->flag = 'j';
}
strlcpy(buf, param, sizeof(buf));
p = strchr(buf, ':');
if (p)
{
*p++ = '\0';
num = atoi(buf);
t = atoi(p);
if (num < 1) num = 1;
if (num > 255) num = 255;
if (t < 1) t = 1;
if (t > 999) t = 999;
r->num = num;
r->t = t;
} else {
r->num = 0;
r->t = 0;
}
return (CmodeParam *)r;
}
char *cmodej_get_param(CmodeParam *r_in)
{
aModejEntry *r = (aModejEntry *)r_in;
static char retbuf[16];
if (!r)
return NULL;
snprintf(retbuf, sizeof(retbuf), "%hu:%hu", r->num, r->t);
return retbuf;
}
char *cmodej_conv_param(char *param_in)
{
static char retbuf[32];
char param[32], *p;
int num, t, fail = 0;
strlcpy(param, param_in, sizeof(param));
p = strchr(param, ':');
if (!p)
return NULL;
*p++ = '\0';
num = atoi(param);
t = atoi(p);
if (num < 1)
num = 1;
if (num > 255)
num = 255;
if (t < 1)
t = 1;
if (t > 999)
t = 999;
snprintf(retbuf, sizeof(retbuf), "%d:%d", num, t);
return retbuf;
}
void cmodej_free_param(CmodeParam *r)
{
MyFree(r);
}
CmodeParam *cmodej_dup_struct(CmodeParam *r_in)
{
aModejEntry *r = (aModejEntry *)r_in;
aModejEntry *w = (aModejEntry *)MyMalloc(sizeof(aModejEntry));
memcpy(w, r, sizeof(aModejEntry));
return (CmodeParam *)w;
}
int cmodej_sjoin_check(aChannel *chptr, CmodeParam *ourx, CmodeParam *theirx)
{
aModejEntry *our = (aModejEntry *)ourx;
aModejEntry *their = (aModejEntry *)theirx;
if (our->t != their->t)
{
if (our->t > their->t)
return EXSJ_WEWON;
else
return EXSJ_THEYWON;
}
else if (our->num != their->num)
{
if (our->num > their->num)
return EXSJ_WEWON;
else
return EXSJ_THEYWON;
} else
return EXSJ_SAME;
}
#endif /* JOINTHROTTLE */
#endif /* EXTCMODE */
+1 -1
View File
@@ -1208,7 +1208,7 @@ int InitwIRCD(int argc, char *argv[])
#ifndef _WIN32
fprintf(stderr, "%s", unreallogo);
fprintf(stderr, " v%s\n", VERSIONONLY);
fprintf(stderr, " using TRE %s\n", tre_version());
fprintf(stderr, " using %s\n", tre_version());
#ifdef USE_SSL
fprintf(stderr, " using %s\n", OPENSSL_VERSION_TEXT);
#endif
+149
View File
@@ -550,5 +550,154 @@ ListStruct *del_ListItem(ListStruct *item, ListStruct **list) {
}
return NULL;
}
#ifdef JOINTHROTTLE
/** Adds a aJFlood entry to user & channel and returns entry.
* NOTE: Does not check for already-existing-entry
*/
aJFlood *cmodej_addentry(aClient *cptr, aChannel *chptr)
{
aJFlood *e;
#ifdef DEBUGMODE
if (!IsPerson(cptr))
abort();
for (e=cptr->user->jflood; e; e=e->next_u)
if (e->chptr == chptr)
abort();
for (e=chptr->jflood; e; e=e->next_c)
if (e->cptr == cptr)
abort();
#endif
e = MyMallocEx(sizeof(aJFlood));
e->cptr = cptr;
e->chptr = chptr;
e->prev_u = e->prev_c = NULL;
e->next_u = cptr->user->jflood;
e->next_c = chptr->jflood;
cptr->user->jflood = chptr->jflood = e;
return e;
}
/** Removes an individual entry from list and frees it.
*/
void cmodej_delentry(aJFlood *e)
{
/* remove from user.. */
if (e->prev_u)
e->prev_u->next_u = e->next_u;
else
e->cptr->user->jflood = e->next_u; /* new head */
if (e->next_u)
e->next_u->prev_u = e->prev_u;
/* remove from channel.. */
if (e->prev_c)
e->prev_c->next_c = e->next_c;
else
e->chptr->jflood = e->next_c; /* new head */
if (e->next_c)
e->next_c->prev_c = e->prev_c;
/* actually free it */
MyFree(e);
}
/** Removes all entries belonging to user from all lists and free them. */
void cmodej_deluserentries(aClient *cptr)
{
aJFlood *e, *e_next;
for (e=cptr->user->jflood; e; e=e_next)
{
e_next = e->next_u;
/* remove from channel.. */
if (e->prev_c)
e->prev_c->next_c = e->next_c;
else
e->chptr->jflood = e->next_c; /* new head */
if (e->next_c)
e->next_c->prev_c = e->prev_c;
/* actually free it */
MyFree(e);
}
}
/** Removes all entries belonging to channel from all lists and free them. */
void cmodej_delchannelentries(aChannel *chptr)
{
aJFlood *e, *e_next;
for (e=chptr->jflood; e; e=e_next)
{
e_next = e->next_c;
/* remove from user.. */
if (e->prev_u)
e->prev_u->next_u = e->next_u;
else
e->cptr->user->jflood = e->next_u; /* new head */
if (e->next_u)
e->next_u->prev_u = e->prev_u;
/* actually free it */
MyFree(e);
}
}
/** Regulary cleans up cmode-j user/chan structs */
EVENT(cmodej_cleanup_structs)
{
aJFlood *e, *e_next;
int i;
aClient *cptr;
aChannel *chptr;
int t;
CmodeParam *cmp;
#ifdef DEBUGMODE
int freed=0;
#endif
for (chptr = channel; chptr; chptr=chptr->nextch)
{
if (!chptr->jflood)
continue;
t=0;
/* t will be kept at 0 if not found or if mode not set,
* but DO still check since there are entries left as indicated by ->jflood!
*/
if (chptr->mode.extmode & EXTMODE_JOINTHROTTLE)
{
for (cmp = chptr->mode.extmodeparam; cmp; cmp=cmp->next)
if (cmp->flag == 'j')
t = ((aModejEntry *)cmp)->t;
}
for (e = chptr->jflood; e; e = e_next)
{
e_next = e->next_c;
if (e->firstjoin + t < TStime())
{
cmodej_delentry(e);
#ifdef DEBUGMODE
freed++;
#endif
}
}
}
#ifdef DEBUGMODE
if (freed)
ircd_log(LOG_ERROR, "cmodej_cleanup_structs: %d entries freed [%d bytes]", freed, freed * sizeof(aJFlood));
#endif
}
#endif
+95
View File
@@ -51,6 +51,10 @@ DLLFUNC CMD_FUNC(_do_join);
DLLFUNC int _can_join(aClient *cptr, aClient *sptr, aChannel *chptr, char *key, char *link, char *parv[]);
static int extended_operoverride(aClient *sptr, aChannel *chptr, char *key, int mval, char mchar);
#define MAXBOUNCE 5 /** Most sensible */
#ifdef JOINTHROTTLE
static int isjthrottled(aClient *cptr, aChannel *chptr);
static void cmodej_increase_usercounter(aClient *cptr, aChannel *chptr);
#endif
/* Externs */
extern MODVAR int spamf_ugly_vchanoverride;
@@ -220,9 +224,97 @@ Ban *banned;
#endif
#endif
#ifdef JOINTHROTTLE
if (!IsAnOper(cptr) &&
(chptr->mode.extmode & EXTMODE_JOINTHROTTLE) && isjthrottled(cptr, chptr))
return ERR_TOOMANYJOINS;
#endif
return 0;
}
#ifdef JOINTHROTTLE
static int isjthrottled(aClient *cptr, aChannel *chptr)
{
CmodeParam *m;
aJFlood *e;
int num=0, t=0;
if (!MyClient(cptr))
return 0;
for (m = chptr->mode.extmodeparam; m; m=m->next)
if (m->flag == 'j')
{
num = ((aModejEntry *)m)->num;
t = ((aModejEntry *)m)->t;
break;
}
if (!num || !t)
return 0;
/* Grab user<->chan entry.. */
for (e = cptr->user->jflood; e; e=e->next_u)
if (e->chptr == chptr)
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) < t) && (e->numjoins == num))
return 1; /* Throttled */
return 0;
}
static void cmodej_increase_usercounter(aClient *cptr, aChannel *chptr)
{
CmodeParam *m;
aJFlood *e;
int num=0, t=0;
if (!MyClient(cptr))
return;
for (m = chptr->mode.extmodeparam; m; m=m->next)
if (m->flag == 'j')
{
num = ((aModejEntry *)m)->num;
t = ((aModejEntry *)m)->t;
break;
}
if (!num || !t)
return;
/* Grab user<->chan entry.. */
for (e = cptr->user->jflood; e; e=e->next_u)
if (e->chptr == chptr)
break;
if (!e)
{
/* Allocate one */
e = cmodej_addentry(cptr, chptr);
e->firstjoin = TStime();
e->numjoins = 1;
} else
if ((TStime() - e->firstjoin) < t) /* still valid? */
{
e->numjoins++;
} else {
/* reset :p */
e->firstjoin = TStime();
e->numjoins = 1;
}
}
#endif
/*
** m_join
** parv[0] = sender prefix
@@ -617,6 +709,9 @@ DLLFUNC CMD_FUNC(_do_join)
me.name, parv[0], name);
continue;
}
#ifdef JOINTHROTTLE
cmodej_increase_usercounter(cptr, chptr);
#endif
}
join_channel(chptr, cptr, sptr, flags);
+1 -1
View File
@@ -556,7 +556,7 @@ static char *replies[] = {
/* 497 */ NULL, /* ircu */
/* 498 */ NULL, /* ircu */
/* 499 ERR_CHANOWNPRIVNEEDED */ ":%s 499 %s %s :You're not a channel owner",
/* 500 */ NULL,
/* 500 ERR_TOOMANYJOINS */ ":%s 500 %s %s :Too many join requests. Please wait a while and try again.",
/* 501 ERR_UMODEUNKNOWNFLAG */ ":%s 501 %s :Unknown MODE flag",
/* 502 ERR_USERSDONTMATCH */ ":%s 502 %s :Cant change mode for other users",
/* 503 */ NULL, /* austhex */
+3
View File
@@ -727,6 +727,9 @@ static void exit_one_client(aClient *cptr, aClient *sptr, aClient *from, char *c
{
RunHook2(HOOKTYPE_REMOTE_QUIT, sptr, comment);
}
#ifdef JOINTHROTTLE
cmodej_deluserentries(sptr);
#endif
while ((mp = sptr->user->channel))
remove_user_from_channel(sptr, mp->chptr);