diff --git a/GNUmakefile b/GNUmakefile
index 0f023e7..7427b7d 100644
--- a/GNUmakefile
+++ b/GNUmakefile
@@ -128,6 +128,7 @@ libuhub_SOURCES := \
src/core/commands.c \
src/core/config.c \
src/core/eventqueue.c \
+ src/core/floodctl.c \
src/core/hub.c \
src/core/hubevent.c \
src/core/hubio.c \
diff --git a/src/core/config.c b/src/core/config.c
index 2a1979b..151ebc7 100644
--- a/src/core/config.c
+++ b/src/core/config.c
@@ -142,6 +142,12 @@
#define DEF_LIMIT_MAX_SHARE 0
#define DEF_LIMIT_MIN_SLOTS 0
#define DEF_LIMIT_MAX_SLOTS 0
+#define DEF_FLOOD_INTERVAL 0
+#define DEF_FLOOD_CHAT 0
+#define DEF_FLOOD_CONNECT 0
+#define DEF_FLOOD_SEARCH 0
+#define DEF_FLOOD_UPDATE 0
+#define DEF_FLOOD_EXTRAS 0
#define DEF_TLS_ENABLE 0
#define DEF_TLS_REQUIRE 1
#define DEF_TLS_PRIVATE_KEY ""
@@ -175,6 +181,11 @@
#define DEF_MSG_USER_SLOTS_HIGH "User have too many upload slots."
#define DEF_MSG_USER_HUB_LIMIT_LOW "User is on too few hubs."
#define DEF_MSG_USER_HUB_LIMIT_HIGH "User is on too many hubs."
+#define DEF_MSG_FLOOD_CHAT "Chat flood detected, messages are dropped."
+#define DEF_MSG_FLOOD_CONNECT "Connect flood detected, connection refused."
+#define DEF_MSG_FLOOD_SEARCH "Search flood detected, search is stopped."
+#define DEF_MSG_FLOOD_UPDATE "Update flood detected."
+#define DEF_MSG_FLOOD_EXTRAS "Flood detected."
#define DEF_MSG_PROTO_NO_COMMON_HASH "No common hash algorithm."
#define DEF_MSG_PROTO_OBSOLETE_ADC0 "Your client does not support ADC/1.0."
@@ -217,6 +228,14 @@ void config_defaults(struct hub_config* config)
DEFAULT_INTEGER(limit_min_slots, DEF_LIMIT_MIN_SLOTS);
DEFAULT_INTEGER(limit_max_slots, DEF_LIMIT_MAX_SLOTS);
+ /* Flood control */
+ DEFAULT_INTEGER(flood_ctl_interval, DEF_FLOOD_INTERVAL);
+ DEFAULT_INTEGER(flood_ctl_chat, DEF_FLOOD_CHAT);
+ DEFAULT_INTEGER(flood_ctl_connect, DEF_FLOOD_CONNECT);
+ DEFAULT_INTEGER(flood_ctl_search, DEF_FLOOD_SEARCH);
+ DEFAULT_INTEGER(flood_ctl_update, DEF_FLOOD_UPDATE);
+ DEFAULT_INTEGER(flood_ctl_extras, DEF_FLOOD_EXTRAS);
+
/* Status/error strings */
DEFAULT_STRING (msg_hub_full, DEF_MSG_HUB_FULL);
DEFAULT_STRING (msg_hub_disabled, DEF_MSG_HUB_DISABLED)
@@ -247,6 +266,11 @@ void config_defaults(struct hub_config* config)
DEFAULT_STRING (msg_user_slots_high, DEF_MSG_USER_SLOTS_HIGH);
DEFAULT_STRING (msg_user_hub_limit_low, DEF_MSG_USER_HUB_LIMIT_LOW);
DEFAULT_STRING (msg_user_hub_limit_high, DEF_MSG_USER_HUB_LIMIT_HIGH);
+ DEFAULT_STRING (msg_user_flood_chat, DEF_MSG_FLOOD_CHAT);
+ DEFAULT_STRING (msg_user_flood_connect, DEF_MSG_FLOOD_CONNECT);
+ DEFAULT_STRING (msg_user_flood_search, DEF_MSG_FLOOD_SEARCH);
+ DEFAULT_STRING (msg_user_flood_update, DEF_MSG_FLOOD_UPDATE);
+ DEFAULT_STRING (msg_user_flood_extras, DEF_MSG_FLOOD_EXTRAS);
DEFAULT_STRING (msg_proto_no_common_hash, DEF_MSG_PROTO_NO_COMMON_HASH);
DEFAULT_STRING (msg_proto_obsolete_adc0, DEF_MSG_PROTO_OBSOLETE_ADC0);
@@ -296,6 +320,13 @@ static int apply_config(struct hub_config* config, char* key, char* data, int li
GET_INT(limit_min_slots);
GET_INT(limit_max_slots);
+ GET_INT(flood_ctl_interval);
+ GET_INT(flood_ctl_chat);
+ GET_INT(flood_ctl_connect);
+ GET_INT(flood_ctl_search);
+ GET_INT(flood_ctl_update);
+ GET_INT(flood_ctl_extras);
+
/* Status/error strings */
GET_STR (msg_hub_full);
GET_STR (msg_hub_disabled);
@@ -326,6 +357,11 @@ static int apply_config(struct hub_config* config, char* key, char* data, int li
GET_STR (msg_user_slots_high);
GET_STR (msg_user_hub_limit_low);
GET_STR (msg_user_hub_limit_high);
+ GET_STR (msg_user_flood_chat);
+ GET_STR (msg_user_flood_connect);
+ GET_STR (msg_user_flood_search);
+ GET_STR (msg_user_flood_update);
+ GET_STR (msg_user_flood_extras);
GET_STR (msg_proto_no_common_hash);
GET_STR (msg_proto_obsolete_adc0);
@@ -380,6 +416,11 @@ void free_config(struct hub_config* config)
hub_free(config->msg_user_slots_high);
hub_free(config->msg_user_hub_limit_low);
hub_free(config->msg_user_hub_limit_high);
+ hub_free(config->msg_user_flood_chat);
+ hub_free(config->msg_user_flood_connect);
+ hub_free(config->msg_user_flood_search);
+ hub_free(config->msg_user_flood_update);
+ hub_free(config->msg_user_flood_extras);
hub_free(config->msg_proto_no_common_hash);
hub_free(config->msg_proto_obsolete_adc0);
@@ -462,7 +503,14 @@ void dump_config(struct hub_config* config, int ignore_defaults)
DUMP_INT(limit_max_share, DEF_LIMIT_MAX_SHARE);
DUMP_INT(limit_min_slots, DEF_LIMIT_MIN_SLOTS);
DUMP_INT(limit_max_slots, DEF_LIMIT_MAX_SLOTS);
-
+
+ DUMP_INT(flood_ctl_interval, DEF_FLOOD_INTERVAL);
+ DUMP_INT(flood_ctl_chat, DEF_FLOOD_CHAT);
+ DUMP_INT(flood_ctl_connect, DEF_FLOOD_CONNECT);
+ DUMP_INT(flood_ctl_search, DEF_FLOOD_SEARCH);
+ DUMP_INT(flood_ctl_update, DEF_FLOOD_UPDATE);
+ DUMP_INT(flood_ctl_extras, DEF_FLOOD_EXTRAS);
+
/* Status/error strings */
DUMP_STR (msg_hub_full, DEF_MSG_HUB_FULL);
DUMP_STR (msg_hub_disabled, DEF_MSG_HUB_DISABLED);
@@ -493,6 +541,11 @@ void dump_config(struct hub_config* config, int ignore_defaults)
DUMP_STR (msg_user_slots_high, DEF_MSG_USER_SLOTS_HIGH);
DUMP_STR (msg_user_hub_limit_low, DEF_MSG_USER_HUB_LIMIT_LOW);
DUMP_STR (msg_user_hub_limit_high, DEF_MSG_USER_HUB_LIMIT_HIGH);
+ DUMP_STR (msg_user_flood_chat, DEF_MSG_FLOOD_CHAT);
+ DUMP_STR (msg_user_flood_connect, DEF_MSG_FLOOD_CONNECT);
+ DUMP_STR (msg_user_flood_search, DEF_MSG_FLOOD_SEARCH);
+ DUMP_STR (msg_user_flood_update, DEF_MSG_FLOOD_UPDATE);
+ DUMP_STR (msg_user_flood_extras, DEF_MSG_FLOOD_EXTRAS);
DUMP_STR (msg_proto_no_common_hash, DEF_MSG_PROTO_NO_COMMON_HASH);
DUMP_STR (msg_proto_obsolete_adc0, DEF_MSG_PROTO_OBSOLETE_ADC0);
}
diff --git a/src/core/config.h b/src/core/config.h
index 061d8a0..2e3e48c 100644
--- a/src/core/config.h
+++ b/src/core/config.h
@@ -60,6 +60,13 @@ struct hub_config
int limit_min_slots; /**<<< "Limit minimum number of slots open per user (0=off, default: 0)" */
int limit_max_slots; /**<<< "Limit maximum number of slots open per user (0=off, default: 0)" */
+ int flood_ctl_interval; /**<<< "Time interval in seconds for flood control check. If 0 then all flood control is disabled. (0=off, default: 0)" */
+ int flood_ctl_chat; /**<<< "Max chat messages allowed inside the time interval (0=off, default: 0)" */
+ int flood_ctl_connect; /**<<< "Max connect requests allowed inside the time interval (0=off, default: 0)" */
+ int flood_ctl_search; /**<<< "Max search requests allowed inside the time interval (0=off, default: 0)" */
+ int flood_ctl_update; /**<<< "Max updates allowed inside the time interval (0=off, default: 0)" */
+ int flood_ctl_extras; /**<<< "Max extra protocol messages allowed inside the time interval (0=off, default: 0)" */
+
/* Messages that can be sent to a user */
char* msg_hub_full; /**<<< "hub is full" */
char* msg_hub_disabled; /**<<< "hub is disabled" */
@@ -90,6 +97,11 @@ struct hub_config
char* msg_user_slots_high; /**<<< "User have too many upload slots." */
char* msg_user_hub_limit_low; /**<<< "User is on too few hubs." */
char* msg_user_hub_limit_high; /**<<< "User is on too many hubs." */
+ char* msg_user_flood_chat; /**<<< "Chat flood detected, messages are dropped." */
+ char* msg_user_flood_connect; /**<<< "Connect flood detected, connection refused." */
+ char* msg_user_flood_search; /**<<< "Search flood detected, search is stopped." */
+ char* msg_user_flood_update; /**<<< "Update flood detected." */
+ char* msg_user_flood_extras; /**<<< "Flood detected." */
char* msg_proto_no_common_hash; /**<<< "No common hash algorithm." */
char* msg_proto_obsolete_adc0; /**<<< "Client is using an obsolete ADC protocol version." */
diff --git a/src/core/floodctl.c b/src/core/floodctl.c
new file mode 100644
index 0000000..58ebdc8
--- /dev/null
+++ b/src/core/floodctl.c
@@ -0,0 +1,55 @@
+/*
+ * uhub - A tiny ADC p2p connection hub
+ * Copyright (C) 2007-2010, Jan Vidar Krey
+ *
+ * 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 "uhub.h"
+
+void flood_control_reset(struct flood_control* data)
+{
+ memset(data, 0, sizeof(struct flood_control));
+}
+
+int flood_control_check(struct flood_control* data, size_t max_count, size_t time_delay, time_t now)
+{
+ // Is flood control disabled?
+ if (!time_delay || !max_count)
+ return 0;
+
+ if (!data->time)
+ {
+ data->time = now;
+ data->count = 0;
+ return 0;
+ }
+
+ if ((now - data->time) > time_delay)
+ {
+ data->time = now;
+ data->count = 0;
+ return 0;
+ }
+
+ if (data->count <= max_count)
+ {
+ data->count++;
+ return 0;
+ }
+
+ return 1;
+}
+
diff --git a/src/core/floodctl.h b/src/core/floodctl.h
new file mode 100644
index 0000000..4fea4a4
--- /dev/null
+++ b/src/core/floodctl.h
@@ -0,0 +1,47 @@
+/*
+ * uhub - A tiny ADC p2p connection hub
+ * Copyright (C) 2007-2010, Jan Vidar Krey
+ *
+ * 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 HAVE_UHUB_FLOOD_CTL_H
+#define HAVE_UHUB_FLOOD_CTL_H
+
+struct flood_control
+{
+ time_t time;
+ size_t count;
+};
+
+/**
+ * Reset flood control statistics
+ */
+void flood_control_reset(struct flood_control*);
+
+/**
+ * @param ctl Flood control data structure
+ * @param max_count Max count for flood control
+ * @param window Time window for max_count to appear.
+ * @param now The current time.
+ *
+ * @return 0 if flood no flood detected.
+ * 1 if flood detected.
+ */
+int flood_control_check(struct flood_control* ctl, size_t max_count, size_t window, time_t now);
+
+
+#endif /* HAVE_UHUB_FLOOD_CTL_H */
+
diff --git a/src/core/hub.c b/src/core/hub.c
index 5061ebe..09529a7 100644
--- a/src/core/hub.c
+++ b/src/core/hub.c
@@ -21,6 +21,31 @@
struct hub_info* g_hub = 0;
+#define CHECK_CHAT_ONLY \
+ if (hub->config->chat_only && u->credentials < cred_operator) \
+ break
+
+#define CHECK_FLOOD(TYPE, WARN) \
+ if (flood_control_check(&u->flood_ ## TYPE , hub->config->flood_ctl_ ## TYPE, hub->config->flood_ctl_interval, net_get_time())) \
+ { \
+ if (WARN) \
+ { \
+ hub_send_flood_warning(hub, u, hub->config->msg_user_flood_ ## TYPE); \
+ } \
+ break; \
+ }
+
+#define ROUTE_MSG \
+ if (user_is_logged_in(u)) \
+ { \
+ ret = route_message(hub, u, cmd); \
+ } \
+ else \
+ { \
+ ret = -1; \
+ } \
+ break;
+
int hub_handle_message(struct hub_info* hub, struct hub_user* u, const char* line, size_t length)
{
int ret = 0;
@@ -36,20 +61,32 @@ int hub_handle_message(struct hub_info* hub, struct hub_user* u, const char* lin
{
switch (cmd->cmd)
{
- case ADC_CMD_HSUP: ret = hub_handle_support(hub, u, cmd); break;
- case ADC_CMD_HPAS: ret = hub_handle_password(hub, u, cmd); break;
- case ADC_CMD_BINF: ret = hub_handle_info(hub, u, cmd); break;
+ case ADC_CMD_HSUP:
+ CHECK_FLOOD(extras, 0);
+ ret = hub_handle_support(hub, u, cmd);
+ break;
+
+ case ADC_CMD_HPAS:
+ CHECK_FLOOD(extras, 0);
+ ret = hub_handle_password(hub, u, cmd);
+ break;
+
+ case ADC_CMD_BINF:
+ CHECK_FLOOD(update, 1);
+ ret = hub_handle_info(hub, u, cmd);
+ break;
+
case ADC_CMD_DINF:
case ADC_CMD_EINF:
case ADC_CMD_FINF:
- /* these must never be allowed for security reasons,
- so we ignore them. */
+ /* these must never be allowed for security reasons, so we ignore them. */
break;
case ADC_CMD_EMSG:
case ADC_CMD_DMSG:
case ADC_CMD_BMSG:
case ADC_CMD_FMSG:
+ CHECK_FLOOD(chat, 1);
ret = hub_handle_chat_message(hub, u, cmd);
break;
@@ -57,26 +94,27 @@ int hub_handle_message(struct hub_info* hub, struct hub_user* u, const char* lin
case ADC_CMD_DSCH:
case ADC_CMD_ESCH:
case ADC_CMD_FSCH:
+ cmd->priority = -1;
+ CHECK_CHAT_ONLY;
+ CHECK_FLOOD(search, 1);
+ ROUTE_MSG;
+
case ADC_CMD_DRES:
+ cmd->priority = -1;
+ CHECK_CHAT_ONLY;
+ /* CHECK_FLOOD(search, 0); */
+ ROUTE_MSG;
+
case ADC_CMD_DRCM:
case ADC_CMD_DCTM:
cmd->priority = -1;
- if (hub->config->chat_only && u->credentials < cred_operator)
- {
- /* These below aren't allowed in chat only hubs */
- break;
- }
+ CHECK_CHAT_ONLY;
+ CHECK_FLOOD(connect, 1);
+ ROUTE_MSG;
default:
- if (user_is_logged_in(u))
- {
- ret = route_message(hub, u, cmd);
- }
- else
- {
- ret = -1;
- }
- break;
+ CHECK_FLOOD(extras, 1);
+ ROUTE_MSG;
}
adc_msg_free(cmd);
}
@@ -422,6 +460,28 @@ void hub_send_password_challenge(struct hub_info* hub, struct hub_user* u)
adc_msg_free(igpa);
}
+void hub_send_flood_warning(struct hub_info* hub, struct hub_user* u, const char* message)
+{
+ struct adc_message* msg;
+ char* tmp;
+
+ if (user_flag_get(u, flag_flood))
+ return;
+
+ msg = adc_msg_construct(ADC_CMD_ISTA, 128);
+ if (msg)
+ {
+ tmp = adc_msg_escape(message);
+ adc_msg_add_argument(msg, "110");
+ adc_msg_add_argument(msg, tmp);
+ hub_free(tmp);
+ }
+
+ route_to_user(hub, u, msg);
+ user_flag_set(u, flag_flood);
+ adc_msg_free(msg);
+}
+
static void hub_event_dispatcher(void* callback_data, struct event_data* message)
{
struct hub_info* hub = (struct hub_info*) callback_data;
diff --git a/src/core/hub.h b/src/core/hub.h
index f984fa0..919707f 100644
--- a/src/core/hub.h
+++ b/src/core/hub.h
@@ -227,6 +227,11 @@ extern void hub_send_password_challenge(struct hub_info* hub, struct hub_user* u
*/
extern void hub_send_status(struct hub_info*, struct hub_user* user, enum status_message msg, enum msg_status_level level);
+/**
+ * Warn user about flooding.
+ */
+extern void hub_send_flood_warning(struct hub_info*, struct hub_user* user, const char* message);
+
/**
* Allocates memory, initializes the hub based on the configuration,
* and returns a hub handle.
diff --git a/src/core/user.c b/src/core/user.c
index 4f57910..7028e6d 100644
--- a/src/core/user.c
+++ b/src/core/user.c
@@ -54,6 +54,12 @@ struct hub_user* user_create(struct hub_info* hub, struct net_connection* con, s
memcpy(&user->id.addr, addr, sizeof(struct ip_addr_encap));
user_set_state(user, state_protocol);
+
+ flood_control_reset(&user->flood_chat);
+ flood_control_reset(&user->flood_connect);
+ flood_control_reset(&user->flood_search);
+ flood_control_reset(&user->flood_update);
+ flood_control_reset(&user->flood_extras);
return user;
}
diff --git a/src/core/user.h b/src/core/user.h
index 74f3770..d131fbe 100644
--- a/src/core/user.h
+++ b/src/core/user.h
@@ -22,6 +22,7 @@
struct hub_info;
struct hub_iobuf;
+struct flood_control;
enum user_state
{
@@ -46,6 +47,7 @@ enum user_flags
feature_link = 0x00000100, /** LINK: Hub link (not supported) */
feature_adcs = 0x00000200, /** ADCS: ADC over TLS/SSL */
feature_bas0 = 0x00000400, /** BAS0: Obsolete pre-ADC/1.0 protocol version */
+ flag_flood = 0x00400000, /** User has been notified about flooding. */
flag_muted = 0x00800000, /** User is muted (cannot chat) */
flag_ignore = 0x01000000, /** Ignore further reads */
flag_maxbuf = 0x02000000, /** Hit max buf read, ignore msg */
@@ -117,6 +119,11 @@ struct hub_user
struct hub_user_limits limits; /** Data used for limitation */
enum user_quit_reason quit_reason; /** Quit reason (see user_quit_reason) */
+ struct flood_control flood_chat;
+ struct flood_control flood_connect;
+ struct flood_control flood_search;
+ struct flood_control flood_update;
+ struct flood_control flood_extras;
};
diff --git a/src/network/backend.c b/src/network/backend.c
index 64fdb24..b7aad55 100644
--- a/src/network/backend.c
+++ b/src/network/backend.c
@@ -155,6 +155,12 @@ int net_backend_process()
return 1;
}
+time_t net_get_time()
+{
+ return g_backend->now;
+}
+
+
void net_con_initialize(struct net_connection* con, int sd, net_connection_cb callback, const void* ptr, int events)
{
g_backend->handler.con_init(g_backend->data, con, sd, callback, ptr);
diff --git a/src/network/backend.h b/src/network/backend.h
index cc4ce6d..cabe755 100644
--- a/src/network/backend.h
+++ b/src/network/backend.h
@@ -75,6 +75,11 @@ extern void net_backend_shutdown();
*/
extern int net_backend_process();
+/**
+ * Get the current time.
+ */
+time_t net_get_time();
+
extern struct timeout_queue* net_backend_get_timeout_queue();
struct net_cleanup_handler* net_cleanup_initialize(size_t max);
diff --git a/src/uhub.h b/src/uhub.h
index adeaa52..cc91942 100644
--- a/src/uhub.h
+++ b/src/uhub.h
@@ -72,6 +72,7 @@ extern "C" {
#include "core/auth.h"
#include "core/config.h"
+#include "core/floodctl.h"
#include "core/eventid.h"
#include "core/eventqueue.h"
#include "core/netevent.h"