BanMatcher.cs 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. using System.Collections.Immutable;
  2. using System.Net;
  3. using Content.Server.IP;
  4. using Content.Shared.Database;
  5. using Robust.Shared.Network;
  6. namespace Content.Server.Database;
  7. /// <summary>
  8. /// Implements logic to match a <see cref="ServerBanDef"/> against a player query.
  9. /// </summary>
  10. /// <remarks>
  11. /// <para>
  12. /// This implementation is used by in-game ban matching code, and partially by the SQLite database layer.
  13. /// Some logic is duplicated into both the SQLite and PostgreSQL database layers to provide more optimal SQL queries.
  14. /// Both should be kept in sync, please!
  15. /// </para>
  16. /// </remarks>
  17. public static class BanMatcher
  18. {
  19. /// <summary>
  20. /// Check whether a ban matches the specified player info.
  21. /// </summary>
  22. /// <remarks>
  23. /// <para>
  24. /// This function does not check whether the ban itself is expired or manually unbanned.
  25. /// </para>
  26. /// </remarks>
  27. /// <param name="ban">The ban information.</param>
  28. /// <param name="player">Information about the player to match against.</param>
  29. /// <returns>True if the ban matches the provided player info.</returns>
  30. public static bool BanMatches(ServerBanDef ban, in PlayerInfo player)
  31. {
  32. var exemptFlags = player.ExemptFlags;
  33. // Any flag to bypass BlacklistedRange bans.
  34. if (exemptFlags != ServerBanExemptFlags.None)
  35. exemptFlags |= ServerBanExemptFlags.BlacklistedRange;
  36. if ((ban.ExemptFlags & exemptFlags) != 0)
  37. return false;
  38. if (!player.ExemptFlags.HasFlag(ServerBanExemptFlags.IP)
  39. && player.Address != null
  40. && ban.Address is not null
  41. && player.Address.IsInSubnet(ban.Address.Value)
  42. && (!ban.ExemptFlags.HasFlag(ServerBanExemptFlags.BlacklistedRange) || player.IsNewPlayer))
  43. {
  44. return true;
  45. }
  46. if (player.UserId is { } id && ban.UserId == id.UserId)
  47. {
  48. return true;
  49. }
  50. switch (ban.HWId?.Type)
  51. {
  52. case HwidType.Legacy:
  53. if (player.HWId is { Length: > 0 } hwIdVar
  54. && hwIdVar.AsSpan().SequenceEqual(ban.HWId.Hwid.AsSpan()))
  55. {
  56. return true;
  57. }
  58. break;
  59. case HwidType.Modern:
  60. if (player.ModernHWIds is { Length: > 0 } modernHwIdVar)
  61. {
  62. foreach (var hwid in modernHwIdVar)
  63. {
  64. if (hwid.AsSpan().SequenceEqual(ban.HWId.Hwid.AsSpan()))
  65. return true;
  66. }
  67. }
  68. break;
  69. }
  70. return false;
  71. }
  72. /// <summary>
  73. /// A simple struct containing player info used to match bans against.
  74. /// </summary>
  75. public struct PlayerInfo
  76. {
  77. /// <summary>
  78. /// The user ID of the player.
  79. /// </summary>
  80. public NetUserId? UserId;
  81. /// <summary>
  82. /// The IP address of the player.
  83. /// </summary>
  84. public IPAddress? Address;
  85. /// <summary>
  86. /// The LEGACY hardware ID of the player. Corresponds with <see cref="NetUserData.HWId"/>.
  87. /// </summary>
  88. public ImmutableArray<byte>? HWId;
  89. /// <summary>
  90. /// The modern hardware IDs of the player. Corresponds with <see cref="NetUserData.ModernHWIds"/>.
  91. /// </summary>
  92. public ImmutableArray<ImmutableArray<byte>>? ModernHWIds;
  93. /// <summary>
  94. /// Exemption flags the player has been granted.
  95. /// </summary>
  96. public ServerBanExemptFlags ExemptFlags;
  97. /// <summary>
  98. /// True if this player is new and is thus eligible for more bans.
  99. /// </summary>
  100. public bool IsNewPlayer;
  101. }
  102. }