diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm index 10e33d4e924..39e8fba0b4d 100644 --- a/code/controllers/configuration/entries/general.dm +++ b/code/controllers/configuration/entries/general.dm @@ -166,6 +166,22 @@ if(.) Holiday = config_entry_value +/datum/config_entry/number/minute_topic_limit + config_entry_value = 250 + min_val = 0 + +/datum/config_entry/number/second_topic_limit + config_entry_value = 15 + min_val = 0 + +/datum/config_entry/number/minute_click_limit + config_entry_value = 400 + min_val = 0 + +/datum/config_entry/number/second_click_limit + config_entry_value = 15 + min_val = 0 + /datum/config_entry/number/fps default = 20 integer = FALSE diff --git a/code/modules/client/client defines.dm b/code/modules/client/client defines.dm index 5b9adc2330b..3819f7be0b4 100644 --- a/code/modules/client/client defines.dm +++ b/code/modules/client/client defines.dm @@ -82,6 +82,10 @@ var/mute_irc = 0 var/ip_reputation = 0 //Do we think they're using a proxy/vpn? Only if IP Reputation checking is enabled in config. + ///Used for limiting the rate of topic sends by the client to avoid abuse + var/list/topiclimiter + ///Used for limiting the rate of clicks sends by the client to avoid abuse + var/list/clicklimiter //////////////////////////////////// //things that require the database// diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index 76bbd764890..e77285dc3be 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -4,9 +4,21 @@ #define UPLOAD_LIMIT 10485760 //Restricts client uploads to the server to 10MB //Boosted this thing. What's the worst that can happen? #define MIN_CLIENT_VERSION 0 //Just an ambiguously low version for now, I don't want to suddenly stop people playing. //I would just like the code ready should it ever need to be used. +#define LIMITER_SIZE 12 +#define CURRENT_SECOND 1 +#define SECOND_COUNT 2 +#define CURRENT_MINUTE 3 +#define MINUTE_COUNT 4 +#define ADMINSWARNED_AT 5 //# define TOPIC_DEBUGGING 1 +/client/proc/reduce_minute_count() + if (!topiclimiter) + topiclimiter = new(LIMITER_SIZE) + if(topiclimiter[MINUTE_COUNT] > 0) + topiclimiter[MINUTE_COUNT] -= 1 + /* When somebody clicks a link in game, this Topic is called first. It does the stuff in this proc and then is redirected to the Topic() proc for the src=[0xWhatever] @@ -41,6 +53,38 @@ if (!asset_cache_job) return + // Rate limiting + var/mtl = CONFIG_GET(number/minute_topic_limit) + if (!holder && mtl) + var/minute = round(world.time, 600) + if (!topiclimiter) + topiclimiter = new(LIMITER_SIZE) + if (minute != topiclimiter[CURRENT_MINUTE]) + topiclimiter[CURRENT_MINUTE] = minute + topiclimiter[MINUTE_COUNT] = 0 + topiclimiter[MINUTE_COUNT] += 1 + if (topiclimiter[MINUTE_COUNT] > mtl) + var/msg = "Your previous action was ignored because you've done too many in a minute." + if (minute != topiclimiter[ADMINSWARNED_AT]) //only one admin message per-minute. (if they spam the admins can just boot/ban them) + topiclimiter[ADMINSWARNED_AT] = minute + msg += " Administrators have been informed." + log_and_message_admins("[key_name(src)] Has hit the per-minute topic limit of [mtl] topic calls in a given game minute", src) + to_chat(src, span_danger("[msg]")) + return + + var/stl = CONFIG_GET(number/second_topic_limit) + if (!holder && stl && href_list["window_id"] != "statbrowser") + var/second = round(world.time, 10) + if (!topiclimiter) + topiclimiter = new(LIMITER_SIZE) + if (second != topiclimiter[CURRENT_SECOND]) + topiclimiter[CURRENT_SECOND] = second + topiclimiter[SECOND_COUNT] = 0 + topiclimiter[SECOND_COUNT] += 1 + if (topiclimiter[SECOND_COUNT] > stl) + to_chat(src, span_danger("Your previous action was ignored because you've done too many in a second")) + return + //search the href for script injection if( findtext(href," mcl) + var/msg = "Your previous click was ignored because you've done too many in a minute." + if (minute != clicklimiter[ADMINSWARNED_AT]) //only one admin message per-minute. (if they spam the admins can just boot/ban them) + clicklimiter[ADMINSWARNED_AT] = minute + + msg += " Administrators have been informed." + log_and_message_admins("Has hit the per-minute click limit of [mcl] clicks in a given game minute", src) + to_chat(src, span_danger("[msg]")) + return + + var/scl = CONFIG_GET(number/second_click_limit) + if (!holder && scl) + var/second = round(world.time, 10) + if (!clicklimiter) + clicklimiter = new(LIMITER_SIZE) + + if (second != clicklimiter[CURRENT_SECOND]) + clicklimiter[CURRENT_SECOND] = second + clicklimiter[SECOND_COUNT] = 0 + + clicklimiter[SECOND_COUNT] += 1 + + if (clicklimiter[SECOND_COUNT] > scl) + to_chat(src, span_danger("Your previous click was ignored because you've done too many in a second")) + return SEND_SIGNAL(src, COMSIG_CLIENT_CLICK, object, location, control, params, usr) . = ..() + +#undef ADMINSWARNED_AT +#undef CURRENT_MINUTE +#undef CURRENT_SECOND +#undef LIMITER_SIZE +#undef MINUTE_COUNT +#undef SECOND_COUNT diff --git a/config/example/config.txt b/config/example/config.txt index c984cb2e42b..91dcda1d346 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -243,6 +243,25 @@ SERVER your.domain:6000 ## Remove the # to allow special 'Easter-egg' events on special holidays such as seasonal holidays and stuff like 'Talk Like a Pirate Day' :3 YAARRR ALLOW_HOLIDAYS +## TOPIC RATE LIMITING +## This allows you to limit how many topic calls (clicking on an interface window) the client can do in any given game second and/or game minute. +## Admins are exempt from these limits. +## Hitting the minute limit notifies admins. +## Set to 0 or comment out to disable. +SECOND_TOPIC_LIMIT 10 + +MINUTE_TOPIC_LIMIT 200 + +## CLICK RATE LIMITING +## Same as above, but applies to clicking on objects in the game window. +## This should be a higher then the interface limit to allow for the spam clickly nature of most battles. +## Admins are exempt from these limits. +## Hitting the minute limit notifies admins. +## Set to 0 to disable. +SECOND_CLICK_LIMIT 15 + +MINUTE_CLICK_LIMIT 400 + ##Defines the ticklag for the world. Ticklag is the amount of time between game ticks (aka byond ticks) (in 1/10ths of a second). ## This also controls the client network update rate, as well as the default client fps TICKLAG 0.5