From c254e7d9a1622b19aae9b4ebecb4082657288f4a Mon Sep 17 00:00:00 2001 From: pk910 Date: Mon, 24 Oct 2011 01:19:45 +0200 Subject: [PATCH] added simple anti flood system --- Makefile.am | 1 + database.upgrade.sql | 6 +- src/ClientSocket.c | 19 +++- src/ClientSocket.h | 6 +- src/IRCQueue.c | 219 +++++++++++++++++++++++++++++++++++++++++++ src/IRCQueue.h | 29 ++++++ src/bot_NeonServ.c | 3 +- src/bot_NeonSpam.c | 3 +- src/main.c | 3 + src/main.h | 3 +- src/mysqlConn.c | 2 +- 11 files changed, 286 insertions(+), 8 deletions(-) create mode 100644 src/IRCQueue.c create mode 100644 src/IRCQueue.h diff --git a/Makefile.am b/Makefile.am index 2e4e9dd..534922f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -28,6 +28,7 @@ neonserv_SOURCES = src/version.c \ src/tools.c \ src/timeq.c \ src/DBHelper.c \ + src/IRCQueue.c \ src/bots.c \ src/bot_NeonServ.c \ src/bot_NeonSpam.c \ diff --git a/database.upgrade.sql b/database.upgrade.sql index dbd063d..4d4a9e4 100644 --- a/database.upgrade.sql +++ b/database.upgrade.sql @@ -58,4 +58,8 @@ ALTER TABLE `channels` ADD `channel_digit_reaction_duration` MEDIUMINT(7) NULL, ADD `channel_digit_except` SMALLINT(3) NULL; --- version: 4 \ No newline at end of file +-- version: 4 + +ALTER TABLE `bots` ADD `queue` TINYINT( 1 ) NOT NULL AFTER `textbot` + +-- version: 5 diff --git a/src/ClientSocket.c b/src/ClientSocket.c index 470fa50..cda3ee7 100644 --- a/src/ClientSocket.c +++ b/src/ClientSocket.c @@ -18,6 +18,7 @@ #include "ClientSocket.h" #include "IRCParser.h" #include "UserNode.h" +#include "IRCQueue.h" struct socket_list { struct ClientSocket *data; @@ -59,6 +60,7 @@ struct ClientSocket* create_socket(char *host, int port, char *pass, struct User client->connection_time = 0; client->botid = 0; client->clientid = 0; + client->queue = NULL; client->next = sockets->data; sockets->data = client; return client; @@ -185,14 +187,15 @@ int close_socket(struct ClientSocket *client) { } else last_sock = sock; } + if(client->queue) + queue_destroy(client); free(client->host); free(client->pass); free(client); return 1; } -int write_socket(struct ClientSocket *client, char* msg, int len) { - if(!(client->flags & SOCKET_FLAG_CONNECTED)) return 0; +int write_socket_force(struct ClientSocket *client, char* msg, int len) { printf("[send %d] %s", len, msg); #ifdef WIN32 send(client->sock, msg, len, 0); @@ -203,6 +206,14 @@ int write_socket(struct ClientSocket *client, char* msg, int len) { return 1; } +int write_socket(struct ClientSocket *client, char* msg, int len) { + if(!(client->flags & SOCKET_FLAG_CONNECTED)) return 0; + if(client->flags & SOCKET_FLAG_USE_QUEUE) + return queue_add(client, msg, len); + else + return write_socket_force(client, msg, len); +} + void socket_loop(int timeout_seconds) { if(sockets == NULL) return; fd_set fds; @@ -249,6 +260,8 @@ void socket_loop(int timeout_seconds) { //error sock->flags &= ~(SOCKET_FLAG_CONNECTED | SOCKET_FLAG_READY); bot_disconnect(sock); + if(sock->queue) + queue_destroy(sock); } else { sock->traffic_in += bytes; int used = parse_lines(sock, sock->buffer, sock->bufferpos); @@ -300,6 +313,8 @@ void free_sockets() { next = client->next; if((client->flags & SOCKET_FLAG_CONNECTED)) close(client->sock); + if(client->queue) + queue_destroy(client); free(client->host); free(client->pass); free(client); diff --git a/src/ClientSocket.h b/src/ClientSocket.h index c00d743..7926767 100644 --- a/src/ClientSocket.h +++ b/src/ClientSocket.h @@ -22,7 +22,8 @@ #define SOCKET_FLAG_DEAD 0x01 #define SOCKET_FLAG_CONNECTED 0x02 #define SOCKET_FLAG_READY 0x04 -#define SOCKET_FLAG_PREFERRED 0x08 /* prefered bot to send datas to the IRC World (NOTICE's WHO's etc pp) */ +#define SOCKET_FLAG_PREFERRED 0x08 /* prefered bot to send datas to the IRC World (NOTICE's WHO's etc pp) */ +#define SOCKET_FLAG_USE_QUEUE 0x10 #define BUF_SIZ 512 @@ -41,6 +42,8 @@ struct ClientSocket { unsigned long traffic_in; unsigned long traffic_out; time_t connection_time; + + struct BotQueue *queue; int botid : 16; int clientid : 16; @@ -51,6 +54,7 @@ struct ClientSocket { struct ClientSocket* create_socket(char *host, int port, char *pass, struct UserNode *user); int connect_socket(struct ClientSocket *client); int close_socket(struct ClientSocket *client); +int write_socket_force(struct ClientSocket *client, char* msg, int len); int write_socket(struct ClientSocket *client, char* msg, int len); void socket_loop(int timeout_seconds); void putsock(struct ClientSocket *client, const char *text, ...) PRINTF_LIKE(2, 3); diff --git a/src/IRCQueue.c b/src/IRCQueue.c new file mode 100644 index 0000000..c30a27e --- /dev/null +++ b/src/IRCQueue.c @@ -0,0 +1,219 @@ +/* IRCQueue.c - NeonServ v5.1 + * Copyright (C) 2011 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#include "IRCQueue.h" +#include "ClientSocket.h" + +#define MAXPENALTY 6 + +struct QueueEntry { + char *msg; + struct QueueEntry *next; +}; + +struct BotQueue { + struct ClientSocket *client; + int penalty; + struct QueueEntry *fastqueue_first, *fastqueue_last; + struct QueueEntry *normalqueue_first, *normalqueue_last; + struct QueueEntry *textqueue_first, *textqueue_last; +}; + +static struct BotQueue *initialize_queue(struct ClientSocket *client) { + struct BotQueue *queue = malloc(sizeof(*queue)); + if (!queue) { + perror("malloc() failed"); + return NULL; + } + queue->client = client; + client->queue = queue; + queue->penalty = 0; + queue->fastqueue_first = NULL; + queue->fastqueue_last = NULL; + queue->normalqueue_first = NULL; + queue->normalqueue_last = NULL; + queue->textqueue_first = NULL; + queue->textqueue_last = NULL; + return queue; +} + +int queue_add(struct ClientSocket *client, char* msg, int len) { + if(!client->queue) + client->queue = initialize_queue(client); + struct BotQueue *queue = client->queue; + char *args = strstr(msg, " "); + int type; + if(args) { + *args = '\0'; + if(!stricmp(msg, "MODE")) + type = 3; + else if(!stricmp(msg, "KICK")) + type = 3; + else if(!stricmp(msg, "PONG")) + type = 3; + else if(!stricmp(msg, "PRIVMSG")) + type = 1; + else if(!stricmp(msg, "NOTICE")) + type = 1; + else if(!stricmp(msg, "WHO")) + type = 1; + else + type = 2; + *args = ' '; + } else + type = 2; + struct QueueEntry *entry = malloc(sizeof(*entry)); + if (!entry) { + perror("malloc() failed"); + return 0; + } + entry->msg = strdup(msg); + entry->next = NULL; + if(type == 1) { //low priority + if(queue->textqueue_last) { + queue->textqueue_last->next = entry; + queue->textqueue_last = entry; + } else { + queue->textqueue_last = entry; + queue->textqueue_first = entry; + } + } else if(type == 2) { //normal priority + if(queue->normalqueue_last) { + queue->normalqueue_last->next = entry; + queue->normalqueue_last = entry; + } else { + queue->normalqueue_last = entry; + queue->normalqueue_first = entry; + } + } else if(type == 3) { //high priority + if(queue->fastqueue_last) { + queue->fastqueue_last->next = entry; + queue->fastqueue_last = entry; + } else { + queue->fastqueue_last = entry; + queue->fastqueue_first = entry; + } + } + return 1; +} + +static int calculate_penalty(char *message) { + int msglen = strlen(message); + int penalty = (2 + msglen / 100); + return penalty; +} + +static void dequeue_bot(struct ClientSocket *client) { + if(client->queue->penalty >= MAXPENALTY) return; + int penalty; + //try to send high priority messages + if(client->queue->fastqueue_first) { + do { + struct QueueEntry *entry = client->queue->fastqueue_first; + if(!entry->next) + client->queue->fastqueue_last = NULL; + client->queue->fastqueue_first = client->queue->fastqueue_first->next; + penalty = calculate_penalty(entry->msg); + write_socket_force(client, entry->msg, strlen(entry->msg)); + client->queue->penalty += penalty; + free(entry->msg); + free(entry); + } while(client->queue->penalty < MAXPENALTY && client->queue->fastqueue_first); + } + if(client->queue->penalty >= MAXPENALTY) return; + //try to send normal priority messages + if(client->queue->normalqueue_first) { + do { + struct QueueEntry *entry = client->queue->normalqueue_first; + if(!entry->next) + client->queue->normalqueue_last = NULL; + client->queue->normalqueue_first = client->queue->normalqueue_first->next; + penalty = calculate_penalty(entry->msg); + write_socket_force(client, entry->msg, strlen(entry->msg)); + client->queue->penalty += penalty; + free(entry->msg); + free(entry); + } while(client->queue->penalty < MAXPENALTY && client->queue->normalqueue_first); + } + if(client->queue->penalty >= MAXPENALTY) return; + //try to send low priority messages + if(client->queue->textqueue_first) { + do { + struct QueueEntry *entry = client->queue->textqueue_first; + if(!entry->next) + client->queue->textqueue_last = NULL; + client->queue->textqueue_first = client->queue->textqueue_first->next; + penalty = calculate_penalty(entry->msg); + write_socket_force(client, entry->msg, strlen(entry->msg)); + client->queue->penalty += penalty; + free(entry->msg); + free(entry); + } while(client->queue->penalty < MAXPENALTY && client->queue->textqueue_first); + } +} + +void queue_destroy(struct ClientSocket *client) { + if(!client->queue) return; + struct QueueEntry *entry, *next; + for(entry = client->queue->fastqueue_first; entry; entry = next) { + next = entry->next; + free(entry->msg); + free(entry); + } + for(entry = client->queue->normalqueue_first; entry; entry = next) { + next = entry->next; + free(entry->msg); + free(entry); + } + for(entry = client->queue->textqueue_first; entry; entry = next) { + next = entry->next; + free(entry->msg); + free(entry); + } + free(client->queue); + client->queue = NULL; +} + +static struct timeval lastloop; +void queue_init() { + gettimeofday(&lastloop, NULL); +} + + +void queue_loop() { + struct ClientSocket *bot; + struct timeval now; + gettimeofday(&now, NULL); + long mtime, seconds, useconds; + seconds = now.tv_sec - lastloop.tv_sec; + useconds = now.tv_usec - lastloop.tv_usec; + mtime = ((seconds) * 1000 + useconds/1000.0) + 0.5; + int fullseconds = mtime/1000; + if(fullseconds) { + lastloop.tv_sec += fullseconds; + for(bot = getBots(0, NULL); bot; bot = getBots(0, bot)) { + if(bot->queue && bot->queue->penalty) { + bot->queue->penalty -= fullseconds; + if(bot->queue->penalty < 0) + bot->queue->penalty = 0; + } + } + } + for(bot = getBots(0, NULL); bot; bot = getBots(0, bot)) { + if(bot->queue) + dequeue_bot(bot); + } +} diff --git a/src/IRCQueue.h b/src/IRCQueue.h new file mode 100644 index 0000000..6b9c759 --- /dev/null +++ b/src/IRCQueue.h @@ -0,0 +1,29 @@ +/* IRCQueue.h - NeonServ v5.1 + * Copyright (C) 2011 Philipp Kreil (pk910) + * + * 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 3 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, see . + */ +#ifndef _IRCQueue_h +#define _IRCQueue_h + +#include "main.h" + +struct ClientSocket; + +int queue_add(struct ClientSocket *client, char* msg, int len); +void queue_destroy(struct ClientSocket *client); +void queue_init(); +void queue_loop(); + +#endif \ No newline at end of file diff --git a/src/bot_NeonServ.c b/src/bot_NeonServ.c index 34414f0..3e87c37 100644 --- a/src/bot_NeonServ.c +++ b/src/bot_NeonServ.c @@ -383,7 +383,7 @@ static void start_bots() { MYSQL_RES *res, *res2; MYSQL_ROW row; - printf_mysql_query("SELECT `nick`, `ident`, `realname`, `server`, `port`, `pass`, `textbot`, `id` FROM `bots` WHERE `botclass` = '%d' AND `active` = '1'", BOTID); + printf_mysql_query("SELECT `nick`, `ident`, `realname`, `server`, `port`, `pass`, `textbot`, `id`, `queue` FROM `bots` WHERE `botclass` = '%d' AND `active` = '1'", BOTID); res = mysql_use(); while ((row = mysql_fetch_row(res)) != NULL) { @@ -394,6 +394,7 @@ static void start_bots() { user->flags |= USERFLAG_ISBOT; client = create_socket(row[3], atoi(row[4]), row[5], user); client->flags |= (strcmp(row[6], "0") ? SOCKET_FLAG_PREFERRED : 0); + client->flags |= (strcmp(row[8], "0") ? SOCKET_FLAG_USE_QUEUE : 0); client->botid = BOTID; client->clientid = atoi(row[7]); connect_socket(client); diff --git a/src/bot_NeonSpam.c b/src/bot_NeonSpam.c index a1a2ba8..0d9c5de 100644 --- a/src/bot_NeonSpam.c +++ b/src/bot_NeonSpam.c @@ -102,7 +102,7 @@ static void start_bots() { MYSQL_RES *res, *res2; MYSQL_ROW row; - printf_mysql_query("SELECT `nick`, `ident`, `realname`, `server`, `port`, `pass`, `textbot`, `id` FROM `bots` WHERE `botclass` = '%d' AND `active` = '1'", BOTID); + printf_mysql_query("SELECT `nick`, `ident`, `realname`, `server`, `port`, `pass`, `textbot`, `id`, `queue` FROM `bots` WHERE `botclass` = '%d' AND `active` = '1'", BOTID); res = mysql_use(); while ((row = mysql_fetch_row(res)) != NULL) { @@ -113,6 +113,7 @@ static void start_bots() { user->flags |= USERFLAG_ISBOT; client = create_socket(row[3], atoi(row[4]), row[5], user); client->flags |= (strcmp(row[6], "0") ? SOCKET_FLAG_PREFERRED : 0); + client->flags |= (strcmp(row[8], "0") ? SOCKET_FLAG_USE_QUEUE : 0); client->botid = BOTID; client->clientid = atoi(row[7]); connect_socket(client); diff --git a/src/main.c b/src/main.c index 8d0cd68..186a271 100644 --- a/src/main.c +++ b/src/main.c @@ -31,6 +31,7 @@ #include "timeq.h" #include "EventLogger.h" #include "ModeNode.h" +#include "IRCQueue.h" #include "lib/ini.h" time_t start_time; @@ -109,6 +110,7 @@ int main(void) if(!load_mysql_config()) return 0; + queue_init(); init_lang(); init_parser(); init_UserNode(); @@ -132,6 +134,7 @@ int main(void) loop_bots(); clearTempUsers(); destroyEvents(); + queue_loop(); } } diff --git a/src/main.h b/src/main.h index 522373f..811ad48 100644 --- a/src/main.h +++ b/src/main.h @@ -40,6 +40,7 @@ #endif #include #include +#include #include #if __GNUC__ @@ -72,7 +73,7 @@ #define COMPILER "Unknown" #endif -#define SOCKET_SELECT_TIME 2 +#define SOCKET_SELECT_TIME 1 #define NICKLEN 30 #define USERLEN 10 diff --git a/src/mysqlConn.c b/src/mysqlConn.c index dfc65a9..0216528 100644 --- a/src/mysqlConn.c +++ b/src/mysqlConn.c @@ -16,7 +16,7 @@ */ #include "mysqlConn.h" -#define DATABASE_VERSION "4" +#define DATABASE_VERSION "5" struct used_result { MYSQL_RES *result; -- 2.20.1