Skip to content

Commit

Permalink
[MISSED MIRRORS] General IP intel tweaks (#82904) + fix ipintel cachi…
Browse files Browse the repository at this point in the history
…ng to db not working. (#3197)

* General IP intel tweaks (#82904)





* fix ipintel caching to db not working. (#83046)

---------

Co-authored-by: GoldenAlpharex <[email protected]>
Co-authored-by: oranges <[email protected]>
Co-authored-by: Zephyr <[email protected]>
Co-authored-by: ZephyrTFA <[email protected]>
Co-authored-by: Kyle Spier-Swenson <[email protected]>
Co-authored-by: Iajret <[email protected]>
  • Loading branch information
7 people authored May 15, 2024
1 parent fcbd301 commit bfe01ad
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 72 deletions.
2 changes: 1 addition & 1 deletion code/__DEFINES/admin_verb.dm
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ _ADMIN_VERB(verb_path_name, verb_permissions, verb_name, verb_desc, verb_categor
#define ADMIN_CATEGORY_OBJECT "Object"
#define ADMIN_CATEGORY_MAPPING "Mapping"
#define ADMIN_CATEGORY_PROFILE "Profile"
#define ADMIN_CATEGORY_IPINTEL "IPIntel"
#define ADMIN_CATEGORY_IPINTEL "Admin.IPIntel"

// Visibility flags
#define ADMIN_VERB_VISIBLITY_FLAG_MAPPING_DEBUG "Map-Debug"
14 changes: 7 additions & 7 deletions code/controllers/configuration/entries/general.dm
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@
/datum/config_entry/string/ipintel_email

/datum/config_entry/string/ipintel_email/ValidateAndSet(str_val)
return str_val != "[email protected]" && ..()
return str_val != "[email protected]" && (!length(str_val) || findtext(str_val, "@")) && ..()

/datum/config_entry/number/ipintel_rating_bad
default = 1
Expand All @@ -466,25 +466,25 @@
max_val = 1

/datum/config_entry/flag/ipintel_reject_rate_limited
default = TRUE
default = FALSE

/datum/config_entry/flag/ipintel_reject_bad
default = TRUE
default = FALSE

/datum/config_entry/flag/ipintel_reject_unknown
default = FALSE

/datum/config_entry/number/ipintel_rate_minute
default = 15

/datum/config_entry/number/ipintel_rate_day
default = 500
min_val = 0

/datum/config_entry/number/ipintel_cache_length
default = 7
min_val = 0

/datum/config_entry/number/ipintel_exempt_playtime_living
default = 0
default = 5
min_val = 0

/datum/config_entry/flag/aggressive_changelog

Expand Down
122 changes: 59 additions & 63 deletions code/controllers/subsystem/ipintel.dm
Original file line number Diff line number Diff line change
@@ -1,26 +1,14 @@
SUBSYSTEM_DEF(ipintel)
name = "XKeyScore"
init_order = INIT_ORDER_XKEYSCORE
flags = SS_OK_TO_FAIL_INIT|SS_NO_FIRE
flags = SS_NO_INIT|SS_NO_FIRE
/// The threshold for probability to be considered a VPN and/or bad IP
var/probability_threshold
/// The email used in conjuction with https://check.getipintel.net/check.php
var/contact_email
/// Maximum number of queries per minute
var/max_queries_per_minute
/// Maximum number of queries per day
var/max_queries_per_day
/// Query base
var/query_base
/// The length of time (days) to cache IP intel
var/ipintel_cache_length
/// The living playtime (minutes) for players to be exempt from IPIntel checks
var/exempt_living_playtime

/// Cache for previously queried IP addresses and those stored in the database
var/list/datum/ip_intel/cached_queries = list()
/// The store for rate limiting
var/list/rate_limits
var/list/rate_limit_minute

/// The ip intel for a given address
/datum/ip_intel
Expand All @@ -30,47 +18,39 @@ SUBSYSTEM_DEF(ipintel)
var/address
var/date

/datum/controller/subsystem/ipintel/Initialize()
/datum/controller/subsystem/ipintel/OnConfigLoad()
var/list/fail_messages = list()

probability_threshold = CONFIG_GET(number/ipintel_rating_bad)
if(probability_threshold < 0 || probability_threshold > 1)
fail_messages += list("invalid probability threshold")
var/contact_email = CONFIG_GET(string/ipintel_email)

contact_email = CONFIG_GET(string/ipintel_email)
if(isnull(contact_email) || !findtext(contact_email, "@"))
fail_messages += list("invalid contact email")
if(!length(contact_email))
fail_messages += "No contact email"

var/max_queries_per_minute = CONFIG_GET(number/ipintel_rate_minute)
var/max_queries_per_day = CONFIG_GET(number/ipintel_rate_day)
if(max_queries_per_minute < 0 || max_queries_per_day < 0)
fail_messages += list("invalid rate limits")
if(!findtext(contact_email, "@"))
fail_messages += "Invalid contact email"

var/query_base = CONFIG_GET(string/ipintel_base)
if(isnull(query_base))
fail_messages += list("invalid query base")
if(!length(CONFIG_GET(string/ipintel_base)))
fail_messages += "Invalid query base"

var/ipintel_cache_length = CONFIG_GET(number/ipintel_cache_length)
if(ipintel_cache_length < 0)
fail_messages += list("invalid cache length")

var/exempt_living_playtime = CONFIG_GET(number/ipintel_exempt_playtime_living)
if(exempt_living_playtime < 0)
fail_messages += list("invalid exempt living playtime")
if (!CONFIG_GET(flag/sql_enabled))
fail_messages += "The database is not enabled"

if(length(fail_messages))
message_admins("IPIntel: Initialization failed check logs!")
logger.Log(LOG_CATEGORY_GAME_ACCESS, "IPIntel failed to initialize.", list(
logger.Log(LOG_CATEGORY_GAME_ACCESS, "IPIntel is not enabled because the configs are not valid.", list(
"fail_messages" = fail_messages,
))
return SS_INIT_FAILURE

return SS_INIT_SUCCESS

/datum/controller/subsystem/ipintel/stat_entry(msg)
return "[..()] | D: [max_queries_per_day - rate_limits[IPINTEL_RATE_LIMIT_DAY]] | M: [max_queries_per_minute - rate_limits[IPINTEL_RATE_LIMIT_MINUTE]]"
return "[..()] | M: [CONFIG_GET(number/ipintel_rate_minute) - rate_limit_minute]"


/datum/controller/subsystem/ipintel/proc/is_enabled()
return length(CONFIG_GET(string/ipintel_email)) && length(CONFIG_GET(string/ipintel_base)) && CONFIG_GET(flag/sql_enabled)

/datum/controller/subsystem/ipintel/proc/get_address_intel_state(address, probability_override)
if (!is_enabled())
return IPINTEL_GOOD_IP
var/datum/ip_intel/intel = query_address(address)
if(isnull(intel))
stack_trace("query_address did not return an ip intel response")
Expand All @@ -81,7 +61,7 @@ SUBSYSTEM_DEF(ipintel)

if(!(intel.query_status in list("success", "cached")))
return IPINTEL_UNKNOWN_QUERY_ERROR
var/check_probability = probability_override || probability_threshold
var/check_probability = probability_override || CONFIG_GET(number/ipintel_rating_bad)
if(intel.result >= check_probability)
return IPINTEL_BAD_IP
return IPINTEL_GOOD_IP
Expand All @@ -92,34 +72,32 @@ SUBSYSTEM_DEF(ipintel)

if(minute_key != expected_minute_key)
minute_key = expected_minute_key
rate_limits[IPINTEL_RATE_LIMIT_MINUTE] = 0
rate_limit_minute = 0

if(rate_limits[IPINTEL_RATE_LIMIT_MINUTE] >= max_queries_per_minute)
if(rate_limit_minute >= CONFIG_GET(number/ipintel_rate_minute))
return IPINTEL_RATE_LIMITED_MINUTE
if(rate_limits[IPINTEL_RATE_LIMIT_DAY] >= max_queries_per_day)
return IPINTEL_RATE_LIMITED_DAY
return FALSE

/datum/controller/subsystem/ipintel/proc/query_address(address, allow_cached = TRUE)
if (!is_enabled())
return
if(allow_cached && fetch_cached_ip_intel(address))
return cached_queries[address]
var/is_rate_limited = is_rate_limited()
if(is_rate_limited)
return is_rate_limited
if(!initialized)
return IPINTEL_UNKNOWN_INTERNAL_ERROR

rate_limits[IPINTEL_RATE_LIMIT_MINUTE] += 1
rate_limits[IPINTEL_RATE_LIMIT_DAY] += 1
rate_limit_minute += 1

var/query_base = "https://[src.query_base]/check.php?ip="
var/query = "[query_base][address]&contact=[contact_email]&flags=b&format=json"
var/query_base = "https://[CONFIG_GET(string/ipintel_base)]/check.php?ip="
var/query = "[query_base][address]&contact=[CONFIG_GET(string/ipintel_email)]&flags=b&format=json"

var/datum/http_request/request = new
request.prepare(RUSTG_HTTP_METHOD_GET, query)
request.execute_blocking()
var/datum/http_response/response = request.into_response()
var/list/data = response.body
var/list/data = json_decode(response.body)
// Log the response
logger.Log(LOG_CATEGORY_DEBUG, "ip check response body", data)

var/datum/ip_intel/intel = new
intel.query_status = data["status"]
Expand All @@ -133,13 +111,16 @@ SUBSYSTEM_DEF(ipintel)
return intel

/datum/controller/subsystem/ipintel/proc/add_intel_to_database(datum/ip_intel/intel)
set waitfor = FALSE //no need to make the client connection wait for this step.
if (!SSdbcore.Connect())
return
var/datum/db_query/query = SSdbcore.NewQuery(
"INSERT INTO [format_table_name("ipintel")] ( \
ip, \
intel, \
intel \
) VALUES ( \
INET_ATON(:address) \
:result, \
INET_ATON(:address), \
:result \
)", list(
"address" = intel.address,
"result" = intel.result,
Expand All @@ -150,13 +131,17 @@ SUBSYSTEM_DEF(ipintel)
qdel(query)

/datum/controller/subsystem/ipintel/proc/fetch_cached_ip_intel(address)
var/date_restrictor
if(ipintel_cache_length > 0)
date_restrictor = " AND date > DATE_SUB(NOW(), INTERVAL [ipintel_cache_length] DAY)"
if (!SSdbcore.Connect())
return
var/ipintel_cache_length = CONFIG_GET(number/ipintel_cache_length)
var/date_restrictor = ""
var/sql_args = list("address" = address)
if(ipintel_cache_length > 1)
date_restrictor = " AND date > DATE_SUB(NOW(), INTERVAL :ipintel_cache_length DAY)"
sql_args["ipintel_cache_length"] = ipintel_cache_length
var/datum/db_query/query = SSdbcore.NewQuery(
"SELECT * FROM [format_table_name("ipintel")] WHERE ip = INET_ATON(:address)[date_restrictor]", list(
"address" = address
)
"SELECT * FROM [format_table_name("ipintel")] WHERE ip = INET_ATON(:address)[date_restrictor]",
sql_args
)
query.warn_execute()
query.sync()
Expand All @@ -178,6 +163,9 @@ SUBSYSTEM_DEF(ipintel)
return TRUE

/datum/controller/subsystem/ipintel/proc/is_exempt(client/player)
if(player.holder || GLOB.deadmins[player.ckey])
return TRUE
var/exempt_living_playtime = CONFIG_GET(number/ipintel_exempt_playtime_living)
if(exempt_living_playtime > 0)
var/list/play_records = player.prefs.exp
if (!play_records.len)
Expand All @@ -199,9 +187,13 @@ SUBSYSTEM_DEF(ipintel)
qdel(query)
return FALSE
query.NextRow()
return !!query.item // if they have a row, they are whitelisted
. = !!query.item // if they have a row, they are whitelisted
qdel(query)


ADMIN_VERB(ipintel_allow, R_BAN, "Whitelist Player VPN", "Allow a player to connect even if they are using a VPN.", ADMIN_CATEGORY_IPINTEL, ckey as text)
if (!SSipintel.is_enabled())
to_chat(user, "The ipintel system is not currently enabled but you can still edit the whitelists")
if(SSipintel.is_whitelisted(ckey))
to_chat(user, "Player is already whitelisted.")
return
Expand All @@ -224,6 +216,8 @@ ADMIN_VERB(ipintel_allow, R_BAN, "Whitelist Player VPN", "Allow a player to conn
message_admins("IPINTEL: [key_name_admin(user)] has whitelisted '[ckey]'")

ADMIN_VERB(ipintel_revoke, R_BAN, "Revoke Player VPN Whitelist", "Revoke a player's VPN whitelist.", ADMIN_CATEGORY_IPINTEL, ckey as text)
if (!SSipintel.is_enabled())
to_chat(user, "The ipintel system is not currently enabled but you can still edit the whitelists")
if(!SSipintel.is_whitelisted(ckey))
to_chat(user, "Player is not whitelisted.")
return
Expand All @@ -238,6 +232,8 @@ ADMIN_VERB(ipintel_revoke, R_BAN, "Revoke Player VPN Whitelist", "Revoke a playe
message_admins("IPINTEL: [key_name_admin(user)] has revoked the VPN whitelist for '[ckey]'")

/client/proc/check_ip_intel()
if (!SSipintel.is_enabled())
return
if(SSipintel.is_exempt(src) || SSipintel.is_whitelisted(ckey))
return

Expand Down
2 changes: 1 addition & 1 deletion code/modules/client/client_procs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -516,7 +516,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
"[key_name(src)] (IP: [address], ID: [computer_id]) is a new BYOND account [account_age] day[(account_age == 1?"":"s")] old, created on [account_join_date].[new_player_alert_role ? " <@&[new_player_alert_role]>" : ""]"
)
scream_about_watchlists(src)
check_ip_intel()
validate_key_in_db()
// If we aren't already generating a ban cache, fire off a build request
// This way hopefully any users of request_ban_cache will never need to yield
Expand Down Expand Up @@ -546,6 +545,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
to_chat(src, span_warning("Unable to access asset cache browser, if you are using a custom skin file, please allow DS to download the updated version, if you are not, then make a bug report. This is not a critical issue but can cause issues with resource downloading, as it is impossible to know when extra resources arrived to you."))

update_ambience_pref()
check_ip_intel()

//This is down here because of the browse() calls in tooltip/New()
if(!tooltips)
Expand Down

0 comments on commit bfe01ad

Please sign in to comment.