This repository has been archived on 2025-02-12. You can view files and clone it, but cannot push or open issues or pull requests.
NeoStats-secureserv/FloodCheck.c
2004-03-08 22:16:09 +00:00

295 lines
9 KiB
C

/* NeoStats - IRC Statistical Services
** Copyright (c) 1999-2004 Justin Hammond
** http://www.neostats.net/
**
** 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 2 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
** USA
**
** NeoStats CVS Identification
** $Id$
*/
/* http://sourceforge.net/projects/muhstik/ */
#include <stdio.h>
#include "neostats.h"
#include "SecureServ.h"
/* the structure to keep track of joins per period (ajpp = average joins per period) */
typedef struct ChanInfo {
Chans *c;
int ajpp;
time_t sampletime;
int locked;
}ChanInfo;
/* this is the nickflood stuff */
typedef struct nicktrack {
char nick[MAXNICK];
int changes;
int when;
}nicktrack;
/* the hash that contains the channels we are tracking */
static hash_t *FC_Chans;
/* the hash that contains the nicks we are tracking */
static hash_t *nickflood;
/* init the channel hash */
int InitJoinFlood(void)
{
SET_SEGV_LOCATION();
FC_Chans = hash_create(-1, 0, 0);
return 1;
}
/* update ajpp for chan, if required */
int JoinFloodJoinChan (User *u, Chans *c)
{
ChanInfo *ci;
hnode_t *cn;
SET_SEGV_LOCATION();
#ifndef NS_FLAGS_NETJOIN
/* check for netjoins!!!*/
/* XXX this isn't really the best, as a lot of
* floodbots could connect to an IRC server, wait
* SecureServ.timediff, and then join the channel,
* and SecureServ isn't going to flag them. It would be
* nicer if the IRCd protocol could easily identify nicks
* that are ridding in on a netjoin.
*/
if ((time(NULL) - u->TS) > SecureServ.timedif) {
nlog(LOG_DEBUG2, LOG_MOD, "Nick %s is Riding a NetJoin", u->nick);
/* forget the update */
return -1;
}
/* ok, now if the server just linked in as well, ignore this */
/* XXX should this time be configurable? */
if ((time(NULL) - u->server->connected_since) < 120) {
nlog(LOG_DEBUG2, LOG_MOD, "Ignoring %s joining %s as it seems server %s just linked", u->nick, c->name, u->server->name);
return -1;
}
#else
if (u->flags && NS_FLAGS_NETJOIN) {
return -1;
}
#endif
/* if channel flood protection is disabled, return here */
if (SecureServ.FloodProt == 0) {
return 1;
}
/* find the chan in SecureServ's list */
cn = hash_lookup(FC_Chans, c->name);
if (!cn) {
/* if it doesn't exist, means we have to create it ! */
nlog(LOG_DEBUG2, LOG_MOD, "Creating Channel Record in JoinSection %s", c->name);
ci = malloc(sizeof(ChanInfo));
ci->ajpp = 0;
ci->sampletime = 0;
ci->c = c;
ci->locked = 0;
cn = hnode_create(ci);
hash_insert(FC_Chans, cn, c->name);
} else {
ci = hnode_get(cn);
}
/* Firstly, if the last join was "SampleTime" seconds ago
* then reset the time, and set ajpp to 1
*/
if ((time(NULL) - ci->sampletime) > SecureServ.sampletime) {
nlog(LOG_DEBUG2, LOG_MOD, "ChanJoin: SampleTime Expired, Resetting %s", c->name);
ci->sampletime = time(NULL);
ci->ajpp = 1;
return 1;
}
/* now check if ajpp has exceeded the threshold */
/* XXX TOTHINK should we have different thresholds for different channel
* sizes? Needs some real life testing I guess
*/
ci->ajpp++;
if ((ci->ajpp > SecureServ.JoinThreshold) && (ci->locked > 0)) {
nlog(LOG_WARNING, LOG_MOD, "Warning, Possible Flood on %s. Closing Channel. (AJPP: %d/%d Sec, SampleTime %d", ci->c->name, ci->ajpp, (int)(time(NULL) - ci->sampletime), SecureServ.sampletime);
chanalert(s_SecureServ, "Warning, Possible Flood on %s. Closing Channel. (AJPP: %d/%d Sec, SampleTime %d)", ci->c->name, ci->ajpp, (int)(time(NULL) - ci->sampletime), SecureServ.sampletime);
globops(s_SecureServ, "Warning, Possible Flood on %s. Closing Channel. (AJPP: %d/%d Sec, SampleTime %d)", ci->c->name, ci->ajpp, (int)(time(NULL) - ci->sampletime), SecureServ.sampletime);
prefmsg(ci->c->name, s_SecureServ, "Temporarily closing channel due to possible floodbot attack. Channel will be re-opened in %d seconds", SecureServ.closechantime);
/* uh oh, channel is under attack. Close it down. */
schmode_cmd(s_SecureServ, ci->c->name, "+ik", SecureServ.ChanKey);
ci->locked = time(NULL);
}
/* just some record keeping */
if (ci->ajpp > SecureServ.MaxAJPP) {
nlog(LOG_DEBUG1, LOG_MOD, "New AJPP record on %s at %d Joins in %d Seconds", c->name, ci->ajpp, (int)(time(NULL) - ci->sampletime));
if (SecureServ.verbose) chanalert(s_SecureServ, "New AJPP record on %s at %d Joins in %d Seconds", c->name, ci->ajpp, (int)(time(NULL) - ci->sampletime));
SecureServ.MaxAJPP = ci->ajpp;
strlcpy(SecureServ.MaxAJPPChan, c->name, CHANLEN);
}
return 1;
}
/* delete the channel from our hash */
int JoinFloodDelChan(Chans *c)
{
ChanInfo *ci;
hnode_t *cn;
SET_SEGV_LOCATION();
cn = hash_lookup(FC_Chans, c->name);
if (cn) {
ci = hnode_get(cn);
hash_delete(FC_Chans, cn);
free(ci);
hnode_destroy(cn);
#if 0
} else {
/* ignore this, as it just means since we started SecureServ, no one has joined the channel, and now the last person has left. Was just flooding logfiles */
//nlog(LOG_WARNING, LOG_MOD, "Can't Find Channel %s in our Hash", c->name);
#endif
}
return 1;
}
int CheckLockChan()
{
hscan_t cs;
hnode_t *cn;
ChanInfo *ci;
SET_SEGV_LOCATION();
/* scan through the channels */
hash_scan_begin(&cs, FC_Chans);
while ((cn = hash_scan_next (&cs)) != NULL) {
ci = hnode_get(cn);
/* if the locked time plus closechantime is greater than current time, then unlock the channel */
if ((ci->locked > 0) && (ci->locked + SecureServ.closechantime < time(NULL))) {
schmode_cmd(s_SecureServ, ci->c->name, "-ik", SecureServ.ChanKey);
chanalert(s_SecureServ, "Unlocking %s after floodprotection timeout", ci->c->name);
globops(s_SecureServ, "Unlocking %s after flood protection timeout", ci->c->name);
prefmsg(ci->c->name, s_SecureServ, "Unlocking the channel now");
ci->locked = 0;
}
}
return 1;
}
int InitNickFlood(void)
{
SET_SEGV_LOCATION();
/* init the nickflood hash */
nickflood = hash_create(-1, 0, 0);
return 1;
}
/* periodically clean up the nickflood hash so it doesn't grow to big */
int CleanNickFlood()
{
hscan_t nfscan;
hnode_t *nfnode;
nicktrack *nick;
SET_SEGV_LOCATION();
hash_scan_begin(&nfscan, nickflood);
while ((nfnode = hash_scan_next(&nfscan)) != NULL) {
nick = hnode_get(nfnode);
if ((time(NULL) - nick->when) > 10) {
/* delete the nickname */
nlog(LOG_DEBUG2, LOG_MOD, "Deleting %s out of NickFlood Hash", nick->nick);
hash_scan_delete(nickflood, nfnode);
free(nick);
}
}
return 1;
}
int CheckNickFlood(User* u)
{
hnode_t *nfnode;
nicktrack *nick;
SET_SEGV_LOCATION();
nfnode = hash_lookup(nickflood, u->nick);
if (nfnode) {
/* its already there */
nick = hnode_get(nfnode);
/* first, remove it from the hash, as the nick has changed */
hash_delete(nickflood, nfnode);
/* increment the nflood count */
nick->changes++;
nlog(LOG_DEBUG2, LOG_MOD, "NickFlood Check: %d in 10", nick->changes);
if ((nick->changes > SecureServ.nfcount) && ((time(NULL) - nick->when) <= 10)) {
/* its a bad bad bad flood */
chanalert(s_SecureServ, "NickFlood Detected on %s", u->nick);
/* XXX Do Something bad !!!! */
/* free the struct */
hnode_destroy(nfnode);
free(nick);
} else if ((time(NULL) - nick->when) > 10) {
nlog(LOG_DEBUG2, LOG_MOD, "Resetting NickFlood Count on %s", u->nick);
strlcpy(nick->nick, u->nick, MAXNICK);
nick->changes = 1;
nick->when = time(NULL);
hash_insert(nickflood, nfnode, nick->nick);
} else {
/* re-insert it into the hash */
strlcpy(nick->nick, u->nick, MAXNICK);
hash_insert(nickflood, nfnode, nick->nick);
}
} else {
/* this is because maybe we already have a record from a signoff etc */
if (!hash_lookup(nickflood, u->nick)) {
/* this is a first */
nick = malloc(sizeof(nicktrack));
strlcpy(nick->nick, u->nick, MAXNICK);
nick->changes = 1;
nick->when = time(NULL);
nfnode = hnode_create(nick);
hash_insert(nickflood, nfnode, nick->nick);
nlog(LOG_DEBUG2, LOG_MOD, "NF: Created New Entry");
} else {
nlog(LOG_DEBUG2, LOG_MOD, "Already got a record for %s in NickFlood", u->nick);
}
}
return 0;
}
int NickFloodSignOff(char * n)
{
hnode_t *nfnode;
nicktrack *nick;
SET_SEGV_LOCATION();
nlog(LOG_DEBUG2, LOG_MOD, "DelNick: looking for %s", n);
nfnode = hash_lookup(nickflood, n);
if (nfnode) {
nick = hnode_get(nfnode);
hash_delete(nickflood, nfnode);
hnode_destroy(nfnode);
free(nick);
}
nlog(LOG_DEBUG2, LOG_MOD, "DelNick: After nickflood Code");
return 1;
}