From 8815118fde169d7a16688b08d419254eb5fa24b9 Mon Sep 17 00:00:00 2001 From: Jan Vidar Krey Date: Mon, 5 Apr 2010 15:51:18 +0200 Subject: [PATCH] Added flood control support. This closes bug #79: Flood control, and rate limits --- GNUmakefile | 1 + src/core/config.c | 55 +++++++++++++++++++++++- src/core/config.h | 12 ++++++ src/core/floodctl.c | 55 ++++++++++++++++++++++++ src/core/floodctl.h | 47 +++++++++++++++++++++ src/core/hub.c | 98 ++++++++++++++++++++++++++++++++++--------- src/core/hub.h | 5 +++ src/core/user.c | 6 +++ src/core/user.h | 7 ++++ src/network/backend.c | 6 +++ src/network/backend.h | 5 +++ src/uhub.h | 1 + 12 files changed, 278 insertions(+), 20 deletions(-) create mode 100644 src/core/floodctl.c create mode 100644 src/core/floodctl.h 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"