#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"
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;
}
#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
#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;
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
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);
{"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},
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
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);
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) {
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
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;
+}