added simple anti flood system
authorpk910 <philipp@zoelle1.de>
Sun, 23 Oct 2011 23:19:45 +0000 (01:19 +0200)
committerpk910 <philipp@zoelle1.de>
Mon, 24 Oct 2011 00:02:32 +0000 (02:02 +0200)
Makefile.am
database.upgrade.sql
src/ClientSocket.c
src/ClientSocket.h
src/IRCQueue.c [new file with mode: 0644]
src/IRCQueue.h [new file with mode: 0644]
src/bot_NeonServ.c
src/bot_NeonSpam.c
src/main.c
src/main.h
src/mysqlConn.c

index 2e4e9ddca6999e71647ed5f59a41ff01e1e28862..534922fbc0a3b69160bfb9aff7f7736f18ba5507 100644 (file)
@@ -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 \
index dbd063d0f8ce8148f665dffa3886bbd32ecfc2a1..4d4a9e4c9638b403f1c2e14b3948fcff4042e918 100644 (file)
@@ -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
index 470fa505432a16980d343ac6c5f35b6d0d771f73..cda3ee76adeb93b82501db4b305fc05322ef7aa1 100644 (file)
@@ -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);
index c00d743c3ce8f70907b303b28a4d5c0afe58b57f..7926767c973b0135b6077c2cc1f3938e8ee2884b 100644 (file)
@@ -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 (file)
index 0000000..c30a27e
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>. 
+ */
+#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 (file)
index 0000000..6b9c759
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>. 
+ */
+#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
index 34414f02a5167edfbc167972b9e2f6d351f5fdb7..3e87c372d224cc0322d0a4e04a92be306449570e 100644 (file)
@@ -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);
index a1a2ba8822eb06223be0c6e26d1a6106eb648437..0d9c5de855e401656f032fdd9ae9d82e58b05197 100644 (file)
@@ -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);
index 8d0cd68d34f57afa54d58ca3b22668300ff959d1..186a27137907f7993a7c712a51d45742bc2247c8 100644 (file)
@@ -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();
     }
 }
 
index 522373fdd00c43674559e8b0fe0bc2572ee0d685..811ad48cd2f930e5918b511b3999df6d2388f845 100644 (file)
@@ -40,6 +40,7 @@
 #endif
 #include <unistd.h>
 #include <stdarg.h>
+#include <sys/time.h>
 #include <time.h>
 
 #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
index dfc65a9f042742bc1c5013380b5de71f0834c300..02165281f6500961a2cc108ed5b1a2b602427c31 100644 (file)
@@ -16,7 +16,7 @@
  */
 
 #include "mysqlConn.h"
-#define DATABASE_VERSION "4"
+#define DATABASE_VERSION "5"
 
 struct used_result {
     MYSQL_RES *result;