Files
tbd-station-14/Content.Server/Database/BanMatcher.cs
Julian Giebel df95be1ce5 Kick on ban for entire server group (#28649)
* Start work on PostgresNotificationManager
Implement initial version of init and listening code

* Finish implementing PostgresNotificationManager
Implement ban insert trigger

* Implement ignoring notifications if the ban was from the same server

* Address reviews

* Fixes and refactorings

Fix typo in migration SQL

Pull new code in BanManager out into its own partial file.

Unify logic to kick somebody with that when a new ban is placed directly on the server.

New bans are now checked against all parameters (IP, HWID) instead of just user ID.

Extracted SQLite ban matching code into a new class so that it can mostly be re-used by the ban notification code. No copy-paste here.

Database notifications are now not implicitly sent to the main thread, this means basic checks will happen in the thread pool beforehand.

Bans without user ID are now sent to servers. Bans are rate limited to avoid undue work from mass ban imports, beyond the rate limit they are dropped.

Improved error handling and logging for the whole system.

Matching bans against connected players requires knowing their ban exemption flags. These are now cached when the player connects.

ServerBanDef now has exemption flags, again to allow matching full ban details for ban notifications.

Made database notifications a proper struct type to reduce copy pasting a tuple.

Remove copy pasted connection string building code by just... passing the string into the constructor.

Add lock around _notificationHandlers just in case.

Fixed postgres connection wait not being called in a loop and therefore spamming LISTEN commands for every received notification.

Added more error handling and logging to notification listener.

Removed some copy pasting from SQLite database layer too while I was at it because god forbid we expect anybody else to do all the work in this project.

Sorry Julian

---------

Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
2024-08-20 23:31:33 +02:00

91 lines
2.9 KiB
C#

using System.Collections.Immutable;
using System.Net;
using Content.Server.IP;
using Robust.Shared.Network;
namespace Content.Server.Database;
/// <summary>
/// Implements logic to match a <see cref="ServerBanDef"/> against a player query.
/// </summary>
/// <remarks>
/// <para>
/// This implementation is used by in-game ban matching code, and partially by the SQLite database layer.
/// Some logic is duplicated into both the SQLite and PostgreSQL database layers to provide more optimal SQL queries.
/// Both should be kept in sync, please!
/// </para>
/// </remarks>
public static class BanMatcher
{
/// <summary>
/// Check whether a ban matches the specified player info.
/// </summary>
/// <remarks>
/// <para>
/// This function does not check whether the ban itself is expired or manually unbanned.
/// </para>
/// </remarks>
/// <param name="ban">The ban information.</param>
/// <param name="player">Information about the player to match against.</param>
/// <returns>True if the ban matches the provided player info.</returns>
public static bool BanMatches(ServerBanDef ban, in PlayerInfo player)
{
var exemptFlags = player.ExemptFlags;
// Any flag to bypass BlacklistedRange bans.
if (exemptFlags != ServerBanExemptFlags.None)
exemptFlags |= ServerBanExemptFlags.BlacklistedRange;
if ((ban.ExemptFlags & exemptFlags) != 0)
return false;
if (!player.ExemptFlags.HasFlag(ServerBanExemptFlags.IP)
&& player.Address != null
&& ban.Address is not null
&& player.Address.IsInSubnet(ban.Address.Value)
&& (!ban.ExemptFlags.HasFlag(ServerBanExemptFlags.BlacklistedRange) || player.IsNewPlayer))
{
return true;
}
if (player.UserId is { } id && ban.UserId == id.UserId)
{
return true;
}
return player.HWId is { Length: > 0 } hwIdVar
&& ban.HWId != null
&& hwIdVar.AsSpan().SequenceEqual(ban.HWId.Value.AsSpan());
}
/// <summary>
/// A simple struct containing player info used to match bans against.
/// </summary>
public struct PlayerInfo
{
/// <summary>
/// The user ID of the player.
/// </summary>
public NetUserId? UserId;
/// <summary>
/// The IP address of the player.
/// </summary>
public IPAddress? Address;
/// <summary>
/// The hardware ID of the player.
/// </summary>
public ImmutableArray<byte>? HWId;
/// <summary>
/// Exemption flags the player has been granted.
/// </summary>
public ServerBanExemptFlags ExemptFlags;
/// <summary>
/// True if this player is new and is thus eligible for more bans.
/// </summary>
public bool IsNewPlayer;
}
}