From 7883ed891507cb802ac13877adf1ebeb86e50a17 Mon Sep 17 00:00:00 2001 From: pk910 Date: Fri, 21 Oct 2011 01:19:14 +0200 Subject: [PATCH] added BotNet scanner to NeonSpam --- src/bot_NeonSpam.c | 6 ++++ src/bot_NeonSpam.h | 24 ++++++++++--- src/cmd_neonspam_set.c | 22 ++++++++++++ src/event_neonspam_chanmsg.c | 70 +++++++++++++++++++++++++++++++----- 4 files changed, 108 insertions(+), 14 deletions(-) diff --git a/src/bot_NeonSpam.c b/src/bot_NeonSpam.c index 5a200c1..fdfd914 100644 --- a/src/bot_NeonSpam.c +++ b/src/bot_NeonSpam.c @@ -52,12 +52,14 @@ static struct NeonSpamJoinNode *getNeonSpamJoinNode(struct ChanUser *chanuser); #define SPAMSERV_CHECK_IGNORE 0 #define SPAMSERV_CHECK_WARN 1 #define SPAMSERV_CHECK_PUNISH 2 +#define SPAMSERV_CHECK_DEAD 3 /* scanner has already killed the user */ #define SPAMSERV_MSG_SPAM "Spamming" #define SPAMSERV_MSG_FLOOD "Flooding the channel/network" #define SPAMSERV_MSG_ADV "Advertising" #define SPAMSERV_MSG_JOINFLOOD "Join flooding the channel" #define SPAMSERV_MSG_WARNING "%s is against the channel rules" +#define SPAMSERV_MSG_BOTNET "BotNet detected." //EVENTS #include "event_neonspam_join.c" @@ -155,6 +157,10 @@ static int loadNeonSpamSettings(struct ChanNode *chan) { settings->join_time = atoi(row[5] ? row[5] : defaults[5]); settings->exceptlevel = atoi(row[6] ? row[6] : defaults[6]); settings->join_nodes = NULL; + settings->lastmsg_time = 0; + int i; + for(i = 0; i < BOTNETSCAN_USERS; i++) + settings->botnicks[i] = NULL; chan->spam_settings = settings; return 1; } diff --git a/src/bot_NeonSpam.h b/src/bot_NeonSpam.h index 838a570..77fb4ee 100644 --- a/src/bot_NeonSpam.h +++ b/src/bot_NeonSpam.h @@ -20,11 +20,13 @@ #include "main.h" -#define SPAMSETTINGS_SPAMSCAN 0x01 -#define SPAMSETTINGS_FLOODSCAN 0x02 -#define SPAMSETTINGS_JOINSCAN 0x04 -#define SPAMSETTINGS_SCANOPS 0x08 -#define SPAMSETTINGS_SCANVOICE 0x10 +#define SPAMSETTINGS_SPAMSCAN 0x0001 +#define SPAMSETTINGS_FLOODSCAN 0x0002 +#define SPAMSETTINGS_JOINSCAN 0x0004 +#define SPAMSETTINGS_SCANOPS 0x0008 +#define SPAMSETTINGS_SCANVOICE 0x0010 +#define SPAMSETTINGS_BOTNETSCAN 0x0020 +#define SPAMSETTINGS_KICKEDBOTQUEUE 0x0040 #define MAX_FLOOD_AMOUNT 300 #define MIN_FLOOD_AMOUNT 2 @@ -34,6 +36,9 @@ #define MIN_JOIN_AMOUNT 2 #define MAX_JOIN_TIME 200 +#define BOTNETSCAN_USERS 4 +#define BOTNETSCAN_TIME 2 + struct NeonSpamSettings { unsigned int flags; unsigned char spam_amount; @@ -42,7 +47,16 @@ struct NeonSpamSettings { unsigned char join_amount; unsigned char join_time; unsigned int exceptlevel : 10; + + //joinflood struct NeonSpamJoinNode *join_nodes; + + //botnet + unsigned long lastmsg; //crc32 hash + time_t lastmsg_time; + char *botnicks[BOTNETSCAN_USERS]; + + }; /* PENALTY SYSTEM * user gets MAX_FLOOD_TIME points per message diff --git a/src/cmd_neonspam_set.c b/src/cmd_neonspam_set.c index 7907dba..41e7825 100644 --- a/src/cmd_neonspam_set.c +++ b/src/cmd_neonspam_set.c @@ -27,6 +27,7 @@ static char* neonspam_cmd_setjoinfloodreaction(struct ClientSocket *client, stru static char* neonspam_cmd_setspamscan(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *cvalue, char *argument); static char* neonspam_cmd_setfloodscan(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *cvalue, char *argument); static char* neonspam_cmd_setjoinfloodscan(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *cvalue, char *argument); +static char* neonspam_cmd_setbotnetscan(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *cvalue, char *argument); static char* neonspam_cmd_setscanchanops(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *cvalue, char *argument); static char* neonspam_cmd_setscanvoiced(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *cvalue, char *argument); static char* neonspam_cmd_setexceptlevel(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *cvalue, char *argument); @@ -63,6 +64,7 @@ static const struct { {"SpamScan", NULL, NS_VALID_BOOLEAN | NS_VALID_FUNCTION, NULL, neonspam_cmd_setspamscan}, {"FloodScan", NULL, NS_VALID_BOOLEAN | NS_VALID_FUNCTION, NULL, neonspam_cmd_setfloodscan}, {"JoinFloodScan", NULL, NS_VALID_BOOLEAN | NS_VALID_FUNCTION, NULL, neonspam_cmd_setjoinfloodscan}, + {"BotNetScan", NULL, NS_VALID_BOOLEAN | NS_VALID_FUNCTION, NULL, neonspam_cmd_setbotnetscan}, {"ScanChanOps", NULL, NS_VALID_BOOLEAN | NS_VALID_FUNCTION, NULL, neonspam_cmd_setscanchanops}, {"ScanVoiced", NULL, NS_VALID_BOOLEAN | NS_VALID_FUNCTION, NULL, neonspam_cmd_setscanvoiced}, {"ExceptLevel", NULL, NS_VALID_ACCESS | NS_VALID_FUNCTION, NULL, neonspam_cmd_setexceptlevel}, @@ -433,6 +435,26 @@ static char* neonspam_cmd_setjoinfloodscan(struct ClientSocket *client, struct U return cvalue; } +static char* neonspam_cmd_setbotnetscan(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *cvalue, char *argument) { + if(!cvalue) { + //get current value + cvalue = ((atoi(neonspam_settings_row[0] ? neonspam_settings_row[0] : neonspam_settings_defaults[0]) & SPAMSETTINGS_BOTNETSCAN) ? "1" : "0"); + } + else if(argument) { + //change value + int cflags = atoi((neonspam_settings_row[0] ? neonspam_settings_row[0] : neonspam_settings_defaults[0])); + if(!strcmp(argument, "0")) + cflags &= ~SPAMSETTINGS_BOTNETSCAN; + else + cflags |= SPAMSETTINGS_BOTNETSCAN; + printf_mysql_query("UPDATE `channels` SET `channel_scanstate` = '%d' WHERE `channel_id` = '%d' ", cflags, chan->channel_id); + cvalue = argument; + if(chan->spam_settings) + chan->spam_settings->flags = cflags; + } + return cvalue; +} + static char* neonspam_cmd_setscanchanops(struct ClientSocket *client, struct UserNode *user, struct ChanNode *chan, struct Event *event, const char *setting, char *cvalue, char *argument) { if(!cvalue) { //get current value diff --git a/src/event_neonspam_chanmsg.c b/src/event_neonspam_chanmsg.c index f55635a..7bcd4b5 100644 --- a/src/event_neonspam_chanmsg.c +++ b/src/event_neonspam_chanmsg.c @@ -17,6 +17,7 @@ static int neonspam_spamscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message); static int neonspam_floodscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser); +static int neonspam_botnetscan(struct ClientSocket *client, struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message); static USERAUTH_CALLBACK(neonspam_event_chanmsg_nick_lookup); static void neonspam_event_chanmsg_punish(struct ClientSocket *client, struct ChanUser *chanuser, struct NeonSpamSettings *settings, int action, char *reason, char *reaction); @@ -87,6 +88,15 @@ static void neonspam_event_chanmsg(struct UserNode *user, struct ChanNode *chan, break; } } + if(action != SPAMSERV_CHECK_PUNISH && (settings->flags & SPAMSETTINGS_BOTNETSCAN)) { + result = neonspam_botnetscan(client, settings, chanuser, message); + switch(result) { + case SPAMSERV_CHECK_DEAD: + return; + case SPAMSERV_CHECK_IGNORE: + break; + } + } //some other checks? if(action != SPAMSERV_CHECK_IGNORE) { @@ -126,21 +136,26 @@ static void neonspam_event_chanmsg_punish(struct ClientSocket *client, struct Ch if(action == SPAMSERV_CHECK_WARN) { reply(client, chanuser->user, "%s", reason); } else if(action == SPAMSERV_CHECK_PUNISH) { - MYSQL_RES *res; - MYSQL_ROW row; loadChannelSettings(chanuser->chan); - printf_mysql_query("SELECT `%s` FROM `channels` WHERE `channel_id` = '%d'", reaction, chanuser->chan->channel_id); - res = mysql_use(); - row = mysql_fetch_row(res); - if(!row[0]) { - printf_mysql_query("SELECT `%s` FROM `channels` WHERE `channel_name` = 'defaults'", reaction); + int reactionid; + if(!(reactionid = atoi(reaction))) { + MYSQL_RES *res; + MYSQL_ROW row; + printf_mysql_query("SELECT `%s` FROM `channels` WHERE `channel_id` = '%d'", reaction, chanuser->chan->channel_id); res = mysql_use(); row = mysql_fetch_row(res); + if(!row[0]) { + printf_mysql_query("SELECT `%s` FROM `channels` WHERE `channel_name` = 'defaults'", reaction); + res = mysql_use(); + row = mysql_fetch_row(res); + } + reactionid = atoi(row[0]); } + int duration = 0; char banmaskBuf[NICKLEN+USERLEN+HOSTLEN+3]; char *banmask = NULL; - switch (atoi(row[0])) { + switch (reactionid) { case 2: //TIMEBAN: 3min duration = 180; case 3: //TIMEBAN: 1h @@ -212,4 +227,41 @@ static int neonspam_floodscan(struct NeonSpamSettings *settings, struct ChanUser return SPAMSERV_CHECK_IGNORE; } - +static int neonspam_botnetscan(struct ClientSocket *client, struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message) { + //crc32 hash of the message + unsigned long msghash = crc32(message); + if((time(0) - settings->lastmsg_time) > BOTNETSCAN_TIME || settings->lastmsg != msghash) { + int i; + for(i = 0; i < BOTNETSCAN_USERS; i++) { + if(settings->botnicks[i]) { + free(settings->botnicks[i]); + settings->botnicks[i] = NULL; + } + } + settings->flags &= ~SPAMSETTINGS_KICKEDBOTQUEUE; + settings->lastmsg = msghash; + } else if(settings->lastmsg == msghash) { + int i; + for(i = 0; i < BOTNETSCAN_USERS; i++) { + if(!settings->botnicks[i]) { + settings->botnicks[i] = strdup(chanuser->user->nick); + break; + } else if(!stricmp(chanuser->user->nick, settings->botnicks[i])) { + return SPAMSERV_CHECK_IGNORE; + } + } + if(i == BOTNETSCAN_USERS) { + //BOTNETSCAN_USERS exceeded + if(!(settings->flags & SPAMSETTINGS_KICKEDBOTQUEUE)) { + for(i = 0; i < BOTNETSCAN_USERS; i++) { + putsock(client, "KICK %s %s :%s", chanuser->chan->name, settings->botnicks[i], SPAMSERV_MSG_BOTNET); + } + settings->flags |= SPAMSETTINGS_KICKEDBOTQUEUE; + } + putsock(client, "KICK %s %s :%s", chanuser->chan->name, chanuser->user->nick, SPAMSERV_MSG_BOTNET); + return SPAMSERV_CHECK_DEAD; + } + } + settings->lastmsg_time = time(0); + return SPAMSERV_CHECK_IGNORE; +} -- 2.20.1