From 85adb0c031e1e1fc1521b30bf05cf54a8711827f Mon Sep 17 00:00:00 2001 From: Rob Bailey Date: Sun, 15 Sep 2019 21:37:14 -0700 Subject: [PATCH] input checking (#46425) Sanity checks + flood checking. Changes keys_held to a rolling buffer instead of a list of arbitrary size. This limits held keys to 15, I recommend not holding keys with your toes personally. --- code/__DEFINES/admin.dm | 7 ++++ code/modules/client/client_defines.dm | 9 +++++ code/modules/keybindings/bindings_client.dm | 41 ++++++++++++++++++++- code/modules/keybindings/setup.dm | 20 +++++++--- 4 files changed, 71 insertions(+), 6 deletions(-) diff --git a/code/__DEFINES/admin.dm b/code/__DEFINES/admin.dm index 7556a482d07..13a2ef79da2 100644 --- a/code/__DEFINES/admin.dm +++ b/code/__DEFINES/admin.dm @@ -83,6 +83,13 @@ #define SPAM_TRIGGER_WARNING 5 //Number of identical messages required before the spam-prevention will warn you to stfu #define SPAM_TRIGGER_AUTOMUTE 10 //Number of identical messages required before the spam-prevention will automute you +///Max length of a keypress command before it's considered to be a forged packet/bogus command +#define MAX_KEYPRESS_COMMANDLENGTH 16 +///Max amount of keypress messages per second over two seconds before client is autokicked +#define MAX_KEYPRESS_AUTOKICK 50 +///Length of held key rolling buffer +#define HELD_KEY_BUFFER_LENGTH 15 + #define STICKYBAN_DB_CACHE_TIME 10 SECONDS #define STICKYBAN_ROGUE_CHECK_TIME 5 diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index 00e20fa2511..4880f4ac89b 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -112,3 +112,12 @@ ///Should only be a key-value list of north/south/east/west = obj/screen. var/list/char_render_holders + + ///Amount of keydowns in the last keysend checking interval + var/client_keysend_amount = 0 + ///World tick time where client_keysend_amount will reset + var/next_keysend_reset = 0 + ///World tick time where keysend_tripped will reset back to false + var/next_keysend_trip_reset = 0 + ///When set to true, user will be autokicked if they trip the keysends in a second limit again + var/keysend_tripped = FALSE diff --git a/code/modules/keybindings/bindings_client.dm b/code/modules/keybindings/bindings_client.dm index e6e4d1a5fc4..0a971dca9ca 100644 --- a/code/modules/keybindings/bindings_client.dm +++ b/code/modules/keybindings/bindings_client.dm @@ -4,7 +4,42 @@ set instant = TRUE set hidden = TRUE + client_keysend_amount += 1 + + var/cache = client_keysend_amount + + if(keysend_tripped && next_keysend_trip_reset <= world.time) + keysend_tripped = FALSE + + if(next_keysend_reset <= world.time) + client_keysend_amount = 0 + next_keysend_reset = world.time + (1 SECONDS) + + //The "tripped" system is to confirm that flooding is still happening after one spike + //not entirely sure how byond commands interact in relation to lag + //don't want to kick people if a lag spike results in a huge flood of commands being sent + if(cache >= MAX_KEYPRESS_AUTOKICK) + if(!keysend_tripped) + keysend_tripped = TRUE + next_keysend_trip_reset = world.time + (2 SECONDS) + else + log_admin("Client [ckey] was just autokicked for flooding keysends; likely abuse but potentially lagspike.") + message_admins("Client [ckey] was just autokicked for flooding keysends; likely abuse but potentially lagspike.") + QDEL_IN(src, 1) + return + + ///Check if the key is short enough to even be a real key + if(LAZYLEN(_key) > MAX_KEYPRESS_COMMANDLENGTH) + to_chat(src, "Invalid KeyDown detected! You have been disconnected from the server automatically.") + log_admin("Client [ckey] just attempted to send an invalid keypress. Keymessage was over [MAX_KEYPRESS_COMMANDLENGTH] characters, autokicking due to likely abuse.") + message_admins("Client [ckey] just attempted to send an invalid keypress. Keymessage was over [MAX_KEYPRESS_COMMANDLENGTH] characters, autokicking due to likely abuse.") + QDEL_IN(src, 1) + return + //offset by 1 because the buffer address is 0 indexed because the math was simpler + keys_held[current_key_address + 1] = _key + //the time a key was pressed isn't actually used anywhere (as of 2019-9-10) but this allows easier access usage/checking keys_held[_key] = world.time + current_key_address = ((current_key_address + 1) % HELD_KEY_BUFFER_LENGTH) var/movement = SSinput.movement_keys[_key] if(!(next_move_dir_sub & movement) && !keys_held["Ctrl"]) next_move_dir_add |= movement @@ -35,7 +70,11 @@ set instant = TRUE set hidden = TRUE - keys_held -= _key + //Can't just do a remove because it would alter the length of the rolling buffer, instead search for the key then null it out if it exists + for(var/i in 1 to HELD_KEY_BUFFER_LENGTH) + if(keys_held[i] == _key) + keys_held[i] = null + break var/movement = SSinput.movement_keys[_key] if(!(next_move_dir_add & movement)) next_move_dir_sub |= movement diff --git a/code/modules/keybindings/setup.dm b/code/modules/keybindings/setup.dm index 54df252f5d0..8433c9bf5a3 100644 --- a/code/modules/keybindings/setup.dm +++ b/code/modules/keybindings/setup.dm @@ -1,9 +1,14 @@ /client - var/list/keys_held = list() // A list of any keys held currently - // These next two vars are to apply movement for keypresses and releases made while move delayed. - // Because discarding that input makes the game less responsive. - var/next_move_dir_add // On next move, add this dir to the move that would otherwise be done - var/next_move_dir_sub // On next move, subtract this dir from the move that would otherwise be done + /// A rolling buffer of any keys held currently + var/list/keys_held = list() + ///used to keep track of the current rolling buffer position + var/current_key_address = 0 + /// These next two vars are to apply movement for keypresses and releases made while move delayed. + /// Because discarding that input makes the game less responsive. + /// On next move, add this dir to the move that would otherwise be done + var/next_move_dir_add + /// On next move, subtract this dir from the move that would otherwise be done + var/next_move_dir_sub // Set a client's focus to an object and override these procs on that object to let it handle keypresses @@ -31,6 +36,11 @@ /client/proc/set_macros() set waitfor = FALSE + //Reset and populate the rolling buffer + keys_held.Cut() + for(var/i in 1 to HELD_KEY_BUFFER_LENGTH) + keys_held += null + erase_all_macros() var/list/macro_sets = SSinput.macro_sets